@develit-services/notification 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/src.cjs +636 -0
  2. package/dist/src.d.cts +491 -0
  3. package/dist/src.d.mts +489 -0
  4. package/dist/src.d.ts +491 -0
  5. package/dist/src.mjs +625 -0
  6. package/package.json +6 -1
  7. package/@types/consts/audit-log.consts.ts +0 -7
  8. package/@types/consts/index.ts +0 -1
  9. package/@types/database/audit-log.types.ts +0 -7
  10. package/@types/database/index.ts +0 -1
  11. package/@types/email/IEmail.connector.ts +0 -21
  12. package/@types/email/IEmail.types.ts +0 -25
  13. package/@types/email/ecomail/ecomail.connector.ts +0 -139
  14. package/@types/email/ecomail/ecomail.types.ts +0 -27
  15. package/@types/email/ecomail/index.ts +0 -2
  16. package/@types/email/index.ts +0 -3
  17. package/@types/index.ts +0 -9
  18. package/@types/io/index.ts +0 -3
  19. package/@types/io/sendEmail.ts +0 -18
  20. package/@types/io/sendSlack.ts +0 -19
  21. package/@types/io/sendSms.ts +0 -20
  22. package/@types/pushNotification/IPushNotification.ts +0 -1
  23. package/@types/pushNotification/index.ts +0 -1
  24. package/@types/queue.ts +0 -19
  25. package/@types/service.ts +0 -30
  26. package/@types/slack/ISlack.types.ts +0 -3
  27. package/@types/slack/index.ts +0 -1
  28. package/@types/slack/slack.connector.ts +0 -27
  29. package/@types/sms/ISms.connector.ts +0 -22
  30. package/@types/sms/ISms.types.ts +0 -4
  31. package/@types/sms/index.ts +0 -3
  32. package/@types/sms/twilio/index.ts +0 -1
  33. package/@types/sms/twilio/twilio.connector.ts +0 -35
  34. package/CHANGELOG.md +0 -32
  35. package/build.config.ts +0 -29
  36. package/drizzle.config.ts +0 -3
  37. package/src/database/drizzle.ts +0 -6
  38. package/src/database/migrations/0000_funny_beast.sql +0 -12
  39. package/src/database/migrations/meta/0000_snapshot.json +0 -101
  40. package/src/database/migrations/meta/_journal.json +0 -13
  41. package/src/database/schema/audit-log.schema.ts +0 -13
  42. package/src/database/schema/index.ts +0 -1
  43. package/src/defineNotificationService.ts +0 -328
  44. package/src/defineNotificationWrangler.ts +0 -78
  45. package/src/index.ts +0 -12
  46. package/src/utils/connectors.ts +0 -1
  47. package/src/utils/database/command/create-audit-log.command.ts +0 -34
  48. package/src/utils/database/command/index.ts +0 -1
  49. package/src/utils/database/index.ts +0 -1
  50. package/src/utils/email.ts +0 -25
  51. package/src/utils/index.ts +0 -3
  52. package/src/utils/sms.ts +0 -25
  53. package/test/env.d.ts +0 -7
  54. package/test/integration/sendEmail.test.ts +0 -88
  55. package/test/setup/migrations.ts +0 -3
  56. package/test/unit/connectors/ecomail.connector.ts +0 -715
  57. package/test/unit/email.test.ts +0 -55
  58. package/tsconfig.json +0 -3
  59. package/vitest.config.ts +0 -31
  60. package/worker-configuration.d.ts +0 -25
  61. package/wrangler.jsonc +0 -209
  62. package/wrangler.ts +0 -106
package/dist/src.cjs ADDED
@@ -0,0 +1,636 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const backendSdk = require('@develit-io/backend-sdk');
6
+ const z = require('zod');
7
+ const v4 = require('zod/v4');
8
+ const twilio = require('twilio');
9
+ const sqliteCore = require('drizzle-orm/sqlite-core');
10
+ require('drizzle-orm');
11
+ const cloudflare_workers = require('cloudflare:workers');
12
+ const d1 = require('drizzle-orm/d1');
13
+
14
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
15
+
16
+ const z__default = /*#__PURE__*/_interopDefaultCompat(z);
17
+ const twilio__default = /*#__PURE__*/_interopDefaultCompat(twilio);
18
+
19
+ const iContactSchema = z.z.union([
20
+ z.z.string(),
21
+ z.z.object({
22
+ email: z.z.string(),
23
+ name: z.z.union([z.z.string(), z.z.undefined()])
24
+ })
25
+ ]);
26
+ const iEmailSchema = z.z.object({
27
+ to: z.z.union([iContactSchema, z.z.array(iContactSchema)]),
28
+ replyTo: z.z.union([iContactSchema, z.z.array(iContactSchema)]).optional(),
29
+ cc: z.z.union([iContactSchema, z.z.array(iContactSchema)]).optional(),
30
+ bcc: z.z.union([iContactSchema, z.z.array(iContactSchema)]).optional(),
31
+ from: iContactSchema.optional(),
32
+ subject: z.z.string(),
33
+ text: z.z.string().optional(),
34
+ html: z.z.string().optional(),
35
+ templateId: z.z.number().optional(),
36
+ templateVariables: z.z.record(z.z.string(), z.z.string()).optional()
37
+ });
38
+
39
+ class IEmailConnector {
40
+ static providerName;
41
+ API_KEY;
42
+ SMTP_HOST;
43
+ SENDER;
44
+ constructor({
45
+ API_KEY,
46
+ SMTP_HOST,
47
+ SENDER
48
+ }) {
49
+ this.API_KEY = API_KEY;
50
+ this.SMTP_HOST = SMTP_HOST;
51
+ this.SENDER = SENDER;
52
+ }
53
+ }
54
+
55
+ class EcomailConnector extends IEmailConnector {
56
+ static providerName = "ecomail";
57
+ constructor({
58
+ API_KEY,
59
+ SMTP_HOST,
60
+ SENDER
61
+ }) {
62
+ super({ API_KEY, SMTP_HOST, SENDER });
63
+ }
64
+ async sendEmail(email) {
65
+ if (email.templateVariables) {
66
+ for (const [key, value] of Object.entries(email.templateVariables)) {
67
+ if (typeof value === "string" && String(value).includes("localhost") && key in email.templateVariables) {
68
+ email.templateVariables[key] = String(value).replace("localhost", "origin");
69
+ }
70
+ }
71
+ }
72
+ const emEmail = this.convertEmail(email);
73
+ const uri = emEmail.message.template_id ? "https://api2.ecomailapp.cz/transactional/send-template" : "https://api2.ecomailapp.cz/transactional/send-message";
74
+ const [data, error] = await backendSdk.useResult(
75
+ fetch(uri, {
76
+ method: "POST",
77
+ headers: {
78
+ "Content-Type": "application/json",
79
+ key: this.API_KEY
80
+ },
81
+ body: JSON.stringify(emEmail)
82
+ })
83
+ );
84
+ if (error) throw error;
85
+ return data;
86
+ }
87
+ convertEmail(email) {
88
+ const toContacts = this.convertContacts(email.to);
89
+ const ccContacts = email.cc ? this.convertContacts(email.cc) : [];
90
+ const bccContacts = email.bcc ? this.convertContacts(email.bcc) : [];
91
+ const replyTo = email.replyTo ? this.convertContacts(email.replyTo)[0] : void 0;
92
+ const from = this.convertContact(email.from || this.SENDER);
93
+ const subject = email.subject;
94
+ const textAttachments = email.text ? [{ name: "plain", type: "text/plain", content: email.text }] : [];
95
+ const htmlAttachments = email.html ? [{ type: "text/html", content: email.html, name: "email_body.html" }] : [];
96
+ const attachments = [...textAttachments, ...htmlAttachments];
97
+ const contacts = toContacts.flatMap((to) => {
98
+ const entries = [];
99
+ if (ccContacts.length > 0) {
100
+ ccContacts.forEach((cc) => {
101
+ entries.push({ email: cc?.email, name: cc?.name, cc: cc?.email });
102
+ });
103
+ }
104
+ if (bccContacts.length > 0) {
105
+ bccContacts.forEach((bcc) => {
106
+ entries.push({ email: bcc?.email, name: bcc?.name, bcc: bcc?.email });
107
+ });
108
+ }
109
+ return entries.length > 0 ? entries : [to];
110
+ });
111
+ return {
112
+ message: {
113
+ from_email: from?.email,
114
+ from_name: from.name || "",
115
+ subject,
116
+ attachments,
117
+ to: contacts,
118
+ text: email.text,
119
+ html: email.html,
120
+ reply_to: replyTo?.email,
121
+ template_id: email.templateId,
122
+ global_merge_vars: email.templateVariables ? Object.keys(email.templateVariables).map((key) => ({
123
+ name: key,
124
+ content: email.templateVariables[key]
125
+ })) : null
126
+ }
127
+ };
128
+ }
129
+ convertContacts(contacts) {
130
+ if (!contacts) {
131
+ return [];
132
+ }
133
+ const contactArray = Array.isArray(contacts) ? contacts : [contacts];
134
+ return contactArray.map(this.convertContact);
135
+ }
136
+ convertContact(contact) {
137
+ if (typeof contact === "string") {
138
+ return { email: contact, name: void 0 };
139
+ }
140
+ return { email: contact?.email, name: contact?.name };
141
+ }
142
+ }
143
+
144
+ const sendEmailInputSchema = z__default.object({
145
+ email: iEmailSchema,
146
+ metadata: z__default.object({
147
+ userAgent: z__default.string().optional(),
148
+ ip: z__default.ipv4().or(z__default.ipv6()).optional(),
149
+ initiator: z__default.object({
150
+ service: z__default.string(),
151
+ userId: z__default.string().optional()
152
+ })
153
+ })
154
+ });
155
+
156
+ const sendSlackInputSchema = v4.z.object({
157
+ slack: v4.z.object({
158
+ message: v4.z.string()
159
+ }),
160
+ metadata: v4.z.object({
161
+ userAgent: v4.z.string().optional(),
162
+ ip: v4.z.ipv4().or(v4.z.ipv6()).optional(),
163
+ initiator: v4.z.object({
164
+ service: v4.z.string(),
165
+ userId: v4.z.string().optional()
166
+ })
167
+ })
168
+ });
169
+
170
+ const sendSmsInputSchema = z.z.object({
171
+ sms: z.z.object({
172
+ message: z.z.string(),
173
+ to: z.z.string()
174
+ }),
175
+ metadata: z.z.object({
176
+ userAgent: z.z.string().optional(),
177
+ ip: z.z.ipv4().or(z.z.ipv6()).optional(),
178
+ initiator: z.z.object({
179
+ service: z.z.string(),
180
+ userId: z.z.string().optional()
181
+ })
182
+ })
183
+ });
184
+
185
+ class ISmsConnector {
186
+ static providerName;
187
+ ACCOUNT_ID;
188
+ AUTH_TOKEN;
189
+ SERVICE_ID;
190
+ constructor({
191
+ ACCOUNT_ID,
192
+ AUTH_TOKEN,
193
+ SERVICE_ID
194
+ }) {
195
+ this.ACCOUNT_ID = ACCOUNT_ID;
196
+ this.AUTH_TOKEN = AUTH_TOKEN;
197
+ this.SERVICE_ID = SERVICE_ID;
198
+ }
199
+ }
200
+
201
+ class TwilioConnector extends ISmsConnector {
202
+ static providerName = "twilio";
203
+ twilioClient;
204
+ constructor({
205
+ ACCOUNT_ID,
206
+ AUTH_TOKEN,
207
+ SERVICE_ID
208
+ }) {
209
+ super({ ACCOUNT_ID, AUTH_TOKEN, SERVICE_ID });
210
+ this.twilioClient = twilio__default(ACCOUNT_ID, AUTH_TOKEN);
211
+ }
212
+ async sendSms(sms) {
213
+ const message = await this.twilioClient.messages.create({
214
+ body: sms.message,
215
+ messagingServiceSid: this.SERVICE_ID,
216
+ to: sms.to
217
+ });
218
+ if (message.errorMessage)
219
+ return backendSdk.createInternalError(null, {
220
+ message: message.errorMessage,
221
+ status: message.errorCode
222
+ });
223
+ }
224
+ }
225
+
226
+ const auditLog = sqliteCore.sqliteTable("audit_log", {
227
+ ...backendSdk.base,
228
+ event: sqliteCore.text("event").$type().notNull(),
229
+ ip: sqliteCore.text("ip"),
230
+ userAgent: sqliteCore.text("user_agent"),
231
+ description: sqliteCore.text("description"),
232
+ initiatorService: sqliteCore.text("initiator_service").notNull(),
233
+ initiatorUserId: sqliteCore.text("initiator_user_id")
234
+ });
235
+
236
+ const schema = {
237
+ __proto__: null,
238
+ auditLog: auditLog
239
+ };
240
+
241
+ const tables = schema;
242
+
243
+ const initiateEmailConnector = async (provider, apiKey, smtpHost, sender) => {
244
+ const connector = [EcomailConnector].find(
245
+ (conn) => conn.providerName === provider
246
+ );
247
+ if (!connector)
248
+ throw backendSdk.createInternalError(null, {
249
+ message: "Unsupported email provider",
250
+ status: 404
251
+ });
252
+ return new connector({ API_KEY: apiKey, SMTP_HOST: smtpHost, SENDER: sender });
253
+ };
254
+
255
+ const createAuditLogCommand = async ({
256
+ db,
257
+ auditLog: {
258
+ event,
259
+ description,
260
+ initiatorService,
261
+ initiatorUserId,
262
+ ip,
263
+ userAgent
264
+ }
265
+ }) => {
266
+ const command = db.insert(tables.auditLog).values({
267
+ id: backendSdk.uuidv4(),
268
+ createdAt: /* @__PURE__ */ new Date(),
269
+ event,
270
+ description,
271
+ ip,
272
+ userAgent,
273
+ initiatorService,
274
+ initiatorUserId
275
+ });
276
+ return {
277
+ command
278
+ };
279
+ };
280
+
281
+ const initiateSmsConnector = async (provider, accountId, authToken, serviceId) => {
282
+ const connector = [TwilioConnector].find(
283
+ (conn) => conn.providerName === provider
284
+ );
285
+ if (!connector)
286
+ throw backendSdk.createInternalError(null, {
287
+ message: "Unsupported sms provider",
288
+ status: 404
289
+ });
290
+ return new connector({
291
+ ACCOUNT_ID: accountId,
292
+ AUTH_TOKEN: authToken,
293
+ SERVICE_ID: serviceId
294
+ });
295
+ };
296
+
297
+ class SlackConnector {
298
+ webhooks;
299
+ constructor(webhooks) {
300
+ this.webhooks = webhooks;
301
+ }
302
+ async sendNotificationToAllSlack(message) {
303
+ const controller = new AbortController();
304
+ const timeoutId = setTimeout(() => controller.abort(), 3e3);
305
+ for (const webhook of this.webhooks) {
306
+ let response = await fetch(webhook, {
307
+ method: "POST",
308
+ body: JSON.stringify({ text: message }),
309
+ headers: { "Content-Type": "application/json" },
310
+ signal: controller.signal
311
+ });
312
+ clearTimeout(timeoutId);
313
+ if (!response.ok) {
314
+ throw new Error("Failed sending Slack notification to " + message);
315
+ }
316
+ }
317
+ }
318
+ }
319
+
320
+ class NotificationServiceBase extends backendSdk.develitWorker(
321
+ cloudflare_workers.WorkerEntrypoint
322
+ ) {
323
+ name;
324
+ slackConnector = new SlackConnector(this.env.SLACK_WEBHOOKS);
325
+ emailConnector;
326
+ smsConnector;
327
+ db;
328
+ constructor(ctx, env) {
329
+ super(ctx, env);
330
+ this.name = "notification-service";
331
+ this.db = d1.drizzle(this.env.NOTIFICATION_D1, { schema: tables });
332
+ }
333
+ @backendSdk.cloudflareQueue({ baseDelay: 60 })
334
+ async queue(batch) {
335
+ for (const message of batch.messages) {
336
+ this.logInput({ message });
337
+ let notificationAction;
338
+ const { type, metadata, payload } = message.body;
339
+ if (type === "email") {
340
+ const [emailConnector, error2] = await backendSdk.useResult(
341
+ initiateEmailConnector(
342
+ this.env.EMAIL_PROVIDER,
343
+ this.env.EMAIL_API_KEY,
344
+ this.env.EMAIL_SMTP_HOST,
345
+ this.env.EMAIL_SENDER
346
+ )
347
+ );
348
+ if (error2) {
349
+ this.logError({ error: error2 });
350
+ message.retry();
351
+ continue;
352
+ }
353
+ this.emailConnector = emailConnector;
354
+ }
355
+ if (type === "sms") {
356
+ const [smsConnector, error2] = await backendSdk.useResult(
357
+ initiateSmsConnector(
358
+ this.env.SMS_PROVIDER,
359
+ this.env.SMS_ACCOUNT_ID,
360
+ this.env.SMS_AUTH_TOKEN,
361
+ this.env.SMS_SERVICE_ID
362
+ )
363
+ );
364
+ if (error2) {
365
+ this.logError({ error: error2 });
366
+ message.retry();
367
+ continue;
368
+ }
369
+ this.smsConnector = smsConnector;
370
+ }
371
+ switch (type) {
372
+ case "email":
373
+ notificationAction = async () => this._sendEmail({
374
+ email: payload.email,
375
+ metadata
376
+ });
377
+ break;
378
+ case "sms":
379
+ notificationAction = async () => this._sendSms({
380
+ sms: payload.sms,
381
+ metadata
382
+ });
383
+ break;
384
+ case "pushNotification":
385
+ notificationAction = async () => this._sendPushNotification();
386
+ break;
387
+ case "slack":
388
+ notificationAction = async () => this.sendSlackNotification({
389
+ slack: message.body.payload.slack,
390
+ metadata
391
+ });
392
+ break;
393
+ default:
394
+ this.logError({ error: `Unknown notification type: ${type}` });
395
+ message.retry();
396
+ continue;
397
+ }
398
+ const { error, message: errorMessage } = await notificationAction();
399
+ if (error) {
400
+ this.logError({ error: errorMessage });
401
+ message.retry();
402
+ continue;
403
+ }
404
+ message.ack();
405
+ }
406
+ }
407
+ @backendSdk.action("private-send-email")
408
+ async _sendEmail(input) {
409
+ return this.handleAction(
410
+ { data: input, schema: sendEmailInputSchema },
411
+ { successMessage: "Email sent." },
412
+ async (params) => {
413
+ const {
414
+ email,
415
+ metadata: {
416
+ ip,
417
+ userAgent,
418
+ initiator: { service, userId }
419
+ }
420
+ } = params;
421
+ if (!this.emailConnector)
422
+ this.emailConnector = await initiateEmailConnector(
423
+ this.env.EMAIL_PROVIDER,
424
+ this.env.EMAIL_API_KEY,
425
+ this.env.EMAIL_SMTP_HOST,
426
+ this.env.EMAIL_SENDER
427
+ );
428
+ const response = await this.emailConnector.sendEmail(email);
429
+ if (!response?.ok) {
430
+ throw backendSdk.createInternalError(null, {
431
+ message: `Could not send email: ${JSON.stringify(await response?.json())}`,
432
+ status: 500
433
+ });
434
+ }
435
+ const { command } = await createAuditLogCommand({
436
+ db: this.db,
437
+ auditLog: {
438
+ id: backendSdk.uuidv4(),
439
+ event: "EMAIL",
440
+ ip,
441
+ initiatorService: service,
442
+ initiatorUserId: userId,
443
+ userAgent,
444
+ description: JSON.stringify(input)
445
+ }
446
+ });
447
+ await this.db.batch([command]);
448
+ return {};
449
+ }
450
+ );
451
+ }
452
+ @backendSdk.action("private-send-sms")
453
+ async _sendSms(input) {
454
+ return this.handleAction(
455
+ { data: input, schema: sendSmsInputSchema },
456
+ { successMessage: "Sms sent." },
457
+ async (params) => {
458
+ const {
459
+ sms: { message, to },
460
+ metadata: {
461
+ ip,
462
+ userAgent,
463
+ initiator: { service, userId }
464
+ }
465
+ } = params;
466
+ await this.smsConnector.sendSms({ message, to });
467
+ const { command } = await createAuditLogCommand({
468
+ db: this.db,
469
+ auditLog: {
470
+ id: backendSdk.uuidv4(),
471
+ event: "SMS",
472
+ ip,
473
+ userAgent,
474
+ initiatorService: service,
475
+ initiatorUserId: userId,
476
+ description: JSON.stringify(input)
477
+ }
478
+ });
479
+ await this.db.batch([command]);
480
+ return {};
481
+ }
482
+ );
483
+ }
484
+ @backendSdk.action("public-send-email")
485
+ async sendEmail(input) {
486
+ return this.handleAction(
487
+ { data: input, schema: sendEmailInputSchema },
488
+ { successMessage: "Email sent." },
489
+ async (params) => {
490
+ const { email, metadata } = params;
491
+ await this.pushToQueue(
492
+ this.env.NOTIFICATIONS_QUEUE,
493
+ {
494
+ type: "email",
495
+ payload: {
496
+ email
497
+ },
498
+ metadata
499
+ }
500
+ );
501
+ return {};
502
+ }
503
+ );
504
+ }
505
+ @backendSdk.action("public-send-sms")
506
+ async sendSms(input) {
507
+ return this.handleAction(
508
+ { data: input, schema: sendSmsInputSchema },
509
+ { successMessage: "SMS sent." },
510
+ async (params) => {
511
+ const {
512
+ sms: { message, to },
513
+ metadata
514
+ } = params;
515
+ await this.pushToQueue(
516
+ this.env.NOTIFICATIONS_QUEUE,
517
+ {
518
+ type: "sms",
519
+ payload: {
520
+ sms: {
521
+ message,
522
+ to
523
+ }
524
+ },
525
+ metadata
526
+ }
527
+ );
528
+ return {};
529
+ }
530
+ );
531
+ }
532
+ @backendSdk.action("send-push-notification")
533
+ async _sendPushNotification() {
534
+ this.logInput({});
535
+ this.logError({ error: "Method not implemented." });
536
+ throw new Error("Method not implemented.");
537
+ }
538
+ @backendSdk.action("send-slack-notification")
539
+ async sendSlackNotification(input) {
540
+ return this.handleAction(
541
+ {
542
+ data: input,
543
+ schema: sendSlackInputSchema
544
+ },
545
+ { successMessage: "Slack sent." },
546
+ async (params) => {
547
+ const { slack } = params;
548
+ await this.slackConnector.sendNotificationToAllSlack(slack.message);
549
+ return {};
550
+ }
551
+ );
552
+ }
553
+ }
554
+ function defineNotificationService() {
555
+ return class NotificationService extends NotificationServiceBase {
556
+ constructor(ctx, env) {
557
+ super(ctx, env);
558
+ }
559
+ };
560
+ }
561
+
562
+ function defineNotificationServiceWrangler(config) {
563
+ const { name, envs } = config;
564
+ const base = {
565
+ name,
566
+ main: "./src/index.ts",
567
+ compatibility_date: "2025-06-04",
568
+ compatibility_flags: ["nodejs_compat"],
569
+ d1_databases: [
570
+ {
571
+ binding: "NOTIFICATION_D1",
572
+ database_name: name,
573
+ database_id: envs.local.d1.id,
574
+ migrations_dir: "./src/database/migrations"
575
+ }
576
+ ],
577
+ queues: {
578
+ producers: [
579
+ {
580
+ binding: "NOTIFICATIONS_QUEUE",
581
+ queue: `${name}`
582
+ }
583
+ ],
584
+ consumers: [
585
+ {
586
+ queue: `${name}`,
587
+ max_batch_size: 1,
588
+ max_batch_timeout: 5,
589
+ dead_letter_queue: `${name}-dlq`
590
+ }
591
+ ]
592
+ },
593
+ env: {}
594
+ };
595
+ for (const [envName, envCfg] of Object.entries(envs).filter(
596
+ ([key]) => key !== "local"
597
+ )) {
598
+ base.env[envName] = {
599
+ vars: {
600
+ ...envCfg.vars,
601
+ ENVIRONMENT: envName
602
+ },
603
+ d1_databases: [
604
+ {
605
+ binding: "NOTIFICATION_D1",
606
+ database_name: `${name}-${envName}`,
607
+ database_id: envCfg.d1.id,
608
+ migrations_dir: "./src/database/migrations"
609
+ }
610
+ ],
611
+ queues: {
612
+ producers: [
613
+ {
614
+ binding: "NOTIFICATIONS_QUEUE",
615
+ queue: `${name}-${envName}`
616
+ }
617
+ ],
618
+ consumers: [
619
+ {
620
+ queue: `${name}-${envName}`,
621
+ max_batch_size: envCfg.queue?.max_batch_size ?? 1,
622
+ max_batch_timeout: envCfg.queue?.max_batch_timeout ?? 5,
623
+ dead_letter_queue: `${name}-dlq-${envName}`
624
+ }
625
+ ]
626
+ }
627
+ };
628
+ }
629
+ return base;
630
+ }
631
+
632
+ const NotificationService = defineNotificationService();
633
+
634
+ exports.default = NotificationService;
635
+ exports.defineNotificationService = defineNotificationService;
636
+ exports.defineNotificationServiceWrangler = defineNotificationServiceWrangler;