@stacksjs/notifications 0.75.113 → 0.75.115

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 (2) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +11 -11
package/dist/index.js CHANGED
@@ -3,4 +3,4 @@ import{log as k}from"@stacksjs/cli";import{notification as Y}from"@stacksjs/conf
3
3
  `).split(/\n\s*\n/).map((t)=>t.trim()).filter(Boolean).map((t)=>{let r=[];return t.split(`
4
4
  `).forEach((n,i)=>{if(i>0)r.push({br:!0});let a=0;for(let s of n.matchAll(G)){if(s.index>a)r.push({text:n.slice(a,s.index)});r.push({url:s[0]}),a=s.index+s[0].length}if(a<n.length)r.push({text:n.slice(a)})}),r})}function w(e){return String(e??"").replace(/[&<>"']/g,(t)=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}async function N(e){try{let{html:r}=await z("notification",{subject:e.subject??"",variables:{subject:e.subject??"",paragraphs:A(e.body),preheader:String(e.body??"").replace(/\s+/g," ").trim().slice(0,140),...e.action?.url?{actionUrl:e.action.url,actionLabel:e.action.label||"Open"}:{}}});if(r)return r}catch{}let t=e.action?.url?`<p><a href="${w(e.action.url)}">${w(e.action.label||e.action.url)}</a></p>`:"";return`<p>${w(e.body)}</p>${t}`}function v(e){return e.action?.url?`${e.body}
5
5
 
6
- ${e.action.label||"Open"}: ${e.action.url}`:e.body}import{log as K}from"@stacksjs/cli";import{db as l,lockRow as j,sqlDateTime as V}from"@stacksjs/database/runtime";var p="notification_preferences";function P(e,t){return t===null?e.whereNull("category"):e.where("category","=",t)}async function x(e,t){let r=new Map;try{let i=await P(l.selectFrom(p).select(["channel","enabled","category"]).where("user_id","=",e),t??null).execute();for(let a of i)r.set(a.channel,a.enabled===!0||a.enabled===1)}catch(n){K.debug?.(`[notifications] preferences lookup failed (table missing?): ${n.message}`)}return r}async function W(e,t,r,n){await l.transaction(async(i)=>{await j(i,"users",{id:e}),await B(e,t,r,n)})}async function B(e,t,r,n){if(!Number.isSafeInteger(e)||e<=0||typeof r!=="boolean")throw Error("A notification preference needs a valid user id and enabled boolean");let i=V(),a=n??null,s=l.selectFrom(p).select(["id"]).where("user_id","=",e).where("channel","=",t),o=await P(s,a).executeTakeFirst();if(o?.id){await l.updateTable(p).set({enabled:r,updated_at:i}).where("id","=",o.id).execute();return}await l.insertInto(p).values({user_id:e,channel:t,enabled:r,category:a,created_at:i,updated_at:i}).execute()}async function X(e,t){await l.transaction(async(r)=>{await j(r,"users",{id:e});for(let n of t)await B(e,n.channel,n.enabled,n.category)})}async function R(e,t,r){let n=await x(e,r);if(r!==void 0){let i=await x(e,void 0);for(let[a,s]of i)if(!s||!n.has(a))n.set(a,s)}return t.filter((i)=>{let a=n.get(i);return a===void 0?!0:a===!0})}var Z=Y;function ee(e){if(!e.success)throw Error(e.message||`Email delivery failed through ${e.provider||"the configured provider"}.`)}function Se(e){if(!e.length)throw Error("No notification channels were delivered");let t=e.filter((r)=>!r.success);if(t.length){let r=t.map((n)=>n.error instanceof Error?n.error:Error(`Notification delivery failed through ${n.channel}`));if(r.length===1)throw r[0];throw AggregateError(r,r.map((n)=>n.message).join("; "))}}function E(e){if(!e.success)throw Error(e.error||e.message||"Notification provider rejected delivery")}function q(e){let t=e||"slack";if(!["slack","discord","teams"].includes(t))throw Error(`Unsupported chat driver: ${t}`);return T[t]}function M(e){if(e)try{return O.use(e)}catch(t){k.warn(`[notifications] email driver '${e}' not registered - falling back to default mail singleton (${t.message})`)}return O}function F(e){if(!e)return h;if(e!=="twilio"&&e!=="vonage")throw Error(`Unsupported SMS driver: ${e}`);let t=e;return{send:async(r)=>(await h.init(),h.getDriver(t).send(r))}}function te(){return D}function re(){return m}function Ie(){return d}function ne(e,t){let r=e||Z?.default||"email",n=t;switch(r){case"email":return M(n);case"chat":return q(n);case"sms":return F(n);case"database":return re();default:throw Error(`Notification type "${r}" is not supported`)}}async function Ae(e,t,r=["email"],n={}){let i=r;if(e.userId&&!n.ignorePreferences)try{i=await R(e.userId,r,n.category)}catch(o){k.warn(`[notify] preferences filter failed, sending all channels: ${o.message}`)}let s=(await Promise.allSettled(i.map(async(o)=>{switch(o){case"email":{if(!e.email)throw Error("[notify] email channel requires recipient.email");let f=await M().send({to:e.email,subject:t.subject??"",text:v(t),html:await N(t)});ee(f);break}case"sms":{if(!e.phone)throw Error("[notify] sms channel requires recipient.phone");if(n.ignorePreferences!==!0){let{isPhoneOptedOut:f}=await import("@stacksjs/sms");if(await f(e.phone))throw Error("[notify] sms recipient has opted out (STOP)")}let c=F();E(await c.send({to:e.phone,body:t.body}));break}case"chat":{if(!e.chatRecipient||Array.isArray(e.chatRecipient)&&!e.chatRecipient.length)throw Error("[notify] chat channel requires recipient.chatRecipient");let c=q();E(await c.send({to:e.chatRecipient,content:t.body,subject:t.subject,data:t.data}));break}case"database":{if(!e.userId)throw Error("[notify] database channel requires recipient.userId");await m.send({userId:e.userId,type:t.subject||"notification",data:{body:t.body,...t.data}});break}case"push":{if(!e.pushTokens||Array.isArray(e.pushTokens)&&e.pushTokens.length===0)throw Error("[notify] push channel requires recipient.pushTokens");let c=te();E(await c.send(e.pushTokens,{title:t.subject,body:t.body,data:t.data}));break}case"broadcast":{let c=await d.send({channel:e.broadcastChannel,userId:e.userId,event:t.subject??"notification",data:{body:t.body,...t.data}});if(!c.delivered)throw Error(c.reason||"Broadcast notification was not delivered");break}default:throw Error(`Unsupported notification channel: ${o}`)}}))).map((o,c)=>{let f=i[c];if(!f)throw Error(`Missing notification channel for result ${c}`);if(o.status==="rejected"){let U=o.reason instanceof Error?o.reason.message:String(o.reason);k.warn(`[notify] ${f} channel failed: ${U}`)}return{channel:f,success:o.status==="fulfilled",error:o.status==="rejected"?o.reason instanceof Error?o.reason:Error(String(o.reason)):void 0}});return await Promise.all(s.map((o)=>y(b(e,t,o,n)))),s}function $e(){return ne()}export{d as BroadcastNotificationDriver,m as DatabaseNotificationDriver,X as bulkSetPreferences,A as emailParagraphs,ee as ensureSuccessfulEmailResult,Se as ensureSuccessfulNotificationResults,R as filterChannelsByPreferences,x as getNotificationPreferences,b as makeDeliveryRecord,$e as notification,N as notificationEmailHtml,v as notificationEmailText,Ae as notify,y as recordNotificationDelivery,I as resolveDeliveryRecipient,W as setNotificationPreference,Ie as useBroadcast,q as useChat,re as useDatabase,M as useEmail,ne as useNotification,te as usePush,F as useSMS,P as wherePreferenceCategory};
6
+ ${e.action.label||"Open"}: ${e.action.url}`:e.body}import{log as K}from"@stacksjs/cli";import{db as l,lockRow as j,sqlDateTime as V}from"@stacksjs/database/runtime";var p="notification_preferences";function P(e,t){return t===null?e.whereNull("category"):e.where("category","=",t)}async function x(e,t){let r=new Map;try{let i=await P(l.selectFrom(p).select(["channel","enabled","category"]).where("user_id","=",e),t??null).execute();for(let a of i)r.set(a.channel,a.enabled===!0||a.enabled===1)}catch(n){K.debug?.(`[notifications] preferences lookup failed (table missing?): ${n.message}`)}return r}async function W(e,t,r,n){await l.transaction(async(i)=>{await j(i,"users",{id:e}),await B(e,t,r,n)})}async function B(e,t,r,n){if(!Number.isSafeInteger(e)||e<=0||typeof r!=="boolean")throw Error("A notification preference needs a valid user id and enabled boolean");let i=V(),a=n??null,s=l.selectFrom(p).select(["id"]).where("user_id","=",e).where("channel","=",t),o=await P(s,a).executeTakeFirst();if(o?.id){await l.updateTable(p).set({enabled:r,updated_at:i}).where("id","=",o.id).execute();return}await l.insertInto(p).values({user_id:e,channel:t,enabled:r,category:a,created_at:i,updated_at:i}).execute()}async function X(e,t){await l.transaction(async(r)=>{await j(r,"users",{id:e});for(let n of t)await B(e,n.channel,n.enabled,n.category)})}async function R(e,t,r){let n=await x(e,r);if(r!==void 0){let i=await x(e,void 0);for(let[a,s]of i)if(!s||!n.has(a))n.set(a,s)}return t.filter((i)=>{let a=n.get(i);return a===void 0?!0:a===!0})}var Z=Y;function ee(e){if(!e.success)throw Error(e.message||`Email delivery failed through ${e.provider||"the configured provider"}.`)}function Se(e){if(!e.length)throw Error("No notification channels were delivered");let t=e.filter((r)=>!r.success);if(t.length){let r=t.map((n)=>n.error instanceof Error?n.error:Error(`Notification delivery failed through ${n.channel}`));if(r.length===1)throw r[0];throw AggregateError(r,r.map((n)=>n.message).join("; "))}}function E(e){if(!e.success)throw Error(e.error||e.message||"Notification provider rejected delivery")}function q(e){let t=e||"slack";if(!["slack","discord","teams"].includes(t))throw Error(`Unsupported chat driver: ${t}`);return T[t]}function M(e){if(e)try{return O.use(e)}catch(t){k.warn(`[notifications] email driver '${e}' not registered - falling back to default mail singleton (${t.message})`)}return O}function F(e){if(!e)return h;if(e!=="twilio"&&e!=="vonage")throw Error(`Unsupported SMS driver: ${e}`);let t=e;return{send:async(r)=>(await h.init(),h.getDriver(t).send(r))}}function te(){return D}function re(){return m}function Ie(){return d}function ne(e,t){let r=e||Z?.default||"email",n=t;switch(r){case"email":return M(n);case"chat":return q(n);case"sms":return F(n);case"database":return re();default:throw Error(`Notification type "${r}" is not supported`)}}async function Ae(e,t,r=["email"],n={}){let i=[...new Set(r)];if(e.userId&&!n.ignorePreferences)try{i=await R(e.userId,i,n.category)}catch(o){k.warn(`[notify] preferences filter failed, sending all channels: ${o.message}`)}let s=(await Promise.allSettled(i.map(async(o)=>{switch(o){case"email":{if(!e.email)throw Error("[notify] email channel requires recipient.email");let f=await M().send({to:e.email,subject:t.subject??"",text:v(t),html:await N(t)});ee(f);break}case"sms":{if(!e.phone)throw Error("[notify] sms channel requires recipient.phone");if(n.ignorePreferences!==!0){let{isPhoneOptedOut:f}=await import("@stacksjs/sms");if(await f(e.phone))throw Error("[notify] sms recipient has opted out (STOP)")}let c=F();E(await c.send({to:e.phone,body:t.body}));break}case"chat":{if(!e.chatRecipient||Array.isArray(e.chatRecipient)&&!e.chatRecipient.length)throw Error("[notify] chat channel requires recipient.chatRecipient");let c=q();E(await c.send({to:e.chatRecipient,content:t.body,subject:t.subject,data:t.data}));break}case"database":{if(!e.userId)throw Error("[notify] database channel requires recipient.userId");await m.send({userId:e.userId,type:t.subject||"notification",data:{body:t.body,...t.data}});break}case"push":{if(!e.pushTokens||Array.isArray(e.pushTokens)&&e.pushTokens.length===0)throw Error("[notify] push channel requires recipient.pushTokens");let c=te();E(await c.send(e.pushTokens,{title:t.subject,body:t.body,data:t.data}));break}case"broadcast":{let c=await d.send({channel:e.broadcastChannel,userId:e.userId,event:t.subject??"notification",data:{body:t.body,...t.data}});if(!c.delivered)throw Error(c.reason||"Broadcast notification was not delivered");break}default:throw Error(`Unsupported notification channel: ${o}`)}}))).map((o,c)=>{let f=i[c];if(!f)throw Error(`Missing notification channel for result ${c}`);if(o.status==="rejected"){let U=o.reason instanceof Error?o.reason.message:String(o.reason);k.warn(`[notify] ${f} channel failed: ${U}`)}return{channel:f,success:o.status==="fulfilled",error:o.status==="rejected"?o.reason instanceof Error?o.reason:Error(String(o.reason)):void 0}});return await Promise.all(s.map((o)=>y(b(e,t,o,n)))),s}function $e(){return ne()}export{d as BroadcastNotificationDriver,m as DatabaseNotificationDriver,X as bulkSetPreferences,A as emailParagraphs,ee as ensureSuccessfulEmailResult,Se as ensureSuccessfulNotificationResults,R as filterChannelsByPreferences,x as getNotificationPreferences,b as makeDeliveryRecord,$e as notification,N as notificationEmailHtml,v as notificationEmailText,Ae as notify,y as recordNotificationDelivery,I as resolveDeliveryRecipient,W as setNotificationPreference,Ie as useBroadcast,q as useChat,re as useDatabase,M as useEmail,ne as useNotification,te as usePush,F as useSMS,P as wherePreferenceCategory};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/notifications",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.113",
5
+ "version": "0.75.115",
6
6
  "description": "The Stacks notifications integration.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -64,18 +64,18 @@
64
64
  "prepublishOnly": "bun run build"
65
65
  },
66
66
  "dependencies": {
67
- "@stacksjs/chat": "0.75.113",
68
- "@stacksjs/cli": "0.75.113",
69
- "@stacksjs/config": "0.75.113",
70
- "@stacksjs/database": "0.75.113",
71
- "@stacksjs/email": "0.75.113",
72
- "@stacksjs/push": "0.75.113",
73
- "@stacksjs/realtime": "0.75.113",
74
- "@stacksjs/sms": "0.75.113"
67
+ "@stacksjs/chat": "0.75.115",
68
+ "@stacksjs/cli": "0.75.115",
69
+ "@stacksjs/config": "0.75.115",
70
+ "@stacksjs/database": "0.75.115",
71
+ "@stacksjs/email": "0.75.115",
72
+ "@stacksjs/push": "0.75.115",
73
+ "@stacksjs/realtime": "0.75.115",
74
+ "@stacksjs/sms": "0.75.115"
75
75
  },
76
76
  "devDependencies": {
77
- "@stacksjs/error-handling": "0.75.113",
78
- "@stacksjs/types": "0.75.113",
77
+ "@stacksjs/error-handling": "0.75.115",
78
+ "@stacksjs/types": "0.75.115",
79
79
  "better-dx": "^0.2.24"
80
80
  }
81
81
  }