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
package/dist/sms.js.map
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/sms.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @module\n * SMS sender orchestrator — plugins, hooks, and transport.send pipeline.\n *\n * Sently-first: apps call {@link createSmsSender}; providers implement\n * {@link SmsTransport}. Vendor-only extras stay on the concrete transport.\n *\n * @example\n * ```ts\n * import { createSmsSender } from \"sently/sms\";\n * import { TwilioSmsTransport } from \"sently/transports/twilio-sms\";\n *\n * const sms = createSmsSender({\n * transport: new TwilioSmsTransport({ accountSid: \"...\", authToken: \"...\" }),\n * });\n *\n * await sms.send({ to: \"+15551234567\", body: \"Hello\", from: \"+15557654321\" });\n * ```\n */\nimport { invokeHook } from \"./core/hooks.js\";\nimport { runPlugins } from \"./core/plugin.js\";\nimport type {\n SmsHookContext,\n SmsHooks,\n SmsOptions,\n SmsPlugin,\n SmsSendResult,\n SmsTransport,\n} from \"./core/sms-types.js\";\nimport type { VerifyResult } from \"./core/types.js\";\n\n/** Configuration for {@link createSmsSender}. */\nexport interface SmsSenderConfig {\n /** SMS delivery transport. */\n transport: SmsTransport;\n /** Optional plugins run before each send. */\n plugins?: SmsPlugin[];\n /** Optional lifecycle hooks for metrics and tracing. */\n hooks?: SmsHooks;\n}\n\n/** SMS sender returned by {@link createSmsSender}. */\nexport interface SmsSender {\n /** Send an SMS through the configured transport. */\n send(options: SmsOptions): Promise<SmsSendResult>;\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 buildSmsHookContext(options: SmsOptions, transport: SmsTransport): SmsHookContext {\n return {\n ...(options.messageId !== undefined ? { messageId: options.messageId } : {}),\n to: options.to,\n provider: transport.provider ?? \"sms\",\n };\n}\n\n/**\n * Create an SMS sender that wraps an {@link SmsTransport}.\n *\n * Pipeline: plugins → onSend → transport.send → onSuccess / onError.\n */\nexport function createSmsSender(config: SmsSenderConfig): SmsSender {\n const { transport, plugins, hooks } = config;\n\n return {\n async send(options: SmsOptions): Promise<SmsSendResult> {\n const processed = await runPlugins(options, plugins);\n const ctx = buildSmsHookContext(processed, transport);\n\n await invokeHook(hooks?.onSend, ctx);\n\n const start = performance.now();\n\n try {\n const result = await transport.send(processed);\n const successCtx: SmsHookContext = {\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 }\n },\n\n async verify(): Promise<VerifyResult> {\n if (transport.verify) {\n return transport.verify();\n }\n return { ok: true, provider: \"sms\" };\n },\n\n async close(): Promise<void> {\n if (transport.close) {\n await transport.close();\n }\n },\n };\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "iFAmDA,GAAS,CAAmB,CAAC,EAAqB,EAAyC,CACzF,MAAO,IACD,EAAQ,YAAc,OAAY,CAAE,UAAW,EAAQ,SAAU,EAAI,CAAC,EAC1E,GAAI,EAAQ,GACZ,SAAU,EAAU,UAAY,KAClC,EAQK,SAAS,CAAe,CAAC,EAAoC,CAClE,IAAQ,YAAW,UAAS,SAAU,EAEtC,MAAO,MACC,KAAI,CAAC,EAA6C,CACtD,IAAM,EAAY,MAAM,EAAW,EAAS,CAAO,EAC7C,EAAM,EAAoB,EAAW,CAAS,EAEpD,MAAM,EAAW,GAAO,OAAQ,CAAG,EAEnC,IAAM,EAAQ,YAAY,IAAI,EAE9B,GAAI,CACF,IAAM,EAAS,MAAM,EAAU,KAAK,CAAS,EACvC,EAA6B,IAC9B,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,SAIJ,OAAM,EAA0B,CACpC,GAAI,EAAU,OACZ,OAAO,EAAU,OAAO,EAE1B,MAAO,CAAE,GAAI,GAAM,SAAU,KAAM,QAG/B,MAAK,EAAkB,CAC3B,GAAI,EAAU,MACZ,MAAM,EAAU,MAAM,EAG5B",
|
|
8
|
+
"debugId": "9AD22B10712186FC64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/smtp-mailer.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{K as g}from"./chunk-29z7fkzy.js";import{M as k}from"./chunk-5915vbcj.js";import"./chunk-gp4fwrs8.js";import"./chunk-0qxws3kj.js";import"./chunk-93vqxxj2.js";import"./chunk-qtkd5bak.js";import"./chunk-ywq6s10g.js";import"./chunk-tamww15j.js";import{fa as v}from"./chunk-6npp3x3c.js";async function F(b){let q={...b.secure!==void 0?{secure:b.secure}:{},...b.connectionTimeout!==void 0?{connectionTimeout:b.connectionTimeout}:{},...b.tls!==void 0?{tls:b.tls}:{}};if(b.pool){let{SMTPPool:z}=await import("./pool/pool.js");return new k(new z(b,{createAdapter:async()=>b.adapter??await g(q)}),b.plugins,b.hooks)}let w=b.adapter??await g(q),{SMTPTransport:x}=await import("./transports/smtp.js");return new k(new x({...b,adapter:w}),b.plugins,b.hooks)}export{F as createSMTPMailer};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=DE7394EDE2CB853864756E2164756E21
|
package/dist/smtp-mailer.js.map
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * @module\n * SMTP-capable mailer factory — host/port config, pooling, and runtime adapters.\n *\n * Import from `sently/smtp` or use {@link createSMTPMailer} from the main package.\n * For HTTP transports, use `sently/mailer` instead (~10 KB smaller in app bundles).\n *\n * @example\n * ```ts\n * import { createSMTPMailer } from \"sently/smtp\";\n *\n * const mailer = await createSMTPMailer({\n * host: \"smtp.example.com\",\n * port: 587,\n * auth: { user: \"you@example.com\", pass: \"secret\" },\n * });\n * ```\n */\nimport type { Mailer, SMTPMailerOptions } from \"./core/types.js\";\nimport { createDefaultAdapter } from \"./detect.js\";\nimport { MailerImpl } from \"./mailer.js\";\n\n/**\n * Create a mailer from SMTP relay options (host, port, auth, pool, adapter).\n */\nexport async function createSMTPMailer(options: SMTPMailerOptions): Promise<Mailer> {\n const adapterOptions = {\n ...(options.secure !== undefined ? { secure: options.secure } : {}),\n ...(options.connectionTimeout !== undefined\n ? { connectionTimeout: options.connectionTimeout }\n : {}),\n ...(options.tls !== undefined ? { tls: options.tls } : {}),\n };\n\n if (options.pool) {\n const { SMTPPool } = await import(\"./pool/pool.js\");\n return new MailerImpl(\n new SMTPPool(options, {\n createAdapter: async () => options.adapter ?? (await createDefaultAdapter(adapterOptions)),\n }),\n options.plugins,\n options.hooks,\n );\n }\n\n const adapter = options.adapter ?? (await createDefaultAdapter(adapterOptions));\n const { SMTPTransport } = await import(\"./transports/smtp.js\");\n\n return new MailerImpl(new SMTPTransport({ ...options, adapter }), options.plugins, options.hooks);\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "iVAyBA,SAAsB,LAAgB,LAAC,JAA6C,LAClF,FAAM,JAAiB,FACjB,EAAQ,SAAW,OAAY,CAAE,OAAQ,EAAQ,MAAO,EAAI,CAAC,KAC7D,EAAQ,oBAAsB,OAC9B,CAAE,kBAAmB,EAAQ,iBAAkB,EAC/C,CAAC,KACD,EAAQ,MAAQ,OAAY,CAAE,IAAK,EAAQ,GAAI,EAAI,CAAC,CAC1D,EAEA,GAAI,EAAQ,KAAM,CAChB,IAAQ,YAAa,KAAa,0BAClC,OAAO,IAAI,EACT,IAAI,EAAS,EAAS,CACpB,cAAe,SAAY,EAAQ,SAAY,MAAM,EAAqB,CAAc,CAC1F,CAAC,EACD,EAAQ,QACR,EAAQ,KACV,EAGF,IAAM,EAAU,EAAQ,SAAY,MAAM,EAAqB,CAAc,GACrE,iBAAkB,KAAa,gCAEvC,OAAO,IAAI,EAAW,IAAI,EAAc,IAAK,EAAS,SAAQ,CAAC,EAAG,EAAQ,QAAS,EAAQ,KAAK",
|
|
8
|
+
"debugId": "DE7394EDE2CB853864756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -10,6 +10,7 @@ export declare class BrevoError extends SentlyError {
|
|
|
10
10
|
* Brevo HTTP API transport.
|
|
11
11
|
*/
|
|
12
12
|
export declare class BrevoTransport implements Transport {
|
|
13
|
+
readonly provider = "brevo";
|
|
13
14
|
/** Brevo API key used for Authorization. */
|
|
14
15
|
private readonly apiKey;
|
|
15
16
|
/** Creates a Brevo transport with the given API key. */
|
package/dist/transports/brevo.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{I as R}from"../chunk-skqhj0rm.js";import{Q as G,S as M}from"../chunk-93vqxxj2.js";import{Y as L}from"../chunk-ywq6s10g.js";import{ca as N,da as Q}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class U extends N{statusCode;constructor(q,w,D){super(q,Q(w),{statusCode:w,provider:"brevo"});this.statusCode=w;this.name="BrevoError",this.code=D}}function J(q){return G(q).map((w)=>({email:w.address,...w.name?{name:w.name}:{}}))}class X{provider="brevo";apiKey;constructor(q){this.apiKey=q.apiKey}async send(q){let w=await R(q.attachments),D=G(q.from)[0],W={sender:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},to:J(q.to),subject:q.subject,...q.cc?{cc:J(q.cc)}:{},...q.bcc?{bcc:J(q.bcc)}:{},...q.replyTo?{replyTo:(()=>{let z=G(q.replyTo)[0];return z?{email:z.address,...z.name?{name:z.name}:{}}:void 0})()}:{},...q.html?{htmlContent:q.html}:{},...q.text?{textContent:q.text}:{},...w.length>0?{attachment:w.map((z)=>({name:z.filename,content:z.content instanceof Uint8Array?L(z.content).replace(/\r\n/g,""):z.content}))}:{}},H=await fetch("https://api.brevo.com/v3/smtp/email",{method:"POST",headers:{"api-key":this.apiKey,"Content-Type":"application/json"},body:JSON.stringify(W)}),F=await H.json();if(!H.ok)throw new U(F.message??"Brevo API error",H.status,F.code??"");let K=M(q.to);return{messageId:F.messageId??"",accepted:K,rejected:[],response:F.messageId??"sent",envelope:{from:D?.address??"",to:K}}}async verify(){try{let q=await fetch("https://api.brevo.com/v3/account",{headers:{"api-key":this.apiKey}}),w=await q.json();if(!q.ok)return{ok:!1,provider:"brevo",message:w.message??`HTTP ${q.status}`};return{ok:!0,provider:"brevo",...w.companyName?{message:w.companyName}:{}}}catch(q){return{ok:!1,provider:"brevo",message:q instanceof Error?q.message:String(q)}}}}export{X as BrevoTransport,U as BrevoError};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=E4625E2F9F65354364756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/transports/brevo.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * @module\n * Brevo (formerly Sendinblue) HTTP transport for sently.\n *\n * @example\n * ```ts\n * import { BrevoTransport } from \"sently/transports/brevo\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new BrevoTransport({ apiKey: \"xkeysib-...\" }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type {\n BrevoConfig,\n MailOptions,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the Brevo API returns a non-success response. */\nexport class BrevoError extends SentlyError {\n /** Creates a Brevo API error with status code and error code. */\n constructor(\n message: string,\n public readonly statusCode: number,\n apiCode: string,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"brevo\",\n });\n this.name = \"BrevoError\";\n this.code = apiCode;\n }\n}\n\nfunction toAddressObjects(input: MailOptions[\"to\"]): Array<{ email: string; name?: string }> {\n return parseAddresses(input).map((addr) => ({\n email: addr.address,\n ...(addr.name ? { name: addr.name } : {}),\n }));\n}\n\n/**\n * Brevo HTTP API transport.\n */\nexport class BrevoTransport implements Transport {\n /** Brevo API key used for Authorization. */\n private readonly apiKey: string;\n\n /** Creates a Brevo transport with the given API key. */\n constructor(config: BrevoConfig) {\n this.apiKey = config.apiKey;\n }\n\n /** Sends an email via the Brevo HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const body: Record<string, unknown> = {\n sender: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n to: toAddressObjects(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: toAddressObjects(options.cc) } : {}),\n ...(options.bcc ? { bcc: toAddressObjects(options.bcc) } : {}),\n ...(options.replyTo\n ? {\n replyTo: (() => {\n const reply = parseAddresses(options.replyTo as MailOptions[\"to\"])[0];\n return reply\n ? { email: reply.address, ...(reply.name ? { name: reply.name } : {}) }\n : undefined;\n })(),\n }\n : {}),\n ...(options.html ? { htmlContent: options.html } : {}),\n ...(options.text ? { textContent: options.text } : {}),\n ...(attachments.length > 0\n ? {\n attachment: attachments.map((att) => ({\n name: att.filename,\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.brevo.com/v3/smtp/email\", {\n method: \"POST\",\n headers: {\n \"api-key\": this.apiKey,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n messageId?: string;\n message?: string;\n code?: string;\n };\n\n if (!response.ok) {\n throw new BrevoError(\n payload.message ?? \"Brevo API error\",\n response.status,\n payload.code ?? \"\",\n );\n }\n\n const toEmails = extractEmails(options.to);\n return {\n messageId: payload.messageId ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: payload.messageId ?? \"sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the Brevo API key by fetching account info. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.brevo.com/v3/account\", {\n headers: {\n \"api-key\": this.apiKey,\n },\n });\n\n const payload = (await response.json()) as { companyName?: string; message?: string };\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"brevo\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return {\n ok: true,\n provider: \"brevo\",\n ...(payload.companyName ? { message: payload.companyName } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n provider: \"brevo\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
|
|
5
|
+
"/**\n * @module\n * Brevo (formerly Sendinblue) HTTP transport for sently.\n *\n * @example\n * ```ts\n * import { BrevoTransport } from \"sently/transports/brevo\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new BrevoTransport({ apiKey: \"xkeysib-...\" }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type {\n BrevoConfig,\n MailOptions,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the Brevo API returns a non-success response. */\nexport class BrevoError extends SentlyError {\n /** Creates a Brevo API error with status code and error code. */\n constructor(\n message: string,\n public readonly statusCode: number,\n apiCode: string,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"brevo\",\n });\n this.name = \"BrevoError\";\n this.code = apiCode;\n }\n}\n\nfunction toAddressObjects(input: MailOptions[\"to\"]): Array<{ email: string; name?: string }> {\n return parseAddresses(input).map((addr) => ({\n email: addr.address,\n ...(addr.name ? { name: addr.name } : {}),\n }));\n}\n\n/**\n * Brevo HTTP API transport.\n */\nexport class BrevoTransport implements Transport {\n readonly provider = \"brevo\";\n\n /** Brevo API key used for Authorization. */\n private readonly apiKey: string;\n\n /** Creates a Brevo transport with the given API key. */\n constructor(config: BrevoConfig) {\n this.apiKey = config.apiKey;\n }\n\n /** Sends an email via the Brevo HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const body: Record<string, unknown> = {\n sender: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n to: toAddressObjects(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: toAddressObjects(options.cc) } : {}),\n ...(options.bcc ? { bcc: toAddressObjects(options.bcc) } : {}),\n ...(options.replyTo\n ? {\n replyTo: (() => {\n const reply = parseAddresses(options.replyTo as MailOptions[\"to\"])[0];\n return reply\n ? { email: reply.address, ...(reply.name ? { name: reply.name } : {}) }\n : undefined;\n })(),\n }\n : {}),\n ...(options.html ? { htmlContent: options.html } : {}),\n ...(options.text ? { textContent: options.text } : {}),\n ...(attachments.length > 0\n ? {\n attachment: attachments.map((att) => ({\n name: att.filename,\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.brevo.com/v3/smtp/email\", {\n method: \"POST\",\n headers: {\n \"api-key\": this.apiKey,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n messageId?: string;\n message?: string;\n code?: string;\n };\n\n if (!response.ok) {\n throw new BrevoError(\n payload.message ?? \"Brevo API error\",\n response.status,\n payload.code ?? \"\",\n );\n }\n\n const toEmails = extractEmails(options.to);\n return {\n messageId: payload.messageId ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: payload.messageId ?? \"sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the Brevo API key by fetching account info. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.brevo.com/v3/account\", {\n headers: {\n \"api-key\": this.apiKey,\n },\n });\n\n const payload = (await response.json()) as { companyName?: string; message?: string };\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"brevo\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return {\n ok: true,\n provider: \"brevo\",\n ...(payload.companyName ? { message: payload.companyName } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n provider: \"brevo\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "qOA2BO,CAAM,KAAmB,JAAY,JAIxB,WAFlB,WAAW,CACT,EACgB,EAChB,EACA,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,OACZ,CAAC,EANe,kBAOhB,KAAK,KAAO,aACZ,KAAK,KAAO,EAEhB,CAEA,SAAS,CAAgB,CAAC,EAAmE,CAC3F,OAAO,EAAe,CAAK,EAAE,IAAI,CAAC,KAAU,CAC1C,MAAO,EAAK,WACR,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CACzC,EAAE,EAMG,MAAM,CAAoC,CACtC,SAAW,QAGH,OAGjB,WAAW,CAAC,EAAqB,CAC/B,KAAK,OAAS,EAAO,YAIjB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAgC,CACpC,OAAQ,EACJ,CAAE,MAAO,EAAK,WAAa,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CAAG,EACjE,CAAE,MAAO,EAAG,EAChB,GAAI,EAAiB,EAAQ,EAAE,EAC/B,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAiB,EAAQ,EAAE,CAAE,EAAI,CAAC,KACrD,EAAQ,IAAM,CAAE,IAAK,EAAiB,EAAQ,GAAG,CAAE,EAAI,CAAC,KACxD,EAAQ,QACR,CACE,SAAU,IAAM,CACd,IAAM,EAAQ,EAAe,EAAQ,OAA4B,EAAE,GACnE,OAAO,EACH,CAAE,MAAO,EAAM,WAAa,EAAM,KAAO,CAAE,KAAM,EAAM,IAAK,EAAI,CAAC,CAAG,EACpE,SACH,CACL,EACA,CAAC,KACD,EAAQ,KAAO,CAAE,YAAa,EAAQ,IAAK,EAAI,CAAC,KAChD,EAAQ,KAAO,CAAE,YAAa,EAAQ,IAAK,EAAI,CAAC,KAChD,EAAY,OAAS,EACrB,CACE,WAAY,EAAY,IAAI,CAAC,KAAS,CACpC,KAAM,EAAI,SACV,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,OACZ,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,sCAAuC,CAClE,OAAQ,OACR,QAAS,CACP,UAAW,KAAK,OAChB,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAMrC,GAAI,CAAC,EAAS,GACZ,MAAM,IAAI,EACR,EAAQ,SAAW,kBACnB,EAAS,OACT,EAAQ,MAAQ,EAClB,EAGF,IAAM,EAAW,EAAc,EAAQ,EAAE,EACzC,MAAO,CACL,UAAW,EAAQ,WAAa,GAChC,SAAU,EACV,SAAU,CAAC,EACX,SAAU,EAAQ,WAAa,OAC/B,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAII,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,mCAAoC,CAC/D,QAAS,CACP,UAAW,KAAK,MAClB,CACF,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAErC,GAAI,CAAC,EAAS,GACZ,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,EAAQ,SAAW,QAAQ,EAAS,QAC/C,EAGF,MAAO,CACL,GAAI,GACJ,SAAU,WACN,EAAQ,YAAc,CAAE,QAAS,EAAQ,WAAY,EAAI,CAAC,CAChE,EACA,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,GAGN",
|
|
8
|
+
"debugId": "E4625E2F9F65354364756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { SentlyError } from "../core/errors.js";
|
|
2
|
+
import type { MailOptions, SendResult, Transport, VerifyResult } from "../core/types.js";
|
|
3
|
+
/** Message shape accepted by the Cloudflare Workers `send_email` binding. */
|
|
4
|
+
export interface CloudflareEmailMessage {
|
|
5
|
+
/** Envelope sender. */
|
|
6
|
+
from: string;
|
|
7
|
+
/** Primary recipient. */
|
|
8
|
+
to: string;
|
|
9
|
+
/** Message subject. */
|
|
10
|
+
subject?: string;
|
|
11
|
+
/** Reply-To address. */
|
|
12
|
+
reply_to?: string;
|
|
13
|
+
/** Carbon-copy recipients. */
|
|
14
|
+
cc?: string[];
|
|
15
|
+
/** Blind carbon-copy recipients. */
|
|
16
|
+
bcc?: string[];
|
|
17
|
+
/** Additional MIME headers. */
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
/** Message body parts. */
|
|
20
|
+
content: Array<{
|
|
21
|
+
type: string;
|
|
22
|
+
value: string;
|
|
23
|
+
}>;
|
|
24
|
+
/** Optional attachments (base64 content). */
|
|
25
|
+
attachments?: Array<{
|
|
26
|
+
disposition: "attachment" | "inline";
|
|
27
|
+
filename: string;
|
|
28
|
+
type: string;
|
|
29
|
+
content: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
/** Callable Workers `send_email` binding. */
|
|
33
|
+
export type CloudflareSendEmailFn = (message: CloudflareEmailMessage) => Promise<void>;
|
|
34
|
+
/** Cloudflare Workers Email binding configuration. */
|
|
35
|
+
export interface CloudflareEmailConfig {
|
|
36
|
+
/** The Workers `send_email` binding (e.g. `env.SEND_EMAIL`). */
|
|
37
|
+
sendEmail: CloudflareSendEmailFn;
|
|
38
|
+
}
|
|
39
|
+
/** Error thrown when the Cloudflare Email binding rejects a send. */
|
|
40
|
+
export declare class CloudflareEmailError extends SentlyError {
|
|
41
|
+
/** Creates a Cloudflare Email binding error. */
|
|
42
|
+
constructor(message: string, cause?: unknown);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Cloudflare Workers Email binding transport.
|
|
46
|
+
*/
|
|
47
|
+
export declare class CloudflareEmailTransport implements Transport {
|
|
48
|
+
readonly provider = "cloudflare-email";
|
|
49
|
+
private readonly sendEmail;
|
|
50
|
+
/** Creates a transport backed by the Workers `send_email` binding. */
|
|
51
|
+
constructor(config: CloudflareEmailConfig);
|
|
52
|
+
/** Sends an email via the Workers `send_email` binding. */
|
|
53
|
+
send(options: MailOptions): Promise<SendResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Confirms the binding is configured — no network validation is available.
|
|
56
|
+
*/
|
|
57
|
+
verify(): Promise<VerifyResult>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{I as O}from"../chunk-skqhj0rm.js";import{Q as M,S as G}from"../chunk-93vqxxj2.js";import{Y as L}from"../chunk-ywq6s10g.js";import{ca as N}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class I extends N{constructor(q,D){super(q,"PROVIDER_ERROR",{provider:"cloudflare-email",cause:D});this.name="CloudflareEmailError"}}class Q{provider="cloudflare-email";sendEmail;constructor(q){this.sendEmail=q.sendEmail}async send(q){let D=M(q.from)[0],H=G(q.to),J=H[0];if(!D||!J)throw new I("Cloudflare Email requires from and to addresses");let F=[];if(q.text!==void 0)F.push({type:"text/plain",value:q.text});if(q.html!==void 0)F.push({type:"text/html",value:q.html});if(F.length===0)F.push({type:"text/plain",value:""});let K=await O(q.attachments),P={from:D.address,to:J,subject:q.subject,content:F,...q.replyTo?{reply_to:G(q.replyTo)[0]}:{},...q.cc?{cc:G(q.cc)}:{},...q.bcc?{bcc:G(q.bcc)}:{},...q.headers?{headers:q.headers}:{},...K.length>0?{attachments:K.map((z)=>({disposition:z.inline?"inline":"attachment",filename:z.filename,type:z.contentType??"application/octet-stream",content:z.content instanceof Uint8Array?L(z.content).replace(/\r\n/g,""):String(z.content??"")}))}:{}};try{await this.sendEmail(P)}catch(z){throw new I(z instanceof Error?z.message:"Cloudflare Email send failed",z)}return{messageId:q.messageId??"",accepted:H,rejected:[],response:"sent via Cloudflare Email binding",envelope:{from:D.address,to:H}}}async verify(){return{ok:!0,provider:"cloudflare-email",message:"send_email binding configured (not validated at runtime)"}}}export{Q as CloudflareEmailTransport,I as CloudflareEmailError};
|
|
2
|
+
|
|
3
|
+
//# debugId=12AACAC7726534CE64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/transports/cloudflare-email.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @module\n * Cloudflare Workers Email transport — wraps the `send_email` binding\n * configured in `wrangler.toml`, not a fetch-based HTTP API.\n *\n * @example\n * ```ts\n * // wrangler.toml: [[send_email]]\n * import { CloudflareEmailTransport } from \"sently/transports/cloudflare-email\";\n * import { createMailer } from \"sently/mailer\";\n *\n * export default {\n * async fetch(request, env) {\n * const mailer = await createMailer({\n * transport: new CloudflareEmailTransport({ sendEmail: env.SEND_EMAIL }),\n * });\n * await mailer.send({ from: \"...\", to: \"...\", subject: \"...\", text: \"...\" });\n * },\n * };\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Message shape accepted by the Cloudflare Workers `send_email` binding. */\nexport interface CloudflareEmailMessage {\n /** Envelope sender. */\n from: string;\n /** Primary recipient. */\n to: string;\n /** Message subject. */\n subject?: string;\n /** Reply-To address. */\n reply_to?: string;\n /** Carbon-copy recipients. */\n cc?: string[];\n /** Blind carbon-copy recipients. */\n bcc?: string[];\n /** Additional MIME headers. */\n headers?: Record<string, string>;\n /** Message body parts. */\n content: Array<{ type: string; value: string }>;\n /** Optional attachments (base64 content). */\n attachments?: Array<{\n disposition: \"attachment\" | \"inline\";\n filename: string;\n type: string;\n content: string;\n }>;\n}\n\n/** Callable Workers `send_email` binding. */\nexport type CloudflareSendEmailFn = (message: CloudflareEmailMessage) => Promise<void>;\n\n/** Cloudflare Workers Email binding configuration. */\nexport interface CloudflareEmailConfig {\n /** The Workers `send_email` binding (e.g. `env.SEND_EMAIL`). */\n sendEmail: CloudflareSendEmailFn;\n}\n\n/** Error thrown when the Cloudflare Email binding rejects a send. */\nexport class CloudflareEmailError extends SentlyError {\n /** Creates a Cloudflare Email binding error. */\n constructor(message: string, cause?: unknown) {\n super(message, \"PROVIDER_ERROR\", { provider: \"cloudflare-email\", cause });\n this.name = \"CloudflareEmailError\";\n }\n}\n\n/**\n * Cloudflare Workers Email binding transport.\n */\nexport class CloudflareEmailTransport implements Transport {\n readonly provider = \"cloudflare-email\";\n\n private readonly sendEmail: CloudflareSendEmailFn;\n\n /** Creates a transport backed by the Workers `send_email` binding. */\n constructor(config: CloudflareEmailConfig) {\n this.sendEmail = config.sendEmail;\n }\n\n /** Sends an email via the Workers `send_email` binding. */\n async send(options: MailOptions): Promise<SendResult> {\n const from = parseAddresses(options.from)[0];\n const toEmails = extractEmails(options.to);\n const primaryTo = toEmails[0];\n\n if (!from || !primaryTo) {\n throw new CloudflareEmailError(\"Cloudflare Email requires from and to addresses\");\n }\n\n const content: CloudflareEmailMessage[\"content\"] = [];\n if (options.text !== undefined) {\n content.push({ type: \"text/plain\", value: options.text });\n }\n if (options.html !== undefined) {\n content.push({ type: \"text/html\", value: options.html });\n }\n if (content.length === 0) {\n content.push({ type: \"text/plain\", value: \"\" });\n }\n\n const attachments = await resolveAttachments(options.attachments);\n const message: CloudflareEmailMessage = {\n from: from.address,\n to: primaryTo,\n subject: options.subject,\n content,\n ...(options.replyTo ? { reply_to: extractEmails(options.replyTo)[0] } : {}),\n ...(options.cc ? { cc: extractEmails(options.cc) } : {}),\n ...(options.bcc ? { bcc: extractEmails(options.bcc) } : {}),\n ...(options.headers ? { headers: options.headers } : {}),\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n disposition: att.inline ? (\"inline\" as const) : (\"attachment\" as const),\n filename: att.filename,\n type: att.contentType ?? \"application/octet-stream\",\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : String(att.content ?? \"\"),\n })),\n }\n : {}),\n };\n\n try {\n await this.sendEmail(message);\n } catch (err) {\n throw new CloudflareEmailError(\n err instanceof Error ? err.message : \"Cloudflare Email send failed\",\n err,\n );\n }\n\n return {\n messageId: options.messageId ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: \"sent via Cloudflare Email binding\",\n envelope: {\n from: from.address,\n to: toEmails,\n },\n };\n }\n\n /**\n * Confirms the binding is configured — no network validation is available.\n */\n async verify(): Promise<VerifyResult> {\n return {\n ok: true,\n provider: \"cloudflare-email\",\n message: \"send_email binding configured (not validated at runtime)\",\n };\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "6NAgEO,CAAM,KAA6B,JAAY,JAEpD,WAAW,CAAC,EAAiB,EAAiB,CAC5C,MAAM,EAAS,iBAAkB,CAAE,SAAU,mBAAoB,OAAM,CAAC,EACxE,KAAK,KAAO,uBAEhB,CAKO,MAAM,CAA8C,CAChD,SAAW,mBAEH,UAGjB,WAAW,CAAC,EAA+B,CACzC,KAAK,UAAY,EAAO,eAIpB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAO,EAAe,EAAQ,IAAI,EAAE,GACpC,EAAW,EAAc,EAAQ,EAAE,EACnC,EAAY,EAAS,GAE3B,GAAI,CAAC,GAAQ,CAAC,EACZ,MAAM,IAAI,EAAqB,iDAAiD,EAGlF,IAAM,EAA6C,CAAC,EACpD,GAAI,EAAQ,OAAS,OACnB,EAAQ,KAAK,CAAE,KAAM,aAAc,MAAO,EAAQ,IAAK,CAAC,EAE1D,GAAI,EAAQ,OAAS,OACnB,EAAQ,KAAK,CAAE,KAAM,YAAa,MAAO,EAAQ,IAAK,CAAC,EAEzD,GAAI,EAAQ,SAAW,EACrB,EAAQ,KAAK,CAAE,KAAM,aAAc,MAAO,EAAG,CAAC,EAGhD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAkC,CACtC,KAAM,EAAK,QACX,GAAI,EACJ,QAAS,EAAQ,QACjB,aACI,EAAQ,QAAU,CAAE,SAAU,EAAc,EAAQ,OAAO,EAAE,EAAG,EAAI,CAAC,KACrE,EAAQ,GAAK,CAAE,GAAI,EAAc,EAAQ,EAAE,CAAE,EAAI,CAAC,KAClD,EAAQ,IAAM,CAAE,IAAK,EAAc,EAAQ,GAAG,CAAE,EAAI,CAAC,KACrD,EAAQ,QAAU,CAAE,QAAS,EAAQ,OAAQ,EAAI,CAAC,KAClD,EAAY,OAAS,EACrB,CACE,YAAa,EAAY,IAAI,CAAC,KAAS,CACrC,YAAa,EAAI,OAAU,SAAsB,aACjD,SAAU,EAAI,SACd,KAAM,EAAI,aAAe,2BACzB,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,OAAO,EAAI,SAAW,EAAE,CAChC,EAAE,CACJ,EACA,CAAC,CACP,EAEA,GAAI,CACF,MAAM,KAAK,UAAU,CAAO,EAC5B,MAAO,EAAK,CACZ,MAAM,IAAI,EACR,aAAe,MAAQ,EAAI,QAAU,+BACrC,CACF,EAGF,MAAO,CACL,UAAW,EAAQ,WAAa,GAChC,SAAU,EACV,SAAU,CAAC,EACX,SAAU,oCACV,SAAU,CACR,KAAM,EAAK,QACX,GAAI,CACN,CACF,OAMI,OAAM,EAA0B,CACpC,MAAO,CACL,GAAI,GACJ,SAAU,mBACV,QAAS,0DACX,EAEJ",
|
|
8
|
+
"debugId": "12AACAC7726534CE64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { getProviderLabel } from "../core/provider-label.js";
|
|
2
|
+
import type { MailOptions, SendResult, Transport, VerifyResult } from "../core/types.js";
|
|
3
|
+
/** One failed provider attempt collected during failover. */
|
|
4
|
+
export interface FallbackAttempt {
|
|
5
|
+
/** Provider label (e.g. `"resend"`, `"ses"`). */
|
|
6
|
+
provider: string;
|
|
7
|
+
/** Error thrown by that transport. */
|
|
8
|
+
error: unknown;
|
|
9
|
+
}
|
|
10
|
+
/** Per-provider result from {@link FallbackTransport.verifyAll}. */
|
|
11
|
+
export interface FallbackProviderVerifyResult {
|
|
12
|
+
/** Provider label. */
|
|
13
|
+
provider: string;
|
|
14
|
+
/** Whether this provider verified successfully. */
|
|
15
|
+
ok: boolean;
|
|
16
|
+
/** Human-readable status from the provider verify call. */
|
|
17
|
+
message?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Result of verifying every transport in a fallback chain. */
|
|
20
|
+
export interface FallbackVerifyAllResult {
|
|
21
|
+
/** True when at least one provider in the chain verified successfully. */
|
|
22
|
+
ok: boolean;
|
|
23
|
+
/** Per-provider verify results in chain order. */
|
|
24
|
+
providers: FallbackProviderVerifyResult[];
|
|
25
|
+
}
|
|
26
|
+
/** Options for {@link FallbackTransport} failover behavior. */
|
|
27
|
+
export interface FallbackOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Decide whether a given error should trigger failover to the next
|
|
30
|
+
* transport. Default: fail over on everything EXCEPT permanent client
|
|
31
|
+
* errors (HTTP 400/401/403, SMTP permanent 5xx auth failures), since
|
|
32
|
+
* those will fail identically on every provider.
|
|
33
|
+
*/
|
|
34
|
+
shouldFallback?: (error: unknown) => boolean;
|
|
35
|
+
/** Optional callback fired before advancing to the next transport. */
|
|
36
|
+
onFallback?: (failedIndex: number, error: unknown) => void;
|
|
37
|
+
/**
|
|
38
|
+
* After a failed send, skip this provider until the cooldown expires (ms).
|
|
39
|
+
* Prevents hammering a failing provider during an outage.
|
|
40
|
+
*/
|
|
41
|
+
cooldownMs?: number;
|
|
42
|
+
/** Injectable clock for cooldown and testing. Default: `Date.now`. */
|
|
43
|
+
now?: () => number;
|
|
44
|
+
/**
|
|
45
|
+
* Shared cooldown state across fallback instances (e.g. {@link WeightedFallbackTransport}).
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
cooldownState?: Map<string, number>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Thrown when every transport in the chain fails.
|
|
52
|
+
* {@link FallbackAttempt} entries record which provider failed and why.
|
|
53
|
+
*/
|
|
54
|
+
export declare class FallbackError extends Error {
|
|
55
|
+
/** All failed attempts in order — which provider failed and why. */
|
|
56
|
+
readonly attempts: FallbackAttempt[];
|
|
57
|
+
/** Creates an error summarizing every failed transport attempt. */
|
|
58
|
+
constructor(attempts: FallbackAttempt[]);
|
|
59
|
+
}
|
|
60
|
+
/** @deprecated Use {@link getProviderLabel} from `sently/core` patterns. */
|
|
61
|
+
export declare const inferProviderLabel: typeof getProviderLabel;
|
|
62
|
+
type MailerOnFallback = (failedProvider: string, nextProvider: string, error: unknown) => void | Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Decorator transport that fails over through an ordered list of transports.
|
|
65
|
+
*/
|
|
66
|
+
export declare class FallbackTransport implements Transport {
|
|
67
|
+
private readonly transports;
|
|
68
|
+
readonly provider = "fallback";
|
|
69
|
+
private readonly shouldFallback;
|
|
70
|
+
private readonly onFallback?;
|
|
71
|
+
private readonly cooldownMs;
|
|
72
|
+
private readonly now;
|
|
73
|
+
private readonly unhealthyUntil;
|
|
74
|
+
private mailerOnFallback;
|
|
75
|
+
/**
|
|
76
|
+
* Creates a failover transport over an ordered list of backends.
|
|
77
|
+
* @throws {Error} When the transports array is empty.
|
|
78
|
+
*/
|
|
79
|
+
constructor(transports: Transport[], options?: FallbackOptions);
|
|
80
|
+
/**
|
|
81
|
+
* Register a per-send onFallback callback from mailer lifecycle hooks.
|
|
82
|
+
* @internal Used by {@link MailerImpl}; cleared after each send.
|
|
83
|
+
*/
|
|
84
|
+
setMailerOnFallback(callback: MailerOnFallback | undefined): void;
|
|
85
|
+
private isCoolingDown;
|
|
86
|
+
private markUnhealthy;
|
|
87
|
+
/** Sends through transports in order until one succeeds. */
|
|
88
|
+
send(message: MailOptions): Promise<SendResult>;
|
|
89
|
+
private findNextTryIndex;
|
|
90
|
+
/**
|
|
91
|
+
* Returns the result of the first transport whose verify() returns `ok: true`.
|
|
92
|
+
* Use {@link verifyAll} for full-chain visibility.
|
|
93
|
+
*/
|
|
94
|
+
verify(): Promise<VerifyResult>;
|
|
95
|
+
/** Verifies every transport in the chain and returns per-provider results. */
|
|
96
|
+
verifyAll(): Promise<FallbackVerifyAllResult>;
|
|
97
|
+
/** Calls close() on every transport; one failure does not block the others. */
|
|
98
|
+
close(): Promise<void>;
|
|
99
|
+
}
|
|
100
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { SentlyError } from "../core/errors.js";
|
|
2
|
+
import type { MailOptions, SendResult, Transport, VerifyResult } from "../core/types.js";
|
|
3
|
+
/** Loops HTTP API configuration. */
|
|
4
|
+
export interface LoopsConfig {
|
|
5
|
+
/** Loops API key for Bearer authentication. */
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/** Default transactional template ID when not set per message via header. */
|
|
8
|
+
defaultTransactionalId?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Error thrown when the Loops API returns a non-success response. */
|
|
11
|
+
export declare class LoopsError extends SentlyError {
|
|
12
|
+
readonly statusCode: number;
|
|
13
|
+
readonly apiError: unknown;
|
|
14
|
+
/** Creates a Loops API error with status code. */
|
|
15
|
+
constructor(message: string, statusCode: number, apiError: unknown);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Loops transactional HTTP API transport.
|
|
19
|
+
*/
|
|
20
|
+
export declare class LoopsTransport implements Transport {
|
|
21
|
+
readonly provider = "loops";
|
|
22
|
+
private readonly apiKey;
|
|
23
|
+
private readonly defaultTransactionalId?;
|
|
24
|
+
/** Creates a Loops transport with the given API key. */
|
|
25
|
+
constructor(config: LoopsConfig);
|
|
26
|
+
/** Sends a transactional email via the Loops HTTP API. */
|
|
27
|
+
send(options: MailOptions): Promise<SendResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Returns success without a network call — Loops has no simple verify endpoint.
|
|
30
|
+
*/
|
|
31
|
+
verify(): Promise<VerifyResult>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{S as K}from"../chunk-93vqxxj2.js";import"../chunk-ywq6s10g.js";import{ca as M,da as P}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class G extends M{statusCode;apiError;constructor(q,B,z){super(q,P(B),{statusCode:B,provider:"loops",cause:z});this.statusCode=B;this.apiError=z;this.name="LoopsError"}}var U="x-loops-transactional-id";class V{provider="loops";apiKey;defaultTransactionalId;constructor(q){if(this.apiKey=q.apiKey,q.defaultTransactionalId!==void 0)this.defaultTransactionalId=q.defaultTransactionalId}async send(q){let B=q.headers?.[U]??this.defaultTransactionalId;if(!B)throw new G("Loops requires a transactional template ID. Set options.headers['x-loops-transactional-id'] or LoopsConfig.defaultTransactionalId. Loops cannot send raw subject/html/text.",400,{code:"MISSING_TRANSACTIONAL_ID"});let z=K(q.to)[0];if(!z)throw new G("Loops requires at least one recipient in options.to",400,{code:"MISSING_RECIPIENT"});let Q={transactionalId:B,email:z,dataVariables:q.data??{}},J=await fetch("https://app.loops.so/api/v1/transactional",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(Q)}),F=await J.json();if(!J.ok||F.success===!1)throw new G(F.message??"Loops API error",J.status,F);return{messageId:F.id??"",accepted:[z],rejected:[],response:F.id??"sent",envelope:{from:K(q.from)[0]??"",to:[z]}}}async verify(){return{ok:!0,provider:"loops",message:"key not validated (no verify endpoint)"}}}export{V as LoopsTransport,G as LoopsError};
|
|
2
|
+
|
|
3
|
+
//# debugId=155A09B7C69A1BDB64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/transports/loops.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @module\n * Loops transactional HTTP transport for sently.\n *\n * Loops is template-first. `subject`, `html`, and `text` are ignored — provide a\n * transactional template ID and data variables instead.\n *\n * Set the template ID per message via the `x-loops-transactional-id` header, or\n * configure a default with `defaultTransactionalId` on the transport constructor.\n * Data variables are read from `options.data`.\n *\n * @example\n * ```ts\n * import { LoopsTransport } from \"sently/transports/loops\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new LoopsTransport({ apiKey: process.env.LOOPS_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"noreply@example.com\",\n * to: \"user@example.com\",\n * subject: \"ignored by Loops\",\n * headers: { \"x-loops-transactional-id\": \"clxxxxxxxx\" },\n * data: { firstName: \"Ada\" },\n * });\n * ```\n */\nimport { extractEmails } from \"../core/address.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\n\n/** Loops HTTP API configuration. */\nexport interface LoopsConfig {\n /** Loops API key for Bearer authentication. */\n apiKey: string;\n /** Default transactional template ID when not set per message via header. */\n defaultTransactionalId?: string;\n}\n\n/** Error thrown when the Loops API returns a non-success response. */\nexport class LoopsError extends SentlyError {\n /** Creates a Loops API error with status code. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"loops\",\n cause: apiError,\n });\n this.name = \"LoopsError\";\n }\n}\n\nconst TRANSACTIONAL_ID_HEADER = \"x-loops-transactional-id\";\n\n/**\n * Loops transactional HTTP API transport.\n */\nexport class LoopsTransport implements Transport {\n readonly provider = \"loops\";\n\n private readonly apiKey: string;\n private readonly defaultTransactionalId?: string;\n\n /** Creates a Loops transport with the given API key. */\n constructor(config: LoopsConfig) {\n this.apiKey = config.apiKey;\n if (config.defaultTransactionalId !== undefined) {\n this.defaultTransactionalId = config.defaultTransactionalId;\n }\n }\n\n /** Sends a transactional email via the Loops HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const transactionalId =\n options.headers?.[TRANSACTIONAL_ID_HEADER] ?? this.defaultTransactionalId;\n\n if (!transactionalId) {\n throw new LoopsError(\n \"Loops requires a transactional template ID. Set options.headers['x-loops-transactional-id'] or LoopsConfig.defaultTransactionalId. Loops cannot send raw subject/html/text.\",\n 400,\n { code: \"MISSING_TRANSACTIONAL_ID\" },\n );\n }\n\n const recipient = extractEmails(options.to)[0];\n if (!recipient) {\n throw new LoopsError(\"Loops requires at least one recipient in options.to\", 400, {\n code: \"MISSING_RECIPIENT\",\n });\n }\n\n const body = {\n transactionalId,\n email: recipient,\n dataVariables: options.data ?? {},\n };\n\n const response = await fetch(\"https://app.loops.so/api/v1/transactional\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n success?: boolean;\n id?: string;\n message?: string;\n };\n\n if (!response.ok || payload.success === false) {\n throw new LoopsError(payload.message ?? \"Loops API error\", response.status, payload);\n }\n\n return {\n messageId: payload.id ?? \"\",\n accepted: [recipient],\n rejected: [],\n response: payload.id ?? \"sent\",\n envelope: {\n from: extractEmails(options.from)[0] ?? \"\",\n to: [recipient],\n },\n };\n }\n\n /**\n * Returns success without a network call — Loops has no simple verify endpoint.\n */\n async verify(): Promise<VerifyResult> {\n return {\n ok: true,\n provider: \"loops\",\n message: \"key not validated (no verify endpoint)\",\n };\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "oKA0CO,CAAM,KAAmB,JAAY,CAIxB,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,QACV,MAAO,CACT,CAAC,EAPe,kBACA,gBAOhB,KAAK,KAAO,aAEhB,CAEA,IAAM,EAA0B,2BAKzB,MAAM,CAAoC,CACtC,SAAW,QAEH,OACA,uBAGjB,WAAW,CAAC,EAAqB,CAE/B,GADA,KAAK,OAAS,EAAO,OACjB,EAAO,yBAA2B,OACpC,KAAK,uBAAyB,EAAO,4BAKnC,KAAI,CAAC,EAA2C,CACpD,IAAM,EACJ,EAAQ,UAAU,IAA4B,KAAK,uBAErD,GAAI,CAAC,EACH,MAAM,IAAI,EACR,8KACA,IACA,CAAE,KAAM,0BAA2B,CACrC,EAGF,IAAM,EAAY,EAAc,EAAQ,EAAE,EAAE,GAC5C,GAAI,CAAC,EACH,MAAM,IAAI,EAAW,sDAAuD,IAAK,CAC/E,KAAM,mBACR,CAAC,EAGH,IAAM,EAAO,CACX,kBACA,MAAO,EACP,cAAe,EAAQ,MAAQ,CAAC,CAClC,EAEM,EAAW,MAAM,MAAM,4CAA6C,CACxE,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,KAAK,SAC9B,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAMrC,GAAI,CAAC,EAAS,IAAM,EAAQ,UAAY,GACtC,MAAM,IAAI,EAAW,EAAQ,SAAW,kBAAmB,EAAS,OAAQ,CAAO,EAGrF,MAAO,CACL,UAAW,EAAQ,IAAM,GACzB,SAAU,CAAC,CAAS,EACpB,SAAU,CAAC,EACX,SAAU,EAAQ,IAAM,OACxB,SAAU,CACR,KAAM,EAAc,EAAQ,IAAI,EAAE,IAAM,GACxC,GAAI,CAAC,CAAS,CAChB,CACF,OAMI,OAAM,EAA0B,CACpC,MAAO,CACL,GAAI,GACJ,SAAU,QACV,QAAS,wCACX,EAEJ",
|
|
8
|
+
"debugId": "155A09B7C69A1BDB64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SentlyError } from "../core/errors.js";
|
|
2
|
+
import type { MailOptions, SendResult, Transport, VerifyResult } from "../core/types.js";
|
|
3
|
+
/** MailerSend HTTP API configuration. */
|
|
4
|
+
export interface MailerSendConfig {
|
|
5
|
+
/** MailerSend API token for Bearer authentication. */
|
|
6
|
+
apiToken: string;
|
|
7
|
+
}
|
|
8
|
+
/** Error thrown when the MailerSend API returns a non-success response. */
|
|
9
|
+
export declare class MailerSendError extends SentlyError {
|
|
10
|
+
readonly statusCode: number;
|
|
11
|
+
readonly apiError: unknown;
|
|
12
|
+
/** Creates a MailerSend API error with status code. */
|
|
13
|
+
constructor(message: string, statusCode: number, apiError: unknown);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* MailerSend HTTP API transport.
|
|
17
|
+
*/
|
|
18
|
+
export declare class MailerSendTransport implements Transport {
|
|
19
|
+
readonly provider = "mailersend";
|
|
20
|
+
private readonly apiToken;
|
|
21
|
+
/** Creates a MailerSend transport with the given API token. */
|
|
22
|
+
constructor(config: MailerSendConfig);
|
|
23
|
+
/** Sends an email via the MailerSend HTTP API. */
|
|
24
|
+
send(options: MailOptions): Promise<SendResult>;
|
|
25
|
+
/** Verifies the MailerSend API token by listing domains. */
|
|
26
|
+
verify(): Promise<VerifyResult>;
|
|
27
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{I as U}from"../chunk-skqhj0rm.js";import{Q as G,S as N}from"../chunk-93vqxxj2.js";import{Y as L}from"../chunk-ywq6s10g.js";import{ca as Q,da as R}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class W extends Q{statusCode;apiError;constructor(q,z,D){super(q,R(z),{statusCode:z,provider:"mailersend",cause:D});this.statusCode=z;this.apiError=D;this.name="MailerSendError"}}function H(q){return G(q).map((z)=>({email:z.address,...z.name?{name:z.name}:{}}))}class Y{provider="mailersend";apiToken;constructor(q){this.apiToken=q.apiToken}async send(q){let z=await U(q.attachments),D=G(q.from)[0],X={from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},to:H(q.to),subject:q.subject,...q.cc?{cc:H(q.cc)}:{},...q.bcc?{bcc:H(q.bcc)}:{},...q.replyTo?{reply_to:(()=>{let w=G(q.replyTo)[0];return w?{email:w.address,...w.name?{name:w.name}:{}}:void 0})()}:{},...q.text?{text:q.text}:{},...q.html?{html:q.html}:{},...z.length>0?{attachments:z.map((w)=>({filename:w.filename,content:w.content instanceof Uint8Array?L(w.content).replace(/\r\n/g,""):w.content,disposition:"attachment"}))}:{}},F=await fetch("https://api.mailersend.com/v1/email",{method:"POST",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify(X)});if(!F.ok){let w=await F.json().catch(()=>({}));throw new W(w.message??"MailerSend API error",F.status,w)}let J=F.headers.get("x-message-id")??"",K=N(q.to);return{messageId:J,accepted:K,rejected:[],response:J||"accepted",envelope:{from:D?.address??"",to:K}}}async verify(){try{let q=await fetch("https://api.mailersend.com/v1/domains",{headers:{Authorization:`Bearer ${this.apiToken}`}});if(!q.ok)return{ok:!1,provider:"mailersend",message:(await q.json().catch(()=>({}))).message??`HTTP ${q.status}`};return{ok:!0,provider:"mailersend",message:"API token is valid"}}catch(q){return{ok:!1,provider:"mailersend",message:q instanceof Error?q.message:String(q)}}}}export{Y as MailerSendTransport,W as MailerSendError};
|
|
2
|
+
|
|
3
|
+
//# debugId=01F773171442CE1F64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/transports/mailersend.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @module\n * MailerSend HTTP transport for sently.\n *\n * @example\n * ```ts\n * import { MailerSendTransport } from \"sently/transports/mailersend\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new MailerSendTransport({ apiToken: process.env.MAILERSEND_API_TOKEN! }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** MailerSend HTTP API configuration. */\nexport interface MailerSendConfig {\n /** MailerSend API token for Bearer authentication. */\n apiToken: string;\n}\n\n/** Error thrown when the MailerSend API returns a non-success response. */\nexport class MailerSendError extends SentlyError {\n /** Creates a MailerSend API error with status code. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"mailersend\",\n cause: apiError,\n });\n this.name = \"MailerSendError\";\n }\n}\n\nfunction toAddressObjects(input: MailOptions[\"to\"]): Array<{ email: string; name?: string }> {\n return parseAddresses(input).map((addr) => ({\n email: addr.address,\n ...(addr.name ? { name: addr.name } : {}),\n }));\n}\n\n/**\n * MailerSend HTTP API transport.\n */\nexport class MailerSendTransport implements Transport {\n readonly provider = \"mailersend\";\n\n private readonly apiToken: string;\n\n /** Creates a MailerSend transport with the given API token. */\n constructor(config: MailerSendConfig) {\n this.apiToken = config.apiToken;\n }\n\n /** Sends an email via the MailerSend HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const body: Record<string, unknown> = {\n from: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n to: toAddressObjects(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: toAddressObjects(options.cc) } : {}),\n ...(options.bcc ? { bcc: toAddressObjects(options.bcc) } : {}),\n ...(options.replyTo\n ? {\n reply_to: (() => {\n const reply = parseAddresses(options.replyTo as MailOptions[\"to\"])[0];\n return reply\n ? { email: reply.address, ...(reply.name ? { name: reply.name } : {}) }\n : undefined;\n })(),\n }\n : {}),\n ...(options.text ? { text: options.text } : {}),\n ...(options.html ? { html: options.html } : {}),\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n filename: att.filename,\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n disposition: \"attachment\",\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.mailersend.com/v1/email\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as {\n message?: string;\n errors?: unknown;\n };\n throw new MailerSendError(\n payload.message ?? \"MailerSend API error\",\n response.status,\n payload,\n );\n }\n\n const messageId = response.headers.get(\"x-message-id\") ?? \"\";\n const toEmails = extractEmails(options.to);\n\n return {\n messageId,\n accepted: toEmails,\n rejected: [],\n response: messageId || \"accepted\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the MailerSend API token by listing domains. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.mailersend.com/v1/domains\", {\n headers: {\n Authorization: `Bearer ${this.apiToken}`,\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n return {\n ok: false,\n provider: \"mailersend\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"mailersend\", message: \"API token is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"mailersend\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "qOA2BO,CAAM,KAAwB,JAAY,JAI7B,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,aACV,MAAO,CACT,CAAC,EAPe,kBACA,gBAOhB,KAAK,KAAO,kBAEhB,CAEA,SAAS,CAAgB,CAAC,EAAmE,CAC3F,OAAO,EAAe,CAAK,EAAE,IAAI,CAAC,KAAU,CAC1C,MAAO,EAAK,WACR,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CACzC,EAAE,EAMG,MAAM,CAAyC,CAC3C,SAAW,aAEH,SAGjB,WAAW,CAAC,EAA0B,CACpC,KAAK,SAAW,EAAO,cAInB,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAgC,CACpC,KAAM,EACF,CAAE,MAAO,EAAK,WAAa,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CAAG,EACjE,CAAE,MAAO,EAAG,EAChB,GAAI,EAAiB,EAAQ,EAAE,EAC/B,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAiB,EAAQ,EAAE,CAAE,EAAI,CAAC,KACrD,EAAQ,IAAM,CAAE,IAAK,EAAiB,EAAQ,GAAG,CAAE,EAAI,CAAC,KACxD,EAAQ,QACR,CACE,UAAW,IAAM,CACf,IAAM,EAAQ,EAAe,EAAQ,OAA4B,EAAE,GACnE,OAAO,EACH,CAAE,MAAO,EAAM,WAAa,EAAM,KAAO,CAAE,KAAM,EAAM,IAAK,EAAI,CAAC,CAAG,EACpE,SACH,CACL,EACA,CAAC,KACD,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAY,OAAS,EACrB,CACE,YAAa,EAAY,IAAI,CAAC,KAAS,CACrC,SAAU,EAAI,SACd,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,QACV,YAAa,YACf,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,sCAAuC,CAClE,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,KAAK,WAC9B,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAED,GAAI,CAAC,EAAS,GAAI,CAChB,IAAM,EAAW,MAAM,EAAS,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,EAIvD,MAAM,IAAI,EACR,EAAQ,SAAW,uBACnB,EAAS,OACT,CACF,EAGF,IAAM,EAAY,EAAS,QAAQ,IAAI,cAAc,GAAK,GACpD,EAAW,EAAc,EAAQ,EAAE,EAEzC,MAAO,CACL,YACA,SAAU,EACV,SAAU,CAAC,EACX,SAAU,GAAa,WACvB,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAII,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,wCAAyC,CACpE,QAAS,CACP,cAAe,UAAU,KAAK,UAChC,CACF,CAAC,EAED,GAAI,CAAC,EAAS,GAEZ,MAAO,CACL,GAAI,GACJ,SAAU,aACV,SAJe,MAAM,EAAS,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,GAIpC,SAAW,QAAQ,EAAS,QAC/C,EAGF,MAAO,CAAE,GAAI,GAAM,SAAU,aAAc,QAAS,oBAAqB,EACzE,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,aACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,GAGN",
|
|
8
|
+
"debugId": "01F773171442CE1F64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -11,6 +11,7 @@ export declare class MailgunError extends SentlyError {
|
|
|
11
11
|
* Mailgun HTTP API transport (multipart/form-data).
|
|
12
12
|
*/
|
|
13
13
|
export declare class MailgunTransport implements Transport {
|
|
14
|
+
readonly provider = "mailgun";
|
|
14
15
|
/** Mailgun API key for basic authentication. */
|
|
15
16
|
private readonly apiKey;
|
|
16
17
|
/** Mailgun sending domain. */
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{I as W}from"../chunk-skqhj0rm.js";import{Q as G,R as J,S as Q}from"../chunk-93vqxxj2.js";import{Y as N}from"../chunk-ywq6s10g.js";import{ca as R,da as U}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class X extends R{statusCode;apiError;constructor(q,z,F){super(q,U(z),{statusCode:z,provider:"mailgun",cause:F});this.statusCode=z;this.apiError=F;this.name="MailgunError"}}class ${provider="mailgun";apiKey;domain;baseUrl;constructor(q){this.apiKey=q.apiKey,this.domain=q.domain,this.baseUrl=q.region==="eu"?"https://api.eu.mailgun.net":"https://api.mailgun.net"}async send(q){let z=await W(q.attachments),F=G(q.from)[0],w=new FormData;if(w.append("from",F?J(F):""),w.append("to",G(q.to).map(J).join(", ")),q.cc)w.append("cc",G(q.cc).map(J).join(", "));if(q.bcc)w.append("bcc",G(q.bcc).map(J).join(", "));if(w.append("subject",q.subject),q.text)w.append("text",q.text);if(q.html)w.append("html",q.html);if(q.replyTo){let D=G(q.replyTo)[0];if(D)w.append("h:Reply-To",J(D))}for(let D of z){let Z=D.content instanceof Uint8Array?D.content:new TextEncoder().encode(String(D.content??"")),_=new Blob([new Uint8Array(Z)],{type:D.contentType??"application/octet-stream"});w.append("attachment",_,D.filename)}let Y=N(`api:${this.apiKey}`).replace(/\r\n/g,""),L=await fetch(`${this.baseUrl}/v3/${this.domain}/messages`,{method:"POST",headers:{Authorization:`Basic ${Y}`},body:w}),K=await L.json();if(!L.ok)throw new X(K.message??"Mailgun API error",L.status,K);let O=Q(q.to);return{messageId:K.id??"",accepted:O,rejected:[],response:K.message??"queued",envelope:{from:F?.address??"",to:O}}}async verify(){try{let q=N(`api:${this.apiKey}`).replace(/\r\n/g,""),z=await fetch(`${this.baseUrl}/v3/domains`,{headers:{Authorization:`Basic ${q}`}});if(!z.ok)return{ok:!1,provider:"mailgun",message:(await z.json().catch(()=>({}))).message??`HTTP ${z.status}`};return{ok:!0,provider:"mailgun",message:"API key is valid"}}catch(q){return{ok:!1,provider:"mailgun",message:q instanceof Error?q.message:String(q)}}}}export{$ as MailgunTransport,X as MailgunError};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=3693D707D234EDC064756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/transports/mailgun.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * @module\n * Mailgun HTTP transport for sently.\n * Uses the Mailgun Messages API v3 with multipart/form-data.\n *\n * @example\n * ```ts\n * import { MailgunTransport } from \"sently/transports/mailgun\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new MailgunTransport({\n * apiKey: \"key-...\",\n * domain: \"mg.example.com\",\n * }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type {\n MailgunConfig,\n MailOptions,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the Mailgun API returns a non-success response. */\nexport class MailgunError extends SentlyError {\n /** Creates a Mailgun API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"mailgun\",\n cause: apiError,\n });\n this.name = \"MailgunError\";\n }\n}\n\n/**\n * Mailgun HTTP API transport (multipart/form-data).\n */\nexport class MailgunTransport implements Transport {\n /** Mailgun API key for basic authentication. */\n private readonly apiKey: string;\n /** Mailgun sending domain. */\n private readonly domain: string;\n /** Mailgun API base URL (US or EU region). */\n private readonly baseUrl: string;\n\n /** Creates a Mailgun transport with the given API key and domain. */\n constructor(config: MailgunConfig) {\n this.apiKey = config.apiKey;\n this.domain = config.domain;\n this.baseUrl =\n config.region === \"eu\" ? \"https://api.eu.mailgun.net\" : \"https://api.mailgun.net\";\n }\n\n /** Sends an email via the Mailgun Messages API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n const form = new FormData();\n\n form.append(\"from\", from ? toMIMEHeader(from) : \"\");\n form.append(\"to\", parseAddresses(options.to).map(toMIMEHeader).join(\", \"));\n\n if (options.cc) {\n form.append(\"cc\", parseAddresses(options.cc).map(toMIMEHeader).join(\", \"));\n }\n if (options.bcc) {\n form.append(\"bcc\", parseAddresses(options.bcc).map(toMIMEHeader).join(\", \"));\n }\n\n form.append(\"subject\", options.subject);\n\n if (options.text) {\n form.append(\"text\", options.text);\n }\n if (options.html) {\n form.append(\"html\", options.html);\n }\n if (options.replyTo) {\n const replyTo = parseAddresses(options.replyTo)[0];\n if (replyTo) {\n form.append(\"h:Reply-To\", toMIMEHeader(replyTo));\n }\n }\n\n for (const attachment of attachments) {\n const content =\n attachment.content instanceof Uint8Array\n ? attachment.content\n : new TextEncoder().encode(String(attachment.content ?? \"\"));\n const blob = new Blob([new Uint8Array(content)], {\n type: attachment.contentType ?? \"application/octet-stream\",\n });\n form.append(\"attachment\", blob, attachment.filename);\n }\n\n const auth = encodeBase64(`api:${this.apiKey}`).replace(/\\r\\n/g, \"\");\n const response = await fetch(`${this.baseUrl}/v3/${this.domain}/messages`, {\n method: \"POST\",\n headers: {\n Authorization: `Basic ${auth}`,\n },\n body: form,\n });\n\n const payload = (await response.json()) as { id?: string; message?: string };\n\n if (!response.ok) {\n throw new MailgunError(payload.message ?? \"Mailgun API error\", response.status, payload);\n }\n\n const toEmails = extractEmails(options.to);\n return {\n messageId: payload.id ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: payload.message ?? \"queued\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the Mailgun API key by listing domains. */\n async verify(): Promise<VerifyResult> {\n try {\n const auth = encodeBase64(`api:${this.apiKey}`).replace(/\\r\\n/g, \"\");\n const response = await fetch(`${this.baseUrl}/v3/domains`, {\n headers: {\n Authorization: `Basic ${auth}`,\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n return {\n ok: false,\n provider: \"mailgun\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"mailgun\", message: \"API key is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"mailgun\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
|
|
5
|
+
"/**\n * @module\n * Mailgun HTTP transport for sently.\n * Uses the Mailgun Messages API v3 with multipart/form-data.\n *\n * @example\n * ```ts\n * import { MailgunTransport } from \"sently/transports/mailgun\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new MailgunTransport({\n * apiKey: \"key-...\",\n * domain: \"mg.example.com\",\n * }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type {\n MailgunConfig,\n MailOptions,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the Mailgun API returns a non-success response. */\nexport class MailgunError extends SentlyError {\n /** Creates a Mailgun API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"mailgun\",\n cause: apiError,\n });\n this.name = \"MailgunError\";\n }\n}\n\n/**\n * Mailgun HTTP API transport (multipart/form-data).\n */\nexport class MailgunTransport implements Transport {\n readonly provider = \"mailgun\";\n\n /** Mailgun API key for basic authentication. */\n private readonly apiKey: string;\n /** Mailgun sending domain. */\n private readonly domain: string;\n /** Mailgun API base URL (US or EU region). */\n private readonly baseUrl: string;\n\n /** Creates a Mailgun transport with the given API key and domain. */\n constructor(config: MailgunConfig) {\n this.apiKey = config.apiKey;\n this.domain = config.domain;\n this.baseUrl =\n config.region === \"eu\" ? \"https://api.eu.mailgun.net\" : \"https://api.mailgun.net\";\n }\n\n /** Sends an email via the Mailgun Messages API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n const form = new FormData();\n\n form.append(\"from\", from ? toMIMEHeader(from) : \"\");\n form.append(\"to\", parseAddresses(options.to).map(toMIMEHeader).join(\", \"));\n\n if (options.cc) {\n form.append(\"cc\", parseAddresses(options.cc).map(toMIMEHeader).join(\", \"));\n }\n if (options.bcc) {\n form.append(\"bcc\", parseAddresses(options.bcc).map(toMIMEHeader).join(\", \"));\n }\n\n form.append(\"subject\", options.subject);\n\n if (options.text) {\n form.append(\"text\", options.text);\n }\n if (options.html) {\n form.append(\"html\", options.html);\n }\n if (options.replyTo) {\n const replyTo = parseAddresses(options.replyTo)[0];\n if (replyTo) {\n form.append(\"h:Reply-To\", toMIMEHeader(replyTo));\n }\n }\n\n for (const attachment of attachments) {\n const content =\n attachment.content instanceof Uint8Array\n ? attachment.content\n : new TextEncoder().encode(String(attachment.content ?? \"\"));\n const blob = new Blob([new Uint8Array(content)], {\n type: attachment.contentType ?? \"application/octet-stream\",\n });\n form.append(\"attachment\", blob, attachment.filename);\n }\n\n const auth = encodeBase64(`api:${this.apiKey}`).replace(/\\r\\n/g, \"\");\n const response = await fetch(`${this.baseUrl}/v3/${this.domain}/messages`, {\n method: \"POST\",\n headers: {\n Authorization: `Basic ${auth}`,\n },\n body: form,\n });\n\n const payload = (await response.json()) as { id?: string; message?: string };\n\n if (!response.ok) {\n throw new MailgunError(payload.message ?? \"Mailgun API error\", response.status, payload);\n }\n\n const toEmails = extractEmails(options.to);\n return {\n messageId: payload.id ?? \"\",\n accepted: toEmails,\n rejected: [],\n response: payload.message ?? \"queued\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies the Mailgun API key by listing domains. */\n async verify(): Promise<VerifyResult> {\n try {\n const auth = encodeBase64(`api:${this.apiKey}`).replace(/\\r\\n/g, \"\");\n const response = await fetch(`${this.baseUrl}/v3/domains`, {\n headers: {\n Authorization: `Basic ${auth}`,\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n return {\n ok: false,\n provider: \"mailgun\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"mailgun\", message: \"API key is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"mailgun\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "4OA+BO,CAAM,KAAqB,JAAY,JAI1B,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,UACV,MAAO,CACT,CAAC,EAPe,kBACA,gBAOhB,KAAK,KAAO,eAEhB,CAKO,MAAM,CAAsC,CACxC,SAAW,UAGH,OAEA,OAEA,QAGjB,WAAW,CAAC,EAAuB,CACjC,KAAK,OAAS,EAAO,OACrB,KAAK,OAAS,EAAO,OACrB,KAAK,QACH,EAAO,SAAW,KAAO,6BAA+B,+BAItD,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GACpC,EAAO,IAAI,SAKjB,GAHA,EAAK,OAAO,OAAQ,EAAO,EAAa,CAAI,EAAI,EAAE,EAClD,EAAK,OAAO,KAAM,EAAe,EAAQ,EAAE,EAAE,IAAI,CAAY,EAAE,KAAK,IAAI,CAAC,EAErE,EAAQ,GACV,EAAK,OAAO,KAAM,EAAe,EAAQ,EAAE,EAAE,IAAI,CAAY,EAAE,KAAK,IAAI,CAAC,EAE3E,GAAI,EAAQ,IACV,EAAK,OAAO,MAAO,EAAe,EAAQ,GAAG,EAAE,IAAI,CAAY,EAAE,KAAK,IAAI,CAAC,EAK7E,GAFA,EAAK,OAAO,UAAW,EAAQ,OAAO,EAElC,EAAQ,KACV,EAAK,OAAO,OAAQ,EAAQ,IAAI,EAElC,GAAI,EAAQ,KACV,EAAK,OAAO,OAAQ,EAAQ,IAAI,EAElC,GAAI,EAAQ,QAAS,CACnB,IAAM,EAAU,EAAe,EAAQ,OAAO,EAAE,GAChD,GAAI,EACF,EAAK,OAAO,aAAc,EAAa,CAAO,CAAC,EAInD,QAAW,KAAc,EAAa,CACpC,IAAM,EACJ,EAAW,mBAAmB,WAC1B,EAAW,QACX,IAAI,YAAY,EAAE,OAAO,OAAO,EAAW,SAAW,EAAE,CAAC,EACzD,EAAO,IAAI,KAAK,CAAC,IAAI,WAAW,CAAO,CAAC,EAAG,CAC/C,KAAM,EAAW,aAAe,0BAClC,CAAC,EACD,EAAK,OAAO,aAAc,EAAM,EAAW,QAAQ,EAGrD,IAAM,EAAO,EAAa,OAAO,KAAK,QAAQ,EAAE,QAAQ,QAAS,EAAE,EAC7D,EAAW,MAAM,MAAM,GAAG,KAAK,cAAc,KAAK,kBAAmB,CACzE,OAAQ,OACR,QAAS,CACP,cAAe,SAAS,GAC1B,EACA,KAAM,CACR,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAErC,GAAI,CAAC,EAAS,GACZ,MAAM,IAAI,EAAa,EAAQ,SAAW,oBAAqB,EAAS,OAAQ,CAAO,EAGzF,IAAM,EAAW,EAAc,EAAQ,EAAE,EACzC,MAAO,CACL,UAAW,EAAQ,IAAM,GACzB,SAAU,EACV,SAAU,CAAC,EACX,SAAU,EAAQ,SAAW,SAC7B,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAII,OAAM,EAA0B,CACpC,GAAI,CACF,IAAM,EAAO,EAAa,OAAO,KAAK,QAAQ,EAAE,QAAQ,QAAS,EAAE,EAC7D,EAAW,MAAM,MAAM,GAAG,KAAK,qBAAsB,CACzD,QAAS,CACP,cAAe,SAAS,GAC1B,CACF,CAAC,EAED,GAAI,CAAC,EAAS,GAEZ,MAAO,CACL,GAAI,GACJ,SAAU,UACV,SAJe,MAAM,EAAS,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,GAIpC,SAAW,QAAQ,EAAS,QAC/C,EAGF,MAAO,CAAE,GAAI,GAAM,SAAU,UAAW,QAAS,kBAAmB,EACpE,MAAO,EAAK,CACZ,MAAO,CACL,GAAI,GACJ,SAAU,UACV,QAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAC1D,GAGN",
|
|
8
|
+
"debugId": "3693D707D234EDC064756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SentlyError } from "../core/errors.js";
|
|
2
|
+
import type { MailOptions, SendResult, Transport, VerifyResult } from "../core/types.js";
|
|
3
|
+
/** Mailtrap HTTP API configuration. */
|
|
4
|
+
export interface MailtrapConfig {
|
|
5
|
+
/** Mailtrap API token for Bearer authentication. */
|
|
6
|
+
apiToken: string;
|
|
7
|
+
/** When true, use the sandbox send endpoint (requires `inboxId`). */
|
|
8
|
+
sandbox?: boolean;
|
|
9
|
+
/** Sandbox inbox ID — required when `sandbox` is true. */
|
|
10
|
+
inboxId?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Error thrown when the Mailtrap API returns a non-success response. */
|
|
13
|
+
export declare class MailtrapError extends SentlyError {
|
|
14
|
+
readonly statusCode: number;
|
|
15
|
+
readonly apiError: unknown;
|
|
16
|
+
/** Creates a Mailtrap API error with status code. */
|
|
17
|
+
constructor(message: string, statusCode: number, apiError: unknown);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Mailtrap HTTP API transport.
|
|
21
|
+
*/
|
|
22
|
+
export declare class MailtrapTransport implements Transport {
|
|
23
|
+
readonly provider = "mailtrap";
|
|
24
|
+
private readonly apiToken;
|
|
25
|
+
private readonly sandbox;
|
|
26
|
+
private readonly inboxId?;
|
|
27
|
+
/** Creates a Mailtrap transport with the given API token. */
|
|
28
|
+
constructor(config: MailtrapConfig);
|
|
29
|
+
private sendUrl;
|
|
30
|
+
/** Sends an email via the Mailtrap HTTP API. */
|
|
31
|
+
send(options: MailOptions): Promise<SendResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Returns success without a network call — Mailtrap has no simple verify endpoint.
|
|
34
|
+
*/
|
|
35
|
+
verify(): Promise<VerifyResult>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{I as X}from"../chunk-skqhj0rm.js";import{Q as L,S as U}from"../chunk-93vqxxj2.js";import{Y as Q}from"../chunk-ywq6s10g.js";import{ca as V,da as W}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";class K extends V{statusCode;apiError;constructor(q,z,D){super(q,W(z),{statusCode:z,provider:"mailtrap",cause:D});this.statusCode=z;this.apiError=D;this.name="MailtrapError"}}function J(q){return L(q).map((z)=>({email:z.address,...z.name?{name:z.name}:{}}))}class Z{provider="mailtrap";apiToken;sandbox;inboxId;constructor(q){if(this.apiToken=q.apiToken,this.sandbox=q.sandbox??!1,q.inboxId!==void 0)this.inboxId=q.inboxId}sendUrl(){if(this.sandbox){if(!this.inboxId)throw new K("Mailtrap sandbox mode requires inboxId",400,{code:"INVALID_CONFIG"});return`https://sandbox.api.mailtrap.io/api/send/${this.inboxId}`}return"https://send.api.mailtrap.io/api/send"}async send(q){let z=await X(q.attachments),D=L(q.from)[0],Y={from:D?{email:D.address,...D.name?{name:D.name}:{}}:{email:""},to:J(q.to),subject:q.subject,...q.cc?{cc:J(q.cc)}:{},...q.bcc?{bcc:J(q.bcc)}:{},...q.text?{text:q.text}:{},...q.html?{html:q.html}:{},...z.length>0?{attachments:z.map((F)=>({filename:F.filename,content:F.content instanceof Uint8Array?Q(F.content).replace(/\r\n/g,""):F.content,type:F.contentType??"application/octet-stream"}))}:{}},H=await fetch(this.sendUrl(),{method:"POST",headers:{Authorization:`Bearer ${this.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify(Y)}),G=await H.json();if(!H.ok||G.success!==!0)throw new K(G.message??"Mailtrap API error",H.status,G);let N=G.message_ids??[],P=U(q.to);return{messageId:N.join(","),accepted:P,rejected:[],response:N.join(",")||"sent",envelope:{from:D?.address??"",to:P}}}async verify(){return{ok:!0,provider:"mailtrap",message:"key not validated (no verify endpoint)"}}}export{Z as MailtrapTransport,K as MailtrapError};
|
|
2
|
+
|
|
3
|
+
//# debugId=332E52E7FE5672B864756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/transports/mailtrap.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @module\n * Mailtrap HTTP transport for sently.\n *\n * @example\n * ```ts\n * import { MailtrapTransport } from \"sently/transports/mailtrap\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new MailtrapTransport({ apiToken: process.env.MAILTRAP_API_TOKEN! }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Mailtrap HTTP API configuration. */\nexport interface MailtrapConfig {\n /** Mailtrap API token for Bearer authentication. */\n apiToken: string;\n /** When true, use the sandbox send endpoint (requires `inboxId`). */\n sandbox?: boolean;\n /** Sandbox inbox ID — required when `sandbox` is true. */\n inboxId?: string;\n}\n\n/** Error thrown when the Mailtrap API returns a non-success response. */\nexport class MailtrapError extends SentlyError {\n /** Creates a Mailtrap API error with status code. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, httpStatusToSentlyCode(statusCode), {\n statusCode,\n provider: \"mailtrap\",\n cause: apiError,\n });\n this.name = \"MailtrapError\";\n }\n}\n\nfunction toAddressObjects(input: MailOptions[\"to\"]): Array<{ email: string; name?: string }> {\n return parseAddresses(input).map((addr) => ({\n email: addr.address,\n ...(addr.name ? { name: addr.name } : {}),\n }));\n}\n\n/**\n * Mailtrap HTTP API transport.\n */\nexport class MailtrapTransport implements Transport {\n readonly provider = \"mailtrap\";\n\n private readonly apiToken: string;\n private readonly sandbox: boolean;\n private readonly inboxId?: string;\n\n /** Creates a Mailtrap transport with the given API token. */\n constructor(config: MailtrapConfig) {\n this.apiToken = config.apiToken;\n this.sandbox = config.sandbox ?? false;\n if (config.inboxId !== undefined) {\n this.inboxId = config.inboxId;\n }\n }\n\n private sendUrl(): string {\n if (this.sandbox) {\n if (!this.inboxId) {\n throw new MailtrapError(\"Mailtrap sandbox mode requires inboxId\", 400, {\n code: \"INVALID_CONFIG\",\n });\n }\n return `https://sandbox.api.mailtrap.io/api/send/${this.inboxId}`;\n }\n return \"https://send.api.mailtrap.io/api/send\";\n }\n\n /** Sends an email via the Mailtrap HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const body: Record<string, unknown> = {\n from: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n to: toAddressObjects(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: toAddressObjects(options.cc) } : {}),\n ...(options.bcc ? { bcc: toAddressObjects(options.bcc) } : {}),\n ...(options.text ? { text: options.text } : {}),\n ...(options.html ? { html: options.html } : {}),\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n filename: att.filename,\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n type: att.contentType ?? \"application/octet-stream\",\n })),\n }\n : {}),\n };\n\n const response = await fetch(this.sendUrl(), {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n success?: boolean;\n message_ids?: string[];\n message?: string;\n };\n\n if (!response.ok || payload.success !== true) {\n throw new MailtrapError(payload.message ?? \"Mailtrap API error\", response.status, payload);\n }\n\n const messageIds = payload.message_ids ?? [];\n const toEmails = extractEmails(options.to);\n\n return {\n messageId: messageIds.join(\",\"),\n accepted: toEmails,\n rejected: [],\n response: messageIds.join(\",\") || \"sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /**\n * Returns success without a network call — Mailtrap has no simple verify endpoint.\n */\n async verify(): Promise<VerifyResult> {\n return {\n ok: true,\n provider: \"mailtrap\",\n message: \"key not validated (no verify endpoint)\",\n };\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "qOA+BO,CAAM,KAAsB,JAAY,JAI3B,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,EAAS,EAAuB,CAAU,EAAG,CACjD,aACA,SAAU,WACV,MAAO,CACT,CAAC,EAPe,kBACA,gBAOhB,KAAK,KAAO,gBAEhB,CAEA,SAAS,CAAgB,CAAC,EAAmE,CAC3F,OAAO,EAAe,CAAK,EAAE,IAAI,CAAC,KAAU,CAC1C,MAAO,EAAK,WACR,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CACzC,EAAE,EAMG,MAAM,CAAuC,CACzC,SAAW,WAEH,SACA,QACA,QAGjB,WAAW,CAAC,EAAwB,CAGlC,GAFA,KAAK,SAAW,EAAO,SACvB,KAAK,QAAU,EAAO,SAAW,GAC7B,EAAO,UAAY,OACrB,KAAK,QAAU,EAAO,QAIlB,OAAO,EAAW,CACxB,GAAI,KAAK,QAAS,CAChB,GAAI,CAAC,KAAK,QACR,MAAM,IAAI,EAAc,yCAA0C,IAAK,CACrE,KAAM,gBACR,CAAC,EAEH,MAAO,4CAA4C,KAAK,UAE1D,MAAO,6CAIH,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAc,MAAM,EAAmB,EAAQ,WAAW,EAC1D,EAAO,EAAe,EAAQ,IAAI,EAAE,GAEpC,EAAgC,CACpC,KAAM,EACF,CAAE,MAAO,EAAK,WAAa,EAAK,KAAO,CAAE,KAAM,EAAK,IAAK,EAAI,CAAC,CAAG,EACjE,CAAE,MAAO,EAAG,EAChB,GAAI,EAAiB,EAAQ,EAAE,EAC/B,QAAS,EAAQ,WACb,EAAQ,GAAK,CAAE,GAAI,EAAiB,EAAQ,EAAE,CAAE,EAAI,CAAC,KACrD,EAAQ,IAAM,CAAE,IAAK,EAAiB,EAAQ,GAAG,CAAE,EAAI,CAAC,KACxD,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAQ,KAAO,CAAE,KAAM,EAAQ,IAAK,EAAI,CAAC,KACzC,EAAY,OAAS,EACrB,CACE,YAAa,EAAY,IAAI,CAAC,KAAS,CACrC,SAAU,EAAI,SACd,QACE,EAAI,mBAAmB,WACnB,EAAa,EAAI,OAAO,EAAE,QAAQ,QAAS,EAAE,EAC7C,EAAI,QACV,KAAM,EAAI,aAAe,0BAC3B,EAAE,CACJ,EACA,CAAC,CACP,EAEM,EAAW,MAAM,MAAM,KAAK,QAAQ,EAAG,CAC3C,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,KAAK,WAC9B,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAEK,EAAW,MAAM,EAAS,KAAK,EAMrC,GAAI,CAAC,EAAS,IAAM,EAAQ,UAAY,GACtC,MAAM,IAAI,EAAc,EAAQ,SAAW,qBAAsB,EAAS,OAAQ,CAAO,EAG3F,IAAM,EAAa,EAAQ,aAAe,CAAC,EACrC,EAAW,EAAc,EAAQ,EAAE,EAEzC,MAAO,CACL,UAAW,EAAW,KAAK,GAAG,EAC9B,SAAU,EACV,SAAU,CAAC,EACX,SAAU,EAAW,KAAK,GAAG,GAAK,OAClC,SAAU,CACR,KAAM,GAAM,SAAW,GACvB,GAAI,CACN,CACF,OAMI,OAAM,EAA0B,CACpC,MAAO,CACL,GAAI,GACJ,SAAU,WACV,QAAS,wCACX,EAEJ",
|
|
8
|
+
"debugId": "332E52E7FE5672B864756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|