@nymbalhq/adapter-smtp 0.0.0-stage → 1.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.
- package/dist/index.d.ts +21 -0
- package/dist/index.js +89 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -3
- package/README.md +0 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Logger, EmailAdapter, AdapterContext } from '@nymbalhq/types';
|
|
2
|
+
|
|
3
|
+
interface SmtpEmailAdapterConfig {
|
|
4
|
+
from?: string;
|
|
5
|
+
smtp?: {
|
|
6
|
+
host: string;
|
|
7
|
+
port: number;
|
|
8
|
+
user?: string;
|
|
9
|
+
pass?: string;
|
|
10
|
+
secure?: boolean;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
declare function createSmtpEmailAdapter(deps: {
|
|
14
|
+
logger: Logger;
|
|
15
|
+
config?: SmtpEmailAdapterConfig;
|
|
16
|
+
}): EmailAdapter;
|
|
17
|
+
|
|
18
|
+
/** The kernel's entry: `email: { provider: 'smtp', config: SmtpEmailAdapterConfig }`. */
|
|
19
|
+
declare function createAdapter(ctx: AdapterContext): EmailAdapter;
|
|
20
|
+
|
|
21
|
+
export { type SmtpEmailAdapterConfig, createAdapter, createSmtpEmailAdapter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { ConfigError } from "@nymbalhq/types";
|
|
3
|
+
|
|
4
|
+
// src/smtp-adapter.ts
|
|
5
|
+
import nodemailer from "nodemailer";
|
|
6
|
+
import {
|
|
7
|
+
AdapterError,
|
|
8
|
+
EVT_CART_ABANDONED,
|
|
9
|
+
EVT_CUSTOMER_CREATED,
|
|
10
|
+
EVT_ORDER_PLACED,
|
|
11
|
+
EVT_ORDER_SHIPPED,
|
|
12
|
+
renderEmailTemplate
|
|
13
|
+
} from "@nymbalhq/types";
|
|
14
|
+
function createSmtpEmailAdapter(deps) {
|
|
15
|
+
const log = deps.logger.child({ adapter: "email", provider: "smtp" });
|
|
16
|
+
let cfg = deps.config ?? {};
|
|
17
|
+
let transporter = null;
|
|
18
|
+
function ensureTransport() {
|
|
19
|
+
if (transporter) return transporter;
|
|
20
|
+
if (!cfg.smtp) {
|
|
21
|
+
throw new AdapterError("email.misconfigured", "SMTP provider selected but smtp.host not set");
|
|
22
|
+
}
|
|
23
|
+
transporter = nodemailer.createTransport({
|
|
24
|
+
host: cfg.smtp.host,
|
|
25
|
+
port: cfg.smtp.port,
|
|
26
|
+
secure: cfg.smtp.secure ?? cfg.smtp.port === 465,
|
|
27
|
+
...cfg.smtp.user !== void 0 && cfg.smtp.pass !== void 0 ? { auth: { user: cfg.smtp.user, pass: cfg.smtp.pass } } : {}
|
|
28
|
+
});
|
|
29
|
+
return transporter;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
kind: "email",
|
|
33
|
+
providerName: "smtp",
|
|
34
|
+
capabilities: () => ["transactional", "list-sync"],
|
|
35
|
+
producesEvents: [],
|
|
36
|
+
consumesEvents: [EVT_ORDER_PLACED, EVT_ORDER_SHIPPED, EVT_CUSTOMER_CREATED, EVT_CART_ABANDONED],
|
|
37
|
+
async initialize(rawConfig) {
|
|
38
|
+
cfg = { ...cfg, ...rawConfig };
|
|
39
|
+
ensureTransport();
|
|
40
|
+
},
|
|
41
|
+
async healthCheck() {
|
|
42
|
+
try {
|
|
43
|
+
await ensureTransport().verify();
|
|
44
|
+
return { status: "healthy", detail: "SMTP verified" };
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return {
|
|
47
|
+
status: "degraded",
|
|
48
|
+
detail: `SMTP verify failed: ${err instanceof Error ? err.message : String(err)}`
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
async sendTransactional(params) {
|
|
53
|
+
const rendered = renderEmailTemplate(params.template, params.vars);
|
|
54
|
+
const info = await ensureTransport().sendMail({
|
|
55
|
+
from: cfg.from ?? "no-reply@nymbal.dev",
|
|
56
|
+
to: params.to,
|
|
57
|
+
subject: rendered.subject,
|
|
58
|
+
text: rendered.text,
|
|
59
|
+
html: rendered.html
|
|
60
|
+
});
|
|
61
|
+
return { messageId: info.messageId ?? null, accepted: (info.accepted ?? []).length > 0 };
|
|
62
|
+
},
|
|
63
|
+
async syncCustomerToList(customer) {
|
|
64
|
+
log.debug({ customerId: customer.customerId }, "email.syncCustomerToList (stub)");
|
|
65
|
+
},
|
|
66
|
+
async removeCustomerFromList(customerId) {
|
|
67
|
+
log.debug({ customerId }, "email.removeCustomerFromList (stub)");
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/index.ts
|
|
73
|
+
function createAdapter(ctx) {
|
|
74
|
+
if (ctx.slot !== "email") {
|
|
75
|
+
throw new ConfigError(`@nymbalhq/adapter-smtp serves email, not ${ctx.slot}`, {
|
|
76
|
+
code: "adapter.wrong_slot",
|
|
77
|
+
context: { slot: ctx.slot }
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return createSmtpEmailAdapter({
|
|
81
|
+
logger: ctx.logger,
|
|
82
|
+
config: ctx.config
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
createAdapter,
|
|
87
|
+
createSmtpEmailAdapter
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/smtp-adapter.ts"],"sourcesContent":["import { ConfigError, type AdapterContext, type EmailAdapter } from '@nymbalhq/types'\nimport { createSmtpEmailAdapter, type SmtpEmailAdapterConfig } from './smtp-adapter.js'\n\nexport { createSmtpEmailAdapter, type SmtpEmailAdapterConfig }\n\n/** The kernel's entry: `email: { provider: 'smtp', config: SmtpEmailAdapterConfig }`. */\nexport function createAdapter(ctx: AdapterContext): EmailAdapter {\n if (ctx.slot !== 'email') {\n throw new ConfigError(`@nymbalhq/adapter-smtp serves email, not ${ctx.slot}`, {\n code: 'adapter.wrong_slot',\n context: { slot: ctx.slot },\n })\n }\n return createSmtpEmailAdapter({\n logger: ctx.logger,\n config: ctx.config as SmtpEmailAdapterConfig,\n })\n}\n","import nodemailer, { type Transporter } from 'nodemailer'\nimport {\n AdapterError,\n EVT_CART_ABANDONED,\n EVT_CUSTOMER_CREATED,\n EVT_ORDER_PLACED,\n EVT_ORDER_SHIPPED,\n renderEmailTemplate,\n type CustomerProfile,\n type EmailAdapter,\n type Logger,\n type TransactionalEmail,\n} from '@nymbalhq/types'\n\nexport interface SmtpEmailAdapterConfig {\n from?: string\n smtp?: {\n host: string\n port: number\n user?: string\n pass?: string\n secure?: boolean\n }\n}\n\nexport function createSmtpEmailAdapter(deps: {\n logger: Logger\n config?: SmtpEmailAdapterConfig\n}): EmailAdapter {\n const log = deps.logger.child({ adapter: 'email', provider: 'smtp' })\n let cfg: SmtpEmailAdapterConfig = deps.config ?? {}\n let transporter: Transporter | null = null\n\n function ensureTransport(): Transporter {\n if (transporter) return transporter\n if (!cfg.smtp) {\n throw new AdapterError('email.misconfigured', 'SMTP provider selected but smtp.host not set')\n }\n transporter = nodemailer.createTransport({\n host: cfg.smtp.host,\n port: cfg.smtp.port,\n secure: cfg.smtp.secure ?? cfg.smtp.port === 465,\n ...(cfg.smtp.user !== undefined && cfg.smtp.pass !== undefined\n ? { auth: { user: cfg.smtp.user, pass: cfg.smtp.pass } }\n : {}),\n })\n return transporter\n }\n\n return {\n kind: 'email',\n providerName: 'smtp',\n capabilities: () => ['transactional', 'list-sync'],\n producesEvents: [],\n consumesEvents: [EVT_ORDER_PLACED, EVT_ORDER_SHIPPED, EVT_CUSTOMER_CREATED, EVT_CART_ABANDONED],\n async initialize(rawConfig) {\n cfg = { ...cfg, ...(rawConfig as SmtpEmailAdapterConfig) }\n ensureTransport()\n },\n async healthCheck() {\n try {\n await ensureTransport().verify()\n return { status: 'healthy', detail: 'SMTP verified' }\n } catch (err) {\n return {\n status: 'degraded',\n detail: `SMTP verify failed: ${err instanceof Error ? err.message : String(err)}`,\n }\n }\n },\n\n async sendTransactional(params: TransactionalEmail) {\n const rendered = renderEmailTemplate(params.template, params.vars)\n const info = await ensureTransport().sendMail({\n from: cfg.from ?? 'no-reply@nymbal.dev',\n to: params.to,\n subject: rendered.subject,\n text: rendered.text,\n html: rendered.html,\n })\n return { messageId: info.messageId ?? null, accepted: (info.accepted ?? []).length > 0 }\n },\n\n async syncCustomerToList(customer: CustomerProfile) {\n log.debug({ customerId: customer.customerId }, 'email.syncCustomerToList (stub)')\n },\n\n async removeCustomerFromList(customerId: string) {\n log.debug({ customerId }, 'email.removeCustomerFromList (stub)')\n },\n }\n}\n"],"mappings":";AAAA,SAAS,mBAA2D;;;ACApE,OAAO,gBAAsC;AAC7C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAKK;AAaA,SAAS,uBAAuB,MAGtB;AACf,QAAM,MAAM,KAAK,OAAO,MAAM,EAAE,SAAS,SAAS,UAAU,OAAO,CAAC;AACpE,MAAI,MAA8B,KAAK,UAAU,CAAC;AAClD,MAAI,cAAkC;AAEtC,WAAS,kBAA+B;AACtC,QAAI,YAAa,QAAO;AACxB,QAAI,CAAC,IAAI,MAAM;AACb,YAAM,IAAI,aAAa,uBAAuB,8CAA8C;AAAA,IAC9F;AACA,kBAAc,WAAW,gBAAgB;AAAA,MACvC,MAAM,IAAI,KAAK;AAAA,MACf,MAAM,IAAI,KAAK;AAAA,MACf,QAAQ,IAAI,KAAK,UAAU,IAAI,KAAK,SAAS;AAAA,MAC7C,GAAI,IAAI,KAAK,SAAS,UAAa,IAAI,KAAK,SAAS,SACjD,EAAE,MAAM,EAAE,MAAM,IAAI,KAAK,MAAM,MAAM,IAAI,KAAK,KAAK,EAAE,IACrD,CAAC;AAAA,IACP,CAAC;AACD,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,cAAc;AAAA,IACd,cAAc,MAAM,CAAC,iBAAiB,WAAW;AAAA,IACjD,gBAAgB,CAAC;AAAA,IACjB,gBAAgB,CAAC,kBAAkB,mBAAmB,sBAAsB,kBAAkB;AAAA,IAC9F,MAAM,WAAW,WAAW;AAC1B,YAAM,EAAE,GAAG,KAAK,GAAI,UAAqC;AACzD,sBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,cAAc;AAClB,UAAI;AACF,cAAM,gBAAgB,EAAE,OAAO;AAC/B,eAAO,EAAE,QAAQ,WAAW,QAAQ,gBAAgB;AAAA,MACtD,SAAS,KAAK;AACZ,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,QAAQ,uBAAuB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QACjF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,kBAAkB,QAA4B;AAClD,YAAM,WAAW,oBAAoB,OAAO,UAAU,OAAO,IAAI;AACjE,YAAM,OAAO,MAAM,gBAAgB,EAAE,SAAS;AAAA,QAC5C,MAAM,IAAI,QAAQ;AAAA,QAClB,IAAI,OAAO;AAAA,QACX,SAAS,SAAS;AAAA,QAClB,MAAM,SAAS;AAAA,QACf,MAAM,SAAS;AAAA,MACjB,CAAC;AACD,aAAO,EAAE,WAAW,KAAK,aAAa,MAAM,WAAW,KAAK,YAAY,CAAC,GAAG,SAAS,EAAE;AAAA,IACzF;AAAA,IAEA,MAAM,mBAAmB,UAA2B;AAClD,UAAI,MAAM,EAAE,YAAY,SAAS,WAAW,GAAG,iCAAiC;AAAA,IAClF;AAAA,IAEA,MAAM,uBAAuB,YAAoB;AAC/C,UAAI,MAAM,EAAE,WAAW,GAAG,qCAAqC;AAAA,IACjE;AAAA,EACF;AACF;;;ADrFO,SAAS,cAAc,KAAmC;AAC/D,MAAI,IAAI,SAAS,SAAS;AACxB,UAAM,IAAI,YAAY,4CAA4C,IAAI,IAAI,IAAI;AAAA,MAC5E,MAAM;AAAA,MACN,SAAS,EAAE,MAAM,IAAI,KAAK;AAAA,IAC5B,CAAC;AAAA,EACH;AACA,SAAO,uBAAuB;AAAA,IAC5B,QAAQ,IAAI;AAAA,IACZ,QAAQ,IAAI;AAAA,EACd,CAAC;AACH;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nymbalhq/adapter-smtp",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "SMTP email for Nymbal: the `smtp` email provider (nodemailer)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/nymbalhq/nymbal.git",
|
|
9
|
+
"directory": "packages/adapter-smtp"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=22"
|
|
17
|
+
},
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@nymbalhq/types": "1.1.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"nodemailer": "^6.9.16"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/nodemailer": "^6.4.17",
|
|
37
|
+
"nodemailer": "^6.9.16",
|
|
38
|
+
"tsup": "^8.3.5",
|
|
39
|
+
"typescript": "^5.6.3",
|
|
40
|
+
"vitest": "^3.0.0"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsup",
|
|
44
|
+
"dev": "tsup --watch",
|
|
45
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
46
|
+
}
|
|
6
47
|
}
|
package/README.md
DELETED