sently 0.7.2 → 0.9.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/AGENTS.md +34 -0
- package/CHANGELOG.md +49 -1
- package/README.md +89 -776
- package/dist/adapters/bun.js +1 -1
- package/dist/adapters/cf.js +2 -2
- package/dist/adapters/cf.js.map +2 -2
- package/dist/adapters/deno.js +1 -1
- package/dist/adapters/node.js +1 -1
- package/dist/auth/oauth2.js +2 -2
- package/dist/auth/oauth2.js.map +1 -1
- package/dist/chunk-0gqy32pe.js +4 -0
- package/dist/chunk-0gqy32pe.js.map +10 -0
- package/dist/{chunk-kebhy657.js → chunk-0qxws3kj.js} +2 -2
- package/dist/{chunk-kebhy657.js.map → chunk-0qxws3kj.js.map} +1 -1
- package/dist/chunk-29z7fkzy.js +4 -0
- package/dist/{chunk-74rha3ac.js.map → chunk-29z7fkzy.js.map} +2 -2
- package/dist/chunk-5915vbcj.js +4 -0
- package/dist/chunk-5915vbcj.js.map +10 -0
- package/dist/{chunk-keksa6fx.js → chunk-6npp3x3c.js} +2 -2
- package/dist/{chunk-keksa6fx.js.map → chunk-6npp3x3c.js.map} +1 -1
- package/dist/chunk-7gy2q9sh.js +4 -0
- package/dist/chunk-7gy2q9sh.js.map +10 -0
- package/dist/{chunk-gkkv7yqm.js → chunk-8dty8c4p.js} +3 -3
- package/dist/{chunk-gkkv7yqm.js.map → chunk-8dty8c4p.js.map} +2 -2
- package/dist/{chunk-qp4y0kp2.js → chunk-8njyga62.js} +2 -2
- package/dist/{chunk-qp4y0kp2.js.map → chunk-8njyga62.js.map} +1 -1
- package/dist/{chunk-gh5n759p.js → chunk-93vqxxj2.js} +3 -3
- package/dist/{chunk-gh5n759p.js.map → chunk-93vqxxj2.js.map} +1 -1
- package/dist/chunk-b602dhck.js +4 -0
- package/dist/{chunk-rm78s1ek.js.map → chunk-b602dhck.js.map} +2 -2
- package/dist/chunk-gp4fwrs8.js +4 -0
- package/dist/chunk-gp4fwrs8.js.map +10 -0
- package/dist/chunk-n0qeyzqm.js +13 -0
- package/dist/{chunk-cvrbfke9.js.map → chunk-n0qeyzqm.js.map} +2 -2
- package/dist/chunk-qtkd5bak.js +4 -0
- package/dist/chunk-qtkd5bak.js.map +11 -0
- package/dist/chunk-skqhj0rm.js +4 -0
- package/dist/{chunk-r7ee9tk7.js.map → chunk-skqhj0rm.js.map} +2 -2
- package/dist/{chunk-a1c87hn1.js → chunk-tamww15j.js} +2 -2
- package/dist/{chunk-a1c87hn1.js.map → chunk-tamww15j.js.map} +1 -1
- package/dist/chunk-vjpds2pf.js +3 -0
- package/dist/chunk-vjpds2pf.js.map +10 -0
- package/dist/chunk-yp311efr.js +4 -0
- package/dist/chunk-yp311efr.js.map +10 -0
- package/dist/chunk-ywq6s10g.js +5 -0
- package/dist/{chunk-d5rwm3br.js.map → chunk-ywq6s10g.js.map} +3 -3
- package/dist/core/base64.d.ts +10 -0
- package/dist/core/errors.js +2 -2
- package/dist/core/errors.js.map +1 -1
- package/dist/core/hooks.d.ts +14 -0
- package/dist/core/plugin.d.ts +7 -7
- package/dist/core/provider-label.d.ts +6 -0
- package/dist/core/push-endpoint.d.ts +29 -0
- package/dist/core/push-types.d.ts +98 -0
- package/dist/core/sms-types.d.ts +76 -0
- package/dist/core/smtp.js +2 -2
- package/dist/core/smtp.js.map +1 -1
- package/dist/core/types.d.ts +27 -4
- package/dist/core/whatsapp-types.d.ts +116 -0
- package/dist/detect.js +2 -2
- package/dist/detect.js.map +1 -1
- package/dist/dkim.js +2 -2
- package/dist/dkim.js.map +2 -2
- package/dist/idempotency.d.ts +1 -0
- package/dist/idempotency.js +2 -2
- package/dist/idempotency.js.map +3 -3
- package/dist/index.d.ts +24 -2
- package/dist/index.js +23 -0
- package/dist/mailer.js +2 -2
- package/dist/mailer.js.map +1 -1
- package/dist/observability/console.d.ts +12 -0
- package/dist/observability/console.js +3 -0
- package/dist/observability/console.js.map +10 -0
- package/dist/plugins/react.js +2 -2
- package/dist/plugins/react.js.map +2 -2
- package/dist/plugins/template.js +1 -1
- package/dist/pool/pool.js +2 -2
- package/dist/pool/pool.js.map +1 -1
- package/dist/push.d.ts +26 -0
- package/dist/push.js +3 -0
- package/dist/push.js.map +10 -0
- package/dist/sms.d.ts +26 -0
- package/dist/sms.js +3 -0
- package/dist/sms.js.map +10 -0
- package/dist/smtp-mailer.js +2 -2
- package/dist/smtp-mailer.js.map +2 -2
- package/dist/transports/brevo.d.ts +1 -0
- package/dist/transports/brevo.js +2 -2
- package/dist/transports/brevo.js.map +3 -3
- package/dist/transports/cloudflare-email.d.ts +58 -0
- package/dist/transports/cloudflare-email.js +3 -0
- package/dist/transports/cloudflare-email.js.map +10 -0
- package/dist/transports/fallback.d.ts +100 -0
- package/dist/transports/fallback.js +3 -0
- package/dist/transports/fallback.js.map +9 -0
- package/dist/transports/loops.d.ts +32 -0
- package/dist/transports/loops.js +3 -0
- package/dist/transports/loops.js.map +10 -0
- package/dist/transports/mailersend.d.ts +27 -0
- package/dist/transports/mailersend.js +3 -0
- package/dist/transports/mailersend.js.map +10 -0
- package/dist/transports/mailgun.d.ts +1 -0
- package/dist/transports/mailgun.js +2 -2
- package/dist/transports/mailgun.js.map +3 -3
- package/dist/transports/mailtrap.d.ts +36 -0
- package/dist/transports/mailtrap.js +3 -0
- package/dist/transports/mailtrap.js.map +10 -0
- package/dist/transports/msegat.d.ts +132 -0
- package/dist/transports/msegat.js +3 -0
- package/dist/transports/msegat.js.map +10 -0
- package/dist/transports/plunk.d.ts +29 -0
- package/dist/transports/plunk.js +3 -0
- package/dist/transports/plunk.js.map +10 -0
- package/dist/transports/postmark.d.ts +1 -0
- package/dist/transports/postmark.js +2 -2
- package/dist/transports/postmark.js.map +3 -3
- package/dist/transports/preview.d.ts +1 -0
- package/dist/transports/preview.js +2 -2
- package/dist/transports/preview.js.map +3 -3
- package/dist/transports/resend.d.ts +1 -0
- package/dist/transports/resend.js +2 -2
- package/dist/transports/resend.js.map +3 -3
- package/dist/transports/retry.d.ts +1 -0
- package/dist/transports/retry.js +2 -2
- package/dist/transports/retry.js.map +3 -3
- package/dist/transports/sendgrid.d.ts +1 -0
- package/dist/transports/sendgrid.js +2 -2
- package/dist/transports/sendgrid.js.map +3 -3
- package/dist/transports/ses.d.ts +1 -0
- package/dist/transports/ses.js +3 -3
- package/dist/transports/ses.js.map +3 -3
- package/dist/transports/smtp.d.ts +1 -0
- package/dist/transports/smtp.js +2 -2
- package/dist/transports/smtp.js.map +1 -1
- package/dist/transports/sndr.d.ts +41 -0
- package/dist/transports/sndr.js +3 -0
- package/dist/transports/sndr.js.map +10 -0
- package/dist/transports/sparkpost.d.ts +30 -0
- package/dist/transports/sparkpost.js +3 -0
- package/dist/transports/sparkpost.js.map +10 -0
- package/dist/transports/taqnyat-mail.d.ts +33 -0
- package/dist/transports/taqnyat-mail.js +3 -0
- package/dist/transports/taqnyat-mail.js.map +10 -0
- package/dist/transports/taqnyat-phone.d.ts +4 -0
- package/dist/transports/taqnyat-sms.d.ts +131 -0
- package/dist/transports/taqnyat-sms.js +3 -0
- package/dist/transports/taqnyat-sms.js.map +10 -0
- package/dist/transports/taqnyat-whatsapp.d.ts +51 -0
- package/dist/transports/taqnyat-whatsapp.js +3 -0
- package/dist/transports/taqnyat-whatsapp.js.map +10 -0
- package/dist/transports/twilio-sms.d.ts +66 -0
- package/dist/transports/twilio-sms.js +3 -0
- package/dist/transports/twilio-sms.js.map +10 -0
- package/dist/transports/webpush.d.ts +46 -0
- package/dist/transports/webpush.js +3 -0
- package/dist/transports/webpush.js.map +10 -0
- package/dist/transports/weighted-fallback.d.ts +65 -0
- package/dist/transports/weighted-fallback.js +3 -0
- package/dist/transports/weighted-fallback.js.map +10 -0
- package/dist/transports/whatsapp-cloud.d.ts +56 -0
- package/dist/transports/whatsapp-cloud.js +3 -0
- package/dist/transports/whatsapp-cloud.js.map +10 -0
- package/dist/webhooks/brevo.js +1 -1
- package/dist/webhooks/mailgun.js +1 -1
- package/dist/webhooks/postmark.js +1 -1
- package/dist/webhooks/resend.js +1 -1
- package/dist/webhooks/sendgrid.js +1 -1
- package/dist/webhooks/ses.js +1 -1
- package/dist/webhooks/sndr.d.ts +16 -0
- package/dist/webhooks/sndr.js +3 -0
- package/dist/webhooks/sndr.js.map +10 -0
- package/dist/webhooks/timing-safe-equal.js +1 -1
- package/dist/webhooks.d.ts +9 -2
- package/dist/webhooks.js +4 -0
- package/dist/whatsapp.d.ts +26 -0
- package/dist/whatsapp.js +3 -0
- package/dist/whatsapp.js.map +10 -0
- package/package.json +121 -4
- package/site/README.md +26 -0
- package/site/content/docs/ai/index.mdx +13 -0
- package/site/content/docs/ai/llms-txt.mdx +12 -0
- package/site/content/docs/ai/mcp.mdx +16 -0
- package/site/content/docs/ai/meta.json +9 -0
- package/site/content/docs/channels/email.mdx +100 -0
- package/site/content/docs/channels/hooks.mdx +87 -0
- package/site/content/docs/channels/index.mdx +81 -0
- package/site/content/docs/channels/meta.json +12 -0
- package/site/content/docs/channels/push.mdx +92 -0
- package/site/content/docs/channels/sms.mdx +88 -0
- package/site/content/docs/channels/whatsapp.mdx +95 -0
- package/site/content/docs/decorators/fallback.mdx +34 -0
- package/site/content/docs/decorators/idempotency.mdx +34 -0
- package/site/content/docs/decorators/index.mdx +50 -0
- package/site/content/docs/decorators/meta.json +12 -0
- package/site/content/docs/decorators/preview.mdx +34 -0
- package/site/content/docs/decorators/retry.mdx +34 -0
- package/site/content/docs/decorators/weighted-fallback.mdx +34 -0
- package/site/content/docs/get-started/entrypoints.mdx +114 -0
- package/site/content/docs/get-started/index.mdx +14 -0
- package/site/content/docs/get-started/installation.mdx +78 -0
- package/site/content/docs/get-started/introduction.mdx +77 -0
- package/site/content/docs/get-started/meta.json +12 -0
- package/site/content/docs/get-started/migrate-nodemailer.mdx +23 -0
- package/site/content/docs/get-started/runtimes.mdx +17 -0
- package/site/content/docs/guides/adapters.mdx +26 -0
- package/site/content/docs/guides/attachments.mdx +19 -0
- package/site/content/docs/guides/bundle-size.mdx +78 -0
- package/site/content/docs/guides/dkim.mdx +26 -0
- package/site/content/docs/guides/index.mdx +15 -0
- package/site/content/docs/guides/meta.json +21 -0
- package/site/content/docs/guides/oauth2.mdx +23 -0
- package/site/content/docs/guides/observability.mdx +16 -0
- package/site/content/docs/guides/plugins-template.mdx +22 -0
- package/site/content/docs/guides/pool.mdx +24 -0
- package/site/content/docs/guides/react-email.mdx +20 -0
- package/site/content/docs/guides/security.mdx +17 -0
- package/site/content/docs/guides/send-bulk.mdx +18 -0
- package/site/content/docs/guides/vendor-extras-otp.mdx +18 -0
- package/site/content/docs/guides/webhooks.mdx +120 -0
- package/site/content/docs/guides/webpush-interop.mdx +18 -0
- package/site/content/docs/index.mdx +15 -0
- package/site/content/docs/meta.json +15 -0
- package/site/content/docs/quick-start/email.mdx +40 -0
- package/site/content/docs/quick-start/index.mdx +15 -0
- package/site/content/docs/quick-start/meta.json +5 -0
- package/site/content/docs/quick-start/push.mdx +36 -0
- package/site/content/docs/quick-start/sms.mdx +36 -0
- package/site/content/docs/quick-start/whatsapp.mdx +38 -0
- package/site/content/docs/reference/errors.mdx +22 -0
- package/site/content/docs/reference/exports.mdx +83 -0
- package/site/content/docs/reference/index.mdx +15 -0
- package/site/content/docs/reference/mail-options.mdx +22 -0
- package/site/content/docs/reference/meta.json +15 -0
- package/site/content/docs/reference/push-options.mdx +20 -0
- package/site/content/docs/reference/sms-options.mdx +17 -0
- package/site/content/docs/reference/transport-contracts.mdx +17 -0
- package/site/content/docs/reference/webhook-events.mdx +70 -0
- package/site/content/docs/reference/whatsapp-options.mdx +15 -0
- package/site/content/docs/transports/brevo.mdx +40 -0
- package/site/content/docs/transports/cloudflare-email.mdx +40 -0
- package/site/content/docs/transports/index.mdx +105 -0
- package/site/content/docs/transports/loops.mdx +41 -0
- package/site/content/docs/transports/mailersend.mdx +40 -0
- package/site/content/docs/transports/mailgun.mdx +42 -0
- package/site/content/docs/transports/mailtrap.mdx +42 -0
- package/site/content/docs/transports/meta.json +32 -0
- package/site/content/docs/transports/msegat.mdx +42 -0
- package/site/content/docs/transports/plunk.mdx +40 -0
- package/site/content/docs/transports/postmark.mdx +40 -0
- package/site/content/docs/transports/resend.mdx +43 -0
- package/site/content/docs/transports/sendgrid.mdx +40 -0
- package/site/content/docs/transports/ses.mdx +44 -0
- package/site/content/docs/transports/smtp.mdx +47 -0
- package/site/content/docs/transports/sndr.mdx +144 -0
- package/site/content/docs/transports/sparkpost.mdx +41 -0
- package/site/content/docs/transports/taqnyat-mail.mdx +41 -0
- package/site/content/docs/transports/taqnyat-sms.mdx +41 -0
- package/site/content/docs/transports/taqnyat-whatsapp.mdx +40 -0
- package/site/content/docs/transports/twilio-sms.mdx +128 -0
- package/site/content/docs/transports/webpush.mdx +43 -0
- package/site/content/docs/transports/whatsapp-cloud.mdx +42 -0
- package/dist/chunk-4mvp3b85.js +0 -3
- package/dist/chunk-4mvp3b85.js.map +0 -10
- package/dist/chunk-74rha3ac.js +0 -4
- package/dist/chunk-cvrbfke9.js +0 -13
- package/dist/chunk-d5rwm3br.js +0 -5
- package/dist/chunk-ka6t8g77.js +0 -4
- package/dist/chunk-ka6t8g77.js.map +0 -11
- package/dist/chunk-r7ee9tk7.js +0 -4
- package/dist/chunk-rm78s1ek.js +0 -4
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: SMTP
|
|
3
|
+
description: Connect to an SMTP relay with an explicit transport.
|
|
4
|
+
icon: Truck
|
|
5
|
+
source: "src/transports/smtp.ts"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Connect to an SMTP relay with an explicit transport.
|
|
9
|
+
|
|
10
|
+
<Callout title="The one rule">Create this transport under the matching sently channel sender.</Callout>
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
| Option | Type | Default or requirement |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| `host` | `string` | required |
|
|
17
|
+
| `port` | `number` | 587 or 465 based on secure |
|
|
18
|
+
| `secure` | `boolean` | false |
|
|
19
|
+
| `auth` | `SMTPAuth` | optional |
|
|
20
|
+
| `requireTLS` | `boolean` | true when auth is set |
|
|
21
|
+
| `adapter` | `SocketAdapter` | optional |
|
|
22
|
+
| `pool` | `boolean` | false |
|
|
23
|
+
| `dkim` | `DKIMConfig` | optional |
|
|
24
|
+
|
|
25
|
+
<Steps>
|
|
26
|
+
<Step title="Configure the transport">
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { createMailer } from "sently/mailer";
|
|
30
|
+
import { SMTPTransport } from "sently/transports/smtp";
|
|
31
|
+
|
|
32
|
+
const sender = createMailer({ transport: new SMTPTransport({ host: "smtp.example.com", auth: { user: "user@example.com", pass: "..." } }) });
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
</Step>
|
|
36
|
+
<Step title="Send with the channel API">
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
await sender.send({ from: "hello@example.com", to: "person@example.com", subject: "Hello", text: "Hi" });
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
</Step>
|
|
43
|
+
</Steps>
|
|
44
|
+
|
|
45
|
+
<Accordions><Accordion title="Should I call the provider SDK?">No. Use the sently sender; provider-specific extras stay on the transport instance.</Accordion></Accordions>
|
|
46
|
+
|
|
47
|
+
<Cards><Card title="Email channel" href="/docs/channels/email" /></Cards>
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: SNDR
|
|
3
|
+
description: Send transactional email through the SNDR HTTP API with createMailer.
|
|
4
|
+
icon: Truck
|
|
5
|
+
source: "src/transports/sndr.ts"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Use SNDR when you want an HTTP email API for receipts, sign-in mail, and other app-triggered messages.
|
|
9
|
+
Wire `SndrTransport` into `createMailer` — do not call the vendor SDK from application code.
|
|
10
|
+
|
|
11
|
+
<Callout title="The one rule">
|
|
12
|
+
Import from `sently/transports/sndr`, pass the transport to `await createMailer(...)`, and send
|
|
13
|
+
with the mailer — keep delivery on the email channel.
|
|
14
|
+
</Callout>
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
|
|
20
|
+
<Step>
|
|
21
|
+
### Configure
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { createMailer } from "sently/mailer";
|
|
25
|
+
import { SndrTransport } from "sently/transports/sndr";
|
|
26
|
+
|
|
27
|
+
const mailer = await createMailer({
|
|
28
|
+
transport: new SndrTransport({
|
|
29
|
+
apiKey: process.env.SNDR_API_KEY!,
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
</Step>
|
|
35
|
+
|
|
36
|
+
<Step>
|
|
37
|
+
### Send
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
const result = await mailer.send({
|
|
41
|
+
from: "hello@yourdomain.com",
|
|
42
|
+
to: "customer@example.com",
|
|
43
|
+
subject: "Welcome aboard",
|
|
44
|
+
html: "<p>Thanks for joining us.</p>",
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
</Step>
|
|
49
|
+
|
|
50
|
+
<Step>
|
|
51
|
+
### See the result
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
console.log(result.messageId); // e.g. em_…
|
|
55
|
+
console.log(result.response); // e.g. queued
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`from` must use a domain verified in the SNDR dashboard.
|
|
59
|
+
|
|
60
|
+
</Step>
|
|
61
|
+
|
|
62
|
+
</Steps>
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
| Option | Type | Default | Meaning |
|
|
67
|
+
| ------ | ---- | ------- | ------- |
|
|
68
|
+
| `apiKey` | `string` | required | Bearer key (`sndr_live_…` / `sndr_test_…`). |
|
|
69
|
+
| `baseUrl` | `string` | `https://api.sndr.sh` | API origin (no trailing slash needed). |
|
|
70
|
+
| `defaultTemplateId` | `string` | — | Default SNDR `template_id` when not set per message. |
|
|
71
|
+
|
|
72
|
+
## Message mapping
|
|
73
|
+
|
|
74
|
+
| Mail option | SNDR field | Notes |
|
|
75
|
+
| ----------- | ---------- | ----- |
|
|
76
|
+
| `from` | `from` | MIME form `Name <addr>` when a display name is present. |
|
|
77
|
+
| `to` / `cc` / `bcc` | `to` / `cc` / `bcc` | Arrays of email addresses. |
|
|
78
|
+
| `replyTo` | `reply_to` | First address only. |
|
|
79
|
+
| `subject` | `subject` | Required. |
|
|
80
|
+
| `html` / `text` | `html` / `text` | Optional body fields. |
|
|
81
|
+
| `headers` | `headers` | Custom headers; template header is stripped. |
|
|
82
|
+
| `idempotencyKey` / `messageId` | `Idempotency-Key` | Sent when present. |
|
|
83
|
+
| `data` | `variables` | Only when a template id is set. |
|
|
84
|
+
|
|
85
|
+
## Templates
|
|
86
|
+
|
|
87
|
+
Set a template with the `x-sndr-template-id` header (or `defaultTemplateId` on the transport).
|
|
88
|
+
Pass template variables through `data`.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { SNDR_TEMPLATE_ID_HEADER } from "sently/transports/sndr";
|
|
92
|
+
|
|
93
|
+
await mailer.send({
|
|
94
|
+
from: "hello@yourdomain.com",
|
|
95
|
+
to: "customer@example.com",
|
|
96
|
+
subject: "Welcome",
|
|
97
|
+
headers: { [SNDR_TEMPLATE_ID_HEADER]: "tpl_welcome" },
|
|
98
|
+
data: { name: "Ada" },
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Verify and errors
|
|
103
|
+
|
|
104
|
+
`verify()` calls `GET /v1/domains` with the same Bearer key.
|
|
105
|
+
|
|
106
|
+
Failed HTTP responses throw `SndrError` (`SentlyError`) with the API `error.message` when present.
|
|
107
|
+
|
|
108
|
+
## Troubleshooting
|
|
109
|
+
|
|
110
|
+
<Accordions>
|
|
111
|
+
|
|
112
|
+
<Accordion title="from domain is not verified">
|
|
113
|
+
|
|
114
|
+
SNDR rejects sends from unverified domains. Verify DNS in the SNDR dashboard, then retry with that domain in `from`.
|
|
115
|
+
|
|
116
|
+
</Accordion>
|
|
117
|
+
|
|
118
|
+
<Accordion title="Should I import SndrTransport from sently?">
|
|
119
|
+
|
|
120
|
+
No. Use `sently/transports/sndr`. The main `sently` package does not re-export HTTP provider transports.
|
|
121
|
+
|
|
122
|
+
</Accordion>
|
|
123
|
+
|
|
124
|
+
<Accordion title="Should I call the @rkiza/sndr SDK?">
|
|
125
|
+
|
|
126
|
+
No. Use `createMailer` plus `SndrTransport`. Keep vendor extras off the shared email contract.
|
|
127
|
+
|
|
128
|
+
</Accordion>
|
|
129
|
+
|
|
130
|
+
</Accordions>
|
|
131
|
+
|
|
132
|
+
## Learn more
|
|
133
|
+
|
|
134
|
+
- [Email channel](/docs/channels/email) — mailer options and send pipeline
|
|
135
|
+
- [Webhooks](/docs/guides/webhooks) — SNDR delivery events and signature verification
|
|
136
|
+
- [Bundle size](/docs/guides/bundle-size) — why subpath imports stay small
|
|
137
|
+
|
|
138
|
+
## Next
|
|
139
|
+
|
|
140
|
+
<Cards>
|
|
141
|
+
<Card title="Email channel" description="createMailer and MailOptions." href="/docs/channels/email" />
|
|
142
|
+
<Card title="Webhooks" description="Parse and verify SNDR events." href="/docs/guides/webhooks" />
|
|
143
|
+
<Card title="Transports" description="All provider transports by channel." href="/docs/transports" />
|
|
144
|
+
</Cards>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: SparkPost
|
|
3
|
+
description: Send email through the SparkPost transmissions API.
|
|
4
|
+
icon: Truck
|
|
5
|
+
source: "src/transports/sparkpost.ts"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Send email through the SparkPost transmissions API.
|
|
9
|
+
|
|
10
|
+
<Callout title="The one rule">Create this transport under the matching sently channel sender.</Callout>
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
| Option | Type | Default or requirement |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| `apiKey` | `string` | required |
|
|
17
|
+
| `euRegion` | `boolean` | false |
|
|
18
|
+
|
|
19
|
+
<Steps>
|
|
20
|
+
<Step title="Configure the transport">
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { createMailer } from "sently/mailer";
|
|
24
|
+
import { SparkPostTransport } from "sently/transports/sparkpost";
|
|
25
|
+
|
|
26
|
+
const sender = createMailer({ transport: new SparkPostTransport({ apiKey: process.env.SPARKPOST_API_KEY! }) });
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
</Step>
|
|
30
|
+
<Step title="Send with the channel API">
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
await sender.send({ from: "hello@example.com", to: "person@example.com", subject: "Hello", text: "Hi" });
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
</Step>
|
|
37
|
+
</Steps>
|
|
38
|
+
|
|
39
|
+
<Accordions><Accordion title="Should I call the provider SDK?">No. Use the sently sender; provider-specific extras stay on the transport instance.</Accordion></Accordions>
|
|
40
|
+
|
|
41
|
+
<Cards><Card title="Email channel" href="/docs/channels/email" /></Cards>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Taqnyat Mail
|
|
3
|
+
description: Send email through the Taqnyat Email API.
|
|
4
|
+
icon: Truck
|
|
5
|
+
source: "src/transports/taqnyat-mail.ts"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Send email through the Taqnyat Email API.
|
|
9
|
+
|
|
10
|
+
<Callout title="The one rule">Create this transport under the matching sently channel sender.</Callout>
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
| Option | Type | Default or requirement |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| `bearerToken` | `string` | required |
|
|
17
|
+
| `campaignName` | `string` | required |
|
|
18
|
+
|
|
19
|
+
<Steps>
|
|
20
|
+
<Step title="Configure the transport">
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { createMailer } from "sently/mailer";
|
|
24
|
+
import { TaqnyatMailTransport } from "sently/transports/taqnyat-mail";
|
|
25
|
+
|
|
26
|
+
const sender = createMailer({ transport: new TaqnyatMailTransport({ bearerToken: process.env.TAQNYAT_MAIL_TOKEN!, campaignName: "transactional" }) });
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
</Step>
|
|
30
|
+
<Step title="Send with the channel API">
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
await sender.send({ from: "hello@example.com", to: "person@example.com", subject: "Hello", text: "Hi" });
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
</Step>
|
|
37
|
+
</Steps>
|
|
38
|
+
|
|
39
|
+
<Accordions><Accordion title="Should I call the provider SDK?">No. Use the sently sender; provider-specific extras stay on the transport instance.</Accordion></Accordions>
|
|
40
|
+
|
|
41
|
+
<Cards><Card title="Email channel" href="/docs/channels/email" /></Cards>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Taqnyat SMS
|
|
3
|
+
description: Send SMS through the Taqnyat API.
|
|
4
|
+
icon: Truck
|
|
5
|
+
source: "src/transports/taqnyat-sms.ts"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Send SMS through the Taqnyat API.
|
|
9
|
+
|
|
10
|
+
<Callout title="The one rule">Create this transport under the matching sently channel sender.</Callout>
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
| Option | Type | Default or requirement |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| `bearerToken` | `string` | required |
|
|
17
|
+
| `sender` | `string` | required |
|
|
18
|
+
|
|
19
|
+
<Steps>
|
|
20
|
+
<Step title="Configure the transport">
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { createSmsSender } from "sently/sms";
|
|
24
|
+
import { TaqnyatSmsTransport } from "sently/transports/taqnyat-sms";
|
|
25
|
+
|
|
26
|
+
const sender = createSmsSender({ transport: new TaqnyatSmsTransport({ bearerToken: "...", sender: "MyBrand" }) });
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
</Step>
|
|
30
|
+
<Step title="Send with the channel API">
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
await sender.send({ to: "+15551234567", body: "Hello" });
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
</Step>
|
|
37
|
+
</Steps>
|
|
38
|
+
|
|
39
|
+
<Accordions><Accordion title="Should I call the provider SDK?">No. Use the sently sender; provider-specific extras stay on the transport instance.</Accordion></Accordions>
|
|
40
|
+
|
|
41
|
+
<Cards><Card title="Sms channel" href="/docs/channels/sms" /></Cards>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Taqnyat WhatsApp
|
|
3
|
+
description: Send WhatsApp templates and session text through Taqnyat.
|
|
4
|
+
icon: Truck
|
|
5
|
+
source: "src/transports/taqnyat-whatsapp.ts"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Send WhatsApp templates and session text through Taqnyat.
|
|
9
|
+
|
|
10
|
+
<Callout title="The one rule">Create this transport under the matching sently channel sender.</Callout>
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
| Option | Type | Default or requirement |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| `bearerToken` | `string` | required |
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
<Step title="Configure the transport">
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { createWhatsAppSender } from "sently/whatsapp";
|
|
23
|
+
import { TaqnyatWhatsAppTransport } from "sently/transports/taqnyat-whatsapp";
|
|
24
|
+
|
|
25
|
+
const sender = createWhatsAppSender({ transport: new TaqnyatWhatsAppTransport({ bearerToken: "..." }) });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
</Step>
|
|
29
|
+
<Step title="Send with the channel API">
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
await sender.send({ to: "15551234567", template: { name: "welcome", language: "en_US" } });
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
</Step>
|
|
36
|
+
</Steps>
|
|
37
|
+
|
|
38
|
+
<Accordions><Accordion title="Should I call the provider SDK?">No. Use the sently sender; provider-specific extras stay on the transport instance.</Accordion></Accordions>
|
|
39
|
+
|
|
40
|
+
<Cards><Card title="Whatsapp channel" href="/docs/channels/whatsapp" /></Cards>
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Twilio SMS
|
|
3
|
+
description: Send SMS through the Twilio Messages API with createSmsSender.
|
|
4
|
+
icon: Truck
|
|
5
|
+
source: "src/transports/twilio-sms.ts"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Use Twilio when your SMS traffic should go through the Programmable Messaging Messages API.
|
|
9
|
+
Wire `TwilioSmsTransport` into `createSmsSender` — Verify, Voice, and other Twilio products stay off this transport.
|
|
10
|
+
|
|
11
|
+
<Callout title="The one rule">
|
|
12
|
+
Provide `From` **or** `messagingServiceSid` (or both), keep E.164 `+` prefixes, and send only
|
|
13
|
+
through `createSmsSender`.
|
|
14
|
+
</Callout>
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
|
|
20
|
+
<Step>
|
|
21
|
+
### Configure
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { createSmsSender } from "sently/sms";
|
|
25
|
+
import { TwilioSmsTransport } from "sently/transports/twilio-sms";
|
|
26
|
+
|
|
27
|
+
const sms = createSmsSender({
|
|
28
|
+
transport: new TwilioSmsTransport({
|
|
29
|
+
accountSid: process.env.TWILIO_ACCOUNT_SID!,
|
|
30
|
+
apiKey: process.env.TWILIO_API_KEY!,
|
|
31
|
+
authToken: process.env.TWILIO_API_KEY_SECRET!,
|
|
32
|
+
from: "+15557654321",
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Production auth: API Key SID as Basic username and API Key Secret as password, with Account SID still in the URL path.
|
|
38
|
+
For local testing you may omit `apiKey` and use Account SID + Auth Token.
|
|
39
|
+
|
|
40
|
+
</Step>
|
|
41
|
+
|
|
42
|
+
<Step>
|
|
43
|
+
### Send
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const result = await sms.send({
|
|
47
|
+
to: "+15551234567",
|
|
48
|
+
body: "Your code is 123456",
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
</Step>
|
|
53
|
+
|
|
54
|
+
<Step>
|
|
55
|
+
### See the result
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
console.log(result.messageId); // SM…
|
|
59
|
+
console.log(result.status); // e.g. queued
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
</Step>
|
|
63
|
+
|
|
64
|
+
</Steps>
|
|
65
|
+
|
|
66
|
+
## Configuration
|
|
67
|
+
|
|
68
|
+
| Option | Type | Default | Meaning |
|
|
69
|
+
| ------ | ---- | ------- | ------- |
|
|
70
|
+
| `accountSid` | `string` | required | Account SID in the Messages URL path. |
|
|
71
|
+
| `authToken` | `string` | required | Auth Token, or API Key Secret when `apiKey` is set. |
|
|
72
|
+
| `apiKey` | `string` | — | API Key SID used as Basic-auth username (recommended in production). |
|
|
73
|
+
| `from` | `string` | — | Default From number / sender id. |
|
|
74
|
+
| `messagingServiceSid` | `string` | — | Messaging Service SID (`MG…`) as sender alternative. |
|
|
75
|
+
| `statusCallback` | `string` | — | Delivery status callback URL. |
|
|
76
|
+
|
|
77
|
+
Either `from` / `options.from` or `messagingServiceSid` is required for each send.
|
|
78
|
+
|
|
79
|
+
## Messaging Service
|
|
80
|
+
|
|
81
|
+
When `messagingServiceSid` is set and `From` is omitted, Twilio picks a sender from the service pool.
|
|
82
|
+
You may set both to pin a specific From inside that pool.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
const sms = createSmsSender({
|
|
86
|
+
transport: new TwilioSmsTransport({
|
|
87
|
+
accountSid: process.env.TWILIO_ACCOUNT_SID!,
|
|
88
|
+
authToken: process.env.TWILIO_AUTH_TOKEN!,
|
|
89
|
+
messagingServiceSid: "MG…",
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Verify and errors
|
|
95
|
+
|
|
96
|
+
`verify()` fetches `GET /2010-04-01/Accounts/{AccountSid}.json` with the same Basic credentials.
|
|
97
|
+
|
|
98
|
+
Failed Messages responses throw `TwilioSmsError` with Twilio’s `message` / `code` payload when present.
|
|
99
|
+
|
|
100
|
+
## Troubleshooting
|
|
101
|
+
|
|
102
|
+
<Accordions>
|
|
103
|
+
|
|
104
|
+
<Accordion title="Twilio SMS requires From or MessagingServiceSid">
|
|
105
|
+
|
|
106
|
+
Set `from` on the transport or per send, or configure `messagingServiceSid`.
|
|
107
|
+
|
|
108
|
+
</Accordion>
|
|
109
|
+
|
|
110
|
+
<Accordion title="Invalid To phone number">
|
|
111
|
+
|
|
112
|
+
Twilio expects E.164 with a leading `+`. Do not strip `+` the way some regional SMS APIs require.
|
|
113
|
+
|
|
114
|
+
</Accordion>
|
|
115
|
+
|
|
116
|
+
</Accordions>
|
|
117
|
+
|
|
118
|
+
## Learn more
|
|
119
|
+
|
|
120
|
+
- [SMS channel](/docs/channels/sms) — `createSmsSender` and hooks
|
|
121
|
+
- [Transports](/docs/transports) — other SMS providers
|
|
122
|
+
|
|
123
|
+
## Next
|
|
124
|
+
|
|
125
|
+
<Cards>
|
|
126
|
+
<Card title="SMS channel" description="createSmsSender options." href="/docs/channels/sms" />
|
|
127
|
+
<Card title="Transports" description="Providers by channel." href="/docs/transports" />
|
|
128
|
+
</Cards>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Web Push
|
|
3
|
+
description: Encrypt and send browser notifications with VAPID.
|
|
4
|
+
icon: Truck
|
|
5
|
+
source: "src/transports/webpush.ts"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Encrypt and send browser notifications with VAPID.
|
|
9
|
+
|
|
10
|
+
<Callout title="The one rule">Create this transport under the matching sently channel sender.</Callout>
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
| Option | Type | Default or requirement |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| `vapidPublicKey` | `string` | required |
|
|
17
|
+
| `vapidPrivateKey` | `string` | required |
|
|
18
|
+
| `subject` | `string` | required |
|
|
19
|
+
| `allowedEndpointHosts` | `string[]` | optional |
|
|
20
|
+
|
|
21
|
+
<Steps>
|
|
22
|
+
<Step title="Configure the transport">
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { createPushSender } from "sently/push";
|
|
26
|
+
import { WebPushTransport } from "sently/transports/webpush";
|
|
27
|
+
|
|
28
|
+
const sender = createPushSender({ transport: new WebPushTransport({ vapidPublicKey: "...", vapidPrivateKey: "...", subject: "mailto:you@example.com" }) });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
</Step>
|
|
32
|
+
<Step title="Send with the channel API">
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
await sender.send({ subscription, title: "Hello", body: "World" });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
</Step>
|
|
39
|
+
</Steps>
|
|
40
|
+
|
|
41
|
+
<Accordions><Accordion title="Should I call the provider SDK?">No. Use the sently sender; provider-specific extras stay on the transport instance.</Accordion></Accordions>
|
|
42
|
+
|
|
43
|
+
<Cards><Card title="Push channel" href="/docs/channels/push" /></Cards>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: WhatsApp Cloud
|
|
3
|
+
description: Send WhatsApp templates and session text through Meta Cloud API.
|
|
4
|
+
icon: Truck
|
|
5
|
+
source: "src/transports/whatsapp-cloud.ts"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Send WhatsApp templates and session text through Meta Cloud API.
|
|
9
|
+
|
|
10
|
+
<Callout title="The one rule">Create this transport under the matching sently channel sender.</Callout>
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
| Option | Type | Default or requirement |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| `accessToken` | `string` | required |
|
|
17
|
+
| `phoneNumberId` | `string` | required |
|
|
18
|
+
| `apiVersion` | `string` | v26.0 |
|
|
19
|
+
|
|
20
|
+
<Steps>
|
|
21
|
+
<Step title="Configure the transport">
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { createWhatsAppSender } from "sently/whatsapp";
|
|
25
|
+
import { WhatsAppCloudTransport } from "sently/transports/whatsapp-cloud";
|
|
26
|
+
|
|
27
|
+
const sender = createWhatsAppSender({ transport: new WhatsAppCloudTransport({ accessToken: "...", phoneNumberId: "..." }) });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
</Step>
|
|
31
|
+
<Step title="Send with the channel API">
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
await sender.send({ to: "15551234567", template: { name: "welcome", language: "en_US" } });
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
</Step>
|
|
38
|
+
</Steps>
|
|
39
|
+
|
|
40
|
+
<Accordions><Accordion title="Should I call the provider SDK?">No. Use the sently sender; provider-specific extras stay on the transport instance.</Accordion></Accordions>
|
|
41
|
+
|
|
42
|
+
<Cards><Card title="Whatsapp channel" href="/docs/channels/whatsapp" /></Cards>
|
package/dist/chunk-4mvp3b85.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{p as U}from"./chunk-rm78s1ek.js";import{r as X}from"./chunk-gkkv7yqm.js";import{s as b}from"./chunk-r7ee9tk7.js";import{D as k,E as $,F as Z,G as O,H as q,I as L,J as v,K as N,L as A,M as j,O as E}from"./chunk-cvrbfke9.js";import{W as C}from"./chunk-keksa6fx.js";class T{config;adapter=null;constructor(z){this.config=P(z)}async send(z){let B={...z,attachments:await b(z.attachments)},G=await X(B,this.config.dkim),F=await this.getAdapter(),W=this.config.direct?await M(G.envelope.from.split("@")[1]??this.config.host):this.config.host;await F.connect(W,this.config.port),this.adapter=F;try{return await D(F,this.config),await S(F,G)}finally{await H(F),this.adapter=null}}async verify(){try{let z=await this.getAdapter();await z.connect(this.config.host,this.config.port);try{return await D(z,this.config),{ok:!0,provider:"smtp"}}finally{await H(z)}}catch(z){return{ok:!1,provider:"smtp",message:z instanceof Error?z.message:String(z)}}}async close(){if(this.adapter)await this.adapter.close(),this.adapter=null}async getAdapter(){if(!this.config.adapter)throw new $("No socket adapter configured",0,"CONNECT","");return this.config.adapter}}function P(z){let B=z.secure??!1;return{host:z.host,port:z.port??(B?465:587),secure:B,...z.auth!==void 0?{auth:z.auth}:{},...z.requireTLS!==void 0?{requireTLS:z.requireTLS}:{},...z.dkim!==void 0?{dkim:z.dkim}:{},...z.tls!==void 0?{tls:z.tls}:{},...z.connectionTimeout!==void 0?{connectionTimeout:z.connectionTimeout}:{},...z.greetingTimeout!==void 0?{greetingTimeout:z.greetingTimeout}:{},...z.socketTimeout!==void 0?{socketTimeout:z.socketTimeout}:{},...z.direct!==void 0?{direct:z.direct}:{},...z.adapter!==void 0?{adapter:z.adapter}:{}}}async function D(z,B){let G=await J(z);N(G,[220],"greeting");let F=await I(z,B.host),W=F.some((Q)=>Q.toUpperCase()==="STARTTLS");if(!B.secure&&!z.secure&&W){await Y(z,Z({type:"STARTTLS"}));let Q=await J(z);N(Q,[220],"STARTTLS"),await z.startTLS(B.tls),F=await I(z,B.host)}if(B.auth){let Q=B.requireTLS??!0,V=z.secure||B.secure;if(Q&&!V)throw new $("Refusing to authenticate over unencrypted connection. Set requireTLS: false to disable this check (not recommended).",0,"AUTH","");await l(z,B.auth,F)}}async function S(z,B){await _(z,{type:"MAIL_FROM",address:B.envelope.from});let G=await J(z);N(G,[250],"MAIL FROM");let F=[],W=[];for(let K of B.envelope.to)if(await Y(z,Z({type:"RCPT_TO",address:K})),(await J(z)).isSuccess)F.push(K);else W.push(K);await _(z,{type:"DATA"});let Q=await J(z);N(Q,[354],"DATA");let V;try{await Y(z,Z({type:"DATA_BODY",content:B.raw})),V=await J(z)}catch(K){if(await Y(z,Z({type:"DATA_BODY",content:B.raw})),V=await J(z),V.isError)throw K}return N(V,[250],"DATA end"),{messageId:B.messageId,accepted:F,rejected:W,response:V.message,envelope:B.envelope}}async function H(z){try{await _(z,{type:"QUIT"}),await J(z)}catch{}finally{await z.close()}}async function I(z,B){await _(z,{type:"EHLO",domain:B});let G=await J(z);return N(G,[250],"EHLO"),v(G)}async function l(z,B,G){if(B.type==="OAUTH2"&&B.oauth2){let K=await new U(B.oauth2).buildXOAUTH2();await _(z,{type:"AUTH_XOAUTH2",xoauth2String:K});let x=await J(z);if(x.code===334)await Y(z,E("")),x=await J(z);N(x,[235],"AUTH XOAUTH2");return}let F=B.type??L(G);if(F==="CRAM-MD5"){let V=y(B,"CRAM-MD5");await _(z,{type:"AUTH_CRAM_MD5_INIT"});let K=await J(z);N(K,[334],"AUTH CRAM-MD5");let x=K.message.trim(),w=await k(x,B.user,V);await _(z,{type:"AUTH_CRAM_MD5_RESPONSE",response:w}),K=await J(z),N(K,[235],"AUTH CRAM-MD5 response");return}if(F==="PLAIN"){let V=y(B,"PLAIN");await Y(z,Z({type:"AUTH_PLAIN",user:B.user,pass:V}));let K=await J(z);N(K,[235],"AUTH PLAIN");return}let W=y(B,"LOGIN");await Y(z,Z({type:"AUTH_LOGIN",user:B.user,pass:W}));let Q=await J(z);N(Q,[334],"AUTH LOGIN"),await Y(z,j(B.user)),Q=await J(z),N(Q,[334],"AUTH LOGIN user"),await Y(z,A(W)),Q=await J(z),N(Q,[235],"AUTH LOGIN pass")}function y(z,B){if(!z.pass)throw new $(`Password required for ${B} authentication`,0,`AUTH ${B}`,"");return z.pass}async function _(z,B){await Y(z,Z(B))}async function Y(z,B){await z.write(B)}async function J(z){let B=[];for await(let G of z.read()){B.push(G);let F=q(B);if(F)return O(F)}throw new $("Connection closed while reading SMTP response",0,"READ","")}async function M(z){let G=await(await import("node:dns/promises")).resolveMx(z);if(G.length===0)throw new $(`No MX records for ${z}`,0,"MX","");return G.sort((F,W)=>F.priority-W.priority),G[0]?.exchange??z}export{T as h,P as i,D as j,S as k,H as l,J as m};
|
|
2
|
-
|
|
3
|
-
//# debugId=7424DBD21313E1C964756E2164756E21
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/transports/smtp.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"/**\n * @module\n * SMTP transport — orchestrates socket adapter, MIME builder, and protocol logic.\n *\n * @example\n * ```ts\n * import { SMTPTransport } from \"sently/transports/smtp\";\n * import { NodeAdapter } from \"sently/adapters/node\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new SMTPTransport({\n * host: \"smtp.example.com\",\n * auth: { user: \"you@example.com\", pass: \"secret\" },\n * adapter: new NodeAdapter(),\n * }),\n * });\n * ```\n */\nimport { OAuth2Client } from \"../auth/oauth2.js\";\nimport { buildMIME, type MIMEBuildResult } from \"../core/mime.js\";\nimport type { SMTPResponse } from \"../core/smtp.js\";\nimport {\n accumulateResponse,\n assertResponse,\n computeCRAMMD5,\n encodeAuthLoginPass,\n encodeAuthLoginUser,\n encodeCommand,\n encodeLine,\n parseEHLO,\n parseResponse,\n SMTPError,\n selectAuthMethod,\n} from \"../core/smtp.js\";\nimport type {\n MailOptions,\n SendResult,\n SMTPConfig,\n SocketAdapter,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/**\n * SMTP transport orchestrating adapter, MIME builder, and protocol logic.\n */\nexport class SMTPTransport implements Transport {\n /** Resolved SMTP configuration with defaults applied. */\n private readonly config: ResolvedSMTPConfig;\n /** Active socket adapter for the current session, if any. */\n private adapter: SocketAdapter | null = null;\n\n /** Creates an SMTP transport with the given configuration. */\n constructor(config: SMTPConfig) {\n this.config = resolveSMTPConfig(config);\n }\n\n /** Sends an email via SMTP using the configured adapter. */\n async send(options: MailOptions): Promise<SendResult> {\n const resolvedOptions = {\n ...options,\n attachments: await resolveAttachments(options.attachments),\n };\n const mime = await buildMIME(resolvedOptions, this.config.dkim);\n const adapter = await this.getAdapter();\n\n const host = this.config.direct\n ? await resolveMX(mime.envelope.from.split(\"@\")[1] ?? this.config.host)\n : this.config.host;\n\n await adapter.connect(host, this.config.port);\n this.adapter = adapter;\n\n try {\n await openSMTPSession(adapter, this.config);\n return await deliverSMTPMessage(adapter, mime);\n } finally {\n await closeSMTPSession(adapter);\n this.adapter = null;\n }\n }\n\n /** Verifies SMTP connectivity and authentication without sending mail. */\n async verify(): Promise<VerifyResult> {\n try {\n const adapter = await this.getAdapter();\n await adapter.connect(this.config.host, this.config.port);\n\n try {\n await openSMTPSession(adapter, this.config);\n return { ok: true, provider: \"smtp\" };\n } finally {\n await closeSMTPSession(adapter);\n }\n } catch (err) {\n return {\n ok: false,\n provider: \"smtp\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n /** Closes the underlying socket adapter if connected. */\n async close(): Promise<void> {\n if (this.adapter) {\n await this.adapter.close();\n this.adapter = null;\n }\n }\n\n /** Returns the configured socket adapter or throws if none is set. */\n private async getAdapter(): Promise<SocketAdapter> {\n if (!this.config.adapter) {\n throw new SMTPError(\"No socket adapter configured\", 0, \"CONNECT\", \"\");\n }\n return this.config.adapter;\n }\n}\n\n/** Resolved SMTP transport configuration with defaults applied. */\nexport interface ResolvedSMTPConfig {\n /** SMTP server hostname. */\n host: string;\n /** SMTP port with secure/default applied. */\n port: number;\n /** Whether implicit TLS is used. */\n secure: boolean;\n /** Resolved authentication credentials, if any. */\n auth?: SMTPConfig[\"auth\"];\n /** Whether AUTH requires an encrypted connection. */\n requireTLS?: boolean;\n /** TLS options for STARTTLS and direct TLS. */\n tls?: SMTPConfig[\"tls\"];\n /** DKIM signing configuration. */\n dkim?: SMTPConfig[\"dkim\"];\n /** Socket connect timeout in milliseconds. */\n connectionTimeout?: number;\n /** Timeout waiting for the SMTP greeting in milliseconds. */\n greetingTimeout?: number;\n /** Idle socket timeout in milliseconds. */\n socketTimeout?: number;\n /** Direct-to-MX delivery mode. */\n direct?: boolean;\n /** Runtime socket adapter. */\n adapter?: SocketAdapter;\n}\n\n/** Apply defaults to SMTP configuration. */\nexport function resolveSMTPConfig(config: SMTPConfig): ResolvedSMTPConfig {\n const secure = config.secure ?? false;\n return {\n host: config.host,\n port: config.port ?? (secure ? 465 : 587),\n secure,\n ...(config.auth !== undefined ? { auth: config.auth } : {}),\n ...(config.requireTLS !== undefined ? { requireTLS: config.requireTLS } : {}),\n ...(config.dkim !== undefined ? { dkim: config.dkim } : {}),\n ...(config.tls !== undefined ? { tls: config.tls } : {}),\n ...(config.connectionTimeout !== undefined\n ? { connectionTimeout: config.connectionTimeout }\n : {}),\n ...(config.greetingTimeout !== undefined ? { greetingTimeout: config.greetingTimeout } : {}),\n ...(config.socketTimeout !== undefined ? { socketTimeout: config.socketTimeout } : {}),\n ...(config.direct !== undefined ? { direct: config.direct } : {}),\n ...(config.adapter !== undefined ? { adapter: config.adapter } : {}),\n };\n}\n\n/**\n * Connect greeting, EHLO, optional STARTTLS, and AUTH on an open adapter.\n */\nexport async function openSMTPSession(\n adapter: SocketAdapter,\n config: ResolvedSMTPConfig,\n): Promise<void> {\n const greeting = await readSMTPResponse(adapter);\n assertResponse(greeting, [220], \"greeting\");\n\n let capabilities = await ehlo(adapter, config.host);\n const supportsStartTls = capabilities.some((cap) => cap.toUpperCase() === \"STARTTLS\");\n if (!config.secure && !adapter.secure && supportsStartTls) {\n await sendRaw(adapter, encodeCommand({ type: \"STARTTLS\" }));\n const starttlsResp = await readSMTPResponse(adapter);\n assertResponse(starttlsResp, [220], \"STARTTLS\");\n await adapter.startTLS(config.tls);\n capabilities = await ehlo(adapter, config.host);\n }\n\n if (config.auth) {\n const tlsRequired = config.requireTLS ?? true;\n const encrypted = adapter.secure || config.secure;\n if (tlsRequired && !encrypted) {\n throw new SMTPError(\n \"Refusing to authenticate over unencrypted connection. \" +\n \"Set requireTLS: false to disable this check (not recommended).\",\n 0,\n \"AUTH\",\n \"\",\n );\n }\n await authenticate(adapter, config.auth, capabilities);\n }\n}\n\n/**\n * MAIL FROM, RCPT TO, and DATA for a built MIME message on an authenticated session.\n */\nexport async function deliverSMTPMessage(\n adapter: SocketAdapter,\n mime: MIMEBuildResult,\n): Promise<SendResult> {\n await sendCommand(adapter, { type: \"MAIL_FROM\", address: mime.envelope.from });\n const mailResp = await readSMTPResponse(adapter);\n assertResponse(mailResp, [250], \"MAIL FROM\");\n\n const accepted: string[] = [];\n const rejected: string[] = [];\n\n for (const recipient of mime.envelope.to) {\n await sendRaw(adapter, encodeCommand({ type: \"RCPT_TO\", address: recipient }));\n const rcptResp = await readSMTPResponse(adapter);\n if (rcptResp.isSuccess) {\n accepted.push(recipient);\n } else {\n rejected.push(recipient);\n }\n }\n\n await sendCommand(adapter, { type: \"DATA\" });\n const dataResp = await readSMTPResponse(adapter);\n assertResponse(dataResp, [354], \"DATA\");\n\n let finalResp: SMTPResponse;\n try {\n await sendRaw(adapter, encodeCommand({ type: \"DATA_BODY\", content: mime.raw }));\n finalResp = await readSMTPResponse(adapter);\n } catch (err) {\n await sendRaw(adapter, encodeCommand({ type: \"DATA_BODY\", content: mime.raw }));\n finalResp = await readSMTPResponse(adapter);\n if (finalResp.isError) {\n throw err;\n }\n }\n assertResponse(finalResp, [250], \"DATA end\");\n\n return {\n messageId: mime.messageId,\n accepted,\n rejected,\n response: finalResp.message,\n envelope: mime.envelope,\n };\n}\n\n/**\n * QUIT and close an SMTP session adapter.\n */\nexport async function closeSMTPSession(adapter: SocketAdapter): Promise<void> {\n try {\n await sendCommand(adapter, { type: \"QUIT\" });\n await readSMTPResponse(adapter);\n } catch {\n // ignore errors during shutdown\n } finally {\n await adapter.close();\n }\n}\n\nasync function ehlo(adapter: SocketAdapter, host: string): Promise<string[]> {\n await sendCommand(adapter, { type: \"EHLO\", domain: host });\n const response = await readSMTPResponse(adapter);\n assertResponse(response, [250], \"EHLO\");\n return parseEHLO(response);\n}\n\nasync function authenticate(\n adapter: SocketAdapter,\n auth: NonNullable<SMTPConfig[\"auth\"]>,\n capabilities: string[],\n): Promise<void> {\n if (auth.type === \"OAUTH2\" && auth.oauth2) {\n const client = new OAuth2Client(auth.oauth2);\n const xoauth2 = await client.buildXOAUTH2();\n await sendCommand(adapter, { type: \"AUTH_XOAUTH2\", xoauth2String: xoauth2 });\n let resp = await readSMTPResponse(adapter);\n if (resp.code === 334) {\n await sendRaw(adapter, encodeLine(\"\"));\n resp = await readSMTPResponse(adapter);\n }\n assertResponse(resp, [235], \"AUTH XOAUTH2\");\n return;\n }\n\n const method = auth.type ?? selectAuthMethod(capabilities);\n\n if (method === \"CRAM-MD5\") {\n const pass = requirePassword(auth, \"CRAM-MD5\");\n await sendCommand(adapter, { type: \"AUTH_CRAM_MD5_INIT\" });\n let resp = await readSMTPResponse(adapter);\n assertResponse(resp, [334], \"AUTH CRAM-MD5\");\n const challenge = resp.message.trim();\n const response = await computeCRAMMD5(challenge, auth.user, pass);\n await sendCommand(adapter, { type: \"AUTH_CRAM_MD5_RESPONSE\", response });\n resp = await readSMTPResponse(adapter);\n assertResponse(resp, [235], \"AUTH CRAM-MD5 response\");\n return;\n }\n\n if (method === \"PLAIN\") {\n const pass = requirePassword(auth, \"PLAIN\");\n await sendRaw(adapter, encodeCommand({ type: \"AUTH_PLAIN\", user: auth.user, pass }));\n const resp = await readSMTPResponse(adapter);\n assertResponse(resp, [235], \"AUTH PLAIN\");\n return;\n }\n\n const pass = requirePassword(auth, \"LOGIN\");\n await sendRaw(adapter, encodeCommand({ type: \"AUTH_LOGIN\", user: auth.user, pass }));\n let resp = await readSMTPResponse(adapter);\n assertResponse(resp, [334], \"AUTH LOGIN\");\n\n await sendRaw(adapter, encodeAuthLoginUser(auth.user));\n resp = await readSMTPResponse(adapter);\n assertResponse(resp, [334], \"AUTH LOGIN user\");\n\n await sendRaw(adapter, encodeAuthLoginPass(pass));\n resp = await readSMTPResponse(adapter);\n assertResponse(resp, [235], \"AUTH LOGIN pass\");\n}\n\nfunction requirePassword(auth: NonNullable<SMTPConfig[\"auth\"]>, method: string): string {\n if (!auth.pass) {\n throw new SMTPError(`Password required for ${method} authentication`, 0, `AUTH ${method}`, \"\");\n }\n return auth.pass;\n}\n\nasync function sendCommand(\n adapter: SocketAdapter,\n command: Parameters<typeof encodeCommand>[0],\n): Promise<void> {\n await sendRaw(adapter, encodeCommand(command));\n}\n\nasync function sendRaw(adapter: SocketAdapter, data: Uint8Array): Promise<void> {\n await adapter.write(data);\n}\n\n/** Reads and parses a complete SMTP response from the adapter. */\nasync function readSMTPResponse(adapter: SocketAdapter): Promise<SMTPResponse> {\n const chunks: Uint8Array[] = [];\n for await (const chunk of adapter.read()) {\n chunks.push(chunk);\n const complete = accumulateResponse(chunks);\n if (complete) {\n return parseResponse(complete);\n }\n }\n throw new SMTPError(\"Connection closed while reading SMTP response\", 0, \"READ\", \"\");\n}\n\nasync function resolveMX(domain: string): Promise<string> {\n const dns = await import(\"node:dns/promises\");\n const records = await dns.resolveMx(domain);\n if (records.length === 0) {\n throw new SMTPError(`No MX records for ${domain}`, 0, \"MX\", \"\");\n }\n records.sort((a: { priority: number }, b: { priority: number }) => a.priority - b.priority);\n return records[0]?.exchange ?? domain;\n}\n\nexport { SMTPError } from \"../core/smtp.js\";\n/** @internal Test helper for raw line writes. */\nexport { encodeLine, readSMTPResponse };\n"
|
|
6
|
-
],
|
|
7
|
-
"mappings": "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",
|
|
8
|
-
"debugId": "7424DBD21313E1C964756E2164756E21",
|
|
9
|
-
"names": []
|
|
10
|
-
}
|
package/dist/chunk-74rha3ac.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{W as q}from"./chunk-keksa6fx.js";function w(){if(typeof Bun<"u")return"bun";if(typeof Deno<"u")return"deno";if(typeof caches<"u"&&globalThis.navigator?.userAgent==="Cloudflare-Workers")return"cf-workers";if(typeof window<"u")return"browser";if(typeof process<"u"&&process.versions?.node)return"node";return"unknown"}async function x(k){let v=w();switch(v){case"node":{let{NodeAdapter:g}=await import("./adapters/node.js");return new g(k)}case"bun":{let{BunAdapter:g}=await import("./adapters/bun.js");return new g(k)}case"deno":{let{DenoAdapter:g}=await import("./adapters/deno.js");return new g(k)}case"cf-workers":{let{CloudflareAdapter:g}=await import("./adapters/cf.js");return new g(k)}default:throw Error(`No socket adapter available for runtime: ${v}`)}}
|
|
2
|
-
export{w as t,x as u};
|
|
3
|
-
|
|
4
|
-
//# debugId=B9C541A293BA86D164756E2164756E21
|
package/dist/chunk-cvrbfke9.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import{P as O,Q as K,S as H}from"./chunk-d5rwm3br.js";import{T as v,V as B}from"./chunk-a1c87hn1.js";var j=64;function X(q){return q>>>0}function U(q){let z=g(q),G=1732584193,Q=4023233417,N=2562383102,J=271733878;for(let F=0;F<z.length;F+=64){let $=z.subarray(F,F+64),w=new Uint32Array(16);for(let W=0;W<16;W++){let A=W*4;w[W]=X(($[A]??0)|($[A+1]??0)<<8|($[A+2]??0)<<16|($[A+3]??0)<<24)}let x=G,D=Q,I=N,R=J;for(let W=0;W<64;W++){let A,_;if(W<16)A=X(D&I|~D&R),_=W;else if(W<32)A=X(D&R|I&~R),_=X((5*W+1)%16);else if(W<48)A=X(D^I^R),_=X((3*W+5)%16);else A=X(I^(D|~R)),_=X(7*W%16);let y=R;R=I,I=D,D=X(D+T(X(x+A+X((P[W]??0)+(w[_]??0))),L[W]??0)),x=y}G=X(G+x),Q=X(Q+D),N=X(N+I),J=X(J+R)}let Y=new Uint8Array(16),V=new DataView(Y.buffer);return V.setUint32(0,G,!0),V.setUint32(4,Q,!0),V.setUint32(8,N,!0),V.setUint32(12,J,!0),Y}var L=[7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21],P=new Uint32Array([3614090360,3905402710,606105819,3250441966,4118548399,1200080426,2821735955,4249261313,1770035416,2336552879,4294925233,2304563134,1804603682,4254626195,2792965006,1236535329,4129170786,3225465664,643717713,3921069994,3593408605,38016083,3634488961,3889429448,568446438,3275163606,4107603335,1163531501,2850285829,4243563512,1735328473,2368359562,4294588738,2272392833,1839030562,4259657740,2763975236,1272893353,4139469664,3200236656,681279174,3936430074,3572445317,76029189,3654602809,3873151461,530742520,3299628645,4096336452,1126891415,2878612391,4237533241,1700485571,2399980690,4293915773,2240044497,1873313359,4264355552,2734768916,1309151649,4149444226,3174756917,718787259,3951481745]);function T(q,z){return X(q<<z|q>>>32-z)}function g(q){let z=q.length*8,G=(56-(q.length+1)%64+64)%64,Q=q.length+1+G+8,N=new Uint8Array(Q);N.set(q),N[q.length]=128;let J=new DataView(N.buffer);return J.setUint32(Q-8,z>>>0,!0),J.setUint32(Q-4,Math.floor(z/4294967296),!0),N}function C(q,z){let G=q;if(G.length>j)G=U(G);let Q=new Uint8Array(j);Q.set(G);let N=new Uint8Array(j),J=new Uint8Array(j);for(let $=0;$<j;$++)N[$]=(Q[$]??0)^54,J[$]=(Q[$]??0)^92;let Y=new Uint8Array(N.length+z.length);Y.set(N),Y.set(z,N.length);let V=U(Y),F=new Uint8Array(J.length+V.length);return F.set(J),F.set(V,J.length),U(F)}function E(q){return Array.from(q,(z)=>z.toString(16).padStart(2,"0")).join("")}async function M(q,z,G){let Q=K(q.trim()),N=H(G),J=C(N,Q),Y=E(J);return O(`${z} ${Y}`).replace(/\r\n/g,"")}class Z extends v{command;response;constructor(q,z,G,Q){super(q,B(z,G),{provider:"smtp"});this.command=G;this.response=Q;this.name="SMTPError",this.code=z}}function u(q){let z;switch(q.type){case"EHLO":{z=`EHLO ${q.domain.replace(/[\r\n]/g,"")}`;break}case"STARTTLS":z="STARTTLS";break;case"AUTH_LOGIN":z="AUTH LOGIN";break;case"AUTH_PLAIN":z=`AUTH PLAIN ${O(`\x00${q.user}\x00${q.pass}`).replace(/\r\n/g,"")}`;break;case"AUTH_CRAM_MD5_INIT":z="AUTH CRAM-MD5";break;case"AUTH_CRAM_MD5_RESPONSE":return H(`${q.response}\r
|
|
2
|
-
`);case"AUTH_XOAUTH2":z=`AUTH XOAUTH2 ${q.xoauth2String}`;break;case"MAIL_FROM":if(/[\r\n]/.test(q.address))throw new Z("Invalid address: contains CRLF",0,"MAIL FROM",q.address);z=`MAIL FROM:<${q.address}>`;break;case"RCPT_TO":if(/[\r\n]/.test(q.address))throw new Z("Invalid address: contains CRLF",0,"RCPT TO",q.address);z=`RCPT TO:<${q.address}>`;break;case"DATA":z="DATA";break;case"DATA_BODY":return H(S(q.content));case"QUIT":z="QUIT";break;case"RSET":z="RSET";break;case"NOOP":z="NOOP";break}return H(`${z}\r
|
|
3
|
-
`)}function l(q){let z=new TextDecoder().decode(q).trim(),G=z.split(/\r?\n/),N=(G[G.length-1]??"").match(/^(\d{3})([\s-])(.*)$/);if(!N)throw new Z("Invalid SMTP response",0,"PARSE",z);let J=Number.parseInt(N[1]??"0",10),Y=G.map((V)=>V.replace(/^\d{3}[\s-]/,"")).join(" ");return{code:J,message:Y,isSuccess:J>=200&&J<300,isReady:J>=300&&J<400,isError:J>=400}}function p(q){if(q.length===0)return null;let z=q.reduce((V,F)=>V+F.length,0),G=new Uint8Array(z),Q=0;for(let V of q)G.set(V,Q),Q+=V.length;let J=new TextDecoder().decode(G).split(/\r?\n/).filter((V)=>V.length>0);if(J.length===0)return null;let Y=J[J.length-1]??"";if(/^\d{3} /.test(Y))return G;return null}function m(q){let z=q.map((G)=>G.toUpperCase());if(z.some((G)=>G.includes("AUTH")&&G.includes("XOAUTH2")))return"OAUTH2";if(z.some((G)=>G.includes("AUTH")&&G.includes("CRAM-MD5")))return"CRAM-MD5";if(z.some((G)=>G.includes("AUTH")&&G.includes("LOGIN")))return"LOGIN";if(z.some((G)=>G.includes("AUTH")&&G.includes("PLAIN")))return"PLAIN";throw new Z("No supported AUTH method",0,"EHLO",q.join(" "))}function i(q){return q.message.split(/\s+/).flatMap((z)=>z.split(/\r?\n/)).filter(Boolean)}function r(q,z,G){if(!z.includes(q.code))throw new Z(`Unexpected SMTP response for ${G}`,q.code,G,q.message)}function S(q){return`${new TextDecoder().decode(q).split(/\r?\n/).map((N)=>N.startsWith(".")?`.${N}`:N).join(`\r
|
|
4
|
-
`)}\r
|
|
5
|
-
.\r
|
|
6
|
-
`}function t(q){return H(`${O(q).replace(/\r\n/g,"")}\r
|
|
7
|
-
`)}function o(q){return H(`${O(q).replace(/\r\n/g,"")}\r
|
|
8
|
-
`)}function n(q){return H(`${q}\r
|
|
9
|
-
`)}function s(q){return H(`${q}\r
|
|
10
|
-
`)}
|
|
11
|
-
export{M as D,Z as E,u as F,l as G,p as H,m as I,i as J,r as K,t as L,o as M,n as N,s as O};
|
|
12
|
-
|
|
13
|
-
//# debugId=8FFDF7C109D0D94564756E2164756E21
|