notifkit 0.1.0 → 0.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/config/index.ts","../src/contracts/common.ts","../src/contracts/metadata.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/registry.ts","../src/contracts/envelope.ts","../src/contracts/streams.ts","../src/contracts/helpers.ts","../src/contracts/sdk.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/rate-limiter/index.ts","../src/redis/index.ts","../src/repositories/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/workers/health.ts","../src/workers/index.ts","../src/shared/index.ts","../src/templates/render.ts","../src/templates/cache.ts","../src/templates/index.ts","../src/transport/index.ts","../src/unsubscribe/index.ts","../src/workflows/sdk.ts","../src/workflows/registry.ts","../src/client.ts","../src/server.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/config/index.ts","../src/contracts/common.ts","../src/contracts/metadata.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/registry.ts","../src/contracts/envelope.ts","../src/contracts/streams.ts","../src/contracts/helpers.ts","../src/contracts/sdk.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/rate-limiter/index.ts","../src/redis/index.ts","../src/repositories/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/workers/health.ts","../src/workers/index.ts","../src/shared/index.ts","../src/templates/render.ts","../src/templates/cache.ts","../src/templates/index.ts","../src/transport/index.ts","../src/unsubscribe/index.ts","../src/workflows/sdk.ts","../src/workflows/registry.ts","../src/client.ts","../src/server.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { $ as childLogger, $t as PUBSUB_CHANNELS, A as ProjectSettingsCache, At as NotificationRequestedPayloadSchema, B as LUA_RELEASE_LOCK, Bt as QuietHoursSchema, C as ProjectRepository, Ct as DeliveryOptionsSchema, D as WorkflowRepository, Dt as NotificationEnrichedPayloadSchema, E as UserRepository, Et as NotificationScheduledPayloadSchema, F as sleep, Ft as CreateWorkflowSchema, G as normaliseTarget, Gt as UpdateUserSchema, H as LUA_SCHEDULER_CLAIM, Ht as TemplateSchema, I as DataLoader, It as IngestEventSchema, J as PendingMessageScanner, Jt as buildStreamEvent, K as LRUCache, Kt as WorkflowNotifyPayloadSchema, L as CircuitBreaker, Lt as InlineUserSchema, M as AppError, Mt as AddContactSchema, N as ValidationError, Nt as AddUserSchema, O as Redis, Ot as RecipientProfileSchema, P as generateId, Pt as ContactChannelSchema, Q as metrics, Qt as OUTBOUND_STREAMS, R as BatchProcessor, Rt as NotifyRequestSchema, S as PreferenceRepository, St as NotificationDeliveredPayloadSchema, T as TemplateRepository, Tt as RenderedContentSchema, U as LUA_SCHEDULER_POLL, Ut as TriggerWorkflowSchema, V as LUA_RENEW_LOCK, Vt as SyncTemplatesSchema, W as getPriorityBucket, Wt as UpdateProjectSchema, X as StreamProducer, Xt as ENRICHED_STREAMS, Y as StreamConsumer, Yt as CONSUMER_GROUPS, Z as getMetricsRegistry, Zt as INBOUND_STREAMS, _ as escapeHeader, _n as setGlobalConfig, a as SuspendExecutionError, an as registry, at as runMigrations, b as renderWithTemplate, bt as NotificationSkippedPayloadSchema, c as BaseWorker, cn as NotificationPrioritySchema, d as buildUnsubscribeHeaders, dn as baseConfigSchema, en as STREAMS, et as createLogger, f as signUnsubscribeToken, fn as getAiConfig, g as TemplateCache, gn as setAiConfig, h as templateRegistry, hn as readBaseConfig, i as workflowRegistry, in as EventRegistry, it as createDatabase, j as UserThrottle, jt as NotificationTargetSchema, k as RedisClient, kt as NotificationCreatedPayloadSchema, l as NonRetryableError, ln as NotificationStatusSchema, m as renderTemplate, mn as parseConfig, n as NotifkitClient, nn as StreamEventMetadataSchema, nt as withRequestId, o as buildStepNotifyPayload, on as EventMetadataSchema, p as verifyUnsubscribeToken, pn as loadEnv, q as globalEmitter, qt as WorkflowStepSchema, r as workflow, rn as StreamEventSchema, rt as IdempotencyGuard, s as resolveStepTarget, sn as NotificationChannelSchema, t as NotifkitServer, tn as EventEnvelopeSchema, tt as withContext, u as startHealthReporter, un as AI_DEFAULTS, v as escapeHtml, vt as NotificationAiPendingPayloadSchema, w as SegmentRepository, wt as NotificationDispatchedPayloadSchema, x as ContactRepository, xt as NotificationFailedPayloadSchema, y as interpolate, yt as NotificationCanceledPayloadSchema, z as AsyncSemaphore, zt as PreferencesSchema } from "./src-DrSN2wCg.mjs";
2
+ import { $ as childLogger, $t as PUBSUB_CHANNELS, A as ProjectSettingsCache, At as NotificationRequestedPayloadSchema, B as LUA_RELEASE_LOCK, Bt as QuietHoursSchema, C as ProjectRepository, Ct as DeliveryOptionsSchema, D as WorkflowRepository, Dt as NotificationEnrichedPayloadSchema, E as UserRepository, Et as NotificationScheduledPayloadSchema, F as sleep, Ft as CreateWorkflowSchema, G as normaliseTarget, Gt as UpdateUserSchema, H as LUA_SCHEDULER_CLAIM, Ht as TemplateSchema, I as DataLoader, It as IngestEventSchema, J as PendingMessageScanner, Jt as buildStreamEvent, K as LRUCache, Kt as WorkflowNotifyPayloadSchema, L as CircuitBreaker, Lt as InlineUserSchema, M as AppError, Mt as AddContactSchema, N as ValidationError, Nt as AddUserSchema, O as Redis, Ot as RecipientProfileSchema, P as generateId, Pt as ContactChannelSchema, Q as metrics, Qt as OUTBOUND_STREAMS, R as BatchProcessor, Rt as NotifyRequestSchema, S as PreferenceRepository, St as NotificationDeliveredPayloadSchema, T as TemplateRepository, Tt as RenderedContentSchema, U as LUA_SCHEDULER_POLL, Ut as TriggerWorkflowSchema, V as LUA_RENEW_LOCK, Vt as SyncTemplatesSchema, W as getPriorityBucket, Wt as UpdateProjectSchema, X as StreamProducer, Xt as ENRICHED_STREAMS, Y as StreamConsumer, Yt as CONSUMER_GROUPS, Z as getMetricsRegistry, Zt as INBOUND_STREAMS, _ as escapeHeader, _n as setGlobalConfig, a as SuspendExecutionError, an as registry, at as runMigrations, b as renderWithTemplate, bt as NotificationSkippedPayloadSchema, c as BaseWorker, cn as NotificationPrioritySchema, d as buildUnsubscribeHeaders, dn as baseConfigSchema, en as STREAMS, et as createLogger, f as signUnsubscribeToken, fn as getAiConfig, g as TemplateCache, gn as setAiConfig, h as templateRegistry, hn as readBaseConfig, i as workflowRegistry, in as EventRegistry, it as createDatabase, j as UserThrottle, jt as NotificationTargetSchema, k as RedisClient, kt as NotificationCreatedPayloadSchema, l as NonRetryableError, ln as NotificationStatusSchema, m as renderTemplate, mn as parseConfig, n as NotifkitClient, nn as StreamEventMetadataSchema, nt as withRequestId, o as buildStepNotifyPayload, on as EventMetadataSchema, p as verifyUnsubscribeToken, pn as loadEnv, q as globalEmitter, qt as WorkflowStepSchema, r as workflow, rn as StreamEventSchema, rt as IdempotencyGuard, s as resolveStepTarget, sn as NotificationChannelSchema, t as NotifkitServer, tn as EventEnvelopeSchema, tt as withContext, u as startHealthReporter, un as AI_DEFAULTS, v as escapeHtml, vt as NotificationAiPendingPayloadSchema, w as SegmentRepository, wt as NotificationDispatchedPayloadSchema, x as ContactRepository, xt as NotificationFailedPayloadSchema, y as interpolate, yt as NotificationCanceledPayloadSchema, z as AsyncSemaphore, zt as PreferencesSchema } from "./src-C-PfEDMY.mjs";
3
3
  //#region src/transport/index.ts
4
4
  var transport_exports = /* @__PURE__ */ __exportAll({
5
5
  registerTransport: () => registerTransport,
@@ -1,4 +1,4 @@
1
- import { J as PendingMessageScanner, Jt as buildStreamEvent, X as StreamProducer, Y as StreamConsumer, Yt as CONSUMER_GROUPS, _t as workflowWaiters, c as BaseWorker, en as STREAMS, et as createLogger, gt as workflowSteps, hn as readBaseConfig, ht as workflowInstances, it as createDatabase, k as RedisClient, pn as loadEnv, st as messageLogs, u as startHealthReporter } from "./src-DrSN2wCg.mjs";
1
+ import { J as PendingMessageScanner, Jt as buildStreamEvent, X as StreamProducer, Y as StreamConsumer, Yt as CONSUMER_GROUPS, _t as workflowWaiters, c as BaseWorker, en as STREAMS, et as createLogger, gt as workflowSteps, hn as readBaseConfig, ht as workflowInstances, it as createDatabase, k as RedisClient, pn as loadEnv, st as messageLogs, u as startHealthReporter } from "./src-C-PfEDMY.mjs";
2
2
  import { and, eq, gt, isNull, or } from "drizzle-orm";
3
3
  //#region src/services/events/main.ts
4
4
  loadEnv();
@@ -231,4 +231,4 @@ async function stopEventWorker() {
231
231
  //#endregion
232
232
  export { startEventWorker, stopEventWorker };
233
233
 
234
- //# sourceMappingURL=main-D-oWWzR3.mjs.map
234
+ //# sourceMappingURL=main-B-jwm8ED.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"main-D-oWWzR3.mjs","names":["sql"],"sources":["../src/services/events/main.ts"],"sourcesContent":["import { loadEnv, readBaseConfig } from \"@/index.js\";\nimport { createLogger } from \"@/index.js\";\nimport { RedisClient } from \"@/index.js\";\nimport {\n StreamConsumer,\n PendingMessageScanner,\n StreamProducer,\n type StreamMessage,\n} from \"@/index.js\";\nimport { BaseWorker } from \"@/index.js\";\nimport { STREAMS, CONSUMER_GROUPS, buildStreamEvent } from \"@/index.js\";\nimport { type StreamName } from \"@/contracts/streams.js\";\nimport { createDatabase } from \"@/db/index.js\";\nimport { workflowWaiters, workflowSteps, workflowInstances, messageLogs } from \"@/db/schema.js\";\nimport { eq, and, or, isNull, gt } from \"drizzle-orm\";\nimport { type WorkerOptions } from \"@/shared/index.js\";\nimport { startHealthReporter } from \"@/workers/index.js\";\n\nloadEnv();\nconst config = readBaseConfig();\nlet logger: ReturnType<typeof createLogger>;\nlet redis: RedisClient;\nlet sql: any;\nlet db: any;\n\nlet consumer: StreamConsumer;\nlet pendingScanner: PendingMessageScanner;\nlet worker: BaseWorker;\nlet healthInterval: NodeJS.Timeout | null = null;\n\nlet workflowProducer: StreamProducer;\n\nexport interface EventWorkerOptions extends WorkerOptions {\n db: any;\n workflowProducer: any;\n}\n\nexport class EventWorker extends BaseWorker {\n private readonly dbConn: any;\n private readonly workflowProducer: any;\n private messageLogBuffer: { log: any; resolve: () => void; reject: (err: any) => void }[] = [];\n private eventBuffer: {\n producer: any;\n event: any;\n resolve: () => void;\n reject: (err: any) => void;\n }[] = [];\n private flushInterval: NodeJS.Timeout | null = null;\n private isFlushing = false;\n\n constructor(options: EventWorkerOptions) {\n super(options);\n this.dbConn = options.db;\n this.workflowProducer = options.workflowProducer;\n this.startFlushInterval();\n }\n\n private startFlushInterval() {\n this.flushInterval = setInterval(() => {\n void this.flushLogs();\n }, config.LOG_FLUSH_INTERVAL_MS);\n }\n\n private async flushLogs() {\n if (this.isFlushing || (this.messageLogBuffer.length === 0 && this.eventBuffer.length === 0))\n return;\n this.isFlushing = true;\n\n const startTime = Date.now();\n const batch = this.messageLogBuffer;\n this.messageLogBuffer = [];\n\n const events = this.eventBuffer;\n this.eventBuffer = [];\n\n try {\n if (batch.length > 0) {\n await this.dbConn\n .insert(messageLogs)\n .values(batch.map((b) => b.log))\n .onConflictDoNothing(); // Idempotent insert\n for (const b of batch) b.resolve();\n\n this.logger.info(\n {\n batchSize: batch.length,\n latencyMs: Date.now() - startTime,\n },\n \"flushed delivery logs to database\",\n );\n }\n\n if (events.length > 0) {\n const byProducer = new Map<any, typeof events>();\n for (const item of events) {\n if (!byProducer.has(item.producer)) byProducer.set(item.producer, []);\n byProducer.get(item.producer)!.push(item);\n }\n for (const [producer, evts] of byProducer) {\n await producer.publishBatch(evts.map((e) => e.event));\n for (const e of evts) e.resolve();\n }\n }\n } catch (error: any) {\n this.logger.error(\n {\n batchSize: batch.length,\n error: error.message,\n },\n \"failed to flush delivery logs — dropping batch\",\n );\n for (const b of batch) b.reject(error);\n for (const e of events) e.reject(error);\n } finally {\n this.isFlushing = false;\n }\n }\n\n override async stop(): Promise<void> {\n if (this.flushInterval) clearInterval(this.flushInterval);\n await super.stop();\n await this.flushLogs();\n }\n\n override async process(message: StreamMessage): Promise<void> {\n const { event } = message;\n const publishPromises: Promise<void>[] = [];\n\n if (\n event.type === \"notification.dispatched\" ||\n event.type === \"notification.delivered\" ||\n event.type === \"notification.failed\"\n ) {\n const payload = event.payload as any;\n if (this.messageLogBuffer.length >= config.LOG_BUFFER_MAX_SIZE) {\n this.logger.warn(\n { dropped: 1, bufferSize: this.messageLogBuffer.length },\n \"log buffer full — dropping oldest delivery log\",\n );\n const dropped = this.messageLogBuffer.shift(); // Drop oldest to apply back-pressure/prevent OOM\n if (dropped) dropped.reject(new Error(\"Dropped due to buffer overflow\"));\n }\n\n const status =\n event.type === \"notification.dispatched\"\n ? \"dispatched\"\n : event.type === \"notification.delivered\"\n ? \"delivered\"\n : \"failed\";\n\n const kind = event.type === \"notification.dispatched\" ? \"dispatched\" : \"attempt\";\n\n publishPromises.push(\n new Promise((resolve, reject) => {\n this.messageLogBuffer.push({\n log: {\n projectId: payload.projectId,\n taskId: payload.taskId,\n providerMessageId: payload.providerMessageId || null,\n channel: payload.channel,\n attempt: payload.attempt || 1,\n kind,\n status,\n templateId: payload.templateId || null,\n workflowInstanceId: payload.workflowInstanceId || null,\n campaignId: payload.campaignId || null,\n },\n resolve,\n reject,\n });\n }),\n );\n await Promise.all(publishPromises);\n return;\n }\n\n if (event.type !== \"event.received\") {\n return;\n }\n\n const payload = event.payload as any;\n const eventName = payload.eventName;\n const eventData = payload.payload;\n const projectId = payload.projectId;\n\n if (!eventName || !projectId) {\n this.logger.warn(\"Missing eventName or projectId in payload\");\n return;\n }\n\n // Find matching waiters\n const now = new Date();\n const waiters = await this.dbConn\n .select()\n .from(workflowWaiters)\n .where(\n and(\n eq(workflowWaiters.eventName, eventName),\n eq(workflowWaiters.projectId, projectId),\n or(isNull(workflowWaiters.expiresAt), gt(workflowWaiters.expiresAt, now)),\n ),\n );\n\n const data: Record<string, any> = eventData && typeof eventData === \"object\" ? eventData : {};\n\n for (const waiter of waiters) {\n // Check match criteria\n let matched = true;\n const criteria = waiter.matchCriteria as Record<string, any>;\n if (criteria && typeof criteria === \"object\") {\n for (const [k, v] of Object.entries(criteria)) {\n // simple dot notation check for a flat object or 1 level deep\n let dataValue = data[k];\n if (k.includes(\".\")) {\n const parts = k.split(\".\");\n dataValue = data;\n for (const p of parts) {\n if (dataValue) dataValue = dataValue[p];\n }\n }\n if (dataValue !== v) {\n matched = false;\n break;\n }\n }\n }\n\n if (matched) {\n this.logger.info(\n { instanceId: waiter.instanceId, eventName },\n \"Event matched a sleeping workflow\",\n );\n\n // The resume event must carry everything WorkflowWorker.process requires\n // (name, instanceId, projectId) or it is dropped and the instance — whose\n // waiter we are about to delete — is stranded forever.\n const instance = (\n await this.dbConn\n .select()\n .from(workflowInstances)\n .where(eq(workflowInstances.id, waiter.instanceId))\n .limit(1)\n )[0];\n\n if (!instance) {\n this.logger.warn(\n { instanceId: waiter.instanceId },\n \"waiter references a missing workflow instance — dropping waiter\",\n );\n await this.dbConn.delete(workflowWaiters).where(eq(workflowWaiters.id, waiter.id));\n continue;\n }\n\n // Find the step that is waiting\n const steps = await this.dbConn\n .select()\n .from(workflowSteps)\n .where(\n and(\n eq(workflowSteps.instanceId, waiter.instanceId),\n eq(workflowSteps.action, \"waitForEvent\"),\n ),\n );\n\n // Get the latest one that has no output\n const pendingStep = steps.find((s: any) => s.output === null);\n\n if (pendingStep) {\n // Update the step with the event payload\n await this.dbConn\n .update(workflowSteps)\n .set({ output: data })\n .where(eq(workflowSteps.id, pendingStep.id));\n }\n\n // Wake up workflow. Publish BEFORE deleting the waiter: a crash after the\n // delete but before the publish would leave the instance unrecoverable,\n // whereas a duplicate resume is idempotent (the instance lock and the\n // replayed step outputs both absorb it).\n publishPromises.push(\n new Promise((resolve, reject) => {\n this.eventBuffer.push({\n producer: this.workflowProducer,\n event: buildStreamEvent(\n \"workflow.resumed\",\n {\n projectId: instance.projectId,\n instanceId: waiter.instanceId,\n name: instance.name,\n input: instance.input ?? {},\n reason: \"event_matched\",\n },\n \"events\",\n event.metadata.traceId,\n ),\n resolve,\n reject,\n });\n }),\n );\n\n // Delete the waiter\n await this.dbConn.delete(workflowWaiters).where(eq(workflowWaiters.id, waiter.id));\n\n // The timer ZSET entry is left to expire: the poll loop re-checks the\n // instance status and drops the entry if it is no longer pending.\n }\n }\n\n await Promise.all(publishPromises);\n }\n}\n\nexport async function startEventWorker() {\n logger = createLogger({ name: \"event-worker\", level: config.LOG_LEVEL });\n redis = new RedisClient({ url: config.REDIS_URL, name: \"events\", logger });\n const dbData = createDatabase({ url: config.DATABASE_URL, applicationName: \"events\", logger });\n sql = dbData.sql;\n db = dbData.db;\n workflowProducer = new StreamProducer({\n redis: redis.native,\n stream: STREAMS.WORKFLOW_INBOUND,\n logger,\n });\n consumer = new StreamConsumer({\n redis: redis.native,\n stream: STREAMS.EVENTS_INBOUND as StreamName,\n group: CONSUMER_GROUPS.EVENTS as any,\n consumer: `events-${process.pid}`,\n dlqStream: STREAMS.DEAD_LETTER,\n batchSize: config.WORKER_CONCURRENCY,\n logger,\n });\n\n pendingScanner = new PendingMessageScanner({\n redis: redis.native,\n stream: STREAMS.EVENTS_INBOUND as StreamName,\n group: CONSUMER_GROUPS.EVENTS as any,\n consumer: `events-${process.pid}`,\n logger,\n });\n\n worker = new EventWorker({\n consumer,\n pendingScanner,\n logger,\n concurrency: config.WORKER_CONCURRENCY,\n db,\n workflowProducer,\n });\n\n healthInterval = startHealthReporter(\"events\", worker, redis, logger);\n\n logger.info(\"event worker starting\");\n await worker.start();\n}\n\nexport async function stopEventWorker(): Promise<void> {\n logger?.info(\"shutdown initiated\");\n if (healthInterval) clearInterval(healthInterval);\n if (worker) await worker.stop();\n if (sql) await sql.end();\n if (redis) await redis.disconnect();\n logger?.info(\"event worker stopped\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"main-B-jwm8ED.mjs","names":["sql"],"sources":["../src/services/events/main.ts"],"sourcesContent":["import { loadEnv, readBaseConfig } from \"@/index.js\";\nimport { createLogger } from \"@/index.js\";\nimport { RedisClient } from \"@/index.js\";\nimport {\n StreamConsumer,\n PendingMessageScanner,\n StreamProducer,\n type StreamMessage,\n} from \"@/index.js\";\nimport { BaseWorker } from \"@/index.js\";\nimport { STREAMS, CONSUMER_GROUPS, buildStreamEvent } from \"@/index.js\";\nimport { type StreamName } from \"@/contracts/streams.js\";\nimport { createDatabase } from \"@/db/index.js\";\nimport { workflowWaiters, workflowSteps, workflowInstances, messageLogs } from \"@/db/schema.js\";\nimport { eq, and, or, isNull, gt } from \"drizzle-orm\";\nimport { type WorkerOptions } from \"@/shared/index.js\";\nimport { startHealthReporter } from \"@/workers/index.js\";\n\nloadEnv();\nconst config = readBaseConfig();\nlet logger: ReturnType<typeof createLogger>;\nlet redis: RedisClient;\nlet sql: any;\nlet db: any;\n\nlet consumer: StreamConsumer;\nlet pendingScanner: PendingMessageScanner;\nlet worker: BaseWorker;\nlet healthInterval: NodeJS.Timeout | null = null;\n\nlet workflowProducer: StreamProducer;\n\nexport interface EventWorkerOptions extends WorkerOptions {\n db: any;\n workflowProducer: any;\n}\n\nexport class EventWorker extends BaseWorker {\n private readonly dbConn: any;\n private readonly workflowProducer: any;\n private messageLogBuffer: { log: any; resolve: () => void; reject: (err: any) => void }[] = [];\n private eventBuffer: {\n producer: any;\n event: any;\n resolve: () => void;\n reject: (err: any) => void;\n }[] = [];\n private flushInterval: NodeJS.Timeout | null = null;\n private isFlushing = false;\n\n constructor(options: EventWorkerOptions) {\n super(options);\n this.dbConn = options.db;\n this.workflowProducer = options.workflowProducer;\n this.startFlushInterval();\n }\n\n private startFlushInterval() {\n this.flushInterval = setInterval(() => {\n void this.flushLogs();\n }, config.LOG_FLUSH_INTERVAL_MS);\n }\n\n private async flushLogs() {\n if (this.isFlushing || (this.messageLogBuffer.length === 0 && this.eventBuffer.length === 0))\n return;\n this.isFlushing = true;\n\n const startTime = Date.now();\n const batch = this.messageLogBuffer;\n this.messageLogBuffer = [];\n\n const events = this.eventBuffer;\n this.eventBuffer = [];\n\n try {\n if (batch.length > 0) {\n await this.dbConn\n .insert(messageLogs)\n .values(batch.map((b) => b.log))\n .onConflictDoNothing(); // Idempotent insert\n for (const b of batch) b.resolve();\n\n this.logger.info(\n {\n batchSize: batch.length,\n latencyMs: Date.now() - startTime,\n },\n \"flushed delivery logs to database\",\n );\n }\n\n if (events.length > 0) {\n const byProducer = new Map<any, typeof events>();\n for (const item of events) {\n if (!byProducer.has(item.producer)) byProducer.set(item.producer, []);\n byProducer.get(item.producer)!.push(item);\n }\n for (const [producer, evts] of byProducer) {\n await producer.publishBatch(evts.map((e) => e.event));\n for (const e of evts) e.resolve();\n }\n }\n } catch (error: any) {\n this.logger.error(\n {\n batchSize: batch.length,\n error: error.message,\n },\n \"failed to flush delivery logs — dropping batch\",\n );\n for (const b of batch) b.reject(error);\n for (const e of events) e.reject(error);\n } finally {\n this.isFlushing = false;\n }\n }\n\n override async stop(): Promise<void> {\n if (this.flushInterval) clearInterval(this.flushInterval);\n await super.stop();\n await this.flushLogs();\n }\n\n override async process(message: StreamMessage): Promise<void> {\n const { event } = message;\n const publishPromises: Promise<void>[] = [];\n\n if (\n event.type === \"notification.dispatched\" ||\n event.type === \"notification.delivered\" ||\n event.type === \"notification.failed\"\n ) {\n const payload = event.payload as any;\n if (this.messageLogBuffer.length >= config.LOG_BUFFER_MAX_SIZE) {\n this.logger.warn(\n { dropped: 1, bufferSize: this.messageLogBuffer.length },\n \"log buffer full — dropping oldest delivery log\",\n );\n const dropped = this.messageLogBuffer.shift(); // Drop oldest to apply back-pressure/prevent OOM\n if (dropped) dropped.reject(new Error(\"Dropped due to buffer overflow\"));\n }\n\n const status =\n event.type === \"notification.dispatched\"\n ? \"dispatched\"\n : event.type === \"notification.delivered\"\n ? \"delivered\"\n : \"failed\";\n\n const kind = event.type === \"notification.dispatched\" ? \"dispatched\" : \"attempt\";\n\n publishPromises.push(\n new Promise((resolve, reject) => {\n this.messageLogBuffer.push({\n log: {\n projectId: payload.projectId,\n taskId: payload.taskId,\n providerMessageId: payload.providerMessageId || null,\n channel: payload.channel,\n attempt: payload.attempt || 1,\n kind,\n status,\n templateId: payload.templateId || null,\n workflowInstanceId: payload.workflowInstanceId || null,\n campaignId: payload.campaignId || null,\n },\n resolve,\n reject,\n });\n }),\n );\n await Promise.all(publishPromises);\n return;\n }\n\n if (event.type !== \"event.received\") {\n return;\n }\n\n const payload = event.payload as any;\n const eventName = payload.eventName;\n const eventData = payload.payload;\n const projectId = payload.projectId;\n\n if (!eventName || !projectId) {\n this.logger.warn(\"Missing eventName or projectId in payload\");\n return;\n }\n\n // Find matching waiters\n const now = new Date();\n const waiters = await this.dbConn\n .select()\n .from(workflowWaiters)\n .where(\n and(\n eq(workflowWaiters.eventName, eventName),\n eq(workflowWaiters.projectId, projectId),\n or(isNull(workflowWaiters.expiresAt), gt(workflowWaiters.expiresAt, now)),\n ),\n );\n\n const data: Record<string, any> = eventData && typeof eventData === \"object\" ? eventData : {};\n\n for (const waiter of waiters) {\n // Check match criteria\n let matched = true;\n const criteria = waiter.matchCriteria as Record<string, any>;\n if (criteria && typeof criteria === \"object\") {\n for (const [k, v] of Object.entries(criteria)) {\n // simple dot notation check for a flat object or 1 level deep\n let dataValue = data[k];\n if (k.includes(\".\")) {\n const parts = k.split(\".\");\n dataValue = data;\n for (const p of parts) {\n if (dataValue) dataValue = dataValue[p];\n }\n }\n if (dataValue !== v) {\n matched = false;\n break;\n }\n }\n }\n\n if (matched) {\n this.logger.info(\n { instanceId: waiter.instanceId, eventName },\n \"Event matched a sleeping workflow\",\n );\n\n // The resume event must carry everything WorkflowWorker.process requires\n // (name, instanceId, projectId) or it is dropped and the instance — whose\n // waiter we are about to delete — is stranded forever.\n const instance = (\n await this.dbConn\n .select()\n .from(workflowInstances)\n .where(eq(workflowInstances.id, waiter.instanceId))\n .limit(1)\n )[0];\n\n if (!instance) {\n this.logger.warn(\n { instanceId: waiter.instanceId },\n \"waiter references a missing workflow instance — dropping waiter\",\n );\n await this.dbConn.delete(workflowWaiters).where(eq(workflowWaiters.id, waiter.id));\n continue;\n }\n\n // Find the step that is waiting\n const steps = await this.dbConn\n .select()\n .from(workflowSteps)\n .where(\n and(\n eq(workflowSteps.instanceId, waiter.instanceId),\n eq(workflowSteps.action, \"waitForEvent\"),\n ),\n );\n\n // Get the latest one that has no output\n const pendingStep = steps.find((s: any) => s.output === null);\n\n if (pendingStep) {\n // Update the step with the event payload\n await this.dbConn\n .update(workflowSteps)\n .set({ output: data })\n .where(eq(workflowSteps.id, pendingStep.id));\n }\n\n // Wake up workflow. Publish BEFORE deleting the waiter: a crash after the\n // delete but before the publish would leave the instance unrecoverable,\n // whereas a duplicate resume is idempotent (the instance lock and the\n // replayed step outputs both absorb it).\n publishPromises.push(\n new Promise((resolve, reject) => {\n this.eventBuffer.push({\n producer: this.workflowProducer,\n event: buildStreamEvent(\n \"workflow.resumed\",\n {\n projectId: instance.projectId,\n instanceId: waiter.instanceId,\n name: instance.name,\n input: instance.input ?? {},\n reason: \"event_matched\",\n },\n \"events\",\n event.metadata.traceId,\n ),\n resolve,\n reject,\n });\n }),\n );\n\n // Delete the waiter\n await this.dbConn.delete(workflowWaiters).where(eq(workflowWaiters.id, waiter.id));\n\n // The timer ZSET entry is left to expire: the poll loop re-checks the\n // instance status and drops the entry if it is no longer pending.\n }\n }\n\n await Promise.all(publishPromises);\n }\n}\n\nexport async function startEventWorker() {\n logger = createLogger({ name: \"event-worker\", level: config.LOG_LEVEL });\n redis = new RedisClient({ url: config.REDIS_URL, name: \"events\", logger });\n const dbData = createDatabase({ url: config.DATABASE_URL, applicationName: \"events\", logger });\n sql = dbData.sql;\n db = dbData.db;\n workflowProducer = new StreamProducer({\n redis: redis.native,\n stream: STREAMS.WORKFLOW_INBOUND,\n logger,\n });\n consumer = new StreamConsumer({\n redis: redis.native,\n stream: STREAMS.EVENTS_INBOUND as StreamName,\n group: CONSUMER_GROUPS.EVENTS as any,\n consumer: `events-${process.pid}`,\n dlqStream: STREAMS.DEAD_LETTER,\n batchSize: config.WORKER_CONCURRENCY,\n logger,\n });\n\n pendingScanner = new PendingMessageScanner({\n redis: redis.native,\n stream: STREAMS.EVENTS_INBOUND as StreamName,\n group: CONSUMER_GROUPS.EVENTS as any,\n consumer: `events-${process.pid}`,\n logger,\n });\n\n worker = new EventWorker({\n consumer,\n pendingScanner,\n logger,\n concurrency: config.WORKER_CONCURRENCY,\n db,\n workflowProducer,\n });\n\n healthInterval = startHealthReporter(\"events\", worker, redis, logger);\n\n logger.info(\"event worker starting\");\n await worker.start();\n}\n\nexport async function stopEventWorker(): Promise<void> {\n logger?.info(\"shutdown initiated\");\n if (healthInterval) clearInterval(healthInterval);\n if (worker) await worker.stop();\n if (sql) await sql.end();\n if (redis) await redis.disconnect();\n logger?.info(\"event worker stopped\");\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { $t as PUBSUB_CHANNELS, A as ProjectSettingsCache, C as ProjectRepository, G as normaliseTarget, I as DataLoader, J as PendingMessageScanner, Jt as buildStreamEvent, T as TemplateRepository, W as getPriorityBucket, X as StreamProducer, Xt as ENRICHED_STREAMS, Y as StreamConsumer, Yt as CONSUMER_GROUPS, an as registry, b as renderWithTemplate, c as BaseWorker, d as buildUnsubscribeHeaders, dt as suppressions, en as STREAMS, et as createLogger, g as TemplateCache, hn as readBaseConfig, it as createDatabase, j as UserThrottle, k as RedisClient, pn as loadEnv, q as globalEmitter, rt as IdempotencyGuard, u as startHealthReporter, ut as scheduledPayloads, x as ContactRepository } from "./src-DrSN2wCg.mjs";
1
+ import { $t as PUBSUB_CHANNELS, A as ProjectSettingsCache, C as ProjectRepository, G as normaliseTarget, I as DataLoader, J as PendingMessageScanner, Jt as buildStreamEvent, T as TemplateRepository, W as getPriorityBucket, X as StreamProducer, Xt as ENRICHED_STREAMS, Y as StreamConsumer, Yt as CONSUMER_GROUPS, an as registry, b as renderWithTemplate, c as BaseWorker, d as buildUnsubscribeHeaders, dt as suppressions, en as STREAMS, et as createLogger, g as TemplateCache, hn as readBaseConfig, it as createDatabase, j as UserThrottle, k as RedisClient, pn as loadEnv, q as globalEmitter, rt as IdempotencyGuard, u as startHealthReporter, ut as scheduledPayloads, x as ContactRepository } from "./src-C-PfEDMY.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { and, eq } from "drizzle-orm";
4
4
  //#region src/services/engine/main.ts
@@ -568,4 +568,4 @@ async function stopEngineWorker() {
568
568
  //#endregion
569
569
  export { startEngineWorker, stopEngineWorker };
570
570
 
571
- //# sourceMappingURL=main-Dlfy9mWs.mjs.map
571
+ //# sourceMappingURL=main-B561M1d3.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"main-Dlfy9mWs.mjs","names":["sql"],"sources":["../src/services/engine/main.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { loadEnv, readBaseConfig } from \"@/index.js\";\nimport { createLogger } from \"@/index.js\";\nimport { RedisClient, type Redis } from \"@/index.js\";\nimport {\n StreamConsumer,\n PendingMessageScanner,\n StreamProducer,\n type StreamMessage,\n} from \"@/index.js\";\nimport { BaseWorker } from \"@/index.js\";\nimport {\n STREAMS,\n ENRICHED_STREAMS,\n CONSUMER_GROUPS,\n PUBSUB_CHANNELS,\n registry,\n buildStreamEvent,\n type NotificationEnrichedPayload,\n type NotificationDispatchedPayload,\n} from \"@/index.js\";\nimport { type StreamName } from \"@/contracts/streams.js\";\nimport { IdempotencyGuard } from \"@/index.js\";\nimport { UserThrottle, ProjectSettingsCache } from \"@/index.js\";\nimport { TemplateRepository, ContactRepository, ProjectRepository } from \"@/index.js\";\nimport { createDatabase } from \"@/db/index.js\";\nimport { scheduledPayloads, suppressions } from \"@/db/schema.js\";\nimport { and, eq } from \"drizzle-orm\";\nimport {\n getPriorityBucket,\n globalEmitter,\n normaliseTarget,\n type WorkerOptions,\n DataLoader,\n} from \"@/shared/index.js\";\nimport { renderWithTemplate, TemplateCache } from \"@/templates/index.js\";\nimport { buildUnsubscribeHeaders } from \"@/unsubscribe/index.js\";\nimport { startHealthReporter } from \"@/workers/index.js\";\n\nfunction localTimeToUtc(\n year: number,\n month: number,\n day: number,\n hour: number,\n minute: number,\n second: number,\n timezone: string,\n): Date {\n let utcMs = Date.UTC(year, month - 1, day, hour, minute, second);\n const formatter = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n });\n\n for (let iter = 0; iter < 3; iter++) {\n const parts = formatter.formatToParts(new Date(utcMs));\n const pYear = parseInt(parts.find((p) => p.type === \"year\")?.value ?? \"0\", 10);\n const pMonth = parseInt(parts.find((p) => p.type === \"month\")?.value ?? \"0\", 10);\n const pDay = parseInt(parts.find((p) => p.type === \"day\")?.value ?? \"0\", 10);\n const pHour = parseInt(parts.find((p) => p.type === \"hour\")?.value ?? \"0\", 10);\n const pMin = parseInt(parts.find((p) => p.type === \"minute\")?.value ?? \"0\", 10);\n const pSec = parseInt(parts.find((p) => p.type === \"second\")?.value ?? \"0\", 10);\n\n const targetMs = Date.UTC(year, month - 1, day, hour, minute, second);\n const actualMs = Date.UTC(pYear, pMonth - 1, pDay, pHour, pMin, pSec);\n const diff = targetMs - actualMs;\n if (diff === 0) break;\n utcMs += diff;\n }\n return new Date(utcMs);\n}\n\nexport function isInQuietHours(\n timezone: string,\n quietHours: { start: string; end: string }[],\n fromDate: Date = new Date(),\n): { inQuietHours: boolean; nextActiveTime?: Date } {\n if (!quietHours || quietHours.length === 0) {\n return { inQuietHours: false };\n }\n\n const checkWindowAt = (date: Date) => {\n let parts: Intl.DateTimeFormatPart[];\n try {\n const formatter = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n });\n parts = formatter.formatToParts(date);\n } catch {\n return null;\n }\n\n const year = parseInt(parts.find((p) => p.type === \"year\")?.value ?? \"0\", 10);\n const month = parseInt(parts.find((p) => p.type === \"month\")?.value ?? \"0\", 10);\n const day = parseInt(parts.find((p) => p.type === \"day\")?.value ?? \"0\", 10);\n const currentHour = parseInt(parts.find((p) => p.type === \"hour\")?.value ?? \"0\", 10);\n const currentMin = parseInt(parts.find((p) => p.type === \"minute\")?.value ?? \"0\", 10);\n\n const currentMinutes = currentHour * 60 + currentMin;\n\n for (const window of quietHours) {\n const [startH, startM] = window.start.split(\":\").map(Number);\n const [endH, endM] = window.end.split(\":\").map(Number);\n\n if (startH === undefined || startM === undefined || endH === undefined || endM === undefined)\n continue;\n\n const startMinutes = startH * 60 + startM;\n const endMinutes = endH * 60 + endM;\n\n let inWindow = false;\n if (startMinutes <= endMinutes) {\n inWindow = currentMinutes >= startMinutes && currentMinutes < endMinutes;\n } else {\n inWindow = currentMinutes >= startMinutes || currentMinutes < endMinutes;\n }\n\n if (inWindow) {\n let targetYear = year;\n let targetMonth = month;\n let targetDay = day;\n\n if (currentMinutes >= endMinutes) {\n const nextDay = new Date(Date.UTC(year, month - 1, day + 1));\n targetYear = nextDay.getUTCFullYear();\n targetMonth = nextDay.getUTCMonth() + 1;\n targetDay = nextDay.getUTCDate();\n }\n\n const nextActive = localTimeToUtc(\n targetYear,\n targetMonth,\n targetDay,\n endH,\n endM,\n 0,\n timezone,\n );\n return { inQuietHours: true, nextActiveTime: nextActive };\n }\n }\n return { inQuietHours: false };\n };\n\n let initialCheck = checkWindowAt(fromDate);\n if (!initialCheck || !initialCheck.inQuietHours) {\n return { inQuietHours: false };\n }\n\n // Chained / overlapping quiet hours intervals\n let candidateTime = initialCheck.nextActiveTime!;\n for (let i = 0; i < 10; i++) {\n const subsequentCheck = checkWindowAt(candidateTime);\n if (subsequentCheck && subsequentCheck.inQuietHours && subsequentCheck.nextActiveTime) {\n if (subsequentCheck.nextActiveTime.getTime() <= candidateTime.getTime()) {\n break;\n }\n candidateTime = subsequentCheck.nextActiveTime;\n } else {\n break;\n }\n }\n\n return { inQuietHours: true, nextActiveTime: candidateTime };\n}\n\n// ─── Bootstrap ─────────────────────────────────────────────────────────────\n\nloadEnv();\nconst config = readBaseConfig();\n\nlet logger: ReturnType<typeof createLogger>;\nlet redis: RedisClient;\nlet sql: any;\nlet db: any;\nlet templateRepo: TemplateRepository;\n\nlet templateCache: TemplateCache;\n\nlet consumer: StreamConsumer;\nlet pendingScanner: PendingMessageScanner;\nlet worker: BaseWorker;\nlet healthInterval: NodeJS.Timeout | null = null;\n\nexport interface EngineWorkerOptions extends WorkerOptions {\n registry: any;\n idempotency: any;\n throttle: any;\n projectSettings: ProjectSettingsCache;\n redis: Redis;\n templateCache: TemplateCache;\n aiPendingProducer: any;\n scheduledProducer: any;\n outboundProducers: any;\n globalEmitter: any;\n contactRepo: any;\n db: any;\n}\n\nexport class EngineWorker extends BaseWorker {\n private readonly registry: any;\n private readonly idempotency: any;\n private readonly throttle: any;\n private readonly projectSettings: ProjectSettingsCache;\n private readonly redisCli: Redis;\n private readonly templateCache: TemplateCache;\n private readonly aiPendingProducer: any;\n private readonly scheduledProducer: any;\n private readonly outboundProducers: any;\n private readonly globalEmitter: any;\n private readonly contactRepo: any;\n private readonly db: any;\n\n constructor(options: EngineWorkerOptions) {\n super(options);\n this.registry = options.registry;\n this.idempotency = options.idempotency;\n this.throttle = options.throttle;\n this.projectSettings = options.projectSettings;\n this.redisCli = options.redis;\n this.templateCache = options.templateCache;\n this.aiPendingProducer = options.aiPendingProducer;\n this.scheduledProducer = options.scheduledProducer;\n this.outboundProducers = options.outboundProducers;\n this.globalEmitter = options.globalEmitter;\n this.contactRepo = options.contactRepo;\n this.db = options.db;\n }\n\n private readonly contactsLoader = new DataLoader<\n { projectId: string; recipientId: string },\n any[]\n >(async (keys) => {\n const byProject = new Map<string, string[]>();\n for (const key of keys) {\n if (!byProject.has(key.projectId)) byProject.set(key.projectId, []);\n byProject.get(key.projectId)!.push(key.recipientId);\n }\n\n const resultsByProjectAndUser = new Map<string, Map<string, any[]>>();\n for (const [projectId, userIds] of byProject) {\n const activeContactsMap = await this.contactRepo.findActiveByUserIds(projectId, userIds);\n resultsByProjectAndUser.set(projectId, activeContactsMap);\n }\n\n return keys.map((key) => {\n const projectMap = resultsByProjectAndUser.get(key.projectId);\n if (!projectMap) return [];\n return projectMap.get(key.recipientId) || [];\n });\n });\n\n /**\n * Suppressed destinations for one (project, channel), as a normalised set.\n *\n * Loaded per project+channel rather than per address: a campaign is thousands\n * of messages against one channel, so this collapses to a single query for\n * the whole batch. Cached for the loader's lifetime of a tick, which means a\n * suppression written mid-batch takes effect on the next batch — acceptable,\n * since the webhook that writes it is itself minutes behind the send.\n */\n private readonly suppressionsLoader = new DataLoader<\n { projectId: string; channel: string },\n Set<string>\n >(async (keys) => {\n const results = new Map<string, Set<string>>();\n for (const key of keys) {\n const cacheKey = `${key.projectId}:${key.channel}`;\n if (results.has(cacheKey)) continue;\n try {\n const rows = await this.db\n .select({ target: suppressions.target })\n .from(suppressions)\n .where(\n and(\n eq(suppressions.projectId, key.projectId),\n eq(suppressions.channel, key.channel as any),\n ),\n );\n results.set(\n cacheKey,\n new Set(rows.map((r: { target: string }) => normaliseTarget(r.target))),\n );\n } catch (err) {\n // A suppression lookup that fails must not silently become \"nothing\n // is suppressed\" — that would resume mailing people who opted out.\n this.logger.error(\n { err, projectId: key.projectId, channel: key.channel },\n \"suppression lookup failed — holding message\",\n );\n throw err;\n }\n }\n return keys.map((key) => results.get(`${key.projectId}:${key.channel}`) ?? new Set<string>());\n });\n\n async process(message: StreamMessage): Promise<void> {\n const { event } = message;\n\n const payloadResult = this.registry.safeParsePayload(\"notification.enriched\", event.payload);\n if (!payloadResult.success) {\n this.logger.warn(\n { messageId: message.id, issues: payloadResult.error.issues },\n \"invalid notification.enriched payload — skipping\",\n );\n return;\n }\n\n const enriched = payloadResult.data as NotificationEnrichedPayload;\n\n // Idempotency\n const idempotencyKey = `${enriched.rawEventId}:${enriched.recipientId}:${enriched.channel}`;\n let customTtl: number | undefined;\n if (enriched.scheduledAt) {\n const msUntil = new Date(enriched.scheduledAt).getTime() - Date.now();\n if (msUntil > 0) {\n // base 24h (86400) + schedule time\n customTtl = 86400 + Math.ceil(msUntil / 1000);\n }\n }\n\n if (!(await this.idempotency.checkAndMark(idempotencyKey, 60))) {\n this.logger.debug({ messageId: message.id, eventId: event.id }, \"duplicate — skipping\");\n return;\n }\n\n try {\n // Opt-in check\n if (enriched.recipient.preferences.optedOut) {\n this.logger.info(\n { messageId: message.id, recipientId: enriched.recipientId, eventType: event.type },\n \"user opted out — dropping\",\n );\n this.globalEmitter.emit(\"notification:skipped\", {\n projectId: enriched.projectId,\n eventId: event.id,\n recipientId: enriched.recipientId,\n reason: \"user_opted_out\",\n });\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n\n if (enriched.recipient.preferences.channels?.includes(enriched.channel)) {\n this.logger.info(\n { messageId: message.id, recipientId: enriched.recipientId, channel: enriched.channel },\n \"user disabled notification channel — dropping\",\n );\n this.globalEmitter.emit(\"notification:skipped\", {\n projectId: enriched.projectId,\n eventId: event.id,\n recipientId: enriched.recipientId,\n reason: \"channel_disabled\",\n });\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n\n // Quiet hours check\n const qh = enriched.recipient.preferences.quietHours;\n if (qh && qh.length > 0 && enriched.priority !== \"critical\") {\n const qhResult = isInQuietHours(enriched.recipient.timezone, qh);\n if (qhResult.inQuietHours && qhResult.nextActiveTime) {\n this.logger.info(\n {\n messageId: message.id,\n recipientId: enriched.recipientId,\n nextActiveTime: qhResult.nextActiveTime.toISOString(),\n },\n \"user is in quiet hours — deferring notification\",\n );\n enriched.scheduledAt = qhResult.nextActiveTime.toISOString();\n }\n }\n\n // Rate limit. Per-project overrides win over the process-wide default;\n // a lookup failure must not drop the notification, so fall back rather\n // than propagate.\n let projectThrottle: { throttleLimit: number | null; throttleWindowHours: number | null } = {\n throttleLimit: null,\n throttleWindowHours: null,\n };\n try {\n projectThrottle = await this.projectSettings.get(enriched.projectId);\n } catch (err) {\n this.logger.warn(\n { err, projectId: enriched.projectId },\n \"could not read project throttle settings — falling back to the global default\",\n );\n }\n\n const throttleResult = await this.throttle.check(\n enriched.projectId,\n enriched.recipientId,\n enriched.priority,\n {\n limit: projectThrottle.throttleLimit,\n windowHours: projectThrottle.throttleWindowHours,\n scheduledAt: enriched.scheduledAt,\n },\n );\n if (!throttleResult.allowed) {\n this.logger.info(\n {\n messageId: message.id,\n recipientId: enriched.recipientId,\n count: throttleResult.count,\n limit: throttleResult.limit,\n priority: enriched.priority,\n },\n \"user throttled — dropping\",\n );\n this.globalEmitter.emit(\n \"notification:throttled\",\n enriched.recipientId,\n throttleResult.count,\n );\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n\n // Gather AI Prompts\n // Gather AI Prompts\n let dbTemplate = null;\n if (enriched.templateId) {\n dbTemplate = await this.templateCache.getCachedTemplate(\n enriched.projectId,\n enriched.templateId,\n );\n if (!dbTemplate) {\n this.logger.warn(\n { messageId: message.id, templateId: enriched.templateId },\n \"template not found — dropping\",\n );\n this.globalEmitter.emit(\"notification:skipped\", {\n projectId: enriched.projectId,\n eventId: event.id,\n recipientId: enriched.recipientId,\n reason: \"template_not_found\",\n });\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n }\n const aiPrompts = {\n ...(dbTemplate?.aiPrompts ?? {}),\n ...(enriched.aiPrompts ?? {}),\n };\n\n // Drives whether this message gets an unsubscribe header, and what the\n // resulting opt-out applies to.\n const templateTopics: string[] = dbTemplate?.topics ?? [];\n\n if (Object.keys(aiPrompts).length > 0) {\n const aiPendingPayload = {\n projectId: enriched.projectId,\n enrichedEventId: event.id,\n recipientId: enriched.recipientId,\n channel: enriched.channel,\n priority: enriched.priority,\n templateId: enriched.templateId,\n templateVariables: enriched.templateVariables,\n recipient: enriched.recipient,\n aiPrompts,\n scheduledAt: enriched.scheduledAt,\n fallbackChain: enriched.fallbackChain,\n };\n\n const aiPendingEnvelope = buildStreamEvent(\n \"notification.ai_pending\",\n aiPendingPayload as Record<string, unknown>,\n \"engine\",\n event.metadata.traceId,\n );\n\n await this.aiPendingProducer.publish(aiPendingEnvelope);\n this.logger.info(\n {\n messageId: message.id,\n eventId: event.id,\n recipientId: enriched.recipientId,\n traceId: event.metadata.traceId,\n },\n \"task routed to AI worker\",\n );\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n\n // Render template\n const rendered = renderWithTemplate(dbTemplate, enriched.templateVariables);\n\n const allContacts = await this.contactsLoader.load({\n projectId: enriched.projectId,\n recipientId: enriched.recipientId,\n });\n const activeContacts = allContacts.filter(\n (c: any) => c.channel === enriched.channel && c.active,\n );\n\n if (activeContacts.length === 0) {\n this.logger.info(\n { messageId: message.id, recipientId: enriched.recipientId, channel: enriched.channel },\n \"no active contacts for channel — dropping\",\n );\n this.globalEmitter.emit(\"notification:skipped\", {\n projectId: enriched.projectId,\n eventId: event.id,\n recipientId: enriched.recipientId,\n reason: \"no_active_contacts\",\n });\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n\n const suppressedTargets = await this.suppressionsLoader.load({\n projectId: enriched.projectId,\n channel: enriched.channel,\n });\n\n for (const contact of activeContacts) {\n if (contact.preferences?.optedOut) {\n continue;\n }\n\n // A suppression outranks every other gate, `critical` included: it\n // records that the person asked us to stop, or that the address is\n // dead. Sending anyway is what gets a domain blocked.\n if (contact.target && suppressedTargets.has(normaliseTarget(contact.target))) {\n this.logger.info(\n {\n messageId: message.id,\n recipientId: enriched.recipientId,\n channel: enriched.channel,\n },\n \"destination suppressed — dropping\",\n );\n this.globalEmitter.emit(\"notification:skipped\", {\n projectId: enriched.projectId,\n eventId: event.id,\n recipientId: enriched.recipientId,\n reason: \"suppressed\",\n });\n continue;\n }\n\n const taskId = `${enriched.rawEventId}:${contact.id || randomUUID()}`;\n const resolvedDestination = contact.target;\n\n // One-click unsubscribe headers, but only on mail that should carry\n // them. A template with no topic is transactional by this codebase's\n // own convention — a password reset, a receipt — and putting an\n // unsubscribe button on those invites people to switch off mail they\n // actually need, with no topic to scope the opt-out to anyway.\n if (\n enriched.channel === \"email\" &&\n templateTopics.length > 0 &&\n !config.UNSUBSCRIBE_SECRET\n ) {\n this.logger.warn(\n { projectId: enriched.projectId, recipientId: enriched.recipientId },\n \"UNSUBSCRIBE_SECRET is not configured — email sent without RFC 8058 one-click unsubscribe headers\",\n );\n }\n\n const unsubscribeHeaders =\n enriched.channel === \"email\" &&\n templateTopics.length > 0 &&\n config.UNSUBSCRIBE_SECRET &&\n config.PUBLIC_URL &&\n resolvedDestination\n ? buildUnsubscribeHeaders({\n claim: {\n projectId: enriched.projectId,\n userId: enriched.recipientId,\n channel: enriched.channel,\n target: resolvedDestination,\n topics: templateTopics,\n },\n secret: config.UNSUBSCRIBE_SECRET,\n publicUrl: config.PUBLIC_URL,\n })\n : undefined;\n\n const taskPayload: NotificationDispatchedPayload = {\n projectId: enriched.projectId,\n taskId,\n enrichedEventId: event.id,\n recipientId: enriched.recipientId,\n channel: enriched.channel,\n priority: enriched.priority,\n templateId: enriched.templateId,\n templateVariables: enriched.templateVariables,\n aiPrompts: enriched.aiPrompts,\n recipient: enriched.recipient,\n renderedContent: rendered,\n destination: resolvedDestination,\n deliveryOptions: {\n maxAttempts: 3,\n timeoutMs: 10_000,\n ...(unsubscribeHeaders ? { headers: unsubscribeHeaders } : {}),\n },\n fallbackChain: enriched.fallbackChain,\n campaignId: enriched.campaignId,\n };\n\n const envelope = buildStreamEvent(\n \"notification.dispatched\",\n taskPayload as Record<string, unknown>,\n \"engine\",\n event.metadata.traceId,\n );\n\n // Route by scheduledAt\n const now = Date.now();\n const scheduledAt = enriched.scheduledAt ? new Date(enriched.scheduledAt).getTime() : now;\n\n if (scheduledAt > now) {\n await this.db\n .insert(scheduledPayloads)\n .values({\n taskId,\n payload: {\n ...taskPayload,\n scheduledAt: enriched.scheduledAt,\n },\n })\n .onConflictDoNothing();\n\n const scheduledEnvelope = buildStreamEvent(\n \"notification.scheduled\",\n {\n projectId: enriched.projectId,\n enrichedEventId: event.id,\n taskId,\n scheduledAt: enriched.scheduledAt!,\n },\n \"engine\",\n event.metadata.traceId,\n );\n\n await this.scheduledProducer.publish(scheduledEnvelope);\n this.logger.info(\n {\n messageId: message.id,\n taskId,\n scheduledAt: enriched.scheduledAt,\n traceId: event.metadata.traceId,\n },\n \"task scheduled and payload cached\",\n );\n } else {\n const p = getPriorityBucket(enriched.priority);\n const outboundProducer = this.outboundProducers[p] ?? this.outboundProducers[\"normal\"]!;\n\n await outboundProducer.publish(envelope);\n this.logger.info(\n {\n messageId: message.id,\n taskId,\n recipientId: enriched.recipientId,\n traceId: event.metadata.traceId,\n },\n \"task dispatched\",\n );\n }\n }\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n } catch (err) {\n await this.idempotency.unmark(idempotencyKey).catch(() => {});\n throw err;\n }\n }\n}\n\nlet subscriber: any = null;\n\nexport async function startEngineWorker() {\n logger = createLogger({ name: \"engine\", level: config.LOG_LEVEL });\n redis = new RedisClient({ url: config.REDIS_URL, name: \"engine\", logger });\n const dbData = createDatabase({ url: config.DATABASE_URL, applicationName: \"engine\", logger });\n sql = dbData.sql;\n db = dbData.db;\n templateRepo = new TemplateRepository(db);\n templateCache = new TemplateCache(templateRepo);\n const contactRepo = new ContactRepository(db);\n consumer = new StreamConsumer({\n redis: redis.native,\n stream: ENRICHED_STREAMS as unknown as StreamName[],\n group: CONSUMER_GROUPS.ENGINE,\n consumer: `engine-${process.pid}`,\n dlqStream: STREAMS.DEAD_LETTER,\n batchSize: config.WORKER_CONCURRENCY,\n logger,\n });\n\n pendingScanner = new PendingMessageScanner({\n redis: redis.native,\n stream: ENRICHED_STREAMS as unknown as StreamName[],\n group: CONSUMER_GROUPS.ENGINE,\n consumer: `engine-${process.pid}`,\n logger,\n });\n\n const outboundProducers = {\n critical: new StreamProducer({\n redis: redis.native,\n stream: STREAMS.OUTBOUND_CRITICAL,\n logger,\n }),\n normal: new StreamProducer({ redis: redis.native, stream: STREAMS.OUTBOUND_NORMAL, logger }),\n low: new StreamProducer({ redis: redis.native, stream: STREAMS.OUTBOUND_LOW, logger }),\n };\n\n const scheduledProducer = new StreamProducer({\n redis: redis.native,\n stream: STREAMS.SCHEDULED,\n logger,\n });\n\n const aiPendingProducer = new StreamProducer({\n redis: redis.native,\n stream: STREAMS.AI_PENDING,\n logger,\n });\n\n const idempotency = new IdempotencyGuard({\n redis: redis.native,\n keyPrefix: \"notif:processed:engine\",\n ttlSeconds: 86_400,\n });\n\n const throttle = new UserThrottle({\n redis: redis.native,\n maxPerHour: parseInt(process.env.RATE_LIMIT_PER_HOUR || \"100\", 10),\n });\n\n // Per-project throttle overrides, cached because this is read once per\n // notification. Stale entries expire on their own; the pub/sub subscriber\n // below only makes an admin's change take effect sooner.\n const projectRepo = new ProjectRepository(db);\n const projectSettings = new ProjectSettingsCache((projectId) =>\n projectRepo.findThrottleSettings(projectId),\n );\n\n // ─── Stage 2: Decision Engine ───────────────────────────────────────────────\n //\n // Pipeline:\n // 1. Parse payload as notification.enriched\n // 2. Idempotency check\n // 3. Check user opt-in (from enriched recipient.preferences)\n // 4. Apply per-user hourly rate limit\n // 5. Render template using user locale\n // 6. Route: if scheduledAt is future → SCHEDULED; else → OUTBOUND\n\n worker = new EngineWorker({\n consumer,\n pendingScanner,\n logger,\n concurrency: config.WORKER_CONCURRENCY,\n registry,\n idempotency,\n throttle,\n projectSettings,\n redis: redis.native,\n templateCache,\n aiPendingProducer,\n scheduledProducer,\n outboundProducers,\n globalEmitter,\n contactRepo,\n db,\n });\n\n subscriber = redis.native.duplicate();\n await subscriber.subscribe(\n PUBSUB_CHANNELS.TEMPLATE_INVALIDATED,\n PUBSUB_CHANNELS.PROJECT_INVALIDATED,\n );\n subscriber.on(\"message\", (channel: string, message: string) => {\n if (channel === PUBSUB_CHANNELS.TEMPLATE_INVALIDATED) {\n templateCache.invalidateKey(message);\n logger.info({ cacheKey: message }, \"invalidated template cache\");\n } else if (channel === PUBSUB_CHANNELS.PROJECT_INVALIDATED) {\n projectSettings.invalidate(message);\n logger.info({ projectId: message }, \"invalidated project settings cache\");\n }\n });\n\n // ─── Health check interval ──────────────────────────────────────────────────\n\n healthInterval = startHealthReporter(\"engine\", worker, redis, logger);\n\n logger.info({ env: config.NODE_ENV }, \"engine starting\");\n await worker.start();\n}\n\n// ─── Shutdown ──────────────────────────────────────────────────────────────\n\nexport async function stopEngineWorker(): Promise<void> {\n logger?.info(\"shutdown initiated\");\n if (healthInterval) {\n clearInterval(healthInterval);\n healthInterval = null;\n }\n if (subscriber) {\n subscriber.disconnect();\n subscriber = null;\n }\n if (worker) await worker.stop();\n if (sql) await sql.end();\n if (redis) await redis.disconnect();\n logger?.info(\"engine stopped\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"main-B561M1d3.mjs","names":["sql"],"sources":["../src/services/engine/main.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { loadEnv, readBaseConfig } from \"@/index.js\";\nimport { createLogger } from \"@/index.js\";\nimport { RedisClient, type Redis } from \"@/index.js\";\nimport {\n StreamConsumer,\n PendingMessageScanner,\n StreamProducer,\n type StreamMessage,\n} from \"@/index.js\";\nimport { BaseWorker } from \"@/index.js\";\nimport {\n STREAMS,\n ENRICHED_STREAMS,\n CONSUMER_GROUPS,\n PUBSUB_CHANNELS,\n registry,\n buildStreamEvent,\n type NotificationEnrichedPayload,\n type NotificationDispatchedPayload,\n} from \"@/index.js\";\nimport { type StreamName } from \"@/contracts/streams.js\";\nimport { IdempotencyGuard } from \"@/index.js\";\nimport { UserThrottle, ProjectSettingsCache } from \"@/index.js\";\nimport { TemplateRepository, ContactRepository, ProjectRepository } from \"@/index.js\";\nimport { createDatabase } from \"@/db/index.js\";\nimport { scheduledPayloads, suppressions } from \"@/db/schema.js\";\nimport { and, eq } from \"drizzle-orm\";\nimport {\n getPriorityBucket,\n globalEmitter,\n normaliseTarget,\n type WorkerOptions,\n DataLoader,\n} from \"@/shared/index.js\";\nimport { renderWithTemplate, TemplateCache } from \"@/templates/index.js\";\nimport { buildUnsubscribeHeaders } from \"@/unsubscribe/index.js\";\nimport { startHealthReporter } from \"@/workers/index.js\";\n\nfunction localTimeToUtc(\n year: number,\n month: number,\n day: number,\n hour: number,\n minute: number,\n second: number,\n timezone: string,\n): Date {\n let utcMs = Date.UTC(year, month - 1, day, hour, minute, second);\n const formatter = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n });\n\n for (let iter = 0; iter < 3; iter++) {\n const parts = formatter.formatToParts(new Date(utcMs));\n const pYear = parseInt(parts.find((p) => p.type === \"year\")?.value ?? \"0\", 10);\n const pMonth = parseInt(parts.find((p) => p.type === \"month\")?.value ?? \"0\", 10);\n const pDay = parseInt(parts.find((p) => p.type === \"day\")?.value ?? \"0\", 10);\n const pHour = parseInt(parts.find((p) => p.type === \"hour\")?.value ?? \"0\", 10);\n const pMin = parseInt(parts.find((p) => p.type === \"minute\")?.value ?? \"0\", 10);\n const pSec = parseInt(parts.find((p) => p.type === \"second\")?.value ?? \"0\", 10);\n\n const targetMs = Date.UTC(year, month - 1, day, hour, minute, second);\n const actualMs = Date.UTC(pYear, pMonth - 1, pDay, pHour, pMin, pSec);\n const diff = targetMs - actualMs;\n if (diff === 0) break;\n utcMs += diff;\n }\n return new Date(utcMs);\n}\n\nexport function isInQuietHours(\n timezone: string,\n quietHours: { start: string; end: string }[],\n fromDate: Date = new Date(),\n): { inQuietHours: boolean; nextActiveTime?: Date } {\n if (!quietHours || quietHours.length === 0) {\n return { inQuietHours: false };\n }\n\n const checkWindowAt = (date: Date) => {\n let parts: Intl.DateTimeFormatPart[];\n try {\n const formatter = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n });\n parts = formatter.formatToParts(date);\n } catch {\n return null;\n }\n\n const year = parseInt(parts.find((p) => p.type === \"year\")?.value ?? \"0\", 10);\n const month = parseInt(parts.find((p) => p.type === \"month\")?.value ?? \"0\", 10);\n const day = parseInt(parts.find((p) => p.type === \"day\")?.value ?? \"0\", 10);\n const currentHour = parseInt(parts.find((p) => p.type === \"hour\")?.value ?? \"0\", 10);\n const currentMin = parseInt(parts.find((p) => p.type === \"minute\")?.value ?? \"0\", 10);\n\n const currentMinutes = currentHour * 60 + currentMin;\n\n for (const window of quietHours) {\n const [startH, startM] = window.start.split(\":\").map(Number);\n const [endH, endM] = window.end.split(\":\").map(Number);\n\n if (startH === undefined || startM === undefined || endH === undefined || endM === undefined)\n continue;\n\n const startMinutes = startH * 60 + startM;\n const endMinutes = endH * 60 + endM;\n\n let inWindow = false;\n if (startMinutes <= endMinutes) {\n inWindow = currentMinutes >= startMinutes && currentMinutes < endMinutes;\n } else {\n inWindow = currentMinutes >= startMinutes || currentMinutes < endMinutes;\n }\n\n if (inWindow) {\n let targetYear = year;\n let targetMonth = month;\n let targetDay = day;\n\n if (currentMinutes >= endMinutes) {\n const nextDay = new Date(Date.UTC(year, month - 1, day + 1));\n targetYear = nextDay.getUTCFullYear();\n targetMonth = nextDay.getUTCMonth() + 1;\n targetDay = nextDay.getUTCDate();\n }\n\n const nextActive = localTimeToUtc(\n targetYear,\n targetMonth,\n targetDay,\n endH,\n endM,\n 0,\n timezone,\n );\n return { inQuietHours: true, nextActiveTime: nextActive };\n }\n }\n return { inQuietHours: false };\n };\n\n let initialCheck = checkWindowAt(fromDate);\n if (!initialCheck || !initialCheck.inQuietHours) {\n return { inQuietHours: false };\n }\n\n // Chained / overlapping quiet hours intervals\n let candidateTime = initialCheck.nextActiveTime!;\n for (let i = 0; i < 10; i++) {\n const subsequentCheck = checkWindowAt(candidateTime);\n if (subsequentCheck && subsequentCheck.inQuietHours && subsequentCheck.nextActiveTime) {\n if (subsequentCheck.nextActiveTime.getTime() <= candidateTime.getTime()) {\n break;\n }\n candidateTime = subsequentCheck.nextActiveTime;\n } else {\n break;\n }\n }\n\n return { inQuietHours: true, nextActiveTime: candidateTime };\n}\n\n// ─── Bootstrap ─────────────────────────────────────────────────────────────\n\nloadEnv();\nconst config = readBaseConfig();\n\nlet logger: ReturnType<typeof createLogger>;\nlet redis: RedisClient;\nlet sql: any;\nlet db: any;\nlet templateRepo: TemplateRepository;\n\nlet templateCache: TemplateCache;\n\nlet consumer: StreamConsumer;\nlet pendingScanner: PendingMessageScanner;\nlet worker: BaseWorker;\nlet healthInterval: NodeJS.Timeout | null = null;\n\nexport interface EngineWorkerOptions extends WorkerOptions {\n registry: any;\n idempotency: any;\n throttle: any;\n projectSettings: ProjectSettingsCache;\n redis: Redis;\n templateCache: TemplateCache;\n aiPendingProducer: any;\n scheduledProducer: any;\n outboundProducers: any;\n globalEmitter: any;\n contactRepo: any;\n db: any;\n}\n\nexport class EngineWorker extends BaseWorker {\n private readonly registry: any;\n private readonly idempotency: any;\n private readonly throttle: any;\n private readonly projectSettings: ProjectSettingsCache;\n private readonly redisCli: Redis;\n private readonly templateCache: TemplateCache;\n private readonly aiPendingProducer: any;\n private readonly scheduledProducer: any;\n private readonly outboundProducers: any;\n private readonly globalEmitter: any;\n private readonly contactRepo: any;\n private readonly db: any;\n\n constructor(options: EngineWorkerOptions) {\n super(options);\n this.registry = options.registry;\n this.idempotency = options.idempotency;\n this.throttle = options.throttle;\n this.projectSettings = options.projectSettings;\n this.redisCli = options.redis;\n this.templateCache = options.templateCache;\n this.aiPendingProducer = options.aiPendingProducer;\n this.scheduledProducer = options.scheduledProducer;\n this.outboundProducers = options.outboundProducers;\n this.globalEmitter = options.globalEmitter;\n this.contactRepo = options.contactRepo;\n this.db = options.db;\n }\n\n private readonly contactsLoader = new DataLoader<\n { projectId: string; recipientId: string },\n any[]\n >(async (keys) => {\n const byProject = new Map<string, string[]>();\n for (const key of keys) {\n if (!byProject.has(key.projectId)) byProject.set(key.projectId, []);\n byProject.get(key.projectId)!.push(key.recipientId);\n }\n\n const resultsByProjectAndUser = new Map<string, Map<string, any[]>>();\n for (const [projectId, userIds] of byProject) {\n const activeContactsMap = await this.contactRepo.findActiveByUserIds(projectId, userIds);\n resultsByProjectAndUser.set(projectId, activeContactsMap);\n }\n\n return keys.map((key) => {\n const projectMap = resultsByProjectAndUser.get(key.projectId);\n if (!projectMap) return [];\n return projectMap.get(key.recipientId) || [];\n });\n });\n\n /**\n * Suppressed destinations for one (project, channel), as a normalised set.\n *\n * Loaded per project+channel rather than per address: a campaign is thousands\n * of messages against one channel, so this collapses to a single query for\n * the whole batch. Cached for the loader's lifetime of a tick, which means a\n * suppression written mid-batch takes effect on the next batch — acceptable,\n * since the webhook that writes it is itself minutes behind the send.\n */\n private readonly suppressionsLoader = new DataLoader<\n { projectId: string; channel: string },\n Set<string>\n >(async (keys) => {\n const results = new Map<string, Set<string>>();\n for (const key of keys) {\n const cacheKey = `${key.projectId}:${key.channel}`;\n if (results.has(cacheKey)) continue;\n try {\n const rows = await this.db\n .select({ target: suppressions.target })\n .from(suppressions)\n .where(\n and(\n eq(suppressions.projectId, key.projectId),\n eq(suppressions.channel, key.channel as any),\n ),\n );\n results.set(\n cacheKey,\n new Set(rows.map((r: { target: string }) => normaliseTarget(r.target))),\n );\n } catch (err) {\n // A suppression lookup that fails must not silently become \"nothing\n // is suppressed\" — that would resume mailing people who opted out.\n this.logger.error(\n { err, projectId: key.projectId, channel: key.channel },\n \"suppression lookup failed — holding message\",\n );\n throw err;\n }\n }\n return keys.map((key) => results.get(`${key.projectId}:${key.channel}`) ?? new Set<string>());\n });\n\n async process(message: StreamMessage): Promise<void> {\n const { event } = message;\n\n const payloadResult = this.registry.safeParsePayload(\"notification.enriched\", event.payload);\n if (!payloadResult.success) {\n this.logger.warn(\n { messageId: message.id, issues: payloadResult.error.issues },\n \"invalid notification.enriched payload — skipping\",\n );\n return;\n }\n\n const enriched = payloadResult.data as NotificationEnrichedPayload;\n\n // Idempotency\n const idempotencyKey = `${enriched.rawEventId}:${enriched.recipientId}:${enriched.channel}`;\n let customTtl: number | undefined;\n if (enriched.scheduledAt) {\n const msUntil = new Date(enriched.scheduledAt).getTime() - Date.now();\n if (msUntil > 0) {\n // base 24h (86400) + schedule time\n customTtl = 86400 + Math.ceil(msUntil / 1000);\n }\n }\n\n if (!(await this.idempotency.checkAndMark(idempotencyKey, 60))) {\n this.logger.debug({ messageId: message.id, eventId: event.id }, \"duplicate — skipping\");\n return;\n }\n\n try {\n // Opt-in check\n if (enriched.recipient.preferences.optedOut) {\n this.logger.info(\n { messageId: message.id, recipientId: enriched.recipientId, eventType: event.type },\n \"user opted out — dropping\",\n );\n this.globalEmitter.emit(\"notification:skipped\", {\n projectId: enriched.projectId,\n eventId: event.id,\n recipientId: enriched.recipientId,\n reason: \"user_opted_out\",\n });\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n\n if (enriched.recipient.preferences.channels?.includes(enriched.channel)) {\n this.logger.info(\n { messageId: message.id, recipientId: enriched.recipientId, channel: enriched.channel },\n \"user disabled notification channel — dropping\",\n );\n this.globalEmitter.emit(\"notification:skipped\", {\n projectId: enriched.projectId,\n eventId: event.id,\n recipientId: enriched.recipientId,\n reason: \"channel_disabled\",\n });\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n\n // Quiet hours check\n const qh = enriched.recipient.preferences.quietHours;\n if (qh && qh.length > 0 && enriched.priority !== \"critical\") {\n const qhResult = isInQuietHours(enriched.recipient.timezone, qh);\n if (qhResult.inQuietHours && qhResult.nextActiveTime) {\n this.logger.info(\n {\n messageId: message.id,\n recipientId: enriched.recipientId,\n nextActiveTime: qhResult.nextActiveTime.toISOString(),\n },\n \"user is in quiet hours — deferring notification\",\n );\n enriched.scheduledAt = qhResult.nextActiveTime.toISOString();\n }\n }\n\n // Rate limit. Per-project overrides win over the process-wide default;\n // a lookup failure must not drop the notification, so fall back rather\n // than propagate.\n let projectThrottle: { throttleLimit: number | null; throttleWindowHours: number | null } = {\n throttleLimit: null,\n throttleWindowHours: null,\n };\n try {\n projectThrottle = await this.projectSettings.get(enriched.projectId);\n } catch (err) {\n this.logger.warn(\n { err, projectId: enriched.projectId },\n \"could not read project throttle settings — falling back to the global default\",\n );\n }\n\n const throttleResult = await this.throttle.check(\n enriched.projectId,\n enriched.recipientId,\n enriched.priority,\n {\n limit: projectThrottle.throttleLimit,\n windowHours: projectThrottle.throttleWindowHours,\n scheduledAt: enriched.scheduledAt,\n },\n );\n if (!throttleResult.allowed) {\n this.logger.info(\n {\n messageId: message.id,\n recipientId: enriched.recipientId,\n count: throttleResult.count,\n limit: throttleResult.limit,\n priority: enriched.priority,\n },\n \"user throttled — dropping\",\n );\n this.globalEmitter.emit(\n \"notification:throttled\",\n enriched.recipientId,\n throttleResult.count,\n );\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n\n // Gather AI Prompts\n // Gather AI Prompts\n let dbTemplate = null;\n if (enriched.templateId) {\n dbTemplate = await this.templateCache.getCachedTemplate(\n enriched.projectId,\n enriched.templateId,\n );\n if (!dbTemplate) {\n this.logger.warn(\n { messageId: message.id, templateId: enriched.templateId },\n \"template not found — dropping\",\n );\n this.globalEmitter.emit(\"notification:skipped\", {\n projectId: enriched.projectId,\n eventId: event.id,\n recipientId: enriched.recipientId,\n reason: \"template_not_found\",\n });\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n }\n const aiPrompts = {\n ...(dbTemplate?.aiPrompts ?? {}),\n ...(enriched.aiPrompts ?? {}),\n };\n\n // Drives whether this message gets an unsubscribe header, and what the\n // resulting opt-out applies to.\n const templateTopics: string[] = dbTemplate?.topics ?? [];\n\n if (Object.keys(aiPrompts).length > 0) {\n const aiPendingPayload = {\n projectId: enriched.projectId,\n enrichedEventId: event.id,\n recipientId: enriched.recipientId,\n channel: enriched.channel,\n priority: enriched.priority,\n templateId: enriched.templateId,\n templateVariables: enriched.templateVariables,\n recipient: enriched.recipient,\n aiPrompts,\n scheduledAt: enriched.scheduledAt,\n fallbackChain: enriched.fallbackChain,\n };\n\n const aiPendingEnvelope = buildStreamEvent(\n \"notification.ai_pending\",\n aiPendingPayload as Record<string, unknown>,\n \"engine\",\n event.metadata.traceId,\n );\n\n await this.aiPendingProducer.publish(aiPendingEnvelope);\n this.logger.info(\n {\n messageId: message.id,\n eventId: event.id,\n recipientId: enriched.recipientId,\n traceId: event.metadata.traceId,\n },\n \"task routed to AI worker\",\n );\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n\n // Render template\n const rendered = renderWithTemplate(dbTemplate, enriched.templateVariables);\n\n const allContacts = await this.contactsLoader.load({\n projectId: enriched.projectId,\n recipientId: enriched.recipientId,\n });\n const activeContacts = allContacts.filter(\n (c: any) => c.channel === enriched.channel && c.active,\n );\n\n if (activeContacts.length === 0) {\n this.logger.info(\n { messageId: message.id, recipientId: enriched.recipientId, channel: enriched.channel },\n \"no active contacts for channel — dropping\",\n );\n this.globalEmitter.emit(\"notification:skipped\", {\n projectId: enriched.projectId,\n eventId: event.id,\n recipientId: enriched.recipientId,\n reason: \"no_active_contacts\",\n });\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n return;\n }\n\n const suppressedTargets = await this.suppressionsLoader.load({\n projectId: enriched.projectId,\n channel: enriched.channel,\n });\n\n for (const contact of activeContacts) {\n if (contact.preferences?.optedOut) {\n continue;\n }\n\n // A suppression outranks every other gate, `critical` included: it\n // records that the person asked us to stop, or that the address is\n // dead. Sending anyway is what gets a domain blocked.\n if (contact.target && suppressedTargets.has(normaliseTarget(contact.target))) {\n this.logger.info(\n {\n messageId: message.id,\n recipientId: enriched.recipientId,\n channel: enriched.channel,\n },\n \"destination suppressed — dropping\",\n );\n this.globalEmitter.emit(\"notification:skipped\", {\n projectId: enriched.projectId,\n eventId: event.id,\n recipientId: enriched.recipientId,\n reason: \"suppressed\",\n });\n continue;\n }\n\n const taskId = `${enriched.rawEventId}:${contact.id || randomUUID()}`;\n const resolvedDestination = contact.target;\n\n // One-click unsubscribe headers, but only on mail that should carry\n // them. A template with no topic is transactional by this codebase's\n // own convention — a password reset, a receipt — and putting an\n // unsubscribe button on those invites people to switch off mail they\n // actually need, with no topic to scope the opt-out to anyway.\n if (\n enriched.channel === \"email\" &&\n templateTopics.length > 0 &&\n !config.UNSUBSCRIBE_SECRET\n ) {\n this.logger.warn(\n { projectId: enriched.projectId, recipientId: enriched.recipientId },\n \"UNSUBSCRIBE_SECRET is not configured — email sent without RFC 8058 one-click unsubscribe headers\",\n );\n }\n\n const unsubscribeHeaders =\n enriched.channel === \"email\" &&\n templateTopics.length > 0 &&\n config.UNSUBSCRIBE_SECRET &&\n config.PUBLIC_URL &&\n resolvedDestination\n ? buildUnsubscribeHeaders({\n claim: {\n projectId: enriched.projectId,\n userId: enriched.recipientId,\n channel: enriched.channel,\n target: resolvedDestination,\n topics: templateTopics,\n },\n secret: config.UNSUBSCRIBE_SECRET,\n publicUrl: config.PUBLIC_URL,\n })\n : undefined;\n\n const taskPayload: NotificationDispatchedPayload = {\n projectId: enriched.projectId,\n taskId,\n enrichedEventId: event.id,\n recipientId: enriched.recipientId,\n channel: enriched.channel,\n priority: enriched.priority,\n templateId: enriched.templateId,\n templateVariables: enriched.templateVariables,\n aiPrompts: enriched.aiPrompts,\n recipient: enriched.recipient,\n renderedContent: rendered,\n destination: resolvedDestination,\n deliveryOptions: {\n maxAttempts: 3,\n timeoutMs: 10_000,\n ...(unsubscribeHeaders ? { headers: unsubscribeHeaders } : {}),\n },\n fallbackChain: enriched.fallbackChain,\n campaignId: enriched.campaignId,\n };\n\n const envelope = buildStreamEvent(\n \"notification.dispatched\",\n taskPayload as Record<string, unknown>,\n \"engine\",\n event.metadata.traceId,\n );\n\n // Route by scheduledAt\n const now = Date.now();\n const scheduledAt = enriched.scheduledAt ? new Date(enriched.scheduledAt).getTime() : now;\n\n if (scheduledAt > now) {\n await this.db\n .insert(scheduledPayloads)\n .values({\n taskId,\n payload: {\n ...taskPayload,\n scheduledAt: enriched.scheduledAt,\n },\n })\n .onConflictDoNothing();\n\n const scheduledEnvelope = buildStreamEvent(\n \"notification.scheduled\",\n {\n projectId: enriched.projectId,\n enrichedEventId: event.id,\n taskId,\n scheduledAt: enriched.scheduledAt!,\n },\n \"engine\",\n event.metadata.traceId,\n );\n\n await this.scheduledProducer.publish(scheduledEnvelope);\n this.logger.info(\n {\n messageId: message.id,\n taskId,\n scheduledAt: enriched.scheduledAt,\n traceId: event.metadata.traceId,\n },\n \"task scheduled and payload cached\",\n );\n } else {\n const p = getPriorityBucket(enriched.priority);\n const outboundProducer = this.outboundProducers[p] ?? this.outboundProducers[\"normal\"]!;\n\n await outboundProducer.publish(envelope);\n this.logger.info(\n {\n messageId: message.id,\n taskId,\n recipientId: enriched.recipientId,\n traceId: event.metadata.traceId,\n },\n \"task dispatched\",\n );\n }\n }\n await this.idempotency.markProcessed(idempotencyKey, customTtl);\n } catch (err) {\n await this.idempotency.unmark(idempotencyKey).catch(() => {});\n throw err;\n }\n }\n}\n\nlet subscriber: any = null;\n\nexport async function startEngineWorker() {\n logger = createLogger({ name: \"engine\", level: config.LOG_LEVEL });\n redis = new RedisClient({ url: config.REDIS_URL, name: \"engine\", logger });\n const dbData = createDatabase({ url: config.DATABASE_URL, applicationName: \"engine\", logger });\n sql = dbData.sql;\n db = dbData.db;\n templateRepo = new TemplateRepository(db);\n templateCache = new TemplateCache(templateRepo);\n const contactRepo = new ContactRepository(db);\n consumer = new StreamConsumer({\n redis: redis.native,\n stream: ENRICHED_STREAMS as unknown as StreamName[],\n group: CONSUMER_GROUPS.ENGINE,\n consumer: `engine-${process.pid}`,\n dlqStream: STREAMS.DEAD_LETTER,\n batchSize: config.WORKER_CONCURRENCY,\n logger,\n });\n\n pendingScanner = new PendingMessageScanner({\n redis: redis.native,\n stream: ENRICHED_STREAMS as unknown as StreamName[],\n group: CONSUMER_GROUPS.ENGINE,\n consumer: `engine-${process.pid}`,\n logger,\n });\n\n const outboundProducers = {\n critical: new StreamProducer({\n redis: redis.native,\n stream: STREAMS.OUTBOUND_CRITICAL,\n logger,\n }),\n normal: new StreamProducer({ redis: redis.native, stream: STREAMS.OUTBOUND_NORMAL, logger }),\n low: new StreamProducer({ redis: redis.native, stream: STREAMS.OUTBOUND_LOW, logger }),\n };\n\n const scheduledProducer = new StreamProducer({\n redis: redis.native,\n stream: STREAMS.SCHEDULED,\n logger,\n });\n\n const aiPendingProducer = new StreamProducer({\n redis: redis.native,\n stream: STREAMS.AI_PENDING,\n logger,\n });\n\n const idempotency = new IdempotencyGuard({\n redis: redis.native,\n keyPrefix: \"notif:processed:engine\",\n ttlSeconds: 86_400,\n });\n\n const throttle = new UserThrottle({\n redis: redis.native,\n maxPerHour: parseInt(process.env.RATE_LIMIT_PER_HOUR || \"100\", 10),\n });\n\n // Per-project throttle overrides, cached because this is read once per\n // notification. Stale entries expire on their own; the pub/sub subscriber\n // below only makes an admin's change take effect sooner.\n const projectRepo = new ProjectRepository(db);\n const projectSettings = new ProjectSettingsCache((projectId) =>\n projectRepo.findThrottleSettings(projectId),\n );\n\n // ─── Stage 2: Decision Engine ───────────────────────────────────────────────\n //\n // Pipeline:\n // 1. Parse payload as notification.enriched\n // 2. Idempotency check\n // 3. Check user opt-in (from enriched recipient.preferences)\n // 4. Apply per-user hourly rate limit\n // 5. Render template using user locale\n // 6. Route: if scheduledAt is future → SCHEDULED; else → OUTBOUND\n\n worker = new EngineWorker({\n consumer,\n pendingScanner,\n logger,\n concurrency: config.WORKER_CONCURRENCY,\n registry,\n idempotency,\n throttle,\n projectSettings,\n redis: redis.native,\n templateCache,\n aiPendingProducer,\n scheduledProducer,\n outboundProducers,\n globalEmitter,\n contactRepo,\n db,\n });\n\n subscriber = redis.native.duplicate();\n await subscriber.subscribe(\n PUBSUB_CHANNELS.TEMPLATE_INVALIDATED,\n PUBSUB_CHANNELS.PROJECT_INVALIDATED,\n );\n subscriber.on(\"message\", (channel: string, message: string) => {\n if (channel === PUBSUB_CHANNELS.TEMPLATE_INVALIDATED) {\n templateCache.invalidateKey(message);\n logger.info({ cacheKey: message }, \"invalidated template cache\");\n } else if (channel === PUBSUB_CHANNELS.PROJECT_INVALIDATED) {\n projectSettings.invalidate(message);\n logger.info({ projectId: message }, \"invalidated project settings cache\");\n }\n });\n\n // ─── Health check interval ──────────────────────────────────────────────────\n\n healthInterval = startHealthReporter(\"engine\", worker, redis, logger);\n\n logger.info({ env: config.NODE_ENV }, \"engine starting\");\n await worker.start();\n}\n\n// ─── Shutdown ──────────────────────────────────────────────────────────────\n\nexport async function stopEngineWorker(): Promise<void> {\n logger?.info(\"shutdown initiated\");\n if (healthInterval) {\n clearInterval(healthInterval);\n healthInterval = null;\n }\n if (subscriber) {\n subscriber.disconnect();\n subscriber = null;\n }\n if (worker) await worker.stop();\n if (sql) await sql.end();\n if (redis) await redis.disconnect();\n logger?.info(\"engine stopped\");\n}\n"],"mappings":";;;;AAuCA,SAAS,eACP,MACA,OACA,KACA,MACA,QACA,QACA,UACM;CACN,IAAI,QAAQ,KAAK,IAAI,MAAM,QAAQ,GAAG,KAAK,MAAM,QAAQ,MAAM;CAC/D,MAAM,YAAY,IAAI,KAAK,eAAe,SAAS;EACjD,UAAU;EACV,MAAM;EACN,OAAO;EACP,KAAK;EACL,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,QAAQ;CACV,CAAC;CAED,KAAK,IAAI,OAAO,GAAG,OAAO,GAAG,QAAQ;EACnC,MAAM,QAAQ,UAAU,cAAc,IAAI,KAAK,KAAK,CAAC;EACrD,MAAM,QAAQ,SAAS,MAAM,MAAM,MAAM,EAAE,SAAS,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE;EAC7E,MAAM,SAAS,SAAS,MAAM,MAAM,MAAM,EAAE,SAAS,OAAO,CAAC,EAAE,SAAS,KAAK,EAAE;EAC/E,MAAM,OAAO,SAAS,MAAM,MAAM,MAAM,EAAE,SAAS,KAAK,CAAC,EAAE,SAAS,KAAK,EAAE;EAC3E,MAAM,QAAQ,SAAS,MAAM,MAAM,MAAM,EAAE,SAAS,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE;EAC7E,MAAM,OAAO,SAAS,MAAM,MAAM,MAAM,EAAE,SAAS,QAAQ,CAAC,EAAE,SAAS,KAAK,EAAE;EAC9E,MAAM,OAAO,SAAS,MAAM,MAAM,MAAM,EAAE,SAAS,QAAQ,CAAC,EAAE,SAAS,KAAK,EAAE;EAI9E,MAAM,OAFW,KAAK,IAAI,MAAM,QAAQ,GAAG,KAAK,MAAM,QAAQ,MAE1C,IADH,KAAK,IAAI,OAAO,SAAS,GAAG,MAAM,OAAO,MAAM,IACjC;EAC/B,IAAI,SAAS,GAAG;EAChB,SAAS;CACX;CACA,OAAO,IAAI,KAAK,KAAK;AACvB;AAEA,SAAgB,eACd,UACA,YACA,2BAAiB,IAAI,KAAK,GACwB;CAClD,IAAI,CAAC,cAAc,WAAW,WAAW,GACvC,OAAO,EAAE,cAAc,MAAM;CAG/B,MAAM,iBAAiB,SAAe;EACpC,IAAI;EACJ,IAAI;GAWF,QAAQ,IAVc,KAAK,eAAe,SAAS;IACjD,UAAU;IACV,MAAM;IACN,OAAO;IACP,KAAK;IACL,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,QAAQ;GACV,CACgB,CAAC,CAAC,cAAc,IAAI;EACtC,QAAQ;GACN,OAAO;EACT;EAEA,MAAM,OAAO,SAAS,MAAM,MAAM,MAAM,EAAE,SAAS,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE;EAC5E,MAAM,QAAQ,SAAS,MAAM,MAAM,MAAM,EAAE,SAAS,OAAO,CAAC,EAAE,SAAS,KAAK,EAAE;EAC9E,MAAM,MAAM,SAAS,MAAM,MAAM,MAAM,EAAE,SAAS,KAAK,CAAC,EAAE,SAAS,KAAK,EAAE;EAC1E,MAAM,cAAc,SAAS,MAAM,MAAM,MAAM,EAAE,SAAS,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE;EACnF,MAAM,aAAa,SAAS,MAAM,MAAM,MAAM,EAAE,SAAS,QAAQ,CAAC,EAAE,SAAS,KAAK,EAAE;EAEpF,MAAM,iBAAiB,cAAc,KAAK;EAE1C,KAAK,MAAM,UAAU,YAAY;GAC/B,MAAM,CAAC,QAAQ,UAAU,OAAO,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;GAC3D,MAAM,CAAC,MAAM,QAAQ,OAAO,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;GAErD,IAAI,WAAW,KAAA,KAAa,WAAW,KAAA,KAAa,SAAS,KAAA,KAAa,SAAS,KAAA,GACjF;GAEF,MAAM,eAAe,SAAS,KAAK;GACnC,MAAM,aAAa,OAAO,KAAK;GAE/B,IAAI,WAAW;GACf,IAAI,gBAAgB,YAClB,WAAW,kBAAkB,gBAAgB,iBAAiB;QAE9D,WAAW,kBAAkB,gBAAgB,iBAAiB;GAGhE,IAAI,UAAU;IACZ,IAAI,aAAa;IACjB,IAAI,cAAc;IAClB,IAAI,YAAY;IAEhB,IAAI,kBAAkB,YAAY;KAChC,MAAM,UAAU,IAAI,KAAK,KAAK,IAAI,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC;KAC3D,aAAa,QAAQ,eAAe;KACpC,cAAc,QAAQ,YAAY,IAAI;KACtC,YAAY,QAAQ,WAAW;IACjC;IAWA,OAAO;KAAE,cAAc;KAAM,gBATV,eACjB,YACA,aACA,WACA,MACA,MACA,GACA,QAEoD;IAAE;GAC1D;EACF;EACA,OAAO,EAAE,cAAc,MAAM;CAC/B;CAEA,IAAI,eAAe,cAAc,QAAQ;CACzC,IAAI,CAAC,gBAAgB,CAAC,aAAa,cACjC,OAAO,EAAE,cAAc,MAAM;CAI/B,IAAI,gBAAgB,aAAa;CACjC,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;EAC3B,MAAM,kBAAkB,cAAc,aAAa;EACnD,IAAI,mBAAmB,gBAAgB,gBAAgB,gBAAgB,gBAAgB;GACrF,IAAI,gBAAgB,eAAe,QAAQ,KAAK,cAAc,QAAQ,GACpE;GAEF,gBAAgB,gBAAgB;EAClC,OACE;CAEJ;CAEA,OAAO;EAAE,cAAc;EAAM,gBAAgB;CAAc;AAC7D;AAIA,QAAQ;AACR,MAAM,SAAS,eAAe;AAE9B,IAAI;AACJ,IAAI;AACJ,IAAIA;AACJ,IAAI;AACJ,IAAI;AAEJ,IAAI;AAEJ,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI,iBAAwC;AAiB5C,IAAa,eAAb,cAAkC,WAAW;CAC3C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,SAA8B;EACxC,MAAM,OAAO;EACb,KAAK,WAAW,QAAQ;EACxB,KAAK,cAAc,QAAQ;EAC3B,KAAK,WAAW,QAAQ;EACxB,KAAK,kBAAkB,QAAQ;EAC/B,KAAK,WAAW,QAAQ;EACxB,KAAK,gBAAgB,QAAQ;EAC7B,KAAK,oBAAoB,QAAQ;EACjC,KAAK,oBAAoB,QAAQ;EACjC,KAAK,oBAAoB,QAAQ;EACjC,KAAK,gBAAgB,QAAQ;EAC7B,KAAK,cAAc,QAAQ;EAC3B,KAAK,KAAK,QAAQ;CACpB;CAEA,iBAAkC,IAAI,WAGpC,OAAO,SAAS;EAChB,MAAM,4BAAY,IAAI,IAAsB;EAC5C,KAAK,MAAM,OAAO,MAAM;GACtB,IAAI,CAAC,UAAU,IAAI,IAAI,SAAS,GAAG,UAAU,IAAI,IAAI,WAAW,CAAC,CAAC;GAClE,UAAU,IAAI,IAAI,SAAS,CAAC,CAAE,KAAK,IAAI,WAAW;EACpD;EAEA,MAAM,0CAA0B,IAAI,IAAgC;EACpE,KAAK,MAAM,CAAC,WAAW,YAAY,WAAW;GAC5C,MAAM,oBAAoB,MAAM,KAAK,YAAY,oBAAoB,WAAW,OAAO;GACvF,wBAAwB,IAAI,WAAW,iBAAiB;EAC1D;EAEA,OAAO,KAAK,KAAK,QAAQ;GACvB,MAAM,aAAa,wBAAwB,IAAI,IAAI,SAAS;GAC5D,IAAI,CAAC,YAAY,OAAO,CAAC;GACzB,OAAO,WAAW,IAAI,IAAI,WAAW,KAAK,CAAC;EAC7C,CAAC;CACH,CAAC;;;;;;;;;;CAWD,qBAAsC,IAAI,WAGxC,OAAO,SAAS;EAChB,MAAM,0BAAU,IAAI,IAAyB;EAC7C,KAAK,MAAM,OAAO,MAAM;GACtB,MAAM,WAAW,GAAG,IAAI,UAAU,GAAG,IAAI;GACzC,IAAI,QAAQ,IAAI,QAAQ,GAAG;GAC3B,IAAI;IACF,MAAM,OAAO,MAAM,KAAK,GACrB,OAAO,EAAE,QAAQ,aAAa,OAAO,CAAC,CAAC,CACvC,KAAK,YAAY,CAAC,CAClB,MACC,IACE,GAAG,aAAa,WAAW,IAAI,SAAS,GACxC,GAAG,aAAa,SAAS,IAAI,OAAc,CAC7C,CACF;IACF,QAAQ,IACN,UACA,IAAI,IAAI,KAAK,KAAK,MAA0B,gBAAgB,EAAE,MAAM,CAAC,CAAC,CACxE;GACF,SAAS,KAAK;IAGZ,KAAK,OAAO,MACV;KAAE;KAAK,WAAW,IAAI;KAAW,SAAS,IAAI;IAAQ,GACtD,6CACF;IACA,MAAM;GACR;EACF;EACA,OAAO,KAAK,KAAK,QAAQ,QAAQ,IAAI,GAAG,IAAI,UAAU,GAAG,IAAI,SAAS,qBAAK,IAAI,IAAY,CAAC;CAC9F,CAAC;CAED,MAAM,QAAQ,SAAuC;EACnD,MAAM,EAAE,UAAU;EAElB,MAAM,gBAAgB,KAAK,SAAS,iBAAiB,yBAAyB,MAAM,OAAO;EAC3F,IAAI,CAAC,cAAc,SAAS;GAC1B,KAAK,OAAO,KACV;IAAE,WAAW,QAAQ;IAAI,QAAQ,cAAc,MAAM;GAAO,GAC5D,kDACF;GACA;EACF;EAEA,MAAM,WAAW,cAAc;EAG/B,MAAM,iBAAiB,GAAG,SAAS,WAAW,GAAG,SAAS,YAAY,GAAG,SAAS;EAClF,IAAI;EACJ,IAAI,SAAS,aAAa;GACxB,MAAM,UAAU,IAAI,KAAK,SAAS,WAAW,CAAC,CAAC,QAAQ,IAAI,KAAK,IAAI;GACpE,IAAI,UAAU,GAEZ,YAAY,QAAQ,KAAK,KAAK,UAAU,GAAI;EAEhD;EAEA,IAAI,CAAE,MAAM,KAAK,YAAY,aAAa,gBAAgB,EAAE,GAAI;GAC9D,KAAK,OAAO,MAAM;IAAE,WAAW,QAAQ;IAAI,SAAS,MAAM;GAAG,GAAG,sBAAsB;GACtF;EACF;EAEA,IAAI;GAEF,IAAI,SAAS,UAAU,YAAY,UAAU;IAC3C,KAAK,OAAO,KACV;KAAE,WAAW,QAAQ;KAAI,aAAa,SAAS;KAAa,WAAW,MAAM;IAAK,GAClF,2BACF;IACA,KAAK,cAAc,KAAK,wBAAwB;KAC9C,WAAW,SAAS;KACpB,SAAS,MAAM;KACf,aAAa,SAAS;KACtB,QAAQ;IACV,CAAC;IACD,MAAM,KAAK,YAAY,cAAc,gBAAgB,SAAS;IAC9D;GACF;GAEA,IAAI,SAAS,UAAU,YAAY,UAAU,SAAS,SAAS,OAAO,GAAG;IACvE,KAAK,OAAO,KACV;KAAE,WAAW,QAAQ;KAAI,aAAa,SAAS;KAAa,SAAS,SAAS;IAAQ,GACtF,+CACF;IACA,KAAK,cAAc,KAAK,wBAAwB;KAC9C,WAAW,SAAS;KACpB,SAAS,MAAM;KACf,aAAa,SAAS;KACtB,QAAQ;IACV,CAAC;IACD,MAAM,KAAK,YAAY,cAAc,gBAAgB,SAAS;IAC9D;GACF;GAGA,MAAM,KAAK,SAAS,UAAU,YAAY;GAC1C,IAAI,MAAM,GAAG,SAAS,KAAK,SAAS,aAAa,YAAY;IAC3D,MAAM,WAAW,eAAe,SAAS,UAAU,UAAU,EAAE;IAC/D,IAAI,SAAS,gBAAgB,SAAS,gBAAgB;KACpD,KAAK,OAAO,KACV;MACE,WAAW,QAAQ;MACnB,aAAa,SAAS;MACtB,gBAAgB,SAAS,eAAe,YAAY;KACtD,GACA,iDACF;KACA,SAAS,cAAc,SAAS,eAAe,YAAY;IAC7D;GACF;GAKA,IAAI,kBAAwF;IAC1F,eAAe;IACf,qBAAqB;GACvB;GACA,IAAI;IACF,kBAAkB,MAAM,KAAK,gBAAgB,IAAI,SAAS,SAAS;GACrE,SAAS,KAAK;IACZ,KAAK,OAAO,KACV;KAAE;KAAK,WAAW,SAAS;IAAU,GACrC,+EACF;GACF;GAEA,MAAM,iBAAiB,MAAM,KAAK,SAAS,MACzC,SAAS,WACT,SAAS,aACT,SAAS,UACT;IACE,OAAO,gBAAgB;IACvB,aAAa,gBAAgB;IAC7B,aAAa,SAAS;GACxB,CACF;GACA,IAAI,CAAC,eAAe,SAAS;IAC3B,KAAK,OAAO,KACV;KACE,WAAW,QAAQ;KACnB,aAAa,SAAS;KACtB,OAAO,eAAe;KACtB,OAAO,eAAe;KACtB,UAAU,SAAS;IACrB,GACA,2BACF;IACA,KAAK,cAAc,KACjB,0BACA,SAAS,aACT,eAAe,KACjB;IACA,MAAM,KAAK,YAAY,cAAc,gBAAgB,SAAS;IAC9D;GACF;GAIA,IAAI,aAAa;GACjB,IAAI,SAAS,YAAY;IACvB,aAAa,MAAM,KAAK,cAAc,kBACpC,SAAS,WACT,SAAS,UACX;IACA,IAAI,CAAC,YAAY;KACf,KAAK,OAAO,KACV;MAAE,WAAW,QAAQ;MAAI,YAAY,SAAS;KAAW,GACzD,+BACF;KACA,KAAK,cAAc,KAAK,wBAAwB;MAC9C,WAAW,SAAS;MACpB,SAAS,MAAM;MACf,aAAa,SAAS;MACtB,QAAQ;KACV,CAAC;KACD,MAAM,KAAK,YAAY,cAAc,gBAAgB,SAAS;KAC9D;IACF;GACF;GACA,MAAM,YAAY;IAChB,GAAI,YAAY,aAAa,CAAC;IAC9B,GAAI,SAAS,aAAa,CAAC;GAC7B;GAIA,MAAM,iBAA2B,YAAY,UAAU,CAAC;GAExD,IAAI,OAAO,KAAK,SAAS,CAAC,CAAC,SAAS,GAAG;IACrC,MAAM,mBAAmB;KACvB,WAAW,SAAS;KACpB,iBAAiB,MAAM;KACvB,aAAa,SAAS;KACtB,SAAS,SAAS;KAClB,UAAU,SAAS;KACnB,YAAY,SAAS;KACrB,mBAAmB,SAAS;KAC5B,WAAW,SAAS;KACpB;KACA,aAAa,SAAS;KACtB,eAAe,SAAS;IAC1B;IAEA,MAAM,oBAAoB,iBACxB,2BACA,kBACA,UACA,MAAM,SAAS,OACjB;IAEA,MAAM,KAAK,kBAAkB,QAAQ,iBAAiB;IACtD,KAAK,OAAO,KACV;KACE,WAAW,QAAQ;KACnB,SAAS,MAAM;KACf,aAAa,SAAS;KACtB,SAAS,MAAM,SAAS;IAC1B,GACA,0BACF;IACA,MAAM,KAAK,YAAY,cAAc,gBAAgB,SAAS;IAC9D;GACF;GAGA,MAAM,WAAW,mBAAmB,YAAY,SAAS,iBAAiB;GAM1E,MAAM,kBAAiB,MAJG,KAAK,eAAe,KAAK;IACjD,WAAW,SAAS;IACpB,aAAa,SAAS;GACxB,CAAC,EAAA,CACkC,QAChC,MAAW,EAAE,YAAY,SAAS,WAAW,EAAE,MAClD;GAEA,IAAI,eAAe,WAAW,GAAG;IAC/B,KAAK,OAAO,KACV;KAAE,WAAW,QAAQ;KAAI,aAAa,SAAS;KAAa,SAAS,SAAS;IAAQ,GACtF,2CACF;IACA,KAAK,cAAc,KAAK,wBAAwB;KAC9C,WAAW,SAAS;KACpB,SAAS,MAAM;KACf,aAAa,SAAS;KACtB,QAAQ;IACV,CAAC;IACD,MAAM,KAAK,YAAY,cAAc,gBAAgB,SAAS;IAC9D;GACF;GAEA,MAAM,oBAAoB,MAAM,KAAK,mBAAmB,KAAK;IAC3D,WAAW,SAAS;IACpB,SAAS,SAAS;GACpB,CAAC;GAED,KAAK,MAAM,WAAW,gBAAgB;IACpC,IAAI,QAAQ,aAAa,UACvB;IAMF,IAAI,QAAQ,UAAU,kBAAkB,IAAI,gBAAgB,QAAQ,MAAM,CAAC,GAAG;KAC5E,KAAK,OAAO,KACV;MACE,WAAW,QAAQ;MACnB,aAAa,SAAS;MACtB,SAAS,SAAS;KACpB,GACA,mCACF;KACA,KAAK,cAAc,KAAK,wBAAwB;MAC9C,WAAW,SAAS;MACpB,SAAS,MAAM;MACf,aAAa,SAAS;MACtB,QAAQ;KACV,CAAC;KACD;IACF;IAEA,MAAM,SAAS,GAAG,SAAS,WAAW,GAAG,QAAQ,MAAM,WAAW;IAClE,MAAM,sBAAsB,QAAQ;IAOpC,IACE,SAAS,YAAY,WACrB,eAAe,SAAS,KACxB,CAAC,OAAO,oBAER,KAAK,OAAO,KACV;KAAE,WAAW,SAAS;KAAW,aAAa,SAAS;IAAY,GACnE,kGACF;IAGF,MAAM,qBACJ,SAAS,YAAY,WACrB,eAAe,SAAS,KACxB,OAAO,sBACP,OAAO,cACP,sBACI,wBAAwB;KACtB,OAAO;MACL,WAAW,SAAS;MACpB,QAAQ,SAAS;MACjB,SAAS,SAAS;MAClB,QAAQ;MACR,QAAQ;KACV;KACA,QAAQ,OAAO;KACf,WAAW,OAAO;IACpB,CAAC,IACD,KAAA;IAEN,MAAM,cAA6C;KACjD,WAAW,SAAS;KACpB;KACA,iBAAiB,MAAM;KACvB,aAAa,SAAS;KACtB,SAAS,SAAS;KAClB,UAAU,SAAS;KACnB,YAAY,SAAS;KACrB,mBAAmB,SAAS;KAC5B,WAAW,SAAS;KACpB,WAAW,SAAS;KACpB,iBAAiB;KACjB,aAAa;KACb,iBAAiB;MACf,aAAa;MACb,WAAW;MACX,GAAI,qBAAqB,EAAE,SAAS,mBAAmB,IAAI,CAAC;KAC9D;KACA,eAAe,SAAS;KACxB,YAAY,SAAS;IACvB;IAEA,MAAM,WAAW,iBACf,2BACA,aACA,UACA,MAAM,SAAS,OACjB;IAGA,MAAM,MAAM,KAAK,IAAI;IAGrB,KAFoB,SAAS,cAAc,IAAI,KAAK,SAAS,WAAW,CAAC,CAAC,QAAQ,IAAI,OAEpE,KAAK;KACrB,MAAM,KAAK,GACR,OAAO,iBAAiB,CAAC,CACzB,OAAO;MACN;MACA,SAAS;OACP,GAAG;OACH,aAAa,SAAS;MACxB;KACF,CAAC,CAAC,CACD,oBAAoB;KAEvB,MAAM,oBAAoB,iBACxB,0BACA;MACE,WAAW,SAAS;MACpB,iBAAiB,MAAM;MACvB;MACA,aAAa,SAAS;KACxB,GACA,UACA,MAAM,SAAS,OACjB;KAEA,MAAM,KAAK,kBAAkB,QAAQ,iBAAiB;KACtD,KAAK,OAAO,KACV;MACE,WAAW,QAAQ;MACnB;MACA,aAAa,SAAS;MACtB,SAAS,MAAM,SAAS;KAC1B,GACA,mCACF;IACF,OAAO;KACL,MAAM,IAAI,kBAAkB,SAAS,QAAQ;KAG7C,OAFyB,KAAK,kBAAkB,MAAM,KAAK,kBAAkB,UAAA,CAEtD,QAAQ,QAAQ;KACvC,KAAK,OAAO,KACV;MACE,WAAW,QAAQ;MACnB;MACA,aAAa,SAAS;MACtB,SAAS,MAAM,SAAS;KAC1B,GACA,iBACF;IACF;GACF;GACA,MAAM,KAAK,YAAY,cAAc,gBAAgB,SAAS;EAChE,SAAS,KAAK;GACZ,MAAM,KAAK,YAAY,OAAO,cAAc,CAAC,CAAC,YAAY,CAAC,CAAC;GAC5D,MAAM;EACR;CACF;AACF;AAEA,IAAI,aAAkB;AAEtB,eAAsB,oBAAoB;CACxC,SAAS,aAAa;EAAE,MAAM;EAAU,OAAO,OAAO;CAAU,CAAC;CACjE,QAAQ,IAAI,YAAY;EAAE,KAAK,OAAO;EAAW,MAAM;EAAU;CAAO,CAAC;CACzE,MAAM,SAAS,eAAe;EAAE,KAAK,OAAO;EAAc,iBAAiB;EAAU;CAAO,CAAC;CAC7F,QAAM,OAAO;CACb,KAAK,OAAO;CACZ,eAAe,IAAI,mBAAmB,EAAE;CACxC,gBAAgB,IAAI,cAAc,YAAY;CAC9C,MAAM,cAAc,IAAI,kBAAkB,EAAE;CAC5C,WAAW,IAAI,eAAe;EAC5B,OAAO,MAAM;EACb,QAAQ;EACR,OAAO,gBAAgB;EACvB,UAAU,UAAU,QAAQ;EAC5B,WAAW,QAAQ;EACnB,WAAW,OAAO;EAClB;CACF,CAAC;CAED,iBAAiB,IAAI,sBAAsB;EACzC,OAAO,MAAM;EACb,QAAQ;EACR,OAAO,gBAAgB;EACvB,UAAU,UAAU,QAAQ;EAC5B;CACF,CAAC;CAED,MAAM,oBAAoB;EACxB,UAAU,IAAI,eAAe;GAC3B,OAAO,MAAM;GACb,QAAQ,QAAQ;GAChB;EACF,CAAC;EACD,QAAQ,IAAI,eAAe;GAAE,OAAO,MAAM;GAAQ,QAAQ,QAAQ;GAAiB;EAAO,CAAC;EAC3F,KAAK,IAAI,eAAe;GAAE,OAAO,MAAM;GAAQ,QAAQ,QAAQ;GAAc;EAAO,CAAC;CACvF;CAEA,MAAM,oBAAoB,IAAI,eAAe;EAC3C,OAAO,MAAM;EACb,QAAQ,QAAQ;EAChB;CACF,CAAC;CAED,MAAM,oBAAoB,IAAI,eAAe;EAC3C,OAAO,MAAM;EACb,QAAQ,QAAQ;EAChB;CACF,CAAC;CAED,MAAM,cAAc,IAAI,iBAAiB;EACvC,OAAO,MAAM;EACb,WAAW;EACX,YAAY;CACd,CAAC;CAED,MAAM,WAAW,IAAI,aAAa;EAChC,OAAO,MAAM;EACb,YAAY,SAAS,QAAQ,IAAI,uBAAuB,OAAO,EAAE;CACnE,CAAC;CAKD,MAAM,cAAc,IAAI,kBAAkB,EAAE;CAC5C,MAAM,kBAAkB,IAAI,sBAAsB,cAChD,YAAY,qBAAqB,SAAS,CAC5C;CAYA,SAAS,IAAI,aAAa;EACxB;EACA;EACA;EACA,aAAa,OAAO;EACpB;EACA;EACA;EACA;EACA,OAAO,MAAM;EACb;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,aAAa,MAAM,OAAO,UAAU;CACpC,MAAM,WAAW,UACf,gBAAgB,sBAChB,gBAAgB,mBAClB;CACA,WAAW,GAAG,YAAY,SAAiB,YAAoB;EAC7D,IAAI,YAAY,gBAAgB,sBAAsB;GACpD,cAAc,cAAc,OAAO;GACnC,OAAO,KAAK,EAAE,UAAU,QAAQ,GAAG,4BAA4B;EACjE,OAAO,IAAI,YAAY,gBAAgB,qBAAqB;GAC1D,gBAAgB,WAAW,OAAO;GAClC,OAAO,KAAK,EAAE,WAAW,QAAQ,GAAG,oCAAoC;EAC1E;CACF,CAAC;CAID,iBAAiB,oBAAoB,UAAU,QAAQ,OAAO,MAAM;CAEpE,OAAO,KAAK,EAAE,KAAK,OAAO,SAAS,GAAG,iBAAiB;CACvD,MAAM,OAAO,MAAM;AACrB;AAIA,eAAsB,mBAAkC;CACtD,QAAQ,KAAK,oBAAoB;CACjC,IAAI,gBAAgB;EAClB,cAAc,cAAc;EAC5B,iBAAiB;CACnB;CACA,IAAI,YAAY;EACd,WAAW,WAAW;EACtB,aAAa;CACf;CACA,IAAI,QAAQ,MAAM,OAAO,KAAK;CAC9B,IAAIA,OAAK,MAAMA,MAAI,IAAI;CACvB,IAAI,OAAO,MAAM,MAAM,WAAW;CAClC,QAAQ,KAAK,gBAAgB;AAC/B"}
@@ -1,4 +1,4 @@
1
- import { J as PendingMessageScanner, Jt as buildStreamEvent, U as LUA_SCHEDULER_POLL, W as getPriorityBucket, X as StreamProducer, Y as StreamConsumer, Yt as CONSUMER_GROUPS, an as registry, c as BaseWorker, en as STREAMS, et as createLogger, hn as readBaseConfig, it as createDatabase, k as RedisClient, pn as loadEnv, u as startHealthReporter, ut as scheduledPayloads } from "./src-DrSN2wCg.mjs";
1
+ import { J as PendingMessageScanner, Jt as buildStreamEvent, U as LUA_SCHEDULER_POLL, W as getPriorityBucket, X as StreamProducer, Y as StreamConsumer, Yt as CONSUMER_GROUPS, an as registry, c as BaseWorker, en as STREAMS, et as createLogger, hn as readBaseConfig, it as createDatabase, k as RedisClient, pn as loadEnv, u as startHealthReporter, ut as scheduledPayloads } from "./src-C-PfEDMY.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { inArray } from "drizzle-orm";
4
4
  //#region src/services/scheduler/main.ts
@@ -221,4 +221,4 @@ async function stopSchedulerWorker() {
221
221
  //#endregion
222
222
  export { startSchedulerWorker, stopSchedulerWorker };
223
223
 
224
- //# sourceMappingURL=main-BHYZfBBq.mjs.map
224
+ //# sourceMappingURL=main-C45e7grq.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"main-BHYZfBBq.mjs","names":["sql"],"sources":["../src/services/scheduler/main.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { loadEnv, readBaseConfig } from \"@/index.js\";\nimport { createLogger } from \"@/index.js\";\nimport { RedisClient, type Redis } from \"@/index.js\";\nimport {\n StreamConsumer,\n PendingMessageScanner,\n StreamProducer,\n type StreamMessage,\n} from \"@/index.js\";\nimport { BaseWorker } from \"@/index.js\";\nimport {\n STREAMS,\n CONSUMER_GROUPS,\n registry,\n buildStreamEvent,\n type NotificationScheduledPayload,\n} from \"@/index.js\";\nimport { getPriorityBucket, type WorkerOptions, LUA_SCHEDULER_POLL } from \"@/shared/index.js\";\nimport { createDatabase } from \"@/db/index.js\";\nimport { scheduledPayloads } from \"@/db/schema.js\";\nimport { inArray } from \"drizzle-orm\";\nimport { startHealthReporter } from \"@/workers/index.js\";\n// ─── Bootstrap ─────────────────────────────────────────────────────────────\n\nloadEnv();\nconst config = readBaseConfig();\n\nlet logger: ReturnType<typeof createLogger>;\nlet redis: RedisClient;\nlet sql: any;\nlet db: any;\n\nlet consumer: StreamConsumer;\nlet pendingScanner: PendingMessageScanner;\nlet worker: BaseWorker;\nlet healthInterval: NodeJS.Timeout | null = null;\nlet pollTimeout: ReturnType<typeof setTimeout> | null = null;\nlet isPolling = false;\n\nexport interface SchedulerWorkerOptions extends WorkerOptions {\n registry: any;\n redis: Redis;\n}\n\nexport class SchedulerWorker extends BaseWorker {\n private readonly registry: any;\n private readonly redisCli: Redis;\n\n constructor(options: SchedulerWorkerOptions) {\n super(options);\n this.registry = options.registry;\n this.redisCli = options.redis;\n }\n async process(message: StreamMessage): Promise<void> {\n const { event } = message;\n\n const payloadResult = this.registry.safeParsePayload(\"notification.scheduled\", event.payload);\n if (!payloadResult.success) {\n this.logger.warn(\n { messageId: message.id, issues: payloadResult.error.issues },\n \"invalid notification.scheduled payload — skipping\",\n );\n return;\n }\n\n const scheduled = payloadResult.data as NotificationScheduledPayload;\n const scheduledAt = new Date(scheduled.scheduledAt).getTime();\n\n const taskData = JSON.stringify({\n taskId: scheduled.taskId,\n enrichedEventId: scheduled.enrichedEventId,\n traceId: event.metadata.traceId,\n });\n\n const shard = parseInt(scheduled.taskId.slice(-1), 16) || 0;\n const zsetKey = `notif:scheduled:zset:${shard}`;\n await this.redisCli.zadd(zsetKey, scheduledAt, taskData);\n this.logger.debug(\n { taskId: scheduled.taskId, scheduledAt: scheduled.scheduledAt, shard },\n \"scheduled task queued in ZSET\",\n );\n }\n}\n\nexport async function executeSchedulerPoll(\n redis: Redis,\n outboundProducers: any,\n logger: any,\n db: any,\n): Promise<boolean> {\n const lockKey = \"notif:lock:scheduler:poll\";\n const lockOwner = randomUUID();\n let acquired = false;\n try {\n const lockAcquired = await redis.set(lockKey, lockOwner, \"EX\", 30, \"NX\");\n if (lockAcquired !== \"OK\") {\n return false; // Another instance is currently polling\n }\n acquired = true;\n\n const now = Date.now();\n const visibilityTimeout = 60000; // 60 seconds\n const perShardLimit = 100;\n const pipeline = redis.pipeline();\n\n for (let i = 0; i < 16; i++) {\n pipeline.eval(\n LUA_SCHEDULER_POLL,\n 1,\n `notif:scheduled:zset:${i}`,\n now,\n perShardLimit,\n visibilityTimeout,\n );\n }\n\n const results = await pipeline.exec();\n const perShard = results?.map((res: any) => (res[0] ? [] : ((res[1] ?? []) as string[]))) ?? [];\n const tasks = perShard.flat();\n // A shard that came back full may still have due tasks behind it, which is\n // what tells the caller to poll again rather than wait for the next tick.\n const anyShardFull = perShard.some((shard) => shard.length >= perShardLimit);\n\n if (tasks.length === 0) return false;\n\n const parsedTasks = tasks\n .map((t) => {\n try {\n return { taskStr: t, ...JSON.parse(t) };\n } catch (err) {\n logger.error({ taskStr: t, err }, \"failed to parse scheduled task JSON\");\n return null;\n }\n })\n .filter(Boolean) as any[];\n if (parsedTasks.length === 0) return false;\n\n const taskIds = parsedTasks.map((t) => t.taskId);\n const dbPayloads = await db\n .select()\n .from(scheduledPayloads)\n .where(inArray(scheduledPayloads.taskId, taskIds));\n const payloadMap = new Map(dbPayloads.map((row: any) => [row.taskId, row.payload]));\n\n const batchedEvents: Record<\n \"critical\" | \"high\" | \"normal\" | \"low\",\n Omit<any, \"id\" | \"timestamp\">[]\n > = {\n critical: [],\n high: [],\n normal: [],\n low: [],\n };\n\n const cleanupPipeline = redis.pipeline();\n const dbCleanupIds: string[] = [];\n\n for (let i = 0; i < parsedTasks.length; i++) {\n const { taskStr, taskId, traceId } = parsedTasks[i];\n const payload = payloadMap.get(taskId);\n\n if (!payload) {\n logger.warn({ taskId }, \"scheduled payload not found in Postgres — skipping release\");\n const shard = parseInt(taskId.slice(-1), 16) || 0;\n cleanupPipeline.zrem(`notif:scheduled:zset:${shard}`, taskStr);\n continue;\n }\n\n const dispatchPayload = payload as any;\n const p = getPriorityBucket(dispatchPayload.priority);\n\n batchedEvents[p].push(\n buildStreamEvent(\"notification.dispatched\", dispatchPayload, \"scheduler\", traceId),\n );\n\n dbCleanupIds.push(taskId);\n const shard = parseInt(taskId.slice(-1), 16) || 0;\n cleanupPipeline.zrem(`notif:scheduled:zset:${shard}`, taskStr);\n }\n\n // Every bucket, not a hardcoded subset: a task binned into one that is not\n // published here has already been added to the cleanup list, so it would be\n // deleted from Redis and Postgres without ever being sent.\n for (const p of Object.keys(batchedEvents) as (keyof typeof batchedEvents)[]) {\n if (batchedEvents[p].length > 0) {\n const producer = outboundProducers[p] ?? outboundProducers[\"normal\"]!;\n await producer!.publishBatch(batchedEvents[p]);\n }\n }\n\n if (dbCleanupIds.length > 0) {\n try {\n await db.delete(scheduledPayloads).where(inArray(scheduledPayloads.taskId, dbCleanupIds));\n } catch (err) {\n logger.error(\n { err },\n \"failed to delete scheduled payloads from Postgres, continuing with Redis cleanup\",\n );\n }\n }\n await cleanupPipeline.exec();\n\n logger.info({ count: parsedTasks.length }, \"scheduled tasks released to outbound\");\n\n // \"Poll again immediately\" — the caller loops while this is true. The old\n // test was `tasks.length === 100` against a total drawn from 16 shards, so\n // a real backlog reported \"nothing more\" and waited for the next tick.\n return anyShardFull;\n } catch (err) {\n logger.error({ err }, \"error in scheduler polling loop\");\n return false;\n } finally {\n if (acquired) {\n // Delete the lock only if we still own it\n const lua = `\n if redis.call(\"get\", KEYS[1]) == ARGV[1] then\n return redis.call(\"del\", KEYS[1])\n else\n return 0\n end\n `;\n await redis.eval(lua, 1, lockKey, lockOwner).catch(() => {});\n }\n }\n}\n\nexport async function startSchedulerWorker() {\n logger = createLogger({ name: \"scheduler\", level: config.LOG_LEVEL });\n redis = new RedisClient({ url: config.REDIS_URL, name: \"scheduler\", logger });\n const dbData = createDatabase({ url: config.DATABASE_URL, applicationName: \"scheduler\", logger });\n sql = dbData.sql;\n db = dbData.db;\n consumer = new StreamConsumer({\n redis: redis.native,\n stream: STREAMS.SCHEDULED,\n group: CONSUMER_GROUPS.SCHEDULER,\n consumer: `scheduler-${process.pid}`,\n dlqStream: STREAMS.DEAD_LETTER,\n batchSize: config.WORKER_CONCURRENCY,\n logger,\n });\n\n pendingScanner = new PendingMessageScanner({\n redis: redis.native,\n stream: STREAMS.SCHEDULED,\n group: CONSUMER_GROUPS.SCHEDULER,\n consumer: `scheduler-${process.pid}`,\n logger,\n });\n\n const outboundProducers = {\n critical: new StreamProducer({\n redis: redis.native,\n stream: STREAMS.OUTBOUND_CRITICAL,\n logger,\n }),\n normal: new StreamProducer({ redis: redis.native, stream: STREAMS.OUTBOUND_NORMAL, logger }),\n low: new StreamProducer({ redis: redis.native, stream: STREAMS.OUTBOUND_LOW, logger }),\n };\n\n // ─── Stage 4a: Scheduled notification processor ─────────────────────────────\n //\n // Reads from SCHEDULED stream, parses, and queues tasks in a Redis ZSET.\n // A background polling interval releases tasks when they are due.\n\n worker = new SchedulerWorker({\n consumer,\n pendingScanner,\n logger,\n concurrency: config.WORKER_CONCURRENCY,\n registry,\n redis: redis.native,\n });\n\n // ─── Polling Loop ─────────────────────────────────────────────────────────\n isPolling = true;\n const pollLoop = async (): Promise<void> => {\n if (!isPolling) return;\n try {\n const hasMore = await executeSchedulerPoll(redis.native, outboundProducers, logger, db);\n if (isPolling) {\n pollTimeout = setTimeout(() => void pollLoop(), hasMore ? 0 : 5000);\n }\n } catch (err) {\n logger.error({ err }, \"scheduler poll loop error\");\n if (isPolling) {\n pollTimeout = setTimeout(() => void pollLoop(), 5000);\n }\n }\n };\n void pollLoop();\n\n // ─── Health check interval ──────────────────────────────────────────────────\n\n healthInterval = startHealthReporter(\"scheduler\", worker, redis, logger);\n\n logger.info({ env: config.NODE_ENV }, \"scheduler starting\");\n await worker.start();\n}\n\n// ─── Shutdown ──────────────────────────────────────────────────────────────\n\nexport async function stopSchedulerWorker(): Promise<void> {\n logger?.info(\"shutdown initiated\");\n if (healthInterval) {\n clearInterval(healthInterval);\n healthInterval = null;\n }\n if (pollTimeout) {\n clearTimeout(pollTimeout);\n pollTimeout = null;\n }\n isPolling = false;\n if (worker) await worker.stop();\n if (sql) await sql.end();\n if (redis) await redis.disconnect();\n logger?.info(\"scheduler stopped\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"main-C45e7grq.mjs","names":["sql"],"sources":["../src/services/scheduler/main.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { loadEnv, readBaseConfig } from \"@/index.js\";\nimport { createLogger } from \"@/index.js\";\nimport { RedisClient, type Redis } from \"@/index.js\";\nimport {\n StreamConsumer,\n PendingMessageScanner,\n StreamProducer,\n type StreamMessage,\n} from \"@/index.js\";\nimport { BaseWorker } from \"@/index.js\";\nimport {\n STREAMS,\n CONSUMER_GROUPS,\n registry,\n buildStreamEvent,\n type NotificationScheduledPayload,\n} from \"@/index.js\";\nimport { getPriorityBucket, type WorkerOptions, LUA_SCHEDULER_POLL } from \"@/shared/index.js\";\nimport { createDatabase } from \"@/db/index.js\";\nimport { scheduledPayloads } from \"@/db/schema.js\";\nimport { inArray } from \"drizzle-orm\";\nimport { startHealthReporter } from \"@/workers/index.js\";\n// ─── Bootstrap ─────────────────────────────────────────────────────────────\n\nloadEnv();\nconst config = readBaseConfig();\n\nlet logger: ReturnType<typeof createLogger>;\nlet redis: RedisClient;\nlet sql: any;\nlet db: any;\n\nlet consumer: StreamConsumer;\nlet pendingScanner: PendingMessageScanner;\nlet worker: BaseWorker;\nlet healthInterval: NodeJS.Timeout | null = null;\nlet pollTimeout: ReturnType<typeof setTimeout> | null = null;\nlet isPolling = false;\n\nexport interface SchedulerWorkerOptions extends WorkerOptions {\n registry: any;\n redis: Redis;\n}\n\nexport class SchedulerWorker extends BaseWorker {\n private readonly registry: any;\n private readonly redisCli: Redis;\n\n constructor(options: SchedulerWorkerOptions) {\n super(options);\n this.registry = options.registry;\n this.redisCli = options.redis;\n }\n async process(message: StreamMessage): Promise<void> {\n const { event } = message;\n\n const payloadResult = this.registry.safeParsePayload(\"notification.scheduled\", event.payload);\n if (!payloadResult.success) {\n this.logger.warn(\n { messageId: message.id, issues: payloadResult.error.issues },\n \"invalid notification.scheduled payload — skipping\",\n );\n return;\n }\n\n const scheduled = payloadResult.data as NotificationScheduledPayload;\n const scheduledAt = new Date(scheduled.scheduledAt).getTime();\n\n const taskData = JSON.stringify({\n taskId: scheduled.taskId,\n enrichedEventId: scheduled.enrichedEventId,\n traceId: event.metadata.traceId,\n });\n\n const shard = parseInt(scheduled.taskId.slice(-1), 16) || 0;\n const zsetKey = `notif:scheduled:zset:${shard}`;\n await this.redisCli.zadd(zsetKey, scheduledAt, taskData);\n this.logger.debug(\n { taskId: scheduled.taskId, scheduledAt: scheduled.scheduledAt, shard },\n \"scheduled task queued in ZSET\",\n );\n }\n}\n\nexport async function executeSchedulerPoll(\n redis: Redis,\n outboundProducers: any,\n logger: any,\n db: any,\n): Promise<boolean> {\n const lockKey = \"notif:lock:scheduler:poll\";\n const lockOwner = randomUUID();\n let acquired = false;\n try {\n const lockAcquired = await redis.set(lockKey, lockOwner, \"EX\", 30, \"NX\");\n if (lockAcquired !== \"OK\") {\n return false; // Another instance is currently polling\n }\n acquired = true;\n\n const now = Date.now();\n const visibilityTimeout = 60000; // 60 seconds\n const perShardLimit = 100;\n const pipeline = redis.pipeline();\n\n for (let i = 0; i < 16; i++) {\n pipeline.eval(\n LUA_SCHEDULER_POLL,\n 1,\n `notif:scheduled:zset:${i}`,\n now,\n perShardLimit,\n visibilityTimeout,\n );\n }\n\n const results = await pipeline.exec();\n const perShard = results?.map((res: any) => (res[0] ? [] : ((res[1] ?? []) as string[]))) ?? [];\n const tasks = perShard.flat();\n // A shard that came back full may still have due tasks behind it, which is\n // what tells the caller to poll again rather than wait for the next tick.\n const anyShardFull = perShard.some((shard) => shard.length >= perShardLimit);\n\n if (tasks.length === 0) return false;\n\n const parsedTasks = tasks\n .map((t) => {\n try {\n return { taskStr: t, ...JSON.parse(t) };\n } catch (err) {\n logger.error({ taskStr: t, err }, \"failed to parse scheduled task JSON\");\n return null;\n }\n })\n .filter(Boolean) as any[];\n if (parsedTasks.length === 0) return false;\n\n const taskIds = parsedTasks.map((t) => t.taskId);\n const dbPayloads = await db\n .select()\n .from(scheduledPayloads)\n .where(inArray(scheduledPayloads.taskId, taskIds));\n const payloadMap = new Map(dbPayloads.map((row: any) => [row.taskId, row.payload]));\n\n const batchedEvents: Record<\n \"critical\" | \"high\" | \"normal\" | \"low\",\n Omit<any, \"id\" | \"timestamp\">[]\n > = {\n critical: [],\n high: [],\n normal: [],\n low: [],\n };\n\n const cleanupPipeline = redis.pipeline();\n const dbCleanupIds: string[] = [];\n\n for (let i = 0; i < parsedTasks.length; i++) {\n const { taskStr, taskId, traceId } = parsedTasks[i];\n const payload = payloadMap.get(taskId);\n\n if (!payload) {\n logger.warn({ taskId }, \"scheduled payload not found in Postgres — skipping release\");\n const shard = parseInt(taskId.slice(-1), 16) || 0;\n cleanupPipeline.zrem(`notif:scheduled:zset:${shard}`, taskStr);\n continue;\n }\n\n const dispatchPayload = payload as any;\n const p = getPriorityBucket(dispatchPayload.priority);\n\n batchedEvents[p].push(\n buildStreamEvent(\"notification.dispatched\", dispatchPayload, \"scheduler\", traceId),\n );\n\n dbCleanupIds.push(taskId);\n const shard = parseInt(taskId.slice(-1), 16) || 0;\n cleanupPipeline.zrem(`notif:scheduled:zset:${shard}`, taskStr);\n }\n\n // Every bucket, not a hardcoded subset: a task binned into one that is not\n // published here has already been added to the cleanup list, so it would be\n // deleted from Redis and Postgres without ever being sent.\n for (const p of Object.keys(batchedEvents) as (keyof typeof batchedEvents)[]) {\n if (batchedEvents[p].length > 0) {\n const producer = outboundProducers[p] ?? outboundProducers[\"normal\"]!;\n await producer!.publishBatch(batchedEvents[p]);\n }\n }\n\n if (dbCleanupIds.length > 0) {\n try {\n await db.delete(scheduledPayloads).where(inArray(scheduledPayloads.taskId, dbCleanupIds));\n } catch (err) {\n logger.error(\n { err },\n \"failed to delete scheduled payloads from Postgres, continuing with Redis cleanup\",\n );\n }\n }\n await cleanupPipeline.exec();\n\n logger.info({ count: parsedTasks.length }, \"scheduled tasks released to outbound\");\n\n // \"Poll again immediately\" — the caller loops while this is true. The old\n // test was `tasks.length === 100` against a total drawn from 16 shards, so\n // a real backlog reported \"nothing more\" and waited for the next tick.\n return anyShardFull;\n } catch (err) {\n logger.error({ err }, \"error in scheduler polling loop\");\n return false;\n } finally {\n if (acquired) {\n // Delete the lock only if we still own it\n const lua = `\n if redis.call(\"get\", KEYS[1]) == ARGV[1] then\n return redis.call(\"del\", KEYS[1])\n else\n return 0\n end\n `;\n await redis.eval(lua, 1, lockKey, lockOwner).catch(() => {});\n }\n }\n}\n\nexport async function startSchedulerWorker() {\n logger = createLogger({ name: \"scheduler\", level: config.LOG_LEVEL });\n redis = new RedisClient({ url: config.REDIS_URL, name: \"scheduler\", logger });\n const dbData = createDatabase({ url: config.DATABASE_URL, applicationName: \"scheduler\", logger });\n sql = dbData.sql;\n db = dbData.db;\n consumer = new StreamConsumer({\n redis: redis.native,\n stream: STREAMS.SCHEDULED,\n group: CONSUMER_GROUPS.SCHEDULER,\n consumer: `scheduler-${process.pid}`,\n dlqStream: STREAMS.DEAD_LETTER,\n batchSize: config.WORKER_CONCURRENCY,\n logger,\n });\n\n pendingScanner = new PendingMessageScanner({\n redis: redis.native,\n stream: STREAMS.SCHEDULED,\n group: CONSUMER_GROUPS.SCHEDULER,\n consumer: `scheduler-${process.pid}`,\n logger,\n });\n\n const outboundProducers = {\n critical: new StreamProducer({\n redis: redis.native,\n stream: STREAMS.OUTBOUND_CRITICAL,\n logger,\n }),\n normal: new StreamProducer({ redis: redis.native, stream: STREAMS.OUTBOUND_NORMAL, logger }),\n low: new StreamProducer({ redis: redis.native, stream: STREAMS.OUTBOUND_LOW, logger }),\n };\n\n // ─── Stage 4a: Scheduled notification processor ─────────────────────────────\n //\n // Reads from SCHEDULED stream, parses, and queues tasks in a Redis ZSET.\n // A background polling interval releases tasks when they are due.\n\n worker = new SchedulerWorker({\n consumer,\n pendingScanner,\n logger,\n concurrency: config.WORKER_CONCURRENCY,\n registry,\n redis: redis.native,\n });\n\n // ─── Polling Loop ─────────────────────────────────────────────────────────\n isPolling = true;\n const pollLoop = async (): Promise<void> => {\n if (!isPolling) return;\n try {\n const hasMore = await executeSchedulerPoll(redis.native, outboundProducers, logger, db);\n if (isPolling) {\n pollTimeout = setTimeout(() => void pollLoop(), hasMore ? 0 : 5000);\n }\n } catch (err) {\n logger.error({ err }, \"scheduler poll loop error\");\n if (isPolling) {\n pollTimeout = setTimeout(() => void pollLoop(), 5000);\n }\n }\n };\n void pollLoop();\n\n // ─── Health check interval ──────────────────────────────────────────────────\n\n healthInterval = startHealthReporter(\"scheduler\", worker, redis, logger);\n\n logger.info({ env: config.NODE_ENV }, \"scheduler starting\");\n await worker.start();\n}\n\n// ─── Shutdown ──────────────────────────────────────────────────────────────\n\nexport async function stopSchedulerWorker(): Promise<void> {\n logger?.info(\"shutdown initiated\");\n if (healthInterval) {\n clearInterval(healthInterval);\n healthInterval = null;\n }\n if (pollTimeout) {\n clearTimeout(pollTimeout);\n pollTimeout = null;\n }\n isPolling = false;\n if (worker) await worker.stop();\n if (sql) await sql.end();\n if (redis) await redis.disconnect();\n logger?.info(\"scheduler stopped\");\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { J as PendingMessageScanner, Jt as buildStreamEvent, T as TemplateRepository, W as getPriorityBucket, X as StreamProducer, Y as StreamConsumer, Yt as CONSUMER_GROUPS, an as registry, b as renderWithTemplate, c as BaseWorker, en as STREAMS, et as createLogger, fn as getAiConfig, g as TemplateCache, hn as readBaseConfig, it as createDatabase, k as RedisClient, pn as loadEnv, q as globalEmitter, rt as IdempotencyGuard, u as startHealthReporter, un as AI_DEFAULTS, ut as scheduledPayloads } from "./src-DrSN2wCg.mjs";
1
+ import { J as PendingMessageScanner, Jt as buildStreamEvent, T as TemplateRepository, W as getPriorityBucket, X as StreamProducer, Y as StreamConsumer, Yt as CONSUMER_GROUPS, an as registry, b as renderWithTemplate, c as BaseWorker, en as STREAMS, et as createLogger, fn as getAiConfig, g as TemplateCache, hn as readBaseConfig, it as createDatabase, k as RedisClient, pn as loadEnv, q as globalEmitter, rt as IdempotencyGuard, u as startHealthReporter, un as AI_DEFAULTS, ut as scheduledPayloads } from "./src-C-PfEDMY.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { generateText } from "ai";
4
4
  //#region src/services/ai/main.ts
@@ -291,4 +291,4 @@ async function stopAiWorker() {
291
291
  //#endregion
292
292
  export { startAiWorker, stopAiWorker };
293
293
 
294
- //# sourceMappingURL=main-Dztc2dqR.mjs.map
294
+ //# sourceMappingURL=main-CAH0_Q6d.mjs.map