@nymbalhq/adapter-smtp 1.0.0 → 1.2.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 CHANGED
@@ -1,4 +1,4 @@
1
- import { Logger, EmailAdapter, AdapterContext } from '@nymbalhq/types';
1
+ import { Logger, EmailAdapter, AdapterContext, AdapterSetting } from '@nymbalhq/types';
2
2
 
3
3
  interface SmtpEmailAdapterConfig {
4
4
  from?: string;
@@ -15,7 +15,10 @@ declare function createSmtpEmailAdapter(deps: {
15
15
  config?: SmtpEmailAdapterConfig;
16
16
  }): EmailAdapter;
17
17
 
18
+ /** The SMTP login the admin's Settings may fill; a server that takes mail without one needs neither. */
19
+ declare const settings: AdapterSetting[];
18
20
  /** The kernel's entry: `email: { provider: 'smtp', config: SmtpEmailAdapterConfig }`. */
19
21
  declare function createAdapter(ctx: AdapterContext): EmailAdapter;
20
22
 
21
- export { type SmtpEmailAdapterConfig, createAdapter, createSmtpEmailAdapter };
23
+ export { createAdapter, createSmtpEmailAdapter, settings };
24
+ export type { SmtpEmailAdapterConfig };
package/dist/index.js CHANGED
@@ -1,16 +1,21 @@
1
1
  // src/index.ts
2
- import { ConfigError } from "@nymbalhq/types";
2
+ import {
3
+ ConfigError
4
+ } from "@nymbalhq/types";
3
5
 
4
6
  // src/smtp-adapter.ts
5
7
  import nodemailer from "nodemailer";
6
8
  import {
7
9
  AdapterError,
10
+ boundedHealthCheck,
11
+ errorText,
8
12
  EVT_CART_ABANDONED,
9
13
  EVT_CUSTOMER_CREATED,
10
14
  EVT_ORDER_PLACED,
11
15
  EVT_ORDER_SHIPPED,
12
16
  renderEmailTemplate
13
17
  } from "@nymbalhq/types";
18
+ var SMTP_TIMEOUT_MS = 4e3;
14
19
  function createSmtpEmailAdapter(deps) {
15
20
  const log = deps.logger.child({ adapter: "email", provider: "smtp" });
16
21
  let cfg = deps.config ?? {};
@@ -24,6 +29,10 @@ function createSmtpEmailAdapter(deps) {
24
29
  host: cfg.smtp.host,
25
30
  port: cfg.smtp.port,
26
31
  secure: cfg.smtp.secure ?? cfg.smtp.port === 465,
32
+ // nodemailer waits two minutes to connect by default; a host that drops packets must fail inside the 6 s bound.
33
+ connectionTimeout: SMTP_TIMEOUT_MS,
34
+ greetingTimeout: SMTP_TIMEOUT_MS,
35
+ socketTimeout: SMTP_TIMEOUT_MS,
27
36
  ...cfg.smtp.user !== void 0 && cfg.smtp.pass !== void 0 ? { auth: { user: cfg.smtp.user, pass: cfg.smtp.pass } } : {}
28
37
  });
29
38
  return transporter;
@@ -39,15 +48,18 @@ function createSmtpEmailAdapter(deps) {
39
48
  ensureTransport();
40
49
  },
41
50
  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
+ return boundedHealthCheck(async () => {
52
+ try {
53
+ await ensureTransport().verify();
54
+ return { status: "healthy", detail: "SMTP verified" };
55
+ } catch (err) {
56
+ const reachable = err?.code === "EAUTH";
57
+ return {
58
+ status: reachable ? "degraded" : "down",
59
+ detail: `SMTP verify failed: ${errorText(err)}`
60
+ };
61
+ }
62
+ });
51
63
  },
52
64
  async sendTransactional(params) {
53
65
  const rendered = renderEmailTemplate(params.template, params.vars);
@@ -70,6 +82,10 @@ function createSmtpEmailAdapter(deps) {
70
82
  }
71
83
 
72
84
  // src/index.ts
85
+ var settings = [
86
+ { path: "smtp.user", label: "SMTP user", secret: true, optional: true },
87
+ { path: "smtp.pass", label: "SMTP password", secret: true, optional: true }
88
+ ];
73
89
  function createAdapter(ctx) {
74
90
  if (ctx.slot !== "email") {
75
91
  throw new ConfigError(`@nymbalhq/adapter-smtp serves email, not ${ctx.slot}`, {
@@ -84,6 +100,7 @@ function createAdapter(ctx) {
84
100
  }
85
101
  export {
86
102
  createAdapter,
87
- createSmtpEmailAdapter
103
+ createSmtpEmailAdapter,
104
+ settings
88
105
  };
89
106
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +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":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/smtp-adapter.ts"],"sourcesContent":["import {\n ConfigError,\n type AdapterContext,\n type AdapterSetting,\n type EmailAdapter,\n} from '@nymbalhq/types'\nimport { createSmtpEmailAdapter, type SmtpEmailAdapterConfig } from './smtp-adapter.js'\n\nexport { createSmtpEmailAdapter, type SmtpEmailAdapterConfig }\n\n/** The SMTP login the admin's Settings may fill; a server that takes mail without one needs neither. */\nexport const settings: AdapterSetting[] = [\n { path: 'smtp.user', label: 'SMTP user', secret: true, optional: true },\n { path: 'smtp.pass', label: 'SMTP password', secret: true, optional: true },\n]\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 boundedHealthCheck,\n errorText,\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\nconst SMTP_TIMEOUT_MS = 4000\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 // nodemailer waits two minutes to connect by default; a host that drops packets must fail inside the 6 s bound.\n connectionTimeout: SMTP_TIMEOUT_MS,\n greetingTimeout: SMTP_TIMEOUT_MS,\n socketTimeout: SMTP_TIMEOUT_MS,\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 return boundedHealthCheck(async () => {\n try {\n await ensureTransport().verify()\n return { status: 'healthy', detail: 'SMTP verified' }\n } catch (err) {\n // A server that answers and refuses the login is there but unusable; one that cannot be reached is down.\n const reachable = (err as { code?: unknown } | null)?.code === 'EAUTH'\n return {\n status: reachable ? 'degraded' : 'down',\n detail: `SMTP verify failed: ${errorText(err)}`,\n }\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":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nymbalhq/adapter-smtp",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "SMTP email for Nymbal: the `smtp` email provider (nodemailer)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,21 +27,22 @@
27
27
  "dist"
28
28
  ],
29
29
  "dependencies": {
30
- "@nymbalhq/types": "1.0.0"
30
+ "@nymbalhq/types": "1.2.0"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "nodemailer": "^6.9.16"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/nodemailer": "^6.4.17",
37
+ "@typescript/native": "npm:typescript@^7.0.2",
37
38
  "nodemailer": "^6.9.16",
38
- "tsup": "^8.3.5",
39
- "typescript": "^5.6.3",
39
+ "tsup": "^9.0.0",
40
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
40
41
  "vitest": "^3.0.0"
41
42
  },
42
43
  "scripts": {
43
44
  "build": "tsup",
44
45
  "dev": "tsup --watch",
45
- "typecheck": "tsc -p tsconfig.json --noEmit"
46
+ "typecheck": "tsc -p tsconfig.json --noEmit --rootDir ../.."
46
47
  }
47
48
  }