@visulima/notification 0.0.1 → 1.0.0-alpha.2

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 (149) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE.md +874 -0
  3. package/README.md +313 -6
  4. package/dist/channels/email/index.d.ts +46 -0
  5. package/dist/channels/email/index.js +1 -0
  6. package/dist/channels/inapp/index.d.ts +99 -0
  7. package/dist/channels/inapp/index.js +1 -0
  8. package/dist/digest/index.d.ts +128 -0
  9. package/dist/digest/index.js +1 -0
  10. package/dist/events/index.d.ts +37 -0
  11. package/dist/events/index.js +1 -0
  12. package/dist/i18n/index.d.ts +42 -0
  13. package/dist/i18n/index.js +1 -0
  14. package/dist/index.d.ts +174 -0
  15. package/dist/index.js +1 -0
  16. package/dist/layouts/index.d.ts +45 -0
  17. package/dist/layouts/index.js +1 -0
  18. package/dist/middleware/index.d.ts +382 -0
  19. package/dist/middleware/index.js +1 -0
  20. package/dist/packem_shared/MemoryDigestStore-Bw-sE5MI.js +1 -0
  21. package/dist/packem_shared/MemoryEventStore-BGD_xRxu.js +1 -0
  22. package/dist/packem_shared/MemoryInAppStore-48qlrDCx.js +1 -0
  23. package/dist/packem_shared/MemoryQueue-CXgWoZoY.js +1 -0
  24. package/dist/packem_shared/Notification-B22tuJPy.js +1 -0
  25. package/dist/packem_shared/NotificationError-DVfMJQ1u.js +1 -0
  26. package/dist/packem_shared/NotificationMessageBuilder-Br_aZusY.js +1 -0
  27. package/dist/packem_shared/RequiredOptionError-CuL42XRq.js +1 -0
  28. package/dist/packem_shared/UnstorageDigestStore-QAcO3Vb_.js +1 -0
  29. package/dist/packem_shared/UnstorageInAppStore-Bq2LoDMO.js +1 -0
  30. package/dist/packem_shared/apnsProvider-C9WSv5sc.js +1 -0
  31. package/dist/packem_shared/circuitBreakerMiddleware-BlZN7SVT.js +1 -0
  32. package/dist/packem_shared/composeMiddleware-D6dR8DUS.js +1 -0
  33. package/dist/packem_shared/createDigester-vMNPejGF.js +1 -0
  34. package/dist/packem_shared/createNotificationWorkflow-B70OgeED.js +1 -0
  35. package/dist/packem_shared/createQueueWorker-DjQBxjXc.js +1 -0
  36. package/dist/packem_shared/createTranslator-CY91C2eS.js +1 -0
  37. package/dist/packem_shared/credentials-CG0zkn-W.js +1 -0
  38. package/dist/packem_shared/dedupeMiddleware-CQmPJd_h.js +1 -0
  39. package/dist/packem_shared/defineLayout-73TNxYPq.js +1 -0
  40. package/dist/packem_shared/defineProvider-C9q-7iwt.js +1 -0
  41. package/dist/packem_shared/discordProvider-ewPfe-J7.js +1 -0
  42. package/dist/packem_shared/emailChannel-CNDlX96Q.js +1 -0
  43. package/dist/packem_shared/expoProvider-CXq101FD.js +1 -0
  44. package/dist/packem_shared/failoverProvider-D1dAmW8U.js +1 -0
  45. package/dist/packem_shared/fcmProvider-WdJGZ55l.js +1 -0
  46. package/dist/packem_shared/getHeader-C_gkdnMQ.js +1 -0
  47. package/dist/packem_shared/hmacBase64-BhhoX0uo.js +1 -0
  48. package/dist/packem_shared/http-BMYcUNBU.js +1 -0
  49. package/dist/packem_shared/id-C_FRCsUM.js +1 -0
  50. package/dist/packem_shared/inAppProvider-ElbahgSj.js +1 -0
  51. package/dist/packem_shared/loggingMiddleware-Cs6EDLcL.js +1 -0
  52. package/dist/packem_shared/messageBirdProvider-CHUFB0hd.js +1 -0
  53. package/dist/packem_shared/mockProvider-BLwTZlkw.js +1 -0
  54. package/dist/packem_shared/msTeamsProvider-DBhrmuSd.js +1 -0
  55. package/dist/packem_shared/notification.d-DFCVTods.d.ts +104 -0
  56. package/dist/packem_shared/otelProvider-b6LVo0BS.js +1 -0
  57. package/dist/packem_shared/plivoProvider-CIOl6DWw.js +1 -0
  58. package/dist/packem_shared/provider.d-BQAGriz6.d.ts +33 -0
  59. package/dist/packem_shared/rateLimitMiddleware-DWmEYq-m.js +1 -0
  60. package/dist/packem_shared/retryMiddleware-uUJDcUoP.js +1 -0
  61. package/dist/packem_shared/roundRobinProvider-BetXJUUm.js +1 -0
  62. package/dist/packem_shared/slackProvider-N1wNtAZ3.js +1 -0
  63. package/dist/packem_shared/slackWebhook-GEx_GdcV.js +1 -0
  64. package/dist/packem_shared/sms-CU-OZvNU.js +1 -0
  65. package/dist/packem_shared/snsProvider-KCciJ-nj.js +4 -0
  66. package/dist/packem_shared/snsWebhook-ChZnBBT3.js +1 -0
  67. package/dist/packem_shared/standardWebhook-B-LxP8U7.js +1 -0
  68. package/dist/packem_shared/suppressionMiddleware-CY-Qo1A_.js +1 -0
  69. package/dist/packem_shared/telegramProvider-jqR5BhJL.js +1 -0
  70. package/dist/packem_shared/telemetryMiddleware-B1N4k171.js +1 -0
  71. package/dist/packem_shared/telnyxProvider-C8TMPir1.js +1 -0
  72. package/dist/packem_shared/tracer.d-Co1s_NwG.d.ts +494 -0
  73. package/dist/packem_shared/twilioProvider-BxI0e6LX.js +1 -0
  74. package/dist/packem_shared/twilioWebhook-F0mTmGXD.js +1 -0
  75. package/dist/packem_shared/types.d-67TogK4k.d.ts +33 -0
  76. package/dist/packem_shared/types.d-C1GoEAlI.d-Bv3grfyD.d.ts +83 -0
  77. package/dist/packem_shared/types.d-CximoA8M.d.ts +29 -0
  78. package/dist/packem_shared/types.d-GUzX1jTk.d.ts +235 -0
  79. package/dist/packem_shared/types.d-td-F2Oh0.d.ts +10 -0
  80. package/dist/packem_shared/unstorage.DqlWKU2I.d-CogP3Etw.d.ts +120 -0
  81. package/dist/packem_shared/vonageProvider-BF76ca89.js +1 -0
  82. package/dist/packem_shared/webPushProvider-B962RwsA.js +1 -0
  83. package/dist/packem_shared/webcrypto-bY7wJCB5.js +1 -0
  84. package/dist/packem_shared/webhookProvider-BALSb2pc.js +1 -0
  85. package/dist/preferences/index.d.ts +49 -0
  86. package/dist/preferences/index.js +1 -0
  87. package/dist/providers/chat/discord/index.d.ts +14 -0
  88. package/dist/providers/chat/discord/index.js +1 -0
  89. package/dist/providers/chat/msteams/index.d.ts +14 -0
  90. package/dist/providers/chat/msteams/index.js +1 -0
  91. package/dist/providers/chat/slack/index.d.ts +19 -0
  92. package/dist/providers/chat/slack/index.js +1 -0
  93. package/dist/providers/chat/telegram/index.d.ts +18 -0
  94. package/dist/providers/chat/telegram/index.js +1 -0
  95. package/dist/providers/failover/index.d.ts +16 -0
  96. package/dist/providers/failover/index.js +1 -0
  97. package/dist/providers/mock/index.d.ts +29 -0
  98. package/dist/providers/mock/index.js +1 -0
  99. package/dist/providers/opentelemetry/index.d.ts +21 -0
  100. package/dist/providers/opentelemetry/index.js +1 -0
  101. package/dist/providers/push/apns/index.d.ts +24 -0
  102. package/dist/providers/push/apns/index.js +1 -0
  103. package/dist/providers/push/expo/index.d.ts +14 -0
  104. package/dist/providers/push/expo/index.js +1 -0
  105. package/dist/providers/push/fcm/index.d.ts +26 -0
  106. package/dist/providers/push/fcm/index.js +1 -0
  107. package/dist/providers/push/web-push/index.d.ts +36 -0
  108. package/dist/providers/push/web-push/index.js +1 -0
  109. package/dist/providers/roundrobin/index.d.ts +17 -0
  110. package/dist/providers/roundrobin/index.js +1 -0
  111. package/dist/providers/sms/messagebird/index.d.ts +16 -0
  112. package/dist/providers/sms/messagebird/index.js +1 -0
  113. package/dist/providers/sms/plivo/index.d.ts +18 -0
  114. package/dist/providers/sms/plivo/index.js +1 -0
  115. package/dist/providers/sms/sns/index.d.ts +21 -0
  116. package/dist/providers/sms/sns/index.js +1 -0
  117. package/dist/providers/sms/telnyx/index.d.ts +18 -0
  118. package/dist/providers/sms/telnyx/index.js +1 -0
  119. package/dist/providers/sms/twilio/index.d.ts +20 -0
  120. package/dist/providers/sms/twilio/index.js +1 -0
  121. package/dist/providers/sms/vonage/index.d.ts +18 -0
  122. package/dist/providers/sms/vonage/index.js +1 -0
  123. package/dist/providers/webhook/index.d.ts +18 -0
  124. package/dist/providers/webhook/index.js +1 -0
  125. package/dist/queue/bullmq-queue.d.ts +58 -0
  126. package/dist/queue/bullmq-queue.js +1 -0
  127. package/dist/queue/index.d.ts +50 -0
  128. package/dist/queue/index.js +1 -0
  129. package/dist/queue/pg-boss-queue.d.ts +66 -0
  130. package/dist/queue/pg-boss-queue.js +1 -0
  131. package/dist/queue/sqs-queue.d.ts +91 -0
  132. package/dist/queue/sqs-queue.js +1 -0
  133. package/dist/queue/unstorage-queue.d.ts +30 -0
  134. package/dist/queue/unstorage-queue.js +1 -0
  135. package/dist/routing/index.d.ts +35 -0
  136. package/dist/routing/index.js +1 -0
  137. package/dist/template-engines/handlebars.d.ts +16 -0
  138. package/dist/template-engines/handlebars.js +1 -0
  139. package/dist/template-engines/liquid.d.ts +16 -0
  140. package/dist/template-engines/liquid.js +1 -0
  141. package/dist/template-engines/string.d.ts +20 -0
  142. package/dist/template-engines/string.js +1 -0
  143. package/dist/utils/result.d.ts +54 -0
  144. package/dist/utils/result.js +1 -0
  145. package/dist/webhooks/index.d.ts +110 -0
  146. package/dist/webhooks/index.js +1 -0
  147. package/dist/workflow/index.d.ts +83 -0
  148. package/dist/workflow/index.js +1 -0
  149. package/package.json +283 -43
@@ -0,0 +1 @@
1
+ import h from"./NotificationError-DVfMJQ1u.js";import{composeMiddleware as u}from"./composeMiddleware-D6dR8DUS.js";const c=(n,s,t)=>{if(n.success&&n.data)return{channel:s,messageId:n.data.messageId,provider:n.data.provider??t,response:n.data.response,successful:!0,timestamp:n.data.timestamp};const{error:e}=n;let i;return e instanceof Error?i=e.message:e==null?i="Unknown error":typeof e=="string"?i=e:i=JSON.stringify(e),{channel:s,errorMessages:[i],provider:t,successful:!1}};class d{#s;#e=[];#t=new Set;#n;constructor(s){this.#s=s}use(s){return this.#e.push(s),this}setLogger(s){return this.#n=s,this}getProvider(s){return this.#s[s]}async sendToChannel(s,t){const e=this.#s[s];if(!e)return{channel:s,errorMessages:[`No provider registered for channel "${s}"`],successful:!1};try{await this.#i(e)}catch(r){return c({error:r,success:!1},s,e.id)}const i=async r=>e.send(r.payload),a=await u(this.#e,i)({channel:s,payload:t,provider:e.id});return c(a,s,e.id)}async send(s){const t=Object.keys(s);if(t.length===0)throw new h("notification","send() called with an empty message");return Promise.all(t.map(async e=>this.sendToChannel(e,s[e])))}async*sendMany(s,t={}){const e=Math.max(1,t.concurrency??10),i=[...s];let a=0;for(;a<i.length;){const r=i.slice(a,a+e);a+=e;const l=await Promise.all(r.map(async o=>this.send(o)));for(const o of l)yield o}}async initialize(){await Promise.all(Object.values(this.#s).map(async s=>this.#i(s)))}async shutdown(){await Promise.all(Object.values(this.#s).map(async s=>{const{shutdown:t}=s;t&&await t()}))}async#i(s){this.#t.has(s.id)||(await s.initialize(),this.#n?.debug(`[@visulima/notification] initialized provider "${s.id}"`),this.#t.add(s.id))}}const p=n=>new d(n),y=async(n,s,t)=>new d({[n]:s}).sendToChannel(n,t);export{d as Notification,p as createNotification,y as send};
@@ -0,0 +1 @@
1
+ import{createRequire as h}from"node:module";const d=h(import.meta.url),t=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,m=o=>{if(typeof t<"u"&&t.versions&&t.versions.node){const[e,r]=t.versions.node.split(".").map(Number);if(e>22||e===22&&r>=3||e===20&&r>=16)return t.getBuiltinModule(o)}return d(o)},{createRequire:c}=m("node:module"),f=new Map([["Error",Error],["EvalError",EvalError],["RangeError",RangeError],["ReferenceError",ReferenceError],["SyntaxError",SyntaxError],["TypeError",TypeError],["URIError",URIError]]);typeof AggregateError<"u"&&f.set("AggregateError",AggregateError);const E=c(import.meta.url),i=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,y=o=>{if(typeof i<"u"&&i.versions&&i.versions.node){const[e,r]=i.versions.node.split(".").map(Number);if(e>22||e===22&&r>=3||e===20&&r>=16)return i.getBuiltinModule(o)}return E(o)};y("node:util");class b extends Error{loc;title;hint;type="VisulimaError";constructor({cause:e,hint:r,location:s,message:l,name:u,stack:p,title:g}){super(l,{cause:e}),this.title=g,this.name=u,this.stack=p??this.stack,this.loc=s,this.hint=r}setLocation(e){this.loc=e}setName(e){this.name=e}setMessage(e){this.message=e}setHint(e){this.hint=e}}const v=c(import.meta.url),n=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,a=o=>{if(typeof n<"u"&&n.versions&&n.versions.node){const[e,r]=n.versions.node.split(".").map(Number);if(e>22||e===22&&r>=3||e===20&&r>=16)return n.getBuiltinModule(o)}return v(o)};a("node:fs");a("node:path");a("node:url");class T extends b{component;code;constructor(e,r,s){super({cause:s?.cause,hint:s?.hint,message:`[@visulima/notification] [${e}] ${r}`,name:"NotificationError",title:`Notification ${e} Error`}),this.component=e,this.code=s?.code}}export{T as default};
@@ -0,0 +1 @@
1
+ class r{#t={};#i;#s;sms(t){return this.#t.sms=t,this}push(t){return this.#t.push=t,this}chat(t){return this.#t.chat=t,this}inApp(t){return this.#t.inapp=t,this}webhook(t){return this.#t.webhook=t,this}email(t){return this.#t.email=t,this}metadata(t){return this.#i=t,this}idempotencyKey(t){return this.#s=t,this}build(){const t=this.#i,s=this.#s;if(t===void 0&&s===void 0)return{...this.#t};const e={},o=Object.keys(this.#t);for(const h of o){const n=this.#t[h];if(n!==void 0){const i={};t!==void 0&&(i.metadata=t),s!==void 0&&(i.idempotencyKey=s),Object.assign(i,n),e[h]=i}}return e}}const c=()=>new r;export{r as NotificationMessageBuilder,c as createNotificationMessage};
@@ -0,0 +1 @@
1
+ import s from"./NotificationError-DVfMJQ1u.js";class a extends s{constructor(o,e){const r=Array.isArray(e)?`Missing required options: ${e.map(i=>`'${i}'`).join(", ")}`:`Missing required option: '${e}'`;super(o,r,{hint:Array.isArray(e)?`Please provide the following required options: ${e.map(i=>`'${i}'`).join(", ")}`:`Please provide the required option: '${e}'`}),this.name="RequiredOptionError"}}export{a as default};
@@ -0,0 +1 @@
1
+ const a="digest:w:";class o{#t;constructor(t){this.#t=t}async append(t,s,i){const e=await this.#t.getItem(a+t);return e!==null?(e.events.push(s),await this.#t.setItem(a+t,e),!1):(await this.#t.setItem(a+t,{events:[s],key:t,wakeAt:i}),!0)}async read(t){return await this.#t.getItem(a+t)??void 0}async remove(t){await this.#t.removeItem(a+t)}async due(t,s){const i=await this.#t.getKeys(a);return(await Promise.all(i.map(async e=>this.#t.getItem(e)))).filter(e=>e!==null&&e.wakeAt<=t).toSorted((e,n)=>e.wakeAt-n.wakeAt).slice(0,s).map(e=>e.key)}}export{o as default};
@@ -0,0 +1 @@
1
+ import{n as c}from"./id-C_FRCsUM.js";class h{#t;#s;constructor(t,a="notification:inapp"){this.#t=t,this.#s=a}async add(t){const a={...t,createdAt:Date.now(),id:t.id??c("inapp"),read:!1};return await this.#t.setItem(this.#a(a.id),a),await this.#n(a.subscriberId,a.id),a}async list(t,a={}){const i=await this.#r(t),s=(a.unreadOnly?i.filter(e=>!e.read):i).toSorted((e,n)=>n.createdAt-e.createdAt);return a.limit?s.slice(0,a.limit):s}async markRead(t){const a=await this.#t.getItem(this.#a(t));a&&(a.read=!0,await this.#t.setItem(this.#a(t),a))}async markAllRead(t){const a=await this.#i(t);await Promise.all(a.map(async i=>this.markRead(i)))}async unreadCount(t){return(await this.#r(t)).filter(a=>!a.read).length}async remove(t){const a=await this.#t.getItem(this.#a(t));await this.#t.removeItem(this.#a(t)),a&&await this.#c(a.subscriberId,t)}#a(t){return`${this.#s}:item:${t}`}#e(t){return`${this.#s}:index:${t}`}async#i(t){return await this.#t.getItem(this.#e(t))??[]}async#r(t){const a=await this.#i(t);return(await Promise.all(a.map(async i=>await this.#t.getItem(this.#a(i))))).filter(i=>i!==null)}async#n(t,a){const i=await this.#i(t);i.includes(a)||(i.push(a),await this.#t.setItem(this.#e(t),i))}async#c(t,a){const i=(await this.#i(t)).filter(s=>s!==a);await this.#t.setItem(this.#e(t),i)}}const d=(r,t)=>new h(r,t);export{h as UnstorageInAppStore,d as createUnstorageInAppStore};
@@ -0,0 +1 @@
1
+ import{createRequire as I}from"node:module";import g from"./RequiredOptionError-CuL42XRq.js";import{defineProvider as j}from"./defineProvider-C9q-7iwt.js";import{t as k}from"./credentials-CG0zkn-W.js";import{a as N}from"./sms-CU-OZvNU.js";const S=I(import.meta.url),f=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,y=o=>{if(typeof f<"u"&&f.versions&&f.versions.node){const[t,r]=f.versions.node.split(".").map(Number);if(t>22||t===22&&r>=3||t===20&&r>=16)return f.getBuiltinModule(o)}return S(o)},{sign:T,createPrivateKey:$}=y("node:crypto"),{connect:P}=y("node:http2"),J="https://api.push.apple.com",K="https://api.sandbox.push.apple.com",O=3e3*1e3,R=200,h=o=>Buffer.from(o,"utf8").toString("base64url"),q=(o,t,r)=>{const l=h(JSON.stringify({alg:"ES256",kid:o.keyId,typ:"JWT"})),c=h(JSON.stringify({iat:r,iss:o.teamId})),u=`${l}.${c}`,n=T("sha256",Buffer.from(u,"utf8"),{dsaEncoding:"ieee-p1363",key:t}).toString("base64url");return`${u}.${n}`},z=j(o=>{const t=o??{};if(!t.teamId)throw new g("apns","teamId");if(!t.keyId)throw new g("apns","keyId");if(!t.signingKey)throw new g("apns","signingKey");if(!t.bundleId)throw new g("apns","bundleId");const r=t.production?J:K;let l,c,u=0,n;const b=()=>(l??=$({format:"pem",key:t.signingKey}),l),w=()=>{const e=Date.now();return(!c||e-u>O)&&(c=q(t,b(),Math.floor(e/1e3)),u=e),c},_=()=>((!n||n.closed||n.destroyed)&&(n=P(r),n.on("error",e=>{t.logger?.warn(`[@visulima/notification] [apns] HTTP/2 session error: ${e.message}`)})),n),v=async(e,m)=>new Promise(a=>{const i=_().request({":method":"POST",":path":`/3/device/${e}`,"apns-topic":t.bundleId,authorization:`bearer ${w()}`,"content-type":"application/json"});let d=0,p="";i.setEncoding("utf8"),i.on("response",s=>{d=s[":status"]??0}),i.on("data",s=>{p+=s}),i.on("error",s=>{a({error:s.message,id:e,status:"failed"})}),i.on("end",()=>{if(d===R){a({id:e,messageId:e,status:"sent"});return}let s=`HTTP ${String(d)}`;if(p)try{s=JSON.parse(p).reason??s}catch{s=p}a({error:s,id:e,status:"failed"})}),i.end(m)});return{channel:"push",endpoint:r,features:{batchSending:!1,media:!0,richContent:!0},id:"apns",initialize:()=>{},isAvailable:()=>!0,options:t,send:async e=>{const m=k(e.to),a={alert:{body:e.body,title:e.title}};e.sound!==void 0&&(a.sound=e.sound),e.badge!==void 0&&(a.badge=e.badge);const i=JSON.stringify({...e.data,aps:a}),d=[];for(const p of m)d.push(await v(p,i));return N("push","apns",d)},shutdown:()=>{n&&!n.closed&&n.close(),n=void 0}}});export{z as default};
@@ -0,0 +1 @@
1
+ import u from"./NotificationError-DVfMJQ1u.js";const p=(o={})=>{const i=o.threshold??5,n=o.resetTimeout??3e4;let r=0,s=0,e="closed";return async(t,a)=>{if(e==="open")if(Date.now()-s>=n)e="half-open";else return{error:new u("circuit-breaker",`Circuit open for "${t.provider}"`),success:!1};const c=await a(t);return c.success?(r=0,e="closed"):(r+=1,r>=i&&(e="open",s=Date.now())),c}};export{p as circuitBreakerMiddleware};
@@ -0,0 +1 @@
1
+ const n=(o,t)=>{let e=t;for(const r of o.toReversed()){const s=e;e=c=>r(c,s)}return e};export{n as composeMiddleware};
@@ -0,0 +1 @@
1
+ import r from"./NotificationError-DVfMJQ1u.js";import u from"./MemoryDigestStore-Bw-sE5MI.js";const d={days:864e5,hours:36e5,milliseconds:1,minutes:6e4,ms:1,seconds:1e3,weeks:6048e5},c=async(e,t)=>{if(typeof e=="number"){if(!Number.isFinite(e))throw new r("digest",`Window must be a finite number of milliseconds. Received: ${String(e)}.`);return t+Math.max(0,e)}if("cron"in e){let i;try{i=await import("croner")}catch(o){throw new r("digest","croner is not installed (required for cron windows). Please install it: pnpm add croner",{cause:o})}const n=new i.Cron(e.cron,e.tz?{timezone:e.tz}:{}).nextRun(new Date(t));if(n===null)throw new r("digest",`Cron expression "${e.cron}" has no future occurrence.`);return n.getTime()}if(!Number.isFinite(e.amount))throw new r("digest",`Window amount must be a finite number. Received: ${String(e.amount)}.`);return t+Math.max(0,e.amount)*d[e.unit]},m=()=>globalThis.crypto.randomUUID(),p=e=>{const t=e.store??new u;return{add:async i=>{const n=e.key(i),o=typeof e.window=="function"?e.window(i):e.window,s={id:m(),payload:i,time:Date.now()};return t.append(n,s,await c(o,Date.now()))},sweep:async(i=Date.now(),n=100)=>{if(!Number.isInteger(n)||n<=0)throw new r("digest",`sweep limit must be a positive integer. Received: ${String(n)}.`);const o=await t.due(i,n);let s=0;for(const w of o){const a=await t.read(w);a!==void 0&&a.events.length>0&&(await e.onFlush(a.events,w),s+=1),a!==void 0&&await t.remove(w)}return s}}};export{p as default};
@@ -0,0 +1 @@
1
+ import{defineWorkflow as y}from"@visulima/workflow";import c from"./NotificationError-DVfMJQ1u.js";const m=(d,p)=>{const l=e=>{const n=async(a,s,o,r)=>e.step(s,async()=>{if(r?.skip&&await r.skip({payload:e.payload}))return;const u=await o({payload:e.payload}),t=await d.sendToChannel(a,u);if(r?.throwOnFailure&&!t.successful)throw new c(a,t.errorMessages.join("; "));return t}),i={chat:(a,s,o)=>n("chat",a,s,o),custom:(a,s)=>e.step(a,s),delay:(a,s)=>e.sleep(a,s),email:(a,s,o)=>n("email",a,s,o),inApp:(a,s,o)=>n("inapp",a,s,o),push:(a,s,o)=>n("push",a,s,o),sms:(a,s,o)=>n("sms",a,s,o),webhook:(a,s,o)=>n("webhook",a,s,o)};return p.run({payload:e.payload,runId:e.runId,step:i})};return y({id:p.id,payload:p.payload,run:l,tags:p.tags})};export{m as default};
@@ -0,0 +1 @@
1
+ const l=async e=>new Promise(n=>{setTimeout(n,e)}),w=(e,n,s={})=>{const o=s.maxAttempts??5,i=s.pollInterval??1e3,u=s.backoff??(a=>Math.min(3e4,1e3*2**(a-1)));let t=!1;const r=async()=>{const a=await e.reserve();if(!a)return!1;const c=await n.send(a.message);return c.some(m=>!m.successful)?a.attempts>=o?(await e.ack(a.id),s.onDrop?.(a,c)):await e.retry(a.id,u(a.attempts)):await e.ack(a.id),!0},f=async()=>{for(;t;)await r()||await l(i)};return{drain:async()=>{for(;await r(););},start:()=>{t||(t=!0,f().catch(()=>{t=!1}))},stop:()=>{t=!1}}};export{w as createQueueWorker};
@@ -0,0 +1 @@
1
+ import{IntlMessageFormat as f}from"intl-messageformat";import g from"./NotificationError-DVfMJQ1u.js";const u=/[-_]/u,m=e=>e.split(u)[0]??e,$=e=>{const n=new Map,c=(t,o)=>{for(const s of[t,m(t),e.fallbackLocale,m(e.fallbackLocale)]){const a=e.messages[s]?.[o];if(a!==void 0)return{locale:s,message:a}}};return{has:(t,o)=>c(t,o)!==void 0,translate:(t,o,s)=>{const a=c(t,o);if(a===void 0)return e.onMissing?e.onMissing(t,o):o;const r=`${a.locale}\0${o}`;let l=n.get(r);l===void 0&&(l=new f(a.message,a.locale.replaceAll("_","-")),n.set(r,l));try{return String(l.format(s))}catch(i){throw new g("i18n",`Failed to format message "${o}" for locale "${a.locale}": ${i.message}`,{cause:i})}}}};export{$ as default};
@@ -0,0 +1 @@
1
+ const s=(t,a)=>`Basic ${btoa(`${t}:${a}`)}`,i=t=>Array.isArray(t)?t:[t];export{s as b,i as t};
@@ -0,0 +1 @@
1
+ const i=(s={})=>{const r=s.ttl??6e4,t=new Map;return async(e,a)=>{const n=s.keyFn?s.keyFn(e):e.payload.idempotencyKey;if(!n)return a(e);const o=Date.now();for(const[d,p]of t)p<o&&t.delete(d);if(t.has(n))return{data:{channel:e.channel,messageId:`deduped:${n}`,provider:e.provider,sent:!1,timestamp:new Date},success:!0};const c=await a(e);return c.success&&t.set(n,o+r),c}};export{i as dedupeMiddleware};
@@ -0,0 +1 @@
1
+ import s from"../template-engines/string.js";const c=r=>{const t=r.render??s,e=r.slot??"content";return{render:async(n,o={})=>t(r.template,{...o,[e]:n})}};export{c as default};
@@ -0,0 +1 @@
1
+ const o=e=>e;export{o as defineProvider};
@@ -0,0 +1 @@
1
+ import s from"./NotificationError-DVfMJQ1u.js";import c from"./RequiredOptionError-CuL42XRq.js";import{defineProvider as l}from"./defineProvider-C9q-7iwt.js";import{r as m}from"./http-BMYcUNBU.js";import{n as u}from"./id-C_FRCsUM.js";const k=l(a=>{const e=a??{};if(!e.webhookUrl)throw new c("discord","webhookUrl");const i=e.retries??3,d=e.timeout??3e4,n=e.webhookUrl.includes("?")?`${e.webhookUrl}&wait=true`:`${e.webhookUrl}?wait=true`;return{channel:"chat",endpoint:e.webhookUrl,features:{batchSending:!1,richContent:!0},id:"discord",initialize:()=>{},isAvailable:()=>!!e.webhookUrl,options:e,send:async r=>{const o={content:r.text,embeds:r.blocks,username:e.username};r.threadId&&(o.thread_id=r.threadId);const t=await m(n,{body:JSON.stringify(o),headers:{"Content-Type":"application/json"},method:"POST",timeout:d},i);return!t.success||!t.data?{error:new s("discord",t.error instanceof Error?t.error.message:"Request failed"),success:!1}:t.data.status>=400?{error:new s("discord",t.data.body.message??`HTTP ${String(t.data.status)}`),success:!1}:{data:{channel:"chat",messageId:t.data.body.id??u("discord"),provider:"discord",sent:!0,timestamp:new Date},success:!0}},validateCredentials:()=>!!e.webhookUrl}});export{k as default};
@@ -0,0 +1 @@
1
+ import n from"./NotificationError-DVfMJQ1u.js";const c=(t,r={})=>{const a=r.id??"email";return{channel:"email",features:{attachments:!0,batchSending:!0,richContent:!0,templates:!0,tracking:!0},id:a,initialize:()=>{},isAvailable:()=>!0,send:async s=>{const e=await t.send(s);return!e.success||!e.data?{error:new n(a,e.error instanceof Error?e.error.message:"Email send failed",{cause:e.error}),success:!1}:{data:{channel:"email",messageId:e.data.messageId,provider:e.data.provider??a,response:e.data.response,sent:e.data.sent??!0,timestamp:e.data.timestamp??new Date},success:!0}}}};export{c as emailChannel};
@@ -0,0 +1 @@
1
+ import d from"./NotificationError-DVfMJQ1u.js";import{defineProvider as x}from"./defineProvider-C9q-7iwt.js";import{t as T}from"./credentials-CG0zkn-W.js";import{r as w}from"./http-BMYcUNBU.js";import{n as A}from"./id-C_FRCsUM.js";const $="https://exp.host",R=x(l=>{const t=l??{},u=t.endpoint??$,f=t.retries??3,h=t.timeout??3e4,g=`${u}/--/api/v2/push/send`;return{channel:"push",endpoint:u,features:{batchSending:!0,media:!0,richContent:!0},id:"expo",initialize:()=>{},isAvailable:()=>!0,options:t,send:async s=>{const p=T(s.to),y=p.map(r=>({badge:s.badge,body:s.body,data:s.data,sound:s.sound??"default",title:s.title,to:r})),c={"Content-Type":"application/json"};t.accessToken&&(c.Authorization=`Bearer ${t.accessToken}`);const e=await w(g,{body:JSON.stringify(y),headers:c,method:"POST",timeout:h},f);if(!e.success||!e.data)return{error:new d("expo",e.error instanceof Error?e.error.message:"Request failed"),success:!1};if(e.data.status>=400)return{error:new d("expo",`HTTP ${String(e.data.status)}`),success:!1};const a=e.data.body.data;let n;Array.isArray(a)?n=a:n=a?[a]:[];const i=p.map((r,b)=>{const o=n[b];return o?.status==="error"?{error:o.details?.error??o.message??"error",id:r,status:"failed"}:{id:r,messageId:o?.id,status:"sent"}}),m=i.filter(r=>r.status==="sent");return m.length===0?{error:new d("expo",i[0]?.error??"All tokens failed"),success:!1}:{data:{channel:"push",messageId:m[0]?.messageId??A("expo"),provider:"expo",recipients:i,sent:!0,timestamp:new Date},success:!0}}}});export{R as default};
@@ -0,0 +1 @@
1
+ import l from"./NotificationError-DVfMJQ1u.js";const d=(r,o={})=>{if(r.length===0)throw new l("failover","At least one provider is required");const n=o.id??"failover";return{channel:r[0].channel,id:n,initialize:async()=>{await Promise.all(r.map(async e=>e.initialize()))},isAvailable:async()=>(await Promise.all(r.map(async e=>e.isAvailable()))).some(Boolean),send:async e=>{const s=[];for(const a of r){const i=await a.send(e);if(i.success)return i;const t=i.error instanceof Error?i.error.message:String(i.error);s.push(`${a.id}: ${t}`),o.logger?.warn(`[@visulima/notification] [${n}] provider "${a.id}" failed, trying next`)}return{error:new l(n,`All ${String(r.length)} providers failed: ${s.join("; ")}`),success:!1}}}};export{d as failoverProvider};
@@ -0,0 +1 @@
1
+ import c from"./NotificationError-DVfMJQ1u.js";import d from"./RequiredOptionError-CuL42XRq.js";import{defineProvider as h}from"./defineProvider-C9q-7iwt.js";import{t as y}from"./credentials-CG0zkn-W.js";import{r as T}from"./http-BMYcUNBU.js";const I=h(m=>{const e=m??{};if(!e.projectId)throw new d("fcm","projectId");if(!e.accessToken&&!e.getAccessToken)throw new d("fcm",["accessToken","getAccessToken"]);const i=e.endpoint??"https://fcm.googleapis.com",f=e.retries??3,u=e.timeout??3e4,p=`${i}/v1/projects/${e.projectId}/messages:send`,l=s=>{if(s)return Object.fromEntries(Object.entries(s).map(([r,o])=>[r,typeof o=="string"?o:JSON.stringify(o)]))},g=async(s,r,o)=>{const a={data:l(r.data),notification:{body:r.body,image:r.imageUrl,title:r.title},token:s},t=await T(p,{body:JSON.stringify({message:a}),headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},method:"POST",timeout:u},f);return!t.success||!t.data?{error:t.error instanceof Error?t.error.message:"Request failed",id:s,status:"failed"}:t.data.status>=400?{error:t.data.body.error?.message??`HTTP ${String(t.data.status)}`,id:s,status:"failed"}:{id:s,messageId:t.data.body.name,status:"sent"}};return{channel:"push",endpoint:i,features:{batchSending:!1,media:!0,richContent:!0},id:"fcm",initialize:()=>{},isAvailable:()=>!!(e.projectId&&(e.accessToken??e.getAccessToken)),options:e,send:async s=>{let r;try{r=e.getAccessToken?await e.getAccessToken():e.accessToken}catch(n){return{error:new c("fcm","Failed to obtain access token",{cause:n}),success:!1}}const o=y(s.to),a=[];for(const n of o)a.push(await g(n,s,r));const t=a.filter(n=>n.status==="sent");return t.length===0?{error:new c("fcm",a[0]?.error??"All tokens failed"),success:!1}:{data:{channel:"push",messageId:t[0]?.messageId??"",provider:"fcm",recipients:a,sent:!0,timestamp:new Date},success:!0}}}});export{I as default};
@@ -0,0 +1 @@
1
+ const a=t=>{try{const r=JSON.parse(t);return typeof r=="object"&&r!==null&&!Array.isArray(r)?r:void 0}catch{return}},c=(t,r)=>{const s=r.toLowerCase();for(const o of Object.keys(t))if(o.toLowerCase()===s){const e=t[o];return Array.isArray(e)?e[0]:e}};export{c as getHeader,a as tryParseObject};
@@ -0,0 +1 @@
1
+ import{g as o,i as r,b as i}from"./webcrypto-bY7wJCB5.js";const l=300,c=async(a,t,e)=>i(await r(a,t,e)),f=async(a,t,e)=>o(await r(a,t,e)),h=(a,t)=>{if(a.length!==t.length)return!1;let e=0;for(let n=0;n<a.length;n+=1)e|=(a.codePointAt(n)??0)^(t.codePointAt(n)??0);return e===0},m=(a,t)=>{if(a===void 0)return!1;const e=Number.parseInt(a,10);if(Number.isNaN(e))return!1;const n=Math.floor(Date.now()/1e3);return Math.abs(n-e)<=t};export{l as REPLAY_WINDOW_SECONDS,f as hmacBase64,c as hmacHex,m as isWithinReplayWindow,h as timingSafeEqual};
@@ -0,0 +1 @@
1
+ const n=async(e,s={})=>{const u=new AbortController,c=s.timeout??3e4,a=setTimeout(()=>{u.abort()},c);try{const t=await fetch(e,{body:s.body,headers:s.headers,method:s.method??"GET",signal:u.signal}),r=await t.text();let o=r;if(r.length>0)try{o=JSON.parse(r)}catch{o=r}const i={};return t.headers.forEach((d,h)=>{i[h]=d}),{data:{body:o,headers:i,status:t.status,statusText:t.statusText},success:!0}}catch(t){return{error:t,success:!1}}finally{clearTimeout(a)}},l=e=>e===408||e===429||e>=500,m=async(e,s={},u=3,c=250)=>{let a=await n(e,s);for(let t=0;t<u;t+=1){if(!(!a.success||a.data!==void 0&&l(a.data.status)))return a;const r=c*2**t+Math.floor(Math.random()*c);await new Promise(o=>setTimeout(o,r)),a=await n(e,s)}return a};export{n as m,m as r};
@@ -0,0 +1 @@
1
+ let n=0;const t=o=>{n+=1;const r=globalThis.crypto;return r?.randomUUID?`${o}-${r.randomUUID()}`:`${o}-${String(n)}-${Math.floor(Math.random()*1e9).toString(36)}`};export{t as n};
@@ -0,0 +1 @@
1
+ import{MemoryInAppStore as r}from"./MemoryInAppStore-48qlrDCx.js";const o=(t={})=>{const a=t.id??"inapp",n=t.store??new r;return{channel:"inapp",features:{richContent:!0},getInstance:()=>n,id:a,initialize:()=>{},isAvailable:()=>!0,send:async e=>{const i=await n.add({actions:e.actions,body:e.body,data:e.data,subscriberId:e.to,title:e.title});return{data:{channel:"inapp",messageId:i.id,provider:a,sent:!0,timestamp:new Date(i.createdAt)},success:!0}}}};export{o as inAppProvider};
@@ -0,0 +1 @@
1
+ const s=(n={})=>{const i=n.logger??console;return async(r,o)=>{i.debug(`[@visulima/notification] sending via "${r.provider}" (${r.channel})`);const e=await o(r);if(e.success)i.debug(`[@visulima/notification] sent ${e.data?.messageId??""} via "${r.provider}"`);else{const a=e.error instanceof Error?e.error.message:String(e.error);i.warn(`[@visulima/notification] send failed via "${r.provider}": ${a}`)}return e}};export{s as loggingMiddleware};
@@ -0,0 +1 @@
1
+ import t from"./NotificationError-DVfMJQ1u.js";import g from"./RequiredOptionError-CuL42XRq.js";import{defineProvider as l}from"./defineProvider-C9q-7iwt.js";import{t as y}from"./credentials-CG0zkn-W.js";import{r as b}from"./http-BMYcUNBU.js";import{b as h}from"./sms-CU-OZvNU.js";const v="https://rest.messagebird.com",$=l(n=>{const e=n??{};if(!e.accessKey)throw new g("messagebird","accessKey");const i=e.endpoint??v,d=e.retries??3,c=e.timeout??3e4,m=`${i}/messages`;return{channel:"sms",endpoint:i,features:{batchSending:!0,deliveryReceipts:!0,media:!1},id:"messagebird",initialize:()=>{},isAvailable:()=>!!e.accessKey,options:e,send:async r=>{const a=y(r.to),o=r.from??e.from;if(!o)return{error:new t("messagebird","Missing originator: provide `from` on the payload or provider config"),success:!1};const s=await b(m,{body:JSON.stringify({body:r.text,originator:o,recipients:a}),headers:{Authorization:`AccessKey ${e.accessKey}`,"Content-Type":"application/json"},method:"POST",timeout:c},d);if(!s.success||!s.data)return{error:new t("messagebird",s.error instanceof Error?s.error.message:"Request failed"),success:!1};if(s.data.status>=400)return{error:new t("messagebird",s.data.body.errors?.[0]?.description??`HTTP ${String(s.data.status)}`),success:!1};const f=s.data.body.id??"",p=a.map(u=>({id:u,messageId:f,status:"sent"}));return h("messagebird",p)},validateCredentials:()=>!!e.accessKey}});export{$ as default};
@@ -0,0 +1 @@
1
+ import c from"./NotificationError-DVfMJQ1u.js";import{defineProvider as d}from"./defineProvider-C9q-7iwt.js";let n=0;const p=d((e={})=>{const t=[],s=e.channel??"sms",r=e.id??"mock",o={clear:()=>{t.length=0},last:()=>t.at(-1),sent:t};return{channel:s,getInstance:()=>o,id:r,initialize:()=>{},isAvailable:()=>!0,send:l=>{if(e.failWith!==void 0)return{error:new c(r,e.failWith),success:!1};n+=1;const a=new Date,i=`mock-${String(n)}`;return t.push({messageId:i,payload:l,timestamp:a}),{data:{channel:s,messageId:i,provider:r,sent:!0,timestamp:a},success:!0}},validateCredentials:()=>!0}});export{p as mockProvider};
@@ -0,0 +1 @@
1
+ import r from"./NotificationError-DVfMJQ1u.js";import m from"./RequiredOptionError-CuL42XRq.js";import{defineProvider as c}from"./defineProvider-C9q-7iwt.js";import{r as d}from"./http-BMYcUNBU.js";import{n as l}from"./id-C_FRCsUM.js";const b=c(a=>{const e=a??{};if(!e.webhookUrl)throw new m("msteams","webhookUrl");const o=e.retries??3,n=e.timeout??3e4;return{channel:"chat",endpoint:e.webhookUrl,features:{batchSending:!1,richContent:!0},id:"msteams",initialize:()=>{},isAvailable:()=>!!e.webhookUrl,options:e,send:async s=>{const i=s.blocks??{"@context":"https://schema.org/extensions","@type":"MessageCard",text:s.text},t=await d(e.webhookUrl,{body:JSON.stringify(i),headers:{"Content-Type":"application/json"},method:"POST",timeout:n},o);return!t.success||!t.data?{error:new r("msteams",t.error instanceof Error?t.error.message:"Request failed"),success:!1}:t.data.status>=400?{error:new r("msteams",`HTTP ${String(t.data.status)}: ${t.data.body}`),success:!1}:{data:{channel:"chat",messageId:l("msteams"),provider:"msteams",sent:!0,timestamp:new Date},success:!0}},validateCredentials:()=>!!e.webhookUrl}});export{b as default};
@@ -0,0 +1,104 @@
1
+ import { M as Middleware } from "./types.d-CximoA8M.js";
2
+ import { P as Provider } from "./provider.d-BQAGriz6.js";
3
+ import { b as ChatPayload, E as EmailChannelPayload, I as InAppPayload, P as PushPayload, S as SmsPayload, W as WebhookPayload, C as ChannelType, c as Receipt } from "./types.d-GUzX1jTk.js";
4
+ /**
5
+ * Maps each channel to its payload type.
6
+ */
7
+ interface ChannelPayloadMap {
8
+ chat: ChatPayload;
9
+ email: EmailChannelPayload;
10
+ inapp: InAppPayload;
11
+ push: PushPayload;
12
+ sms: SmsPayload;
13
+ webhook: WebhookPayload;
14
+ }
15
+ /**
16
+ * A multi-channel message: a payload per channel to deliver on.
17
+ */
18
+ type NotificationMessage = { [K in ChannelType]?: ChannelPayloadMap[K] };
19
+ /**
20
+ * Providers registered per channel.
21
+ */
22
+ type NotificationProviders = { [K in ChannelType]?: Provider<unknown, ChannelPayloadMap[K]> };
23
+ interface SendManyOptions {
24
+ /** Maximum number of messages delivered concurrently (default 10). */
25
+ concurrency?: number;
26
+ }
27
+ /**
28
+ * The high-level multi-channel notification facade. Holds a provider per channel,
29
+ * a shared middleware chain and dispatches messages across channels.
30
+ */
31
+ declare class Notification {
32
+ #private;
33
+ constructor(providers: NotificationProviders);
34
+ /**
35
+ * Registers a middleware. First registered runs outermost.
36
+ * @param middleware The middleware to add.
37
+ * @returns This instance for chaining.
38
+ */
39
+ use(middleware: Middleware): this;
40
+ /**
41
+ * Sets a logger used by the facade.
42
+ * @param logger The console-like logger.
43
+ * @returns This instance for chaining.
44
+ */
45
+ setLogger(logger: Console): this;
46
+ /**
47
+ * Returns the provider registered for a channel, if any.
48
+ * @param channel The channel whose registered provider should be looked up.
49
+ * @returns The provider or undefined.
50
+ */
51
+ getProvider(channel: ChannelType): Provider | undefined;
52
+ /**
53
+ * Sends a payload on a single channel.
54
+ * @param channel The target channel.
55
+ * @param payload The channel-specific payload to deliver.
56
+ * @returns A receipt describing the outcome.
57
+ */
58
+ sendToChannel<K extends ChannelType>(channel: K, payload: ChannelPayloadMap[K]): Promise<Receipt>;
59
+ /**
60
+ * Sends a multi-channel message, delivering each present channel in parallel.
61
+ * @param message The multi-channel message.
62
+ * @returns One receipt per attempted channel.
63
+ */
64
+ send(message: NotificationMessage): Promise<Receipt[]>;
65
+ /**
66
+ * Sends many messages with bounded concurrency, yielding the receipts for each.
67
+ * @param messages The messages to send.
68
+ * @param options Concurrency options.
69
+ * @yields The receipts for each message, in completion order.
70
+ */
71
+ sendMany(messages: Iterable<NotificationMessage>, options?: SendManyOptions): AsyncGenerator<Receipt[]>;
72
+ /**
73
+ * Initializes every registered provider.
74
+ */
75
+ initialize(): Promise<void>;
76
+ /**
77
+ * Shuts down every registered provider that supports it.
78
+ */
79
+ shutdown(): Promise<void>;
80
+ }
81
+ /**
82
+ * Creates a {@link Notification} facade from a map of channel providers.
83
+ * @param providers Providers keyed by channel.
84
+ * @returns A configured notification facade.
85
+ */
86
+ declare const createNotification: (providers: NotificationProviders) => Notification;
87
+ /**
88
+ * One-shot send through a single provider — the quickest path for a single-channel
89
+ * send without wiring a {@link Notification} facade. For multiple channels,
90
+ * middleware, routing or reuse, use {@link createNotification}.
91
+ * @param channel The channel to deliver on.
92
+ * @param provider The provider to deliver through.
93
+ * @param payload The channel-specific payload.
94
+ * @returns The send {@link Receipt}.
95
+ * @example
96
+ * ```ts
97
+ * import { send } from "@visulima/notification";
98
+ * import { twilioProvider } from "@visulima/notification/providers/twilio";
99
+ *
100
+ * await send("sms", twilioProvider({ accountSid, authToken, from }), { to: "+15555550100", text: "Hi" });
101
+ * ```
102
+ */
103
+ declare const send: <ChannelT extends ChannelType>(channel: ChannelT, provider: Provider<unknown, ChannelPayloadMap[ChannelT]>, payload: ChannelPayloadMap[ChannelT]) => Promise<Receipt>;
104
+ export { ChannelPayloadMap as C, Notification as N, SendManyOptions as S, NotificationMessage as a, NotificationProviders as b, createNotification as c, send as s };
@@ -0,0 +1 @@
1
+ const i="notification.send",a=(e,o={})=>{const{tracer:t}=o;return{...e,send:async s=>{if(!t)return e.send(s);const n=t.startSpan(i,{attributes:{"notification.channel":e.channel,"notification.provider":e.id}});try{const r=await e.send(s);if(!r.success){const c=r.error instanceof Error?r.error.message:String(r.error);n.setStatus({code:2,message:c})}return r}catch(r){throw n.recordException(r),n.setStatus({code:2,message:r instanceof Error?r.message:String(r)}),r}finally{n.end()}}}};export{a as otelProvider};
@@ -0,0 +1 @@
1
+ import o from"./NotificationError-DVfMJQ1u.js";import d from"./RequiredOptionError-CuL42XRq.js";import{defineProvider as w}from"./defineProvider-C9q-7iwt.js";import{b as y,t as T}from"./credentials-CG0zkn-W.js";import{r as b}from"./http-BMYcUNBU.js";import{b as I}from"./sms-CU-OZvNU.js";const S="https://api.plivo.com",q=w(p=>{const e=p??{};if(!e.authId)throw new d("plivo","authId");if(!e.authToken)throw new d("plivo","authToken");const a=e.endpoint??S,m=e.retries??3,c=e.timeout??3e4,f=`${a}/v1/Account/${e.authId}/Message/`,l=y(e.authId,e.authToken);return{channel:"sms",endpoint:a,features:{batchSending:!0,deliveryReceipts:!0,media:!0},id:"plivo",initialize:()=>{},isAvailable:()=>!!(e.authId&&e.authToken),options:e,send:async s=>{const r=T(s.to),i=s.from??e.from;if(!i)return{error:new o("plivo","Missing sender: provide `from` on the payload or provider config"),success:!1};const t=await b(f,{body:JSON.stringify({dst:r.join("<"),src:i,text:s.text}),headers:{Authorization:l,"Content-Type":"application/json"},method:"POST",timeout:c},m);if(!t.success||!t.data)return{error:new o("plivo",t.error instanceof Error?t.error.message:"Request failed"),success:!1};if(t.data.status>=400)return{error:new o("plivo",t.data.body.error??`HTTP ${String(t.data.status)}`),success:!1};const{body:n}=t.data,h=n.message_uuid?.[0]??"",u=r.map((v,g)=>({id:v,messageId:n.message_uuid?.[g],status:"sent"}));return I("plivo",u.length>0?u:[{id:r[0]??"",messageId:h,status:"sent"}])},validateCredentials:()=>!!(e.authId&&e.authToken)}});export{q as default};
@@ -0,0 +1,33 @@
1
+ import { B as BaseNotificationPayload, C as ChannelType, F as FeatureFlags, M as MaybePromise, N as NotificationEvent, a as NotificationResult, R as Result } from "./types.d-GUzX1jTk.js";
2
+ /**
3
+ * Standard provider interface for notification channels. Every provider belongs to
4
+ * exactly one {@link ChannelType} and accepts a channel-specific payload.
5
+ */
6
+ interface Provider<OptionsT = unknown, PayloadT extends BaseNotificationPayload = BaseNotificationPayload, InstanceT = unknown> {
7
+ /** The channel this provider delivers on. */
8
+ readonly channel: ChannelType;
9
+ endpoint?: string;
10
+ features?: FeatureFlags;
11
+ getInstance?: () => InstanceT;
12
+ /** Unique provider id, e.g. `"twilio"`, `"slack"`, `"fcm"`. */
13
+ readonly id: string;
14
+ initialize: () => MaybePromise<void>;
15
+ isAvailable: () => MaybePromise<boolean>;
16
+ options?: OptionsT;
17
+ /** Map a provider webhook body to a normalised lifecycle event. */
18
+ parseEventBody?: (body: unknown, headers?: Record<string, string>) => MaybePromise<NotificationEvent | undefined>;
19
+ send: (payload: PayloadT) => MaybePromise<Result<NotificationResult>>;
20
+ shutdown?: () => MaybePromise<void>;
21
+ validateCredentials?: () => MaybePromise<boolean>;
22
+ }
23
+ /**
24
+ * Type for a provider factory function.
25
+ */
26
+ type ProviderFactory<OptionsT = unknown, PayloadT extends BaseNotificationPayload = BaseNotificationPayload, InstanceT = unknown> = (options?: OptionsT) => Provider<OptionsT, PayloadT, InstanceT>;
27
+ /**
28
+ * Helper to define a notification provider (identity function for type inference).
29
+ * @param factory The provider factory function.
30
+ * @returns The same factory function.
31
+ */
32
+ declare const defineProvider: <OptionsT = unknown, PayloadT extends BaseNotificationPayload = BaseNotificationPayload, InstanceT = unknown>(factory: ProviderFactory<OptionsT, PayloadT, InstanceT>) => ProviderFactory<OptionsT, PayloadT, InstanceT>;
33
+ export { Provider as P, ProviderFactory as a, defineProvider as d };
@@ -0,0 +1 @@
1
+ const c=async t=>new Promise(e=>{setTimeout(e,t)}),l=t=>{const e=t.interval??1e3,a=Math.max(1,t.rate);let n=a,i=Date.now();const s=()=>{const o=Date.now(),r=o-i;n=Math.min(a,n+r/e*a),i=o};return async(o,r)=>{for(s();n<1;)await c(Math.ceil(e/a)),s();return n-=1,r(o)}};export{l as rateLimitMiddleware};
@@ -0,0 +1 @@
1
+ const l=async r=>new Promise(e=>{setTimeout(e,r)}),c=(r={})=>{const e=r.retries??3,s=r.baseDelay??250;return async(o,n)=>{let t=await n(o);for(let a=0;a<e&&!t.success&&!(r.shouldRetry&&!r.shouldRetry(t.error));a+=1)await l(s*2**a+Math.floor(Math.random()*s)),t=await n(o);return t}};export{c as retryMiddleware};
@@ -0,0 +1 @@
1
+ import c from"./NotificationError-DVfMJQ1u.js";const g=(e,a={})=>{if(e.length===0)throw new c("roundrobin","At least one provider is required");const t=a.id??"roundrobin",d=a.failover??!0,u=e[0];let i=0;return{channel:u.channel,id:t,initialize:async()=>{await Promise.all(e.map(async r=>r.initialize()))},isAvailable:async()=>(await Promise.all(e.map(async r=>r.isAvailable()))).some(Boolean),send:async r=>{const f=i;i=(i+1)%e.length;const o=[],h=d?e.length:1;for(let s=0;s<h;s+=1){const l=e[(f+s)%e.length],n=await l.send(r);if(n.success)return n;o.push(`${l.id}: ${n.error instanceof Error?n.error.message:String(n.error)}`)}return{error:new c(t,`Send failed: ${o.join("; ")}`),success:!1}}}};export{g as roundRobinProvider};
@@ -0,0 +1 @@
1
+ import r from"./NotificationError-DVfMJQ1u.js";import{defineProvider as b}from"./defineProvider-C9q-7iwt.js";import{r as m}from"./http-BMYcUNBU.js";import{n as d}from"./id-C_FRCsUM.js";const w="https://slack.com/api",S=b(h=>{const e=h??{};if(!e.token&&!e.webhookUrl)throw new r("slack","Provide either `token` (Web API) or `webhookUrl`",{hint:"Set one of token/webhookUrl in the provider config"});const n=e.endpoint??w,k=e.retries??3,u=e.timeout??3e4;return{channel:"chat",endpoint:n,features:{batchSending:!1,richContent:!0},id:"slack",initialize:()=>{},isAvailable:()=>!!(e.token??e.webhookUrl),options:e,send:async s=>{const o=!!e.webhookUrl,f=o?e.webhookUrl:`${n}/chat.postMessage`,i=s.to??e.defaultChannel;if(!o&&!i)return{error:new r("slack","Missing channel: provide `to` or `defaultChannel`"),success:!1};const c={blocks:s.blocks,text:s.text,thread_ts:s.threadId};o||(c.channel=i);const l={"Content-Type":"application/json"};o||(l.Authorization=`Bearer ${e.token??""}`);const t=await m(f,{body:JSON.stringify(c),headers:l,method:"POST",timeout:u},k);if(!t.success||!t.data)return{error:new r("slack",t.error instanceof Error?t.error.message:"Request failed"),success:!1};if(o){if(t.data.status>=400||t.data.body!=="ok"){const p=typeof t.data.body=="string"?t.data.body:JSON.stringify(t.data.body);return{error:new r("slack",`Webhook failed: ${p}`),success:!1}}return{data:{channel:"chat",messageId:d("slack"),provider:"slack",sent:!0,timestamp:new Date},success:!0}}const a=t.data.body;return a.ok?{data:{channel:"chat",messageId:a.ts??d("slack"),provider:"slack",response:a,sent:!0,timestamp:new Date},success:!0}:{error:new r("slack",a.error??"Slack API error"),success:!1}},validateCredentials:()=>!!(e.token??e.webhookUrl)}});export{S as default};
@@ -0,0 +1 @@
1
+ import{isWithinReplayWindow as o,REPLAY_WINDOW_SECONDS as c,hmacHex as l,timingSafeEqual as d}from"./hmacBase64-BhhoX0uo.js";import{getHeader as a,tryParseObject as f}from"./getHeader-C_gkdnMQ.js";const m="X-Slack-Signature",p="X-Slack-Request-Timestamp",y={parse:r=>{const e=f(r);if(e===void 0)return;const t=e.event??e,n=typeof t.channel=="string"?t.channel:void 0,i=typeof t.ts=="string"?t.ts:"";return{channel:"chat",messageId:typeof e.event_id=="string"?e.event_id:i,provider:"slack",recipient:n,timestamp:new Date,type:"delivered"}},verify:async(r,e,t)=>{if(t.trim()==="")return!1;const n=a(e,m),i=a(e,p);if(n===void 0||i===void 0||!o(i,c))return!1;const s=await l(t,`v0:${i}:${r}`,"SHA-256");return d(`v0=${s}`,n)}};export{y as default,y as slackWebhook};
@@ -0,0 +1 @@
1
+ import n from"./NotificationError-DVfMJQ1u.js";const i=(t,e,s)=>{const r=s.filter(a=>a.status==="sent");return r.length===0?{error:new n(e,s[0]?.error??"All recipients failed"),success:!1}:{data:{channel:t,messageId:r[0]?.messageId??"",provider:e,recipients:s,sent:!0,timestamp:new Date},success:!0}},o=(t,e)=>i("sms",t,e),u=async(t,e)=>{const s=[];for(const r of t)s.push(await e(r));return s};export{i as a,o as b,u as s};
@@ -0,0 +1,4 @@
1
+ import x from"./RequiredOptionError-CuL42XRq.js";import{defineProvider as K}from"./defineProvider-C9q-7iwt.js";import{t as z}from"./credentials-CG0zkn-W.js";import{r as C}from"./http-BMYcUNBU.js";import{n as R}from"./id-C_FRCsUM.js";import{s as j,b as P}from"./sms-CU-OZvNU.js";import{b as w,d as h,u as c,e as p}from"./webcrypto-bY7wJCB5.js";const b="sns",T="us-east-1",H=/<Message>([\s\S]*?)<\/Message>/,q=/<MessageId>([\s\S]*?)<\/MessageId>/,D=t=>{const e=t.toISOString().replaceAll(/[:-]|\.\d{3}/g,"");return{amzDate:e,dateStamp:e.slice(0,8)}},O=async(t,e,o)=>{const i=await p(c(`AWS4${t}`),c(e)),m=await p(i,c(o)),d=await p(m,c(b));return p(d,c("aws4_request"))},k=t=>{const{pathname:e}=new URL(t);return e===""||e==="/"?"/":e.split("/").map(o=>encodeURIComponent(o).replaceAll("*","%2A").replaceAll("'","%27").replaceAll("(","%28").replaceAll(")","%29").replaceAll("!","%21")).join("/")},L=async(t,e,o,i,m)=>{const{amzDate:d,dateStamp:S}=D(new Date),f=w(await h(c(m))),a={"Content-Type":"application/x-www-form-urlencoded",host:o,"x-amz-content-sha256":f,"x-amz-date":d};t.sessionToken&&(a["x-amz-security-token"]=t.sessionToken);const n={};for(const[l,g]of Object.entries(a))n[l.toLowerCase()]=g.trim();const s=Object.keys(n).toSorted((l,g)=>l.localeCompare(g)),u=s.join(";"),y=s.map(l=>`${l}:${n[l]??""}
2
+ `).join(""),r=["POST",k(i),"",y,u,f].join(`
3
+ `),A=`${S}/${e}/${b}/aws4_request`,$=["AWS4-HMAC-SHA256",d,A,w(await h(c(r)))].join(`
4
+ `),I=await O(t.secretAccessKey,S,e),M=w(await p(I,c($)));return a.Authorization=`AWS4-HMAC-SHA256 Credential=${t.accessKeyId}/${A}, SignedHeaders=${u}, Signature=${M}`,{headers:a,url:i}},B=K(t=>{const e=t??{};if(!e.accessKeyId||!e.secretAccessKey)throw new x("sns",["accessKeyId","secretAccessKey"]);const o=e.region??T,i=e.endpoint??`https://sns.${o}.amazonaws.com/`,{host:m}=new URL(i),d=e.retries??3,S=e.timeout??3e4,f=async(a,n)=>{const s=new URLSearchParams;s.set("Action","Publish"),s.set("Version","2010-03-31"),s.set("PhoneNumber",a),s.set("Message",n.text),n.from&&(s.set("MessageAttributes.entry.1.Name","AWS.SNS.SMS.SenderID"),s.set("MessageAttributes.entry.1.Value.DataType","String"),s.set("MessageAttributes.entry.1.Value.StringValue",n.from));const u=s.toString(),y=await L(e,o,m,i,u),r=await C(y.url,{body:u,headers:y.headers,method:"POST",timeout:S},d);if(!r.success||!r.data)return{error:r.error instanceof Error?r.error.message:"Request failed",id:a,status:"failed"};if(r.data.status>=400)return{error:H.exec(r.data.body)?.[1]??`HTTP ${String(r.data.status)}`,id:a,status:"failed"};const A=q.exec(r.data.body)?.[1];return{id:a,messageId:A??R("sns"),status:"sent"}};return{channel:"sms",endpoint:i,features:{batchSending:!1,deliveryReceipts:!1,media:!1,scheduling:!1},id:"sns",initialize:()=>{},isAvailable:()=>!!(e.accessKeyId&&e.secretAccessKey),options:e,send:async a=>{const n=await j(z(a.to),async s=>f(s,a));return P("sns",n)},validateCredentials:()=>!!(e.accessKeyId&&e.secretAccessKey)}});export{B as default};
@@ -0,0 +1 @@
1
+ import{tryParseObject as t}from"./getHeader-C_gkdnMQ.js";const i=s=>t(s),o={parse:s=>{const e=i(s);if(e?.Type===void 0)return;const r={type:e.Type};return e.Type==="SubscriptionConfirmation"||e.Type==="UnsubscribeConfirmation"?r.subscribeUrl=e.SubscribeURL:e.Message!==void 0&&(r.message=e.Message),{messageId:e.MessageId??"",metadata:r,provider:"sns",timestamp:e.Timestamp===void 0?new Date:new Date(e.Timestamp),type:"delivered"}},verify:()=>Promise.resolve(!1)};export{o as default,o as snsWebhook};
@@ -0,0 +1 @@
1
+ import{isWithinReplayWindow as m,REPLAY_WINDOW_SECONDS as v,hmacBase64 as w,hmacHex as y,timingSafeEqual as p}from"./hmacBase64-BhhoX0uo.js";import{getHeader as s,tryParseObject as g}from"./getHeader-C_gkdnMQ.js";const b="webhook-id",k="webhook-timestamp",A="webhook-signature",S=new Set(["bounced","clicked","delivered","failed","interacted","queued","read","sent"]),W=r=>{const e=atob(r),t=new Uint8Array(e.length);for(let i=0;i<e.length;i+=1)t[i]=e.codePointAt(i)??0;return t},E={parse:r=>{const e=g(r);if(e===void 0)return;const t=typeof e.type=="string"?e.type:void 0,i=t!==void 0&&S.has(t)?t:"delivered";return{messageId:typeof e.id=="string"?e.id:"",provider:"standard-webhooks",recipient:typeof e.recipient=="string"?e.recipient:void 0,timestamp:new Date,type:i}},verify:async(r,e,t)=>{if(t.trim()==="")return!1;const i=s(e,b),o=s(e,k),a=s(e,A);if(i===void 0||o===void 0||a===void 0||!m(o,v))return!1;const d=t.startsWith("whsec_")?W(t.slice(6)):t,c=`${i}.${o}.${r}`,l=await w(d,c,"SHA-256"),u=await y(d,c,"SHA-256"),h=a.split(" ");for(const n of h){const f=n.includes(",")?n.split(",")[1]??"":n;if(p(l,f)||p(u,f))return!0}return!1}};export{E as default,E as standardWebhook};
@@ -0,0 +1 @@
1
+ const i=e=>typeof e=="string"?e:JSON.stringify(e),n=e=>{const{to:r}=e;if(r!=null)return Array.isArray(r)?r.map(s=>i(s)).join(","):i(r)},o=e=>{const r=e.resolveRecipient??n;return async(s,a)=>{const t=r(s.payload);return t===void 0?a(s):await e.isSuppressed(t,s.channel)?{data:{channel:s.channel,messageId:`suppressed:${t}`,provider:s.provider,sent:!1,timestamp:new Date},success:!0}:a(s)}};export{o as suppressionMiddleware};
@@ -0,0 +1 @@
1
+ import r from"./NotificationError-DVfMJQ1u.js";import m from"./RequiredOptionError-CuL42XRq.js";import{defineProvider as p}from"./defineProvider-C9q-7iwt.js";import{r as u}from"./http-BMYcUNBU.js";const f="https://api.telegram.org",T=p(i=>{const e=i??{};if(!e.botToken)throw new m("telegram","botToken");const o=e.endpoint??f,d=e.retries??3,c=e.timeout??3e4,l=`${o}/bot${e.botToken}/sendMessage`;return{channel:"chat",endpoint:o,features:{batchSending:!1,richContent:!0},id:"telegram",initialize:()=>{},isAvailable:()=>!!e.botToken,options:e,send:async a=>{const s=a.to??e.defaultChatId;if(s===void 0)return{error:new r("telegram","Missing chat id: provide `to` or `defaultChatId`"),success:!1};const n={chat_id:s,text:a.text};e.parseMode&&(n.parse_mode=e.parseMode);const t=await u(l,{body:JSON.stringify(n),headers:{"Content-Type":"application/json"},method:"POST",timeout:c},d);return!t.success||!t.data?{error:new r("telegram",t.error instanceof Error?t.error.message:"Request failed"),success:!1}:t.data.body.ok?{data:{channel:"chat",messageId:String(t.data.body.result?.message_id??""),provider:"telegram",response:t.data.body,sent:!0,timestamp:new Date},success:!0}:{error:new r("telegram",t.data.body.description??`HTTP ${String(t.data.status)}`),success:!1}},validateCredentials:()=>!!e.botToken}});export{T as default};
@@ -0,0 +1 @@
1
+ const l="notification.send",p=(d={})=>{const{meter:n,tracer:u}=d;let i,r;return n&&(i=n.createCounter("notification.send.count",{description:"Number of notification send attempts"}),r=n.createHistogram("notification.send.duration",{description:"Notification send duration in milliseconds",unit:"ms"})),async(c,f)=>{const o={"notification.channel":c.channel,"notification.provider":c.provider},e=u?.startSpan(l,{attributes:o}),s=Date.now();try{const t=await f(c),a=t.success?"success":"failure";if(!t.success&&e){const m=t.error instanceof Error?t.error.message:String(t.error);e.setStatus({code:2,message:m})}return i?.add(1,{...o,"notification.outcome":a}),r?.record(Date.now()-s,{...o,"notification.outcome":a}),t}catch(t){throw e&&(e.recordException(t),e.setStatus({code:2,message:t instanceof Error?t.message:String(t)})),i?.add(1,{...o,"notification.outcome":"error"}),r?.record(Date.now()-s,{...o,"notification.outcome":"error"}),t}finally{e?.end()}}};export{p as telemetryMiddleware};
@@ -0,0 +1 @@
1
+ import u from"./RequiredOptionError-CuL42XRq.js";import{defineProvider as p}from"./defineProvider-C9q-7iwt.js";import{t as g}from"./credentials-CG0zkn-W.js";import{r as c}from"./http-BMYcUNBU.js";import{s as y,b as h}from"./sms-CU-OZvNU.js";const x="https://api.telnyx.com",I=p(n=>{const e=n??{};if(!e.apiKey)throw new u("telnyx","apiKey");const o=e.endpoint??x,d=e.retries??3,m=e.timeout??3e4,l=`${o}/v2/messages`,f=async(t,s)=>{const i=s.from??e.from;if(!i&&!e.messagingProfileId)return{error:"Missing sender: provide `from`, provider `from`, or `messagingProfileId`",id:t,status:"failed"};const a={text:s.text,to:t};i&&(a.from=i),e.messagingProfileId&&(a.messaging_profile_id=e.messagingProfileId),s.mediaUrls&&s.mediaUrls.length>0&&(a.media_urls=s.mediaUrls);const r=await c(l,{body:JSON.stringify(a),headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":"application/json"},method:"POST",timeout:m},d);return!r.success||!r.data?{error:r.error instanceof Error?r.error.message:"Request failed",id:t,status:"failed"}:r.data.status>=400?{error:r.data.body.errors?.[0]?.detail??`HTTP ${String(r.data.status)}`,id:t,status:"failed"}:{id:t,messageId:r.data.body.data?.id,status:"sent"}};return{channel:"sms",endpoint:o,features:{batchSending:!1,deliveryReceipts:!0,media:!0,scheduling:!0},id:"telnyx",initialize:()=>{},isAvailable:()=>!!e.apiKey,options:e,send:async t=>{const s=await y(g(t.to),async i=>f(i,t));return h("telnyx",s)},validateCredentials:()=>!!e.apiKey}});export{I as default};