@application-kit/mailer 0.0.0-stage → 0.1.0

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 (177) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +74 -2
  4. package/dist/cjs/application/errors/index.d.ts +1 -0
  5. package/dist/cjs/application/errors/index.js +17 -0
  6. package/dist/cjs/application/errors/send-email.error.d.ts +8 -0
  7. package/dist/cjs/application/errors/send-email.error.js +12 -0
  8. package/dist/cjs/application/index.d.ts +3 -0
  9. package/dist/cjs/application/index.js +19 -0
  10. package/dist/cjs/application/ports/email-sender.interface.d.ts +14 -0
  11. package/dist/cjs/application/ports/email-sender.interface.js +2 -0
  12. package/dist/cjs/application/ports/email-subscriptions.interface.d.ts +10 -0
  13. package/dist/cjs/application/ports/email-subscriptions.interface.js +2 -0
  14. package/dist/cjs/application/ports/email-template-catalog.interface.d.ts +8 -0
  15. package/dist/cjs/application/ports/email-template-catalog.interface.js +2 -0
  16. package/dist/cjs/application/ports/email-template-renderer.interface.d.ts +9 -0
  17. package/dist/cjs/application/ports/email-template-renderer.interface.js +2 -0
  18. package/dist/cjs/application/ports/index.d.ts +7 -0
  19. package/dist/cjs/application/ports/index.js +23 -0
  20. package/dist/cjs/application/ports/mailer-logger.interface.d.ts +5 -0
  21. package/dist/cjs/application/ports/mailer-logger.interface.js +2 -0
  22. package/dist/cjs/application/ports/mailer.interface.d.ts +9 -0
  23. package/dist/cjs/application/ports/mailer.interface.js +2 -0
  24. package/dist/cjs/application/ports/sent-email-log.interface.d.ts +4 -0
  25. package/dist/cjs/application/ports/sent-email-log.interface.js +2 -0
  26. package/dist/cjs/application/services/email-dispatcher.service.d.ts +12 -0
  27. package/dist/cjs/application/services/email-dispatcher.service.js +56 -0
  28. package/dist/cjs/application/services/email-idempotency-key.service.d.ts +4 -0
  29. package/dist/cjs/application/services/email-idempotency-key.service.js +24 -0
  30. package/dist/cjs/application/services/email-sender.service.d.ts +17 -0
  31. package/dist/cjs/application/services/email-sender.service.js +69 -0
  32. package/dist/cjs/application/services/index.d.ts +3 -0
  33. package/dist/cjs/application/services/index.js +19 -0
  34. package/dist/cjs/configs/envs/index.d.ts +1 -0
  35. package/dist/cjs/configs/envs/index.js +17 -0
  36. package/dist/cjs/configs/envs/mailer.environment.d.ts +7 -0
  37. package/dist/cjs/configs/envs/mailer.environment.js +48 -0
  38. package/dist/cjs/configs/index.d.ts +2 -0
  39. package/dist/cjs/configs/index.js +18 -0
  40. package/dist/cjs/configs/mailer.config.d.ts +15 -0
  41. package/dist/cjs/configs/mailer.config.js +41 -0
  42. package/dist/cjs/constants.d.ts +7 -0
  43. package/dist/cjs/constants.js +10 -0
  44. package/dist/cjs/index.d.ts +6 -0
  45. package/dist/cjs/index.js +22 -0
  46. package/dist/cjs/infrastructure/adapters/index.d.ts +3 -0
  47. package/dist/cjs/infrastructure/adapters/index.js +19 -0
  48. package/dist/cjs/infrastructure/adapters/interpolating-email-template-renderer.d.ts +7 -0
  49. package/dist/cjs/infrastructure/adapters/interpolating-email-template-renderer.js +52 -0
  50. package/dist/cjs/infrastructure/adapters/json-mailer.adapter.d.ts +7 -0
  51. package/dist/cjs/infrastructure/adapters/json-mailer.adapter.js +28 -0
  52. package/dist/cjs/infrastructure/adapters/smtp-mailer.adapter.d.ts +8 -0
  53. package/dist/cjs/infrastructure/adapters/smtp-mailer.adapter.js +44 -0
  54. package/dist/cjs/infrastructure/database/index.d.ts +1 -0
  55. package/dist/cjs/infrastructure/database/index.js +17 -0
  56. package/dist/cjs/infrastructure/database/typeorm/entities/index.d.ts +1 -0
  57. package/dist/cjs/infrastructure/database/typeorm/entities/index.js +17 -0
  58. package/dist/cjs/infrastructure/database/typeorm/entities/sent-email.entity.d.ts +4 -0
  59. package/dist/cjs/infrastructure/database/typeorm/entities/sent-email.entity.js +30 -0
  60. package/dist/cjs/infrastructure/database/typeorm/index.d.ts +3 -0
  61. package/dist/cjs/infrastructure/database/typeorm/index.js +19 -0
  62. package/dist/cjs/infrastructure/database/typeorm/migrations/add-sent-emails.migration.d.ts +6 -0
  63. package/dist/cjs/infrastructure/database/typeorm/migrations/add-sent-emails.migration.js +13 -0
  64. package/dist/cjs/infrastructure/database/typeorm/migrations/index.d.ts +1 -0
  65. package/dist/cjs/infrastructure/database/typeorm/migrations/index.js +17 -0
  66. package/dist/cjs/infrastructure/database/typeorm/repositories/index.d.ts +1 -0
  67. package/dist/cjs/infrastructure/database/typeorm/repositories/index.js +17 -0
  68. package/dist/cjs/infrastructure/database/typeorm/repositories/sent-email-log.repository.d.ts +9 -0
  69. package/dist/cjs/infrastructure/database/typeorm/repositories/sent-email-log.repository.js +44 -0
  70. package/dist/cjs/infrastructure/index.d.ts +2 -0
  71. package/dist/cjs/infrastructure/index.js +18 -0
  72. package/dist/cjs/mailer-logger.d.ts +5 -0
  73. package/dist/cjs/mailer-logger.js +59 -0
  74. package/dist/cjs/mailer.module.d.ts +9 -0
  75. package/dist/cjs/mailer.module.js +55 -0
  76. package/dist/cjs/package.json +3 -0
  77. package/dist/cjs/testing/email-sender.fake.d.ts +7 -0
  78. package/dist/cjs/testing/email-sender.fake.js +15 -0
  79. package/dist/cjs/testing/email-subscriptions.fake.d.ts +6 -0
  80. package/dist/cjs/testing/email-subscriptions.fake.js +15 -0
  81. package/dist/cjs/testing/email-template-catalog.fake.d.ts +11 -0
  82. package/dist/cjs/testing/email-template-catalog.fake.js +15 -0
  83. package/dist/cjs/testing/email-template-renderer.fake.d.ts +12 -0
  84. package/dist/cjs/testing/email-template-renderer.fake.js +19 -0
  85. package/dist/cjs/testing/index.d.ts +6 -0
  86. package/dist/cjs/testing/index.js +22 -0
  87. package/dist/cjs/testing/mailer.fake.d.ts +8 -0
  88. package/dist/cjs/testing/mailer.fake.js +21 -0
  89. package/dist/cjs/testing/sent-email-log.fake.d.ts +8 -0
  90. package/dist/cjs/testing/sent-email-log.fake.js +26 -0
  91. package/dist/esm/application/errors/index.d.ts +1 -0
  92. package/dist/esm/application/errors/index.js +1 -0
  93. package/dist/esm/application/errors/send-email.error.d.ts +8 -0
  94. package/dist/esm/application/errors/send-email.error.js +8 -0
  95. package/dist/esm/application/index.d.ts +3 -0
  96. package/dist/esm/application/index.js +3 -0
  97. package/dist/esm/application/ports/email-sender.interface.d.ts +14 -0
  98. package/dist/esm/application/ports/email-sender.interface.js +1 -0
  99. package/dist/esm/application/ports/email-subscriptions.interface.d.ts +10 -0
  100. package/dist/esm/application/ports/email-subscriptions.interface.js +1 -0
  101. package/dist/esm/application/ports/email-template-catalog.interface.d.ts +8 -0
  102. package/dist/esm/application/ports/email-template-catalog.interface.js +1 -0
  103. package/dist/esm/application/ports/email-template-renderer.interface.d.ts +9 -0
  104. package/dist/esm/application/ports/email-template-renderer.interface.js +1 -0
  105. package/dist/esm/application/ports/index.d.ts +7 -0
  106. package/dist/esm/application/ports/index.js +7 -0
  107. package/dist/esm/application/ports/mailer-logger.interface.d.ts +5 -0
  108. package/dist/esm/application/ports/mailer-logger.interface.js +1 -0
  109. package/dist/esm/application/ports/mailer.interface.d.ts +9 -0
  110. package/dist/esm/application/ports/mailer.interface.js +1 -0
  111. package/dist/esm/application/ports/sent-email-log.interface.d.ts +4 -0
  112. package/dist/esm/application/ports/sent-email-log.interface.js +1 -0
  113. package/dist/esm/application/services/email-dispatcher.service.d.ts +12 -0
  114. package/dist/esm/application/services/email-dispatcher.service.js +53 -0
  115. package/dist/esm/application/services/email-idempotency-key.service.d.ts +4 -0
  116. package/dist/esm/application/services/email-idempotency-key.service.js +21 -0
  117. package/dist/esm/application/services/email-sender.service.d.ts +17 -0
  118. package/dist/esm/application/services/email-sender.service.js +66 -0
  119. package/dist/esm/application/services/index.d.ts +3 -0
  120. package/dist/esm/application/services/index.js +3 -0
  121. package/dist/esm/configs/envs/index.d.ts +1 -0
  122. package/dist/esm/configs/envs/index.js +1 -0
  123. package/dist/esm/configs/envs/mailer.environment.d.ts +7 -0
  124. package/dist/esm/configs/envs/mailer.environment.js +44 -0
  125. package/dist/esm/configs/index.d.ts +2 -0
  126. package/dist/esm/configs/index.js +2 -0
  127. package/dist/esm/configs/mailer.config.d.ts +15 -0
  128. package/dist/esm/configs/mailer.config.js +38 -0
  129. package/dist/esm/constants.d.ts +7 -0
  130. package/dist/esm/constants.js +7 -0
  131. package/dist/esm/index.d.ts +6 -0
  132. package/dist/esm/index.js +6 -0
  133. package/dist/esm/infrastructure/adapters/index.d.ts +3 -0
  134. package/dist/esm/infrastructure/adapters/index.js +3 -0
  135. package/dist/esm/infrastructure/adapters/interpolating-email-template-renderer.d.ts +7 -0
  136. package/dist/esm/infrastructure/adapters/interpolating-email-template-renderer.js +49 -0
  137. package/dist/esm/infrastructure/adapters/json-mailer.adapter.d.ts +7 -0
  138. package/dist/esm/infrastructure/adapters/json-mailer.adapter.js +25 -0
  139. package/dist/esm/infrastructure/adapters/smtp-mailer.adapter.d.ts +8 -0
  140. package/dist/esm/infrastructure/adapters/smtp-mailer.adapter.js +41 -0
  141. package/dist/esm/infrastructure/database/index.d.ts +1 -0
  142. package/dist/esm/infrastructure/database/index.js +1 -0
  143. package/dist/esm/infrastructure/database/typeorm/entities/index.d.ts +1 -0
  144. package/dist/esm/infrastructure/database/typeorm/entities/index.js +1 -0
  145. package/dist/esm/infrastructure/database/typeorm/entities/sent-email.entity.d.ts +4 -0
  146. package/dist/esm/infrastructure/database/typeorm/entities/sent-email.entity.js +27 -0
  147. package/dist/esm/infrastructure/database/typeorm/index.d.ts +3 -0
  148. package/dist/esm/infrastructure/database/typeorm/index.js +3 -0
  149. package/dist/esm/infrastructure/database/typeorm/migrations/add-sent-emails.migration.d.ts +6 -0
  150. package/dist/esm/infrastructure/database/typeorm/migrations/add-sent-emails.migration.js +9 -0
  151. package/dist/esm/infrastructure/database/typeorm/migrations/index.d.ts +1 -0
  152. package/dist/esm/infrastructure/database/typeorm/migrations/index.js +1 -0
  153. package/dist/esm/infrastructure/database/typeorm/repositories/index.d.ts +1 -0
  154. package/dist/esm/infrastructure/database/typeorm/repositories/index.js +1 -0
  155. package/dist/esm/infrastructure/database/typeorm/repositories/sent-email-log.repository.d.ts +9 -0
  156. package/dist/esm/infrastructure/database/typeorm/repositories/sent-email-log.repository.js +41 -0
  157. package/dist/esm/infrastructure/index.d.ts +2 -0
  158. package/dist/esm/infrastructure/index.js +2 -0
  159. package/dist/esm/mailer-logger.d.ts +5 -0
  160. package/dist/esm/mailer-logger.js +23 -0
  161. package/dist/esm/mailer.module.d.ts +9 -0
  162. package/dist/esm/mailer.module.js +52 -0
  163. package/dist/esm/testing/email-sender.fake.d.ts +7 -0
  164. package/dist/esm/testing/email-sender.fake.js +11 -0
  165. package/dist/esm/testing/email-subscriptions.fake.d.ts +6 -0
  166. package/dist/esm/testing/email-subscriptions.fake.js +11 -0
  167. package/dist/esm/testing/email-template-catalog.fake.d.ts +11 -0
  168. package/dist/esm/testing/email-template-catalog.fake.js +11 -0
  169. package/dist/esm/testing/email-template-renderer.fake.d.ts +12 -0
  170. package/dist/esm/testing/email-template-renderer.fake.js +15 -0
  171. package/dist/esm/testing/index.d.ts +6 -0
  172. package/dist/esm/testing/index.js +6 -0
  173. package/dist/esm/testing/mailer.fake.d.ts +8 -0
  174. package/dist/esm/testing/mailer.fake.js +17 -0
  175. package/dist/esm/testing/sent-email-log.fake.d.ts +8 -0
  176. package/dist/esm/testing/sent-email-log.fake.js +22 -0
  177. package/package.json +96 -3
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.MailerModule = void 0;
10
+ const nestjs_config_module_1 = require("@libs-for-dev/nestjs-config-module");
11
+ const common_1 = require("@nestjs/common");
12
+ const typeorm_1 = require("@nestjs/typeorm");
13
+ const email_dispatcher_service_js_1 = require("./application/services/email-dispatcher.service.js");
14
+ const email_idempotency_key_service_js_1 = require("./application/services/email-idempotency-key.service.js");
15
+ const email_sender_service_js_1 = require("./application/services/email-sender.service.js");
16
+ const mailer_environment_js_1 = require("./configs/envs/mailer.environment.js");
17
+ const mailer_config_js_1 = require("./configs/mailer.config.js");
18
+ const constants_js_1 = require("./constants.js");
19
+ const interpolating_email_template_renderer_js_1 = require("./infrastructure/adapters/interpolating-email-template-renderer.js");
20
+ const json_mailer_adapter_js_1 = require("./infrastructure/adapters/json-mailer.adapter.js");
21
+ const smtp_mailer_adapter_js_1 = require("./infrastructure/adapters/smtp-mailer.adapter.js");
22
+ const sent_email_entity_js_1 = require("./infrastructure/database/typeorm/entities/sent-email.entity.js");
23
+ const sent_email_log_repository_js_1 = require("./infrastructure/database/typeorm/repositories/sent-email-log.repository.js");
24
+ const selectMailer = (config, logger) => (config.isSmtpConfigured
25
+ ? new smtp_mailer_adapter_js_1.SmtpMailerAdapter(config)
26
+ : new json_mailer_adapter_js_1.JsonMailerAdapter(logger));
27
+ const mailerByConfig = {
28
+ inject: [mailer_config_js_1.MailerConfig, constants_js_1.MAILER_LOGGER],
29
+ provide: constants_js_1.MAILER_PORT,
30
+ useFactory: selectMailer,
31
+ };
32
+ let MailerModule = class MailerModule {
33
+ static register(options) {
34
+ return {
35
+ imports: [nestjs_config_module_1.ConfigsModule.forFeature([mailer_config_js_1.MailerConfig], [mailer_environment_js_1.MailerEnvironment]), options.subscriptions],
36
+ module: this,
37
+ providers: [{ provide: constants_js_1.EMAIL_TEMPLATE_CATALOG_PORT, useClass: options.templates }],
38
+ };
39
+ }
40
+ };
41
+ exports.MailerModule = MailerModule;
42
+ exports.MailerModule = MailerModule = __decorate([
43
+ (0, common_1.Module)({
44
+ exports: [email_dispatcher_service_js_1.EmailDispatcherService],
45
+ imports: [typeorm_1.TypeOrmModule.forFeature([sent_email_entity_js_1.SentEmailEntity])],
46
+ providers: [
47
+ { provide: constants_js_1.EMAIL_SENDER_PORT, useClass: email_sender_service_js_1.EmailSenderService },
48
+ { provide: constants_js_1.EMAIL_TEMPLATE_RENDERER_PORT, useClass: interpolating_email_template_renderer_js_1.InterpolatingEmailTemplateRenderer },
49
+ { provide: constants_js_1.SENT_EMAIL_LOG_PORT, useClass: sent_email_log_repository_js_1.SentEmailLogRepository },
50
+ email_dispatcher_service_js_1.EmailDispatcherService,
51
+ email_idempotency_key_service_js_1.EmailIdempotencyKeyService,
52
+ mailerByConfig,
53
+ ],
54
+ })
55
+ ], MailerModule);
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,7 @@
1
+ import type { EmailSenderInterface, SendEmailRequestInterface, SendEmailResult } from '../application/ports/email-sender.interface.js';
2
+ export declare class FakeEmailSender implements EmailSenderInterface {
3
+ result: SendEmailResult;
4
+ readonly resultByTemplate: Map<string, SendEmailResult>;
5
+ readonly sent: SendEmailRequestInterface[];
6
+ send(request: SendEmailRequestInterface): Promise<SendEmailResult>;
7
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FakeEmailSender = void 0;
4
+ const oxide_ts_1 = require("oxide.ts");
5
+ class FakeEmailSender {
6
+ result = (0, oxide_ts_1.Ok)(true);
7
+ resultByTemplate = new Map();
8
+ sent = [];
9
+ async send(request) {
10
+ await Promise.resolve();
11
+ this.sent.push(request);
12
+ return this.resultByTemplate.get(request.template) ?? this.result;
13
+ }
14
+ }
15
+ exports.FakeEmailSender = FakeEmailSender;
@@ -0,0 +1,6 @@
1
+ import type { EmailSubscriptionInterface, EmailSubscriptionsInterface } from '../application/ports/email-subscriptions.interface.js';
2
+ export declare class FakeEmailSubscriptions implements EmailSubscriptionsInterface {
3
+ private readonly unsubscribed;
4
+ isSubscribed(subscription: EmailSubscriptionInterface): Promise<boolean>;
5
+ unsubscribe(subscription: EmailSubscriptionInterface): void;
6
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FakeEmailSubscriptions = void 0;
4
+ const keyOf = ({ accountId, category }) => `${accountId}:${category}`;
5
+ class FakeEmailSubscriptions {
6
+ unsubscribed = new Set();
7
+ async isSubscribed(subscription) {
8
+ await Promise.resolve();
9
+ return !this.unsubscribed.has(keyOf(subscription));
10
+ }
11
+ unsubscribe(subscription) {
12
+ this.unsubscribed.add(keyOf(subscription));
13
+ }
14
+ }
15
+ exports.FakeEmailSubscriptions = FakeEmailSubscriptions;
@@ -0,0 +1,11 @@
1
+ import type { EmailTemplateCatalogInterface, EmailTemplateInterface } from '../application/ports/email-template-catalog.interface.js';
2
+ export interface EmailTemplateRequestInterface {
3
+ readonly locale: string;
4
+ readonly name: string;
5
+ }
6
+ export declare class FakeEmailTemplateCatalog implements EmailTemplateCatalogInterface {
7
+ readonly requests: EmailTemplateRequestInterface[];
8
+ private template;
9
+ get(name: string, locale: string): EmailTemplateInterface;
10
+ returns(template: EmailTemplateInterface): void;
11
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FakeEmailTemplateCatalog = void 0;
4
+ class FakeEmailTemplateCatalog {
5
+ requests = [];
6
+ template = { html: '<p>{name}</p>', subject: 'subject', text: '{name}' };
7
+ get(name, locale) {
8
+ this.requests.push({ locale, name });
9
+ return this.template;
10
+ }
11
+ returns(template) {
12
+ this.template = template;
13
+ }
14
+ }
15
+ exports.FakeEmailTemplateCatalog = FakeEmailTemplateCatalog;
@@ -0,0 +1,12 @@
1
+ import type { EmailTemplateParameters, EmailTemplateRendererInterface, RenderedEmailInterface } from '../application/ports/email-template-renderer.interface.js';
2
+ export interface EmailRenderRequestInterface {
3
+ readonly locale: string;
4
+ readonly name: string;
5
+ readonly parameters: EmailTemplateParameters;
6
+ }
7
+ export declare class FakeEmailTemplateRenderer implements EmailTemplateRendererInterface {
8
+ readonly requests: EmailRenderRequestInterface[];
9
+ private rendered;
10
+ render(name: string, locale: string, parameters: EmailTemplateParameters): RenderedEmailInterface;
11
+ returns(rendered: RenderedEmailInterface): void;
12
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FakeEmailTemplateRenderer = void 0;
4
+ class FakeEmailTemplateRenderer {
5
+ requests = [];
6
+ rendered = {
7
+ html: '<p>rendered html</p>',
8
+ subject: 'rendered subject',
9
+ text: 'rendered text',
10
+ };
11
+ render(name, locale, parameters) {
12
+ this.requests.push({ locale, name, parameters });
13
+ return this.rendered;
14
+ }
15
+ returns(rendered) {
16
+ this.rendered = rendered;
17
+ }
18
+ }
19
+ exports.FakeEmailTemplateRenderer = FakeEmailTemplateRenderer;
@@ -0,0 +1,6 @@
1
+ export * from './email-sender.fake.js';
2
+ export * from './email-subscriptions.fake.js';
3
+ export * from './email-template-catalog.fake.js';
4
+ export * from './email-template-renderer.fake.js';
5
+ export * from './mailer.fake.js';
6
+ export * from './sent-email-log.fake.js';
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./email-sender.fake.js"), exports);
18
+ __exportStar(require("./email-subscriptions.fake.js"), exports);
19
+ __exportStar(require("./email-template-catalog.fake.js"), exports);
20
+ __exportStar(require("./email-template-renderer.fake.js"), exports);
21
+ __exportStar(require("./mailer.fake.js"), exports);
22
+ __exportStar(require("./sent-email-log.fake.js"), exports);
@@ -0,0 +1,8 @@
1
+ import type { MailerInterface, MailerMessageInterface } from '../application/ports/mailer.interface.js';
2
+ export declare class FakeMailer implements MailerInterface {
3
+ readonly sent: MailerMessageInterface[];
4
+ private failure;
5
+ failWith(error: Error): void;
6
+ recover(): void;
7
+ send(message: MailerMessageInterface): Promise<void>;
8
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FakeMailer = void 0;
4
+ class FakeMailer {
5
+ sent = [];
6
+ failure = undefined;
7
+ failWith(error) {
8
+ this.failure = error;
9
+ }
10
+ recover() {
11
+ this.failure = undefined;
12
+ }
13
+ async send(message) {
14
+ await Promise.resolve();
15
+ if (this.failure !== undefined) {
16
+ throw this.failure;
17
+ }
18
+ this.sent.push(message);
19
+ }
20
+ }
21
+ exports.FakeMailer = FakeMailer;
@@ -0,0 +1,8 @@
1
+ import type { SentEmailLogInterface } from '../application/ports/sent-email-log.interface.js';
2
+ export declare class FakeSentEmailLog implements SentEmailLogInterface {
3
+ readonly claimed: Set<string>;
4
+ private failure;
5
+ claim(key: string): Promise<boolean>;
6
+ failWith(error: Error): void;
7
+ release(key: string): Promise<void>;
8
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FakeSentEmailLog = void 0;
4
+ class FakeSentEmailLog {
5
+ claimed = new Set();
6
+ failure = undefined;
7
+ async claim(key) {
8
+ await Promise.resolve();
9
+ if (this.failure !== undefined) {
10
+ throw this.failure;
11
+ }
12
+ if (this.claimed.has(key)) {
13
+ return false;
14
+ }
15
+ this.claimed.add(key);
16
+ return true;
17
+ }
18
+ failWith(error) {
19
+ this.failure = error;
20
+ }
21
+ async release(key) {
22
+ await Promise.resolve();
23
+ this.claimed.delete(key);
24
+ }
25
+ }
26
+ exports.FakeSentEmailLog = FakeSentEmailLog;
@@ -0,0 +1 @@
1
+ export * from './send-email.error.js';
@@ -0,0 +1 @@
1
+ export * from './send-email.error.js';
@@ -0,0 +1,8 @@
1
+ export interface SendEmailErrorContextInterface {
2
+ locale: string;
3
+ template: string;
4
+ }
5
+ export declare class SendEmailError extends Error {
6
+ readonly context: SendEmailErrorContextInterface;
7
+ constructor(context: SendEmailErrorContextInterface, options?: ErrorOptions);
8
+ }
@@ -0,0 +1,8 @@
1
+ export class SendEmailError extends Error {
2
+ context;
3
+ constructor(context, options) {
4
+ super(`Email delivery failed for template "${context.template}"`, options);
5
+ this.name = 'SendEmailError';
6
+ this.context = context;
7
+ }
8
+ }
@@ -0,0 +1,3 @@
1
+ export * from './errors/index.js';
2
+ export * from './ports/index.js';
3
+ export * from './services/index.js';
@@ -0,0 +1,3 @@
1
+ export * from './errors/index.js';
2
+ export * from './ports/index.js';
3
+ export * from './services/index.js';
@@ -0,0 +1,14 @@
1
+ import type { Result } from 'oxide.ts';
2
+ import type { SendEmailError } from '../errors/send-email.error.js';
3
+ import type { EmailTemplateParameters } from './email-template-renderer.interface.js';
4
+ export interface EmailSenderInterface {
5
+ send(request: SendEmailRequestInterface): Promise<SendEmailResult>;
6
+ }
7
+ export interface SendEmailRequestInterface {
8
+ readonly idempotencyMaterial: readonly string[];
9
+ readonly locale: string;
10
+ readonly parameters: EmailTemplateParameters;
11
+ readonly template: string;
12
+ readonly to: string;
13
+ }
14
+ export type SendEmailResult = Result<true, SendEmailError>;
@@ -0,0 +1,10 @@
1
+ export type EmailCategory = keyof EmailCategoryRegistryInterface;
2
+ export interface EmailCategoryRegistryInterface {
3
+ }
4
+ export interface EmailSubscriptionInterface {
5
+ readonly accountId: string;
6
+ readonly category: EmailCategory;
7
+ }
8
+ export interface EmailSubscriptionsInterface {
9
+ isSubscribed(subscription: EmailSubscriptionInterface): Promise<boolean>;
10
+ }
@@ -0,0 +1,8 @@
1
+ export interface EmailTemplateCatalogInterface {
2
+ get(name: string, locale: string): EmailTemplateInterface;
3
+ }
4
+ export interface EmailTemplateInterface {
5
+ readonly html: string;
6
+ readonly subject: string;
7
+ readonly text: string;
8
+ }
@@ -0,0 +1,9 @@
1
+ export type EmailTemplateParameters = Readonly<Record<string, string>>;
2
+ export interface EmailTemplateRendererInterface {
3
+ render(name: string, locale: string, parameters: EmailTemplateParameters): RenderedEmailInterface;
4
+ }
5
+ export interface RenderedEmailInterface {
6
+ readonly html: string;
7
+ readonly subject: string;
8
+ readonly text: string;
9
+ }
@@ -0,0 +1,7 @@
1
+ export * from './email-sender.interface.js';
2
+ export * from './email-subscriptions.interface.js';
3
+ export * from './email-template-catalog.interface.js';
4
+ export * from './email-template-renderer.interface.js';
5
+ export * from './mailer-logger.interface.js';
6
+ export * from './mailer.interface.js';
7
+ export * from './sent-email-log.interface.js';
@@ -0,0 +1,7 @@
1
+ export * from './email-sender.interface.js';
2
+ export * from './email-subscriptions.interface.js';
3
+ export * from './email-template-catalog.interface.js';
4
+ export * from './email-template-renderer.interface.js';
5
+ export * from './mailer-logger.interface.js';
6
+ export * from './mailer.interface.js';
7
+ export * from './sent-email-log.interface.js';
@@ -0,0 +1,5 @@
1
+ export interface MailerLoggerInterface {
2
+ error(context: unknown, message: string): void;
3
+ info(context: unknown, message: string): void;
4
+ warn(context: unknown, message: string): void;
5
+ }
@@ -0,0 +1,9 @@
1
+ export interface MailerInterface {
2
+ send(message: MailerMessageInterface): Promise<void>;
3
+ }
4
+ export interface MailerMessageInterface {
5
+ readonly html: string;
6
+ readonly subject: string;
7
+ readonly text: string;
8
+ readonly to: string;
9
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export interface SentEmailLogInterface {
2
+ claim(key: string): Promise<boolean>;
3
+ release(key: string): Promise<void>;
4
+ }
@@ -0,0 +1,12 @@
1
+ import type { EmailSenderInterface, SendEmailRequestInterface } from '../ports/email-sender.interface.js';
2
+ import type { EmailSubscriptionInterface, EmailSubscriptionsInterface } from '../ports/email-subscriptions.interface.js';
3
+ import type { MailerLoggerInterface } from '../ports/mailer-logger.interface.js';
4
+ export declare class EmailDispatcherService {
5
+ private readonly emails;
6
+ private readonly logger;
7
+ private readonly subscriptions;
8
+ constructor(emails: EmailSenderInterface, logger: MailerLoggerInterface, subscriptions: EmailSubscriptionsInterface);
9
+ sendIfSubscribed(subscription: EmailSubscriptionInterface, request: SendEmailRequestInterface, failureMessage: string): Promise<void>;
10
+ sendOrLog(request: SendEmailRequestInterface, failureMessage: string): Promise<void>;
11
+ sendOrRethrow(request: SendEmailRequestInterface, failureMessage: string): Promise<void>;
12
+ }
@@ -0,0 +1,53 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Inject, Injectable } from '@nestjs/common';
14
+ import { EMAIL_SENDER_PORT, EMAIL_SUBSCRIPTIONS_PORT, MAILER_LOGGER } from '../../constants.js';
15
+ let EmailDispatcherService = class EmailDispatcherService {
16
+ emails;
17
+ logger;
18
+ subscriptions;
19
+ constructor(emails, logger, subscriptions) {
20
+ this.emails = emails;
21
+ this.logger = logger;
22
+ this.subscriptions = subscriptions;
23
+ }
24
+ async sendIfSubscribed(subscription, request, failureMessage) {
25
+ if (!await this.subscriptions.isSubscribed(subscription)) {
26
+ return;
27
+ }
28
+ await this.sendOrLog(request, failureMessage);
29
+ }
30
+ async sendOrLog(request, failureMessage) {
31
+ const result = await this.emails.send(request);
32
+ if (result.isErr()) {
33
+ this.logger.error({ err: result.unwrapErr() }, failureMessage);
34
+ }
35
+ }
36
+ async sendOrRethrow(request, failureMessage) {
37
+ const result = await this.emails.send(request);
38
+ if (result.isOk()) {
39
+ return;
40
+ }
41
+ const error = result.unwrapErr();
42
+ this.logger.error({ err: error }, failureMessage);
43
+ throw error;
44
+ }
45
+ };
46
+ EmailDispatcherService = __decorate([
47
+ Injectable(),
48
+ __param(0, Inject(EMAIL_SENDER_PORT)),
49
+ __param(1, Inject(MAILER_LOGGER)),
50
+ __param(2, Inject(EMAIL_SUBSCRIPTIONS_PORT)),
51
+ __metadata("design:paramtypes", [Object, Object, Object])
52
+ ], EmailDispatcherService);
53
+ export { EmailDispatcherService };
@@ -0,0 +1,4 @@
1
+ export declare const EMAIL_IDEMPOTENCY_KEY_MAX_LENGTH = 128;
2
+ export declare class EmailIdempotencyKeyService {
3
+ build(template: string, ...material: string[]): string;
4
+ }
@@ -0,0 +1,21 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Injectable } from '@nestjs/common';
8
+ import { createHash } from 'node:crypto';
9
+ export const EMAIL_IDEMPOTENCY_KEY_MAX_LENGTH = 128;
10
+ const MATERIAL_SEPARATOR = String.fromCodePoint(0);
11
+ let EmailIdempotencyKeyService = class EmailIdempotencyKeyService {
12
+ build(template, ...material) {
13
+ const digest = createHash('sha256').update(material.join(MATERIAL_SEPARATOR))
14
+ .digest('hex');
15
+ return `${template}:${digest}`;
16
+ }
17
+ };
18
+ EmailIdempotencyKeyService = __decorate([
19
+ Injectable()
20
+ ], EmailIdempotencyKeyService);
21
+ export { EmailIdempotencyKeyService };
@@ -0,0 +1,17 @@
1
+ import type { EmailSenderInterface, SendEmailRequestInterface, SendEmailResult } from '../ports/email-sender.interface.js';
2
+ import type { EmailTemplateRendererInterface } from '../ports/email-template-renderer.interface.js';
3
+ import type { MailerLoggerInterface } from '../ports/mailer-logger.interface.js';
4
+ import type { MailerInterface } from '../ports/mailer.interface.js';
5
+ import type { SentEmailLogInterface } from '../ports/sent-email-log.interface.js';
6
+ import { EmailIdempotencyKeyService } from './email-idempotency-key.service.js';
7
+ export declare class EmailSenderService implements EmailSenderInterface {
8
+ private readonly mailer;
9
+ private readonly renderer;
10
+ private readonly sentEmails;
11
+ private readonly keys;
12
+ private readonly logger;
13
+ constructor(mailer: MailerInterface, renderer: EmailTemplateRendererInterface, sentEmails: SentEmailLogInterface, keys: EmailIdempotencyKeyService, logger: MailerLoggerInterface);
14
+ send(request: SendEmailRequestInterface): Promise<SendEmailResult>;
15
+ private failAndRelease;
16
+ private skipRepeat;
17
+ }
@@ -0,0 +1,66 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Inject, Injectable } from '@nestjs/common';
14
+ import { Err, Ok } from 'oxide.ts';
15
+ import { EMAIL_TEMPLATE_RENDERER_PORT, MAILER_LOGGER, MAILER_PORT, SENT_EMAIL_LOG_PORT, } from '../../constants.js';
16
+ import { SendEmailError } from '../errors/send-email.error.js';
17
+ import { EmailIdempotencyKeyService } from './email-idempotency-key.service.js';
18
+ let EmailSenderService = class EmailSenderService {
19
+ mailer;
20
+ renderer;
21
+ sentEmails;
22
+ keys;
23
+ logger;
24
+ constructor(mailer, renderer, sentEmails, keys, logger) {
25
+ this.mailer = mailer;
26
+ this.renderer = renderer;
27
+ this.sentEmails = sentEmails;
28
+ this.keys = keys;
29
+ this.logger = logger;
30
+ }
31
+ async send(request) {
32
+ const idempotencyKey = this.keys.build(request.template, ...request.idempotencyMaterial);
33
+ const isClaimed = await this.sentEmails.claim(idempotencyKey);
34
+ if (!isClaimed) {
35
+ return this.skipRepeat(idempotencyKey);
36
+ }
37
+ const rendered = this.renderer.render(request.template, request.locale, request.parameters);
38
+ try {
39
+ await this.mailer.send({ ...rendered, to: request.to });
40
+ }
41
+ catch (error) {
42
+ return this.failAndRelease(request, idempotencyKey, error);
43
+ }
44
+ this.logger.info({ locale: request.locale, template: request.template, to: request.to }, 'Email handed to the transport');
45
+ return Ok(true);
46
+ }
47
+ async failAndRelease(request, idempotencyKey, error) {
48
+ await this.sentEmails.release(idempotencyKey);
49
+ const failure = new SendEmailError({ locale: request.locale, template: request.template }, { cause: error });
50
+ this.logger.error(failure.context, failure.message);
51
+ return Err(failure);
52
+ }
53
+ skipRepeat(idempotencyKey) {
54
+ this.logger.info({ idempotencyKey }, 'Email already sent for this idempotency key, skipping');
55
+ return Ok(true);
56
+ }
57
+ };
58
+ EmailSenderService = __decorate([
59
+ Injectable(),
60
+ __param(0, Inject(MAILER_PORT)),
61
+ __param(1, Inject(EMAIL_TEMPLATE_RENDERER_PORT)),
62
+ __param(2, Inject(SENT_EMAIL_LOG_PORT)),
63
+ __param(4, Inject(MAILER_LOGGER)),
64
+ __metadata("design:paramtypes", [Object, Object, Object, EmailIdempotencyKeyService, Object])
65
+ ], EmailSenderService);
66
+ export { EmailSenderService };
@@ -0,0 +1,3 @@
1
+ export * from './email-dispatcher.service.js';
2
+ export * from './email-idempotency-key.service.js';
3
+ export * from './email-sender.service.js';
@@ -0,0 +1,3 @@
1
+ export * from './email-dispatcher.service.js';
2
+ export * from './email-idempotency-key.service.js';
3
+ export * from './email-sender.service.js';
@@ -0,0 +1 @@
1
+ export * from './mailer.environment.js';
@@ -0,0 +1 @@
1
+ export * from './mailer.environment.js';
@@ -0,0 +1,7 @@
1
+ export declare class MailerEnvironment {
2
+ MAIL_FROM: string;
3
+ MAIL_SMTP_HOST: string;
4
+ MAIL_SMTP_PASSWORD: string;
5
+ MAIL_SMTP_PORT: number;
6
+ MAIL_SMTP_USER: string;
7
+ }