@facteurjs/core 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/handlers/notifications.js +77 -0
- package/dist/api/handlers/preferences.js +43 -0
- package/dist/api/index.d.ts +16 -0
- package/dist/api/index.js +21 -0
- package/dist/api/types.d.ts +22 -0
- package/dist/api/types.js +0 -0
- package/dist/channels/discord/channel.d.ts +18 -0
- package/dist/channels/discord/channel.js +15 -0
- package/dist/channels/discord/index.d.ts +3 -0
- package/dist/channels/discord/index.js +4 -0
- package/dist/channels/discord/message.d.ts +147 -0
- package/dist/channels/discord/message.js +176 -0
- package/dist/channels/discord/types.d.ts +52 -0
- package/dist/channels/discord/types.js +0 -0
- package/dist/channels/fcm/channel.d.ts +22 -0
- package/dist/channels/fcm/channel.js +44 -0
- package/dist/channels/fcm/index.d.ts +3 -0
- package/dist/channels/fcm/index.js +4 -0
- package/dist/channels/fcm/message.d.ts +64 -0
- package/dist/channels/fcm/message.js +122 -0
- package/dist/channels/fcm/types.d.ts +29 -0
- package/dist/channels/fcm/types.js +0 -0
- package/dist/channels/slack/channel.d.ts +18 -0
- package/dist/channels/slack/channel.js +15 -0
- package/dist/channels/slack/index.d.ts +3 -0
- package/dist/channels/slack/index.js +4 -0
- package/dist/channels/slack/message.d.ts +209 -0
- package/dist/channels/slack/message.js +390 -0
- package/dist/channels/slack/types.d.ts +7 -0
- package/dist/channels/slack/types.js +0 -0
- package/dist/channels/transmit/channel.d.ts +21 -0
- package/dist/channels/transmit/channel.js +27 -0
- package/dist/channels/transmit/index.d.ts +3 -0
- package/dist/channels/transmit/index.js +4 -0
- package/dist/channels/transmit/message.d.ts +11 -0
- package/dist/channels/transmit/message.js +17 -0
- package/dist/channels/transmit/types.d.ts +11 -0
- package/dist/channels/transmit/types.js +0 -0
- package/dist/channels/twilio/channel.d.ts +21 -0
- package/dist/channels/twilio/channel.js +56 -0
- package/dist/channels/twilio/index.d.ts +4 -0
- package/dist/channels/twilio/index.js +4 -0
- package/dist/channels/twilio/message.d.ts +86 -0
- package/dist/channels/twilio/message.js +152 -0
- package/dist/channels/twilio/types.d.ts +51 -0
- package/dist/channels/twilio/types.js +0 -0
- package/dist/channels/webhook/exceptions.d.ts +18 -0
- package/dist/channels/webhook/exceptions.js +24 -0
- package/dist/channels/webhook/index.d.ts +4 -0
- package/dist/channels/webhook/index.js +5 -0
- package/dist/channels/webhook/message.d.ts +24 -0
- package/dist/channels/webhook/message.js +40 -0
- package/dist/channels/webhook/provider.d.ts +19 -0
- package/dist/channels/webhook/provider.js +63 -0
- package/dist/channels/webhook/types.d.ts +15 -0
- package/dist/channels/webhook/types.js +0 -0
- package/dist/channels/webpush/channel.d.ts +26 -0
- package/dist/channels/webpush/channel.js +55 -0
- package/dist/channels/webpush/index.d.ts +3 -0
- package/dist/channels/webpush/index.js +4 -0
- package/dist/channels/webpush/message.d.ts +90 -0
- package/dist/channels/webpush/message.js +174 -0
- package/dist/channels/webpush/types.d.ts +50 -0
- package/dist/channels/webpush/types.js +0 -0
- package/dist/database/adapters/knex.d.ts +6 -0
- package/dist/database/adapters/knex.js +116 -0
- package/dist/database/adapters/kysely.d.ts +6 -0
- package/dist/database/adapters/kysely.js +101 -0
- package/dist/database/channel.d.ts +24 -0
- package/dist/database/channel.js +42 -0
- package/dist/database/database.d.ts +18 -0
- package/dist/database/database.js +89 -0
- package/dist/database/index.d.ts +3 -0
- package/dist/database/index.js +4 -0
- package/dist/database/message.d.ts +26 -0
- package/dist/database/message.js +51 -0
- package/dist/database/types.d.ts +147 -0
- package/dist/database/types.js +0 -0
- package/dist/debug.js +7 -0
- package/dist/errors/duplicate_notification_exception.d.ts +13 -0
- package/dist/errors/duplicate_notification_exception.js +21 -0
- package/dist/errors/http_error.d.ts +16 -0
- package/dist/errors/http_error.js +30 -0
- package/dist/errors/index.d.ts +38 -0
- package/dist/errors/index.js +39 -0
- package/dist/events/events.d.ts +90 -0
- package/dist/events/events.js +83 -0
- package/dist/facteur.d.ts +37 -0
- package/dist/facteur.js +83 -0
- package/dist/fake.d.ts +47 -0
- package/dist/fake.js +85 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +16 -0
- package/dist/notifications/channel_resolver.js +91 -0
- package/dist/notifications/notification_discoverer.d.ts +40 -0
- package/dist/notifications/notification_discoverer.js +113 -0
- package/dist/notifications/notification_sender.js +210 -0
- package/dist/options.d.ts +22 -0
- package/dist/options.js +57 -0
- package/dist/types/channel.d.ts +18 -0
- package/dist/types/channel.js +5 -0
- package/dist/types/events.d.ts +27 -0
- package/dist/types/extend.d.ts +25 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.js +4 -0
- package/dist/types/notifications.d.ts +60 -0
- package/dist/types/notifications.js +38 -0
- package/dist/types/options.d.ts +157 -0
- package/dist/types/preferences.d.ts +40 -0
- package/dist/types/queue.d.ts +11 -0
- package/package.json +65 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
//#region src/channels/twilio/message.ts
|
|
2
|
+
var TwilioMessage = class TwilioMessage {
|
|
3
|
+
#body = "";
|
|
4
|
+
#from;
|
|
5
|
+
#messagingServiceSid;
|
|
6
|
+
#alphanumericSender;
|
|
7
|
+
#maxPrice;
|
|
8
|
+
#validityPeriod;
|
|
9
|
+
#forceDelivery;
|
|
10
|
+
#provideFeedback;
|
|
11
|
+
#applicationSid;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new instance of TwilioMessage
|
|
14
|
+
*/
|
|
15
|
+
static create() {
|
|
16
|
+
return new TwilioMessage();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sets the message content/body
|
|
20
|
+
*/
|
|
21
|
+
setBody(body) {
|
|
22
|
+
this.#body = body;
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Gets the message body
|
|
27
|
+
*/
|
|
28
|
+
getBody() {
|
|
29
|
+
return this.#body;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Sets the sender phone number
|
|
33
|
+
*/
|
|
34
|
+
setFrom(from) {
|
|
35
|
+
this.#from = from;
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Gets the sender phone number
|
|
40
|
+
*/
|
|
41
|
+
getFrom() {
|
|
42
|
+
return this.#from;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Sets the messaging service SID
|
|
46
|
+
*/
|
|
47
|
+
setMessagingServiceSid(sid) {
|
|
48
|
+
this.#messagingServiceSid = sid;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Gets the messaging service SID.
|
|
53
|
+
*/
|
|
54
|
+
getMessagingServiceSid() {
|
|
55
|
+
return this.#messagingServiceSid;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Sets the alphanumeric sender
|
|
59
|
+
*/
|
|
60
|
+
setAlphanumericSender(sender) {
|
|
61
|
+
this.#alphanumericSender = sender;
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Gets the alphanumeric sender
|
|
66
|
+
*/
|
|
67
|
+
getAlphanumericSender() {
|
|
68
|
+
return this.#alphanumericSender;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Sets the maximum price per message in USD
|
|
72
|
+
*/
|
|
73
|
+
setMaxPrice(price) {
|
|
74
|
+
this.#maxPrice = price;
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Gets the maximum price.
|
|
79
|
+
*/
|
|
80
|
+
getMaxPrice() {
|
|
81
|
+
return this.#maxPrice;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Sets the validity period in seconds
|
|
85
|
+
*/
|
|
86
|
+
setValidityPeriod(seconds) {
|
|
87
|
+
this.#validityPeriod = seconds;
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Gets the validity period.
|
|
92
|
+
*/
|
|
93
|
+
getValidityPeriod() {
|
|
94
|
+
return this.#validityPeriod;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Sets force delivery option.
|
|
98
|
+
*/
|
|
99
|
+
setForceDelivery(force) {
|
|
100
|
+
this.#forceDelivery = force;
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Gets force delivery option
|
|
105
|
+
*/
|
|
106
|
+
getForceDelivery() {
|
|
107
|
+
return this.#forceDelivery;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Sets provide feedback option
|
|
111
|
+
*/
|
|
112
|
+
setProvideFeedback(provide) {
|
|
113
|
+
this.#provideFeedback = provide;
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Gets provide feedback option.
|
|
118
|
+
*/
|
|
119
|
+
getProvideFeedback() {
|
|
120
|
+
return this.#provideFeedback;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Sets the application SID for status callbacks.
|
|
124
|
+
*/
|
|
125
|
+
setApplicationSid(sid) {
|
|
126
|
+
this.#applicationSid = sid;
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Gets the application SID
|
|
131
|
+
*/
|
|
132
|
+
getApplicationSid() {
|
|
133
|
+
return this.#applicationSid;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Serializes the message to Twilio API format
|
|
137
|
+
*/
|
|
138
|
+
serialize() {
|
|
139
|
+
const data = { body: this.#body };
|
|
140
|
+
if (this.#from) data.from = this.#from;
|
|
141
|
+
if (this.#messagingServiceSid) data.messagingServiceSid = this.#messagingServiceSid;
|
|
142
|
+
if (this.#maxPrice !== void 0) data.maxPrice = this.#maxPrice;
|
|
143
|
+
if (this.#validityPeriod !== void 0) data.validityPeriod = this.#validityPeriod;
|
|
144
|
+
if (this.#forceDelivery !== void 0) data.forceDelivery = this.#forceDelivery;
|
|
145
|
+
if (this.#provideFeedback !== void 0) data.provideFeedback = this.#provideFeedback;
|
|
146
|
+
if (this.#applicationSid) data.applicationSid = this.#applicationSid;
|
|
147
|
+
return data;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
//#endregion
|
|
152
|
+
export { TwilioMessage };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//#region src/channels/twilio/types.d.ts
|
|
2
|
+
interface TwilioConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Twilio Account SID
|
|
5
|
+
*/
|
|
6
|
+
accountSid: string;
|
|
7
|
+
/**
|
|
8
|
+
* Twilio Auth Token
|
|
9
|
+
*/
|
|
10
|
+
authToken: string;
|
|
11
|
+
/**
|
|
12
|
+
* Default phone number to send messages from
|
|
13
|
+
*/
|
|
14
|
+
from?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Messaging Service SID (recommended by Twilio)
|
|
17
|
+
*/
|
|
18
|
+
messagingServiceSid?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Alphanumeric sender (for supported regions)
|
|
21
|
+
*/
|
|
22
|
+
alphanumericSender?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Maximum price per message in USD
|
|
25
|
+
*/
|
|
26
|
+
maxPrice?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Debug mode - redirect all messages to this number
|
|
29
|
+
*/
|
|
30
|
+
debugTo?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Enable URL shortening
|
|
33
|
+
*/
|
|
34
|
+
shortenUrls?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Twilio error codes to ignore
|
|
37
|
+
*/
|
|
38
|
+
ignoredErrorCodes?: (string | number)[];
|
|
39
|
+
}
|
|
40
|
+
interface TwilioTargets {
|
|
41
|
+
/**
|
|
42
|
+
* The phone number to send the message to
|
|
43
|
+
*/
|
|
44
|
+
to: string;
|
|
45
|
+
/**
|
|
46
|
+
* Override the from number for this specific message
|
|
47
|
+
*/
|
|
48
|
+
from?: string;
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { TwilioConfig, TwilioTargets };
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { HTTPErrorInfo } from "../../errors/http_error.js";
|
|
2
|
+
|
|
3
|
+
//#region src/channels/webhook/exceptions.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Exception thrown when a webhook request fails
|
|
7
|
+
*/
|
|
8
|
+
declare class WebhookRequestException extends Error {
|
|
9
|
+
code: string;
|
|
10
|
+
url: string;
|
|
11
|
+
responseBody: string;
|
|
12
|
+
constructor(httpErrorInfo: HTTPErrorInfo);
|
|
13
|
+
}
|
|
14
|
+
declare const errors: {
|
|
15
|
+
E_WEBHOOK_REQUEST_FAILED: typeof WebhookRequestException;
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { WebhookRequestException, errors };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/channels/webhook/exceptions.ts
|
|
2
|
+
/**
|
|
3
|
+
* Exception thrown when a webhook request fails
|
|
4
|
+
*/
|
|
5
|
+
var WebhookRequestException = class extends Error {
|
|
6
|
+
constructor(httpErrorInfo) {
|
|
7
|
+
const { url, status, statusText, responseBody } = httpErrorInfo;
|
|
8
|
+
const message = [
|
|
9
|
+
`\n- Webhook request failed to ${url}`,
|
|
10
|
+
`- Status: ${status} ${statusText}`,
|
|
11
|
+
`- Response body:`,
|
|
12
|
+
responseBody
|
|
13
|
+
].join("\n");
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "WebhookRequestException";
|
|
16
|
+
this.code = "E_WEBHOOK_REQUEST_FAILED";
|
|
17
|
+
this.url = url;
|
|
18
|
+
this.responseBody = responseBody;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const errors = { E_WEBHOOK_REQUEST_FAILED: WebhookRequestException };
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
export { WebhookRequestException, errors };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { WebhookMessage } from "./message.js";
|
|
2
|
+
import { WebhookChannel, webhookChannel } from "./provider.js";
|
|
3
|
+
import { WebhookRequestException, errors } from "./exceptions.js";
|
|
4
|
+
export { WebhookChannel, WebhookMessage, WebhookRequestException, errors, webhookChannel };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { WebhookMessage } from "./message.js";
|
|
2
|
+
import { WebhookRequestException, errors } from "./exceptions.js";
|
|
3
|
+
import { WebhookChannel, webhookChannel } from "./provider.js";
|
|
4
|
+
|
|
5
|
+
export { WebhookChannel, WebhookMessage, WebhookRequestException, errors, webhookChannel };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/channels/webhook/message.d.ts
|
|
2
|
+
declare class WebhookMessage {
|
|
3
|
+
#private;
|
|
4
|
+
static create(): WebhookMessage;
|
|
5
|
+
/**
|
|
6
|
+
* Set the data for the webhook message
|
|
7
|
+
*/
|
|
8
|
+
setBody(body: any): this;
|
|
9
|
+
/**
|
|
10
|
+
* Set a header for the webhook message
|
|
11
|
+
*/
|
|
12
|
+
setHeader(name: string, value: string): this;
|
|
13
|
+
/**
|
|
14
|
+
* Set query parameters that will be appended to the webhook URL
|
|
15
|
+
*/
|
|
16
|
+
setQueryParameters(params: Record<string, string>): this;
|
|
17
|
+
serialize(): {
|
|
18
|
+
body: any;
|
|
19
|
+
headers: Record<string, string>;
|
|
20
|
+
queryParameters: Record<string, string>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { WebhookMessage };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/channels/webhook/message.ts
|
|
2
|
+
var WebhookMessage = class WebhookMessage {
|
|
3
|
+
#body;
|
|
4
|
+
#headers = {};
|
|
5
|
+
#queryParameters = {};
|
|
6
|
+
static create() {
|
|
7
|
+
return new WebhookMessage();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Set the data for the webhook message
|
|
11
|
+
*/
|
|
12
|
+
setBody(body) {
|
|
13
|
+
this.#body = body;
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Set a header for the webhook message
|
|
18
|
+
*/
|
|
19
|
+
setHeader(name, value) {
|
|
20
|
+
this.#headers[name] = value;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Set query parameters that will be appended to the webhook URL
|
|
25
|
+
*/
|
|
26
|
+
setQueryParameters(params) {
|
|
27
|
+
this.#queryParameters = params;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
serialize() {
|
|
31
|
+
return {
|
|
32
|
+
body: this.#body,
|
|
33
|
+
headers: this.#headers,
|
|
34
|
+
queryParameters: this.#queryParameters
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
export { WebhookMessage };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Channel, ChannelSendParams, kTargetSymbol } from "../../types/channel.js";
|
|
2
|
+
import { WebhookMessage } from "./message.js";
|
|
3
|
+
import { WebhookOptions, WebhookTargets } from "./types.js";
|
|
4
|
+
|
|
5
|
+
//#region src/channels/webhook/provider.d.ts
|
|
6
|
+
declare function webhookChannel<Options extends WebhookOptions<any>>(options: Options & {
|
|
7
|
+
name: string;
|
|
8
|
+
}): WebhookChannel<Options>;
|
|
9
|
+
declare class WebhookChannel<T extends WebhookOptions<any>> implements Channel<T, WebhookMessage, any, WebhookTargets<T>> {
|
|
10
|
+
#private;
|
|
11
|
+
name: "webhook";
|
|
12
|
+
[kTargetSymbol]: WebhookTargets<T>;
|
|
13
|
+
constructor(options: T & {
|
|
14
|
+
name: string;
|
|
15
|
+
});
|
|
16
|
+
send(params: ChannelSendParams<WebhookMessage, WebhookTargets<T>>): Promise<any>;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { WebhookChannel, webhookChannel };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { errors } from "../../errors/index.js";
|
|
2
|
+
import { kTargetSymbol } from "../../types/channel.js";
|
|
3
|
+
import "../../types/index.js";
|
|
4
|
+
import { WebhookRequestException } from "./exceptions.js";
|
|
5
|
+
import { HTTPErrorExtractor } from "../../errors/http_error.js";
|
|
6
|
+
import ky, { HTTPError } from "ky";
|
|
7
|
+
|
|
8
|
+
//#region src/channels/webhook/provider.ts
|
|
9
|
+
function webhookChannel(options) {
|
|
10
|
+
return new WebhookChannel(options);
|
|
11
|
+
}
|
|
12
|
+
var WebhookChannel = class {
|
|
13
|
+
name = "webhook";
|
|
14
|
+
[kTargetSymbol] = null;
|
|
15
|
+
#webhooksUrls = /* @__PURE__ */ new Map();
|
|
16
|
+
#name;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.#name = options.name;
|
|
19
|
+
this.#initWebhooksEndpoints(options);
|
|
20
|
+
}
|
|
21
|
+
#buildWebhookEntry(key, endpoint) {
|
|
22
|
+
const url = new URL(endpoint);
|
|
23
|
+
return [key, url];
|
|
24
|
+
}
|
|
25
|
+
#initWebhooksEndpoints(options) {
|
|
26
|
+
if ("webhookUrl" in options) {
|
|
27
|
+
this.#webhooksUrls.set(...this.#buildWebhookEntry("default", options.webhookUrl));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!options.webhooks) return;
|
|
31
|
+
for (const [key, endpoint] of Object.entries(options.webhooks)) this.#webhooksUrls.set(key, new URL(endpoint));
|
|
32
|
+
}
|
|
33
|
+
async #sendMessageToWebhook(url, message) {
|
|
34
|
+
const serialized = message.serialize();
|
|
35
|
+
return await ky.post(url, {
|
|
36
|
+
searchParams: serialized.queryParameters,
|
|
37
|
+
headers: serialized.headers,
|
|
38
|
+
json: serialized.body
|
|
39
|
+
}).catch(async (error) => {
|
|
40
|
+
if (error instanceof HTTPError) throw new WebhookRequestException(await HTTPErrorExtractor.extract(error));
|
|
41
|
+
throw error;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
#normalizeTargets(targets) {
|
|
45
|
+
if (!targets) return [...this.#webhooksUrls.values()];
|
|
46
|
+
if ("webhookUrl" in targets) return [this.#buildWebhookEntry("default", targets.webhookUrl)[1]];
|
|
47
|
+
return Object.keys(targets).map((key) => this.#webhooksUrls.get(key)).filter(Boolean);
|
|
48
|
+
}
|
|
49
|
+
#resolveTargets(options) {
|
|
50
|
+
if (options.targets) return options.targets;
|
|
51
|
+
throw new errors.E_UNAVAILABLE_TARGETS([this.#name]);
|
|
52
|
+
}
|
|
53
|
+
async send(params) {
|
|
54
|
+
const { message } = params;
|
|
55
|
+
const targets = this.#resolveTargets(params);
|
|
56
|
+
const normalizedTargets = this.#normalizeTargets(targets);
|
|
57
|
+
for (const url of normalizedTargets) await this.#sendMessageToWebhook(url, message);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
//#endregion
|
|
63
|
+
export { WebhookChannel, webhookChannel };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/channels/webhook/types.d.ts
|
|
2
|
+
type WebhookOptions<WebhookNames extends string> = {
|
|
3
|
+
webhookUrl: string;
|
|
4
|
+
} | {
|
|
5
|
+
webhooks: Record<WebhookNames, string>;
|
|
6
|
+
};
|
|
7
|
+
type WebhookTargets<Options extends WebhookOptions<any>> = Options extends {
|
|
8
|
+
webhooks: infer Webhooks;
|
|
9
|
+
} ? { [key in keyof Webhooks]?: boolean } & {
|
|
10
|
+
webhookUrl?: string;
|
|
11
|
+
} : {
|
|
12
|
+
webhookUrl: string;
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { WebhookOptions, WebhookTargets };
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Channel, ChannelSendParams, kTargetSymbol } from "../../types/channel.js";
|
|
2
|
+
import { WebpushMessage } from "./message.js";
|
|
3
|
+
import { WebpushConfig, WebpushTargets } from "./types.js";
|
|
4
|
+
import webpush from "web-push";
|
|
5
|
+
|
|
6
|
+
//#region src/channels/webpush/channel.d.ts
|
|
7
|
+
declare function webpushChannel(config: WebpushConfig): WebpushChannel;
|
|
8
|
+
declare class WebpushChannel implements Channel<WebpushConfig, WebpushMessage, any, WebpushTargets> {
|
|
9
|
+
#private;
|
|
10
|
+
private config;
|
|
11
|
+
name: "webpush";
|
|
12
|
+
[kTargetSymbol]: WebpushTargets;
|
|
13
|
+
constructor(config: WebpushConfig);
|
|
14
|
+
send(options: ChannelSendParams<WebpushMessage, WebpushTargets>): Promise<{
|
|
15
|
+
statusCode: number;
|
|
16
|
+
headers: webpush.Headers;
|
|
17
|
+
body: string;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
declare module '@facteurjs/core/types' {
|
|
21
|
+
interface Notification {
|
|
22
|
+
asWebpushMessage(): WebpushMessage;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { WebpushChannel, webpushChannel };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { errors } from "../../errors/index.js";
|
|
2
|
+
import { kTargetSymbol } from "../../types/channel.js";
|
|
3
|
+
import "../../types/index.js";
|
|
4
|
+
import webpush from "web-push";
|
|
5
|
+
|
|
6
|
+
//#region src/channels/webpush/channel.ts
|
|
7
|
+
function webpushChannel(config) {
|
|
8
|
+
return new WebpushChannel(config);
|
|
9
|
+
}
|
|
10
|
+
var WebpushChannel = class {
|
|
11
|
+
name = "webpush";
|
|
12
|
+
[kTargetSymbol] = null;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
webpush.setVapidDetails(config.vapidSubject, config.vapidPublicKey, config.vapidPrivateKey);
|
|
16
|
+
if (config.gcmApiKey) webpush.setGCMAPIKey(config.gcmApiKey);
|
|
17
|
+
}
|
|
18
|
+
#resolveTargets(options) {
|
|
19
|
+
if (options.targets) return options.targets;
|
|
20
|
+
throw new errors.E_UNAVAILABLE_TARGETS(["Webpush"]);
|
|
21
|
+
}
|
|
22
|
+
#buildOptions() {
|
|
23
|
+
const options = {};
|
|
24
|
+
if (this.config.ttl !== void 0) options.TTL = this.config.ttl;
|
|
25
|
+
if (this.config.urgency) options.urgency = this.config.urgency;
|
|
26
|
+
if (this.config.proxy) options.proxy = this.config.proxy;
|
|
27
|
+
if (this.config.timeout) options.timeout = this.config.timeout;
|
|
28
|
+
return options;
|
|
29
|
+
}
|
|
30
|
+
#handleError(error) {
|
|
31
|
+
if (error.statusCode === 410) throw new Error(`Webpush subscription is no longer valid: ${error.body}`);
|
|
32
|
+
if (error.statusCode === 413) throw new Error(`Webpush payload too large: ${error.body}`);
|
|
33
|
+
if (error.statusCode === 400) throw new Error(`Invalid webpush request: ${error.body}`);
|
|
34
|
+
if (error.statusCode === 429) throw new Error(`Webpush rate limit exceeded: ${error.body}`);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
async send(options) {
|
|
38
|
+
const payload = options.message.serialize();
|
|
39
|
+
const targets = this.#resolveTargets(options);
|
|
40
|
+
const webpushOptions = this.#buildOptions();
|
|
41
|
+
try {
|
|
42
|
+
const result = await webpush.sendNotification(targets.subscription, payload, webpushOptions);
|
|
43
|
+
return {
|
|
44
|
+
statusCode: result.statusCode,
|
|
45
|
+
headers: result.headers,
|
|
46
|
+
body: result.body
|
|
47
|
+
};
|
|
48
|
+
} catch (error) {
|
|
49
|
+
return this.#handleError(error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
export { WebpushChannel, webpushChannel };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
//#region src/channels/webpush/message.d.ts
|
|
2
|
+
declare class WebpushMessage {
|
|
3
|
+
#private;
|
|
4
|
+
/**
|
|
5
|
+
* Creates a new instance of WebpushMessage
|
|
6
|
+
*/
|
|
7
|
+
static create(): WebpushMessage;
|
|
8
|
+
/**
|
|
9
|
+
* Sets the notification title
|
|
10
|
+
*/
|
|
11
|
+
setTitle(title: string): this;
|
|
12
|
+
/**
|
|
13
|
+
* Sets the notification body
|
|
14
|
+
*/
|
|
15
|
+
setBody(body: string): this;
|
|
16
|
+
/**
|
|
17
|
+
* Sets the notification icon
|
|
18
|
+
*/
|
|
19
|
+
setIcon(icon: string): this;
|
|
20
|
+
/**
|
|
21
|
+
* Sets the notification image
|
|
22
|
+
*/
|
|
23
|
+
setImage(image: string): this;
|
|
24
|
+
/**
|
|
25
|
+
* Sets the notification badge
|
|
26
|
+
*/
|
|
27
|
+
setBadge(badge: string): this;
|
|
28
|
+
/**
|
|
29
|
+
* Sets the notification tag
|
|
30
|
+
*/
|
|
31
|
+
setTag(tag: string): this;
|
|
32
|
+
/**
|
|
33
|
+
* Sets the notification data
|
|
34
|
+
*/
|
|
35
|
+
setData(data: any): this;
|
|
36
|
+
/**
|
|
37
|
+
* Sets the notification actions
|
|
38
|
+
*/
|
|
39
|
+
setActions(actions: Array<{
|
|
40
|
+
action: string;
|
|
41
|
+
title: string;
|
|
42
|
+
icon?: string;
|
|
43
|
+
}>): this;
|
|
44
|
+
/**
|
|
45
|
+
* Adds a single action
|
|
46
|
+
*/
|
|
47
|
+
addAction(options: {
|
|
48
|
+
action: string;
|
|
49
|
+
title: string;
|
|
50
|
+
icon?: string;
|
|
51
|
+
}): this;
|
|
52
|
+
/**
|
|
53
|
+
* Sets the URL to open when notification is clicked
|
|
54
|
+
*/
|
|
55
|
+
setUrl(url: string): this;
|
|
56
|
+
/**
|
|
57
|
+
* Sets whether the notification requires interaction
|
|
58
|
+
*/
|
|
59
|
+
setRequireInteraction(requireInteraction: boolean): this;
|
|
60
|
+
/**
|
|
61
|
+
* Sets whether the notification should be silent
|
|
62
|
+
*/
|
|
63
|
+
setSilent(silent: boolean): this;
|
|
64
|
+
/**
|
|
65
|
+
* Sets whether to renotify when replacing an existing notification
|
|
66
|
+
*/
|
|
67
|
+
setRenotify(renotify: boolean): this;
|
|
68
|
+
/**
|
|
69
|
+
* Sets the notification timestamp
|
|
70
|
+
*/
|
|
71
|
+
setTimestamp(timestamp: number): this;
|
|
72
|
+
/**
|
|
73
|
+
* Sets the vibration pattern
|
|
74
|
+
*/
|
|
75
|
+
setVibrate(vibrate: number[]): this;
|
|
76
|
+
/**
|
|
77
|
+
* Sets the text direction
|
|
78
|
+
*/
|
|
79
|
+
setDir(dir: 'auto' | 'ltr' | 'rtl'): this;
|
|
80
|
+
/**
|
|
81
|
+
* Sets the notification language
|
|
82
|
+
*/
|
|
83
|
+
setLang(lang: string): this;
|
|
84
|
+
/**
|
|
85
|
+
* Serializes the message to web push format
|
|
86
|
+
*/
|
|
87
|
+
serialize(): string;
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
export { WebpushMessage };
|