sently 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +60 -0
  3. package/README.md +18 -16
  4. package/dist/adapters/bun.js +1 -1
  5. package/dist/adapters/cf.js +2 -2
  6. package/dist/adapters/cf.js.map +1 -1
  7. package/dist/adapters/deno.js +1 -1
  8. package/dist/adapters/node.js +1 -1
  9. package/dist/auth/oauth2.js +2 -2
  10. package/dist/auth/oauth2.js.map +1 -1
  11. package/dist/channel-result.d.ts +6 -0
  12. package/dist/channel-result.js +1 -0
  13. package/dist/{chunk-skqhj0rm.js → chunk-1ke3nmyg.js} +3 -3
  14. package/dist/{chunk-skqhj0rm.js.map → chunk-1ke3nmyg.js.map} +1 -1
  15. package/dist/{chunk-7gy2q9sh.js → chunk-5es0v1j7.js} +2 -2
  16. package/dist/{chunk-7gy2q9sh.js.map → chunk-5es0v1j7.js.map} +1 -1
  17. package/dist/{chunk-0qxws3kj.js → chunk-7wtfjx0d.js} +2 -2
  18. package/dist/{chunk-0qxws3kj.js.map → chunk-7wtfjx0d.js.map} +1 -1
  19. package/dist/chunk-8kpgbrba.js +4 -0
  20. package/dist/{chunk-93vqxxj2.js.map → chunk-8kpgbrba.js.map} +2 -2
  21. package/dist/{chunk-vjpds2pf.js → chunk-a057jzsy.js} +2 -2
  22. package/dist/{chunk-vjpds2pf.js.map → chunk-a057jzsy.js.map} +1 -1
  23. package/dist/{chunk-8njyga62.js → chunk-bhr7w3gg.js} +2 -2
  24. package/dist/{chunk-8njyga62.js.map → chunk-bhr7w3gg.js.map} +1 -1
  25. package/dist/chunk-cs1tvehn.js +4 -0
  26. package/dist/chunk-cs1tvehn.js.map +10 -0
  27. package/dist/{chunk-29z7fkzy.js → chunk-d5aw5vag.js} +3 -3
  28. package/dist/{chunk-29z7fkzy.js.map → chunk-d5aw5vag.js.map} +1 -1
  29. package/dist/chunk-f5x8vesr.js +4 -0
  30. package/dist/{chunk-b602dhck.js.map → chunk-f5x8vesr.js.map} +2 -2
  31. package/dist/chunk-fv0bemgf.js +4 -0
  32. package/dist/{chunk-yp311efr.js.map → chunk-fv0bemgf.js.map} +3 -3
  33. package/dist/{chunk-ywq6s10g.js → chunk-hnzmn4s4.js} +2 -2
  34. package/dist/{chunk-ywq6s10g.js.map → chunk-hnzmn4s4.js.map} +1 -1
  35. package/dist/chunk-htdg075t.js +4 -0
  36. package/dist/{chunk-qtkd5bak.js.map → chunk-htdg075t.js.map} +4 -3
  37. package/dist/{chunk-0gqy32pe.js → chunk-q2mahnb7.js} +3 -3
  38. package/dist/chunk-q2mahnb7.js.map +10 -0
  39. package/dist/chunk-sbgt4165.js +4 -0
  40. package/dist/chunk-sbgt4165.js.map +10 -0
  41. package/dist/{chunk-6npp3x3c.js → chunk-th4cwrpb.js} +2 -2
  42. package/dist/{chunk-6npp3x3c.js.map → chunk-th4cwrpb.js.map} +1 -1
  43. package/dist/{chunk-tamww15j.js → chunk-ttbwyxmh.js} +2 -2
  44. package/dist/{chunk-tamww15j.js.map → chunk-ttbwyxmh.js.map} +1 -1
  45. package/dist/chunk-w4c9kj70.js +13 -0
  46. package/dist/{chunk-n0qeyzqm.js.map → chunk-w4c9kj70.js.map} +2 -2
  47. package/dist/chunk-w9sabmb0.js +4 -0
  48. package/dist/chunk-w9sabmb0.js.map +10 -0
  49. package/dist/{chunk-8dty8c4p.js → chunk-yg3kj723.js} +3 -3
  50. package/dist/{chunk-8dty8c4p.js.map → chunk-yg3kj723.js.map} +2 -2
  51. package/dist/chunk-z1589fjk.js +4 -0
  52. package/dist/chunk-z1589fjk.js.map +10 -0
  53. package/dist/core/channel-result.d.ts +36 -0
  54. package/dist/core/channel-result.js +3 -0
  55. package/dist/core/channel-result.js.map +10 -0
  56. package/dist/core/decorated-transport.d.ts +30 -0
  57. package/dist/core/decorator-hooks.d.ts +17 -0
  58. package/dist/core/errors.js +2 -2
  59. package/dist/core/errors.js.map +1 -1
  60. package/dist/core/provider-label.d.ts +7 -3
  61. package/dist/core/push-endpoint.d.ts +5 -0
  62. package/dist/core/push-types.d.ts +48 -14
  63. package/dist/core/sms-types.d.ts +12 -0
  64. package/dist/core/smtp.js +2 -2
  65. package/dist/core/smtp.js.map +1 -1
  66. package/dist/core/whatsapp-types.d.ts +12 -0
  67. package/dist/detect.js +2 -2
  68. package/dist/detect.js.map +1 -1
  69. package/dist/dkim.js +2 -2
  70. package/dist/dkim.js.map +2 -2
  71. package/dist/idempotency.js +2 -2
  72. package/dist/idempotency.js.map +1 -1
  73. package/dist/index.d.ts +4 -0
  74. package/dist/index.js +5 -0
  75. package/dist/mailer.js +2 -2
  76. package/dist/mailer.js.map +1 -1
  77. package/dist/observability/console.js +1 -1
  78. package/dist/plugins/react.js +2 -2
  79. package/dist/plugins/react.js.map +1 -1
  80. package/dist/plugins/template.js +1 -1
  81. package/dist/pool/pool.js +2 -2
  82. package/dist/pool/pool.js.map +1 -1
  83. package/dist/push.d.ts +1 -0
  84. package/dist/push.js +2 -2
  85. package/dist/push.js.map +3 -3
  86. package/dist/sms.d.ts +1 -0
  87. package/dist/sms.js +2 -2
  88. package/dist/sms.js.map +3 -3
  89. package/dist/smtp-mailer.js +2 -2
  90. package/dist/smtp-mailer.js.map +1 -1
  91. package/dist/transports/brevo.js +2 -2
  92. package/dist/transports/brevo.js.map +2 -2
  93. package/dist/transports/cloudflare-email.js +2 -2
  94. package/dist/transports/cloudflare-email.js.map +2 -2
  95. package/dist/transports/fallback.d.ts +32 -6
  96. package/dist/transports/fallback.js +2 -2
  97. package/dist/transports/fallback.js.map +1 -1
  98. package/dist/transports/fcm.d.ts +55 -0
  99. package/dist/transports/fcm.js +4 -0
  100. package/dist/transports/fcm.js.map +10 -0
  101. package/dist/transports/loops.js +2 -2
  102. package/dist/transports/loops.js.map +1 -1
  103. package/dist/transports/mailersend.js +2 -2
  104. package/dist/transports/mailersend.js.map +2 -2
  105. package/dist/transports/mailgun.js +2 -2
  106. package/dist/transports/mailgun.js.map +2 -2
  107. package/dist/transports/mailtrap.js +2 -2
  108. package/dist/transports/mailtrap.js.map +2 -2
  109. package/dist/transports/msegat.js +2 -2
  110. package/dist/transports/msegat.js.map +1 -1
  111. package/dist/transports/plunk.js +2 -2
  112. package/dist/transports/plunk.js.map +1 -1
  113. package/dist/transports/postmark.js +2 -2
  114. package/dist/transports/postmark.js.map +2 -2
  115. package/dist/transports/preview.js +2 -2
  116. package/dist/transports/preview.js.map +1 -1
  117. package/dist/transports/resend.js +2 -2
  118. package/dist/transports/resend.js.map +2 -2
  119. package/dist/transports/retry.d.ts +28 -9
  120. package/dist/transports/retry.js +2 -2
  121. package/dist/transports/retry.js.map +3 -3
  122. package/dist/transports/sendgrid.js +2 -2
  123. package/dist/transports/sendgrid.js.map +2 -2
  124. package/dist/transports/ses.js +2 -2
  125. package/dist/transports/ses.js.map +2 -2
  126. package/dist/transports/smtp.js +2 -2
  127. package/dist/transports/smtp.js.map +1 -1
  128. package/dist/transports/sndr.js +2 -2
  129. package/dist/transports/sndr.js.map +1 -1
  130. package/dist/transports/sparkpost.js +2 -2
  131. package/dist/transports/sparkpost.js.map +2 -2
  132. package/dist/transports/taqnyat-mail.js +2 -2
  133. package/dist/transports/taqnyat-mail.js.map +1 -1
  134. package/dist/transports/taqnyat-sms.js +2 -2
  135. package/dist/transports/taqnyat-sms.js.map +1 -1
  136. package/dist/transports/taqnyat-whatsapp.js +2 -2
  137. package/dist/transports/taqnyat-whatsapp.js.map +1 -1
  138. package/dist/transports/twilio-sms.js +2 -2
  139. package/dist/transports/twilio-sms.js.map +2 -2
  140. package/dist/transports/unifonic.d.ts +81 -0
  141. package/dist/transports/unifonic.js +3 -0
  142. package/dist/transports/unifonic.js.map +10 -0
  143. package/dist/transports/webpush.js +2 -2
  144. package/dist/transports/webpush.js.map +3 -3
  145. package/dist/transports/weighted-fallback.d.ts +11 -9
  146. package/dist/transports/weighted-fallback.js +2 -2
  147. package/dist/transports/weighted-fallback.js.map +3 -3
  148. package/dist/transports/whatsapp-cloud.js +2 -2
  149. package/dist/transports/whatsapp-cloud.js.map +1 -1
  150. package/dist/webhooks/brevo.js +2 -2
  151. package/dist/webhooks/brevo.js.map +1 -1
  152. package/dist/webhooks/mailgun.js +2 -2
  153. package/dist/webhooks/mailgun.js.map +1 -1
  154. package/dist/webhooks/postmark.js +2 -2
  155. package/dist/webhooks/postmark.js.map +1 -1
  156. package/dist/webhooks/resend.js +2 -2
  157. package/dist/webhooks/resend.js.map +1 -1
  158. package/dist/webhooks/sendgrid.js +2 -2
  159. package/dist/webhooks/sendgrid.js.map +1 -1
  160. package/dist/webhooks/ses.js +2 -2
  161. package/dist/webhooks/ses.js.map +1 -1
  162. package/dist/webhooks/sndr.js +2 -2
  163. package/dist/webhooks/sndr.js.map +1 -1
  164. package/dist/webhooks/timing-safe-equal.js +1 -1
  165. package/dist/webhooks/twilio-sms.d.ts +20 -0
  166. package/dist/webhooks/twilio-sms.js +3 -0
  167. package/dist/webhooks/twilio-sms.js.map +10 -0
  168. package/dist/webhooks/types.d.ts +37 -3
  169. package/dist/webhooks/types.js +3 -0
  170. package/dist/webhooks/types.js.map +9 -0
  171. package/dist/webhooks/unifonic.d.ts +13 -0
  172. package/dist/webhooks/unifonic.js +3 -0
  173. package/dist/webhooks/unifonic.js.map +10 -0
  174. package/dist/webhooks/whatsapp-cloud.d.ts +14 -0
  175. package/dist/webhooks/whatsapp-cloud.js +3 -0
  176. package/dist/webhooks/whatsapp-cloud.js.map +10 -0
  177. package/dist/webhooks.d.ts +10 -5
  178. package/dist/webhooks.js +11 -0
  179. package/dist/whatsapp.d.ts +1 -0
  180. package/dist/whatsapp.js +2 -2
  181. package/dist/whatsapp.js.map +3 -3
  182. package/package.json +26 -2
  183. package/site/content/docs/channels/hooks.mdx +6 -5
  184. package/site/content/docs/channels/index.mdx +2 -2
  185. package/site/content/docs/channels/push.mdx +27 -21
  186. package/site/content/docs/channels/sms.mdx +4 -1
  187. package/site/content/docs/decorators/fallback.mdx +41 -14
  188. package/site/content/docs/decorators/index.mdx +29 -12
  189. package/site/content/docs/decorators/retry.mdx +32 -9
  190. package/site/content/docs/decorators/weighted-fallback.mdx +27 -6
  191. package/site/content/docs/get-started/entrypoints.mdx +6 -5
  192. package/site/content/docs/get-started/installation.mdx +3 -2
  193. package/site/content/docs/get-started/introduction.mdx +1 -1
  194. package/site/content/docs/guides/webhooks.mdx +35 -20
  195. package/site/content/docs/index.mdx +1 -1
  196. package/site/content/docs/quick-start/push.mdx +22 -5
  197. package/site/content/docs/reference/channel-result.mdx +82 -0
  198. package/site/content/docs/reference/exports.mdx +5 -3
  199. package/site/content/docs/reference/meta.json +1 -0
  200. package/site/content/docs/reference/push-options.mdx +44 -3
  201. package/site/content/docs/reference/sms-options.mdx +18 -2
  202. package/site/content/docs/reference/webhook-events.mdx +38 -14
  203. package/site/content/docs/reference/whatsapp-options.mdx +19 -1
  204. package/site/content/docs/transports/fcm.mdx +80 -0
  205. package/site/content/docs/transports/index.mdx +3 -1
  206. package/site/content/docs/transports/meta.json +3 -1
  207. package/site/content/docs/transports/unifonic.mdx +85 -0
  208. package/dist/chunk-0gqy32pe.js.map +0 -10
  209. package/dist/chunk-5915vbcj.js +0 -4
  210. package/dist/chunk-5915vbcj.js.map +0 -10
  211. package/dist/chunk-93vqxxj2.js +0 -4
  212. package/dist/chunk-b602dhck.js +0 -4
  213. package/dist/chunk-gp4fwrs8.js +0 -4
  214. package/dist/chunk-gp4fwrs8.js.map +0 -10
  215. package/dist/chunk-n0qeyzqm.js +0 -13
  216. package/dist/chunk-q8k3m6qe.js +0 -4
  217. package/dist/chunk-q8k3m6qe.js.map +0 -10
  218. package/dist/chunk-qtkd5bak.js +0 -4
  219. package/dist/chunk-yp311efr.js +0 -4
@@ -0,0 +1,85 @@
1
+ ---
2
+ title: Unifonic
3
+ description: Send SMS through the Unifonic el.cloud REST API.
4
+ icon: Truck
5
+ source: "src/transports/unifonic.ts"
6
+ ---
7
+
8
+ Send SMS through Unifonic’s el.cloud REST API for transactional texts such as OTPs and alerts.
9
+ Wire the transport into `createSmsSender` so app code stays on the SMS channel API.
10
+
11
+ <Callout title="The one rule">Create this transport under `createSmsSender` — do not call Unifonic from app code directly.</Callout>
12
+
13
+ ## Quick start
14
+
15
+ <Steps>
16
+ <Step title="Configure the transport">
17
+
18
+ ```ts
19
+ import { createSmsSender } from "sently/sms";
20
+ import { UnifonicTransport } from "sently/transports/unifonic";
21
+
22
+ const sender = createSmsSender({
23
+ transport: new UnifonicTransport({
24
+ appSid: process.env.UNIFONIC_APPSID!,
25
+ senderId: "MyBrand",
26
+ }),
27
+ });
28
+ ```
29
+
30
+ </Step>
31
+ <Step title="Send with the channel API">
32
+
33
+ ```ts
34
+ const result = await sender.send({ to: "+9665xxxxxxxx", body: "Hello" });
35
+ console.log(result.messageId, result.status);
36
+ ```
37
+
38
+ </Step>
39
+ </Steps>
40
+
41
+ ## Configuration
42
+
43
+ | Option | Type | Default or requirement |
44
+ | --- | --- | --- |
45
+ | `appSid` | `string` | required (`AppSid`) |
46
+ | `senderId` | `string` | required on send unless `options.from` is set |
47
+ | `statusCallback` | `string` | optional public HTTPS DLR URL |
48
+ | `async` | `boolean` | `false` |
49
+
50
+ `options.from` maps to `SenderID`. `options.messageId` maps to `CorrelationID`.
51
+ Phone numbers are normalized to international digits without `+` or leading `00`.
52
+
53
+ ## Delivery webhooks
54
+
55
+ Parse Unifonic status callbacks with `sently/webhooks/unifonic` into `DeliveryEvent[]`.
56
+
57
+ ```ts
58
+ import { parse } from "sently/webhooks/unifonic";
59
+
60
+ const events = parse(await request.json());
61
+ ```
62
+
63
+ ## Troubleshooting
64
+
65
+ <Accordions>
66
+ <Accordion title="Should I call the Unifonic SDK?">
67
+ No. Use the sently SMS sender; this transport posts to `el.cloud.unifonic.com` for you.
68
+ </Accordion>
69
+ <Accordion title="Why do I get a SenderID error?">
70
+ Set `senderId` on the transport or pass `from` on each `send`.
71
+ </Accordion>
72
+ </Accordions>
73
+
74
+ ## Learn more
75
+
76
+ - [SMS channel](/docs/channels/sms)
77
+ - [Webhooks](/docs/guides/webhooks)
78
+ - [Channel send result](/docs/reference/channel-result)
79
+
80
+ ## Next
81
+
82
+ <Cards>
83
+ <Card title="SMS channel" href="/docs/channels/sms" />
84
+ <Card title="Webhooks" href="/docs/guides/webhooks" />
85
+ </Cards>
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/transports/fallback.ts"],
4
- "sourcesContent": [
5
- "/**\n * @module\n * FallbackTransport — routes through an ordered list of transports,\n * advancing to the next when the current one fails. Use for provider\n * failover: if your primary provider has an outage, the next takes over.\n * Composes with RetryTransport — wrap each entry in RetryTransport to\n * retry within a provider before failing over to the next.\n *\n * @example\n * ```ts\n * import { FallbackTransport } from \"sently/transports/fallback\";\n * import { RetryTransport } from \"sently/transports/retry\";\n * import { ResendTransport } from \"sently/transports/resend\";\n * import { SESTransport } from \"sently/transports/ses\";\n *\n * const transport = new FallbackTransport(\n * [\n * new RetryTransport(new ResendTransport({ apiKey })),\n * new RetryTransport(new SESTransport({ accessKeyId, secretAccessKey })),\n * ],\n * { cooldownMs: 300_000 },\n * );\n * ```\n */\nimport { SentlyError } from \"../core/errors.js\";\nimport { getProviderLabel } from \"../core/provider-label.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\n\n/** One failed provider attempt collected during failover. */\nexport interface FallbackAttempt {\n /** Provider label (e.g. `\"resend\"`, `\"ses\"`). */\n provider: string;\n /** Error thrown by that transport. */\n error: unknown;\n}\n\n/** Per-provider result from {@link FallbackTransport.verifyAll}. */\nexport interface FallbackProviderVerifyResult {\n /** Provider label. */\n provider: string;\n /** Whether this provider verified successfully. */\n ok: boolean;\n /** Human-readable status from the provider verify call. */\n message?: string;\n}\n\n/** Result of verifying every transport in a fallback chain. */\nexport interface FallbackVerifyAllResult {\n /** True when at least one provider in the chain verified successfully. */\n ok: boolean;\n /** Per-provider verify results in chain order. */\n providers: FallbackProviderVerifyResult[];\n}\n\n/** Options for {@link FallbackTransport} failover behavior. */\nexport interface FallbackOptions {\n /**\n * Decide whether a given error should trigger failover to the next\n * transport. Default: fail over on everything EXCEPT permanent client\n * errors (HTTP 400/401/403, SMTP permanent 5xx auth failures), since\n * those will fail identically on every provider.\n */\n shouldFallback?: (error: unknown) => boolean;\n /** Optional callback fired before advancing to the next transport. */\n onFallback?: (failedIndex: number, error: unknown) => void;\n /**\n * After a failed send, skip this provider until the cooldown expires (ms).\n * Prevents hammering a failing provider during an outage.\n */\n cooldownMs?: number;\n /** Injectable clock for cooldown and testing. Default: `Date.now`. */\n now?: () => number;\n /**\n * Shared cooldown state across fallback instances (e.g. {@link WeightedFallbackTransport}).\n * @internal\n */\n cooldownState?: Map<string, number>;\n}\n\n/**\n * Thrown when every transport in the chain fails.\n * {@link FallbackAttempt} entries record which provider failed and why.\n */\nexport class FallbackError extends Error {\n /** All failed attempts in order — which provider failed and why. */\n readonly attempts: FallbackAttempt[];\n\n /** Creates an error summarizing every failed transport attempt. */\n constructor(attempts: FallbackAttempt[]) {\n super(`All ${attempts.length} transports failed`);\n this.name = \"FallbackError\";\n this.attempts = attempts;\n }\n}\n\n/** @deprecated Use {@link getProviderLabel} from `sently/core` patterns. */\nexport const inferProviderLabel = getProviderLabel;\n\nfunction defaultShouldFallback(error: unknown): boolean {\n if (error instanceof SentlyError && error.sentlyCode === \"SMTP_AUTH_FAILED\") {\n return false;\n }\n\n const status = (error as { statusCode?: number })?.statusCode;\n if (typeof status === \"number\") {\n if (status === 400 || status === 401 || status === 403) {\n return false;\n }\n return true;\n }\n\n const code = (error as { code?: number })?.code;\n if (code === 535) {\n return false;\n }\n\n return true;\n}\n\ntype MailerOnFallback = (\n failedProvider: string,\n nextProvider: string,\n error: unknown,\n) => void | Promise<void>;\n\n/**\n * Decorator transport that fails over through an ordered list of transports.\n */\nexport class FallbackTransport implements Transport {\n readonly provider = \"fallback\";\n\n private readonly shouldFallback: (error: unknown) => boolean;\n private readonly onFallback?: FallbackOptions[\"onFallback\"];\n private readonly cooldownMs: number;\n private readonly now: () => number;\n private readonly unhealthyUntil: Map<string, number>;\n private mailerOnFallback: MailerOnFallback | undefined;\n\n /**\n * Creates a failover transport over an ordered list of backends.\n * @throws {Error} When the transports array is empty.\n */\n constructor(\n private readonly transports: Transport[],\n options?: FallbackOptions,\n ) {\n if (transports.length === 0) {\n throw new Error(\"FallbackTransport requires at least one transport\");\n }\n this.shouldFallback = options?.shouldFallback ?? defaultShouldFallback;\n this.onFallback = options?.onFallback;\n this.cooldownMs = options?.cooldownMs ?? 0;\n this.now = options?.now ?? (() => Date.now());\n this.unhealthyUntil = options?.cooldownState ?? new Map<string, number>();\n }\n\n /**\n * Register a per-send onFallback callback from mailer lifecycle hooks.\n * @internal Used by {@link MailerImpl}; cleared after each send.\n */\n setMailerOnFallback(callback: MailerOnFallback | undefined): void {\n this.mailerOnFallback = callback;\n }\n\n private isCoolingDown(provider: string): boolean {\n const until = this.unhealthyUntil.get(provider);\n if (until === undefined) {\n return false;\n }\n if (this.now() >= until) {\n this.unhealthyUntil.delete(provider);\n return false;\n }\n return true;\n }\n\n private markUnhealthy(provider: string): void {\n if (this.cooldownMs > 0) {\n this.unhealthyUntil.set(provider, this.now() + this.cooldownMs);\n }\n }\n\n /** Sends through transports in order until one succeeds. */\n async send(message: MailOptions): Promise<SendResult> {\n const attempts: FallbackAttempt[] = [];\n const skipped: FallbackAttempt[] = [];\n\n for (let i = 0; i < this.transports.length; i++) {\n const transport = this.transports[i] as Transport;\n const provider = getProviderLabel(transport);\n\n if (this.isCoolingDown(provider)) {\n skipped.push({\n provider,\n error: new Error(`Provider ${provider} is in cooldown`),\n });\n continue;\n }\n\n try {\n const result = await transport.send(message);\n return {\n ...result,\n provider,\n providerIndex: i,\n };\n } catch (err) {\n attempts.push({ provider, error: err });\n this.markUnhealthy(provider);\n\n const nextIndex = this.findNextTryIndex(i);\n if (nextIndex === -1) {\n break;\n }\n if (this.shouldFallback(err) === false) {\n throw err;\n }\n\n const nextProvider = getProviderLabel(this.transports[nextIndex] as Transport);\n this.onFallback?.(i, err);\n if (this.mailerOnFallback !== undefined) {\n await this.mailerOnFallback(provider, nextProvider, err);\n }\n }\n }\n\n const allAttempts = [...attempts, ...skipped];\n if (allAttempts.length === 0) {\n throw new FallbackError([\n { provider: \"fallback\", error: new Error(\"All providers are in cooldown\") },\n ]);\n }\n\n throw new FallbackError(allAttempts);\n }\n\n private findNextTryIndex(currentIndex: number): number {\n for (let j = currentIndex + 1; j < this.transports.length; j++) {\n const provider = getProviderLabel(this.transports[j] as Transport);\n if (!this.isCoolingDown(provider)) {\n return j;\n }\n }\n return -1;\n }\n\n /**\n * Returns the result of the first transport whose verify() returns `ok: true`.\n * Use {@link verifyAll} for full-chain visibility.\n */\n async verify(): Promise<VerifyResult> {\n const all = await this.verifyAll();\n const firstOk = all.providers.find((entry) => entry.ok);\n if (firstOk !== undefined) {\n return {\n ok: true,\n provider: firstOk.provider,\n ...(firstOk.message !== undefined ? { message: firstOk.message } : {}),\n };\n }\n\n return {\n ok: false,\n provider: \"fallback\",\n message: \"no transport verified\",\n };\n }\n\n /** Verifies every transport in the chain and returns per-provider results. */\n async verifyAll(): Promise<FallbackVerifyAllResult> {\n const providers: FallbackProviderVerifyResult[] = [];\n\n for (const transport of this.transports) {\n const provider = getProviderLabel(transport);\n if (transport.verify === undefined) {\n providers.push({ provider, ok: false, message: \"no verify() method\" });\n continue;\n }\n\n try {\n const result = await transport.verify();\n providers.push({\n provider,\n ok: result.ok,\n ...(result.message !== undefined ? { message: result.message } : {}),\n });\n } catch (err) {\n providers.push({\n provider,\n ok: false,\n message: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return {\n ok: providers.some((entry) => entry.ok),\n providers,\n };\n }\n\n /** Calls close() on every transport; one failure does not block the others. */\n async close(): Promise<void> {\n await Promise.allSettled(this.transports.map((transport) => transport.close?.()));\n }\n}\n"
6
- ],
7
- "mappings": "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",
8
- "debugId": "833078D22947C91264756E2164756E21",
9
- "names": []
10
- }
@@ -1,4 +0,0 @@
1
- import{N as S}from"./chunk-gp4fwrs8.js";import{O as E}from"./chunk-0qxws3kj.js";import{S as D}from"./chunk-93vqxxj2.js";import{W as z,X as f}from"./chunk-qtkd5bak.js";import{ca as k}from"./chunk-tamww15j.js";function N(j){return typeof j.setMailerOnRetry==="function"}function P(j){return typeof j.setMailerOnFallback==="function"}var v="SMTP config passed to transport-only createMailer. Use: import { createSMTPMailer } from 'sently' or import { createSMTPMailer } from 'sently/smtp'.";async function c(j){if(j.transport===void 0||"host"in j&&j.transport===void 0)throw new k(v,"INVALID_CONFIG");return new M(j.transport,j.plugins??[],j.hooks)}function b(j){return j.attachments!==void 0&&j.attachments.length>0}function y(j){return S(j)}function _(j,K){return{...j.messageId!==void 0?{messageId:j.messageId}:{},to:D(j.to),subject:j.subject,provider:y(K)}}class M{transport;plugins;hooks;constructor(j,K=[],V){this.transport=j;this.plugins=K;this.hooks=V}async send(j){let K=await f(j,this.plugins),V=_(K,this.transport);if(await z(this.hooks?.onSend,V),this.hooks?.onRetry!==void 0&&N(this.transport))this.transport.setMailerOnRetry((w,C)=>{z(this.hooks?.onRetry,V,w,C)});if(this.hooks?.onFallback!==void 0&&P(this.transport))this.transport.setMailerOnFallback((w,C,T)=>{z(this.hooks?.onFallback,V,w,C,T)});let A=performance.now();try{let w=await this.transport.send(K),C={...V,messageId:w.messageId};return await z(this.hooks?.onSuccess,C,w,performance.now()-A),w}catch(w){throw await z(this.hooks?.onError,V,w,performance.now()-A),w}finally{if(N(this.transport))this.transport.setMailerOnRetry(void 0);if(P(this.transport))this.transport.setMailerOnFallback(void 0)}}async processMessage(j){return f(j,this.plugins)}async sendBulk(j,K){if(j.length===0)return{total:0,sent:0,failed:0,results:[]};let V=Array(j.length),A=K?.stopOnError??!1,w=!1,C=async(X,$,B,Q,Z)=>{if(V[$]={status:"sent",result:B},Q!==void 0&&this.hooks!==void 0){let W=_(Q,this.transport);await z(this.hooks.onSend,W),await z(this.hooks.onSuccess,{...W,messageId:B.messageId},B,Z)}K?.onSuccess?.(X,$,B)},T=async(X,$,B,Q,Z)=>{if(V[$]={status:"failed",error:B},Q!==void 0&&this.hooks!==void 0){let W=_(Q,this.transport);await z(this.hooks.onSend,W),await z(this.hooks.onError,W,B,Z)}if(K?.onError?.(X,$,B),A)w=!0};if(this.transport.sendBatch){let X=[],$=[];for(let[B,Q]of j.entries())if(b(Q))$.push({index:B,message:Q});else X.push({index:B,message:Q});if(X.length>0&&!w){let B=this.transport.batchMax??X.length,Q=K?.rateDelta??2,Z=Q>0?new E(Q,K?.rateLimit??1000,K?.now):null;for(let W=0;W<X.length&&!w;W+=B){let U=X.slice(W,W+B);if(Z)await Z.acquire();try{let Y=await Promise.all(U.map(({message:G})=>this.processMessage(G))),J=performance.now(),H=await this.transport.sendBatch(Y),I=performance.now()-J;for(let G=0;G<U.length;G++){let R=U[G],L=Y[G],F=H[G];if(F===void 0)await T(R.message,R.index,Error("Batch response missing result for message"),L,I);else if(F.batchError!==void 0)await T(R.message,R.index,F.batchError,L,I);else await C(R.message,R.index,F,L,I)}}catch(Y){let J=await Promise.all(U.map(({message:H})=>this.processMessage(H)));for(let H=0;H<U.length;H++){if(w)break;let I=U[H];await T(I.message,I.index,Y,J[H])}}}}if($.length>0&&!w){let B=K?.concurrency??1,Q=[...$],Z=0;await new Promise((W)=>{let U=()=>{if(Q.length===0&&Z===0||w)W()},Y=()=>{if(Q.length===0||w){U();return}let J=Q.shift();if(J===void 0){U();return}Z++,this.send(J.message).then((H)=>{V[J.index]={status:"sent",result:H},K?.onSuccess?.(J.message,J.index,H)}).catch((H)=>{if(V[J.index]={status:"failed",error:H},K?.onError?.(J.message,J.index,H),A)w=!0}).finally(()=>{Z--,Y(),U()})};for(let J=0;J<B;J++)Y()})}}else{let X=K?.concurrency??1,$=[...j.entries()],B=0;await new Promise((Q)=>{let Z=()=>{if($.length===0&&B===0||w)Q()},W=()=>{if($.length===0||w){Z();return}let U=$.shift();if(U===void 0){Z();return}let[Y,J]=U;B++,this.send(J).then((H)=>{V[Y]={status:"sent",result:H},K?.onSuccess?.(J,Y,H)}).catch((H)=>{if(V[Y]={status:"failed",error:H},K?.onError?.(J,Y,H),A)w=!0}).finally(()=>{B--,W(),Z()})};for(let U=0;U<X;U++)W()})}let q=0,O=0;for(let X of V){if(X===void 0)continue;if(X.status==="sent")q++;else O++}return{total:j.length,sent:q,failed:O,results:V}}verify(){if(this.transport.verify)return this.transport.verify();return Promise.resolve({ok:!0,provider:"mailer"})}close(){if(this.transport.close)return this.transport.close();return Promise.resolve()}}
2
- export{c as L,M};
3
-
4
- //# debugId=DF7737883683888864756E2164756E21
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/mailer.ts"],
4
- "sourcesContent": [
5
- "/**\n * @module\n * Lightweight mailer factory for custom transports — no SMTP code in the bundle.\n *\n * Sently-first: apps call {@link createMailer}; providers implement `Transport`.\n * Use this entry instead of `sently` when you pass a transport explicitly\n * (Resend, SendGrid, etc.) and want the smallest bundle size.\n *\n * @example\n * ```ts\n * import { createMailer } from \"sently/mailer\";\n * import { ResendTransport } from \"sently/transports/resend\";\n *\n * const mailer = await createMailer({\n * transport: new ResendTransport({ apiKey: process.env.RESEND_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"onboarding@yourdomain.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * html: \"<p>Sent via Resend</p>\",\n * });\n * ```\n */\nimport { extractEmails } from \"./core/address.js\";\nimport { SentlyError } from \"./core/errors.js\";\nimport { invokeHook } from \"./core/hooks.js\";\nimport { runPlugins } from \"./core/plugin.js\";\nimport { getProviderLabel } from \"./core/provider-label.js\";\nimport { RateLimiter } from \"./core/rate-limiter.js\";\nimport type {\n BulkSendOptions,\n BulkSendResult,\n Mailer,\n MailerHookContext,\n MailerHooks,\n MailOptions,\n MailPlugin,\n SendResult,\n Transport,\n TransportMailerOptions,\n VerifyResult,\n} from \"./core/types.js\";\n\nexport type { MailerHookContext, MailerHooks, TransportMailerOptions };\n\n/** Retry decorator wired by mailer hooks (duck-typed to avoid pulling retry into all bundles). */\ninterface RetryHookTransport extends Transport {\n setMailerOnRetry(callback: ((attempt: number, error: unknown) => void) | undefined): void;\n}\n\nfunction isRetryHookTransport(transport: Transport): transport is RetryHookTransport {\n return typeof (transport as RetryHookTransport).setMailerOnRetry === \"function\";\n}\n\n/** Fallback decorator wired by mailer hooks (duck-typed). */\ninterface FallbackHookTransport extends Transport {\n setMailerOnFallback(\n callback: ((failedProvider: string, nextProvider: string, error: unknown) => void) | undefined,\n ): void;\n}\n\nfunction isFallbackHookTransport(transport: Transport): transport is FallbackHookTransport {\n return typeof (transport as FallbackHookTransport).setMailerOnFallback === \"function\";\n}\n\nconst TRANSPORT_ONLY_SMTP_CONFIG_MESSAGE =\n \"SMTP config passed to transport-only createMailer. Use: import { createSMTPMailer } from 'sently' or import { createSMTPMailer } from 'sently/smtp'.\";\n\n/**\n * Create a mailer that wraps a custom {@link Transport} (HTTP API, preview, retry, etc.).\n */\nexport async function createMailer(options: TransportMailerOptions): Promise<Mailer> {\n if (options.transport === undefined || (\"host\" in options && options.transport === undefined)) {\n throw new SentlyError(TRANSPORT_ONLY_SMTP_CONFIG_MESSAGE, \"INVALID_CONFIG\");\n }\n\n return new MailerImpl(options.transport, options.plugins ?? [], options.hooks);\n}\n\nfunction hasAttachments(message: MailOptions): boolean {\n return message.attachments !== undefined && message.attachments.length > 0;\n}\n\n/** Infer a provider label from a transport instance for hook context. */\nfunction inferProvider(transport: Transport): string {\n return getProviderLabel(transport);\n}\n\n/** Build hook context from mail options (no body fields). */\nfunction buildHookContext(options: MailOptions, transport: Transport): MailerHookContext {\n return {\n ...(options.messageId !== undefined ? { messageId: options.messageId } : {}),\n to: extractEmails(options.to),\n subject: options.subject,\n provider: inferProvider(transport),\n };\n}\n\n/** Internal mailer implementation shared with the full `sently` entry. */\nexport class MailerImpl implements Mailer {\n constructor(\n private readonly transport: Transport,\n private readonly plugins: MailPlugin[] = [],\n private readonly hooks?: MailerHooks,\n ) {}\n\n async send(options: MailOptions): Promise<SendResult> {\n const processed = await runPlugins(options, this.plugins);\n const ctx = buildHookContext(processed, this.transport);\n\n await invokeHook(this.hooks?.onSend, ctx);\n\n if (this.hooks?.onRetry !== undefined && isRetryHookTransport(this.transport)) {\n this.transport.setMailerOnRetry((attempt, error) => {\n void invokeHook(this.hooks?.onRetry, ctx, attempt, error);\n });\n }\n\n if (this.hooks?.onFallback !== undefined && isFallbackHookTransport(this.transport)) {\n this.transport.setMailerOnFallback((failedProvider, nextProvider, error) => {\n void invokeHook(this.hooks?.onFallback, ctx, failedProvider, nextProvider, error);\n });\n }\n\n const start = performance.now();\n\n try {\n const result = await this.transport.send(processed);\n const successCtx: MailerHookContext = {\n ...ctx,\n messageId: result.messageId,\n };\n await invokeHook(this.hooks?.onSuccess, successCtx, result, performance.now() - start);\n return result;\n } catch (error) {\n await invokeHook(this.hooks?.onError, ctx, error, performance.now() - start);\n throw error;\n } finally {\n if (isRetryHookTransport(this.transport)) {\n this.transport.setMailerOnRetry(undefined);\n }\n if (isFallbackHookTransport(this.transport)) {\n this.transport.setMailerOnFallback(undefined);\n }\n }\n }\n\n private async processMessage(message: MailOptions): Promise<MailOptions> {\n return runPlugins(message, this.plugins);\n }\n\n async sendBulk(messages: MailOptions[], options?: BulkSendOptions): Promise<BulkSendResult> {\n if (messages.length === 0) {\n return { total: 0, sent: 0, failed: 0, results: [] };\n }\n\n const results: BulkSendResult[\"results\"] = new Array(messages.length);\n const stopOnError = options?.stopOnError ?? false;\n let halted = false;\n\n const recordSuccess = async (\n message: MailOptions,\n index: number,\n result: SendResult,\n processed?: MailOptions,\n durationMs?: number,\n ): Promise<void> => {\n results[index] = { status: \"sent\", result };\n if (processed !== undefined && this.hooks !== undefined) {\n const ctx = buildHookContext(processed, this.transport);\n await invokeHook(this.hooks.onSend, ctx);\n await invokeHook(\n this.hooks.onSuccess,\n { ...ctx, messageId: result.messageId },\n result,\n durationMs,\n );\n }\n options?.onSuccess?.(message, index, result);\n };\n\n const recordFailure = async (\n message: MailOptions,\n index: number,\n error: unknown,\n processed?: MailOptions,\n durationMs?: number,\n ): Promise<void> => {\n results[index] = { status: \"failed\", error };\n if (processed !== undefined && this.hooks !== undefined) {\n const ctx = buildHookContext(processed, this.transport);\n await invokeHook(this.hooks.onSend, ctx);\n await invokeHook(this.hooks.onError, ctx, error, durationMs);\n }\n options?.onError?.(message, index, error);\n if (stopOnError) {\n halted = true;\n }\n };\n\n if (this.transport.sendBatch) {\n const batchEntries: Array<{ index: number; message: MailOptions }> = [];\n const singleEntries: Array<{ index: number; message: MailOptions }> = [];\n\n for (const [index, message] of messages.entries()) {\n if (hasAttachments(message)) {\n singleEntries.push({ index, message });\n } else {\n batchEntries.push({ index, message });\n }\n }\n\n if (batchEntries.length > 0 && !halted) {\n const chunkSize = this.transport.batchMax ?? batchEntries.length;\n const rateDelta = options?.rateDelta ?? 2;\n const rateLimiter =\n rateDelta > 0\n ? new RateLimiter(rateDelta, options?.rateLimit ?? 1000, options?.now)\n : null;\n\n for (\n let chunkStart = 0;\n chunkStart < batchEntries.length && !halted;\n chunkStart += chunkSize\n ) {\n const chunk = batchEntries.slice(chunkStart, chunkStart + chunkSize);\n\n if (rateLimiter) {\n await rateLimiter.acquire();\n }\n\n try {\n const processed = await Promise.all(\n chunk.map(({ message }) => this.processMessage(message)),\n );\n const batchStart = performance.now();\n const batchResults = await this.transport.sendBatch(processed);\n const batchDurationMs = performance.now() - batchStart;\n\n for (let i = 0; i < chunk.length; i++) {\n const entry = chunk[i] as { index: number; message: MailOptions };\n const processedMessage = processed[i] as MailOptions;\n const result = batchResults[i];\n if (result === undefined) {\n await recordFailure(\n entry.message,\n entry.index,\n new Error(\"Batch response missing result for message\"),\n processedMessage,\n batchDurationMs,\n );\n } else if (result.batchError !== undefined) {\n await recordFailure(\n entry.message,\n entry.index,\n result.batchError,\n processedMessage,\n batchDurationMs,\n );\n } else {\n await recordSuccess(\n entry.message,\n entry.index,\n result,\n processedMessage,\n batchDurationMs,\n );\n }\n }\n } catch (error) {\n const processed = await Promise.all(\n chunk.map(({ message }) => this.processMessage(message)),\n );\n for (let i = 0; i < chunk.length; i++) {\n if (halted) {\n break;\n }\n const entry = chunk[i] as { index: number; message: MailOptions };\n await recordFailure(entry.message, entry.index, error, processed[i]);\n }\n }\n }\n }\n\n if (singleEntries.length > 0 && !halted) {\n const concurrency = options?.concurrency ?? 1;\n const queue = [...singleEntries];\n let active = 0;\n\n await new Promise<void>((resolve) => {\n const maybeDone = (): void => {\n if ((queue.length === 0 && active === 0) || halted) {\n resolve();\n }\n };\n\n const processNext = (): void => {\n if (queue.length === 0 || halted) {\n maybeDone();\n return;\n }\n\n const entry = queue.shift();\n if (entry === undefined) {\n maybeDone();\n return;\n }\n\n active++;\n void this.send(entry.message)\n .then((result) => {\n results[entry.index] = { status: \"sent\", result };\n options?.onSuccess?.(entry.message, entry.index, result);\n })\n .catch((error: unknown) => {\n results[entry.index] = { status: \"failed\", error };\n options?.onError?.(entry.message, entry.index, error);\n if (stopOnError) {\n halted = true;\n }\n })\n .finally(() => {\n active--;\n processNext();\n maybeDone();\n });\n };\n\n for (let i = 0; i < concurrency; i++) {\n processNext();\n }\n });\n }\n } else {\n const concurrency = options?.concurrency ?? 1;\n const queue = [...messages.entries()];\n let active = 0;\n\n await new Promise<void>((resolve) => {\n const maybeDone = (): void => {\n if ((queue.length === 0 && active === 0) || halted) {\n resolve();\n }\n };\n\n const processNext = (): void => {\n if (queue.length === 0 || halted) {\n maybeDone();\n return;\n }\n\n const entry = queue.shift();\n if (entry === undefined) {\n maybeDone();\n return;\n }\n\n const [index, message] = entry;\n active++;\n\n void this.send(message)\n .then((result) => {\n results[index] = { status: \"sent\", result };\n options?.onSuccess?.(message, index, result);\n })\n .catch((error: unknown) => {\n results[index] = { status: \"failed\", error };\n options?.onError?.(message, index, error);\n if (stopOnError) {\n halted = true;\n }\n })\n .finally(() => {\n active--;\n processNext();\n maybeDone();\n });\n };\n\n for (let i = 0; i < concurrency; i++) {\n processNext();\n }\n });\n }\n\n let sent = 0;\n let failed = 0;\n for (const result of results) {\n if (result === undefined) {\n continue;\n }\n if (result.status === \"sent\") {\n sent++;\n } else {\n failed++;\n }\n }\n\n return {\n total: messages.length,\n sent,\n failed,\n results,\n };\n }\n\n verify(): Promise<VerifyResult> {\n if (this.transport.verify) {\n return this.transport.verify();\n }\n return Promise.resolve({ ok: true, provider: \"mailer\" });\n }\n\n close(): Promise<void> {\n if (this.transport.close) {\n return this.transport.close();\n }\n return Promise.resolve();\n }\n}\n"
6
- ],
7
- "mappings": "wOAoDA,GAAS,LAAoB,LAAC,JAAuD,CACnF,OAAO,OAAQ,EAAiC,mBAAqB,WAUvE,SAAS,CAAuB,CAAC,EAA0D,CACzF,OAAO,OAAQ,EAAoC,sBAAwB,WAG7E,IAAM,EACJ,uJAKF,eAAsB,CAAY,CAAC,EAAkD,CACnF,GAAI,EAAQ,YAAc,QAAc,SAAU,GAAW,EAAQ,YAAc,OACjF,MAAM,IAAI,EAAY,EAAoC,gBAAgB,EAG5E,OAAO,IAAI,EAAW,EAAQ,UAAW,EAAQ,SAAW,CAAC,EAAG,EAAQ,KAAK,EAG/E,SAAS,CAAc,CAAC,EAA+B,CACrD,OAAO,EAAQ,cAAgB,QAAa,EAAQ,YAAY,OAAS,EAI3E,SAAS,CAAa,CAAC,EAA8B,CACnD,OAAO,EAAiB,CAAS,EAInC,SAAS,CAAgB,CAAC,EAAsB,EAAyC,CACvF,MAAO,IACD,EAAQ,YAAc,OAAY,CAAE,UAAW,EAAQ,SAAU,EAAI,CAAC,EAC1E,GAAI,EAAc,EAAQ,EAAE,EAC5B,QAAS,EAAQ,QACjB,SAAU,EAAc,CAAS,CACnC,EAIK,MAAM,CAA6B,CAErB,UACA,QACA,MAHnB,WAAW,CACQ,EACA,EAAwB,CAAC,EACzB,EACjB,CAHiB,iBACA,eACA,kBAGb,KAAI,CAAC,EAA2C,CACpD,IAAM,EAAY,MAAM,EAAW,EAAS,KAAK,OAAO,EAClD,EAAM,EAAiB,EAAW,KAAK,SAAS,EAItD,GAFA,MAAM,EAAW,KAAK,OAAO,OAAQ,CAAG,EAEpC,KAAK,OAAO,UAAY,QAAa,EAAqB,KAAK,SAAS,EAC1E,KAAK,UAAU,iBAAiB,CAAC,EAAS,IAAU,CAC7C,EAAW,KAAK,OAAO,QAAS,EAAK,EAAS,CAAK,EACzD,EAGH,GAAI,KAAK,OAAO,aAAe,QAAa,EAAwB,KAAK,SAAS,EAChF,KAAK,UAAU,oBAAoB,CAAC,EAAgB,EAAc,IAAU,CACrE,EAAW,KAAK,OAAO,WAAY,EAAK,EAAgB,EAAc,CAAK,EACjF,EAGH,IAAM,EAAQ,YAAY,IAAI,EAE9B,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,UAAU,KAAK,CAAS,EAC5C,EAAgC,IACjC,EACH,UAAW,EAAO,SACpB,EAEA,OADA,MAAM,EAAW,KAAK,OAAO,UAAW,EAAY,EAAQ,YAAY,IAAI,EAAI,CAAK,EAC9E,EACP,MAAO,EAAO,CAEd,MADA,MAAM,EAAW,KAAK,OAAO,QAAS,EAAK,EAAO,YAAY,IAAI,EAAI,CAAK,EACrE,SACN,CACA,GAAI,EAAqB,KAAK,SAAS,EACrC,KAAK,UAAU,iBAAiB,MAAS,EAE3C,GAAI,EAAwB,KAAK,SAAS,EACxC,KAAK,UAAU,oBAAoB,MAAS,QAKpC,eAAc,CAAC,EAA4C,CACvE,OAAO,EAAW,EAAS,KAAK,OAAO,OAGnC,SAAQ,CAAC,EAAyB,EAAoD,CAC1F,GAAI,EAAS,SAAW,EACtB,MAAO,CAAE,MAAO,EAAG,KAAM,EAAG,OAAQ,EAAG,QAAS,CAAC,CAAE,EAGrD,IAAM,EAAyC,MAAM,EAAS,MAAM,EAC9D,EAAc,GAAS,aAAe,GACxC,EAAS,GAEP,EAAgB,MACpB,EACA,EACA,EACA,EACA,IACkB,CAElB,GADA,EAAQ,GAAS,CAAE,OAAQ,OAAQ,QAAO,EACtC,IAAc,QAAa,KAAK,QAAU,OAAW,CACvD,IAAM,EAAM,EAAiB,EAAW,KAAK,SAAS,EACtD,MAAM,EAAW,KAAK,MAAM,OAAQ,CAAG,EACvC,MAAM,EACJ,KAAK,MAAM,UACX,IAAK,EAAK,UAAW,EAAO,SAAU,EACtC,EACA,CACF,EAEF,GAAS,YAAY,EAAS,EAAO,CAAM,GAGvC,EAAgB,MACpB,EACA,EACA,EACA,EACA,IACkB,CAElB,GADA,EAAQ,GAAS,CAAE,OAAQ,SAAU,OAAM,EACvC,IAAc,QAAa,KAAK,QAAU,OAAW,CACvD,IAAM,EAAM,EAAiB,EAAW,KAAK,SAAS,EACtD,MAAM,EAAW,KAAK,MAAM,OAAQ,CAAG,EACvC,MAAM,EAAW,KAAK,MAAM,QAAS,EAAK,EAAO,CAAU,EAG7D,GADA,GAAS,UAAU,EAAS,EAAO,CAAK,EACpC,EACF,EAAS,IAIb,GAAI,KAAK,UAAU,UAAW,CAC5B,IAAM,EAA+D,CAAC,EAChE,EAAgE,CAAC,EAEvE,QAAY,EAAO,KAAY,EAAS,QAAQ,EAC9C,GAAI,EAAe,CAAO,EACxB,EAAc,KAAK,CAAE,QAAO,SAAQ,CAAC,EAErC,OAAa,KAAK,CAAE,QAAO,SAAQ,CAAC,EAIxC,GAAI,EAAa,OAAS,GAAK,CAAC,EAAQ,CACtC,IAAM,EAAY,KAAK,UAAU,UAAY,EAAa,OACpD,EAAY,GAAS,WAAa,EAClC,EACJ,EAAY,EACR,IAAI,EAAY,EAAW,GAAS,WAAa,KAAM,GAAS,GAAG,EACnE,KAEN,QACM,EAAa,EACjB,EAAa,EAAa,QAAU,CAAC,EACrC,GAAc,EACd,CACA,IAAM,EAAQ,EAAa,MAAM,EAAY,EAAa,CAAS,EAEnE,GAAI,EACF,MAAM,EAAY,QAAQ,EAG5B,GAAI,CACF,IAAM,EAAY,MAAM,QAAQ,IAC9B,EAAM,IAAI,EAAG,aAAc,KAAK,eAAe,CAAO,CAAC,CACzD,EACM,EAAa,YAAY,IAAI,EAC7B,EAAe,MAAM,KAAK,UAAU,UAAU,CAAS,EACvD,EAAkB,YAAY,IAAI,EAAI,EAE5C,QAAS,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAM,EAAQ,EAAM,GACd,EAAmB,EAAU,GAC7B,EAAS,EAAa,GAC5B,GAAI,IAAW,OACb,MAAM,EACJ,EAAM,QACN,EAAM,MACF,MAAM,2CAA2C,EACrD,EACA,CACF,EACK,QAAI,EAAO,aAAe,OAC/B,MAAM,EACJ,EAAM,QACN,EAAM,MACN,EAAO,WACP,EACA,CACF,EAEA,WAAM,EACJ,EAAM,QACN,EAAM,MACN,EACA,EACA,CACF,GAGJ,MAAO,EAAO,CACd,IAAM,EAAY,MAAM,QAAQ,IAC9B,EAAM,IAAI,EAAG,aAAc,KAAK,eAAe,CAAO,CAAC,CACzD,EACA,QAAS,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,GAAI,EACF,MAEF,IAAM,EAAQ,EAAM,GACpB,MAAM,EAAc,EAAM,QAAS,EAAM,MAAO,EAAO,EAAU,EAAE,KAM3E,GAAI,EAAc,OAAS,GAAK,CAAC,EAAQ,CACvC,IAAM,EAAc,GAAS,aAAe,EACtC,EAAQ,CAAC,GAAG,CAAa,EAC3B,EAAS,EAEb,MAAM,IAAI,QAAc,CAAC,IAAY,CACnC,IAAM,EAAY,IAAY,CAC5B,GAAK,EAAM,SAAW,GAAK,IAAW,GAAM,EAC1C,EAAQ,GAIN,EAAc,IAAY,CAC9B,GAAI,EAAM,SAAW,GAAK,EAAQ,CAChC,EAAU,EACV,OAGF,IAAM,EAAQ,EAAM,MAAM,EAC1B,GAAI,IAAU,OAAW,CACvB,EAAU,EACV,OAGF,IACK,KAAK,KAAK,EAAM,OAAO,EACzB,KAAK,CAAC,IAAW,CAChB,EAAQ,EAAM,OAAS,CAAE,OAAQ,OAAQ,QAAO,EAChD,GAAS,YAAY,EAAM,QAAS,EAAM,MAAO,CAAM,EACxD,EACA,MAAM,CAAC,IAAmB,CAGzB,GAFA,EAAQ,EAAM,OAAS,CAAE,OAAQ,SAAU,OAAM,EACjD,GAAS,UAAU,EAAM,QAAS,EAAM,MAAO,CAAK,EAChD,EACF,EAAS,GAEZ,EACA,QAAQ,IAAM,CACb,IACA,EAAY,EACZ,EAAU,EACX,GAGL,QAAS,EAAI,EAAG,EAAI,EAAa,IAC/B,EAAY,EAEf,GAEE,KACL,IAAM,EAAc,GAAS,aAAe,EACtC,EAAQ,CAAC,GAAG,EAAS,QAAQ,CAAC,EAChC,EAAS,EAEb,MAAM,IAAI,QAAc,CAAC,IAAY,CACnC,IAAM,EAAY,IAAY,CAC5B,GAAK,EAAM,SAAW,GAAK,IAAW,GAAM,EAC1C,EAAQ,GAIN,EAAc,IAAY,CAC9B,GAAI,EAAM,SAAW,GAAK,EAAQ,CAChC,EAAU,EACV,OAGF,IAAM,EAAQ,EAAM,MAAM,EAC1B,GAAI,IAAU,OAAW,CACvB,EAAU,EACV,OAGF,IAAO,EAAO,GAAW,EACzB,IAEK,KAAK,KAAK,CAAO,EACnB,KAAK,CAAC,IAAW,CAChB,EAAQ,GAAS,CAAE,OAAQ,OAAQ,QAAO,EAC1C,GAAS,YAAY,EAAS,EAAO,CAAM,EAC5C,EACA,MAAM,CAAC,IAAmB,CAGzB,GAFA,EAAQ,GAAS,CAAE,OAAQ,SAAU,OAAM,EAC3C,GAAS,UAAU,EAAS,EAAO,CAAK,EACpC,EACF,EAAS,GAEZ,EACA,QAAQ,IAAM,CACb,IACA,EAAY,EACZ,EAAU,EACX,GAGL,QAAS,EAAI,EAAG,EAAI,EAAa,IAC/B,EAAY,EAEf,EAGH,IAAI,EAAO,EACP,EAAS,EACb,QAAW,KAAU,EAAS,CAC5B,GAAI,IAAW,OACb,SAEF,GAAI,EAAO,SAAW,OACpB,IAEA,SAIJ,MAAO,CACL,MAAO,EAAS,OAChB,OACA,SACA,SACF,EAGF,MAAM,EAA0B,CAC9B,GAAI,KAAK,UAAU,OACjB,OAAO,KAAK,UAAU,OAAO,EAE/B,OAAO,QAAQ,QAAQ,CAAE,GAAI,GAAM,SAAU,QAAS,CAAC,EAGzD,KAAK,EAAkB,CACrB,GAAI,KAAK,UAAU,MACjB,OAAO,KAAK,UAAU,MAAM,EAE9B,OAAO,QAAQ,QAAQ,EAE3B",
8
- "debugId": "DF7737883683888864756E2164756E21",
9
- "names": []
10
- }
@@ -1,4 +0,0 @@
1
- import{_ as V}from"./chunk-ywq6s10g.js";function W(q){for(let E=0;E<q.length;E++){let z=q.charCodeAt(E);if(z<=31||z===127||z===8232||z===8233)return z}return-1}function K(q,E="address"){let z=W(q);if(z!==-1){let G=z.toString(16).padStart(2,"0");throw Error(`Email ${E} contains a forbidden control character (0x${G}). CR, LF, NUL, and other control characters are not allowed.`)}}function X(q){if(Array.isArray(q))return q.flatMap((z)=>X(z));if(typeof q==="object"){if(K(q.address,"address"),q.name!==void 0)K(q.name,"display name");return[{...q}]}K(q,"address");let E=q.trim();if(!E)return[];return Y(E).map(Z)}function $(q){if(K(q.address,"address"),q.name)return K(q.name,"display name"),`${V(q.name)} <${q.address}>`;return q.address}function w(q){return X(q).map((E)=>E.address)}function x(q){if(W(q)!==-1)return!1;return/^[^\s@<>]+@[^\s@<>]+\.[^\s@<>]+$/.test(q)}function Y(q){let E=[],z="",G=!1,J=!1;for(let O=0;O<q.length;O++){let I=q[O]??"";if(I==='"'&&q[O-1]!=="\\"){G=!G,z+=I;continue}if(I==="<"&&!G){J=!0,z+=I;continue}if(I===">"&&!G){J=!1,z+=I;continue}if(I===","&&!G&&!J){if(z.trim())E.push(z.trim());z="";continue}z+=I}if(z.trim())E.push(z.trim());return E}function Z(q){let E=q.trim(),z=E.match(/^(?:"([^"]*)"|([^<]*?))\s*<([^>]+)>$/);if(z){let G=(z[1]??z[2]??"").trim(),J=(z[3]??"").trim();if(G)return{name:G,address:J};return{address:J}}if(E.startsWith('"')&&E.endsWith('"'))return{address:E.slice(1,-1)};return{address:E}}
2
- export{K as P,X as Q,$ as R,w as S,x as T};
3
-
4
- //# debugId=72AC62736D4BEF9164756E2164756E21
@@ -1,4 +0,0 @@
1
- import{Y as D,ba as J}from"./chunk-ywq6s10g.js";var Q="https://oauth2.googleapis.com/token",$="https://login.microsoftonline.com/common/oauth2/v2.0/token",V=30000;class W{config;cachedToken=null;expiresAt=0;refreshPromise=null;constructor(m){if(this.config=m,m.accessToken)this.cachedToken=m.accessToken,this.expiresAt=Date.now()+3600000}async getAccessToken(){if(this.config.getToken)return this.config.getToken();if(this.cachedToken&&Date.now()<this.expiresAt-V)return this.cachedToken;if(!this.refreshPromise)this.refreshPromise=this.refreshAccessToken().finally(()=>{this.refreshPromise=null});return this.refreshPromise}async refreshAccessToken(){if(this.config.getToken){let v=await this.config.getToken();return this.cachedToken=v,this.expiresAt=Date.now()+3600000,v}let m=this.config.tokenUrl??Q,z=new URLSearchParams({client_id:this.config.clientId,client_secret:this.config.clientSecret,refresh_token:this.config.refreshToken,grant_type:"refresh_token"}),q=await fetch(m,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:z.toString()});if(!q.ok){let v=await q.text();throw Error(`OAuth2 token refresh failed (${q.status}): ${v}`)}let C=await q.json();return this.cachedToken=C.access_token,this.expiresAt=Date.now()+C.expires_in*1000,C.access_token}async buildXOAUTH2(){let m=await this.getAccessToken(),z=`user=${this.config.user}\x01auth=Bearer ${m}\x01\x01`;return D(J(z)).replace(/\r\n/g,"")}}
2
- export{Q as r,$ as s,W as t};
3
-
4
- //# debugId=3EE074B9ACEA904764756E2164756E21
@@ -1,4 +0,0 @@
1
- function x(j){if(j.provider!==void 0&&j.provider.length>0)return j.provider;let q=j.constructor.name;if(q==="Object")return"custom";if(q.endsWith("Transport"))return q.slice(0,-9).toLowerCase();return q.toLowerCase()||"unknown"}
2
- export{x as N};
3
-
4
- //# debugId=7281097CE34FD17664756E2164756E21
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/core/provider-label.ts"],
4
- "sourcesContent": [
5
- "import type { Transport } from \"./types.js\";\n\n/**\n * Resolve a stable provider label for observability and diagnostics.\n * Prefers {@link Transport.provider} when set; falls back to constructor name.\n */\nexport function getProviderLabel(transport: Transport): string {\n if (transport.provider !== undefined && transport.provider.length > 0) {\n return transport.provider;\n }\n\n const name = transport.constructor.name;\n if (name === \"Object\") {\n return \"custom\";\n }\n if (name.endsWith(\"Transport\")) {\n return name.slice(0, -\"Transport\".length).toLowerCase();\n }\n return name.toLowerCase() || \"unknown\";\n}\n"
6
- ],
7
- "mappings": "AAMO,SAAS,CAAgB,CAAC,EAA8B,CAC7D,GAAI,EAAU,WAAa,QAAa,EAAU,SAAS,OAAS,EAClE,OAAO,EAAU,SAGnB,IAAM,EAAO,EAAU,YAAY,KACnC,GAAI,IAAS,SACX,MAAO,SAET,GAAI,EAAK,SAAS,WAAW,EAC3B,OAAO,EAAK,MAAM,EAAG,EAAmB,EAAE,YAAY,EAExD,OAAO,EAAK,YAAY,GAAK",
8
- "debugId": "7281097CE34FD17664756E2164756E21",
9
- "names": []
10
- }
@@ -1,13 +0,0 @@
1
- import{Y as O,Z as K,ba as H}from"./chunk-ywq6s10g.js";import{ca as v,ea as B}from"./chunk-tamww15j.js";var j=64;function X(q){return q>>>0}function U(q){let z=g(q),G=1732584193,Q=4023233417,N=2562383102,J=271733878;for(let F=0;F<z.length;F+=64){let $=z.subarray(F,F+64),w=new Uint32Array(16);for(let W=0;W<16;W++){let A=W*4;w[W]=X(($[A]??0)|($[A+1]??0)<<8|($[A+2]??0)<<16|($[A+3]??0)<<24)}let x=G,D=Q,I=N,R=J;for(let W=0;W<64;W++){let A,_;if(W<16)A=X(D&I|~D&R),_=W;else if(W<32)A=X(D&R|I&~R),_=X((5*W+1)%16);else if(W<48)A=X(D^I^R),_=X((3*W+5)%16);else A=X(I^(D|~R)),_=X(7*W%16);let y=R;R=I,I=D,D=X(D+T(X(x+A+X((P[W]??0)+(w[_]??0))),L[W]??0)),x=y}G=X(G+x),Q=X(Q+D),N=X(N+I),J=X(J+R)}let Y=new Uint8Array(16),V=new DataView(Y.buffer);return V.setUint32(0,G,!0),V.setUint32(4,Q,!0),V.setUint32(8,N,!0),V.setUint32(12,J,!0),Y}var L=[7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21],P=new Uint32Array([3614090360,3905402710,606105819,3250441966,4118548399,1200080426,2821735955,4249261313,1770035416,2336552879,4294925233,2304563134,1804603682,4254626195,2792965006,1236535329,4129170786,3225465664,643717713,3921069994,3593408605,38016083,3634488961,3889429448,568446438,3275163606,4107603335,1163531501,2850285829,4243563512,1735328473,2368359562,4294588738,2272392833,1839030562,4259657740,2763975236,1272893353,4139469664,3200236656,681279174,3936430074,3572445317,76029189,3654602809,3873151461,530742520,3299628645,4096336452,1126891415,2878612391,4237533241,1700485571,2399980690,4293915773,2240044497,1873313359,4264355552,2734768916,1309151649,4149444226,3174756917,718787259,3951481745]);function T(q,z){return X(q<<z|q>>>32-z)}function g(q){let z=q.length*8,G=(56-(q.length+1)%64+64)%64,Q=q.length+1+G+8,N=new Uint8Array(Q);N.set(q),N[q.length]=128;let J=new DataView(N.buffer);return J.setUint32(Q-8,z>>>0,!0),J.setUint32(Q-4,Math.floor(z/4294967296),!0),N}function C(q,z){let G=q;if(G.length>j)G=U(G);let Q=new Uint8Array(j);Q.set(G);let N=new Uint8Array(j),J=new Uint8Array(j);for(let $=0;$<j;$++)N[$]=(Q[$]??0)^54,J[$]=(Q[$]??0)^92;let Y=new Uint8Array(N.length+z.length);Y.set(N),Y.set(z,N.length);let V=U(Y),F=new Uint8Array(J.length+V.length);return F.set(J),F.set(V,J.length),U(F)}function E(q){return Array.from(q,(z)=>z.toString(16).padStart(2,"0")).join("")}async function M(q,z,G){let Q=K(q.trim()),N=H(G),J=C(N,Q),Y=E(J);return O(`${z} ${Y}`).replace(/\r\n/g,"")}class Z extends v{command;response;constructor(q,z,G,Q){super(q,B(z,G),{provider:"smtp"});this.command=G;this.response=Q;this.name="SMTPError",this.code=z}}function u(q){let z;switch(q.type){case"EHLO":{z=`EHLO ${q.domain.replace(/[\r\n]/g,"")}`;break}case"STARTTLS":z="STARTTLS";break;case"AUTH_LOGIN":z="AUTH LOGIN";break;case"AUTH_PLAIN":z=`AUTH PLAIN ${O(`\x00${q.user}\x00${q.pass}`).replace(/\r\n/g,"")}`;break;case"AUTH_CRAM_MD5_INIT":z="AUTH CRAM-MD5";break;case"AUTH_CRAM_MD5_RESPONSE":return H(`${q.response}\r
2
- `);case"AUTH_XOAUTH2":z=`AUTH XOAUTH2 ${q.xoauth2String}`;break;case"MAIL_FROM":if(/[\r\n]/.test(q.address))throw new Z("Invalid address: contains CRLF",0,"MAIL FROM",q.address);z=`MAIL FROM:<${q.address}>`;break;case"RCPT_TO":if(/[\r\n]/.test(q.address))throw new Z("Invalid address: contains CRLF",0,"RCPT TO",q.address);z=`RCPT TO:<${q.address}>`;break;case"DATA":z="DATA";break;case"DATA_BODY":return H(S(q.content));case"QUIT":z="QUIT";break;case"RSET":z="RSET";break;case"NOOP":z="NOOP";break}return H(`${z}\r
3
- `)}function l(q){let z=new TextDecoder().decode(q).trim(),G=z.split(/\r?\n/),N=(G[G.length-1]??"").match(/^(\d{3})([\s-])(.*)$/);if(!N)throw new Z("Invalid SMTP response",0,"PARSE",z);let J=Number.parseInt(N[1]??"0",10),Y=G.map((V)=>V.replace(/^\d{3}[\s-]/,"")).join(" ");return{code:J,message:Y,isSuccess:J>=200&&J<300,isReady:J>=300&&J<400,isError:J>=400}}function p(q){if(q.length===0)return null;let z=q.reduce((V,F)=>V+F.length,0),G=new Uint8Array(z),Q=0;for(let V of q)G.set(V,Q),Q+=V.length;let J=new TextDecoder().decode(G).split(/\r?\n/).filter((V)=>V.length>0);if(J.length===0)return null;let Y=J[J.length-1]??"";if(/^\d{3} /.test(Y))return G;return null}function m(q){let z=q.map((G)=>G.toUpperCase());if(z.some((G)=>G.includes("AUTH")&&G.includes("XOAUTH2")))return"OAUTH2";if(z.some((G)=>G.includes("AUTH")&&G.includes("CRAM-MD5")))return"CRAM-MD5";if(z.some((G)=>G.includes("AUTH")&&G.includes("LOGIN")))return"LOGIN";if(z.some((G)=>G.includes("AUTH")&&G.includes("PLAIN")))return"PLAIN";throw new Z("No supported AUTH method",0,"EHLO",q.join(" "))}function i(q){return q.message.split(/\s+/).flatMap((z)=>z.split(/\r?\n/)).filter(Boolean)}function r(q,z,G){if(!z.includes(q.code))throw new Z(`Unexpected SMTP response for ${G}`,q.code,G,q.message)}function S(q){return`${new TextDecoder().decode(q).split(/\r?\n/).map((N)=>N.startsWith(".")?`.${N}`:N).join(`\r
4
- `)}\r
5
- .\r
6
- `}function t(q){return H(`${O(q).replace(/\r\n/g,"")}\r
7
- `)}function o(q){return H(`${O(q).replace(/\r\n/g,"")}\r
8
- `)}function n(q){return H(`${q}\r
9
- `)}function s(q){return H(`${q}\r
10
- `)}
11
- export{M as v,Z as w,u as x,l as y,p as z,m as A,i as B,r as C,t as D,o as E,n as F,s as G};
12
-
13
- //# debugId=E879B8E2491095AF64756E2164756E21
@@ -1,4 +0,0 @@
1
- function j(b,f){return f[b]??"unknown"}function q(b){if(typeof b==="number"){let f=b<1000000000000?b*1000:b,h=new Date(f);return Number.isNaN(h.getTime())?void 0:h}if(typeof b==="string"){let f=new Date(b);return Number.isNaN(f.getTime())?void 0:f}return}function w(b){return Array.isArray(b)?b:[b]}function z(b){let f={provider:b.provider,type:b.type,raw:b.raw};if(b.messageId!==void 0)f.messageId=b.messageId;if(b.recipient!==void 0)f.recipient=b.recipient;if(b.timestamp!==void 0)f.timestamp=b.timestamp;return f}
2
- export{j as d,q as e,w as f,z as g};
3
-
4
- //# debugId=F5B96C321D2447A964756E2164756E21
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/webhooks/types.ts"],
4
- "sourcesContent": [
5
- "/** Normalized email lifecycle event from any provider webhook. */\nexport interface EmailEvent {\n /** Provider identifier (e.g. `\"resend\"`, `\"sendgrid\"`). */\n provider: string;\n /** Normalized event type. */\n type: \"delivered\" | \"bounced\" | \"complained\" | \"opened\" | \"clicked\" | \"deferred\" | \"unknown\";\n /** Provider message identifier when available. */\n messageId?: string;\n /** Recipient email address when available. */\n recipient?: string;\n /** Event timestamp when available. */\n timestamp?: Date;\n /** Original provider payload for debugging or custom handling. */\n raw: unknown;\n}\n\n/** Map a provider-specific event string to a normalized {@link EmailEvent.type}. */\nexport type EventTypeMapper = (event: string) => EmailEvent[\"type\"];\n\n/** Default mapper — returns `\"unknown\"` for unrecognized events. */\nexport function mapEventType(\n event: string,\n mapping: Record<string, EmailEvent[\"type\"]>,\n): EmailEvent[\"type\"] {\n return mapping[event] ?? \"unknown\";\n}\n\n/** Parse an ISO or Unix timestamp into a Date, or undefined when invalid. */\nexport function parseTimestamp(value: unknown): Date | undefined {\n if (typeof value === \"number\") {\n const ms = value < 1_000_000_000_000 ? value * 1000 : value;\n const date = new Date(ms);\n return Number.isNaN(date.getTime()) ? undefined : date;\n }\n if (typeof value === \"string\") {\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? undefined : date;\n }\n return undefined;\n}\n\n/** Ensure payload is an array (for providers that batch events). */\nexport function asArray(payload: unknown): unknown[] {\n return Array.isArray(payload) ? payload : [payload];\n}\n\n/** Build an {@link EmailEvent} respecting exact optional property types. */\nexport function emailEvent(input: {\n provider: string;\n type: EmailEvent[\"type\"];\n raw: unknown;\n messageId?: string | undefined;\n recipient?: string | undefined;\n timestamp?: Date | undefined;\n}): EmailEvent {\n const event: EmailEvent = {\n provider: input.provider,\n type: input.type,\n raw: input.raw,\n };\n\n if (input.messageId !== undefined) {\n event.messageId = input.messageId;\n }\n if (input.recipient !== undefined) {\n event.recipient = input.recipient;\n }\n if (input.timestamp !== undefined) {\n event.timestamp = input.timestamp;\n }\n\n return event;\n}\n"
6
- ],
7
- "mappings": "AAoBO,SAAS,CAAY,CAC1B,EACA,EACoB,CACpB,OAAO,EAAQ,IAAU,UAIpB,SAAS,CAAc,CAAC,EAAkC,CAC/D,GAAI,OAAO,IAAU,SAAU,CAC7B,IAAM,EAAK,EAAQ,cAAoB,EAAQ,KAAO,EAChD,EAAO,IAAI,KAAK,CAAE,EACxB,OAAO,OAAO,MAAM,EAAK,QAAQ,CAAC,EAAI,OAAY,EAEpD,GAAI,OAAO,IAAU,SAAU,CAC7B,IAAM,EAAO,IAAI,KAAK,CAAK,EAC3B,OAAO,OAAO,MAAM,EAAK,QAAQ,CAAC,EAAI,OAAY,EAEpD,OAIK,SAAS,CAAO,CAAC,EAA6B,CACnD,OAAO,MAAM,QAAQ,CAAO,EAAI,EAAU,CAAC,CAAO,EAI7C,SAAS,CAAU,CAAC,EAOZ,CACb,IAAM,EAAoB,CACxB,SAAU,EAAM,SAChB,KAAM,EAAM,KACZ,IAAK,EAAM,GACb,EAEA,GAAI,EAAM,YAAc,OACtB,EAAM,UAAY,EAAM,UAE1B,GAAI,EAAM,YAAc,OACtB,EAAM,UAAY,EAAM,UAE1B,GAAI,EAAM,YAAc,OACtB,EAAM,UAAY,EAAM,UAG1B,OAAO",
8
- "debugId": "F5B96C321D2447A964756E2164756E21",
9
- "names": []
10
- }
@@ -1,4 +0,0 @@
1
- async function t(n,...e){if(n===void 0)return;try{await n(...e)}catch(o){if(!(typeof process<"u"&&process.env?.NODE_ENV==="production"))console.warn("[sently] hook threw; send continues:",o)}}async function i(n,e){if(!e||e.length===0)return n;let o=n;for(let r of e)o=await r(o);return o}
2
- export{t as W,i as X};
3
-
4
- //# debugId=675428B56EEB607264756E2164756E21
@@ -1,4 +0,0 @@
1
- import{$ as K,ba as M}from"./chunk-ywq6s10g.js";var V=["fcm.googleapis.com","android.googleapis.com","updates.push.services.mozilla.com","push.services.mozilla.com","web.push.apple.com"],W=[".push.services.mozilla.com",".notify.windows.com",".push.apple.com"];function g(C,G=[]){let z;try{z=new URL(C)}catch{throw Error("Invalid push subscription endpoint URL")}if(z.protocol!=="https:")throw Error("Push subscription endpoint must use https:");if(z.username||z.password)throw Error("Push subscription endpoint must not include credentials");let J=z.hostname.toLowerCase();if(Z(J))throw Error("Push subscription endpoint must not be an IP address");if(!Y(J,G))throw Error(`Push subscription endpoint host is not allowlisted: ${J}`)}async function j(C){try{let G=new URL(C),z=M(C),J=await crypto.subtle.digest("SHA-256",z.buffer.slice(z.byteOffset,z.byteOffset+z.byteLength)),Q=K(new Uint8Array(J).subarray(0,8));return`${G.origin}/#${Q}`}catch{return"[invalid-endpoint]"}}function Y(C,G){if(V.includes(C)||G.includes(C))return!0;return W.some((z)=>C.endsWith(z)&&C.length>z.length)}function Z(C){if(C.includes(":"))return!0;if(/^\d{1,3}(?:\.\d{1,3}){3}$/u.test(C))return!0;return!1}
2
- export{g as U,j as V};
3
-
4
- //# debugId=6BC361CE3959323764756E2164756E21