sently 0.10.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +50 -2
  3. package/README.md +21 -18
  4. package/SECURITY.md +59 -0
  5. package/dist/chunk-z1589fjk.js.map +1 -1
  6. package/dist/core/push-types.d.ts +1 -1
  7. package/dist/transports/mailpit.d.ts +166 -0
  8. package/dist/transports/mailpit.js +3 -0
  9. package/dist/transports/mailpit.js.map +10 -0
  10. package/dist/transports/webpush.d.ts +5 -1
  11. package/dist/transports/webpush.js +2 -2
  12. package/dist/transports/webpush.js.map +3 -3
  13. package/package.json +15 -2
  14. package/site/content/docs/ai/llms-txt.mdx +47 -2
  15. package/site/content/docs/channels/email.mdx +3 -0
  16. package/site/content/docs/channels/index.mdx +3 -1
  17. package/site/content/docs/channels/push.mdx +4 -0
  18. package/site/content/docs/decorators/fallback.mdx +5 -0
  19. package/site/content/docs/decorators/index.mdx +2 -1
  20. package/site/content/docs/decorators/preview.mdx +52 -9
  21. package/site/content/docs/get-started/entrypoints.mdx +5 -3
  22. package/site/content/docs/get-started/index.mdx +8 -2
  23. package/site/content/docs/get-started/introduction.mdx +7 -3
  24. package/site/content/docs/get-started/meta.json +4 -1
  25. package/site/content/docs/get-started/migrate-nodemailer.mdx +132 -5
  26. package/site/content/docs/get-started/non-goals.mdx +57 -0
  27. package/site/content/docs/get-started/stability.mdx +69 -0
  28. package/site/content/docs/get-started/support-matrix.mdx +62 -0
  29. package/site/content/docs/guides/compare.mdx +87 -0
  30. package/site/content/docs/guides/failover.mdx +121 -0
  31. package/site/content/docs/guides/index.mdx +2 -0
  32. package/site/content/docs/guides/meta.json +2 -0
  33. package/site/content/docs/guides/security.mdx +38 -8
  34. package/site/content/docs/index.mdx +78 -4
  35. package/site/content/docs/meta.json +0 -1
  36. package/site/content/docs/reference/exports.mdx +5 -2
  37. package/site/content/docs/reference/push-options.mdx +14 -0
  38. package/site/content/docs/transports/fcm.mdx +8 -4
  39. package/site/content/docs/transports/index.mdx +9 -3
  40. package/site/content/docs/transports/mailpit.mdx +123 -0
  41. package/site/content/docs/transports/meta.json +1 -0
  42. package/site/content/docs/transports/smtp.mdx +61 -22
  43. package/site/content/docs/transports/webpush.mdx +9 -2
  44. package/site/content/docs/quick-start/email.mdx +0 -40
  45. package/site/content/docs/quick-start/index.mdx +0 -15
  46. package/site/content/docs/quick-start/meta.json +0 -5
  47. package/site/content/docs/quick-start/push.mdx +0 -53
  48. package/site/content/docs/quick-start/sms.mdx +0 -36
  49. package/site/content/docs/quick-start/whatsapp.mdx +0 -38
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sently",
3
- "version": "0.10.0",
4
- "description": "Runtime-agnostic messaging library for Node.js, Bun, Deno, and Cloudflare Workers. Channel-first email, SMS, WhatsApp, and push with pluggable provider transports.",
3
+ "version": "1.0.1",
4
+ "description": "Runtime-agnostic channel-delivery library for Node.js, Bun, Deno, and Cloudflare Workers. One sender shape, one error model, and one retry path across email, SMS, WhatsApp, and push — with pluggable provider transports.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "main": "./dist/index.js",
@@ -11,6 +11,7 @@
11
11
  "README.md",
12
12
  "LICENSE",
13
13
  "CHANGELOG.md",
14
+ "SECURITY.md",
14
15
  "AGENTS.md",
15
16
  "site/content/docs"
16
17
  ],
@@ -58,11 +59,18 @@
58
59
  "typescript": "6.0.3"
59
60
  },
60
61
  "keywords": [
62
+ "messaging",
61
63
  "email",
64
+ "sms",
65
+ "whatsapp",
66
+ "push",
67
+ "web-push",
68
+ "fcm",
62
69
  "smtp",
63
70
  "mailer",
64
71
  "nodemailer",
65
72
  "nodemailer-alternative",
73
+ "channel-first",
66
74
  "bun",
67
75
  "deno",
68
76
  "cloudflare-workers",
@@ -75,6 +83,7 @@
75
83
  "ses",
76
84
  "aws-ses",
77
85
  "brevo",
86
+ "twilio",
78
87
  "dkim",
79
88
  "oauth2",
80
89
  "transactional-email",
@@ -179,6 +188,10 @@
179
188
  "import": "./dist/transports/mailtrap.js",
180
189
  "types": "./dist/transports/mailtrap.d.ts"
181
190
  },
191
+ "./transports/mailpit": {
192
+ "import": "./dist/transports/mailpit.js",
193
+ "types": "./dist/transports/mailpit.d.ts"
194
+ },
182
195
  "./transports/loops": {
183
196
  "import": "./dist/transports/loops.js",
184
197
  "types": "./dist/transports/loops.d.ts"
@@ -6,7 +6,52 @@ source: "site/app/llms.txt/route.ts"
6
6
  ---
7
7
 
8
8
  The docs site serves `/llms.txt` and `/llms-full.txt` for concise and complete machine-readable documentation.
9
+ Use the live site indexes when an agent needs current handbook navigation.
9
10
 
10
- Use `/llms.txt` for navigation and `/llms-full.txt` when an agent needs page content.
11
+ <Callout title="The one rule">
12
+ Prefer the docs site `/llms.txt` over the repo-root `llms.txt` file when both exist — the site index is generated from the handbook MDX.
13
+ </Callout>
11
14
 
12
- <Cards><Card title="MCP" href="/docs/ai/mcp" /></Cards>
15
+ ## Quick start
16
+
17
+ <Steps>
18
+ <Step title="Fetch the short index">
19
+
20
+ ```text
21
+ https://sently.omqkhafi.dev/llms.txt
22
+ ```
23
+
24
+ </Step>
25
+ <Step title="Fetch full page text when needed">
26
+
27
+ ```text
28
+ https://sently.omqkhafi.dev/llms-full.txt
29
+ ```
30
+
31
+ </Step>
32
+ </Steps>
33
+
34
+ ## Related agent surfaces
35
+
36
+ | Resource | Use |
37
+ | --- | --- |
38
+ | Site `/llms.txt` | Navigation + short model |
39
+ | Site `/llms-full.txt` | Full handbook dump |
40
+ | Repo `AGENTS.md` | Channel-first contract |
41
+ | [Stability](/docs/get-started/stability) | What is frozen at 1.x |
42
+ | [Compare](/docs/guides/compare) | Library vs platform positioning |
43
+
44
+ ## Troubleshooting
45
+
46
+ <Accordions>
47
+ <Accordion title="Why does the repo-root llms.txt disagree with the site?">
48
+ The live route is built from `site/content/docs`. Refresh or regenerate from the handbook after docs changes.
49
+ </Accordion>
50
+ </Accordions>
51
+
52
+ ## Next
53
+
54
+ <Cards>
55
+ <Card title="MCP" href="/docs/ai/mcp" />
56
+ <Card title="Introduction" href="/docs/get-started/introduction" />
57
+ </Cards>
@@ -91,10 +91,13 @@ const mailer = await createSMTPMailer({
91
91
  - [Email options](../reference/mail-options)
92
92
  - [Transport contracts](../reference/transport-contracts)
93
93
  - [Hooks](./hooks)
94
+ - [Mailpit](../transports/mailpit) — local catcher for development
95
+ - [Preview](../decorators/preview) — write `.eml` files to disk
94
96
 
95
97
  ## Next
96
98
 
97
99
  <Cards>
98
100
  <Card title="Transports" href="/docs/transports" />
101
+ <Card title="Mailpit" href="/docs/transports/mailpit" />
99
102
  <Card title="Send in bulk" href="/docs/guides/send-bulk" />
100
103
  </Cards>
@@ -6,7 +6,7 @@ source: "README.md"
6
6
  ---
7
7
 
8
8
  Choose a channel sender for the kind of message your application sends.
9
- Each sender accepts a provider transport, so delivery providers can change without rewriting send calls.
9
+ Each sender accepts a provider transport — retry, fallback, and `SentlyError` codes stay the same as you add channels.
10
10
 
11
11
  <Callout title="The one rule">Application code calls a sently channel sender; provider-specific code stays in its transport.</Callout>
12
12
 
@@ -68,6 +68,8 @@ await sms.send({
68
68
  ## Learn more
69
69
 
70
70
  - [Choose an entrypoint](../get-started/entrypoints)
71
+ - [Support matrix](../get-started/support-matrix)
72
+ - [Failover](../guides/failover)
71
73
  - [Browse transports](../transports)
72
74
 
73
75
  ## Next
@@ -78,6 +78,9 @@ Shared fields: `title`, `body`, optional `data`, `icon`, `ttl`, `messageId`.
78
78
  <Accordion title="Why does the transport reject my endpoint?">
79
79
  Web Push validates subscription endpoint hosts before sending. Add only exact private relay hostnames with `allowedEndpointHosts` when needed.
80
80
  </Accordion>
81
+ <Accordion title="Why does FCM reject my subscription object?">
82
+ `FcmTransport` requires `token`. Pass a Web Push `subscription` only to `WebPushTransport`.
83
+ </Accordion>
81
84
  <Accordion title="Why is the endpoint redacted in hooks?">
82
85
  Endpoint paths and FCM tokens are long-lived credentials, so hook context keeps only a redacted fingerprint.
83
86
  </Accordion>
@@ -88,6 +91,7 @@ Shared fields: `title`, `body`, optional `data`, `icon`, `ttl`, `messageId`.
88
91
  - [Push options](../reference/push-options)
89
92
  - [Web Push](../transports/webpush)
90
93
  - [FCM](../transports/fcm)
94
+ - [Failover](../guides/failover)
91
95
  - [Hooks](./hooks)
92
96
 
93
97
  ## Next
@@ -53,9 +53,14 @@ Successful sends include `provider` and `providerIndex` for the transport that h
53
53
  </Accordion>
54
54
  </Accordions>
55
55
 
56
+ ## Learn more
57
+
58
+ - [Failover guide](/docs/guides/failover) — retry-then-fallback recipes across channels
59
+
56
60
  ## Next
57
61
 
58
62
  <Cards>
63
+ <Card title="Failover guide" href="/docs/guides/failover" />
59
64
  <Card title="Weighted fallback" href="/docs/decorators/weighted-fallback" />
60
65
  <Card title="Retry" href="/docs/decorators/retry" />
61
66
  </Cards>
@@ -55,13 +55,14 @@ const sms = createSmsSender({
55
55
 
56
56
  ## Learn more
57
57
 
58
+ - [Failover recipe](../guides/failover) — retry inside a provider, then fail over
58
59
  - [Transports](../transports)
59
60
  - [Hooks](../channels/hooks)
60
61
 
61
62
  ## Next
62
63
 
63
64
  <Cards>
65
+ <Card title="Failover guide" href="/docs/guides/failover" />
64
66
  <Card title="Retry" href="/docs/decorators/retry" />
65
67
  <Card title="Fallback" href="/docs/decorators/fallback" />
66
- <Card title="Transports" href="/docs/transports" />
67
68
  </Cards>
@@ -6,29 +6,72 @@ source: "src/transports/preview.ts"
6
6
  ---
7
7
 
8
8
  Write email previews to disk instead of delivering them.
9
+ Use this when you want a local `.eml` or HTML file for the welcome email without a mail server.
9
10
 
10
11
  <Callout title="The one rule">Preview is for development, not delivery.</Callout>
11
12
 
12
- ## Configuration
13
-
14
- `outDir`, `open`, and `format` are optional; defaults are `./.emails`, false, and `eml`.
13
+ ## Quick start
15
14
 
16
- <Steps><Step title="Wrap an email transport">
15
+ <Steps>
16
+ <Step title="Create the preview transport">
17
17
 
18
18
  ```ts
19
+ import { createMailer } from "sently/mailer";
19
20
  import { PreviewTransport } from "sently/transports/preview";
20
21
 
21
22
  const transport = new PreviewTransport({ outDir: ".emails", open: true });
23
+ const mailer = await createMailer({ transport });
22
24
  ```
23
25
 
24
- </Step><Step title="Create the mailer">
26
+ </Step>
27
+ <Step title="Send and open the file">
25
28
 
26
29
  ```ts
27
- const mailer = await createMailer({ transport });
30
+ await mailer.send({
31
+ from: "dev@example.com",
32
+ to: "you@example.com",
33
+ subject: "Welcome",
34
+ text: "Thanks for joining.",
35
+ });
28
36
  ```
29
37
 
30
- </Step></Steps>
38
+ Files land under `outDir` (default `./.emails`). With `open: true`, the OS opens the written file.
39
+
40
+ </Step>
41
+ </Steps>
42
+
43
+ ## Configuration
44
+
45
+ | Option | Type | Default | Meaning |
46
+ | --- | --- | --- | --- |
47
+ | `outDir` | `string` | `"./.emails"` | Directory for preview files |
48
+ | `open` | `boolean` | `false` | Open the file after write |
49
+ | `format` | `"eml" \| "html"` | `"eml"` | Full MIME or HTML body only |
50
+
51
+ `provider` is `"preview"`. `verify()` always succeeds.
52
+
53
+ For a real SMTP catcher with a web UI, use [Mailpit](/docs/transports/mailpit) instead.
54
+
55
+ ## Troubleshooting
56
+
57
+ <Accordions>
58
+ <Accordion title="Does Preview wrap another transport?">
59
+ No. `PreviewTransport` is the delivery transport — it writes to disk and does not call a provider.
60
+ </Accordion>
61
+ <Accordion title="Where is the file?">
62
+ Check `outDir` (default `./.emails`). The console logs `[sently preview] Written: …` on each send.
63
+ </Accordion>
64
+ </Accordions>
65
+
66
+ ## Learn more
67
+
68
+ - [Mailpit](/docs/transports/mailpit) — local SMTP catcher with inbox API
69
+ - [Email channel](/docs/channels/email) — `createMailer` contract
31
70
 
32
- <Accordions><Accordion title="Does this replace the inner provider?">No. It delegates sends to the wrapped transport.</Accordion></Accordions>
71
+ ## Next
33
72
 
34
- <Cards><Card title="Email channel" href="/docs/channels/email" /></Cards>
73
+ <Cards>
74
+ <Card title="Mailpit" href="/docs/transports/mailpit" />
75
+ <Card title="Email channel" href="/docs/channels/email" />
76
+ <Card title="Decorators" href="/docs/decorators" />
77
+ </Cards>
@@ -88,7 +88,7 @@ No. `createMailer` requires a transport. Import `createSMTPMailer` from `sently/
88
88
 
89
89
  <Accordion title="Where do provider transports come from?">
90
90
 
91
- Import the concrete transport from its exported `sently/transports/*` subpath, such as `sently/transports/sndr` or `sently/transports/resend`.
91
+ Import the concrete transport from its exported `sently/transports/*` subpath, such as `sently/transports/sndr`, `sently/transports/resend`, or `sently/transports/mailpit`.
92
92
 
93
93
  </Accordion>
94
94
 
@@ -103,13 +103,15 @@ HTTP providers were removed from the main barrel so bare Node/Deno imports do no
103
103
  ## Learn more
104
104
 
105
105
  - [Export reference](/docs/reference/exports) — full entrypoint map
106
+ - [Support matrix](/docs/get-started/support-matrix) — Supported vs Available
107
+ - [Stability policy](/docs/get-started/stability) — frozen entrypoints at 1.x
106
108
  - [All transports](/docs/transports) — providers by channel
107
- - [Bundle size](/docs/guides/bundle-size) — keep stacks small
109
+ - [Bundle size](/docs/guides/bundle-size) — keep stacks small on Workers
108
110
 
109
111
  ## Next
110
112
 
111
113
  <Cards>
112
114
  <Card title="Exports reference" description="Public package entrypoints." href="/docs/reference/exports" />
115
+ <Card title="Support matrix" description="Production support promise." href="/docs/get-started/support-matrix" />
113
116
  <Card title="Email channel" description="createMailer and MailOptions." href="/docs/channels/email" />
114
- <Card title="SNDR" description="HTTP email via SNDR." href="/docs/transports/sndr" />
115
117
  </Cards>
@@ -5,10 +5,16 @@ icon: FolderOpen
5
5
  source: "README.md"
6
6
  ---
7
7
 
8
- Install sently and send your first message.
8
+ Install sently and send your first message. Learn the channel model once, then add SMS or push without a new failure model.
9
9
 
10
10
  <Cards>
11
11
  <Card title="Introduction" href="/docs/get-started/introduction" />
12
12
  <Card title="Installation" href="/docs/get-started/installation" />
13
- <Card title="Quick start" href="/docs/quick-start" />
13
+ <Card title="Entrypoints" href="/docs/get-started/entrypoints" />
14
+ <Card title="Runtimes" href="/docs/get-started/runtimes" />
15
+ <Card title="Channels" href="/docs/channels" />
16
+ <Card title="Migrate from Nodemailer" href="/docs/get-started/migrate-nodemailer" />
17
+ <Card title="Stability policy" href="/docs/get-started/stability" />
18
+ <Card title="Support matrix" href="/docs/get-started/support-matrix" />
19
+ <Card title="Non-goals" href="/docs/get-started/non-goals" />
14
20
  </Cards>
@@ -5,8 +5,8 @@ icon: Compass
5
5
  source: "README.md"
6
6
  ---
7
7
 
8
- sently sends email, SMS, WhatsApp, and push (Web Push or FCM) without making provider SDK calls your application API.
9
- Choose the channel sender first, then attach the provider transport that performs delivery.
8
+ sently is the channel-delivery layer for email, SMS, WhatsApp, and push (Web Push or FCM).
9
+ Choose a channel sender first, then attach a provider transport — so adding SMS or push later does not invent a new error and retry model in your app.
10
10
 
11
11
  <Callout title="The one rule">Use `createMailer`, `createSmsSender`, `createWhatsAppSender`, or `createPushSender` in app code; swap transports when provider configuration changes.</Callout>
12
12
 
@@ -67,11 +67,15 @@ Email factories are asynchronous. The other channel factories return their sende
67
67
 
68
68
  - [Install sently](./installation)
69
69
  - [Choose an entrypoint](./entrypoints)
70
+ - [Stability policy](./stability)
71
+ - [Support matrix](./support-matrix)
72
+ - [Compare](/docs/guides/compare)
70
73
  - [Explore channels](../channels)
71
74
 
72
75
  ## Next
73
76
 
74
77
  <Cards>
75
- <Card title="Email quick start" href="/docs/quick-start/email" />
78
+ <Card title="Email channel" href="/docs/channels/email" />
79
+ <Card title="Support matrix" href="/docs/get-started/support-matrix" />
76
80
  <Card title="Channels" href="/docs/channels" />
77
81
  </Cards>
@@ -7,6 +7,9 @@
7
7
  "installation",
8
8
  "entrypoints",
9
9
  "runtimes",
10
- "migrate-nodemailer"
10
+ "migrate-nodemailer",
11
+ "stability",
12
+ "support-matrix",
13
+ "non-goals"
11
14
  ]
12
15
  }
@@ -2,22 +2,149 @@
2
2
  title: Migrate from Nodemailer
3
3
  description: Move SMTP configuration and message fields to sently incrementally.
4
4
  icon: ArrowRightLeft
5
- source: "README.md"
5
+ source: "src/smtp-mailer.ts"
6
6
  ---
7
7
 
8
- Keep your message object, replace transporter construction, and await the sently factory.
8
+ Keep your welcome email, receipt, or password-reset fields (`from`, `to`, `subject`, `text` / `html`).
9
+ Replace `createTransport` with `createSMTPMailer`, rename `sendMail` to `send`, and `await` the factory before the first send.
10
+
11
+ <Callout title="The one rule">
12
+ Use `createSMTPMailer` for relay host/port/auth. Use `createMailer` only when you already have an explicit transport.
13
+ </Callout>
14
+
15
+ ## Quick start
16
+
17
+ <Steps>
18
+ <Step title="Create the mailer">
9
19
 
10
20
  ```ts
11
21
  import { createSMTPMailer } from "sently/smtp";
12
22
 
13
23
  const mailer = await createSMTPMailer({
14
24
  host: "smtp.example.com",
25
+ port: 587,
15
26
  auth: { user: "user@example.com", pass: process.env.SMTP_PASSWORD! },
16
27
  });
17
28
  ```
18
29
 
19
- <Callout title="The one rule">Use `createSMTPMailer` for relay configuration; use `createMailer` only with an explicit transport.</Callout>
30
+ </Step>
31
+ <Step title="Send with the same fields">
32
+
33
+ ```diff
34
+ - const info = await transporter.sendMail({
35
+ + const result = await mailer.send({
36
+ from: "Acme <hello@example.com>",
37
+ to: "person@example.com",
38
+ subject: "Welcome",
39
+ text: "Thanks for joining.",
40
+ });
41
+ ```
42
+
43
+ </Step>
44
+ <Step title="Read the result">
45
+
46
+ ```ts
47
+ console.log(result.messageId, result.accepted, result.rejected);
48
+ ```
49
+
50
+ </Step>
51
+ </Steps>
52
+
53
+ ## API map
54
+
55
+ | Nodemailer | sently |
56
+ | --- | --- |
57
+ | `createTransport({ host, port, auth })` | `await createSMTPMailer({ host, port, auth })` |
58
+ | `transporter.sendMail(msg)` | `mailer.send(msg)` |
59
+ | `transporter.verify()` | `mailer.verify()` |
60
+ | `transporter.close()` | `mailer.close()` |
61
+ | `info.messageId` / `accepted` / `rejected` / `response` / `envelope` | Same fields on `SendResult` |
62
+ | HTTP providers via plugins / custom | `createMailer` + `sently/transports/<provider>` |
63
+
64
+ ## Message fields
65
+
66
+ | Nodemailer | sently |
67
+ | --- | --- |
68
+ | `from` / `to` / `cc` / `bcc` / `replyTo` | Same names on `MailOptions` |
69
+ | `subject`, `text`, `html`, `headers`, `messageId`, `date` | Same names |
70
+ | `priority` | `"high" \| "normal" \| "low"` |
71
+ | `attachments[].filename` / `content` / `path` / `contentType` | Same names |
72
+ | `attachments[].cid` | `attachments[].contentId` |
73
+ | `attachments[].contentDisposition: "inline"` | `attachments[].inline: true` |
74
+ | `icalEvent`, SOCKS proxy options | Not supported — see [Non-goals](/docs/get-started/non-goals) |
75
+
76
+ ## SMTP options
77
+
78
+ | Option | Type | Default | Meaning |
79
+ | --- | --- | --- | --- |
80
+ | `host` | `string` | required | Relay hostname |
81
+ | `port` | `number` | `587` (`465` if `secure`) | SMTP port |
82
+ | `secure` | `boolean` | `false` | Implicit TLS on connect |
83
+ | `auth` | `SMTPAuth` | — | `{ user, pass?, type?, oauth2? }` |
84
+ | `pool` | `boolean` | `false` | Connection pooling |
85
+ | `requireTLS` | `boolean` | `true` when `auth` is set | Refuse AUTH on a cleartext connection |
86
+ | `tls` | `TLSOptions` | — | `rejectUnauthorized`, `servername`, `minVersion` |
87
+ | `connectionTimeout` | `number` | — | Socket connect timeout (ms) |
88
+ | `greetingTimeout` | `number` | — | Wait for SMTP greeting (ms) |
89
+ | `socketTimeout` | `number` | — | Idle socket timeout (ms) |
90
+
91
+ ## Switch to an HTTP provider later
92
+
93
+ Keep `mailer.send` and change only construction:
94
+
95
+ ```diff
96
+ - import { createSMTPMailer } from "sently/smtp";
97
+ - const mailer = await createSMTPMailer({ host, port, auth });
98
+ + import { createMailer } from "sently/mailer";
99
+ + import { ResendTransport } from "sently/transports/resend";
100
+ + const mailer = await createMailer({
101
+ + transport: new ResendTransport({ apiKey: process.env.RESEND_API_KEY! }),
102
+ + });
103
+ ```
104
+
105
+ **Consequence:** message fields stay the same; only the factory and transport change.
106
+
107
+ ## Troubleshooting
108
+
109
+ <Accordions>
110
+ <Accordion title='Error: "SMTP config passed to transport-only createMailer"'>
111
+ `createMailer` accepts only `{ transport, plugins?, hooks? }`. Move host/port/auth to `createSMTPMailer` from `sently/smtp`.
112
+ </Accordion>
113
+ <Accordion title="Do I need to rewrite every message object?">
114
+ Usually no. Keep `from`, `to`, `subject`, and body fields; change construction and `sendMail` → `send`. Rename attachment `cid` to `contentId` when you use inline images.
115
+ </Accordion>
116
+ <Accordion title="Why is createSMTPMailer async?">
117
+ The factory prepares the runtime SMTP connection path (and optionally the pool) before returning. Always `await` it before `send`.
118
+ </Accordion>
119
+ <Accordion title="Is sently a drop-in for every Nodemailer plugin?">
120
+ No. SOCKS and iCal are intentional non-goals. See [Non-goals](/docs/get-started/non-goals) and [Compare](/docs/guides/compare).
121
+ </Accordion>
122
+ </Accordions>
123
+
124
+ ## Learn more
125
+
126
+ - [Email channel](/docs/channels/email) — mailer + transport model after you migrate
127
+ - [Mail options](/docs/reference/mail-options) — full `MailOptions` field list
128
+ - [Attachments](/docs/guides/attachments) — `content`, `path`, and `contentId`
129
+ - [Entrypoints](/docs/get-started/entrypoints) — `sently/smtp` vs `sently/mailer`
130
+ - [Support matrix](/docs/get-started/support-matrix) — which runtimes and exports are supported
20
131
 
21
- <Accordions><Accordion title="Why does transport-only createMailer reject host?">It only accepts a `transport`. Move relay configuration to `createSMTPMailer`.</Accordion></Accordions>
132
+ ## Next
22
133
 
23
- <Cards><Card title="Email channel" href="/docs/channels/email" /></Cards>
134
+ <Cards>
135
+ <Card
136
+ title="Email channel"
137
+ description="Send with createMailer or createSMTPMailer."
138
+ href="/docs/channels/email"
139
+ />
140
+ <Card
141
+ title="SMTP transport"
142
+ description="Relay options when you wire SMTPTransport yourself."
143
+ href="/docs/transports/smtp"
144
+ />
145
+ <Card
146
+ title="Compare"
147
+ description="Nodemailer, vendor SDKs, and orchestration platforms."
148
+ href="/docs/guides/compare"
149
+ />
150
+ </Cards>
@@ -0,0 +1,57 @@
1
+ ---
2
+ title: Non-goals
3
+ description: What sently intentionally does not ship — and what belongs on top.
4
+ icon: Ban
5
+ source: "README.md"
6
+ ---
7
+
8
+ sently is a **channel-delivery library**. Keeping the surface focused is part of the 1.x promise.
9
+
10
+ <Callout title="The one rule">
11
+ Do not expect dashboards, preference centers, digests, or workflow builders inside sently. Put that product logic — or Novu / Knock / Courier — on top of the delivery layer.
12
+ </Callout>
13
+
14
+ ## Out of scope
15
+
16
+ | Non-goal | Notes |
17
+ | --- | --- |
18
+ | Orchestration platforms | Preference centers, digests, journey builders, in-app inboxes |
19
+ | Hosted SaaS / per-send billing | MIT library in your process |
20
+ | APNs as a native push transport | Use FCM and/or Web Push for 1.x |
21
+ | Voice channels | Not a sently channel |
22
+ | Inbound email productization | Outbound + webhook parsers only |
23
+ | Nodemailer SOCKS / iCal parity | Intentionally omitted |
24
+ | Equal support for every export | See [Support matrix](./support-matrix) |
25
+ | Cross-channel preview / idempotency | Email-only today |
26
+
27
+ ## Complementary stack
28
+
29
+ | Need | Place it… |
30
+ | --- | --- |
31
+ | Send email / SMS / WhatsApp / push | sently channel senders |
32
+ | Swap providers without rewriting call sites | sently transports |
33
+ | Preferences, digests, workflows, in-app | Your app or Novu / Knock / Courier **on top of** sently |
34
+
35
+ ## Troubleshooting
36
+
37
+ <Accordions>
38
+ <Accordion title="Will sently add a preference center?">
39
+ No. That is platform territory and conflicts with the library-not-platform position.
40
+ </Accordion>
41
+ <Accordion title="Where should I read the positioning story?">
42
+ See [Compare](/docs/guides/compare).
43
+ </Accordion>
44
+ </Accordions>
45
+
46
+ ## Learn more
47
+
48
+ - [Compare](/docs/guides/compare)
49
+ - [Support matrix](./support-matrix)
50
+ - [Stability policy](./stability)
51
+
52
+ ## Next
53
+
54
+ <Cards>
55
+ <Card title="Compare" href="/docs/guides/compare" />
56
+ <Card title="Introduction" href="/docs/get-started/introduction" />
57
+ </Cards>
@@ -0,0 +1,69 @@
1
+ ---
2
+ title: Stability policy
3
+ description: What is frozen at 1.0 and what may grow under semver.
4
+ icon: Shield
5
+ source: "package.json"
6
+ ---
7
+
8
+ From **1.0.0**, sently follows semver for the public channel-delivery surface.
9
+ Apps should depend on channel senders and contracts — not on vendor SDK shapes.
10
+
11
+ <Callout title="The one rule">
12
+ Treat channel factories, transport contracts, hooks, `SentlyError` codes, and
13
+ published subpath entrypoints as stable. New transports and optional fields may
14
+ appear in minor releases; renames or narrowed types require a major.
15
+ </Callout>
16
+
17
+ ## Frozen at 1.x (Tier A)
18
+
19
+ | Surface | Stable APIs |
20
+ | --- | --- |
21
+ | Factories | `createMailer`, `createSMTPMailer`, `createSmsSender`, `createWhatsAppSender`, `createPushSender` |
22
+ | Contracts | `Transport`, `SmsTransport`, `WhatsAppTransport`, `PushTransport` option and result shapes |
23
+ | Shared | Lifecycle hooks, `SentlyError` / `sentlyCode`, `ChannelSendResult` / `toChannelSendResult` |
24
+ | Decorators | `RetryTransport`, `FallbackTransport` on all channels |
25
+ | Email-only decorators | `IdempotencyTransport`, `PreviewTransport` (documented as email-only; not removed) |
26
+ | Entrypoints | Subpaths listed in `package.json` `exports` — no silent moves |
27
+
28
+ ## Intentional shapes (stable, not transitional)
29
+
30
+ | Shape | Meaning |
31
+ | --- | --- |
32
+ | `PushOptions` | Discriminated union: Web Push `subscription` **or** FCM `token` |
33
+ | `providerIndex` | Optional on send results when a fallback decorator handled the send |
34
+ | Email-only preview / idempotency | Stay email-scoped; not a signal they will move to other channels soon |
35
+
36
+ ## Allowed without a major
37
+
38
+ - New transport classes and webhook parsers
39
+ - New optional fields on options/results
40
+ - New subpath exports
41
+ - Bug fixes and security patches on the current **1.x** line
42
+
43
+ ## Security patches
44
+
45
+ See [`SECURITY.md`](https://github.com/alialnaghmoush/sently/blob/main/SECURITY.md) for reporting and which versions receive patches.
46
+
47
+ ## Troubleshooting
48
+
49
+ <Accordions>
50
+ <Accordion title="Can I pin 0.x in production after 1.0?">
51
+ Prefer upgrading to 1.x. Pre-1.0 may still receive best-effort fixes but is not the supported security line.
52
+ </Accordion>
53
+ <Accordion title="Where is the production support promise?">
54
+ See [Support matrix](./support-matrix) for Supported vs Available transports.
55
+ </Accordion>
56
+ </Accordions>
57
+
58
+ ## Learn more
59
+
60
+ - [Support matrix](./support-matrix)
61
+ - [Non-goals](./non-goals)
62
+ - [Compare](/docs/guides/compare)
63
+
64
+ ## Next
65
+
66
+ <Cards>
67
+ <Card title="Support matrix" href="/docs/get-started/support-matrix" />
68
+ <Card title="Non-goals" href="/docs/get-started/non-goals" />
69
+ </Cards>