@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.
- package/README.md +1 -1
- package/dist/index.js +253 -1
- 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](
|
|
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
|
-
|
|
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.
|
|
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.
|
|
18
|
+
"@azure/communication-email": "^1.1.0",
|
|
19
19
|
"@azure/communication-sms": "^1.1.0",
|
|
20
|
-
"@azure/notification-hubs": "^2.0.
|
|
20
|
+
"@azure/notification-hubs": "^2.0.2"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@biomejs/biome": "^2.
|
|
24
|
-
"@types/node": "^24.
|
|
25
|
-
"@vitest/coverage-v8": "^
|
|
26
|
-
"rimraf": "^6.0
|
|
27
|
-
"tsup": "^8.5.
|
|
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": "^
|
|
29
|
+
"vitest": "^4.0.7"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"airhorn": "5.
|
|
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
|
|
60
|
+
"build": "rimraf ./dist && tsup src/index.ts --format esm --dts --clean"
|
|
61
61
|
}
|
|
62
62
|
}
|