@travetto/email 3.1.7 → 3.1.9

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/service.ts +52 -56
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/email",
3
- "version": "3.1.7",
3
+ "version": "3.1.9",
4
4
  "description": "Email transmission module.",
5
5
  "keywords": [
6
6
  "email",
package/src/service.ts CHANGED
@@ -7,7 +7,8 @@ import { MailTemplateEngine } from './template';
7
7
  import { MailUtil } from './util';
8
8
  import { EmailResource } from './resource';
9
9
 
10
- type MessageWithoutBody = Omit<MessageOptions, 'html' | 'text' | 'subject'>;
10
+ type Compiled = { html: string, text?: string, subject: string };
11
+ type MessageWithoutBody = Omit<MessageOptions, keyof Compiled>;
11
12
 
12
13
  /**
13
14
  * Email service for sending and templating emails
@@ -15,7 +16,7 @@ type MessageWithoutBody = Omit<MessageOptions, 'html' | 'text' | 'subject'>;
15
16
  @Injectable()
16
17
  export class MailService {
17
18
 
18
- #compiled = new Map<string, { html: string, subject: string, text?: string }>();
19
+ #compiled = new Map<string, Compiled>();
19
20
  #transport: MailTransport;
20
21
  #tplEngine: MailTemplateEngine;
21
22
  #resources: EmailResource;
@@ -30,41 +31,10 @@ export class MailService {
30
31
  this.#resources = resources;
31
32
  }
32
33
 
33
- /**
34
- * Force content into alternative slots, satisfies node mailer
35
- */
36
- #forceContentToAlternative(msg: MessageOptions): MessageOptions {
37
- for (const [key, mime] of [['text', 'text/plain'], ['html', 'text/html']] as const) {
38
- if (msg[key]) {
39
- (msg.alternatives ??= []).push({
40
- content: msg[key], contentDisposition: 'inline', contentTransferEncoding: '7bit', contentType: `${mime}; charset=utf-8`
41
- });
42
- delete msg[key];
43
- }
44
- }
45
- return msg;
46
- }
47
-
48
- /**
49
- * Send multiple messages.
50
- */
51
- async sendAll<S extends SentMessage = SentMessage>(messages: MessageOptions[], base: Partial<MessageOptions> = {}): Promise<S[]> {
52
- return Promise.all(messages.map(msg => this.send<S>({
53
- ...base,
54
- ...msg,
55
- ...(msg.context || base.context ? {
56
- context: {
57
- ...(base.context || {}),
58
- ...(msg.context || {})
59
- }
60
- } : {})
61
- })));
62
- }
63
-
64
34
  /**
65
35
  * Get compiled content by key
66
36
  */
67
- async getCompiled(key: string): Promise<{ html: string, text?: string, subject: string }> {
37
+ async getCompiled(key: string): Promise<Compiled> {
68
38
  if (GlobalEnv.dynamic || !this.#compiled.has(key)) {
69
39
  const [html, text, subject] = await Promise.all([
70
40
  this.#resources.read(`${key}.compiled.html`),
@@ -79,42 +49,68 @@ export class MailService {
79
49
 
80
50
  /**
81
51
  * Build message from key/context
82
- * @param key
52
+ * @param keyOrMessage
83
53
  * @param ctx
84
54
  * @returns
85
55
  */
86
- async buildMessage(key: string | MessageOptions, ctx?: Record<string, unknown>): Promise<MessageOptions> {
87
- const tpl = (typeof key === 'string' ? await this.getCompiled(key) : key);
88
- ctx ??= (typeof key === 'string' ? {} : key.context ?? {});
89
- const [rawHtml, text, subject] = await Promise.all([
56
+ async templateMessage(keyOrMessage: string | Compiled, ctx: Record<string, unknown>): Promise<Compiled> {
57
+ const tpl = (typeof keyOrMessage === 'string' ? await this.getCompiled(keyOrMessage) : keyOrMessage);
58
+
59
+ const [html, text, subject] = await Promise.all([
90
60
  tpl.html ? this.#tplEngine!.template(tpl.html, ctx) : undefined,
91
61
  tpl.text ? this.#tplEngine!.template(tpl.text, ctx) : undefined,
92
62
  tpl.subject ? this.#tplEngine!.template(tpl.subject, ctx) : undefined
93
63
  ]);
94
64
 
95
- const msg: MessageOptions = {
96
- html: rawHtml ?? '',
97
- text,
98
- subject
99
- };
65
+ return { html: html!, text, subject: subject! };
66
+ }
67
+
68
+ /**
69
+ * Send a single message
70
+ */
71
+ async send<S extends SentMessage = SentMessage>(
72
+ message: Pick<MessageOptions, 'to' | 'from' | 'replyTo'>,
73
+ key: string,
74
+ ctx?: Record<string, unknown>,
75
+ base?: MessageWithoutBody
76
+ ): Promise<S>;
77
+ async send<S extends SentMessage = SentMessage>(message: MessageOptions): Promise<S>;
78
+ async send<S extends SentMessage = SentMessage>(
79
+ message: MessageOptions | Pick<MessageOptions, 'to' | 'from' | 'replyTo'>,
80
+ key?: string,
81
+ ctx?: Record<string, unknown>,
82
+ base?: MessageWithoutBody
83
+ ): Promise<S> {
84
+ const keyOrMessage = key ?? ('html' in message ? message : '') ?? '';
85
+ const context = ctx ?? (('context' in message) ? message.context : {}) ?? {};
86
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
87
+ const compiled = await this.templateMessage(keyOrMessage as Compiled, context);
100
88
 
101
- if (msg.html) {
102
- const { html, attachments } = await MailUtil.extractImageAttachments(msg.html);
103
- msg.html = html;
104
- msg.attachments = attachments;
89
+ const final = { ...base, ...message, ...compiled };
90
+
91
+ // Extract images
92
+ if (compiled.html) {
93
+ const { html, attachments } = await MailUtil.extractImageAttachments(compiled.html);
94
+ final.html = html;
95
+ final.attachments = [...attachments, ...(final.attachments ?? [])];
105
96
  }
106
97
 
107
- return msg;
98
+ return this.#transport.send<S>(final);
108
99
  }
109
100
 
110
101
  /**
111
- * Send a single message
102
+ * Send multiple messages.
112
103
  */
113
- async send<S extends SentMessage = SentMessage>(key: string, base?: MessageWithoutBody): Promise<S>;
114
- async send<S extends SentMessage = SentMessage>(message: MessageOptions): Promise<S>;
115
- async send<S extends SentMessage = SentMessage>(keyOrMessage: MessageOptions | string, base?: MessageWithoutBody): Promise<S> {
116
- let msg = await this.buildMessage(keyOrMessage);
117
- msg = this.#forceContentToAlternative(msg);
118
- return this.#transport.send<S>({ ...base, ...msg });
104
+ async sendAll<S extends SentMessage = SentMessage>(messages: MessageOptions[], base: Partial<MessageOptions> = {}): Promise<S[]> {
105
+ return Promise.all(messages.map(msg => this.send<S>({
106
+ ...base,
107
+ ...msg,
108
+ ...(msg.context || base.context ? {
109
+ context: {
110
+ ...(base.context || {}),
111
+ ...(msg.context || {})
112
+ }
113
+ } : {})
114
+ })));
119
115
  }
120
116
  }