@airhornjs/azure 5.0.12 → 5.1.1

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +253 -1
  3. package/package.json +11 -11
package/README.md CHANGED
@@ -247,7 +247,7 @@ pnpm test
247
247
 
248
248
  # How to Contribute
249
249
 
250
- Now that you've set up your workspace, you're ready to contribute changes to the `airhorn` repository you can refer to the [CONTRIBUTING](../../CONTRIBUTING.md) guide. If you have any questions please feel free to ask by creating an issue and label it `question`.
250
+ Now that you've set up your workspace, you're ready to contribute changes to the `airhorn` repository you can refer to the [CONTRIBUTING](CONTRIBUTING.md) guide. If you have any questions please feel free to ask by creating an issue and label it `question`.
251
251
 
252
252
  # Licensing and Copyright
253
253
 
package/dist/index.js CHANGED
@@ -1 +1,253 @@
1
- import{EmailClient as u}from"@azure/communication-email";import{SmsClient as f}from"@azure/communication-sms";import{createAppleNotification as c,createFcmLegacyNotification as l,NotificationHubsClient as h}from"@azure/notification-hubs";import{AirhornSendType as a}from"airhorn";var d=class{name="azure";capabilities;emailClient;smsClient;notificationHubClient;constructor(r){if(this.capabilities=r.capabilities||[a.SMS,a.MobilePush,a.Email],this.capabilities.includes(a.Email)){let i=r.emailConnectionString||r.connectionString;i&&(this.emailClient=new u(i))}if(this.capabilities.includes(a.SMS)){let i=r.smsConnectionString||r.connectionString;i&&(this.smsClient=new f(i))}this.capabilities.includes(a.MobilePush)&&r.notificationHubConnectionString&&r.notificationHubName&&(this.notificationHubClient=new h(r.notificationHubConnectionString,r.notificationHubName))}async send(r,i){let t={success:!1,response:null,errors:[]};try{if(r.type===a.SMS)return this.sendSMS(r,i);if(r.type===a.Email)return this.sendEmail(r,i);if(r.type===a.MobilePush)return this.sendMobilePush(r,i);throw new Error(`AirhornAzure does not support message type: ${r.type}`)}catch(e){let o=e instanceof Error?e:new Error(String(e));t.errors.push(o),t.response={error:o.message,details:e}}return t}async sendSMS(r,i){let t={success:!1,response:null,errors:[]};try{if(!this.smsClient)throw new Error("SMS client is not configured");if(!r.from)throw new Error("From phone number is required for SMS messages");let e=await this.smsClient.send({from:r.from,to:[r.to],message:r.content,...i}),o=e.filter(n=>n.successful),s=e.filter(n=>!n.successful);if(s.length>0)for(let n of s)t.errors.push(new Error(`Failed to send SMS to ${n.to}: ${n.errorMessage}`));t.success=o.length>0,t.response={successful:o.length,failed:s.length,results:e}}catch(e){let o=e instanceof Error?e:new Error(String(e));t.errors.push(o),t.response={error:o.message,details:e}}return t}async sendEmail(r,i){let t={success:!1,response:null,errors:[]};try{if(!this.emailClient)throw new Error("Email client is not configured");if(!r.from)throw new Error("From email address is required for email messages");let e={senderAddress:r.from,recipients:{to:[{address:r.to}],cc:i?.cc?.map(n=>({address:n})),bcc:i?.bcc?.map(n=>({address:n}))},content:{subject:r.subject||"Notification",plainText:r.content,html:i?.html||r.content},attachments:i?.attachments,replyTo:i?.replyTo?[{address:i.replyTo}]:void 0,headers:i?.headers},s=await(await this.emailClient.beginSend(e)).pollUntilDone();t.success=s.status==="Succeeded",t.response={id:s.id,status:s.status,error:s.error},s.error&&t.errors.push(new Error(`Email send failed: ${s.error.message}`))}catch(e){let o=e instanceof Error?e:new Error(String(e));t.errors.push(o),t.response={error:o.message,details:e}}return t}async sendMobilePush(r,i){let t={success:!1,response:null,errors:[]};try{if(!this.notificationHubClient)throw new Error("Notification Hub client is not configured");if(!r.from)throw new Error("From identifier is required for mobile push messages");let e,o={tagExpression:i?.tags};if(i?.platform==="apple"||r.to.includes("apple")){let n=typeof r.content=="string"?JSON.parse(r.content):r.content;e=c({body:JSON.stringify(n),headers:i?.apnsHeaders})}else if(i?.platform==="android"||r.to.includes("android")||r.to.includes("fcm")){let n=typeof r.content=="string"?JSON.parse(r.content):r.content;e=l({body:JSON.stringify(n)})}else{o.tagExpression=r.to;let n=typeof r.content=="string"?JSON.parse(r.content):r.content;if(n.aps)e=c({body:JSON.stringify(n)});else if(n.notification||n.data)e=l({body:JSON.stringify(n)});else throw new Error("Invalid notification payload structure")}let s=await this.notificationHubClient.sendNotification(e,o);t.success=s.state==="Enqueued",t.response={notificationId:s.notificationId,state:s.state,correlationId:s.correlationId},s.state!=="Enqueued"&&t.errors.push(new Error(`Notification failed with state: ${s.state}`))}catch(e){let o=e instanceof Error?e:new Error(String(e));t.errors.push(o),t.response={error:o.message,details:e}}return t}};export{d as AirhornAzure};
1
+ // src/index.ts
2
+ import {
3
+ EmailClient
4
+ } from "@azure/communication-email";
5
+ import { SmsClient } from "@azure/communication-sms";
6
+ import {
7
+ createAppleNotification,
8
+ createFcmLegacyNotification,
9
+ NotificationHubsClient
10
+ } from "@azure/notification-hubs";
11
+ import {
12
+ AirhornSendType
13
+ } from "airhorn";
14
+ var AirhornAzure = class {
15
+ name = "azure";
16
+ capabilities;
17
+ emailClient;
18
+ smsClient;
19
+ notificationHubClient;
20
+ constructor(options) {
21
+ this.capabilities = options.capabilities || [
22
+ AirhornSendType.SMS,
23
+ AirhornSendType.MobilePush,
24
+ AirhornSendType.Email
25
+ ];
26
+ if (this.capabilities.includes(AirhornSendType.Email)) {
27
+ const emailConnStr = options.emailConnectionString || options.connectionString;
28
+ if (emailConnStr) {
29
+ this.emailClient = new EmailClient(emailConnStr);
30
+ }
31
+ }
32
+ if (this.capabilities.includes(AirhornSendType.SMS)) {
33
+ const smsConnStr = options.smsConnectionString || options.connectionString;
34
+ if (smsConnStr) {
35
+ this.smsClient = new SmsClient(smsConnStr);
36
+ }
37
+ }
38
+ if (this.capabilities.includes(AirhornSendType.MobilePush)) {
39
+ if (options.notificationHubConnectionString && options.notificationHubName) {
40
+ this.notificationHubClient = new NotificationHubsClient(
41
+ options.notificationHubConnectionString,
42
+ options.notificationHubName
43
+ );
44
+ }
45
+ }
46
+ }
47
+ async send(message, options) {
48
+ const result = {
49
+ success: false,
50
+ response: null,
51
+ errors: []
52
+ };
53
+ try {
54
+ if (message.type === AirhornSendType.SMS) {
55
+ return this.sendSMS(message, options);
56
+ }
57
+ if (message.type === AirhornSendType.Email) {
58
+ return this.sendEmail(message, options);
59
+ }
60
+ if (message.type === AirhornSendType.MobilePush) {
61
+ return this.sendMobilePush(message, options);
62
+ }
63
+ throw new Error(
64
+ `AirhornAzure does not support message type: ${message.type}`
65
+ );
66
+ } catch (error) {
67
+ const err = error instanceof Error ? error : new Error(String(error));
68
+ result.errors.push(err);
69
+ result.response = {
70
+ error: err.message,
71
+ details: error
72
+ };
73
+ }
74
+ return result;
75
+ }
76
+ async sendSMS(message, options) {
77
+ const result = {
78
+ success: false,
79
+ response: null,
80
+ errors: []
81
+ };
82
+ try {
83
+ if (!this.smsClient) {
84
+ throw new Error("SMS client is not configured");
85
+ }
86
+ if (!message.from) {
87
+ throw new Error("From phone number is required for SMS messages");
88
+ }
89
+ const sendResult = await this.smsClient.send({
90
+ from: message.from,
91
+ to: [message.to],
92
+ message: message.content,
93
+ ...options
94
+ });
95
+ const successfulMessages = sendResult.filter(
96
+ // biome-ignore lint/suspicious/noExplicitAny: Azure SDK type
97
+ (msg) => msg.successful
98
+ );
99
+ const failedMessages = sendResult.filter(
100
+ // biome-ignore lint/suspicious/noExplicitAny: Azure SDK type
101
+ (msg) => !msg.successful
102
+ );
103
+ if (failedMessages.length > 0) {
104
+ for (const failed of failedMessages) {
105
+ result.errors.push(
106
+ new Error(
107
+ `Failed to send SMS to ${failed.to}: ${failed.errorMessage}`
108
+ )
109
+ );
110
+ }
111
+ }
112
+ result.success = successfulMessages.length > 0;
113
+ result.response = {
114
+ successful: successfulMessages.length,
115
+ failed: failedMessages.length,
116
+ results: sendResult
117
+ };
118
+ } catch (error) {
119
+ const err = error instanceof Error ? error : new Error(String(error));
120
+ result.errors.push(err);
121
+ result.response = {
122
+ error: err.message,
123
+ details: error
124
+ };
125
+ }
126
+ return result;
127
+ }
128
+ async sendEmail(message, options) {
129
+ const result = {
130
+ success: false,
131
+ response: null,
132
+ errors: []
133
+ };
134
+ try {
135
+ if (!this.emailClient) {
136
+ throw new Error("Email client is not configured");
137
+ }
138
+ if (!message.from) {
139
+ throw new Error("From email address is required for email messages");
140
+ }
141
+ const emailMessage = {
142
+ senderAddress: message.from,
143
+ recipients: {
144
+ to: [{ address: message.to }],
145
+ cc: options?.cc?.map((email) => ({ address: email })),
146
+ bcc: options?.bcc?.map((email) => ({ address: email }))
147
+ },
148
+ content: {
149
+ subject: message.subject || "Notification",
150
+ plainText: message.content,
151
+ html: options?.html || message.content
152
+ },
153
+ attachments: options?.attachments,
154
+ replyTo: options?.replyTo ? [{ address: options.replyTo }] : void 0,
155
+ headers: options?.headers
156
+ };
157
+ const poller = await this.emailClient.beginSend(emailMessage);
158
+ const sendResult = await poller.pollUntilDone();
159
+ result.success = sendResult.status === "Succeeded";
160
+ result.response = {
161
+ id: sendResult.id,
162
+ status: sendResult.status,
163
+ error: sendResult.error
164
+ };
165
+ if (sendResult.error) {
166
+ result.errors.push(
167
+ new Error(`Email send failed: ${sendResult.error.message}`)
168
+ );
169
+ }
170
+ } catch (error) {
171
+ const err = error instanceof Error ? error : new Error(String(error));
172
+ result.errors.push(err);
173
+ result.response = {
174
+ error: err.message,
175
+ details: error
176
+ };
177
+ }
178
+ return result;
179
+ }
180
+ async sendMobilePush(message, options) {
181
+ const result = {
182
+ success: false,
183
+ response: null,
184
+ errors: []
185
+ };
186
+ try {
187
+ if (!this.notificationHubClient) {
188
+ throw new Error("Notification Hub client is not configured");
189
+ }
190
+ if (!message.from) {
191
+ throw new Error("From identifier is required for mobile push messages");
192
+ }
193
+ let notification;
194
+ const sendOptions = {
195
+ tagExpression: options?.tags
196
+ };
197
+ if (options?.platform === "apple" || message.to.includes("apple")) {
198
+ const apnsPayload = typeof message.content === "string" ? JSON.parse(message.content) : message.content;
199
+ notification = createAppleNotification({
200
+ body: JSON.stringify(apnsPayload),
201
+ headers: options?.apnsHeaders
202
+ });
203
+ } else if (options?.platform === "android" || message.to.includes("android") || message.to.includes("fcm")) {
204
+ const fcmPayload = typeof message.content === "string" ? JSON.parse(message.content) : message.content;
205
+ notification = createFcmLegacyNotification({
206
+ body: JSON.stringify(fcmPayload)
207
+ });
208
+ } else {
209
+ sendOptions.tagExpression = message.to;
210
+ const payload = typeof message.content === "string" ? JSON.parse(message.content) : message.content;
211
+ if (payload.aps) {
212
+ notification = createAppleNotification({
213
+ body: JSON.stringify(payload)
214
+ });
215
+ } else if (payload.notification || payload.data) {
216
+ notification = createFcmLegacyNotification({
217
+ body: JSON.stringify(payload)
218
+ });
219
+ } else {
220
+ throw new Error("Invalid notification payload structure");
221
+ }
222
+ }
223
+ const notificationResult = await this.notificationHubClient.sendNotification(
224
+ notification,
225
+ sendOptions
226
+ );
227
+ result.success = notificationResult.state === "Enqueued";
228
+ result.response = {
229
+ notificationId: notificationResult.notificationId,
230
+ state: notificationResult.state,
231
+ correlationId: notificationResult.correlationId
232
+ };
233
+ if (notificationResult.state !== "Enqueued") {
234
+ result.errors.push(
235
+ new Error(
236
+ `Notification failed with state: ${notificationResult.state}`
237
+ )
238
+ );
239
+ }
240
+ } catch (error) {
241
+ const err = error instanceof Error ? error : new Error(String(error));
242
+ result.errors.push(err);
243
+ result.response = {
244
+ error: err.message,
245
+ details: error
246
+ };
247
+ }
248
+ return result;
249
+ }
250
+ };
251
+ export {
252
+ AirhornAzure
253
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@airhornjs/azure",
3
- "version": "5.0.12",
3
+ "version": "5.1.1",
4
4
  "description": "Azure provider for Airhorn",
5
5
  "license": "MIT",
6
6
  "author": "Jared Wray <me@jaredwray.com>",
@@ -15,21 +15,21 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
- "@azure/communication-email": "^1.0.0",
18
+ "@azure/communication-email": "^1.1.0",
19
19
  "@azure/communication-sms": "^1.1.0",
20
- "@azure/notification-hubs": "^2.0.0"
20
+ "@azure/notification-hubs": "^2.0.2"
21
21
  },
22
22
  "devDependencies": {
23
- "@biomejs/biome": "^2.2.5",
24
- "@types/node": "^24.6.2",
25
- "@vitest/coverage-v8": "^3.2.4",
26
- "rimraf": "^6.0.1",
27
- "tsup": "^8.5.0",
23
+ "@biomejs/biome": "^2.3.4",
24
+ "@types/node": "^24.10.0",
25
+ "@vitest/coverage-v8": "^4.0.7",
26
+ "rimraf": "^6.1.0",
27
+ "tsup": "^8.5.1",
28
28
  "typescript": "^5.9.3",
29
- "vitest": "^3.2.4"
29
+ "vitest": "^4.0.7"
30
30
  },
31
31
  "peerDependencies": {
32
- "airhorn": "5.0.12"
32
+ "airhorn": "5.1.1"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",
@@ -57,6 +57,6 @@
57
57
  "test:ci": "biome check --error-on-warnings && vitest run --coverage",
58
58
  "clean": "rimraf ./dist ./coverage ./node_modules ./package-lock.json ./pnpm-lock.yaml",
59
59
  "build:publish": "pnpm publish --access public --no-git-checks",
60
- "build": "rimraf ./dist && tsup src/index.ts --format esm --dts --clean --minify"
60
+ "build": "rimraf ./dist && tsup src/index.ts --format esm --dts --clean"
61
61
  }
62
62
  }