sently 0.8.0 → 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.
Files changed (249) hide show
  1. package/AGENTS.md +34 -0
  2. package/CHANGELOG.md +33 -1
  3. package/README.md +89 -960
  4. package/dist/adapters/bun.js +1 -1
  5. package/dist/adapters/cf.js +2 -2
  6. package/dist/adapters/cf.js.map +2 -2
  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/chunk-0gqy32pe.js +4 -0
  12. package/dist/{chunk-n5dan5bz.js.map → chunk-0gqy32pe.js.map} +2 -2
  13. package/dist/{chunk-z62q7kqr.js → chunk-0qxws3kj.js} +2 -2
  14. package/dist/{chunk-z62q7kqr.js.map → chunk-0qxws3kj.js.map} +1 -1
  15. package/dist/chunk-29z7fkzy.js +4 -0
  16. package/dist/{chunk-wsymfbkn.js.map → chunk-29z7fkzy.js.map} +2 -2
  17. package/dist/chunk-5915vbcj.js +4 -0
  18. package/dist/chunk-5915vbcj.js.map +10 -0
  19. package/dist/{chunk-nbmq8ejv.js → chunk-6npp3x3c.js} +2 -2
  20. package/dist/{chunk-nbmq8ejv.js.map → chunk-6npp3x3c.js.map} +1 -1
  21. package/dist/chunk-7gy2q9sh.js +4 -0
  22. package/dist/chunk-7gy2q9sh.js.map +10 -0
  23. package/dist/{chunk-zst8an2p.js → chunk-8dty8c4p.js} +2 -2
  24. package/dist/{chunk-zst8an2p.js.map → chunk-8dty8c4p.js.map} +2 -2
  25. package/dist/{chunk-f3dz43cy.js → chunk-8njyga62.js} +2 -2
  26. package/dist/{chunk-f3dz43cy.js.map → chunk-8njyga62.js.map} +1 -1
  27. package/dist/chunk-93vqxxj2.js +4 -0
  28. package/dist/{chunk-qh90yr6d.js.map → chunk-93vqxxj2.js.map} +2 -2
  29. package/dist/chunk-b602dhck.js +4 -0
  30. package/dist/{chunk-wbvhaqcp.js.map → chunk-b602dhck.js.map} +2 -2
  31. package/dist/{chunk-9zkv5azm.js → chunk-gp4fwrs8.js} +2 -2
  32. package/dist/{chunk-9zkv5azm.js.map → chunk-gp4fwrs8.js.map} +1 -1
  33. package/dist/chunk-n0qeyzqm.js +13 -0
  34. package/dist/{chunk-8phpsns1.js.map → chunk-n0qeyzqm.js.map} +2 -2
  35. package/dist/chunk-qtkd5bak.js +4 -0
  36. package/dist/chunk-qtkd5bak.js.map +11 -0
  37. package/dist/chunk-skqhj0rm.js +4 -0
  38. package/dist/{chunk-vhxzhx8b.js.map → chunk-skqhj0rm.js.map} +2 -2
  39. package/dist/{chunk-6bkj4y4r.js → chunk-tamww15j.js} +2 -2
  40. package/dist/{chunk-6bkj4y4r.js.map → chunk-tamww15j.js.map} +1 -1
  41. package/dist/chunk-vjpds2pf.js +3 -0
  42. package/dist/{chunk-m07smapm.js.map → chunk-vjpds2pf.js.map} +2 -2
  43. package/dist/chunk-yp311efr.js +4 -0
  44. package/dist/chunk-yp311efr.js.map +10 -0
  45. package/dist/chunk-ywq6s10g.js +5 -0
  46. package/dist/{chunk-5scdgffb.js.map → chunk-ywq6s10g.js.map} +3 -3
  47. package/dist/core/base64.d.ts +10 -0
  48. package/dist/core/errors.js +2 -2
  49. package/dist/core/errors.js.map +1 -1
  50. package/dist/core/hooks.d.ts +14 -0
  51. package/dist/core/plugin.d.ts +7 -7
  52. package/dist/core/push-endpoint.d.ts +29 -0
  53. package/dist/core/push-types.d.ts +98 -0
  54. package/dist/core/sms-types.d.ts +76 -0
  55. package/dist/core/smtp.js +2 -2
  56. package/dist/core/smtp.js.map +1 -1
  57. package/dist/core/whatsapp-types.d.ts +116 -0
  58. package/dist/detect.js +2 -2
  59. package/dist/detect.js.map +1 -1
  60. package/dist/dkim.js +2 -2
  61. package/dist/dkim.js.map +2 -2
  62. package/dist/idempotency.js +2 -2
  63. package/dist/idempotency.js.map +1 -1
  64. package/dist/index.d.ts +5 -32
  65. package/dist/index.js +0 -35
  66. package/dist/mailer.js +2 -2
  67. package/dist/mailer.js.map +1 -1
  68. package/dist/observability/console.js +1 -1
  69. package/dist/plugins/react.js +2 -2
  70. package/dist/plugins/react.js.map +2 -2
  71. package/dist/plugins/template.js +1 -1
  72. package/dist/pool/pool.js +2 -2
  73. package/dist/pool/pool.js.map +1 -1
  74. package/dist/push.d.ts +26 -0
  75. package/dist/push.js +3 -0
  76. package/dist/push.js.map +10 -0
  77. package/dist/sms.d.ts +26 -0
  78. package/dist/sms.js +3 -0
  79. package/dist/sms.js.map +10 -0
  80. package/dist/smtp-mailer.js +2 -2
  81. package/dist/smtp-mailer.js.map +2 -2
  82. package/dist/transports/brevo.js +2 -2
  83. package/dist/transports/brevo.js.map +2 -2
  84. package/dist/transports/cloudflare-email.js +2 -2
  85. package/dist/transports/cloudflare-email.js.map +2 -2
  86. package/dist/transports/fallback.js +2 -2
  87. package/dist/transports/fallback.js.map +1 -1
  88. package/dist/transports/loops.js +2 -2
  89. package/dist/transports/loops.js.map +2 -2
  90. package/dist/transports/mailersend.js +2 -2
  91. package/dist/transports/mailersend.js.map +2 -2
  92. package/dist/transports/mailgun.js +2 -2
  93. package/dist/transports/mailgun.js.map +2 -2
  94. package/dist/transports/mailtrap.js +2 -2
  95. package/dist/transports/mailtrap.js.map +2 -2
  96. package/dist/transports/msegat.d.ts +132 -0
  97. package/dist/transports/msegat.js +3 -0
  98. package/dist/transports/msegat.js.map +10 -0
  99. package/dist/transports/plunk.js +2 -2
  100. package/dist/transports/plunk.js.map +2 -2
  101. package/dist/transports/postmark.js +2 -2
  102. package/dist/transports/postmark.js.map +2 -2
  103. package/dist/transports/preview.js +2 -2
  104. package/dist/transports/preview.js.map +2 -2
  105. package/dist/transports/resend.js +2 -2
  106. package/dist/transports/resend.js.map +2 -2
  107. package/dist/transports/retry.js +2 -2
  108. package/dist/transports/retry.js.map +2 -2
  109. package/dist/transports/sendgrid.js +2 -2
  110. package/dist/transports/sendgrid.js.map +2 -2
  111. package/dist/transports/ses.js +2 -2
  112. package/dist/transports/ses.js.map +2 -2
  113. package/dist/transports/smtp.js +2 -2
  114. package/dist/transports/smtp.js.map +1 -1
  115. package/dist/transports/sndr.d.ts +41 -0
  116. package/dist/transports/sndr.js +3 -0
  117. package/dist/transports/sndr.js.map +10 -0
  118. package/dist/transports/sparkpost.js +2 -2
  119. package/dist/transports/sparkpost.js.map +2 -2
  120. package/dist/transports/taqnyat-mail.d.ts +33 -0
  121. package/dist/transports/taqnyat-mail.js +3 -0
  122. package/dist/transports/taqnyat-mail.js.map +10 -0
  123. package/dist/transports/taqnyat-phone.d.ts +4 -0
  124. package/dist/transports/taqnyat-sms.d.ts +131 -0
  125. package/dist/transports/taqnyat-sms.js +3 -0
  126. package/dist/transports/taqnyat-sms.js.map +10 -0
  127. package/dist/transports/taqnyat-whatsapp.d.ts +51 -0
  128. package/dist/transports/taqnyat-whatsapp.js +3 -0
  129. package/dist/transports/taqnyat-whatsapp.js.map +10 -0
  130. package/dist/transports/twilio-sms.d.ts +66 -0
  131. package/dist/transports/twilio-sms.js +3 -0
  132. package/dist/transports/twilio-sms.js.map +10 -0
  133. package/dist/transports/webpush.d.ts +46 -0
  134. package/dist/transports/webpush.js +3 -0
  135. package/dist/transports/webpush.js.map +10 -0
  136. package/dist/transports/weighted-fallback.js +2 -2
  137. package/dist/transports/weighted-fallback.js.map +1 -1
  138. package/dist/transports/whatsapp-cloud.d.ts +56 -0
  139. package/dist/transports/whatsapp-cloud.js +3 -0
  140. package/dist/transports/whatsapp-cloud.js.map +10 -0
  141. package/dist/webhooks/brevo.js +1 -1
  142. package/dist/webhooks/mailgun.js +1 -1
  143. package/dist/webhooks/postmark.js +1 -1
  144. package/dist/webhooks/resend.js +1 -1
  145. package/dist/webhooks/sendgrid.js +1 -1
  146. package/dist/webhooks/ses.js +1 -1
  147. package/dist/webhooks/sndr.d.ts +16 -0
  148. package/dist/webhooks/sndr.js +3 -0
  149. package/dist/webhooks/sndr.js.map +10 -0
  150. package/dist/webhooks/timing-safe-equal.js +1 -1
  151. package/dist/webhooks.d.ts +9 -2
  152. package/dist/webhooks.js +4 -0
  153. package/dist/whatsapp.d.ts +26 -0
  154. package/dist/whatsapp.js +3 -0
  155. package/dist/whatsapp.js.map +10 -0
  156. package/package.json +85 -4
  157. package/site/README.md +26 -0
  158. package/site/content/docs/ai/index.mdx +13 -0
  159. package/site/content/docs/ai/llms-txt.mdx +12 -0
  160. package/site/content/docs/ai/mcp.mdx +16 -0
  161. package/site/content/docs/ai/meta.json +9 -0
  162. package/site/content/docs/channels/email.mdx +100 -0
  163. package/site/content/docs/channels/hooks.mdx +87 -0
  164. package/site/content/docs/channels/index.mdx +81 -0
  165. package/site/content/docs/channels/meta.json +12 -0
  166. package/site/content/docs/channels/push.mdx +92 -0
  167. package/site/content/docs/channels/sms.mdx +88 -0
  168. package/site/content/docs/channels/whatsapp.mdx +95 -0
  169. package/site/content/docs/decorators/fallback.mdx +34 -0
  170. package/site/content/docs/decorators/idempotency.mdx +34 -0
  171. package/site/content/docs/decorators/index.mdx +50 -0
  172. package/site/content/docs/decorators/meta.json +12 -0
  173. package/site/content/docs/decorators/preview.mdx +34 -0
  174. package/site/content/docs/decorators/retry.mdx +34 -0
  175. package/site/content/docs/decorators/weighted-fallback.mdx +34 -0
  176. package/site/content/docs/get-started/entrypoints.mdx +114 -0
  177. package/site/content/docs/get-started/index.mdx +14 -0
  178. package/site/content/docs/get-started/installation.mdx +78 -0
  179. package/site/content/docs/get-started/introduction.mdx +77 -0
  180. package/site/content/docs/get-started/meta.json +12 -0
  181. package/site/content/docs/get-started/migrate-nodemailer.mdx +23 -0
  182. package/site/content/docs/get-started/runtimes.mdx +17 -0
  183. package/site/content/docs/guides/adapters.mdx +26 -0
  184. package/site/content/docs/guides/attachments.mdx +19 -0
  185. package/site/content/docs/guides/bundle-size.mdx +78 -0
  186. package/site/content/docs/guides/dkim.mdx +26 -0
  187. package/site/content/docs/guides/index.mdx +15 -0
  188. package/site/content/docs/guides/meta.json +21 -0
  189. package/site/content/docs/guides/oauth2.mdx +23 -0
  190. package/site/content/docs/guides/observability.mdx +16 -0
  191. package/site/content/docs/guides/plugins-template.mdx +22 -0
  192. package/site/content/docs/guides/pool.mdx +24 -0
  193. package/site/content/docs/guides/react-email.mdx +20 -0
  194. package/site/content/docs/guides/security.mdx +17 -0
  195. package/site/content/docs/guides/send-bulk.mdx +18 -0
  196. package/site/content/docs/guides/vendor-extras-otp.mdx +18 -0
  197. package/site/content/docs/guides/webhooks.mdx +120 -0
  198. package/site/content/docs/guides/webpush-interop.mdx +18 -0
  199. package/site/content/docs/index.mdx +15 -0
  200. package/site/content/docs/meta.json +15 -0
  201. package/site/content/docs/quick-start/email.mdx +40 -0
  202. package/site/content/docs/quick-start/index.mdx +15 -0
  203. package/site/content/docs/quick-start/meta.json +5 -0
  204. package/site/content/docs/quick-start/push.mdx +36 -0
  205. package/site/content/docs/quick-start/sms.mdx +36 -0
  206. package/site/content/docs/quick-start/whatsapp.mdx +38 -0
  207. package/site/content/docs/reference/errors.mdx +22 -0
  208. package/site/content/docs/reference/exports.mdx +83 -0
  209. package/site/content/docs/reference/index.mdx +15 -0
  210. package/site/content/docs/reference/mail-options.mdx +22 -0
  211. package/site/content/docs/reference/meta.json +15 -0
  212. package/site/content/docs/reference/push-options.mdx +20 -0
  213. package/site/content/docs/reference/sms-options.mdx +17 -0
  214. package/site/content/docs/reference/transport-contracts.mdx +17 -0
  215. package/site/content/docs/reference/webhook-events.mdx +70 -0
  216. package/site/content/docs/reference/whatsapp-options.mdx +15 -0
  217. package/site/content/docs/transports/brevo.mdx +40 -0
  218. package/site/content/docs/transports/cloudflare-email.mdx +40 -0
  219. package/site/content/docs/transports/index.mdx +105 -0
  220. package/site/content/docs/transports/loops.mdx +41 -0
  221. package/site/content/docs/transports/mailersend.mdx +40 -0
  222. package/site/content/docs/transports/mailgun.mdx +42 -0
  223. package/site/content/docs/transports/mailtrap.mdx +42 -0
  224. package/site/content/docs/transports/meta.json +32 -0
  225. package/site/content/docs/transports/msegat.mdx +42 -0
  226. package/site/content/docs/transports/plunk.mdx +40 -0
  227. package/site/content/docs/transports/postmark.mdx +40 -0
  228. package/site/content/docs/transports/resend.mdx +43 -0
  229. package/site/content/docs/transports/sendgrid.mdx +40 -0
  230. package/site/content/docs/transports/ses.mdx +44 -0
  231. package/site/content/docs/transports/smtp.mdx +47 -0
  232. package/site/content/docs/transports/sndr.mdx +144 -0
  233. package/site/content/docs/transports/sparkpost.mdx +41 -0
  234. package/site/content/docs/transports/taqnyat-mail.mdx +41 -0
  235. package/site/content/docs/transports/taqnyat-sms.mdx +41 -0
  236. package/site/content/docs/transports/taqnyat-whatsapp.mdx +40 -0
  237. package/site/content/docs/transports/twilio-sms.mdx +128 -0
  238. package/site/content/docs/transports/webpush.mdx +43 -0
  239. package/site/content/docs/transports/whatsapp-cloud.mdx +42 -0
  240. package/dist/chunk-5scdgffb.js +0 -5
  241. package/dist/chunk-8phpsns1.js +0 -13
  242. package/dist/chunk-m07smapm.js +0 -3
  243. package/dist/chunk-n5dan5bz.js +0 -4
  244. package/dist/chunk-qh90yr6d.js +0 -4
  245. package/dist/chunk-vhxzhx8b.js +0 -4
  246. package/dist/chunk-wbvhaqcp.js +0 -4
  247. package/dist/chunk-wsymfbkn.js +0 -4
  248. package/dist/chunk-yr56b0ts.js +0 -4
  249. package/dist/chunk-yr56b0ts.js.map +0 -11
@@ -5,7 +5,7 @@
5
5
  "/**\n * @module\n * Pure-JS HMAC-MD5 implementation for SMTP CRAM-MD5 authentication.\n * Web Crypto does not support MD5, so this is implemented in pure TypeScript\n * with no external dependencies.\n *\n * @example\n * ```ts\n * import { computeCRAMMD5 } from \"sently/core/cram-md5\";\n * const response = await computeCRAMMD5(\"<challenge>\", \"user\", \"pass\");\n * ```\n */\nimport { decodeBase64, encodeBase64, encodeUtf8 } from \"./base64.js\";\n\n/** MD5 block size in bytes (HMAC block size per RFC 2104). */\nconst BLOCK_SIZE = 64;\n\n/** Coerce to unsigned 32-bit integer. */\nfunction u32(x: number): number {\n return x >>> 0;\n}\n\n/**\n * Compute an MD5 hash of the given data (RFC 1321).\n */\nexport function md5(data: Uint8Array): Uint8Array {\n const padded = padMessage(data);\n\n let a0 = 0x67452301;\n let b0 = 0xefcdab89;\n let c0 = 0x98badcfe;\n let d0 = 0x10325476;\n\n for (let i = 0; i < padded.length; i += 64) {\n const block = padded.subarray(i, i + 64);\n const m = new Uint32Array(16);\n for (let j = 0; j < 16; j++) {\n const o = j * 4;\n m[j] = u32(\n (block[o] ?? 0) |\n ((block[o + 1] ?? 0) << 8) |\n ((block[o + 2] ?? 0) << 16) |\n ((block[o + 3] ?? 0) << 24),\n );\n }\n\n let a = a0;\n let b = b0;\n let c = c0;\n let d = d0;\n\n for (let k = 0; k < 64; k++) {\n let f: number;\n let g: number;\n if (k < 16) {\n f = u32((b & c) | (~b & d));\n g = k;\n } else if (k < 32) {\n f = u32((b & d) | (c & ~d));\n g = u32((5 * k + 1) % 16);\n } else if (k < 48) {\n f = u32(b ^ c ^ d);\n g = u32((3 * k + 5) % 16);\n } else {\n f = u32(c ^ (b | ~d));\n g = u32((7 * k) % 16);\n }\n\n const temp = d;\n d = c;\n c = b;\n b = u32(b + leftRotate(u32(a + f + u32((K[k] ?? 0) + (m[g] ?? 0))), S[k] ?? 0));\n a = temp;\n }\n\n a0 = u32(a0 + a);\n b0 = u32(b0 + b);\n c0 = u32(c0 + c);\n d0 = u32(d0 + d);\n }\n\n const out = new Uint8Array(16);\n const view = new DataView(out.buffer);\n view.setUint32(0, a0, true);\n view.setUint32(4, b0, true);\n view.setUint32(8, c0, true);\n view.setUint32(12, d0, true);\n return out;\n}\n\nconst S = [\n 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,\n 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,\n 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,\n];\n\nconst K = new Uint32Array([\n 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,\n 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,\n 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,\n 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,\n 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,\n 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,\n 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,\n 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,\n]);\n\nfunction leftRotate(value: number, shift: number): number {\n return u32((value << shift) | (value >>> (32 - shift)));\n}\n\nfunction padMessage(data: Uint8Array): Uint8Array {\n const bitLen = data.length * 8;\n const padLen = (56 - ((data.length + 1) % 64) + 64) % 64;\n const totalLen = data.length + 1 + padLen + 8;\n const padded = new Uint8Array(totalLen);\n padded.set(data);\n padded[data.length] = 0x80;\n\n const view = new DataView(padded.buffer);\n view.setUint32(totalLen - 8, bitLen >>> 0, true);\n view.setUint32(totalLen - 4, Math.floor(bitLen / 0x100000000), true);\n return padded;\n}\n\n/**\n * Compute HMAC-MD5(key, data) per RFC 2104.\n */\nexport function hmacMD5(key: Uint8Array, data: Uint8Array): Uint8Array {\n let k = key;\n if (k.length > BLOCK_SIZE) {\n k = md5(k);\n }\n const paddedKey = new Uint8Array(BLOCK_SIZE);\n paddedKey.set(k);\n\n const ipad = new Uint8Array(BLOCK_SIZE);\n const opad = new Uint8Array(BLOCK_SIZE);\n for (let i = 0; i < BLOCK_SIZE; i++) {\n ipad[i] = (paddedKey[i] ?? 0) ^ 0x36;\n opad[i] = (paddedKey[i] ?? 0) ^ 0x5c;\n }\n\n const inner = new Uint8Array(ipad.length + data.length);\n inner.set(ipad);\n inner.set(data, ipad.length);\n const innerHash = md5(inner);\n\n const outer = new Uint8Array(opad.length + innerHash.length);\n outer.set(opad);\n outer.set(innerHash, opad.length);\n return md5(outer);\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * Compute the CRAM-MD5 response string for SMTP authentication.\n *\n * @param challenge - base64-encoded challenge from server\n * @param user - SMTP username\n * @param pass - SMTP password\n * @returns base64-encoded CRAM-MD5 response\n */\nexport async function computeCRAMMD5(\n challenge: string,\n user: string,\n pass: string,\n): Promise<string> {\n const challengeBytes = decodeBase64(challenge.trim());\n const passBytes = encodeUtf8(pass);\n const digest = hmacMD5(passBytes, challengeBytes);\n const hex = bytesToHex(digest);\n return encodeBase64(`${user} ${hex}`).replace(/\\r\\n/g, \"\");\n}\n",
6
6
  "// src/core/smtp.ts\nimport { encodeBase64, encodeUtf8 } from \"./base64.js\";\nimport { SentlyError, smtpCodeToSentlyCode } from \"./errors.js\";\n\nexport { computeCRAMMD5 } from \"./cram-md5.js\";\n\n/** SMTP command to send to the server. */\nexport type SMTPCommand =\n | { type: \"EHLO\"; domain: string }\n | { type: \"STARTTLS\" }\n | { type: \"AUTH_LOGIN\"; user: string; pass: string }\n | { type: \"AUTH_PLAIN\"; user: string; pass: string }\n | { type: \"AUTH_CRAM_MD5_INIT\" }\n | { type: \"AUTH_CRAM_MD5_RESPONSE\"; response: string }\n | { type: \"AUTH_XOAUTH2\"; xoauth2String: string }\n | { type: \"MAIL_FROM\"; address: string }\n | { type: \"RCPT_TO\"; address: string }\n | { type: \"DATA\" }\n | { type: \"DATA_BODY\"; content: Uint8Array }\n | { type: \"QUIT\" }\n | { type: \"RSET\" }\n | { type: \"NOOP\" };\n\n/** Parsed SMTP server response. */\nexport interface SMTPResponse {\n /** Three-digit SMTP status code. */\n code: number;\n /** Human-readable response text after the status code. */\n message: string;\n /** True when the code is in the 2xx success range. */\n isSuccess: boolean;\n /** True when the code is 354 (ready for message data). */\n isReady: boolean;\n /** True when the code is 4xx or 5xx. */\n isError: boolean;\n}\n\n/** SMTP protocol error with server response details. */\nexport class SMTPError extends SentlyError {\n /** Creates an SMTP protocol error. */\n constructor(\n message: string,\n smtpCode: number,\n public readonly command: string,\n public readonly response: string,\n ) {\n super(message, smtpCodeToSentlyCode(smtpCode, command), { provider: \"smtp\" });\n this.name = \"SMTPError\";\n this.code = smtpCode;\n }\n}\n\n/**\n * Encode an SMTPCommand into a Uint8Array for sending over the socket.\n */\nexport function encodeCommand(cmd: SMTPCommand): Uint8Array {\n let line: string;\n\n switch (cmd.type) {\n case \"EHLO\": {\n const safeDomain = cmd.domain.replace(/[\\r\\n]/g, \"\");\n line = `EHLO ${safeDomain}`;\n break;\n }\n case \"STARTTLS\":\n line = \"STARTTLS\";\n break;\n case \"AUTH_LOGIN\":\n line = \"AUTH LOGIN\";\n break;\n case \"AUTH_PLAIN\":\n line = `AUTH PLAIN ${encodeBase64(`\\0${cmd.user}\\0${cmd.pass}`).replace(/\\r\\n/g, \"\")}`;\n break;\n case \"AUTH_CRAM_MD5_INIT\":\n line = \"AUTH CRAM-MD5\";\n break;\n case \"AUTH_CRAM_MD5_RESPONSE\":\n return encodeUtf8(`${cmd.response}\\r\\n`);\n case \"AUTH_XOAUTH2\":\n line = `AUTH XOAUTH2 ${cmd.xoauth2String}`;\n break;\n case \"MAIL_FROM\":\n if (/[\\r\\n]/.test(cmd.address)) {\n throw new SMTPError(`Invalid address: contains CRLF`, 0, \"MAIL FROM\", cmd.address);\n }\n line = `MAIL FROM:<${cmd.address}>`;\n break;\n case \"RCPT_TO\":\n if (/[\\r\\n]/.test(cmd.address)) {\n throw new SMTPError(`Invalid address: contains CRLF`, 0, \"RCPT TO\", cmd.address);\n }\n line = `RCPT TO:<${cmd.address}>`;\n break;\n case \"DATA\":\n line = \"DATA\";\n break;\n case \"DATA_BODY\":\n return encodeUtf8(applyDotStuffing(cmd.content));\n case \"QUIT\":\n line = \"QUIT\";\n break;\n case \"RSET\":\n line = \"RSET\";\n break;\n case \"NOOP\":\n line = \"NOOP\";\n break;\n }\n\n return encodeUtf8(`${line}\\r\\n`);\n}\n\n/**\n * Parse raw bytes from the server into an SMTPResponse.\n */\nexport function parseResponse(data: Uint8Array): SMTPResponse {\n const text = new TextDecoder().decode(data).trim();\n const lines = text.split(/\\r?\\n/);\n const lastLine = lines[lines.length - 1] ?? \"\";\n const match = lastLine.match(/^(\\d{3})([\\s-])(.*)$/);\n\n if (!match) {\n throw new SMTPError(\"Invalid SMTP response\", 0, \"PARSE\", text);\n }\n\n const code = Number.parseInt(match[1] ?? \"0\", 10);\n const message = lines.map((l) => l.replace(/^\\d{3}[\\s-]/, \"\")).join(\" \");\n\n return {\n code,\n message,\n isSuccess: code >= 200 && code < 300,\n isReady: code >= 300 && code < 400,\n isError: code >= 400,\n };\n}\n\n/**\n * Accumulate byte chunks until a complete SMTP response is received.\n */\nexport function accumulateResponse(chunks: Uint8Array[]): Uint8Array | null {\n if (chunks.length === 0) {\n return null;\n }\n\n const total = chunks.reduce((sum, c) => sum + c.length, 0);\n const combined = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n\n const text = new TextDecoder().decode(combined);\n const lines = text.split(/\\r?\\n/).filter((l) => l.length > 0);\n\n if (lines.length === 0) {\n return null;\n }\n\n const lastLine = lines[lines.length - 1] ?? \"\";\n if (/^\\d{3} /.test(lastLine)) {\n return combined;\n }\n\n return null;\n}\n\n/**\n * Select the best AUTH method from EHLO capability lines.\n * Priority: XOAUTH2 > CRAM-MD5 > LOGIN > PLAIN.\n */\nexport function selectAuthMethod(\n capabilities: string[],\n): \"LOGIN\" | \"PLAIN\" | \"CRAM-MD5\" | \"OAUTH2\" {\n const upper = capabilities.map((c) => c.toUpperCase());\n if (upper.some((c) => c.includes(\"AUTH\") && c.includes(\"XOAUTH2\"))) {\n return \"OAUTH2\";\n }\n if (upper.some((c) => c.includes(\"AUTH\") && c.includes(\"CRAM-MD5\"))) {\n return \"CRAM-MD5\";\n }\n if (upper.some((c) => c.includes(\"AUTH\") && c.includes(\"LOGIN\"))) {\n return \"LOGIN\";\n }\n if (upper.some((c) => c.includes(\"AUTH\") && c.includes(\"PLAIN\"))) {\n return \"PLAIN\";\n }\n throw new SMTPError(\"No supported AUTH method\", 0, \"EHLO\", capabilities.join(\" \"));\n}\n\n/**\n * Parse an EHLO multi-line response and extract capability keywords.\n */\nexport function parseEHLO(response: SMTPResponse): string[] {\n return response.message\n .split(/\\s+/)\n .flatMap((part) => part.split(/\\r?\\n/))\n .filter(Boolean);\n}\n\n/**\n * Assert that an SMTPResponse code is within the expected set.\n */\nexport function assertResponse(\n response: SMTPResponse,\n expectedCodes: number[],\n command: string,\n): void {\n if (!expectedCodes.includes(response.code)) {\n throw new SMTPError(\n `Unexpected SMTP response for ${command}`,\n response.code,\n command,\n response.message,\n );\n }\n}\n\nfunction applyDotStuffing(content: Uint8Array): string {\n const text = new TextDecoder().decode(content);\n const lines = text.split(/\\r?\\n/);\n const stuffed = lines.map((line) => (line.startsWith(\".\") ? `.${line}` : line));\n return `${stuffed.join(\"\\r\\n\")}\\r\\n.\\r\\n`;\n}\n\n/** Encode AUTH LOGIN password step (second base64 chunk). */\nexport function encodeAuthLoginPass(pass: string): Uint8Array {\n return encodeUtf8(`${encodeBase64(pass).replace(/\\r\\n/g, \"\")}\\r\\n`);\n}\n\n/** Encode AUTH LOGIN user step when sent separately after 334. */\nexport function encodeAuthLoginUser(user: string): Uint8Array {\n return encodeUtf8(`${encodeBase64(user).replace(/\\r\\n/g, \"\")}\\r\\n`);\n}\n\n/** Encode CRAM-MD5 response after challenge. */\nexport function encodeAuthCramResponse(response: string): Uint8Array {\n return encodeUtf8(`${response}\\r\\n`);\n}\n\n/** Encode raw SMTP line with CRLF. */\nexport function encodeLine(line: string): Uint8Array {\n return encodeUtf8(`${line}\\r\\n`);\n}\n"
7
7
  ],
8
- "mappings": "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",
9
- "debugId": "B24DD712C817A87F64756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "E879B8E2491095AF64756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -0,0 +1,4 @@
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
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/core/hooks.ts", "../src/core/plugin.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @module\n * Shared lifecycle hook invocation for mailer and channel senders.\n * Hook failures are swallowed so they never break the send path.\n */\n\n/**\n * Invoke a lifecycle hook without letting hook failures break the send.\n * In non-production environments, hook errors are logged with `console.warn`.\n *\n * @typeParam T - Hook argument tuple types.\n * @param hook - Optional sync or async hook callback.\n * @param args - Arguments forwarded to the hook.\n */\nexport async function invokeHook<T extends unknown[]>(\n hook: ((...args: T) => void | Promise<void>) | undefined,\n ...args: T\n): Promise<void> {\n if (hook === undefined) {\n return;\n }\n try {\n await hook(...args);\n } catch (hookError) {\n const isProduction = typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\";\n if (!isProduction) {\n console.warn(\"[sently] hook threw; send continues:\", hookError);\n }\n }\n}\n",
6
+ "/**\n * @module\n * Plugin pipeline for sently channel senders.\n * Plugins transform channel options before the transport sends.\n * They run sequentially — each receives the previous plugin's output.\n *\n * @example\n * ```ts\n * import { runPlugins } from \"sently/core/plugin\";\n * const result = await runPlugins(options, [pluginA, pluginB]);\n * ```\n */\n\n/**\n * Run a list of plugins sequentially over channel options.\n * If plugins is empty or undefined, returns options unchanged.\n * Each plugin may be sync or async.\n *\n * @typeParam T - Channel options type (e.g. MailOptions, SmsOptions).\n * @param options - the original options\n * @param plugins - ordered list of plugins to apply\n * @returns transformed options after all plugins have run\n */\nexport async function runPlugins<T>(\n options: T,\n plugins: Array<(o: T) => T | Promise<T>> | undefined,\n): Promise<T> {\n if (!plugins || plugins.length === 0) {\n return options;\n }\n\n let current = options;\n for (const plugin of plugins) {\n current = await plugin(current);\n }\n return current;\n}\n"
7
+ ],
8
+ "mappings": "AAcA,eAAsB,CAA+B,CACnD,KACG,EACY,CACf,GAAI,IAAS,OACX,OAEF,GAAI,CACF,MAAM,EAAK,GAAG,CAAI,EAClB,MAAO,EAAW,CAElB,GAAI,EADiB,OAAO,QAAY,KAAe,QAAQ,KAAK,WAAa,cAE/E,QAAQ,KAAK,uCAAwC,CAAS,GCHpE,eAAsB,CAAa,CACjC,EACA,EACY,CACZ,GAAI,CAAC,GAAW,EAAQ,SAAW,EACjC,OAAO,EAGT,IAAI,EAAU,EACd,QAAW,KAAU,EACnB,EAAU,MAAM,EAAO,CAAO,EAEhC,OAAO",
9
+ "debugId": "675428B56EEB607264756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,4 @@
1
+ import{fa as C}from"./chunk-6npp3x3c.js";async function K(D,y){let E=D??[],j=[];for(let b of E){if(b.content instanceof Uint8Array){j.push(b);continue}if(b.path){let z;try{z=await import("node:fs/promises")}catch{throw Error("attachment.path is not supported on this runtime — use attachment.content (Uint8Array) instead")}if(y?.basePath){let{resolve:A,sep:H}=await import("node:path"),k=A(b.path),w=A(y.basePath);if(!(k===w||k.startsWith(w+H)))throw Error(`[sently] Attachment path "${k}" escapes basePath "${w}". Use absolute paths within the allowed directory.`)}let F=await z.readFile(b.path),{path:I,...G}=b;j.push({...G,content:new Uint8Array(F)});continue}if(typeof b.content==="string"){j.push(b);continue}j.push(b)}return j}
2
+ export{K as I};
3
+
4
+ //# debugId=B9395DEA67ACA6C864756E2164756E21
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "import type { Attachment } from \"../core/types.js\";\n\n/** Options for {@link resolveAttachments}. */\nexport interface ResolveAttachmentsOptions {\n /**\n * If set, attachment paths must resolve within this directory.\n * Prevents path traversal via `..` segments and sibling-directory\n * prefix matches. Opt-in only.\n *\n * Note: this check uses `node:path` `resolve()`, which does NOT\n * dereference symlinks. A symlink located inside `basePath` that\n * points outside of it will pass this check. If symlink traversal is\n * a concern, resolve paths with `fs.realpath()` before passing them in.\n */\n basePath?: string;\n}\n\n/**\n * Resolve attachment.path to in-memory Uint8Array content.\n * @throws When attachment.path is used on runtimes without node:fs/promises\n */\nexport async function resolveAttachments(\n attachments: Attachment[] | undefined,\n options?: ResolveAttachmentsOptions,\n): Promise<Attachment[]> {\n const list = attachments ?? [];\n const resolved: Attachment[] = [];\n\n for (const attachment of list) {\n if (attachment.content instanceof Uint8Array) {\n resolved.push(attachment);\n continue;\n }\n\n if (attachment.path) {\n let fs: typeof import(\"node:fs/promises\");\n try {\n fs = await import(\"node:fs/promises\");\n } catch {\n throw new Error(\n \"attachment.path is not supported on this runtime — use attachment.content (Uint8Array) instead\",\n );\n }\n\n if (options?.basePath) {\n const { resolve, sep } = await import(\"node:path\");\n const resolvedPath = resolve(attachment.path);\n const resolvedBase = resolve(options.basePath);\n // startsWith alone is vulnerable: \"/var/data-secret\" passes \"/var/data\".\n // Require an exact match or a trailing path separator.\n const isWithin =\n resolvedPath === resolvedBase || resolvedPath.startsWith(resolvedBase + sep);\n if (!isWithin) {\n throw new Error(\n `[sently] Attachment path \"${resolvedPath}\" escapes basePath \"${resolvedBase}\". ` +\n \"Use absolute paths within the allowed directory.\",\n );\n }\n }\n\n const data = await fs.readFile(attachment.path);\n const { path: _path, ...rest } = attachment;\n resolved.push({ ...rest, content: new Uint8Array(data) });\n continue;\n }\n\n if (typeof attachment.content === \"string\") {\n resolved.push(attachment);\n continue;\n }\n\n resolved.push(attachment);\n }\n\n return resolved;\n}\n"
6
6
  ],
7
- "mappings": "wCAqBA,eAAsB,CAAkB,CACtC,EACA,EACuB,CACvB,IAAM,EAAO,GAAe,CAAC,EACvB,EAAyB,CAAC,EAEhC,QAAW,KAAc,EAAM,CAC7B,GAAI,EAAW,mBAAmB,WAAY,CAC5C,EAAS,KAAK,CAAU,EACxB,SAGF,GAAI,EAAW,KAAM,CACnB,IAAI,EACJ,GAAI,CACF,EAAK,KAAa,4BAClB,KAAM,CACN,MAAU,MACR,gGACF,EAGF,GAAI,GAAS,SAAU,CACrB,IAAQ,UAAS,OAAQ,KAAa,qBAChC,EAAe,EAAQ,EAAW,IAAI,EACtC,EAAe,EAAQ,EAAQ,QAAQ,EAK7C,GAAI,EADF,IAAiB,GAAgB,EAAa,WAAW,EAAe,CAAG,GAE3E,MAAU,MACR,6BAA6B,wBAAmC,sDAElE,EAIJ,IAAM,EAAO,MAAM,EAAG,SAAS,EAAW,IAAI,GACtC,KAAM,KAAU,GAAS,EACjC,EAAS,KAAK,IAAK,EAAM,QAAS,IAAI,WAAW,CAAI,CAAE,CAAC,EACxD,SAGF,GAAI,OAAO,EAAW,UAAY,SAAU,CAC1C,EAAS,KAAK,CAAU,EACxB,SAGF,EAAS,KAAK,CAAU,EAG1B,OAAO",
8
- "debugId": "3442FCDDB36F8C9264756E2164756E21",
7
+ "mappings": "yCAqBA,eAAsB,CAAkB,CACtC,EACA,EACuB,CACvB,IAAM,EAAO,GAAe,CAAC,EACvB,EAAyB,CAAC,EAEhC,QAAW,KAAc,EAAM,CAC7B,GAAI,EAAW,mBAAmB,WAAY,CAC5C,EAAS,KAAK,CAAU,EACxB,SAGF,GAAI,EAAW,KAAM,CACnB,IAAI,EACJ,GAAI,CACF,EAAK,KAAa,4BAClB,KAAM,CACN,MAAU,MACR,gGACF,EAGF,GAAI,GAAS,SAAU,CACrB,IAAQ,UAAS,OAAQ,KAAa,qBAChC,EAAe,EAAQ,EAAW,IAAI,EACtC,EAAe,EAAQ,EAAQ,QAAQ,EAK7C,GAAI,EADF,IAAiB,GAAgB,EAAa,WAAW,EAAe,CAAG,GAE3E,MAAU,MACR,6BAA6B,wBAAmC,sDAElE,EAIJ,IAAM,EAAO,MAAM,EAAG,SAAS,EAAW,IAAI,GACtC,KAAM,KAAU,GAAS,EACjC,EAAS,KAAK,IAAK,EAAM,QAAS,IAAI,WAAW,CAAI,CAAE,CAAC,EACxD,SAGF,GAAI,OAAO,EAAW,UAAY,SAAU,CAC1C,EAAS,KAAK,CAAU,EACxB,SAGF,EAAS,KAAK,CAAU,EAG1B,OAAO",
8
+ "debugId": "B9395DEA67ACA6C864756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,4 +1,4 @@
1
1
  class v extends Error{sentlyCode;statusCode;provider;code;constructor(j,k,q){super(j,q?.cause!==void 0?{cause:q.cause}:void 0);if(this.name="SentlyError",this.sentlyCode=k,this.code=k,q?.statusCode!==void 0)this.statusCode=q.statusCode;if(q?.provider!==void 0)this.provider=q.provider}}function w(j){if(j===429)return"RATE_LIMITED";if(j>=500)return"PROVIDER_ERROR";if(j>=400)return"BAD_REQUEST";return"PROVIDER_ERROR"}function x(j,k){if(j===535||j===534)return"SMTP_AUTH_FAILED";if(j===421||j===450||j===451)return"TIMEOUT";if(j===0){if(k==="CONNECT"||k==="READ"||k==="MX")return"CONNECTION_FAILED";return"PROVIDER_ERROR"}if(j>=500)return"PROVIDER_ERROR";if(j>=400)return"BAD_REQUEST";return"PROVIDER_ERROR"}
2
- export{v as X,w as Y,x as Z};
2
+ export{v as ca,w as da,x as ea};
3
3
 
4
- //# debugId=894390EEDA2D4BE364756E2164756E21
4
+ //# debugId=A59A134E1BF4550C64756E2164756E21
@@ -5,6 +5,6 @@
5
5
  "/**\n * @module\n * Unified error hierarchy for sently transports and SMTP.\n */\n\n/** Stable machine-readable error codes shared across providers. */\nexport type SentlyErrorCode =\n | \"INVALID_CONFIG\"\n | \"SMTP_AUTH_FAILED\"\n | \"RATE_LIMITED\"\n | \"TIMEOUT\"\n | \"CONNECTION_FAILED\"\n | \"PROVIDER_ERROR\"\n | \"BAD_REQUEST\";\n\n/** Options for constructing a {@link SentlyError}. */\nexport interface SentlyErrorOptions {\n /** HTTP status code when the failure originated from an HTTP transport. */\n statusCode?: number;\n /** Original error or response payload. */\n cause?: unknown;\n /** Provider identifier (e.g. `\"smtp\"`, `\"resend\"`). */\n provider?: string;\n}\n\n/**\n * Base error class for all sently transport and protocol failures.\n *\n * Subclasses may shadow {@link code} with provider-specific or numeric values\n * (e.g. SMTP response codes). Use {@link sentlyCode} for the stable machine-readable\n * code in those cases.\n */\nexport class SentlyError extends Error {\n /**\n * Stable machine-readable error code.\n * Preserved even when a subclass shadows {@link code}.\n */\n readonly sentlyCode: SentlyErrorCode;\n\n /** HTTP status code when applicable. */\n readonly statusCode?: number;\n\n /** Provider identifier when applicable. */\n readonly provider?: string;\n\n /**\n * Error code — machine-readable string on {@link SentlyError}, or a legacy\n * provider/SMTP-specific value on subclasses (numeric SMTP codes, Brevo/SES API codes).\n */\n code: string | number;\n\n /** Creates a sently error with a machine-readable code and optional metadata. */\n constructor(message: string, sentlyCode: SentlyErrorCode, options?: SentlyErrorOptions) {\n super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = \"SentlyError\";\n this.sentlyCode = sentlyCode;\n this.code = sentlyCode;\n if (options?.statusCode !== undefined) {\n this.statusCode = options.statusCode;\n }\n if (options?.provider !== undefined) {\n this.provider = options.provider;\n }\n }\n}\n\n/**\n * Map an HTTP status code to a stable sently error code.\n *\n * @param status - HTTP response status code\n * @returns Machine-readable error code\n */\nexport function httpStatusToSentlyCode(status: number): SentlyErrorCode {\n if (status === 429) {\n return \"RATE_LIMITED\";\n }\n if (status >= 500) {\n return \"PROVIDER_ERROR\";\n }\n if (status >= 400) {\n return \"BAD_REQUEST\";\n }\n return \"PROVIDER_ERROR\";\n}\n\n/**\n * Map an SMTP response code and command to a stable sently error code.\n *\n * @param smtpCode - Three-digit SMTP response code (0 for local/parse errors)\n * @param command - SMTP command that failed\n * @returns Machine-readable error code\n */\nexport function smtpCodeToSentlyCode(smtpCode: number, command: string): SentlyErrorCode {\n if (smtpCode === 535 || smtpCode === 534) {\n return \"SMTP_AUTH_FAILED\";\n }\n if (smtpCode === 421 || smtpCode === 450 || smtpCode === 451) {\n return \"TIMEOUT\";\n }\n if (smtpCode === 0) {\n if (command === \"CONNECT\" || command === \"READ\" || command === \"MX\") {\n return \"CONNECTION_FAILED\";\n }\n return \"PROVIDER_ERROR\";\n }\n if (smtpCode >= 500) {\n return \"PROVIDER_ERROR\";\n }\n if (smtpCode >= 400) {\n return \"BAD_REQUEST\";\n }\n return \"PROVIDER_ERROR\";\n}\n"
6
6
  ],
7
7
  "mappings": "AAgCO,MAAM,UAAoB,KAAM,CAK5B,WAGA,WAGA,SAMT,KAGA,WAAW,CAAC,EAAiB,EAA6B,EAA8B,CACtF,MAAM,EAAS,GAAS,QAAU,OAAY,CAAE,MAAO,EAAQ,KAAM,EAAI,MAAS,EAIlF,GAHA,KAAK,KAAO,cACZ,KAAK,WAAa,EAClB,KAAK,KAAO,EACR,GAAS,aAAe,OAC1B,KAAK,WAAa,EAAQ,WAE5B,GAAI,GAAS,WAAa,OACxB,KAAK,SAAW,EAAQ,SAG9B,CAQO,SAAS,CAAsB,CAAC,EAAiC,CACtE,GAAI,IAAW,IACb,MAAO,eAET,GAAI,GAAU,IACZ,MAAO,iBAET,GAAI,GAAU,IACZ,MAAO,cAET,MAAO,iBAUF,SAAS,CAAoB,CAAC,EAAkB,EAAkC,CACvF,GAAI,IAAa,KAAO,IAAa,IACnC,MAAO,mBAET,GAAI,IAAa,KAAO,IAAa,KAAO,IAAa,IACvD,MAAO,UAET,GAAI,IAAa,EAAG,CAClB,GAAI,IAAY,WAAa,IAAY,QAAU,IAAY,KAC7D,MAAO,oBAET,MAAO,iBAET,GAAI,GAAY,IACd,MAAO,iBAET,GAAI,GAAY,IACd,MAAO,cAET,MAAO",
8
- "debugId": "894390EEDA2D4BE364756E2164756E21",
8
+ "debugId": "A59A134E1BF4550C64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -0,0 +1,3 @@
1
+ import{t as U}from"./chunk-b602dhck.js";import{u as X}from"./chunk-8dty8c4p.js";import{A as L,B as v,C as N,D as A,E as j,G as E,v as k,w as $,x as Z,y as O,z as q}from"./chunk-n0qeyzqm.js";import{I as b}from"./chunk-skqhj0rm.js";import{fa as C}from"./chunk-6npp3x3c.js";class T{provider="smtp";config;adapter=null;constructor(z){this.config=P(z)}async send(z){let B={...z,attachments:await b(z.attachments)},G=await X(B,this.config.dkim),F=await this.getAdapter(),W=this.config.direct?await M(G.envelope.from.split("@")[1]??this.config.host):this.config.host;await F.connect(W,this.config.port),this.adapter=F;try{return await D(F,this.config),await S(F,G)}finally{await H(F),this.adapter=null}}async verify(){try{let z=await this.getAdapter();await z.connect(this.config.host,this.config.port);try{return await D(z,this.config),{ok:!0,provider:"smtp"}}finally{await H(z)}}catch(z){return{ok:!1,provider:"smtp",message:z instanceof Error?z.message:String(z)}}}async close(){if(this.adapter)await this.adapter.close(),this.adapter=null}async getAdapter(){if(!this.config.adapter)throw new $("No socket adapter configured",0,"CONNECT","");return this.config.adapter}}function P(z){let B=z.secure??!1;return{host:z.host,port:z.port??(B?465:587),secure:B,...z.auth!==void 0?{auth:z.auth}:{},...z.requireTLS!==void 0?{requireTLS:z.requireTLS}:{},...z.dkim!==void 0?{dkim:z.dkim}:{},...z.tls!==void 0?{tls:z.tls}:{},...z.connectionTimeout!==void 0?{connectionTimeout:z.connectionTimeout}:{},...z.greetingTimeout!==void 0?{greetingTimeout:z.greetingTimeout}:{},...z.socketTimeout!==void 0?{socketTimeout:z.socketTimeout}:{},...z.direct!==void 0?{direct:z.direct}:{},...z.adapter!==void 0?{adapter:z.adapter}:{}}}async function D(z,B){let G=await J(z);N(G,[220],"greeting");let F=await I(z,B.host),W=F.some((Q)=>Q.toUpperCase()==="STARTTLS");if(!B.secure&&!z.secure&&W){await Y(z,Z({type:"STARTTLS"}));let Q=await J(z);N(Q,[220],"STARTTLS"),await z.startTLS(B.tls),F=await I(z,B.host)}if(B.auth){let Q=B.requireTLS??!0,V=z.secure||B.secure;if(Q&&!V)throw new $("Refusing to authenticate over unencrypted connection. Set requireTLS: false to disable this check (not recommended).",0,"AUTH","");await l(z,B.auth,F)}}async function S(z,B){await _(z,{type:"MAIL_FROM",address:B.envelope.from});let G=await J(z);N(G,[250],"MAIL FROM");let F=[],W=[];for(let K of B.envelope.to)if(await Y(z,Z({type:"RCPT_TO",address:K})),(await J(z)).isSuccess)F.push(K);else W.push(K);await _(z,{type:"DATA"});let Q=await J(z);N(Q,[354],"DATA");let V;try{await Y(z,Z({type:"DATA_BODY",content:B.raw})),V=await J(z)}catch(K){if(await Y(z,Z({type:"DATA_BODY",content:B.raw})),V=await J(z),V.isError)throw K}return N(V,[250],"DATA end"),{messageId:B.messageId,accepted:F,rejected:W,response:V.message,envelope:B.envelope}}async function H(z){try{await _(z,{type:"QUIT"}),await J(z)}catch{}finally{await z.close()}}async function I(z,B){await _(z,{type:"EHLO",domain:B});let G=await J(z);return N(G,[250],"EHLO"),v(G)}async function l(z,B,G){if(B.type==="OAUTH2"&&B.oauth2){let K=await new U(B.oauth2).buildXOAUTH2();await _(z,{type:"AUTH_XOAUTH2",xoauth2String:K});let x=await J(z);if(x.code===334)await Y(z,E("")),x=await J(z);N(x,[235],"AUTH XOAUTH2");return}let F=B.type??L(G);if(F==="CRAM-MD5"){let V=y(B,"CRAM-MD5");await _(z,{type:"AUTH_CRAM_MD5_INIT"});let K=await J(z);N(K,[334],"AUTH CRAM-MD5");let x=K.message.trim(),w=await k(x,B.user,V);await _(z,{type:"AUTH_CRAM_MD5_RESPONSE",response:w}),K=await J(z),N(K,[235],"AUTH CRAM-MD5 response");return}if(F==="PLAIN"){let V=y(B,"PLAIN");await Y(z,Z({type:"AUTH_PLAIN",user:B.user,pass:V}));let K=await J(z);N(K,[235],"AUTH PLAIN");return}let W=y(B,"LOGIN");await Y(z,Z({type:"AUTH_LOGIN",user:B.user,pass:W}));let Q=await J(z);N(Q,[334],"AUTH LOGIN"),await Y(z,j(B.user)),Q=await J(z),N(Q,[334],"AUTH LOGIN user"),await Y(z,A(W)),Q=await J(z),N(Q,[235],"AUTH LOGIN pass")}function y(z,B){if(!z.pass)throw new $(`Password required for ${B} authentication`,0,`AUTH ${B}`,"");return z.pass}async function _(z,B){await Y(z,Z(B))}async function Y(z,B){await z.write(B)}async function J(z){let B=[];for await(let G of z.read()){B.push(G);let F=q(B);if(F)return O(F)}throw new $("Connection closed while reading SMTP response",0,"READ","")}async function M(z){let G=await(await import("node:dns/promises")).resolveMx(z);if(G.length===0)throw new $(`No MX records for ${z}`,0,"MX","");return G.sort((F,W)=>F.priority-W.priority),G[0]?.exchange??z}export{T as l,P as m,D as n,S as o,H as p,J as q};
2
+
3
+ //# debugId=B20B39B95D5ED1FD64756E2164756E21
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * @module\n * SMTP transport — orchestrates socket adapter, MIME builder, and protocol logic.\n *\n * @example\n * ```ts\n * import { SMTPTransport } from \"sently/transports/smtp\";\n * import { NodeAdapter } from \"sently/adapters/node\";\n * import { createMailer } from \"sently/mailer\";\n *\n * const mailer = await createMailer({\n * transport: new SMTPTransport({\n * host: \"smtp.example.com\",\n * auth: { user: \"you@example.com\", pass: \"secret\" },\n * adapter: new NodeAdapter(),\n * }),\n * });\n * ```\n */\nimport { OAuth2Client } from \"../auth/oauth2.js\";\nimport { buildMIME, type MIMEBuildResult } from \"../core/mime.js\";\nimport type { SMTPResponse } from \"../core/smtp.js\";\nimport {\n accumulateResponse,\n assertResponse,\n computeCRAMMD5,\n encodeAuthLoginPass,\n encodeAuthLoginUser,\n encodeCommand,\n encodeLine,\n parseEHLO,\n parseResponse,\n SMTPError,\n selectAuthMethod,\n} from \"../core/smtp.js\";\nimport type {\n MailOptions,\n SendResult,\n SMTPConfig,\n SocketAdapter,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/**\n * SMTP transport orchestrating adapter, MIME builder, and protocol logic.\n */\nexport class SMTPTransport implements Transport {\n readonly provider = \"smtp\";\n\n /** Resolved SMTP configuration with defaults applied. */\n private readonly config: ResolvedSMTPConfig;\n /** Active socket adapter for the current session, if any. */\n private adapter: SocketAdapter | null = null;\n\n /** Creates an SMTP transport with the given configuration. */\n constructor(config: SMTPConfig) {\n this.config = resolveSMTPConfig(config);\n }\n\n /** Sends an email via SMTP using the configured adapter. */\n async send(options: MailOptions): Promise<SendResult> {\n const resolvedOptions = {\n ...options,\n attachments: await resolveAttachments(options.attachments),\n };\n const mime = await buildMIME(resolvedOptions, this.config.dkim);\n const adapter = await this.getAdapter();\n\n const host = this.config.direct\n ? await resolveMX(mime.envelope.from.split(\"@\")[1] ?? this.config.host)\n : this.config.host;\n\n await adapter.connect(host, this.config.port);\n this.adapter = adapter;\n\n try {\n await openSMTPSession(adapter, this.config);\n return await deliverSMTPMessage(adapter, mime);\n } finally {\n await closeSMTPSession(adapter);\n this.adapter = null;\n }\n }\n\n /** Verifies SMTP connectivity and authentication without sending mail. */\n async verify(): Promise<VerifyResult> {\n try {\n const adapter = await this.getAdapter();\n await adapter.connect(this.config.host, this.config.port);\n\n try {\n await openSMTPSession(adapter, this.config);\n return { ok: true, provider: \"smtp\" };\n } finally {\n await closeSMTPSession(adapter);\n }\n } catch (err) {\n return {\n ok: false,\n provider: \"smtp\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n /** Closes the underlying socket adapter if connected. */\n async close(): Promise<void> {\n if (this.adapter) {\n await this.adapter.close();\n this.adapter = null;\n }\n }\n\n /** Returns the configured socket adapter or throws if none is set. */\n private async getAdapter(): Promise<SocketAdapter> {\n if (!this.config.adapter) {\n throw new SMTPError(\"No socket adapter configured\", 0, \"CONNECT\", \"\");\n }\n return this.config.adapter;\n }\n}\n\n/** Resolved SMTP transport configuration with defaults applied. */\nexport interface ResolvedSMTPConfig {\n /** SMTP server hostname. */\n host: string;\n /** SMTP port with secure/default applied. */\n port: number;\n /** Whether implicit TLS is used. */\n secure: boolean;\n /** Resolved authentication credentials, if any. */\n auth?: SMTPConfig[\"auth\"];\n /** Whether AUTH requires an encrypted connection. */\n requireTLS?: boolean;\n /** TLS options for STARTTLS and direct TLS. */\n tls?: SMTPConfig[\"tls\"];\n /** DKIM signing configuration. */\n dkim?: SMTPConfig[\"dkim\"];\n /** Socket connect timeout in milliseconds. */\n connectionTimeout?: number;\n /** Timeout waiting for the SMTP greeting in milliseconds. */\n greetingTimeout?: number;\n /** Idle socket timeout in milliseconds. */\n socketTimeout?: number;\n /** Direct-to-MX delivery mode. */\n direct?: boolean;\n /** Runtime socket adapter. */\n adapter?: SocketAdapter;\n}\n\n/** Apply defaults to SMTP configuration. */\nexport function resolveSMTPConfig(config: SMTPConfig): ResolvedSMTPConfig {\n const secure = config.secure ?? false;\n return {\n host: config.host,\n port: config.port ?? (secure ? 465 : 587),\n secure,\n ...(config.auth !== undefined ? { auth: config.auth } : {}),\n ...(config.requireTLS !== undefined ? { requireTLS: config.requireTLS } : {}),\n ...(config.dkim !== undefined ? { dkim: config.dkim } : {}),\n ...(config.tls !== undefined ? { tls: config.tls } : {}),\n ...(config.connectionTimeout !== undefined\n ? { connectionTimeout: config.connectionTimeout }\n : {}),\n ...(config.greetingTimeout !== undefined ? { greetingTimeout: config.greetingTimeout } : {}),\n ...(config.socketTimeout !== undefined ? { socketTimeout: config.socketTimeout } : {}),\n ...(config.direct !== undefined ? { direct: config.direct } : {}),\n ...(config.adapter !== undefined ? { adapter: config.adapter } : {}),\n };\n}\n\n/**\n * Connect greeting, EHLO, optional STARTTLS, and AUTH on an open adapter.\n */\nexport async function openSMTPSession(\n adapter: SocketAdapter,\n config: ResolvedSMTPConfig,\n): Promise<void> {\n const greeting = await readSMTPResponse(adapter);\n assertResponse(greeting, [220], \"greeting\");\n\n let capabilities = await ehlo(adapter, config.host);\n const supportsStartTls = capabilities.some((cap) => cap.toUpperCase() === \"STARTTLS\");\n if (!config.secure && !adapter.secure && supportsStartTls) {\n await sendRaw(adapter, encodeCommand({ type: \"STARTTLS\" }));\n const starttlsResp = await readSMTPResponse(adapter);\n assertResponse(starttlsResp, [220], \"STARTTLS\");\n await adapter.startTLS(config.tls);\n capabilities = await ehlo(adapter, config.host);\n }\n\n if (config.auth) {\n const tlsRequired = config.requireTLS ?? true;\n const encrypted = adapter.secure || config.secure;\n if (tlsRequired && !encrypted) {\n throw new SMTPError(\n \"Refusing to authenticate over unencrypted connection. \" +\n \"Set requireTLS: false to disable this check (not recommended).\",\n 0,\n \"AUTH\",\n \"\",\n );\n }\n await authenticate(adapter, config.auth, capabilities);\n }\n}\n\n/**\n * MAIL FROM, RCPT TO, and DATA for a built MIME message on an authenticated session.\n */\nexport async function deliverSMTPMessage(\n adapter: SocketAdapter,\n mime: MIMEBuildResult,\n): Promise<SendResult> {\n await sendCommand(adapter, { type: \"MAIL_FROM\", address: mime.envelope.from });\n const mailResp = await readSMTPResponse(adapter);\n assertResponse(mailResp, [250], \"MAIL FROM\");\n\n const accepted: string[] = [];\n const rejected: string[] = [];\n\n for (const recipient of mime.envelope.to) {\n await sendRaw(adapter, encodeCommand({ type: \"RCPT_TO\", address: recipient }));\n const rcptResp = await readSMTPResponse(adapter);\n if (rcptResp.isSuccess) {\n accepted.push(recipient);\n } else {\n rejected.push(recipient);\n }\n }\n\n await sendCommand(adapter, { type: \"DATA\" });\n const dataResp = await readSMTPResponse(adapter);\n assertResponse(dataResp, [354], \"DATA\");\n\n let finalResp: SMTPResponse;\n try {\n await sendRaw(adapter, encodeCommand({ type: \"DATA_BODY\", content: mime.raw }));\n finalResp = await readSMTPResponse(adapter);\n } catch (err) {\n await sendRaw(adapter, encodeCommand({ type: \"DATA_BODY\", content: mime.raw }));\n finalResp = await readSMTPResponse(adapter);\n if (finalResp.isError) {\n throw err;\n }\n }\n assertResponse(finalResp, [250], \"DATA end\");\n\n return {\n messageId: mime.messageId,\n accepted,\n rejected,\n response: finalResp.message,\n envelope: mime.envelope,\n };\n}\n\n/**\n * QUIT and close an SMTP session adapter.\n */\nexport async function closeSMTPSession(adapter: SocketAdapter): Promise<void> {\n try {\n await sendCommand(adapter, { type: \"QUIT\" });\n await readSMTPResponse(adapter);\n } catch {\n // ignore errors during shutdown\n } finally {\n await adapter.close();\n }\n}\n\nasync function ehlo(adapter: SocketAdapter, host: string): Promise<string[]> {\n await sendCommand(adapter, { type: \"EHLO\", domain: host });\n const response = await readSMTPResponse(adapter);\n assertResponse(response, [250], \"EHLO\");\n return parseEHLO(response);\n}\n\nasync function authenticate(\n adapter: SocketAdapter,\n auth: NonNullable<SMTPConfig[\"auth\"]>,\n capabilities: string[],\n): Promise<void> {\n if (auth.type === \"OAUTH2\" && auth.oauth2) {\n const client = new OAuth2Client(auth.oauth2);\n const xoauth2 = await client.buildXOAUTH2();\n await sendCommand(adapter, { type: \"AUTH_XOAUTH2\", xoauth2String: xoauth2 });\n let resp = await readSMTPResponse(adapter);\n if (resp.code === 334) {\n await sendRaw(adapter, encodeLine(\"\"));\n resp = await readSMTPResponse(adapter);\n }\n assertResponse(resp, [235], \"AUTH XOAUTH2\");\n return;\n }\n\n const method = auth.type ?? selectAuthMethod(capabilities);\n\n if (method === \"CRAM-MD5\") {\n const pass = requirePassword(auth, \"CRAM-MD5\");\n await sendCommand(adapter, { type: \"AUTH_CRAM_MD5_INIT\" });\n let resp = await readSMTPResponse(adapter);\n assertResponse(resp, [334], \"AUTH CRAM-MD5\");\n const challenge = resp.message.trim();\n const response = await computeCRAMMD5(challenge, auth.user, pass);\n await sendCommand(adapter, { type: \"AUTH_CRAM_MD5_RESPONSE\", response });\n resp = await readSMTPResponse(adapter);\n assertResponse(resp, [235], \"AUTH CRAM-MD5 response\");\n return;\n }\n\n if (method === \"PLAIN\") {\n const pass = requirePassword(auth, \"PLAIN\");\n await sendRaw(adapter, encodeCommand({ type: \"AUTH_PLAIN\", user: auth.user, pass }));\n const resp = await readSMTPResponse(adapter);\n assertResponse(resp, [235], \"AUTH PLAIN\");\n return;\n }\n\n const pass = requirePassword(auth, \"LOGIN\");\n await sendRaw(adapter, encodeCommand({ type: \"AUTH_LOGIN\", user: auth.user, pass }));\n let resp = await readSMTPResponse(adapter);\n assertResponse(resp, [334], \"AUTH LOGIN\");\n\n await sendRaw(adapter, encodeAuthLoginUser(auth.user));\n resp = await readSMTPResponse(adapter);\n assertResponse(resp, [334], \"AUTH LOGIN user\");\n\n await sendRaw(adapter, encodeAuthLoginPass(pass));\n resp = await readSMTPResponse(adapter);\n assertResponse(resp, [235], \"AUTH LOGIN pass\");\n}\n\nfunction requirePassword(auth: NonNullable<SMTPConfig[\"auth\"]>, method: string): string {\n if (!auth.pass) {\n throw new SMTPError(`Password required for ${method} authentication`, 0, `AUTH ${method}`, \"\");\n }\n return auth.pass;\n}\n\nasync function sendCommand(\n adapter: SocketAdapter,\n command: Parameters<typeof encodeCommand>[0],\n): Promise<void> {\n await sendRaw(adapter, encodeCommand(command));\n}\n\nasync function sendRaw(adapter: SocketAdapter, data: Uint8Array): Promise<void> {\n await adapter.write(data);\n}\n\n/** Reads and parses a complete SMTP response from the adapter. */\nasync function readSMTPResponse(adapter: SocketAdapter): Promise<SMTPResponse> {\n const chunks: Uint8Array[] = [];\n for await (const chunk of adapter.read()) {\n chunks.push(chunk);\n const complete = accumulateResponse(chunks);\n if (complete) {\n return parseResponse(complete);\n }\n }\n throw new SMTPError(\"Connection closed while reading SMTP response\", 0, \"READ\", \"\");\n}\n\nasync function resolveMX(domain: string): Promise<string> {\n const dns = await import(\"node:dns/promises\");\n const records = await dns.resolveMx(domain);\n if (records.length === 0) {\n throw new SMTPError(`No MX records for ${domain}`, 0, \"MX\", \"\");\n }\n records.sort((a: { priority: number }, b: { priority: number }) => a.priority - b.priority);\n return records[0]?.exchange ?? domain;\n}\n\nexport { SMTPError } from \"../core/smtp.js\";\n/** @internal Test helper for raw line writes. */\nexport { encodeLine, readSMTPResponse };\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "27963FE5C2BAA8F464756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "B20B39B95D5ED1FD64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -0,0 +1,4 @@
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
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/core/push-endpoint.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @module\n * Push subscription endpoint safety and hook redaction helpers.\n *\n * Full push endpoints embed long-lived delivery tokens. Never put the raw URL\n * in logs or hook context — use {@link redactPushEndpoint} instead.\n */\n\nimport { encodeBase64Url, encodeUtf8 } from \"./base64.js\";\n\n/**\n * Built-in hosts for major browser push services.\n * Custom hosts can be added via {@link WebPushConfig.allowedEndpointHosts}.\n */\nexport const DEFAULT_PUSH_ENDPOINT_HOSTS: readonly string[] = [\n \"fcm.googleapis.com\",\n \"android.googleapis.com\",\n \"updates.push.services.mozilla.com\",\n \"push.services.mozilla.com\",\n \"web.push.apple.com\",\n];\n\n/** Suffix patterns for regional / vendor push hosts. */\nconst DEFAULT_PUSH_ENDPOINT_SUFFIXES: readonly string[] = [\n \".push.services.mozilla.com\",\n \".notify.windows.com\",\n \".push.apple.com\",\n];\n\n/**\n * Validate a push subscription endpoint before server-side fetch.\n *\n * Rejects non-HTTPS schemes, IP literals (including link-local / private),\n * and hostnames outside the allowlist — mitigates SSRF when subscriptions\n * come from untrusted clients.\n *\n * @param endpoint - Raw subscription endpoint URL.\n * @param extraHosts - Additional exact hostnames to allow.\n * @throws Error when the endpoint is unsafe.\n */\nexport function assertSafePushEndpoint(endpoint: string, extraHosts: readonly string[] = []): void {\n let url: URL;\n try {\n url = new URL(endpoint);\n } catch {\n throw new Error(\"Invalid push subscription endpoint URL\");\n }\n\n if (url.protocol !== \"https:\") {\n throw new Error(\"Push subscription endpoint must use https:\");\n }\n\n if (url.username || url.password) {\n throw new Error(\"Push subscription endpoint must not include credentials\");\n }\n\n const host = url.hostname.toLowerCase();\n\n if (isIpLiteral(host)) {\n throw new Error(\"Push subscription endpoint must not be an IP address\");\n }\n\n if (!isAllowedPushHost(host, extraHosts)) {\n throw new Error(`Push subscription endpoint host is not allowlisted: ${host}`);\n }\n}\n\n/**\n * Redact a push endpoint for hook/observability context.\n * Returns `origin/#<8-byte-hash>` so logs never store the delivery token path.\n */\nexport async function redactPushEndpoint(endpoint: string): Promise<string> {\n try {\n const url = new URL(endpoint);\n const bytes = encodeUtf8(endpoint);\n const digest = await crypto.subtle.digest(\n \"SHA-256\",\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer,\n );\n const short = encodeBase64Url(new Uint8Array(digest).subarray(0, 8));\n return `${url.origin}/#${short}`;\n } catch {\n return \"[invalid-endpoint]\";\n }\n}\n\nfunction isAllowedPushHost(host: string, extraHosts: readonly string[]): boolean {\n if (DEFAULT_PUSH_ENDPOINT_HOSTS.includes(host) || extraHosts.includes(host)) {\n return true;\n }\n return DEFAULT_PUSH_ENDPOINT_SUFFIXES.some(\n (suffix) => host.endsWith(suffix) && host.length > suffix.length,\n );\n}\n\nfunction isIpLiteral(host: string): boolean {\n // IPv6 in URLs is bracketed; URL.hostname strips brackets.\n if (host.includes(\":\")) {\n return true;\n }\n // IPv4 dotted-quad\n if (/^\\d{1,3}(?:\\.\\d{1,3}){3}$/u.test(host)) {\n return true;\n }\n return false;\n}\n"
6
+ ],
7
+ "mappings": "gDAcO,IAAM,EAAiD,CAC5D,qBACA,yBACA,oCACA,4BACA,oBACF,EAGM,EAAoD,CACxD,6BACA,sBACA,iBACF,EAaO,SAAS,CAAsB,CAAC,EAAkB,EAAgC,CAAC,EAAS,CACjG,IAAI,EACJ,GAAI,CACF,EAAM,IAAI,IAAI,CAAQ,EACtB,KAAM,CACN,MAAU,MAAM,wCAAwC,EAG1D,GAAI,EAAI,WAAa,SACnB,MAAU,MAAM,4CAA4C,EAG9D,GAAI,EAAI,UAAY,EAAI,SACtB,MAAU,MAAM,yDAAyD,EAG3E,IAAM,EAAO,EAAI,SAAS,YAAY,EAEtC,GAAI,EAAY,CAAI,EAClB,MAAU,MAAM,sDAAsD,EAGxE,GAAI,CAAC,EAAkB,EAAM,CAAU,EACrC,MAAU,MAAM,uDAAuD,GAAM,EAQjF,eAAsB,CAAkB,CAAC,EAAmC,CAC1E,GAAI,CACF,IAAM,EAAM,IAAI,IAAI,CAAQ,EACtB,EAAQ,EAAW,CAAQ,EAC3B,EAAS,MAAM,OAAO,OAAO,OACjC,UACA,EAAM,OAAO,MAAM,EAAM,WAAY,EAAM,WAAa,EAAM,UAAU,CAC1E,EACM,EAAQ,EAAgB,IAAI,WAAW,CAAM,EAAE,SAAS,EAAG,CAAC,CAAC,EACnE,MAAO,GAAG,EAAI,WAAW,IACzB,KAAM,CACN,MAAO,sBAIX,SAAS,CAAiB,CAAC,EAAc,EAAwC,CAC/E,GAAI,EAA4B,SAAS,CAAI,GAAK,EAAW,SAAS,CAAI,EACxE,MAAO,GAET,OAAO,EAA+B,KACpC,CAAC,IAAW,EAAK,SAAS,CAAM,GAAK,EAAK,OAAS,EAAO,MAC5D,EAGF,SAAS,CAAW,CAAC,EAAuB,CAE1C,GAAI,EAAK,SAAS,GAAG,EACnB,MAAO,GAGT,GAAI,6BAA6B,KAAK,CAAI,EACxC,MAAO,GAET,MAAO",
8
+ "debugId": "6BC361CE3959323764756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,5 @@
1
+ var M=new TextEncoder;function N(j){let f=typeof j==="string"?M.encode(j):j,k="",G=0;while(G<f.length){let q=f[G]??0,A=f[G+1],D=f[G+2];if(A===void 0){k+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[q>>2],k+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(q&3)<<4],k+="==";break}if(D===void 0){k+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[q>>2],k+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(q&3)<<4|A>>4],k+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(A&15)<<2],k+="=";break}k+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[q>>2],k+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(q&3)<<4|A>>4],k+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(A&15)<<2|D>>6],k+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[D&63],G+=3}return X(k)}function S(j){let f=j.replace(/\s/g,""),k=f.length;if(k===0)return new Uint8Array(0);if(k%4!==0)throw Error("Invalid base64 string length");let G=f.endsWith("==")?2:f.endsWith("=")?1:0,q=k*3/4-G,A=new Uint8Array(q),D=0;for(let H=0;H<k;H+=4){let O=J(f[H]??"="),P=J(f[H+1]??"="),Q=J(f[H+2]??"="),R=J(f[H+3]??"="),K=O<<18|P<<12|Q<<6|R;if(D<q)A[D++]=K>>16&255;if(D<q)A[D++]=K>>8&255;if(D<q)A[D++]=K&255}return A}function Y(j){if(!W(j))return j;return`=?UTF-8?B?${N(j).replace(/\r\n/g,"")}?=`}function W(j){for(let f=0;f<j.length;f++)if(j.charCodeAt(f)>127)return!0;return!1}function J(j){if(j==="=")return 0;let f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(j);if(f===-1)throw Error(`Invalid base64 character: ${j}`);return f}function X(j){let f=[];for(let k=0;k<j.length;k+=76)f.push(j.slice(k,k+76));return f.join(`\r
2
+ `)}function Z(j){return N(j).replace(/\r\n/g,"").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/u,"")}function _(j){let f=j.replace(/-/g,"+").replace(/_/g,"/"),k=f.length%4===0?"":"=".repeat(4-f.length%4);return S(f+k)}function $(j){return M.encode(j)}
3
+ export{N as Y,S as Z,Y as _,Z as $,_ as aa,$ as ba};
4
+
5
+ //# debugId=2107CB27BCD4972964756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/core/base64.ts"],
4
4
  "sourcesContent": [
5
- "// src/core/base64.ts\n\nconst BASE64_CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\nconst BASE64_LINE_LENGTH = 76;\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * Encode a Uint8Array or string to Base64.\n * Uses TextEncoder + manual base64 to support binary data correctly.\n */\nexport function encodeBase64(data: Uint8Array | string): string {\n const bytes = typeof data === \"string\" ? encoder.encode(data) : data;\n let result = \"\";\n let i = 0;\n\n while (i < bytes.length) {\n const b0 = bytes[i] ?? 0;\n const b1 = bytes[i + 1];\n const b2 = bytes[i + 2];\n\n if (b1 === undefined) {\n result += BASE64_CHARS[b0 >> 2];\n result += BASE64_CHARS[(b0 & 0x03) << 4];\n result += \"==\";\n break;\n }\n\n if (b2 === undefined) {\n result += BASE64_CHARS[b0 >> 2];\n result += BASE64_CHARS[((b0 & 0x03) << 4) | (b1 >> 4)];\n result += BASE64_CHARS[(b1 & 0x0f) << 2];\n result += \"=\";\n break;\n }\n\n result += BASE64_CHARS[b0 >> 2];\n result += BASE64_CHARS[((b0 & 0x03) << 4) | (b1 >> 4)];\n result += BASE64_CHARS[((b1 & 0x0f) << 2) | (b2 >> 6)];\n result += BASE64_CHARS[b2 & 0x3f];\n i += 3;\n }\n\n return wrapBase64Lines(result);\n}\n\n/**\n * Decode a Base64 string to Uint8Array.\n */\nexport function decodeBase64(data: string): Uint8Array {\n const cleaned = data.replace(/\\s/g, \"\");\n const len = cleaned.length;\n\n if (len === 0) {\n return new Uint8Array(0);\n }\n\n if (len % 4 !== 0) {\n throw new Error(\"Invalid base64 string length\");\n }\n\n const padding = cleaned.endsWith(\"==\") ? 2 : cleaned.endsWith(\"=\") ? 1 : 0;\n const outputLen = (len * 3) / 4 - padding;\n const output = new Uint8Array(outputLen);\n\n let outIndex = 0;\n for (let i = 0; i < len; i += 4) {\n const c0 = base64CharToValue(cleaned[i] ?? \"=\");\n const c1 = base64CharToValue(cleaned[i + 1] ?? \"=\");\n const c2 = base64CharToValue(cleaned[i + 2] ?? \"=\");\n const c3 = base64CharToValue(cleaned[i + 3] ?? \"=\");\n const triple = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3;\n\n if (outIndex < outputLen) {\n output[outIndex++] = (triple >> 16) & 0xff;\n }\n if (outIndex < outputLen) {\n output[outIndex++] = (triple >> 8) & 0xff;\n }\n if (outIndex < outputLen) {\n output[outIndex++] = triple & 0xff;\n }\n }\n\n return output;\n}\n\n/**\n * Encode text using Quoted-Printable (RFC 2045).\n */\nexport function encodeQP(text: string): string {\n const bytes = encoder.encode(text);\n const lines: string[] = [];\n let line = \"\";\n\n for (let i = 0; i < bytes.length; i++) {\n const byte = bytes[i] ?? 0;\n\n if (byte === 0x0a) {\n lines.push(line);\n line = \"\";\n continue;\n }\n\n if (byte === 0x0d) {\n continue;\n }\n\n let encoded: string;\n if (\n (byte >= 33 && byte <= 60) ||\n (byte >= 62 && byte <= 126) ||\n byte === 0x09 ||\n byte === 0x20\n ) {\n encoded = String.fromCharCode(byte);\n } else {\n encoded = `=${byte.toString(16).toUpperCase().padStart(2, \"0\")}`;\n }\n\n if (line.length + encoded.length > 75) {\n lines.push(`${line}=`);\n line = encoded;\n } else {\n line += encoded;\n }\n }\n\n if (line.length > 0) {\n lines.push(line);\n }\n\n return lines.join(\"\\r\\n\");\n}\n\n/**\n * Encode an email header value per RFC 2047.\n * Non-ASCII values become: =?UTF-8?B?<base64>?=\n */\nexport function encodeHeader(value: string): string {\n if (!needsEncoding(value)) {\n return value;\n }\n\n const encoded = encodeBase64(value).replace(/\\r\\n/g, \"\");\n return `=?UTF-8?B?${encoded}?=`;\n}\n\n/**\n * Returns true if the string contains non-ASCII characters\n * and therefore requires RFC 2047 encoding in headers.\n */\nexport function needsEncoding(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) > 127) {\n return true;\n }\n }\n return false;\n}\n\nfunction base64CharToValue(char: string): number {\n if (char === \"=\") {\n return 0;\n }\n const index = BASE64_CHARS.indexOf(char);\n if (index === -1) {\n throw new Error(`Invalid base64 character: ${char}`);\n }\n return index;\n}\n\nfunction wrapBase64Lines(base64: string): string {\n const lines: string[] = [];\n for (let i = 0; i < base64.length; i += BASE64_LINE_LENGTH) {\n lines.push(base64.slice(i, i + BASE64_LINE_LENGTH));\n }\n return lines.join(\"\\r\\n\");\n}\n\n/** Decode bytes to UTF-8 string. */\nexport function decodeUtf8(bytes: Uint8Array): string {\n return decoder.decode(bytes);\n}\n\n/** Encode string to UTF-8 bytes. */\nexport function encodeUtf8(text: string): Uint8Array {\n return encoder.encode(text);\n}\n"
5
+ "// src/core/base64.ts\n\nconst BASE64_CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\nconst BASE64_LINE_LENGTH = 76;\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * Encode a Uint8Array or string to Base64.\n * Uses TextEncoder + manual base64 to support binary data correctly.\n */\nexport function encodeBase64(data: Uint8Array | string): string {\n const bytes = typeof data === \"string\" ? encoder.encode(data) : data;\n let result = \"\";\n let i = 0;\n\n while (i < bytes.length) {\n const b0 = bytes[i] ?? 0;\n const b1 = bytes[i + 1];\n const b2 = bytes[i + 2];\n\n if (b1 === undefined) {\n result += BASE64_CHARS[b0 >> 2];\n result += BASE64_CHARS[(b0 & 0x03) << 4];\n result += \"==\";\n break;\n }\n\n if (b2 === undefined) {\n result += BASE64_CHARS[b0 >> 2];\n result += BASE64_CHARS[((b0 & 0x03) << 4) | (b1 >> 4)];\n result += BASE64_CHARS[(b1 & 0x0f) << 2];\n result += \"=\";\n break;\n }\n\n result += BASE64_CHARS[b0 >> 2];\n result += BASE64_CHARS[((b0 & 0x03) << 4) | (b1 >> 4)];\n result += BASE64_CHARS[((b1 & 0x0f) << 2) | (b2 >> 6)];\n result += BASE64_CHARS[b2 & 0x3f];\n i += 3;\n }\n\n return wrapBase64Lines(result);\n}\n\n/**\n * Decode a Base64 string to Uint8Array.\n */\nexport function decodeBase64(data: string): Uint8Array {\n const cleaned = data.replace(/\\s/g, \"\");\n const len = cleaned.length;\n\n if (len === 0) {\n return new Uint8Array(0);\n }\n\n if (len % 4 !== 0) {\n throw new Error(\"Invalid base64 string length\");\n }\n\n const padding = cleaned.endsWith(\"==\") ? 2 : cleaned.endsWith(\"=\") ? 1 : 0;\n const outputLen = (len * 3) / 4 - padding;\n const output = new Uint8Array(outputLen);\n\n let outIndex = 0;\n for (let i = 0; i < len; i += 4) {\n const c0 = base64CharToValue(cleaned[i] ?? \"=\");\n const c1 = base64CharToValue(cleaned[i + 1] ?? \"=\");\n const c2 = base64CharToValue(cleaned[i + 2] ?? \"=\");\n const c3 = base64CharToValue(cleaned[i + 3] ?? \"=\");\n const triple = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3;\n\n if (outIndex < outputLen) {\n output[outIndex++] = (triple >> 16) & 0xff;\n }\n if (outIndex < outputLen) {\n output[outIndex++] = (triple >> 8) & 0xff;\n }\n if (outIndex < outputLen) {\n output[outIndex++] = triple & 0xff;\n }\n }\n\n return output;\n}\n\n/**\n * Encode text using Quoted-Printable (RFC 2045).\n */\nexport function encodeQP(text: string): string {\n const bytes = encoder.encode(text);\n const lines: string[] = [];\n let line = \"\";\n\n for (let i = 0; i < bytes.length; i++) {\n const byte = bytes[i] ?? 0;\n\n if (byte === 0x0a) {\n lines.push(line);\n line = \"\";\n continue;\n }\n\n if (byte === 0x0d) {\n continue;\n }\n\n let encoded: string;\n if (\n (byte >= 33 && byte <= 60) ||\n (byte >= 62 && byte <= 126) ||\n byte === 0x09 ||\n byte === 0x20\n ) {\n encoded = String.fromCharCode(byte);\n } else {\n encoded = `=${byte.toString(16).toUpperCase().padStart(2, \"0\")}`;\n }\n\n if (line.length + encoded.length > 75) {\n lines.push(`${line}=`);\n line = encoded;\n } else {\n line += encoded;\n }\n }\n\n if (line.length > 0) {\n lines.push(line);\n }\n\n return lines.join(\"\\r\\n\");\n}\n\n/**\n * Encode an email header value per RFC 2047.\n * Non-ASCII values become: =?UTF-8?B?<base64>?=\n */\nexport function encodeHeader(value: string): string {\n if (!needsEncoding(value)) {\n return value;\n }\n\n const encoded = encodeBase64(value).replace(/\\r\\n/g, \"\");\n return `=?UTF-8?B?${encoded}?=`;\n}\n\n/**\n * Returns true if the string contains non-ASCII characters\n * and therefore requires RFC 2047 encoding in headers.\n */\nexport function needsEncoding(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) > 127) {\n return true;\n }\n }\n return false;\n}\n\nfunction base64CharToValue(char: string): number {\n if (char === \"=\") {\n return 0;\n }\n const index = BASE64_CHARS.indexOf(char);\n if (index === -1) {\n throw new Error(`Invalid base64 character: ${char}`);\n }\n return index;\n}\n\nfunction wrapBase64Lines(base64: string): string {\n const lines: string[] = [];\n for (let i = 0; i < base64.length; i += BASE64_LINE_LENGTH) {\n lines.push(base64.slice(i, i + BASE64_LINE_LENGTH));\n }\n return lines.join(\"\\r\\n\");\n}\n\n/**\n * Encode bytes to Base64url (RFC 4648 §5) without padding.\n * Used by Web Push (VAPID / RFC 8291) and similar protocols.\n */\nexport function encodeBase64Url(data: Uint8Array | string): string {\n const encoded = encodeBase64(data).replace(/\\r\\n/g, \"\");\n return encoded.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/u, \"\");\n}\n\n/**\n * Decode a Base64url (RFC 4648 §5) string to bytes.\n * Accepts both padded and unpadded input.\n */\nexport function decodeBase64Url(data: string): Uint8Array {\n const normalized = data.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padding = normalized.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (normalized.length % 4));\n return decodeBase64(normalized + padding);\n}\n\n/** Decode bytes to UTF-8 string. */\nexport function decodeUtf8(bytes: Uint8Array): string {\n return decoder.decode(bytes);\n}\n\n/** Encode string to UTF-8 bytes. */\nexport function encodeUtf8(text: string): Uint8Array {\n return encoder.encode(text);\n}\n"
6
6
  ],
7
- "mappings": "AAKA,IAAM,EAAU,IAAI,YAOb,SAAS,CAAY,CAAC,EAAmC,CAC9D,IAAM,EAAQ,OAAO,IAAS,SAAW,EAAQ,OAAO,CAAI,EAAI,EAC5D,EAAS,GACT,EAAI,EAER,MAAO,EAAI,EAAM,OAAQ,CACvB,IAAM,EAAK,EAAM,IAAM,EACjB,EAAK,EAAM,EAAI,GACf,EAAK,EAAM,EAAI,GAErB,GAAI,IAAO,OAAW,CACpB,GArBe,mEAqBQ,GAAM,GAC7B,GAtBe,mEAsBS,GAAK,IAAS,GACtC,GAAU,KACV,MAGF,GAAI,IAAO,OAAW,CACpB,GA5Be,mEA4BQ,GAAM,GAC7B,GA7Be,mEA6BU,GAAK,IAAS,EAAM,GAAM,GACnD,GA9Be,mEA8BS,GAAK,KAAS,GACtC,GAAU,IACV,MAGF,GAnCiB,mEAmCM,GAAM,GAC7B,GApCiB,mEAoCQ,GAAK,IAAS,EAAM,GAAM,GACnD,GArCiB,mEAqCQ,GAAK,KAAS,EAAM,GAAM,GACnD,GAtCiB,mEAsCM,EAAK,IAC5B,GAAK,EAGP,OAAO,EAAgB,CAAM,EAMxB,SAAS,CAAY,CAAC,EAA0B,CACrD,IAAM,EAAU,EAAK,QAAQ,MAAO,EAAE,EAChC,EAAM,EAAQ,OAEpB,GAAI,IAAQ,EACV,OAAO,IAAI,WAAW,CAAC,EAGzB,GAAI,EAAM,IAAM,EACd,MAAU,MAAM,8BAA8B,EAGhD,IAAM,EAAU,EAAQ,SAAS,IAAI,EAAI,EAAI,EAAQ,SAAS,GAAG,EAAI,EAAI,EACnE,EAAa,EAAM,EAAK,EAAI,EAC5B,EAAS,IAAI,WAAW,CAAS,EAEnC,EAAW,EACf,QAAS,EAAI,EAAG,EAAI,EAAK,GAAK,EAAG,CAC/B,IAAM,EAAK,EAAkB,EAAQ,IAAM,GAAG,EACxC,EAAK,EAAkB,EAAQ,EAAI,IAAM,GAAG,EAC5C,EAAK,EAAkB,EAAQ,EAAI,IAAM,GAAG,EAC5C,EAAK,EAAkB,EAAQ,EAAI,IAAM,GAAG,EAC5C,EAAU,GAAM,GAAO,GAAM,GAAO,GAAM,EAAK,EAErD,GAAI,EAAW,EACb,EAAO,KAAe,GAAU,GAAM,IAExC,GAAI,EAAW,EACb,EAAO,KAAe,GAAU,EAAK,IAEvC,GAAI,EAAW,EACb,EAAO,KAAc,EAAS,IAIlC,OAAO,EAuDF,SAAS,CAAY,CAAC,EAAuB,CAClD,GAAI,CAAC,EAAc,CAAK,EACtB,OAAO,EAIT,MAAO,aADS,EAAa,CAAK,EAAE,QAAQ,QAAS,EAAE,MAQlD,SAAS,CAAa,CAAC,EAAuB,CACnD,QAAS,EAAI,EAAG,EAAI,EAAK,OAAQ,IAC/B,GAAI,EAAK,WAAW,CAAC,EAAI,IACvB,MAAO,GAGX,MAAO,GAGT,SAAS,CAAiB,CAAC,EAAsB,CAC/C,GAAI,IAAS,IACX,MAAO,GAET,IAAM,EApKa,mEAoKQ,QAAQ,CAAI,EACvC,GAAI,IAAU,GACZ,MAAU,MAAM,6BAA6B,GAAM,EAErD,OAAO,EAGT,SAAS,CAAe,CAAC,EAAwB,CAC/C,IAAM,EAAkB,CAAC,EACzB,QAAS,EAAI,EAAG,EAAI,EAAO,OAAQ,GA5KV,GA6KvB,EAAM,KAAK,EAAO,MAAM,EAAG,EA7KJ,EA6K0B,CAAC,EAEpD,OAAO,EAAM,KAAK;AAAA,CAAM,EASnB,SAAS,CAAU,CAAC,EAA0B,CACnD,OAAO,EAAQ,OAAO,CAAI",
8
- "debugId": "4A06DB0A7917D84164756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "2107CB27BCD4972964756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -21,6 +21,16 @@ export declare function encodeHeader(value: string): string;
21
21
  * and therefore requires RFC 2047 encoding in headers.
22
22
  */
23
23
  export declare function needsEncoding(text: string): boolean;
24
+ /**
25
+ * Encode bytes to Base64url (RFC 4648 §5) without padding.
26
+ * Used by Web Push (VAPID / RFC 8291) and similar protocols.
27
+ */
28
+ export declare function encodeBase64Url(data: Uint8Array | string): string;
29
+ /**
30
+ * Decode a Base64url (RFC 4648 §5) string to bytes.
31
+ * Accepts both padded and unpadded input.
32
+ */
33
+ export declare function decodeBase64Url(data: string): Uint8Array;
24
34
  /** Decode bytes to UTF-8 string. */
25
35
  export declare function decodeUtf8(bytes: Uint8Array): string;
26
36
  /** Encode string to UTF-8 bytes. */
@@ -1,3 +1,3 @@
1
- import{X as a,Y as b,Z as c}from"../chunk-6bkj4y4r.js";import"../chunk-nbmq8ejv.js";export{c as smtpCodeToSentlyCode,b as httpStatusToSentlyCode,a as SentlyError};
1
+ import{ca as a,da as b,ea as c}from"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";export{c as smtpCodeToSentlyCode,b as httpStatusToSentlyCode,a as SentlyError};
2
2
 
3
- //# debugId=EA669FFC2ACB394064756E2164756E21
3
+ //# debugId=8BB8C05CE319F39464756E2164756E21
@@ -4,6 +4,6 @@
4
4
  "sourcesContent": [
5
5
  ],
6
6
  "mappings": "",
7
- "debugId": "EA669FFC2ACB394064756E2164756E21",
7
+ "debugId": "8BB8C05CE319F39464756E2164756E21",
8
8
  "names": []
9
9
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @module
3
+ * Shared lifecycle hook invocation for mailer and channel senders.
4
+ * Hook failures are swallowed so they never break the send path.
5
+ */
6
+ /**
7
+ * Invoke a lifecycle hook without letting hook failures break the send.
8
+ * In non-production environments, hook errors are logged with `console.warn`.
9
+ *
10
+ * @typeParam T - Hook argument tuple types.
11
+ * @param hook - Optional sync or async hook callback.
12
+ * @param args - Arguments forwarded to the hook.
13
+ */
14
+ export declare function invokeHook<T extends unknown[]>(hook: ((...args: T) => void | Promise<void>) | undefined, ...args: T): Promise<void>;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @module
3
- * Plugin pipeline for sently.
4
- * Plugins transform MailOptions before message construction.
3
+ * Plugin pipeline for sently channel senders.
4
+ * Plugins transform channel options before the transport sends.
5
5
  * They run sequentially — each receives the previous plugin's output.
6
6
  *
7
7
  * @example
@@ -10,14 +10,14 @@
10
10
  * const result = await runPlugins(options, [pluginA, pluginB]);
11
11
  * ```
12
12
  */
13
- import type { MailOptions, MailPlugin } from "./types.js";
14
13
  /**
15
- * Run a list of plugins sequentially over MailOptions.
14
+ * Run a list of plugins sequentially over channel options.
16
15
  * If plugins is empty or undefined, returns options unchanged.
17
16
  * Each plugin may be sync or async.
18
17
  *
19
- * @param options - the original mail options
18
+ * @typeParam T - Channel options type (e.g. MailOptions, SmsOptions).
19
+ * @param options - the original options
20
20
  * @param plugins - ordered list of plugins to apply
21
- * @returns transformed mail options after all plugins have run
21
+ * @returns transformed options after all plugins have run
22
22
  */
23
- export declare function runPlugins(options: MailOptions, plugins: MailPlugin[] | undefined): Promise<MailOptions>;
23
+ export declare function runPlugins<T>(options: T, plugins: Array<(o: T) => T | Promise<T>> | undefined): Promise<T>;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @module
3
+ * Push subscription endpoint safety and hook redaction helpers.
4
+ *
5
+ * Full push endpoints embed long-lived delivery tokens. Never put the raw URL
6
+ * in logs or hook context — use {@link redactPushEndpoint} instead.
7
+ */
8
+ /**
9
+ * Built-in hosts for major browser push services.
10
+ * Custom hosts can be added via {@link WebPushConfig.allowedEndpointHosts}.
11
+ */
12
+ export declare const DEFAULT_PUSH_ENDPOINT_HOSTS: readonly string[];
13
+ /**
14
+ * Validate a push subscription endpoint before server-side fetch.
15
+ *
16
+ * Rejects non-HTTPS schemes, IP literals (including link-local / private),
17
+ * and hostnames outside the allowlist — mitigates SSRF when subscriptions
18
+ * come from untrusted clients.
19
+ *
20
+ * @param endpoint - Raw subscription endpoint URL.
21
+ * @param extraHosts - Additional exact hostnames to allow.
22
+ * @throws Error when the endpoint is unsafe.
23
+ */
24
+ export declare function assertSafePushEndpoint(endpoint: string, extraHosts?: readonly string[]): void;
25
+ /**
26
+ * Redact a push endpoint for hook/observability context.
27
+ * Returns `origin/#<8-byte-hash>` so logs never store the delivery token path.
28
+ */
29
+ export declare function redactPushEndpoint(endpoint: string): Promise<string>;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @module
3
+ * Shared Web Push channel contracts — sibling types to email MailOptions/Transport/Hooks.
4
+ *
5
+ * Sently-first: this contract is the stable push surface. Provider transports
6
+ * implement {@link PushTransport}.
7
+ */
8
+ import type { VerifyResult } from "./types.js";
9
+ /** Browser push subscription as returned by the Push API. */
10
+ export interface PushSubscription {
11
+ /** Push service endpoint URL. */
12
+ endpoint: string;
13
+ /** Client encryption keys for RFC 8291 payload encryption. */
14
+ keys: {
15
+ /** Base64url-encoded P-256 ECDH public key. */
16
+ p256dh: string;
17
+ /** Base64url-encoded authentication secret. */
18
+ auth: string;
19
+ };
20
+ }
21
+ /** Options for sending a Web Push notification. */
22
+ export interface PushOptions {
23
+ /** Target browser push subscription. */
24
+ subscription: PushSubscription;
25
+ /** Notification title. */
26
+ title: string;
27
+ /** Notification body text. */
28
+ body: string;
29
+ /** Arbitrary application data attached to the notification. */
30
+ data?: Record<string, unknown>;
31
+ /** Notification icon URL. */
32
+ icon?: string;
33
+ /** Time-to-live in seconds for the push message. */
34
+ ttl?: number;
35
+ /** Client-supplied message identifier when known. */
36
+ messageId?: string;
37
+ }
38
+ /** Result returned after a push notification is accepted by the push service. */
39
+ export interface PushSendResult {
40
+ /** Client-generated or assigned message identifier. */
41
+ messageId: string;
42
+ /** Delivery status string (e.g. `"accepted"`). */
43
+ status: string;
44
+ /** Human-readable or raw status text (often the HTTP status). */
45
+ response: string;
46
+ /** Transport or provider identifier (e.g. `"webpush"`). */
47
+ provider?: string;
48
+ }
49
+ /** Pluggable Web Push delivery backend. */
50
+ export interface PushTransport {
51
+ /**
52
+ * Stable provider identifier for observability (e.g. `"webpush"`).
53
+ * Prefer this over constructor-name inference in hooks.
54
+ */
55
+ readonly provider?: string;
56
+ /** Send a push notification through this transport. */
57
+ send(options: PushOptions): Promise<PushSendResult>;
58
+ /** Test connectivity and credentials without sending. */
59
+ verify?(): Promise<VerifyResult>;
60
+ /** Release resources held by the transport. */
61
+ close?(): Promise<void>;
62
+ }
63
+ /**
64
+ * Context passed to push lifecycle hooks.
65
+ * Uses a redacted subscription endpoint instead of a recipient address (no natural `to`).
66
+ */
67
+ export interface PushHookContext {
68
+ /** Message-ID when known (from options or send result). */
69
+ messageId?: string;
70
+ /**
71
+ * Redacted push endpoint for observability: `origin/#<short-hash>`.
72
+ * Never the full URL — the path embeds a long-lived delivery token.
73
+ */
74
+ endpoint: string;
75
+ /** Transport or provider identifier (e.g. `"webpush"`). */
76
+ provider: string;
77
+ }
78
+ /** Optional lifecycle hooks for metrics, tracing, and observability on every push send. */
79
+ export interface PushHooks {
80
+ /** Fired before the transport sends the notification. */
81
+ onSend?: (ctx: PushHookContext) => void | Promise<void>;
82
+ /**
83
+ * Fired after a successful send.
84
+ * @param durationMs — elapsed milliseconds from send start to success (optional third argument).
85
+ */
86
+ onSuccess?: (ctx: PushHookContext, result: PushSendResult, durationMs?: number) => void | Promise<void>;
87
+ /**
88
+ * Fired when a send throws (error is re-thrown after the hook runs).
89
+ * @param durationMs — elapsed milliseconds from send start to failure (optional third argument).
90
+ */
91
+ onError?: (ctx: PushHookContext, error: unknown, durationMs?: number) => void | Promise<void>;
92
+ }
93
+ /**
94
+ * A push plugin transforms {@link PushOptions} before the transport sends.
95
+ * Plugins run sequentially. Each receives the output of the previous.
96
+ * Return a new options object — do not mutate the input.
97
+ */
98
+ export type PushPlugin = ((options: PushOptions) => PushOptions) | ((options: PushOptions) => Promise<PushOptions>);
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @module
3
+ * Shared SMS channel contracts — sibling types to email MailOptions/Transport/Hooks.
4
+ *
5
+ * Sently-first: this contract is the stable SMS surface. Provider transports
6
+ * implement {@link SmsTransport}; vendor extras stay off this module.
7
+ */
8
+ import type { VerifyResult } from "./types.js";
9
+ /** Options for sending an SMS message. */
10
+ export interface SmsOptions {
11
+ /** Recipient phone number (E.164 recommended). */
12
+ to: string;
13
+ /** Message body text. */
14
+ body: string;
15
+ /** Sender phone number or alphanumeric sender ID. */
16
+ from?: string;
17
+ /** Client-supplied message identifier when known. */
18
+ messageId?: string;
19
+ }
20
+ /** Result returned after an SMS is accepted for delivery. */
21
+ export interface SmsSendResult {
22
+ /** Provider-assigned or client message identifier. */
23
+ messageId: string;
24
+ /** Recipient phone number. */
25
+ to: string;
26
+ /** Provider delivery status string (e.g. `"queued"`, `"sent"`). */
27
+ status: string;
28
+ /** Human-readable or raw status text from the provider. */
29
+ response: string;
30
+ /** Transport or provider identifier (e.g. `"twilio-sms"`). */
31
+ provider?: string;
32
+ }
33
+ /** Pluggable SMS delivery backend. */
34
+ export interface SmsTransport {
35
+ /**
36
+ * Stable provider identifier for observability (e.g. `"twilio-sms"`).
37
+ * Prefer this over constructor-name inference in hooks.
38
+ */
39
+ readonly provider?: string;
40
+ /** Send an SMS through this transport. */
41
+ send(options: SmsOptions): Promise<SmsSendResult>;
42
+ /** Test connectivity and credentials without sending. */
43
+ verify?(): Promise<VerifyResult>;
44
+ /** Release resources held by the transport. */
45
+ close?(): Promise<void>;
46
+ }
47
+ /** Context passed to SMS lifecycle hooks (no message body — avoids PII in logs). */
48
+ export interface SmsHookContext {
49
+ /** Message-ID when known (from options or send result). */
50
+ messageId?: string;
51
+ /** Recipient phone number. */
52
+ to: string;
53
+ /** Transport or provider identifier (e.g. `"twilio-sms"`). */
54
+ provider: string;
55
+ }
56
+ /** Optional lifecycle hooks for metrics, tracing, and observability on every SMS send. */
57
+ export interface SmsHooks {
58
+ /** Fired before the transport sends the message. */
59
+ onSend?: (ctx: SmsHookContext) => void | Promise<void>;
60
+ /**
61
+ * Fired after a successful send.
62
+ * @param durationMs — elapsed milliseconds from send start to success (optional third argument).
63
+ */
64
+ onSuccess?: (ctx: SmsHookContext, result: SmsSendResult, durationMs?: number) => void | Promise<void>;
65
+ /**
66
+ * Fired when a send throws (error is re-thrown after the hook runs).
67
+ * @param durationMs — elapsed milliseconds from send start to failure (optional third argument).
68
+ */
69
+ onError?: (ctx: SmsHookContext, error: unknown, durationMs?: number) => void | Promise<void>;
70
+ }
71
+ /**
72
+ * An SMS plugin transforms {@link SmsOptions} before the transport sends.
73
+ * Plugins run sequentially. Each receives the output of the previous.
74
+ * Return a new options object — do not mutate the input.
75
+ */
76
+ export type SmsPlugin = ((options: SmsOptions) => SmsOptions) | ((options: SmsOptions) => Promise<SmsOptions>);
package/dist/core/smtp.js CHANGED
@@ -1,3 +1,3 @@
1
- import{H as a,I as b,J as c,K as d,L as e,M as f,N as g,O as h,P as i,Q as j,R as k,S as l}from"../chunk-8phpsns1.js";import"../chunk-5scdgffb.js";import"../chunk-6bkj4y4r.js";import"../chunk-nbmq8ejv.js";export{f as selectAuthMethod,d as parseResponse,g as parseEHLO,l as encodeLine,c as encodeCommand,j as encodeAuthLoginUser,i as encodeAuthLoginPass,k as encodeAuthCramResponse,a as computeCRAMMD5,h as assertResponse,e as accumulateResponse,b as SMTPError};
1
+ import{A as f,B as g,C as h,D as i,E as j,F as k,G as l,v as a,w as b,x as c,y as d,z as e}from"../chunk-n0qeyzqm.js";import"../chunk-ywq6s10g.js";import"../chunk-tamww15j.js";import"../chunk-6npp3x3c.js";export{f as selectAuthMethod,d as parseResponse,g as parseEHLO,l as encodeLine,c as encodeCommand,j as encodeAuthLoginUser,i as encodeAuthLoginPass,k as encodeAuthCramResponse,a as computeCRAMMD5,h as assertResponse,e as accumulateResponse,b as SMTPError};
2
2
 
3
- //# debugId=48767988D5CC2FF564756E2164756E21
3
+ //# debugId=323A7FADB3AD978F64756E2164756E21