@veap/mail 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.
- package/README.md +3 -2
- package/dist/composition.d.ts +63 -0
- package/dist/composition.js +8 -0
- package/dist/context.d.ts +1 -1
- package/dist/index.d.ts +11 -10
- package/dist/index.js +10 -9
- package/dist/mail.d.ts +1 -1
- package/dist/mail.js +2 -2
- package/dist/mailer.d.ts +8 -3
- package/dist/mailer.js +4 -0
- package/dist/provider.d.ts +2 -2
- package/dist/provider.js +51 -26
- package/dist/providers/console.provider.d.ts +3 -4
- package/dist/providers/console.provider.js +1 -2
- package/dist/providers/nodemailer.provider.d.ts +14 -8
- package/dist/providers/nodemailer.provider.js +53 -10
- package/dist/translator.d.ts +4 -0
- package/dist/translator.js +16 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
[](https://github.com/veapjs/veap-mail/actions/workflows/CI.yml)
|
|
2
|
+
[](https://github.com/veapjs/veap-mail/actions/workflows/RELEASE.yml)
|
|
3
|
+
[](https://www.npmjs.com/package/@veap/mail)
|
|
3
4
|
|
|
4
5
|
# @veap/mail
|
|
5
6
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mail configuration options supported in veap.config.ts.
|
|
3
|
+
*/
|
|
4
|
+
export interface MailConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Transport driver: "smtp" (default), "console", or "custom".
|
|
7
|
+
*/
|
|
8
|
+
transport?: "smtp" | "console" | "custom" | string;
|
|
9
|
+
/**
|
|
10
|
+
* Nodemailer well-known service (e.g. "gmail", "sendgrid").
|
|
11
|
+
*/
|
|
12
|
+
service?: string;
|
|
13
|
+
/**
|
|
14
|
+
* SMTP server hostname.
|
|
15
|
+
*/
|
|
16
|
+
host?: string;
|
|
17
|
+
/**
|
|
18
|
+
* SMTP server port (e.g. 587, 465, 25, 1025).
|
|
19
|
+
*/
|
|
20
|
+
port?: number | string;
|
|
21
|
+
/**
|
|
22
|
+
* Use TLS/SSL directly (default: true for port 465, false otherwise).
|
|
23
|
+
*/
|
|
24
|
+
secure?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Ignore TLS certificate issues (useful for local dev/test servers).
|
|
27
|
+
*/
|
|
28
|
+
ignoreTLS?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* SMTP authentication username.
|
|
31
|
+
*/
|
|
32
|
+
username?: string;
|
|
33
|
+
/**
|
|
34
|
+
* SMTP authentication password.
|
|
35
|
+
*/
|
|
36
|
+
password?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Default sender address (e.g. "noreply@example.com" or "Acme <noreply@example.com>").
|
|
39
|
+
*/
|
|
40
|
+
from?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Default sender name (e.g. "Acme").
|
|
43
|
+
*/
|
|
44
|
+
fromName?: string;
|
|
45
|
+
}
|
|
46
|
+
declare module "@veap/kernel" {
|
|
47
|
+
interface ApplicationBuilder {
|
|
48
|
+
/**
|
|
49
|
+
* Registers the communication/mail service provider into the application.
|
|
50
|
+
*/
|
|
51
|
+
withMail(): this;
|
|
52
|
+
/**
|
|
53
|
+
* Alias for `withMail()`.
|
|
54
|
+
*/
|
|
55
|
+
withCommunication(): this;
|
|
56
|
+
}
|
|
57
|
+
interface VeapConfig {
|
|
58
|
+
/**
|
|
59
|
+
* Communication and mail transport configuration.
|
|
60
|
+
*/
|
|
61
|
+
mail?: MailConfig;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ApplicationBuilder } from "@veap/kernel";
|
|
2
|
+
import { CommunicationServiceProvider } from "./provider.js";
|
|
3
|
+
ApplicationBuilder.prototype.withMail = function () {
|
|
4
|
+
return this.use(CommunicationServiceProvider);
|
|
5
|
+
};
|
|
6
|
+
ApplicationBuilder.prototype.withCommunication = function () {
|
|
7
|
+
return this.use(CommunicationServiceProvider);
|
|
8
|
+
};
|
package/dist/context.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export * from "./
|
|
6
|
-
export * from "./
|
|
7
|
-
export * from "./
|
|
8
|
-
export * from "./providers/
|
|
9
|
-
export
|
|
10
|
-
export
|
|
1
|
+
export * from "./composition.js";
|
|
2
|
+
export * from "./context.js";
|
|
3
|
+
export * from "./mail.js";
|
|
4
|
+
export type { MailMessage as SendMailOptions } from "./mail-message.js";
|
|
5
|
+
export * from "./mail-message.js";
|
|
6
|
+
export * from "./mailer.js";
|
|
7
|
+
export * from "./provider.js";
|
|
8
|
+
export * from "./providers/console.provider.js";
|
|
9
|
+
export * from "./providers/nodemailer.provider.js";
|
|
10
|
+
export { NodemailerMailService as MailService } from "./providers/nodemailer.provider.js";
|
|
11
|
+
export * from "./translator.js";
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./
|
|
3
|
-
export * from "./mail
|
|
4
|
-
export * from "./
|
|
5
|
-
export * from "./
|
|
6
|
-
export * from "./
|
|
7
|
-
export * from "./providers/
|
|
8
|
-
export
|
|
9
|
-
export
|
|
1
|
+
export * from "./composition.js";
|
|
2
|
+
export * from "./context.js";
|
|
3
|
+
export * from "./mail.js";
|
|
4
|
+
export * from "./mail-message.js";
|
|
5
|
+
export * from "./mailer.js";
|
|
6
|
+
export * from "./provider.js";
|
|
7
|
+
export * from "./providers/console.provider.js";
|
|
8
|
+
export * from "./providers/nodemailer.provider.js";
|
|
9
|
+
export { NodemailerMailService as MailService } from "./providers/nodemailer.provider.js";
|
|
10
|
+
export * from "./translator.js";
|
package/dist/mail.d.ts
CHANGED
package/dist/mail.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { communicationContext } from "./context";
|
|
2
|
-
import { getMailTranslator } from "./translator";
|
|
1
|
+
import { communicationContext } from "./context.js";
|
|
2
|
+
import { getMailTranslator } from "./translator.js";
|
|
3
3
|
/**
|
|
4
4
|
* Mailables - the domain vocabulary of email.
|
|
5
5
|
*
|
package/dist/mailer.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Token } from "@veap/kernel";
|
|
2
|
+
import type { MailMessage } from "./mail-message.js";
|
|
2
3
|
/**
|
|
3
4
|
* Mailer port.
|
|
4
5
|
*
|
|
@@ -22,7 +23,11 @@ export interface IMailer {
|
|
|
22
23
|
* boundaries, the same reason the container keys class tokens with
|
|
23
24
|
* `Symbol.for(...)`.
|
|
24
25
|
*/
|
|
25
|
-
export declare const MAILER:
|
|
26
|
+
export declare const MAILER: Token<IMailer>;
|
|
27
|
+
/**
|
|
28
|
+
* Backward compatibility alias for the mailer port token.
|
|
29
|
+
*/
|
|
30
|
+
export declare const MAIL_SERVICE: Token<IMailer>;
|
|
26
31
|
/**
|
|
27
32
|
* Injection token for a custom transport supplied by the host application.
|
|
28
33
|
*
|
|
@@ -30,4 +35,4 @@ export declare const MAILER: unique symbol;
|
|
|
30
35
|
* this token (e.g. SES, Postmark, Resend, an outbox pattern…) to route all
|
|
31
36
|
* mail through your own delivery system without touching the framework.
|
|
32
37
|
*/
|
|
33
|
-
export declare const CUSTOM_MAILER:
|
|
38
|
+
export declare const CUSTOM_MAILER: Token<IMailer>;
|
package/dist/mailer.js
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
* `Symbol.for(...)`.
|
|
7
7
|
*/
|
|
8
8
|
export const MAILER = Symbol.for("veap:communication:mailer");
|
|
9
|
+
/**
|
|
10
|
+
* Backward compatibility alias for the mailer port token.
|
|
11
|
+
*/
|
|
12
|
+
export const MAIL_SERVICE = MAILER;
|
|
9
13
|
/**
|
|
10
14
|
* Injection token for a custom transport supplied by the host application.
|
|
11
15
|
*
|
package/dist/provider.d.ts
CHANGED
|
@@ -6,8 +6,8 @@ import { ServiceProvider } from "@veap/kernel";
|
|
|
6
6
|
* and binds it into `CommunicationContext`, so facades and services never
|
|
7
7
|
* look transports up themselves:
|
|
8
8
|
*
|
|
9
|
-
* - `smtp` (default) - Nodemailer (`MAIL_SERVICE`, `
|
|
10
|
-
* or `GOOGLE_SMTP_APP_*`, `MAIL_FROM_ADDRESS`)
|
|
9
|
+
* - `smtp` (default) - Nodemailer (`MAIL_SERVICE`, `MAIL_HOST`, `MAIL_PORT`,
|
|
10
|
+
* `MAIL_USERNAME`/`MAIL_PASSWORD` or `GOOGLE_SMTP_APP_*`, `MAIL_FROM_ADDRESS`)
|
|
11
11
|
* - `console` - logs the message; dev/test with zero mail configuration
|
|
12
12
|
* - `custom` - resolves `CUSTOM_MAILER`; bring your own SES/Postmark/Resend…
|
|
13
13
|
*/
|
package/dist/provider.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ConfigService, logger, ServiceProvider } from "@veap/kernel";
|
|
2
|
-
import { bindCommunicationContext } from "./context";
|
|
3
|
-
import { CUSTOM_MAILER, MAILER } from "./mailer";
|
|
4
|
-
import { ConsoleMailService } from "./providers/console.provider";
|
|
5
|
-
import { NodemailerMailService } from "./providers/nodemailer.provider";
|
|
6
|
-
import { setMailTranslatorFactory } from "./translator";
|
|
2
|
+
import { bindCommunicationContext } from "./context.js";
|
|
3
|
+
import { CUSTOM_MAILER, MAILER } from "./mailer.js";
|
|
4
|
+
import { ConsoleMailService } from "./providers/console.provider.js";
|
|
5
|
+
import { NodemailerMailService } from "./providers/nodemailer.provider.js";
|
|
6
|
+
import { setMailTranslatorFactory } from "./translator.js";
|
|
7
7
|
/**
|
|
8
8
|
* Composition root of the communication context.
|
|
9
9
|
*
|
|
@@ -11,8 +11,8 @@ import { setMailTranslatorFactory } from "./translator";
|
|
|
11
11
|
* and binds it into `CommunicationContext`, so facades and services never
|
|
12
12
|
* look transports up themselves:
|
|
13
13
|
*
|
|
14
|
-
* - `smtp` (default) - Nodemailer (`MAIL_SERVICE`, `
|
|
15
|
-
* or `GOOGLE_SMTP_APP_*`, `MAIL_FROM_ADDRESS`)
|
|
14
|
+
* - `smtp` (default) - Nodemailer (`MAIL_SERVICE`, `MAIL_HOST`, `MAIL_PORT`,
|
|
15
|
+
* `MAIL_USERNAME`/`MAIL_PASSWORD` or `GOOGLE_SMTP_APP_*`, `MAIL_FROM_ADDRESS`)
|
|
16
16
|
* - `console` - logs the message; dev/test with zero mail configuration
|
|
17
17
|
* - `custom` - resolves `CUSTOM_MAILER`; bring your own SES/Postmark/Resend…
|
|
18
18
|
*/
|
|
@@ -25,24 +25,44 @@ export class CommunicationServiceProvider extends ServiceProvider {
|
|
|
25
25
|
// selects another transport.
|
|
26
26
|
this.container.register({
|
|
27
27
|
token: MAILER,
|
|
28
|
-
useFactory: (config) =>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
useFactory: (config) => {
|
|
29
|
+
const getEnv = (key) => {
|
|
30
|
+
return (config?.get?.(key) ??
|
|
31
|
+
(typeof process !== "undefined" ? process.env[key] : undefined));
|
|
32
|
+
};
|
|
33
|
+
return new NodemailerMailService({
|
|
34
|
+
getService: () => getEnv("MAIL_SERVICE"),
|
|
35
|
+
getHost: () => getEnv("MAIL_HOST"),
|
|
36
|
+
getPort: () => getEnv("MAIL_PORT"),
|
|
37
|
+
getSecure: () => {
|
|
38
|
+
const val = getEnv("MAIL_SECURE");
|
|
39
|
+
return val ? val === "true" || val === "1" : undefined;
|
|
40
|
+
},
|
|
41
|
+
getIgnoreTLS: () => {
|
|
42
|
+
const val = getEnv("MAIL_IGNORE_TLS");
|
|
43
|
+
return val ? val === "true" || val === "1" : undefined;
|
|
44
|
+
},
|
|
45
|
+
getUsername: () => getEnv("GOOGLE_SMTP_APP_USERNAME") ?? getEnv("MAIL_USERNAME"),
|
|
46
|
+
getPassword: () => getEnv("GOOGLE_SMTP_APP_PASSWORD") ?? getEnv("MAIL_PASSWORD"),
|
|
47
|
+
getFromAddress: () => getEnv("MAIL_FROM_ADDRESS"),
|
|
48
|
+
getFromName: () => getEnv("MAIL_FROM_NAME"),
|
|
49
|
+
});
|
|
50
|
+
},
|
|
36
51
|
inject: [ConfigService],
|
|
37
52
|
singleton: true,
|
|
38
53
|
});
|
|
39
54
|
}
|
|
40
55
|
async boot() {
|
|
41
|
-
// Bind the
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
// Bind the default translation factory that supports `{placeholder}`
|
|
57
|
+
// interpolation into the message text.
|
|
58
|
+
setMailTranslatorFactory(async () => (key, params) => {
|
|
59
|
+
if (!params)
|
|
60
|
+
return key;
|
|
61
|
+
return Object.entries(params).reduce((acc, [k, v]) => acc.replaceAll(`{${k}}`, String(v)), key);
|
|
62
|
+
});
|
|
63
|
+
const config = this.container.has(ConfigService)
|
|
64
|
+
? await this.container.resolve(ConfigService)
|
|
65
|
+
: undefined;
|
|
46
66
|
const mailer = await this.selectTransport(config);
|
|
47
67
|
// Rebind the port token to the selected transport and expose it through
|
|
48
68
|
// the typed context boundary.
|
|
@@ -55,7 +75,11 @@ export class CommunicationServiceProvider extends ServiceProvider {
|
|
|
55
75
|
logger.debug("veap:communication", `Mail transport bound: ${mailer.constructor.name}`);
|
|
56
76
|
}
|
|
57
77
|
async selectTransport(config) {
|
|
58
|
-
const
|
|
78
|
+
const getVal = (key) => {
|
|
79
|
+
return (config?.get?.(key) ??
|
|
80
|
+
(typeof process !== "undefined" ? process.env[key] : undefined));
|
|
81
|
+
};
|
|
82
|
+
const transport = getVal("MAIL_TRANSPORT")?.trim().toLowerCase();
|
|
59
83
|
if (transport === "custom") {
|
|
60
84
|
if (!this.container.has(CUSTOM_MAILER)) {
|
|
61
85
|
throw new Error("[Communication] MAIL_TRANSPORT=custom but no provider is registered under the CUSTOM_MAILER token (register an IMailer in a ServiceProvider).");
|
|
@@ -65,13 +89,14 @@ export class CommunicationServiceProvider extends ServiceProvider {
|
|
|
65
89
|
if (transport === "console") {
|
|
66
90
|
return new ConsoleMailService((line) => logger.info("veap:mail", line));
|
|
67
91
|
}
|
|
68
|
-
const hasSmtpCredentials = Boolean(
|
|
69
|
-
Boolean(
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
const hasSmtpCredentials = Boolean(getVal("GOOGLE_SMTP_APP_USERNAME") || getVal("MAIL_USERNAME")) &&
|
|
93
|
+
Boolean(getVal("GOOGLE_SMTP_APP_PASSWORD") || getVal("MAIL_PASSWORD"));
|
|
94
|
+
const hasSmtpHostOrService = Boolean(getVal("MAIL_HOST") || getVal("MAIL_SERVICE"));
|
|
95
|
+
if (!transport && !hasSmtpCredentials && !hasSmtpHostOrService) {
|
|
96
|
+
logger.warn("veap:communication", "MAIL_TRANSPORT is not set and no SMTP credentials (MAIL_USERNAME/MAIL_PASSWORD) or host (MAIL_HOST) are configured. Defaulting to ConsoleMailService (emails will be logged to server console).");
|
|
72
97
|
return new ConsoleMailService((line) => logger.info("veap:mail", line));
|
|
73
98
|
}
|
|
74
|
-
// Default ("smtp" / unset with credentials): whatever register() wired - Nodemailer.
|
|
99
|
+
// Default ("smtp" / unset with credentials/host): whatever register() wired - Nodemailer.
|
|
75
100
|
return this.container.resolve(MAILER);
|
|
76
101
|
}
|
|
77
102
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type { MailMessage } from "
|
|
2
|
-
import type { IMailer } from "
|
|
1
|
+
import type { MailMessage } from "../mail-message.js";
|
|
2
|
+
import type { IMailer } from "../mailer.js";
|
|
3
3
|
/**
|
|
4
|
-
* Console transport implementing the {@link IMailer} port
|
|
5
|
-
*
|
|
4
|
+
* Console transport implementing the {@link IMailer} port.\n *
|
|
6
5
|
* A pure delivery service for development and testing: prints the
|
|
7
6
|
* `MailMessage` instead of dialling an SMTP server. Selected with
|
|
8
7
|
* `MAIL_TRANSPORT=console`. It knows nothing about message semantics -
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Console transport implementing the {@link IMailer} port
|
|
3
|
-
*
|
|
2
|
+
* Console transport implementing the {@link IMailer} port.\n *
|
|
4
3
|
* A pure delivery service for development and testing: prints the
|
|
5
4
|
* `MailMessage` instead of dialling an SMTP server. Selected with
|
|
6
5
|
* `MAIL_TRANSPORT=console`. It knows nothing about message semantics -
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import type { SendMailOptions } from "nodemailer";
|
|
2
|
-
import type { MailMessage, MailRecipient } from "
|
|
3
|
-
import type { IMailer } from "
|
|
2
|
+
import type { MailMessage, MailRecipient } from "../mail-message.js";
|
|
3
|
+
import type { IMailer } from "../mailer.js";
|
|
4
|
+
export interface NodemailerMailConfig {
|
|
5
|
+
getService?: () => string | undefined;
|
|
6
|
+
getHost?: () => string | undefined;
|
|
7
|
+
getPort?: () => number | string | undefined;
|
|
8
|
+
getSecure?: () => boolean | undefined;
|
|
9
|
+
getIgnoreTLS?: () => boolean | undefined;
|
|
10
|
+
getUsername?: () => string | undefined;
|
|
11
|
+
getPassword?: () => string | undefined;
|
|
12
|
+
getFromAddress?: () => string | undefined;
|
|
13
|
+
getFromName?: () => string | undefined;
|
|
14
|
+
}
|
|
4
15
|
/**
|
|
5
16
|
* SMTP transport (Nodemailer) implementing the {@link IMailer} port.
|
|
6
17
|
*
|
|
@@ -16,12 +27,7 @@ import type { IMailer } from ".././mailer";
|
|
|
16
27
|
export declare class NodemailerMailService implements IMailer {
|
|
17
28
|
private transporter;
|
|
18
29
|
private fromAddress;
|
|
19
|
-
constructor(config:
|
|
20
|
-
getService: () => string;
|
|
21
|
-
getUsername: () => string | undefined;
|
|
22
|
-
getPassword: () => string | undefined;
|
|
23
|
-
getFromAddress: () => string | undefined;
|
|
24
|
-
});
|
|
30
|
+
constructor(config: NodemailerMailConfig);
|
|
25
31
|
/** The sender used when a message does not specify one. */
|
|
26
32
|
get defaultFrom(): MailRecipient;
|
|
27
33
|
/** Exposed for tests and provider-agnostic integrations. */
|
|
@@ -15,16 +15,59 @@ export class NodemailerMailService {
|
|
|
15
15
|
transporter;
|
|
16
16
|
fromAddress;
|
|
17
17
|
constructor(config) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
18
|
+
const fromAddress = config.getFromAddress?.();
|
|
19
|
+
const fromName = config.getFromName?.();
|
|
20
|
+
const username = config.getUsername?.();
|
|
21
|
+
const password = config.getPassword?.();
|
|
22
|
+
if (fromAddress) {
|
|
23
|
+
this.fromAddress =
|
|
24
|
+
fromName && !fromAddress.includes("<")
|
|
25
|
+
? `${fromName} <${fromAddress}>`
|
|
26
|
+
: fromAddress;
|
|
27
|
+
}
|
|
28
|
+
else if (username) {
|
|
29
|
+
this.fromAddress = fromName
|
|
30
|
+
? `${fromName} <${username}>`
|
|
31
|
+
: `Veap <${username}>`;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this.fromAddress = "Veap";
|
|
35
|
+
}
|
|
36
|
+
const host = config.getHost?.();
|
|
37
|
+
const port = config.getPort?.();
|
|
38
|
+
const service = config.getService?.();
|
|
39
|
+
const secure = config.getSecure?.();
|
|
40
|
+
const ignoreTLS = config.getIgnoreTLS?.();
|
|
41
|
+
const auth = username || password
|
|
42
|
+
? {
|
|
43
|
+
user: username,
|
|
44
|
+
pass: password,
|
|
45
|
+
}
|
|
46
|
+
: undefined;
|
|
47
|
+
if (host) {
|
|
48
|
+
const parsedPort = port ? Number(port) : 587;
|
|
49
|
+
this.transporter = nodemailer.createTransport({
|
|
50
|
+
host,
|
|
51
|
+
port: parsedPort,
|
|
52
|
+
secure: secure !== undefined ? Boolean(secure) : parsedPort === 465,
|
|
53
|
+
ignoreTLS: Boolean(ignoreTLS),
|
|
54
|
+
auth,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else if (service) {
|
|
58
|
+
this.transporter = nodemailer.createTransport({
|
|
59
|
+
service,
|
|
60
|
+
auth,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// Default local or fallback SMTP transport
|
|
65
|
+
this.transporter = nodemailer.createTransport({
|
|
66
|
+
host: "localhost",
|
|
67
|
+
port: 587,
|
|
68
|
+
auth,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
28
71
|
}
|
|
29
72
|
/** The sender used when a message does not specify one. */
|
|
30
73
|
get defaultFrom() {
|
package/dist/translator.d.ts
CHANGED
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
* directly and the package never imports its own entry points (ADR-006).
|
|
8
8
|
*/
|
|
9
9
|
export type Translator = (key: string, params?: Record<string, unknown>) => string;
|
|
10
|
+
/**
|
|
11
|
+
* Helper to interpolate template placeholders like `{name}` or `{code}`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function interpolateMailText(text: string, params?: Record<string, unknown>): string;
|
|
10
14
|
/**
|
|
11
15
|
* Binds the translation factory. Invoked per message build, so the locale
|
|
12
16
|
* always reflects the current request.
|
package/dist/translator.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
let translatorFactory = null;
|
|
2
|
+
/**
|
|
3
|
+
* Helper to interpolate template placeholders like `{name}` or `{code}`.
|
|
4
|
+
*/
|
|
5
|
+
export function interpolateMailText(text, params) {
|
|
6
|
+
if (!params)
|
|
7
|
+
return text;
|
|
8
|
+
return Object.entries(params).reduce((acc, [k, v]) => acc.replaceAll(`{${k}}`, String(v)), text);
|
|
9
|
+
}
|
|
2
10
|
/**
|
|
3
11
|
* Binds the translation factory. Invoked per message build, so the locale
|
|
4
12
|
* always reflects the current request.
|
|
@@ -12,9 +20,12 @@ export function setMailTranslatorFactory(factory) {
|
|
|
12
20
|
* (unit tests, early boot).
|
|
13
21
|
*/
|
|
14
22
|
export async function getMailTranslator() {
|
|
15
|
-
if (translatorFactory)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
if (translatorFactory) {
|
|
24
|
+
const fn = await translatorFactory();
|
|
25
|
+
return (key, params) => {
|
|
26
|
+
const translated = fn(key, params);
|
|
27
|
+
return interpolateMailText(translated, params);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return (key, params) => interpolateMailText(key, params);
|
|
20
31
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veap/mail",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Mail and communication transport abstraction for Veap",
|
|
5
5
|
"bugs": "https://github.com/veapjs/veap-mail/issues",
|
|
6
6
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"test": "vitest run"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@veap/kernel": "0.1.
|
|
31
|
+
"@veap/kernel": "0.1.2",
|
|
32
32
|
"nodemailer": "^10.0.1"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|