sently 0.9.1 → 0.10.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 +1 -1
- package/CHANGELOG.md +60 -0
- package/README.md +18 -16
- package/dist/adapters/bun.js +1 -1
- package/dist/adapters/cf.js +2 -2
- package/dist/adapters/cf.js.map +1 -1
- 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/channel-result.d.ts +6 -0
- package/dist/channel-result.js +1 -0
- package/dist/{chunk-skqhj0rm.js → chunk-1ke3nmyg.js} +3 -3
- package/dist/{chunk-skqhj0rm.js.map → chunk-1ke3nmyg.js.map} +1 -1
- package/dist/{chunk-7gy2q9sh.js → chunk-5es0v1j7.js} +2 -2
- package/dist/{chunk-7gy2q9sh.js.map → chunk-5es0v1j7.js.map} +1 -1
- package/dist/{chunk-0qxws3kj.js → chunk-7wtfjx0d.js} +2 -2
- package/dist/{chunk-0qxws3kj.js.map → chunk-7wtfjx0d.js.map} +1 -1
- package/dist/chunk-8kpgbrba.js +4 -0
- package/dist/{chunk-93vqxxj2.js.map → chunk-8kpgbrba.js.map} +2 -2
- package/dist/{chunk-vjpds2pf.js → chunk-a057jzsy.js} +2 -2
- package/dist/{chunk-vjpds2pf.js.map → chunk-a057jzsy.js.map} +1 -1
- package/dist/{chunk-8njyga62.js → chunk-bhr7w3gg.js} +2 -2
- package/dist/{chunk-8njyga62.js.map → chunk-bhr7w3gg.js.map} +1 -1
- package/dist/chunk-cs1tvehn.js +4 -0
- package/dist/chunk-cs1tvehn.js.map +10 -0
- package/dist/{chunk-29z7fkzy.js → chunk-d5aw5vag.js} +3 -3
- package/dist/{chunk-29z7fkzy.js.map → chunk-d5aw5vag.js.map} +1 -1
- package/dist/chunk-f5x8vesr.js +4 -0
- package/dist/{chunk-b602dhck.js.map → chunk-f5x8vesr.js.map} +2 -2
- package/dist/chunk-fv0bemgf.js +4 -0
- package/dist/{chunk-yp311efr.js.map → chunk-fv0bemgf.js.map} +3 -3
- package/dist/{chunk-ywq6s10g.js → chunk-hnzmn4s4.js} +2 -2
- package/dist/{chunk-ywq6s10g.js.map → chunk-hnzmn4s4.js.map} +1 -1
- package/dist/chunk-htdg075t.js +4 -0
- package/dist/{chunk-qtkd5bak.js.map → chunk-htdg075t.js.map} +4 -3
- package/dist/{chunk-0gqy32pe.js → chunk-q2mahnb7.js} +3 -3
- package/dist/chunk-q2mahnb7.js.map +10 -0
- package/dist/chunk-sbgt4165.js +4 -0
- package/dist/chunk-sbgt4165.js.map +10 -0
- package/dist/{chunk-6npp3x3c.js → chunk-th4cwrpb.js} +2 -2
- package/dist/{chunk-6npp3x3c.js.map → chunk-th4cwrpb.js.map} +1 -1
- package/dist/{chunk-tamww15j.js → chunk-ttbwyxmh.js} +2 -2
- package/dist/{chunk-tamww15j.js.map → chunk-ttbwyxmh.js.map} +1 -1
- package/dist/chunk-w4c9kj70.js +13 -0
- package/dist/{chunk-n0qeyzqm.js.map → chunk-w4c9kj70.js.map} +2 -2
- package/dist/chunk-w9sabmb0.js +4 -0
- package/dist/chunk-w9sabmb0.js.map +10 -0
- package/dist/{chunk-8dty8c4p.js → chunk-yg3kj723.js} +3 -3
- package/dist/{chunk-8dty8c4p.js.map → chunk-yg3kj723.js.map} +2 -2
- package/dist/chunk-z1589fjk.js +4 -0
- package/dist/chunk-z1589fjk.js.map +10 -0
- package/dist/core/channel-result.d.ts +36 -0
- package/dist/core/channel-result.js +3 -0
- package/dist/core/channel-result.js.map +10 -0
- package/dist/core/decorated-transport.d.ts +30 -0
- package/dist/core/decorator-hooks.d.ts +17 -0
- package/dist/core/errors.js +2 -2
- package/dist/core/errors.js.map +1 -1
- package/dist/core/provider-label.d.ts +7 -3
- package/dist/core/push-endpoint.d.ts +5 -0
- package/dist/core/push-types.d.ts +48 -14
- package/dist/core/sms-types.d.ts +12 -0
- package/dist/core/smtp.js +2 -2
- package/dist/core/smtp.js.map +1 -1
- package/dist/core/whatsapp-types.d.ts +12 -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.js +2 -2
- package/dist/idempotency.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/mailer.js +2 -2
- package/dist/mailer.js.map +1 -1
- package/dist/observability/console.js +1 -1
- package/dist/plugins/react.js +2 -2
- package/dist/plugins/react.js.map +1 -1
- 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 +1 -0
- package/dist/push.js +2 -2
- package/dist/push.js.map +3 -3
- package/dist/sms.d.ts +1 -0
- package/dist/sms.js +2 -2
- package/dist/sms.js.map +3 -3
- package/dist/smtp-mailer.js +2 -2
- package/dist/smtp-mailer.js.map +1 -1
- package/dist/transports/brevo.js +2 -2
- package/dist/transports/brevo.js.map +2 -2
- package/dist/transports/cloudflare-email.js +2 -2
- package/dist/transports/cloudflare-email.js.map +2 -2
- package/dist/transports/fallback.d.ts +32 -6
- package/dist/transports/fallback.js +2 -2
- package/dist/transports/fallback.js.map +1 -1
- package/dist/transports/fcm.d.ts +55 -0
- package/dist/transports/fcm.js +4 -0
- package/dist/transports/fcm.js.map +10 -0
- package/dist/transports/loops.js +2 -2
- package/dist/transports/loops.js.map +1 -1
- package/dist/transports/mailersend.js +2 -2
- package/dist/transports/mailersend.js.map +2 -2
- package/dist/transports/mailgun.js +2 -2
- package/dist/transports/mailgun.js.map +2 -2
- package/dist/transports/mailtrap.js +2 -2
- package/dist/transports/mailtrap.js.map +2 -2
- package/dist/transports/msegat.js +2 -2
- package/dist/transports/msegat.js.map +1 -1
- package/dist/transports/plunk.js +2 -2
- package/dist/transports/plunk.js.map +1 -1
- package/dist/transports/postmark.js +2 -2
- package/dist/transports/postmark.js.map +2 -2
- package/dist/transports/preview.js +2 -2
- package/dist/transports/preview.js.map +1 -1
- package/dist/transports/resend.js +2 -2
- package/dist/transports/resend.js.map +2 -2
- package/dist/transports/retry.d.ts +28 -9
- package/dist/transports/retry.js +2 -2
- package/dist/transports/retry.js.map +3 -3
- package/dist/transports/sendgrid.js +2 -2
- package/dist/transports/sendgrid.js.map +2 -2
- package/dist/transports/ses.js +2 -2
- package/dist/transports/ses.js.map +2 -2
- package/dist/transports/smtp.js +2 -2
- package/dist/transports/smtp.js.map +1 -1
- package/dist/transports/sndr.js +2 -2
- package/dist/transports/sndr.js.map +1 -1
- package/dist/transports/sparkpost.js +2 -2
- package/dist/transports/sparkpost.js.map +2 -2
- package/dist/transports/taqnyat-mail.js +2 -2
- package/dist/transports/taqnyat-mail.js.map +1 -1
- package/dist/transports/taqnyat-sms.js +2 -2
- package/dist/transports/taqnyat-sms.js.map +1 -1
- package/dist/transports/taqnyat-whatsapp.js +2 -2
- package/dist/transports/taqnyat-whatsapp.js.map +1 -1
- package/dist/transports/twilio-sms.js +2 -2
- package/dist/transports/twilio-sms.js.map +2 -2
- package/dist/transports/unifonic.d.ts +81 -0
- package/dist/transports/unifonic.js +3 -0
- package/dist/transports/unifonic.js.map +10 -0
- package/dist/transports/webpush.js +2 -2
- package/dist/transports/webpush.js.map +3 -3
- package/dist/transports/weighted-fallback.d.ts +11 -9
- package/dist/transports/weighted-fallback.js +2 -2
- package/dist/transports/weighted-fallback.js.map +3 -3
- package/dist/transports/whatsapp-cloud.js +2 -2
- package/dist/transports/whatsapp-cloud.js.map +1 -1
- package/dist/webhooks/brevo.js +2 -2
- package/dist/webhooks/brevo.js.map +1 -1
- package/dist/webhooks/mailgun.js +2 -2
- package/dist/webhooks/mailgun.js.map +1 -1
- package/dist/webhooks/postmark.js +2 -2
- package/dist/webhooks/postmark.js.map +1 -1
- package/dist/webhooks/resend.js +2 -2
- package/dist/webhooks/resend.js.map +1 -1
- package/dist/webhooks/sendgrid.js +2 -2
- package/dist/webhooks/sendgrid.js.map +1 -1
- package/dist/webhooks/ses.js +2 -2
- package/dist/webhooks/ses.js.map +1 -1
- package/dist/webhooks/sndr.js +2 -2
- package/dist/webhooks/sndr.js.map +1 -1
- package/dist/webhooks/timing-safe-equal.js +1 -1
- package/dist/webhooks/twilio-sms.d.ts +20 -0
- package/dist/webhooks/twilio-sms.js +3 -0
- package/dist/webhooks/twilio-sms.js.map +10 -0
- package/dist/webhooks/types.d.ts +37 -3
- package/dist/webhooks/types.js +3 -0
- package/dist/webhooks/types.js.map +9 -0
- package/dist/webhooks/unifonic.d.ts +13 -0
- package/dist/webhooks/unifonic.js +3 -0
- package/dist/webhooks/unifonic.js.map +10 -0
- package/dist/webhooks/whatsapp-cloud.d.ts +14 -0
- package/dist/webhooks/whatsapp-cloud.js +3 -0
- package/dist/webhooks/whatsapp-cloud.js.map +10 -0
- package/dist/webhooks.d.ts +10 -5
- package/dist/webhooks.js +11 -0
- package/dist/whatsapp.d.ts +1 -0
- package/dist/whatsapp.js +2 -2
- package/dist/whatsapp.js.map +3 -3
- package/package.json +26 -2
- package/site/content/docs/channels/hooks.mdx +6 -5
- package/site/content/docs/channels/index.mdx +2 -2
- package/site/content/docs/channels/push.mdx +27 -21
- package/site/content/docs/channels/sms.mdx +4 -1
- package/site/content/docs/decorators/fallback.mdx +41 -14
- package/site/content/docs/decorators/index.mdx +29 -12
- package/site/content/docs/decorators/retry.mdx +32 -9
- package/site/content/docs/decorators/weighted-fallback.mdx +27 -6
- package/site/content/docs/get-started/entrypoints.mdx +6 -5
- package/site/content/docs/get-started/installation.mdx +3 -2
- package/site/content/docs/get-started/introduction.mdx +1 -1
- package/site/content/docs/guides/webhooks.mdx +35 -20
- package/site/content/docs/index.mdx +1 -1
- package/site/content/docs/quick-start/push.mdx +22 -5
- package/site/content/docs/reference/channel-result.mdx +82 -0
- package/site/content/docs/reference/exports.mdx +5 -3
- package/site/content/docs/reference/meta.json +1 -0
- package/site/content/docs/reference/push-options.mdx +44 -3
- package/site/content/docs/reference/sms-options.mdx +18 -2
- package/site/content/docs/reference/webhook-events.mdx +38 -14
- package/site/content/docs/reference/whatsapp-options.mdx +19 -1
- package/site/content/docs/transports/fcm.mdx +80 -0
- package/site/content/docs/transports/index.mdx +3 -1
- package/site/content/docs/transports/meta.json +3 -1
- package/site/content/docs/transports/unifonic.mdx +85 -0
- package/dist/chunk-0gqy32pe.js.map +0 -10
- package/dist/chunk-5915vbcj.js +0 -4
- package/dist/chunk-5915vbcj.js.map +0 -10
- package/dist/chunk-93vqxxj2.js +0 -4
- package/dist/chunk-b602dhck.js +0 -4
- package/dist/chunk-gp4fwrs8.js +0 -4
- package/dist/chunk-gp4fwrs8.js.map +0 -10
- package/dist/chunk-n0qeyzqm.js +0 -13
- package/dist/chunk-q8k3m6qe.js +0 -4
- package/dist/chunk-q8k3m6qe.js.map +0 -10
- package/dist/chunk-qtkd5bak.js +0 -4
- package/dist/chunk-yp311efr.js +0 -4
package/dist/whatsapp.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{$ as K,aa as L,ba as D,ca as O}from"./chunk-htdg075t.js";import"./chunk-th4cwrpb.js";function X(G,q){return{...G.messageId!==void 0?{messageId:G.messageId}:{},to:G.to,provider:q.provider??"whatsapp"}}function $(G){let{transport:q,plugins:Q,hooks:B}=G;return{async send(U){let M=await O(U,Q),I=X(M,q);if(await D(B?.onSend,I),B?.onRetry!==void 0&&K(q))q.setMailerOnRetry((z,J)=>{D(B.onRetry,I,z,J)});if(B?.onFallback!==void 0&&L(q))q.setMailerOnFallback((z,J,V)=>{D(B.onFallback,I,z,J,V)});let N=performance.now();try{let z=await q.send(M),J={...I,messageId:z.messageId};return await D(B?.onSuccess,J,z,performance.now()-N),z}catch(z){throw await D(B?.onError,I,z,performance.now()-N),z}finally{if(K(q))q.setMailerOnRetry(void 0);if(L(q))q.setMailerOnFallback(void 0)}},async verify(){if(q.verify)return q.verify();return{ok:!0,provider:"whatsapp"}},async close(){if(q.close)await q.close()}}}export{$ as createWhatsAppSender};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=4C481E7BE929D0B764756E2164756E21
|
package/dist/whatsapp.js.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/whatsapp.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * @module\n * WhatsApp sender orchestrator — plugins, hooks, and transport.send pipeline.\n *\n * Sently-first: apps call {@link createWhatsAppSender}; providers implement\n * {@link WhatsAppTransport}. Multi-product vendors get a separate WhatsApp\n * transport (e.g.
|
|
5
|
+
"/**\n * @module\n * WhatsApp sender orchestrator — plugins, hooks, and transport.send pipeline.\n *\n * Sently-first: apps call {@link createWhatsAppSender}; providers implement\n * {@link WhatsAppTransport}. Multi-product vendors get a separate WhatsApp\n * transport (e.g. `taqnyat-whatsapp`), not a mega vendor client.\n *\n * @example\n * ```ts\n * import { createWhatsAppSender } from \"sently/whatsapp\";\n * import { WhatsAppCloudTransport } from \"sently/transports/whatsapp-cloud\";\n *\n * const wa = createWhatsAppSender({\n * transport: new WhatsAppCloudTransport({\n * accessToken: \"...\",\n * phoneNumberId: \"...\",\n * }),\n * });\n *\n * await wa.send({ to: \"15551234567\", text: \"Hello\" });\n * ```\n */\nimport { isFallbackHookTransport, isRetryHookTransport } from \"./core/decorator-hooks.js\";\nimport { invokeHook } from \"./core/hooks.js\";\nimport { runPlugins } from \"./core/plugin.js\";\nimport type { VerifyResult } from \"./core/types.js\";\nimport type {\n WhatsAppHookContext,\n WhatsAppHooks,\n WhatsAppOptions,\n WhatsAppPlugin,\n WhatsAppSendResult,\n WhatsAppTransport,\n} from \"./core/whatsapp-types.js\";\n\n/** Configuration for {@link createWhatsAppSender}. */\nexport interface WhatsAppSenderConfig {\n /** WhatsApp delivery transport. */\n transport: WhatsAppTransport;\n /** Optional plugins run before each send. */\n plugins?: WhatsAppPlugin[];\n /** Optional lifecycle hooks for metrics and tracing. */\n hooks?: WhatsAppHooks;\n}\n\n/** WhatsApp sender returned by {@link createWhatsAppSender}. */\nexport interface WhatsAppSender {\n /** Send a WhatsApp message through the configured transport. */\n send(options: WhatsAppOptions): Promise<WhatsAppSendResult>;\n /** Test connectivity and credentials without sending. */\n verify(): Promise<VerifyResult>;\n /** Release resources held by the transport. */\n close(): Promise<void>;\n}\n\nfunction buildWhatsAppHookContext(\n options: WhatsAppOptions,\n transport: WhatsAppTransport,\n): WhatsAppHookContext {\n return {\n ...(options.messageId !== undefined ? { messageId: options.messageId } : {}),\n to: options.to,\n provider: transport.provider ?? \"whatsapp\",\n };\n}\n\n/**\n * Create a WhatsApp sender that wraps a {@link WhatsAppTransport}.\n *\n * Pipeline: plugins → onSend → transport.send → onSuccess / onError.\n * `onRetry` / `onFallback` wire when the transport is a retry or fallback decorator.\n */\nexport function createWhatsAppSender(config: WhatsAppSenderConfig): WhatsAppSender {\n const { transport, plugins, hooks } = config;\n\n return {\n async send(options: WhatsAppOptions): Promise<WhatsAppSendResult> {\n const processed = await runPlugins(options, plugins);\n const ctx = buildWhatsAppHookContext(processed, transport);\n\n await invokeHook(hooks?.onSend, ctx);\n\n if (hooks?.onRetry !== undefined && isRetryHookTransport(transport)) {\n transport.setMailerOnRetry((attempt, error) => {\n void invokeHook(hooks.onRetry, ctx, attempt, error);\n });\n }\n\n if (hooks?.onFallback !== undefined && isFallbackHookTransport(transport)) {\n transport.setMailerOnFallback((failedProvider, nextProvider, error) => {\n void invokeHook(hooks.onFallback, ctx, failedProvider, nextProvider, error);\n });\n }\n\n const start = performance.now();\n\n try {\n const result = await transport.send(processed);\n const successCtx: WhatsAppHookContext = {\n ...ctx,\n messageId: result.messageId,\n };\n await invokeHook(hooks?.onSuccess, successCtx, result, performance.now() - start);\n return result;\n } catch (error) {\n await invokeHook(hooks?.onError, ctx, error, performance.now() - start);\n throw error;\n } finally {\n if (isRetryHookTransport(transport)) {\n transport.setMailerOnRetry(undefined);\n }\n if (isFallbackHookTransport(transport)) {\n transport.setMailerOnFallback(undefined);\n }\n }\n },\n\n async verify(): Promise<VerifyResult> {\n if (transport.verify) {\n return transport.verify();\n }\n return { ok: true, provider: \"whatsapp\" };\n },\n\n async close(): Promise<void> {\n if (transport.close) {\n await transport.close();\n }\n },\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "kGAwDA,GAAS,CAAwB,CAC/B,EACA,EACqB,CACrB,MAAO,IACD,EAAQ,YAAc,OAAY,CAAE,UAAW,EAAQ,SAAU,EAAI,CAAC,EAC1E,GAAI,EAAQ,GACZ,SAAU,EAAU,UAAY,UAClC,EASK,SAAS,CAAoB,CAAC,EAA8C,CACjF,IAAQ,YAAW,UAAS,SAAU,EAEtC,MAAO,MACC,KAAI,CAAC,EAAuD,CAChE,IAAM,EAAY,MAAM,EAAW,EAAS,CAAO,EAC7C,EAAM,EAAyB,EAAW,CAAS,EAIzD,GAFA,MAAM,EAAW,GAAO,OAAQ,CAAG,EAE/B,GAAO,UAAY,QAAa,EAAqB,CAAS,EAChE,EAAU,iBAAiB,CAAC,EAAS,IAAU,CACxC,EAAW,EAAM,QAAS,EAAK,EAAS,CAAK,EACnD,EAGH,GAAI,GAAO,aAAe,QAAa,EAAwB,CAAS,EACtE,EAAU,oBAAoB,CAAC,EAAgB,EAAc,IAAU,CAChE,EAAW,EAAM,WAAY,EAAK,EAAgB,EAAc,CAAK,EAC3E,EAGH,IAAM,EAAQ,YAAY,IAAI,EAE9B,GAAI,CACF,IAAM,EAAS,MAAM,EAAU,KAAK,CAAS,EACvC,EAAkC,IACnC,EACH,UAAW,EAAO,SACpB,EAEA,OADA,MAAM,EAAW,GAAO,UAAW,EAAY,EAAQ,YAAY,IAAI,EAAI,CAAK,EACzE,EACP,MAAO,EAAO,CAEd,MADA,MAAM,EAAW,GAAO,QAAS,EAAK,EAAO,YAAY,IAAI,EAAI,CAAK,EAChE,SACN,CACA,GAAI,EAAqB,CAAS,EAChC,EAAU,iBAAiB,MAAS,EAEtC,GAAI,EAAwB,CAAS,EACnC,EAAU,oBAAoB,MAAS,SAKvC,OAAM,EAA0B,CACpC,GAAI,EAAU,OACZ,OAAO,EAAU,OAAO,EAE1B,MAAO,CAAE,GAAI,GAAM,SAAU,UAAW,QAGpC,MAAK,EAAkB,CAC3B,GAAI,EAAU,MACZ,MAAM,EAAU,MAAM,EAG5B",
|
|
8
|
+
"debugId": "4C481E7BE929D0B764756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sently",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"mcp": "bun tools/mcp/index.ts",
|
|
32
32
|
"site:dev": "bun run --cwd site dev",
|
|
33
33
|
"site:build": "bun run --cwd site build",
|
|
34
|
-
"verify": "bun run lint && bun run typecheck && bun run build && bun test",
|
|
34
|
+
"verify": "bun run lint && bun run typecheck && bun run build && bun run site:build && bun test",
|
|
35
35
|
"prepack": "bun run verify",
|
|
36
36
|
"pack:npm": "npm pack --dry-run --cache ./.npm-cache",
|
|
37
37
|
"publish:npm-d": "npm publish --dry-run --cache ./.npm-cache",
|
|
@@ -207,6 +207,10 @@
|
|
|
207
207
|
"import": "./dist/transports/msegat.js",
|
|
208
208
|
"types": "./dist/transports/msegat.d.ts"
|
|
209
209
|
},
|
|
210
|
+
"./transports/unifonic": {
|
|
211
|
+
"import": "./dist/transports/unifonic.js",
|
|
212
|
+
"types": "./dist/transports/unifonic.d.ts"
|
|
213
|
+
},
|
|
210
214
|
"./transports/whatsapp-cloud": {
|
|
211
215
|
"import": "./dist/transports/whatsapp-cloud.js",
|
|
212
216
|
"types": "./dist/transports/whatsapp-cloud.d.ts"
|
|
@@ -215,6 +219,14 @@
|
|
|
215
219
|
"import": "./dist/transports/webpush.js",
|
|
216
220
|
"types": "./dist/transports/webpush.d.ts"
|
|
217
221
|
},
|
|
222
|
+
"./transports/fcm": {
|
|
223
|
+
"import": "./dist/transports/fcm.js",
|
|
224
|
+
"types": "./dist/transports/fcm.d.ts"
|
|
225
|
+
},
|
|
226
|
+
"./channel-result": {
|
|
227
|
+
"import": "./dist/channel-result.js",
|
|
228
|
+
"types": "./dist/channel-result.d.ts"
|
|
229
|
+
},
|
|
218
230
|
"./observability": {
|
|
219
231
|
"import": "./dist/observability/console.js",
|
|
220
232
|
"types": "./dist/observability/console.d.ts"
|
|
@@ -295,6 +307,18 @@
|
|
|
295
307
|
"import": "./dist/webhooks/sndr.js",
|
|
296
308
|
"types": "./dist/webhooks/sndr.d.ts"
|
|
297
309
|
},
|
|
310
|
+
"./webhooks/twilio-sms": {
|
|
311
|
+
"import": "./dist/webhooks/twilio-sms.js",
|
|
312
|
+
"types": "./dist/webhooks/twilio-sms.d.ts"
|
|
313
|
+
},
|
|
314
|
+
"./webhooks/whatsapp-cloud": {
|
|
315
|
+
"import": "./dist/webhooks/whatsapp-cloud.js",
|
|
316
|
+
"types": "./dist/webhooks/whatsapp-cloud.d.ts"
|
|
317
|
+
},
|
|
318
|
+
"./webhooks/unifonic": {
|
|
319
|
+
"import": "./dist/webhooks/unifonic.js",
|
|
320
|
+
"types": "./dist/webhooks/unifonic.d.ts"
|
|
321
|
+
},
|
|
298
322
|
"./errors": {
|
|
299
323
|
"import": "./dist/errors.js",
|
|
300
324
|
"types": "./dist/errors.d.ts"
|
|
@@ -50,21 +50,22 @@ await mailer.send({
|
|
|
50
50
|
| Channel | Callbacks | Context |
|
|
51
51
|
| --- | --- | --- |
|
|
52
52
|
| Email | `onSend`, `onSuccess`, `onError`, `onRetry`, `onFallback` | `messageId`, recipients, subject, provider. |
|
|
53
|
-
| SMS | `onSend`, `onSuccess`, `onError` | `messageId`, recipient number, provider. |
|
|
54
|
-
| WhatsApp | `onSend`, `onSuccess`, `onError` | `messageId`, recipient number, provider. |
|
|
55
|
-
| Push | `onSend`, `onSuccess`, `onError` | `messageId`, redacted endpoint, provider. |
|
|
53
|
+
| SMS | `onSend`, `onSuccess`, `onError`, `onRetry`, `onFallback` | `messageId`, recipient number, provider. |
|
|
54
|
+
| WhatsApp | `onSend`, `onSuccess`, `onError`, `onRetry`, `onFallback` | `messageId`, recipient number, provider. |
|
|
55
|
+
| Push | `onSend`, `onSuccess`, `onError`, `onRetry`, `onFallback` | `messageId`, redacted endpoint / FCM token fingerprint, provider. |
|
|
56
56
|
|
|
57
57
|
`onSuccess` receives the delivery result and optional duration in milliseconds. `onError` receives the error and optional duration.
|
|
58
58
|
|
|
59
59
|
## Retry and fallback
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
`onRetry` runs only with `RetryTransport`. `onFallback` runs only with a fallback (or weighted fallback) transport.
|
|
62
|
+
Wire either decorator around the channel transport you pass to `createMailer`, `createSmsSender`, `createWhatsAppSender`, or `createPushSender`.
|
|
62
63
|
|
|
63
64
|
## Troubleshooting
|
|
64
65
|
|
|
65
66
|
<Accordions>
|
|
66
67
|
<Accordion title="Why does onRetry not run?">
|
|
67
|
-
`onRetry` is wired only when the
|
|
68
|
+
`onRetry` is wired only when the sender's transport is `RetryTransport`.
|
|
68
69
|
</Accordion>
|
|
69
70
|
<Accordion title="Why does my hook error not stop the send?">
|
|
70
71
|
Hook failures are intentionally swallowed so observability code cannot break message delivery.
|
|
@@ -18,7 +18,7 @@ Each sender accepts a provider transport, so delivery providers can change witho
|
|
|
18
18
|
| Email | `createMailer` or `createSMTPMailer` | Sender, recipient, subject, and body. |
|
|
19
19
|
| SMS | `createSmsSender` | Recipient number and body. |
|
|
20
20
|
| WhatsApp | `createWhatsAppSender` | Template or text message. |
|
|
21
|
-
| Push | `createPushSender` |
|
|
21
|
+
| Push | `createPushSender` | Web Push subscription or FCM token, plus title and body. |
|
|
22
22
|
|
|
23
23
|
</Step>
|
|
24
24
|
<Step title="Wire in a transport">
|
|
@@ -52,7 +52,7 @@ await sms.send({
|
|
|
52
52
|
| `createSMTPMailer` | Yes | Email configured with SMTP host, port, and authentication. |
|
|
53
53
|
| `createSmsSender` | No | SMS with an `SmsTransport`. |
|
|
54
54
|
| `createWhatsAppSender` | No | WhatsApp with a `WhatsAppTransport`. |
|
|
55
|
-
| `createPushSender` | No | Web Push with a `PushTransport`. |
|
|
55
|
+
| `createPushSender` | No | Web Push or FCM with a `PushTransport`. |
|
|
56
56
|
|
|
57
57
|
## Troubleshooting
|
|
58
58
|
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Push
|
|
3
|
-
description: Send
|
|
3
|
+
description: Send Web Push or FCM notifications through a push transport.
|
|
4
4
|
icon: Bell
|
|
5
5
|
source: "src/push.ts"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Use
|
|
9
|
-
The sender passes the
|
|
8
|
+
Use push for browser Web Push subscriptions or FCM device tokens.
|
|
9
|
+
The sender passes the target and notification payload to a push transport.
|
|
10
10
|
|
|
11
|
-
<Callout title="The one rule">Treat subscription endpoints as secrets
|
|
11
|
+
<Callout title="The one rule">Treat subscription endpoints and device tokens as secrets — they are long-lived delivery credentials.</Callout>
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
12
14
|
|
|
13
15
|
<Steps>
|
|
14
16
|
<Step title="Create a Push sender">
|
|
@@ -27,10 +29,11 @@ const push = createPushSender({
|
|
|
27
29
|
```
|
|
28
30
|
|
|
29
31
|
</Step>
|
|
30
|
-
<Step title="Send to a
|
|
32
|
+
<Step title="Send to a browser subscription or FCM token">
|
|
31
33
|
|
|
32
34
|
```ts
|
|
33
|
-
|
|
35
|
+
// Web Push
|
|
36
|
+
await push.send({
|
|
34
37
|
subscription: {
|
|
35
38
|
endpoint: "https://fcm.googleapis.com/fcm/send/example",
|
|
36
39
|
keys: { p256dh: "browser-public-key", auth: "browser-auth-secret" },
|
|
@@ -39,7 +42,12 @@ const result = await push.send({
|
|
|
39
42
|
body: "Your weekly report is ready to view.",
|
|
40
43
|
});
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
// FCM (with FcmTransport)
|
|
46
|
+
await push.send({
|
|
47
|
+
token: deviceRegistrationToken,
|
|
48
|
+
title: "Report ready",
|
|
49
|
+
body: "Your weekly report is ready to view.",
|
|
50
|
+
});
|
|
43
51
|
```
|
|
44
52
|
|
|
45
53
|
</Step>
|
|
@@ -53,15 +61,16 @@ console.log(result.status);
|
|
|
53
61
|
| `plugins` | `PushPlugin[]` | Optional transforms run before delivery. |
|
|
54
62
|
| `hooks` | `PushHooks` | Optional callbacks for delivery activity. |
|
|
55
63
|
|
|
56
|
-
##
|
|
64
|
+
## Options shapes
|
|
57
65
|
|
|
58
|
-
|
|
66
|
+
`PushOptions` is a union:
|
|
67
|
+
|
|
68
|
+
| Shape | Required target | Transport |
|
|
59
69
|
| --- | --- | --- |
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
| `subject` | `string` | Required Web Push contact URI for the VAPID transport. |
|
|
70
|
+
| Web Push | `subscription` | `WebPushTransport` |
|
|
71
|
+
| FCM | `token` | `FcmTransport` |
|
|
72
|
+
|
|
73
|
+
Shared fields: `title`, `body`, optional `data`, `icon`, `ttl`, `messageId`.
|
|
65
74
|
|
|
66
75
|
## Troubleshooting
|
|
67
76
|
|
|
@@ -70,23 +79,20 @@ console.log(result.status);
|
|
|
70
79
|
Web Push validates subscription endpoint hosts before sending. Add only exact private relay hostnames with `allowedEndpointHosts` when needed.
|
|
71
80
|
</Accordion>
|
|
72
81
|
<Accordion title="Why is the endpoint redacted in hooks?">
|
|
73
|
-
|
|
74
|
-
</Accordion>
|
|
75
|
-
<Accordion title="What does verify do?">
|
|
76
|
-
`push.verify()` delegates to the transport when available; the Web Push transport checks its VAPID credential shape.
|
|
82
|
+
Endpoint paths and FCM tokens are long-lived credentials, so hook context keeps only a redacted fingerprint.
|
|
77
83
|
</Accordion>
|
|
78
84
|
</Accordions>
|
|
79
85
|
|
|
80
86
|
## Learn more
|
|
81
87
|
|
|
82
88
|
- [Push options](../reference/push-options)
|
|
83
|
-
- [
|
|
84
|
-
- [
|
|
89
|
+
- [Web Push](../transports/webpush)
|
|
90
|
+
- [FCM](../transports/fcm)
|
|
85
91
|
- [Hooks](./hooks)
|
|
86
92
|
|
|
87
93
|
## Next
|
|
88
94
|
|
|
89
95
|
<Cards>
|
|
90
96
|
<Card title="Web Push transport" href="/docs/transports/webpush" />
|
|
91
|
-
<Card title="
|
|
97
|
+
<Card title="FCM transport" href="/docs/transports/fcm" />
|
|
92
98
|
</Cards>
|
|
@@ -77,12 +77,15 @@ Twilio also accepts a default `from` or `messagingServiceSid` when you construct
|
|
|
77
77
|
## Learn more
|
|
78
78
|
|
|
79
79
|
- [SMS options](../reference/sms-options)
|
|
80
|
-
- [
|
|
80
|
+
- [Unifonic](../transports/unifonic)
|
|
81
|
+
- [Decorators](../decorators) — retry / fallback across SMS providers
|
|
82
|
+
- [Channel send result](../reference/channel-result)
|
|
81
83
|
- [Hooks](./hooks)
|
|
82
84
|
|
|
83
85
|
## Next
|
|
84
86
|
|
|
85
87
|
<Cards>
|
|
88
|
+
<Card title="Unifonic" href="/docs/transports/unifonic" />
|
|
86
89
|
<Card title="SMS transports" href="/docs/transports" />
|
|
87
90
|
<Card title="Provider OTP extras" href="/docs/guides/vendor-extras-otp" />
|
|
88
91
|
</Cards>
|
|
@@ -1,34 +1,61 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Fallback
|
|
3
|
-
description: Fail over
|
|
3
|
+
description: Fail over through an ordered list of channel transports.
|
|
4
4
|
icon: GitBranch
|
|
5
5
|
source: "src/transports/fallback.ts"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Fail over
|
|
8
|
+
Fail over through an ordered list of transports when the current one errors.
|
|
9
|
+
Works with email, SMS, WhatsApp, and push.
|
|
9
10
|
|
|
10
|
-
<Callout title="The one rule">
|
|
11
|
+
<Callout title="The one rule">Wrap each entry in `RetryTransport` when you want in-provider retries before failover.</Callout>
|
|
11
12
|
|
|
12
|
-
##
|
|
13
|
+
## Quick start
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<Steps><Step title="Wrap an email transport">
|
|
15
|
+
<Steps>
|
|
16
|
+
<Step title="Build a chain">
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
19
|
import { FallbackTransport } from "sently/transports/fallback";
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
import { TaqnyatSmsTransport } from "sently/transports/taqnyat-sms";
|
|
21
|
+
import { UnifonicTransport } from "sently/transports/unifonic";
|
|
22
|
+
|
|
23
|
+
const transport = new FallbackTransport(
|
|
24
|
+
[
|
|
25
|
+
new TaqnyatSmsTransport({ bearerToken: process.env.TAQNYAT_TOKEN!, sender: "MyBrand" }),
|
|
26
|
+
new UnifonicTransport({ appSid: process.env.UNIFONIC_APPSID!, senderId: "MyBrand" }),
|
|
27
|
+
],
|
|
28
|
+
{ cooldownMs: 300_000 },
|
|
29
|
+
);
|
|
22
30
|
```
|
|
23
31
|
|
|
24
|
-
</Step
|
|
32
|
+
</Step>
|
|
33
|
+
<Step title="Use the matching sender">
|
|
25
34
|
|
|
26
35
|
```ts
|
|
27
|
-
|
|
36
|
+
import { createSmsSender } from "sently/sms";
|
|
37
|
+
|
|
38
|
+
const sms = createSmsSender({ transport });
|
|
28
39
|
```
|
|
29
40
|
|
|
30
|
-
</Step
|
|
41
|
+
</Step>
|
|
42
|
+
</Steps>
|
|
43
|
+
|
|
44
|
+
## Result fields
|
|
45
|
+
|
|
46
|
+
Successful sends include `provider` and `providerIndex` for the transport that handled the message.
|
|
47
|
+
|
|
48
|
+
## Troubleshooting
|
|
49
|
+
|
|
50
|
+
<Accordions>
|
|
51
|
+
<Accordion title="Why did it not fail over on 400?">
|
|
52
|
+
Permanent client errors (400 / 401 / 403) do not fail over by default — they usually fail on every provider.
|
|
53
|
+
</Accordion>
|
|
54
|
+
</Accordions>
|
|
31
55
|
|
|
32
|
-
|
|
56
|
+
## Next
|
|
33
57
|
|
|
34
|
-
<Cards
|
|
58
|
+
<Cards>
|
|
59
|
+
<Card title="Weighted fallback" href="/docs/decorators/weighted-fallback" />
|
|
60
|
+
<Card title="Retry" href="/docs/decorators/retry" />
|
|
61
|
+
</Cards>
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Decorators
|
|
3
|
-
description: Wrap
|
|
3
|
+
description: Wrap any channel transport with retry, fallback, preview, or idempotency.
|
|
4
4
|
icon: Layers
|
|
5
5
|
source: "src/transports/retry.ts"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Decorators wrap another
|
|
8
|
+
Decorators wrap another transport. The channel sender still calls `send` the same way — only the transport path changes.
|
|
9
|
+
Retry, fallback, and weighted fallback work for email, SMS, WhatsApp, and push.
|
|
10
|
+
Preview and idempotency remain email-oriented.
|
|
9
11
|
|
|
10
|
-
<Callout title="The one rule">Pass the outermost decorator to
|
|
12
|
+
<Callout title="The one rule">Pass the outermost decorator to the matching channel sender (`createMailer`, `createSmsSender`, …).</Callout>
|
|
11
13
|
|
|
12
14
|
## Available decorators
|
|
13
15
|
|
|
14
|
-
| Decorator | Import |
|
|
15
|
-
| --- | --- |
|
|
16
|
-
| [Preview](./preview) | `sently/transports/preview` |
|
|
17
|
-
| [Retry](./retry) | `sently/transports/retry` |
|
|
18
|
-
| [Fallback](./fallback) | `sently/transports/fallback` |
|
|
19
|
-
| [Weighted fallback](./weighted-fallback) | `sently/transports/weighted-fallback` |
|
|
20
|
-
| [Idempotency](./idempotency) | `sently/idempotency` |
|
|
16
|
+
| Decorator | Import | Channels |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| [Preview](./preview) | `sently/transports/preview` | Email |
|
|
19
|
+
| [Retry](./retry) | `sently/transports/retry` | Email, SMS, WhatsApp, Push |
|
|
20
|
+
| [Fallback](./fallback) | `sently/transports/fallback` | Email, SMS, WhatsApp, Push |
|
|
21
|
+
| [Weighted fallback](./weighted-fallback) | `sently/transports/weighted-fallback` | Email, SMS, WhatsApp, Push |
|
|
22
|
+
| [Idempotency](./idempotency) | `sently/idempotency` | Email |
|
|
21
23
|
|
|
22
|
-
## Typical stack
|
|
24
|
+
## Typical stack (email)
|
|
23
25
|
|
|
24
26
|
```ts
|
|
25
27
|
import { createMailer } from "sently/mailer";
|
|
@@ -35,10 +37,25 @@ const transport = new FallbackTransport([
|
|
|
35
37
|
const mailer = await createMailer({ transport });
|
|
36
38
|
```
|
|
37
39
|
|
|
40
|
+
## Typical stack (SMS)
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { createSmsSender } from "sently/sms";
|
|
44
|
+
import { FallbackTransport } from "sently/transports/fallback";
|
|
45
|
+
import { TaqnyatSmsTransport } from "sently/transports/taqnyat-sms";
|
|
46
|
+
import { UnifonicTransport } from "sently/transports/unifonic";
|
|
47
|
+
|
|
48
|
+
const sms = createSmsSender({
|
|
49
|
+
transport: new FallbackTransport([
|
|
50
|
+
new TaqnyatSmsTransport({ bearerToken: process.env.TAQNYAT_TOKEN!, sender: "MyBrand" }),
|
|
51
|
+
new UnifonicTransport({ appSid: process.env.UNIFONIC_APPSID!, senderId: "MyBrand" }),
|
|
52
|
+
]),
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
38
56
|
## Learn more
|
|
39
57
|
|
|
40
58
|
- [Transports](../transports)
|
|
41
|
-
- [Email channel](../channels/email)
|
|
42
59
|
- [Hooks](../channels/hooks)
|
|
43
60
|
|
|
44
61
|
## Next
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Retry
|
|
3
|
-
description: Retry failed
|
|
3
|
+
description: Retry failed sends with configurable backoff on any channel transport.
|
|
4
4
|
icon: RotateCw
|
|
5
5
|
source: "src/transports/retry.ts"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Retry failed
|
|
8
|
+
Retry failed sends with configurable backoff.
|
|
9
|
+
Works with email, SMS, WhatsApp, and push transports.
|
|
9
10
|
|
|
10
11
|
<Callout title="The one rule">Authentication errors are not retried.</Callout>
|
|
11
12
|
|
|
@@ -13,22 +14,44 @@ Retry failed email sends with configurable backoff.
|
|
|
13
14
|
|
|
14
15
|
`maxAttempts`, `backoff`, `baseDelay`, `retryOn`, and `onRetry` are optional.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
<Steps>
|
|
20
|
+
<Step title="Wrap a transport">
|
|
17
21
|
|
|
18
22
|
```ts
|
|
19
23
|
import { RetryTransport } from "sently/transports/retry";
|
|
24
|
+
import { TaqnyatSmsTransport } from "sently/transports/taqnyat-sms";
|
|
20
25
|
|
|
21
|
-
const transport = new RetryTransport(
|
|
26
|
+
const transport = new RetryTransport(
|
|
27
|
+
new TaqnyatSmsTransport({ bearerToken: "...", sender: "MyBrand" }),
|
|
28
|
+
{ maxAttempts: 3 },
|
|
29
|
+
);
|
|
22
30
|
```
|
|
23
31
|
|
|
24
|
-
</Step
|
|
32
|
+
</Step>
|
|
33
|
+
<Step title="Pass it to the channel sender">
|
|
25
34
|
|
|
26
35
|
```ts
|
|
27
|
-
|
|
36
|
+
import { createSmsSender } from "sently/sms";
|
|
37
|
+
|
|
38
|
+
const sms = createSmsSender({ transport });
|
|
28
39
|
```
|
|
29
40
|
|
|
30
|
-
</Step
|
|
41
|
+
</Step>
|
|
42
|
+
</Steps>
|
|
43
|
+
|
|
44
|
+
## Troubleshooting
|
|
45
|
+
|
|
46
|
+
<Accordions>
|
|
47
|
+
<Accordion title="Does this replace the inner provider?">
|
|
48
|
+
No. It delegates sends to the wrapped transport.
|
|
49
|
+
</Accordion>
|
|
50
|
+
</Accordions>
|
|
31
51
|
|
|
32
|
-
|
|
52
|
+
## Next
|
|
33
53
|
|
|
34
|
-
<Cards
|
|
54
|
+
<Cards>
|
|
55
|
+
<Card title="Fallback" href="/docs/decorators/fallback" />
|
|
56
|
+
<Card title="Hooks" href="/docs/channels/hooks" />
|
|
57
|
+
</Cards>
|
|
@@ -6,6 +6,7 @@ source: "src/transports/weighted-fallback.ts"
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
Choose a weighted primary transport and fail over on errors.
|
|
9
|
+
Works with email, SMS, WhatsApp, and push.
|
|
9
10
|
|
|
10
11
|
<Callout title="The one rule">At least one entry with positive total weight is required.</Callout>
|
|
11
12
|
|
|
@@ -13,22 +14,42 @@ Choose a weighted primary transport and fail over on errors.
|
|
|
13
14
|
|
|
14
15
|
Each entry has `transport` and positive `weight`; it accepts fallback options and optional `random`.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
<Steps>
|
|
20
|
+
<Step title="Build weighted entries">
|
|
17
21
|
|
|
18
22
|
```ts
|
|
19
23
|
import { WeightedFallbackTransport } from "sently/transports/weighted-fallback";
|
|
20
24
|
|
|
21
|
-
const transport = new WeightedFallbackTransport([
|
|
25
|
+
const transport = new WeightedFallbackTransport([
|
|
26
|
+
{ transport: primary, weight: 80 },
|
|
27
|
+
{ transport: secondary, weight: 20 },
|
|
28
|
+
]);
|
|
22
29
|
```
|
|
23
30
|
|
|
24
|
-
</Step
|
|
31
|
+
</Step>
|
|
32
|
+
<Step title="Pass it to the channel sender">
|
|
25
33
|
|
|
26
34
|
```ts
|
|
27
35
|
const mailer = await createMailer({ transport });
|
|
36
|
+
// or createSmsSender / createWhatsAppSender / createPushSender
|
|
28
37
|
```
|
|
29
38
|
|
|
30
|
-
</Step
|
|
39
|
+
</Step>
|
|
40
|
+
</Steps>
|
|
41
|
+
|
|
42
|
+
## Troubleshooting
|
|
43
|
+
|
|
44
|
+
<Accordions>
|
|
45
|
+
<Accordion title="Does this replace the inner provider?">
|
|
46
|
+
No. It delegates sends to the wrapped transports.
|
|
47
|
+
</Accordion>
|
|
48
|
+
</Accordions>
|
|
31
49
|
|
|
32
|
-
|
|
50
|
+
## Next
|
|
33
51
|
|
|
34
|
-
<Cards
|
|
52
|
+
<Cards>
|
|
53
|
+
<Card title="Fallback" href="/docs/decorators/fallback" />
|
|
54
|
+
<Card title="Decorators" href="/docs/decorators" />
|
|
55
|
+
</Cards>
|
|
@@ -54,26 +54,27 @@ const mailer = await createSMTPMailer({
|
|
|
54
54
|
|
|
55
55
|
| Import | Exports | Use |
|
|
56
56
|
| ------ | ------- | --- |
|
|
57
|
-
| `sently` | Shared factories, types, OAuth2, errors, Fallback / WeightedFallback / Cloudflare Email | General email
|
|
57
|
+
| `sently` | Shared factories, types, OAuth2, errors, `toChannelSendResult`, Fallback / WeightedFallback / Cloudflare Email | General email + shared helpers. |
|
|
58
58
|
| `sently/mailer` | `createMailer` | Email with an explicit transport (smallest HTTP path). |
|
|
59
59
|
| `sently/smtp` | `createSMTPMailer` | SMTP relay configuration. |
|
|
60
60
|
| `sently/sms` | `createSmsSender` | SMS sender. |
|
|
61
61
|
| `sently/whatsapp` | `createWhatsAppSender` | WhatsApp sender. |
|
|
62
|
-
| `sently/push` | `createPushSender` | Web Push
|
|
62
|
+
| `sently/push` | `createPushSender` | Push sender (Web Push or FCM). |
|
|
63
|
+
| `sently/channel-result` | `toChannelSendResult` | Shared `{ messageId, provider, accepted }` mapping. |
|
|
63
64
|
|
|
64
65
|
## Supporting entrypoints
|
|
65
66
|
|
|
66
67
|
| Import | Use |
|
|
67
68
|
| ------ | --- |
|
|
68
|
-
| `sently/transports/*` | One provider or
|
|
69
|
-
| `sently/webhooks/<provider>` | One webhook parser (
|
|
69
|
+
| `sently/transports/*` | One provider or decorator transport (retry / fallback work across channels). |
|
|
70
|
+
| `sently/webhooks/<provider>` | One webhook parser (email, SMS, or WhatsApp). |
|
|
70
71
|
| `sently/webhooks` | Convenience barrel of all webhook helpers. |
|
|
71
72
|
| `sently/errors` | Stable sently error classes and codes. |
|
|
72
73
|
| `sently/plugins/template` | Template plugin and simple engine. |
|
|
73
74
|
| `sently/auth/oauth2` | SMTP OAuth2 client. |
|
|
74
75
|
| `sently/pool` | SMTP connection pool. |
|
|
75
76
|
| `sently/dkim` | DKIM signing entrypoint. |
|
|
76
|
-
| `sently/idempotency` | Idempotency transport and memory store. |
|
|
77
|
+
| `sently/idempotency` | Idempotency transport and memory store (email). |
|
|
77
78
|
|
|
78
79
|
## Troubleshooting
|
|
79
80
|
|
|
@@ -5,7 +5,7 @@ icon: Package
|
|
|
5
5
|
source: "package.json"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
sently is an ESM package for applications that send email, SMS, WhatsApp, or
|
|
8
|
+
sently is an ESM package for applications that send email, SMS, WhatsApp, or push (Web Push / FCM).
|
|
9
9
|
Install it in the application that owns your delivery credentials and provider transport configuration.
|
|
10
10
|
|
|
11
11
|
<Callout title="The one rule">Import the narrowest sently entrypoint that contains the sender or transport you use.</Callout>
|
|
@@ -52,7 +52,8 @@ const mailer = await createMailer({
|
|
|
52
52
|
| Email over SMTP | `sently/smtp` |
|
|
53
53
|
| SMS | `sently/sms` |
|
|
54
54
|
| WhatsApp | `sently/whatsapp` |
|
|
55
|
-
| Web Push | `sently/push` |
|
|
55
|
+
| Push (Web Push or FCM) | `sently/push` |
|
|
56
|
+
| Shared send-result mapping | `sently/channel-result` |
|
|
56
57
|
|
|
57
58
|
## Troubleshooting
|
|
58
59
|
|
|
@@ -5,7 +5,7 @@ icon: Compass
|
|
|
5
5
|
source: "README.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
sently sends email, SMS, WhatsApp, and
|
|
8
|
+
sently sends email, SMS, WhatsApp, and push (Web Push or FCM) without making provider SDK calls your application API.
|
|
9
9
|
Choose the channel sender first, then attach the provider transport that performs delivery.
|
|
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>
|