@dudousxd/nestjs-notifications-webhook 0.4.0 → 0.5.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/LICENSE +21 -0
- package/dist/index.cjs +246 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +119 -0
- package/dist/index.d.ts +119 -5
- package/dist/index.js +215 -13
- package/dist/index.js.map +1 -1
- package/package.json +19 -6
- package/dist/index.d.ts.map +0 -1
- package/dist/tokens.d.ts +0 -5
- package/dist/tokens.d.ts.map +0 -1
- package/dist/tokens.js +0 -8
- package/dist/tokens.js.map +0 -1
- package/dist/webhook-message.d.ts +0 -44
- package/dist/webhook-message.d.ts.map +0 -1
- package/dist/webhook-message.js +0 -67
- package/dist/webhook-message.js.map +0 -1
- package/dist/webhook.channel.d.ts +0 -43
- package/dist/webhook.channel.d.ts.map +0 -1
- package/dist/webhook.channel.js +0 -85
- package/dist/webhook.channel.js.map +0 -1
- package/dist/webhook.module.d.ts +0 -26
- package/dist/webhook.module.d.ts.map +0 -1
- package/dist/webhook.module.js +0 -44
- package/dist/webhook.module.js.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Davide Carvalho
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
WEBHOOK_OPTIONS: () => WEBHOOK_OPTIONS,
|
|
25
|
+
WEBHOOK_OPTIONS_RESOLVER: () => WEBHOOK_OPTIONS_RESOLVER,
|
|
26
|
+
Webhook: () => Webhook,
|
|
27
|
+
WebhookChannel: () => WebhookChannel,
|
|
28
|
+
WebhookChannelModule: () => WebhookChannelModule,
|
|
29
|
+
WebhookMessage: () => WebhookMessage
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
|
|
33
|
+
// src/webhook-message.ts
|
|
34
|
+
var WebhookMessage = class {
|
|
35
|
+
static {
|
|
36
|
+
__name(this, "WebhookMessage");
|
|
37
|
+
}
|
|
38
|
+
_url;
|
|
39
|
+
_method = "POST";
|
|
40
|
+
_headers = {};
|
|
41
|
+
_payload = {};
|
|
42
|
+
/** Set the target URL (overrides the route and configured default). */
|
|
43
|
+
url(u) {
|
|
44
|
+
this._url = u;
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
/** Set the JSON body to send. */
|
|
48
|
+
payload(obj) {
|
|
49
|
+
this._payload = obj;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/** Set a single request header. */
|
|
53
|
+
header(key, value) {
|
|
54
|
+
this._headers[key] = value;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
/** Merge multiple request headers. */
|
|
58
|
+
headers(obj) {
|
|
59
|
+
this._headers = {
|
|
60
|
+
...this._headers,
|
|
61
|
+
...obj
|
|
62
|
+
};
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
/** Set the HTTP method (default `POST`). */
|
|
66
|
+
method(m) {
|
|
67
|
+
this._method = m;
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
get urlValue() {
|
|
71
|
+
return this._url;
|
|
72
|
+
}
|
|
73
|
+
get methodValue() {
|
|
74
|
+
return this._method;
|
|
75
|
+
}
|
|
76
|
+
get headerValues() {
|
|
77
|
+
return this._headers;
|
|
78
|
+
}
|
|
79
|
+
get payloadValue() {
|
|
80
|
+
return this._payload;
|
|
81
|
+
}
|
|
82
|
+
/** Serialize to a {@link WebhookRequest}. */
|
|
83
|
+
toRequest() {
|
|
84
|
+
return {
|
|
85
|
+
url: this._url,
|
|
86
|
+
method: this._method,
|
|
87
|
+
headers: {
|
|
88
|
+
...this._headers
|
|
89
|
+
},
|
|
90
|
+
body: this._payload
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/webhook.channel.ts
|
|
96
|
+
var import_node_crypto = require("crypto");
|
|
97
|
+
var import_nestjs_notifications_core = require("@dudousxd/nestjs-notifications-core");
|
|
98
|
+
var import_common = require("@nestjs/common");
|
|
99
|
+
|
|
100
|
+
// src/tokens.ts
|
|
101
|
+
var WEBHOOK_OPTIONS = /* @__PURE__ */ Symbol("WEBHOOK_OPTIONS");
|
|
102
|
+
var WEBHOOK_OPTIONS_RESOLVER = /* @__PURE__ */ Symbol("WEBHOOK_OPTIONS_RESOLVER");
|
|
103
|
+
|
|
104
|
+
// src/webhook.channel.ts
|
|
105
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
106
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
107
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
108
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
109
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
110
|
+
}
|
|
111
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
112
|
+
function _ts_metadata(k, v) {
|
|
113
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
114
|
+
}
|
|
115
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
116
|
+
function _ts_param(paramIndex, decorator) {
|
|
117
|
+
return function(target, key) {
|
|
118
|
+
decorator(target, key, paramIndex);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
__name(_ts_param, "_ts_param");
|
|
122
|
+
var DEFAULT_SIGNATURE_HEADER = "X-Signature-256";
|
|
123
|
+
var Webhook = (0, import_nestjs_notifications_core.createChannel)("webhook");
|
|
124
|
+
var WebhookChannel = class {
|
|
125
|
+
static {
|
|
126
|
+
__name(this, "WebhookChannel");
|
|
127
|
+
}
|
|
128
|
+
options;
|
|
129
|
+
resolveOptions;
|
|
130
|
+
channel = "webhook";
|
|
131
|
+
constructor(options, resolveOptions) {
|
|
132
|
+
this.options = options;
|
|
133
|
+
this.resolveOptions = resolveOptions;
|
|
134
|
+
}
|
|
135
|
+
async send(notifiable, notification, context) {
|
|
136
|
+
const options = context?.tenant && this.resolveOptions ? this.resolveOptions(context.tenant) : this.options;
|
|
137
|
+
const handler = (0, import_nestjs_notifications_core.getHandler)(notification, "webhook", "toWebhook");
|
|
138
|
+
if (!handler) {
|
|
139
|
+
const name = notification.constructor.notificationName ?? notification.constructor.name;
|
|
140
|
+
throw new import_nestjs_notifications_core.MissingChannelMethodError("webhook", "toWebhook()", name);
|
|
141
|
+
}
|
|
142
|
+
const result = handler({
|
|
143
|
+
notifiable,
|
|
144
|
+
localization: context?.localization,
|
|
145
|
+
tenant: context?.tenant
|
|
146
|
+
});
|
|
147
|
+
const message = result instanceof WebhookMessage ? result : new WebhookMessage().payload(result);
|
|
148
|
+
const request = message.toRequest();
|
|
149
|
+
const route = (0, import_nestjs_notifications_core.routeFor)(notifiable, "webhook", notification);
|
|
150
|
+
const url = request.url ?? (typeof route === "string" ? route : void 0) ?? options.url;
|
|
151
|
+
if (!url) {
|
|
152
|
+
throw new Error('The webhook channel needs a target URL. Set one on the WebhookMessage via .url(), return one from routeNotificationFor("webhook"), or set url in forRoot().');
|
|
153
|
+
}
|
|
154
|
+
await this.post(options, url, request.method, request.body, request.headers);
|
|
155
|
+
}
|
|
156
|
+
async post(options, url, method, body, messageHeaders) {
|
|
157
|
+
const payload = JSON.stringify(body);
|
|
158
|
+
const headers = {
|
|
159
|
+
...options.headers,
|
|
160
|
+
...messageHeaders,
|
|
161
|
+
"Content-Type": "application/json"
|
|
162
|
+
};
|
|
163
|
+
if (options.secret) {
|
|
164
|
+
const signature = (0, import_node_crypto.createHmac)("sha256", options.secret).update(payload).digest("hex");
|
|
165
|
+
headers[options.signatureHeader ?? DEFAULT_SIGNATURE_HEADER] = `sha256=${signature}`;
|
|
166
|
+
}
|
|
167
|
+
const response = await fetch(url, {
|
|
168
|
+
method,
|
|
169
|
+
headers,
|
|
170
|
+
body: payload
|
|
171
|
+
});
|
|
172
|
+
if (!response.ok) {
|
|
173
|
+
throw new Error(`Webhook request to ${url} failed with status ${response.status}.`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
WebhookChannel = _ts_decorate([
|
|
178
|
+
(0, import_common.Injectable)(),
|
|
179
|
+
_ts_param(0, (0, import_common.Inject)(WEBHOOK_OPTIONS)),
|
|
180
|
+
_ts_param(1, (0, import_common.Optional)()),
|
|
181
|
+
_ts_param(1, (0, import_common.Inject)(WEBHOOK_OPTIONS_RESOLVER)),
|
|
182
|
+
_ts_metadata("design:type", Function),
|
|
183
|
+
_ts_metadata("design:paramtypes", [
|
|
184
|
+
typeof WebhookChannelOptions === "undefined" ? Object : WebhookChannelOptions,
|
|
185
|
+
typeof WebhookOptionsResolver === "undefined" ? Object : WebhookOptionsResolver
|
|
186
|
+
])
|
|
187
|
+
], WebhookChannel);
|
|
188
|
+
|
|
189
|
+
// src/webhook.module.ts
|
|
190
|
+
var import_common2 = require("@nestjs/common");
|
|
191
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
192
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
193
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
194
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
195
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
196
|
+
}
|
|
197
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
198
|
+
var WebhookChannelModule = class _WebhookChannelModule {
|
|
199
|
+
static {
|
|
200
|
+
__name(this, "WebhookChannelModule");
|
|
201
|
+
}
|
|
202
|
+
static forRoot(options = {}) {
|
|
203
|
+
const webhookOptions = {
|
|
204
|
+
// Set each field only when provided (exactOptionalPropertyTypes); the channel resolves a
|
|
205
|
+
// per-notifiable URL and default headers when these are absent.
|
|
206
|
+
...options.url !== void 0 ? {
|
|
207
|
+
url: options.url
|
|
208
|
+
} : {},
|
|
209
|
+
...options.headers !== void 0 ? {
|
|
210
|
+
headers: options.headers
|
|
211
|
+
} : {}
|
|
212
|
+
};
|
|
213
|
+
const providers = [
|
|
214
|
+
{
|
|
215
|
+
provide: WEBHOOK_OPTIONS,
|
|
216
|
+
useValue: webhookOptions
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
provide: WEBHOOK_OPTIONS_RESOLVER,
|
|
220
|
+
useValue: options.resolveOptions ?? null
|
|
221
|
+
},
|
|
222
|
+
WebhookChannel
|
|
223
|
+
];
|
|
224
|
+
return {
|
|
225
|
+
module: _WebhookChannelModule,
|
|
226
|
+
global: options.global ?? true,
|
|
227
|
+
providers,
|
|
228
|
+
exports: [
|
|
229
|
+
WebhookChannel
|
|
230
|
+
]
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
WebhookChannelModule = _ts_decorate2([
|
|
235
|
+
(0, import_common2.Module)({})
|
|
236
|
+
], WebhookChannelModule);
|
|
237
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
238
|
+
0 && (module.exports = {
|
|
239
|
+
WEBHOOK_OPTIONS,
|
|
240
|
+
WEBHOOK_OPTIONS_RESOLVER,
|
|
241
|
+
Webhook,
|
|
242
|
+
WebhookChannel,
|
|
243
|
+
WebhookChannelModule,
|
|
244
|
+
WebhookMessage
|
|
245
|
+
});
|
|
246
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/webhook-message.ts","../src/webhook.channel.ts","../src/tokens.ts","../src/webhook.module.ts"],"sourcesContent":["export {\n WebhookMessage,\n type WebhookMethod,\n type WebhookRequest,\n} from './webhook-message';\nexport {\n Webhook,\n WebhookChannel,\n type WebhookChannelOptions,\n type WebhookNotification,\n type WebhookOptionsResolver,\n} from './webhook.channel';\nexport {\n WebhookChannelModule,\n type WebhookChannelModuleOptions,\n} from './webhook.module';\nexport { WEBHOOK_OPTIONS, WEBHOOK_OPTIONS_RESOLVER } from './tokens';\n","/** The HTTP method used for a webhook request. */\nexport type WebhookMethod = 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'GET';\n\n/** The resolved request description produced by {@link WebhookMessage.toRequest}. */\nexport interface WebhookRequest {\n /** Target URL, when set on the message; otherwise resolved by the channel. */\n url?: string | undefined;\n method: WebhookMethod;\n headers: Record<string, string>;\n /** The JSON body to send (a plain object). */\n body: Record<string, unknown>;\n}\n\n/**\n * A fluent builder for a webhook request.\n *\n * ```ts\n * new WebhookMessage()\n * .url('https://example.com/hooks/orders')\n * .header('X-Signature', sign(payload))\n * .payload({ event: 'order.paid', id: 42 });\n * ```\n */\nexport class WebhookMessage {\n private _url?: string;\n private _method: WebhookMethod = 'POST';\n private _headers: Record<string, string> = {};\n private _payload: Record<string, unknown> = {};\n\n /** Set the target URL (overrides the route and configured default). */\n url(u: string): this {\n this._url = u;\n return this;\n }\n\n /** Set the JSON body to send. */\n payload(obj: Record<string, unknown>): this {\n this._payload = obj;\n return this;\n }\n\n /** Set a single request header. */\n header(key: string, value: string): this {\n this._headers[key] = value;\n return this;\n }\n\n /** Merge multiple request headers. */\n headers(obj: Record<string, string>): this {\n this._headers = { ...this._headers, ...obj };\n return this;\n }\n\n /** Set the HTTP method (default `POST`). */\n method(m: WebhookMethod): this {\n this._method = m;\n return this;\n }\n\n get urlValue(): string | undefined {\n return this._url;\n }\n\n get methodValue(): WebhookMethod {\n return this._method;\n }\n\n get headerValues(): Record<string, string> {\n return this._headers;\n }\n\n get payloadValue(): Record<string, unknown> {\n return this._payload;\n }\n\n /** Serialize to a {@link WebhookRequest}. */\n toRequest(): WebhookRequest {\n return {\n url: this._url,\n method: this._method,\n headers: { ...this._headers },\n body: this._payload,\n };\n }\n}\n","import { createHmac } from 'node:crypto';\nimport {\n type ChannelContext,\n type ChannelDriver,\n type DeliveryContext,\n MissingChannelMethodError,\n type Notifiable,\n type Notification,\n createChannel,\n getHandler,\n routeFor,\n} from '@dudousxd/nestjs-notifications-core';\nimport { Inject, Injectable, Optional } from '@nestjs/common';\nimport { WEBHOOK_OPTIONS, WEBHOOK_OPTIONS_RESOLVER } from './tokens';\nimport { WebhookMessage } from './webhook-message';\nimport type { WebhookMethod } from './webhook-message';\n\nconst DEFAULT_SIGNATURE_HEADER = 'X-Signature-256';\n\n/** Channel handle: use as `@Webhook()` on a payload method, or as a token in `via()`. */\nexport const Webhook = createChannel('webhook');\n\n/** Resolved runtime options for the webhook channel. */\nexport interface WebhookChannelOptions {\n /** Default URL used when neither the message nor the route supplies one. */\n url?: string;\n /** Default headers merged into every request. */\n headers?: Record<string, string>;\n /**\n * HMAC-SHA256 secret. When set, the request is signed: the header (default\n * `X-Signature-256`) is set to `sha256=<hex digest of the raw JSON body>`, so the receiver\n * can verify authenticity. Per-tenant secrets work via `resolveOptions`.\n */\n secret?: string;\n /** Header name for the signature. Default `X-Signature-256`. */\n signatureHeader?: string;\n}\n\n/**\n * Implement this on a notification to define its webhook payload. Returning a plain\n * object is treated as the JSON body to POST; return a {@link WebhookMessage} for full\n * control over the url, method and headers.\n */\nexport interface WebhookNotification extends Notification {\n toWebhook(ctx: ChannelContext): WebhookMessage | Record<string, unknown>;\n}\n\n/** Resolves per-tenant {@link WebhookChannelOptions} from a tenant id. */\nexport type WebhookOptionsResolver = (tenant: string) => WebhookChannelOptions;\n\n/**\n * Delivers a notification by sending an HTTP request (JSON body) to a webhook endpoint.\n * The target URL comes from the {@link WebhookMessage}, else from\n * `routeNotificationFor('webhook')` (a URL string), else from the configured default.\n */\n@Injectable()\nexport class WebhookChannel implements ChannelDriver {\n readonly channel = 'webhook';\n\n constructor(\n @Inject(WEBHOOK_OPTIONS)\n private readonly options: WebhookChannelOptions,\n @Optional()\n @Inject(WEBHOOK_OPTIONS_RESOLVER)\n private readonly resolveOptions?: WebhookOptionsResolver,\n ) {}\n\n async send(\n notifiable: Notifiable,\n notification: Notification,\n context?: DeliveryContext,\n ): Promise<void> {\n const options =\n context?.tenant && this.resolveOptions ? this.resolveOptions(context.tenant) : this.options;\n\n const handler = getHandler(notification, 'webhook', 'toWebhook');\n if (!handler) {\n const name =\n (notification.constructor as { notificationName?: string }).notificationName ??\n notification.constructor.name;\n throw new MissingChannelMethodError('webhook', 'toWebhook()', name);\n }\n\n const result = handler({\n notifiable,\n localization: context?.localization,\n tenant: context?.tenant,\n }) as WebhookMessage | Record<string, unknown>;\n const message =\n result instanceof WebhookMessage ? result : new WebhookMessage().payload(result);\n const request = message.toRequest();\n\n const route = routeFor(notifiable, 'webhook', notification);\n const url = request.url ?? (typeof route === 'string' ? route : undefined) ?? options.url;\n\n if (!url) {\n throw new Error(\n 'The webhook channel needs a target URL. Set one on the WebhookMessage via .url(), ' +\n 'return one from routeNotificationFor(\"webhook\"), or set url in forRoot().',\n );\n }\n\n await this.post(options, url, request.method, request.body, request.headers);\n }\n\n private async post(\n options: WebhookChannelOptions,\n url: string,\n method: WebhookMethod,\n body: Record<string, unknown>,\n messageHeaders: Record<string, string>,\n ): Promise<void> {\n const payload = JSON.stringify(body);\n const headers: Record<string, string> = {\n ...options.headers,\n ...messageHeaders,\n 'Content-Type': 'application/json',\n };\n\n if (options.secret) {\n const signature = createHmac('sha256', options.secret).update(payload).digest('hex');\n headers[options.signatureHeader ?? DEFAULT_SIGNATURE_HEADER] = `sha256=${signature}`;\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: payload,\n });\n\n if (!response.ok) {\n throw new Error(`Webhook request to ${url} failed with status ${response.status}.`);\n }\n }\n}\n","/** DI token for the resolved {@link WebhookChannelOptions} (default url, headers). */\nexport const WEBHOOK_OPTIONS = Symbol('WEBHOOK_OPTIONS');\n\n/** DI token for the optional per-tenant {@link WebhookChannelOptions} resolver. */\nexport const WEBHOOK_OPTIONS_RESOLVER = Symbol('WEBHOOK_OPTIONS_RESOLVER');\n","import { type DynamicModule, Module, type Provider } from '@nestjs/common';\nimport { WEBHOOK_OPTIONS, WEBHOOK_OPTIONS_RESOLVER } from './tokens';\nimport {\n WebhookChannel,\n type WebhookChannelOptions,\n type WebhookOptionsResolver,\n} from './webhook.channel';\n\nexport interface WebhookChannelModuleOptions {\n /** Default URL used when neither the message nor the route supplies one. */\n url?: string;\n /** Default headers merged into every request. */\n headers?: Record<string, string>;\n /**\n * Optional per-tenant options resolver. When a notification is delivered with a\n * `context.tenant`, the returned options are used instead of the defaults.\n */\n resolveOptions?: WebhookOptionsResolver;\n /** Register globally so the channel is discoverable app-wide. Default true. */\n global?: boolean;\n}\n\n/**\n * Registers the webhook channel.\n *\n * ```ts\n * WebhookChannelModule.forRoot({ url: 'https://example.com/hooks/notifications' });\n * ```\n */\n@Module({})\nexport class WebhookChannelModule {\n static forRoot(options: WebhookChannelModuleOptions = {}): DynamicModule {\n const webhookOptions: WebhookChannelOptions = {\n // Set each field only when provided (exactOptionalPropertyTypes); the channel resolves a\n // per-notifiable URL and default headers when these are absent.\n ...(options.url !== undefined ? { url: options.url } : {}),\n ...(options.headers !== undefined ? { headers: options.headers } : {}),\n };\n\n const providers: Provider[] = [\n { provide: WEBHOOK_OPTIONS, useValue: webhookOptions },\n { provide: WEBHOOK_OPTIONS_RESOLVER, useValue: options.resolveOptions ?? null },\n WebhookChannel,\n ];\n\n return {\n module: WebhookChannelModule,\n global: options.global ?? true,\n providers,\n exports: [WebhookChannel],\n };\n }\n}\n"],"mappings":"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","names":["WebhookMessage","_url","_method","_headers","_payload","url","u","payload","obj","header","key","value","headers","method","m","urlValue","methodValue","headerValues","payloadValue","toRequest","body","WEBHOOK_OPTIONS","Symbol","WEBHOOK_OPTIONS_RESOLVER","DEFAULT_SIGNATURE_HEADER","Webhook","createChannel","WebhookChannel","channel","options","resolveOptions","send","notifiable","notification","context","tenant","handler","getHandler","name","notificationName","MissingChannelMethodError","result","localization","message","WebhookMessage","payload","request","toRequest","route","routeFor","url","undefined","Error","post","method","body","headers","messageHeaders","JSON","stringify","secret","signature","createHmac","update","digest","signatureHeader","response","fetch","ok","status","import_common","WebhookChannelModule","forRoot","options","webhookOptions","url","undefined","headers","providers","provide","WEBHOOK_OPTIONS","useValue","WEBHOOK_OPTIONS_RESOLVER","resolveOptions","WebhookChannel","module","global","exports"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as _dudousxd_nestjs_notifications_core from '@dudousxd/nestjs-notifications-core';
|
|
2
|
+
import { ChannelDriver, Notifiable, Notification, DeliveryContext, ChannelContext } from '@dudousxd/nestjs-notifications-core';
|
|
3
|
+
import { DynamicModule } from '@nestjs/common';
|
|
4
|
+
|
|
5
|
+
/** The HTTP method used for a webhook request. */
|
|
6
|
+
type WebhookMethod = 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'GET';
|
|
7
|
+
/** The resolved request description produced by {@link WebhookMessage.toRequest}. */
|
|
8
|
+
interface WebhookRequest {
|
|
9
|
+
/** Target URL, when set on the message; otherwise resolved by the channel. */
|
|
10
|
+
url?: string | undefined;
|
|
11
|
+
method: WebhookMethod;
|
|
12
|
+
headers: Record<string, string>;
|
|
13
|
+
/** The JSON body to send (a plain object). */
|
|
14
|
+
body: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A fluent builder for a webhook request.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* new WebhookMessage()
|
|
21
|
+
* .url('https://example.com/hooks/orders')
|
|
22
|
+
* .header('X-Signature', sign(payload))
|
|
23
|
+
* .payload({ event: 'order.paid', id: 42 });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare class WebhookMessage {
|
|
27
|
+
private _url?;
|
|
28
|
+
private _method;
|
|
29
|
+
private _headers;
|
|
30
|
+
private _payload;
|
|
31
|
+
/** Set the target URL (overrides the route and configured default). */
|
|
32
|
+
url(u: string): this;
|
|
33
|
+
/** Set the JSON body to send. */
|
|
34
|
+
payload(obj: Record<string, unknown>): this;
|
|
35
|
+
/** Set a single request header. */
|
|
36
|
+
header(key: string, value: string): this;
|
|
37
|
+
/** Merge multiple request headers. */
|
|
38
|
+
headers(obj: Record<string, string>): this;
|
|
39
|
+
/** Set the HTTP method (default `POST`). */
|
|
40
|
+
method(m: WebhookMethod): this;
|
|
41
|
+
get urlValue(): string | undefined;
|
|
42
|
+
get methodValue(): WebhookMethod;
|
|
43
|
+
get headerValues(): Record<string, string>;
|
|
44
|
+
get payloadValue(): Record<string, unknown>;
|
|
45
|
+
/** Serialize to a {@link WebhookRequest}. */
|
|
46
|
+
toRequest(): WebhookRequest;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Channel handle: use as `@Webhook()` on a payload method, or as a token in `via()`. */
|
|
50
|
+
declare const Webhook: _dudousxd_nestjs_notifications_core.ChannelHandle;
|
|
51
|
+
/** Resolved runtime options for the webhook channel. */
|
|
52
|
+
interface WebhookChannelOptions {
|
|
53
|
+
/** Default URL used when neither the message nor the route supplies one. */
|
|
54
|
+
url?: string;
|
|
55
|
+
/** Default headers merged into every request. */
|
|
56
|
+
headers?: Record<string, string>;
|
|
57
|
+
/**
|
|
58
|
+
* HMAC-SHA256 secret. When set, the request is signed: the header (default
|
|
59
|
+
* `X-Signature-256`) is set to `sha256=<hex digest of the raw JSON body>`, so the receiver
|
|
60
|
+
* can verify authenticity. Per-tenant secrets work via `resolveOptions`.
|
|
61
|
+
*/
|
|
62
|
+
secret?: string;
|
|
63
|
+
/** Header name for the signature. Default `X-Signature-256`. */
|
|
64
|
+
signatureHeader?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Implement this on a notification to define its webhook payload. Returning a plain
|
|
68
|
+
* object is treated as the JSON body to POST; return a {@link WebhookMessage} for full
|
|
69
|
+
* control over the url, method and headers.
|
|
70
|
+
*/
|
|
71
|
+
interface WebhookNotification extends Notification {
|
|
72
|
+
toWebhook(ctx: ChannelContext): WebhookMessage | Record<string, unknown>;
|
|
73
|
+
}
|
|
74
|
+
/** Resolves per-tenant {@link WebhookChannelOptions} from a tenant id. */
|
|
75
|
+
type WebhookOptionsResolver = (tenant: string) => WebhookChannelOptions;
|
|
76
|
+
/**
|
|
77
|
+
* Delivers a notification by sending an HTTP request (JSON body) to a webhook endpoint.
|
|
78
|
+
* The target URL comes from the {@link WebhookMessage}, else from
|
|
79
|
+
* `routeNotificationFor('webhook')` (a URL string), else from the configured default.
|
|
80
|
+
*/
|
|
81
|
+
declare class WebhookChannel implements ChannelDriver {
|
|
82
|
+
private readonly options;
|
|
83
|
+
private readonly resolveOptions?;
|
|
84
|
+
readonly channel = "webhook";
|
|
85
|
+
constructor(options: WebhookChannelOptions, resolveOptions?: WebhookOptionsResolver | undefined);
|
|
86
|
+
send(notifiable: Notifiable, notification: Notification, context?: DeliveryContext): Promise<void>;
|
|
87
|
+
private post;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface WebhookChannelModuleOptions {
|
|
91
|
+
/** Default URL used when neither the message nor the route supplies one. */
|
|
92
|
+
url?: string;
|
|
93
|
+
/** Default headers merged into every request. */
|
|
94
|
+
headers?: Record<string, string>;
|
|
95
|
+
/**
|
|
96
|
+
* Optional per-tenant options resolver. When a notification is delivered with a
|
|
97
|
+
* `context.tenant`, the returned options are used instead of the defaults.
|
|
98
|
+
*/
|
|
99
|
+
resolveOptions?: WebhookOptionsResolver;
|
|
100
|
+
/** Register globally so the channel is discoverable app-wide. Default true. */
|
|
101
|
+
global?: boolean;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Registers the webhook channel.
|
|
105
|
+
*
|
|
106
|
+
* ```ts
|
|
107
|
+
* WebhookChannelModule.forRoot({ url: 'https://example.com/hooks/notifications' });
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare class WebhookChannelModule {
|
|
111
|
+
static forRoot(options?: WebhookChannelModuleOptions): DynamicModule;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** DI token for the resolved {@link WebhookChannelOptions} (default url, headers). */
|
|
115
|
+
declare const WEBHOOK_OPTIONS: unique symbol;
|
|
116
|
+
/** DI token for the optional per-tenant {@link WebhookChannelOptions} resolver. */
|
|
117
|
+
declare const WEBHOOK_OPTIONS_RESOLVER: unique symbol;
|
|
118
|
+
|
|
119
|
+
export { WEBHOOK_OPTIONS, WEBHOOK_OPTIONS_RESOLVER, Webhook, WebhookChannel, WebhookChannelModule, type WebhookChannelModuleOptions, type WebhookChannelOptions, WebhookMessage, type WebhookMethod, type WebhookNotification, type WebhookOptionsResolver, type WebhookRequest };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,119 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import * as _dudousxd_nestjs_notifications_core from '@dudousxd/nestjs-notifications-core';
|
|
2
|
+
import { ChannelDriver, Notifiable, Notification, DeliveryContext, ChannelContext } from '@dudousxd/nestjs-notifications-core';
|
|
3
|
+
import { DynamicModule } from '@nestjs/common';
|
|
4
|
+
|
|
5
|
+
/** The HTTP method used for a webhook request. */
|
|
6
|
+
type WebhookMethod = 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'GET';
|
|
7
|
+
/** The resolved request description produced by {@link WebhookMessage.toRequest}. */
|
|
8
|
+
interface WebhookRequest {
|
|
9
|
+
/** Target URL, when set on the message; otherwise resolved by the channel. */
|
|
10
|
+
url?: string | undefined;
|
|
11
|
+
method: WebhookMethod;
|
|
12
|
+
headers: Record<string, string>;
|
|
13
|
+
/** The JSON body to send (a plain object). */
|
|
14
|
+
body: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A fluent builder for a webhook request.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* new WebhookMessage()
|
|
21
|
+
* .url('https://example.com/hooks/orders')
|
|
22
|
+
* .header('X-Signature', sign(payload))
|
|
23
|
+
* .payload({ event: 'order.paid', id: 42 });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare class WebhookMessage {
|
|
27
|
+
private _url?;
|
|
28
|
+
private _method;
|
|
29
|
+
private _headers;
|
|
30
|
+
private _payload;
|
|
31
|
+
/** Set the target URL (overrides the route and configured default). */
|
|
32
|
+
url(u: string): this;
|
|
33
|
+
/** Set the JSON body to send. */
|
|
34
|
+
payload(obj: Record<string, unknown>): this;
|
|
35
|
+
/** Set a single request header. */
|
|
36
|
+
header(key: string, value: string): this;
|
|
37
|
+
/** Merge multiple request headers. */
|
|
38
|
+
headers(obj: Record<string, string>): this;
|
|
39
|
+
/** Set the HTTP method (default `POST`). */
|
|
40
|
+
method(m: WebhookMethod): this;
|
|
41
|
+
get urlValue(): string | undefined;
|
|
42
|
+
get methodValue(): WebhookMethod;
|
|
43
|
+
get headerValues(): Record<string, string>;
|
|
44
|
+
get payloadValue(): Record<string, unknown>;
|
|
45
|
+
/** Serialize to a {@link WebhookRequest}. */
|
|
46
|
+
toRequest(): WebhookRequest;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Channel handle: use as `@Webhook()` on a payload method, or as a token in `via()`. */
|
|
50
|
+
declare const Webhook: _dudousxd_nestjs_notifications_core.ChannelHandle;
|
|
51
|
+
/** Resolved runtime options for the webhook channel. */
|
|
52
|
+
interface WebhookChannelOptions {
|
|
53
|
+
/** Default URL used when neither the message nor the route supplies one. */
|
|
54
|
+
url?: string;
|
|
55
|
+
/** Default headers merged into every request. */
|
|
56
|
+
headers?: Record<string, string>;
|
|
57
|
+
/**
|
|
58
|
+
* HMAC-SHA256 secret. When set, the request is signed: the header (default
|
|
59
|
+
* `X-Signature-256`) is set to `sha256=<hex digest of the raw JSON body>`, so the receiver
|
|
60
|
+
* can verify authenticity. Per-tenant secrets work via `resolveOptions`.
|
|
61
|
+
*/
|
|
62
|
+
secret?: string;
|
|
63
|
+
/** Header name for the signature. Default `X-Signature-256`. */
|
|
64
|
+
signatureHeader?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Implement this on a notification to define its webhook payload. Returning a plain
|
|
68
|
+
* object is treated as the JSON body to POST; return a {@link WebhookMessage} for full
|
|
69
|
+
* control over the url, method and headers.
|
|
70
|
+
*/
|
|
71
|
+
interface WebhookNotification extends Notification {
|
|
72
|
+
toWebhook(ctx: ChannelContext): WebhookMessage | Record<string, unknown>;
|
|
73
|
+
}
|
|
74
|
+
/** Resolves per-tenant {@link WebhookChannelOptions} from a tenant id. */
|
|
75
|
+
type WebhookOptionsResolver = (tenant: string) => WebhookChannelOptions;
|
|
76
|
+
/**
|
|
77
|
+
* Delivers a notification by sending an HTTP request (JSON body) to a webhook endpoint.
|
|
78
|
+
* The target URL comes from the {@link WebhookMessage}, else from
|
|
79
|
+
* `routeNotificationFor('webhook')` (a URL string), else from the configured default.
|
|
80
|
+
*/
|
|
81
|
+
declare class WebhookChannel implements ChannelDriver {
|
|
82
|
+
private readonly options;
|
|
83
|
+
private readonly resolveOptions?;
|
|
84
|
+
readonly channel = "webhook";
|
|
85
|
+
constructor(options: WebhookChannelOptions, resolveOptions?: WebhookOptionsResolver | undefined);
|
|
86
|
+
send(notifiable: Notifiable, notification: Notification, context?: DeliveryContext): Promise<void>;
|
|
87
|
+
private post;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface WebhookChannelModuleOptions {
|
|
91
|
+
/** Default URL used when neither the message nor the route supplies one. */
|
|
92
|
+
url?: string;
|
|
93
|
+
/** Default headers merged into every request. */
|
|
94
|
+
headers?: Record<string, string>;
|
|
95
|
+
/**
|
|
96
|
+
* Optional per-tenant options resolver. When a notification is delivered with a
|
|
97
|
+
* `context.tenant`, the returned options are used instead of the defaults.
|
|
98
|
+
*/
|
|
99
|
+
resolveOptions?: WebhookOptionsResolver;
|
|
100
|
+
/** Register globally so the channel is discoverable app-wide. Default true. */
|
|
101
|
+
global?: boolean;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Registers the webhook channel.
|
|
105
|
+
*
|
|
106
|
+
* ```ts
|
|
107
|
+
* WebhookChannelModule.forRoot({ url: 'https://example.com/hooks/notifications' });
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare class WebhookChannelModule {
|
|
111
|
+
static forRoot(options?: WebhookChannelModuleOptions): DynamicModule;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** DI token for the resolved {@link WebhookChannelOptions} (default url, headers). */
|
|
115
|
+
declare const WEBHOOK_OPTIONS: unique symbol;
|
|
116
|
+
/** DI token for the optional per-tenant {@link WebhookChannelOptions} resolver. */
|
|
117
|
+
declare const WEBHOOK_OPTIONS_RESOLVER: unique symbol;
|
|
118
|
+
|
|
119
|
+
export { WEBHOOK_OPTIONS, WEBHOOK_OPTIONS_RESOLVER, Webhook, WebhookChannel, WebhookChannelModule, type WebhookChannelModuleOptions, type WebhookChannelOptions, WebhookMessage, type WebhookMethod, type WebhookNotification, type WebhookOptionsResolver, type WebhookRequest };
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,216 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/webhook-message.ts
|
|
5
|
+
var WebhookMessage = class {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "WebhookMessage");
|
|
8
|
+
}
|
|
9
|
+
_url;
|
|
10
|
+
_method = "POST";
|
|
11
|
+
_headers = {};
|
|
12
|
+
_payload = {};
|
|
13
|
+
/** Set the target URL (overrides the route and configured default). */
|
|
14
|
+
url(u) {
|
|
15
|
+
this._url = u;
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
/** Set the JSON body to send. */
|
|
19
|
+
payload(obj) {
|
|
20
|
+
this._payload = obj;
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
/** Set a single request header. */
|
|
24
|
+
header(key, value) {
|
|
25
|
+
this._headers[key] = value;
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
/** Merge multiple request headers. */
|
|
29
|
+
headers(obj) {
|
|
30
|
+
this._headers = {
|
|
31
|
+
...this._headers,
|
|
32
|
+
...obj
|
|
33
|
+
};
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
/** Set the HTTP method (default `POST`). */
|
|
37
|
+
method(m) {
|
|
38
|
+
this._method = m;
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
get urlValue() {
|
|
42
|
+
return this._url;
|
|
43
|
+
}
|
|
44
|
+
get methodValue() {
|
|
45
|
+
return this._method;
|
|
46
|
+
}
|
|
47
|
+
get headerValues() {
|
|
48
|
+
return this._headers;
|
|
49
|
+
}
|
|
50
|
+
get payloadValue() {
|
|
51
|
+
return this._payload;
|
|
52
|
+
}
|
|
53
|
+
/** Serialize to a {@link WebhookRequest}. */
|
|
54
|
+
toRequest() {
|
|
55
|
+
return {
|
|
56
|
+
url: this._url,
|
|
57
|
+
method: this._method,
|
|
58
|
+
headers: {
|
|
59
|
+
...this._headers
|
|
60
|
+
},
|
|
61
|
+
body: this._payload
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// src/webhook.channel.ts
|
|
67
|
+
import { createHmac } from "crypto";
|
|
68
|
+
import { MissingChannelMethodError, createChannel, getHandler, routeFor } from "@dudousxd/nestjs-notifications-core";
|
|
69
|
+
import { Inject, Injectable, Optional } from "@nestjs/common";
|
|
70
|
+
|
|
71
|
+
// src/tokens.ts
|
|
72
|
+
var WEBHOOK_OPTIONS = /* @__PURE__ */ Symbol("WEBHOOK_OPTIONS");
|
|
73
|
+
var WEBHOOK_OPTIONS_RESOLVER = /* @__PURE__ */ Symbol("WEBHOOK_OPTIONS_RESOLVER");
|
|
74
|
+
|
|
75
|
+
// src/webhook.channel.ts
|
|
76
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
77
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
78
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
79
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
80
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
81
|
+
}
|
|
82
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
83
|
+
function _ts_metadata(k, v) {
|
|
84
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
85
|
+
}
|
|
86
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
87
|
+
function _ts_param(paramIndex, decorator) {
|
|
88
|
+
return function(target, key) {
|
|
89
|
+
decorator(target, key, paramIndex);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
__name(_ts_param, "_ts_param");
|
|
93
|
+
var DEFAULT_SIGNATURE_HEADER = "X-Signature-256";
|
|
94
|
+
var Webhook = createChannel("webhook");
|
|
95
|
+
var WebhookChannel = class {
|
|
96
|
+
static {
|
|
97
|
+
__name(this, "WebhookChannel");
|
|
98
|
+
}
|
|
99
|
+
options;
|
|
100
|
+
resolveOptions;
|
|
101
|
+
channel = "webhook";
|
|
102
|
+
constructor(options, resolveOptions) {
|
|
103
|
+
this.options = options;
|
|
104
|
+
this.resolveOptions = resolveOptions;
|
|
105
|
+
}
|
|
106
|
+
async send(notifiable, notification, context) {
|
|
107
|
+
const options = context?.tenant && this.resolveOptions ? this.resolveOptions(context.tenant) : this.options;
|
|
108
|
+
const handler = getHandler(notification, "webhook", "toWebhook");
|
|
109
|
+
if (!handler) {
|
|
110
|
+
const name = notification.constructor.notificationName ?? notification.constructor.name;
|
|
111
|
+
throw new MissingChannelMethodError("webhook", "toWebhook()", name);
|
|
112
|
+
}
|
|
113
|
+
const result = handler({
|
|
114
|
+
notifiable,
|
|
115
|
+
localization: context?.localization,
|
|
116
|
+
tenant: context?.tenant
|
|
117
|
+
});
|
|
118
|
+
const message = result instanceof WebhookMessage ? result : new WebhookMessage().payload(result);
|
|
119
|
+
const request = message.toRequest();
|
|
120
|
+
const route = routeFor(notifiable, "webhook", notification);
|
|
121
|
+
const url = request.url ?? (typeof route === "string" ? route : void 0) ?? options.url;
|
|
122
|
+
if (!url) {
|
|
123
|
+
throw new Error('The webhook channel needs a target URL. Set one on the WebhookMessage via .url(), return one from routeNotificationFor("webhook"), or set url in forRoot().');
|
|
124
|
+
}
|
|
125
|
+
await this.post(options, url, request.method, request.body, request.headers);
|
|
126
|
+
}
|
|
127
|
+
async post(options, url, method, body, messageHeaders) {
|
|
128
|
+
const payload = JSON.stringify(body);
|
|
129
|
+
const headers = {
|
|
130
|
+
...options.headers,
|
|
131
|
+
...messageHeaders,
|
|
132
|
+
"Content-Type": "application/json"
|
|
133
|
+
};
|
|
134
|
+
if (options.secret) {
|
|
135
|
+
const signature = createHmac("sha256", options.secret).update(payload).digest("hex");
|
|
136
|
+
headers[options.signatureHeader ?? DEFAULT_SIGNATURE_HEADER] = `sha256=${signature}`;
|
|
137
|
+
}
|
|
138
|
+
const response = await fetch(url, {
|
|
139
|
+
method,
|
|
140
|
+
headers,
|
|
141
|
+
body: payload
|
|
142
|
+
});
|
|
143
|
+
if (!response.ok) {
|
|
144
|
+
throw new Error(`Webhook request to ${url} failed with status ${response.status}.`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
WebhookChannel = _ts_decorate([
|
|
149
|
+
Injectable(),
|
|
150
|
+
_ts_param(0, Inject(WEBHOOK_OPTIONS)),
|
|
151
|
+
_ts_param(1, Optional()),
|
|
152
|
+
_ts_param(1, Inject(WEBHOOK_OPTIONS_RESOLVER)),
|
|
153
|
+
_ts_metadata("design:type", Function),
|
|
154
|
+
_ts_metadata("design:paramtypes", [
|
|
155
|
+
typeof WebhookChannelOptions === "undefined" ? Object : WebhookChannelOptions,
|
|
156
|
+
typeof WebhookOptionsResolver === "undefined" ? Object : WebhookOptionsResolver
|
|
157
|
+
])
|
|
158
|
+
], WebhookChannel);
|
|
159
|
+
|
|
160
|
+
// src/webhook.module.ts
|
|
161
|
+
import { Module } from "@nestjs/common";
|
|
162
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
163
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
164
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
165
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
166
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
167
|
+
}
|
|
168
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
169
|
+
var WebhookChannelModule = class _WebhookChannelModule {
|
|
170
|
+
static {
|
|
171
|
+
__name(this, "WebhookChannelModule");
|
|
172
|
+
}
|
|
173
|
+
static forRoot(options = {}) {
|
|
174
|
+
const webhookOptions = {
|
|
175
|
+
// Set each field only when provided (exactOptionalPropertyTypes); the channel resolves a
|
|
176
|
+
// per-notifiable URL and default headers when these are absent.
|
|
177
|
+
...options.url !== void 0 ? {
|
|
178
|
+
url: options.url
|
|
179
|
+
} : {},
|
|
180
|
+
...options.headers !== void 0 ? {
|
|
181
|
+
headers: options.headers
|
|
182
|
+
} : {}
|
|
183
|
+
};
|
|
184
|
+
const providers = [
|
|
185
|
+
{
|
|
186
|
+
provide: WEBHOOK_OPTIONS,
|
|
187
|
+
useValue: webhookOptions
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
provide: WEBHOOK_OPTIONS_RESOLVER,
|
|
191
|
+
useValue: options.resolveOptions ?? null
|
|
192
|
+
},
|
|
193
|
+
WebhookChannel
|
|
194
|
+
];
|
|
195
|
+
return {
|
|
196
|
+
module: _WebhookChannelModule,
|
|
197
|
+
global: options.global ?? true,
|
|
198
|
+
providers,
|
|
199
|
+
exports: [
|
|
200
|
+
WebhookChannel
|
|
201
|
+
]
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
WebhookChannelModule = _ts_decorate2([
|
|
206
|
+
Module({})
|
|
207
|
+
], WebhookChannelModule);
|
|
208
|
+
export {
|
|
209
|
+
WEBHOOK_OPTIONS,
|
|
210
|
+
WEBHOOK_OPTIONS_RESOLVER,
|
|
211
|
+
Webhook,
|
|
212
|
+
WebhookChannel,
|
|
213
|
+
WebhookChannelModule,
|
|
214
|
+
WebhookMessage
|
|
215
|
+
};
|
|
14
216
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qDAI2B;AAHzB,iHAAA,cAAc,OAAA;AAIhB,qDAM2B;AALzB,0GAAA,OAAO,OAAA;AACP,iHAAA,cAAc,OAAA;AAKhB,mDAG0B;AAFxB,sHAAA,oBAAoB,OAAA;AAGtB,mCAAqE;AAA5D,yGAAA,eAAe,OAAA;AAAE,kHAAA,wBAAwB,OAAA"}
|
|
1
|
+
{"version":3,"sources":["../src/webhook-message.ts","../src/webhook.channel.ts","../src/tokens.ts","../src/webhook.module.ts"],"sourcesContent":["/** The HTTP method used for a webhook request. */\nexport type WebhookMethod = 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'GET';\n\n/** The resolved request description produced by {@link WebhookMessage.toRequest}. */\nexport interface WebhookRequest {\n /** Target URL, when set on the message; otherwise resolved by the channel. */\n url?: string | undefined;\n method: WebhookMethod;\n headers: Record<string, string>;\n /** The JSON body to send (a plain object). */\n body: Record<string, unknown>;\n}\n\n/**\n * A fluent builder for a webhook request.\n *\n * ```ts\n * new WebhookMessage()\n * .url('https://example.com/hooks/orders')\n * .header('X-Signature', sign(payload))\n * .payload({ event: 'order.paid', id: 42 });\n * ```\n */\nexport class WebhookMessage {\n private _url?: string;\n private _method: WebhookMethod = 'POST';\n private _headers: Record<string, string> = {};\n private _payload: Record<string, unknown> = {};\n\n /** Set the target URL (overrides the route and configured default). */\n url(u: string): this {\n this._url = u;\n return this;\n }\n\n /** Set the JSON body to send. */\n payload(obj: Record<string, unknown>): this {\n this._payload = obj;\n return this;\n }\n\n /** Set a single request header. */\n header(key: string, value: string): this {\n this._headers[key] = value;\n return this;\n }\n\n /** Merge multiple request headers. */\n headers(obj: Record<string, string>): this {\n this._headers = { ...this._headers, ...obj };\n return this;\n }\n\n /** Set the HTTP method (default `POST`). */\n method(m: WebhookMethod): this {\n this._method = m;\n return this;\n }\n\n get urlValue(): string | undefined {\n return this._url;\n }\n\n get methodValue(): WebhookMethod {\n return this._method;\n }\n\n get headerValues(): Record<string, string> {\n return this._headers;\n }\n\n get payloadValue(): Record<string, unknown> {\n return this._payload;\n }\n\n /** Serialize to a {@link WebhookRequest}. */\n toRequest(): WebhookRequest {\n return {\n url: this._url,\n method: this._method,\n headers: { ...this._headers },\n body: this._payload,\n };\n }\n}\n","import { createHmac } from 'node:crypto';\nimport {\n type ChannelContext,\n type ChannelDriver,\n type DeliveryContext,\n MissingChannelMethodError,\n type Notifiable,\n type Notification,\n createChannel,\n getHandler,\n routeFor,\n} from '@dudousxd/nestjs-notifications-core';\nimport { Inject, Injectable, Optional } from '@nestjs/common';\nimport { WEBHOOK_OPTIONS, WEBHOOK_OPTIONS_RESOLVER } from './tokens';\nimport { WebhookMessage } from './webhook-message';\nimport type { WebhookMethod } from './webhook-message';\n\nconst DEFAULT_SIGNATURE_HEADER = 'X-Signature-256';\n\n/** Channel handle: use as `@Webhook()` on a payload method, or as a token in `via()`. */\nexport const Webhook = createChannel('webhook');\n\n/** Resolved runtime options for the webhook channel. */\nexport interface WebhookChannelOptions {\n /** Default URL used when neither the message nor the route supplies one. */\n url?: string;\n /** Default headers merged into every request. */\n headers?: Record<string, string>;\n /**\n * HMAC-SHA256 secret. When set, the request is signed: the header (default\n * `X-Signature-256`) is set to `sha256=<hex digest of the raw JSON body>`, so the receiver\n * can verify authenticity. Per-tenant secrets work via `resolveOptions`.\n */\n secret?: string;\n /** Header name for the signature. Default `X-Signature-256`. */\n signatureHeader?: string;\n}\n\n/**\n * Implement this on a notification to define its webhook payload. Returning a plain\n * object is treated as the JSON body to POST; return a {@link WebhookMessage} for full\n * control over the url, method and headers.\n */\nexport interface WebhookNotification extends Notification {\n toWebhook(ctx: ChannelContext): WebhookMessage | Record<string, unknown>;\n}\n\n/** Resolves per-tenant {@link WebhookChannelOptions} from a tenant id. */\nexport type WebhookOptionsResolver = (tenant: string) => WebhookChannelOptions;\n\n/**\n * Delivers a notification by sending an HTTP request (JSON body) to a webhook endpoint.\n * The target URL comes from the {@link WebhookMessage}, else from\n * `routeNotificationFor('webhook')` (a URL string), else from the configured default.\n */\n@Injectable()\nexport class WebhookChannel implements ChannelDriver {\n readonly channel = 'webhook';\n\n constructor(\n @Inject(WEBHOOK_OPTIONS)\n private readonly options: WebhookChannelOptions,\n @Optional()\n @Inject(WEBHOOK_OPTIONS_RESOLVER)\n private readonly resolveOptions?: WebhookOptionsResolver,\n ) {}\n\n async send(\n notifiable: Notifiable,\n notification: Notification,\n context?: DeliveryContext,\n ): Promise<void> {\n const options =\n context?.tenant && this.resolveOptions ? this.resolveOptions(context.tenant) : this.options;\n\n const handler = getHandler(notification, 'webhook', 'toWebhook');\n if (!handler) {\n const name =\n (notification.constructor as { notificationName?: string }).notificationName ??\n notification.constructor.name;\n throw new MissingChannelMethodError('webhook', 'toWebhook()', name);\n }\n\n const result = handler({\n notifiable,\n localization: context?.localization,\n tenant: context?.tenant,\n }) as WebhookMessage | Record<string, unknown>;\n const message =\n result instanceof WebhookMessage ? result : new WebhookMessage().payload(result);\n const request = message.toRequest();\n\n const route = routeFor(notifiable, 'webhook', notification);\n const url = request.url ?? (typeof route === 'string' ? route : undefined) ?? options.url;\n\n if (!url) {\n throw new Error(\n 'The webhook channel needs a target URL. Set one on the WebhookMessage via .url(), ' +\n 'return one from routeNotificationFor(\"webhook\"), or set url in forRoot().',\n );\n }\n\n await this.post(options, url, request.method, request.body, request.headers);\n }\n\n private async post(\n options: WebhookChannelOptions,\n url: string,\n method: WebhookMethod,\n body: Record<string, unknown>,\n messageHeaders: Record<string, string>,\n ): Promise<void> {\n const payload = JSON.stringify(body);\n const headers: Record<string, string> = {\n ...options.headers,\n ...messageHeaders,\n 'Content-Type': 'application/json',\n };\n\n if (options.secret) {\n const signature = createHmac('sha256', options.secret).update(payload).digest('hex');\n headers[options.signatureHeader ?? DEFAULT_SIGNATURE_HEADER] = `sha256=${signature}`;\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: payload,\n });\n\n if (!response.ok) {\n throw new Error(`Webhook request to ${url} failed with status ${response.status}.`);\n }\n }\n}\n","/** DI token for the resolved {@link WebhookChannelOptions} (default url, headers). */\nexport const WEBHOOK_OPTIONS = Symbol('WEBHOOK_OPTIONS');\n\n/** DI token for the optional per-tenant {@link WebhookChannelOptions} resolver. */\nexport const WEBHOOK_OPTIONS_RESOLVER = Symbol('WEBHOOK_OPTIONS_RESOLVER');\n","import { type DynamicModule, Module, type Provider } from '@nestjs/common';\nimport { WEBHOOK_OPTIONS, WEBHOOK_OPTIONS_RESOLVER } from './tokens';\nimport {\n WebhookChannel,\n type WebhookChannelOptions,\n type WebhookOptionsResolver,\n} from './webhook.channel';\n\nexport interface WebhookChannelModuleOptions {\n /** Default URL used when neither the message nor the route supplies one. */\n url?: string;\n /** Default headers merged into every request. */\n headers?: Record<string, string>;\n /**\n * Optional per-tenant options resolver. When a notification is delivered with a\n * `context.tenant`, the returned options are used instead of the defaults.\n */\n resolveOptions?: WebhookOptionsResolver;\n /** Register globally so the channel is discoverable app-wide. Default true. */\n global?: boolean;\n}\n\n/**\n * Registers the webhook channel.\n *\n * ```ts\n * WebhookChannelModule.forRoot({ url: 'https://example.com/hooks/notifications' });\n * ```\n */\n@Module({})\nexport class WebhookChannelModule {\n static forRoot(options: WebhookChannelModuleOptions = {}): DynamicModule {\n const webhookOptions: WebhookChannelOptions = {\n // Set each field only when provided (exactOptionalPropertyTypes); the channel resolves a\n // per-notifiable URL and default headers when these are absent.\n ...(options.url !== undefined ? { url: options.url } : {}),\n ...(options.headers !== undefined ? { headers: options.headers } : {}),\n };\n\n const providers: Provider[] = [\n { provide: WEBHOOK_OPTIONS, useValue: webhookOptions },\n { provide: WEBHOOK_OPTIONS_RESOLVER, useValue: options.resolveOptions ?? null },\n WebhookChannel,\n ];\n\n return {\n module: WebhookChannelModule,\n global: options.global ?? true,\n providers,\n exports: [WebhookChannel],\n };\n }\n}\n"],"mappings":"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","names":["WebhookMessage","_url","_method","_headers","_payload","url","u","payload","obj","header","key","value","headers","method","m","urlValue","methodValue","headerValues","payloadValue","toRequest","body","createHmac","MissingChannelMethodError","createChannel","getHandler","routeFor","Inject","Injectable","Optional","WEBHOOK_OPTIONS","Symbol","WEBHOOK_OPTIONS_RESOLVER","DEFAULT_SIGNATURE_HEADER","Webhook","createChannel","WebhookChannel","channel","options","resolveOptions","send","notifiable","notification","context","tenant","handler","getHandler","name","notificationName","MissingChannelMethodError","result","localization","message","WebhookMessage","payload","request","toRequest","route","routeFor","url","undefined","Error","post","method","body","headers","messageHeaders","JSON","stringify","secret","signature","createHmac","update","digest","signatureHeader","response","fetch","ok","status","Module","WebhookChannelModule","forRoot","options","webhookOptions","url","undefined","headers","providers","provide","WEBHOOK_OPTIONS","useValue","WEBHOOK_OPTIONS_RESOLVER","resolveOptions","WebhookChannel","module","global","exports"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dudousxd/nestjs-notifications-webhook",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Webhook channel for nestjs-notifications — deliver notifications by POSTing JSON to an HTTP endpoint",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Davide Carvalho",
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
"url": "https://github.com/DavideCarvalho/nestjs-notifications.git",
|
|
10
10
|
"directory": "packages/webhook"
|
|
11
11
|
},
|
|
12
|
-
"type": "
|
|
13
|
-
"main": "dist/index.
|
|
14
|
-
"types": "dist/index.d.ts",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/index.cjs",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
@@ -23,10 +23,23 @@
|
|
|
23
23
|
"@nestjs/common": "11.1.26",
|
|
24
24
|
"reflect-metadata": "0.2.2",
|
|
25
25
|
"typescript": "5.9.3",
|
|
26
|
-
"@dudousxd/nestjs-notifications-core": "^0.
|
|
26
|
+
"@dudousxd/nestjs-notifications-core": "^0.7.0"
|
|
27
|
+
},
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"import": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"require": {
|
|
36
|
+
"types": "./dist/index.d.cts",
|
|
37
|
+
"default": "./dist/index.cjs"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
27
40
|
},
|
|
28
41
|
"scripts": {
|
|
29
|
-
"build": "
|
|
42
|
+
"build": "tsup",
|
|
30
43
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
31
44
|
}
|
|
32
45
|
}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,OAAO,EACP,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,GAC5B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,GACjC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/tokens.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/** DI token for the resolved {@link WebhookChannelOptions} (default url, headers). */
|
|
2
|
-
export declare const WEBHOOK_OPTIONS: unique symbol;
|
|
3
|
-
/** DI token for the optional per-tenant {@link WebhookChannelOptions} resolver. */
|
|
4
|
-
export declare const WEBHOOK_OPTIONS_RESOLVER: unique symbol;
|
|
5
|
-
//# sourceMappingURL=tokens.d.ts.map
|
package/dist/tokens.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,eAAO,MAAM,eAAe,eAA4B,CAAC;AAEzD,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,eAAqC,CAAC"}
|
package/dist/tokens.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WEBHOOK_OPTIONS_RESOLVER = exports.WEBHOOK_OPTIONS = void 0;
|
|
4
|
-
/** DI token for the resolved {@link WebhookChannelOptions} (default url, headers). */
|
|
5
|
-
exports.WEBHOOK_OPTIONS = Symbol('WEBHOOK_OPTIONS');
|
|
6
|
-
/** DI token for the optional per-tenant {@link WebhookChannelOptions} resolver. */
|
|
7
|
-
exports.WEBHOOK_OPTIONS_RESOLVER = Symbol('WEBHOOK_OPTIONS_RESOLVER');
|
|
8
|
-
//# sourceMappingURL=tokens.js.map
|
package/dist/tokens.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":";;;AAAA,sFAAsF;AACzE,QAAA,eAAe,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;AAEzD,mFAAmF;AACtE,QAAA,wBAAwB,GAAG,MAAM,CAAC,0BAA0B,CAAC,CAAC"}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/** The HTTP method used for a webhook request. */
|
|
2
|
-
export type WebhookMethod = 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'GET';
|
|
3
|
-
/** The resolved request description produced by {@link WebhookMessage.toRequest}. */
|
|
4
|
-
export interface WebhookRequest {
|
|
5
|
-
/** Target URL, when set on the message; otherwise resolved by the channel. */
|
|
6
|
-
url?: string;
|
|
7
|
-
method: WebhookMethod;
|
|
8
|
-
headers: Record<string, string>;
|
|
9
|
-
/** The JSON body to send (a plain object). */
|
|
10
|
-
body: Record<string, unknown>;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* A fluent builder for a webhook request.
|
|
14
|
-
*
|
|
15
|
-
* ```ts
|
|
16
|
-
* new WebhookMessage()
|
|
17
|
-
* .url('https://example.com/hooks/orders')
|
|
18
|
-
* .header('X-Signature', sign(payload))
|
|
19
|
-
* .payload({ event: 'order.paid', id: 42 });
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
export declare class WebhookMessage {
|
|
23
|
-
private _url?;
|
|
24
|
-
private _method;
|
|
25
|
-
private _headers;
|
|
26
|
-
private _payload;
|
|
27
|
-
/** Set the target URL (overrides the route and configured default). */
|
|
28
|
-
url(u: string): this;
|
|
29
|
-
/** Set the JSON body to send. */
|
|
30
|
-
payload(obj: Record<string, unknown>): this;
|
|
31
|
-
/** Set a single request header. */
|
|
32
|
-
header(key: string, value: string): this;
|
|
33
|
-
/** Merge multiple request headers. */
|
|
34
|
-
headers(obj: Record<string, string>): this;
|
|
35
|
-
/** Set the HTTP method (default `POST`). */
|
|
36
|
-
method(m: WebhookMethod): this;
|
|
37
|
-
get urlValue(): string | undefined;
|
|
38
|
-
get methodValue(): WebhookMethod;
|
|
39
|
-
get headerValues(): Record<string, string>;
|
|
40
|
-
get payloadValue(): Record<string, unknown>;
|
|
41
|
-
/** Serialize to a {@link WebhookRequest}. */
|
|
42
|
-
toRequest(): WebhookRequest;
|
|
43
|
-
}
|
|
44
|
-
//# sourceMappingURL=webhook-message.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-message.d.ts","sourceRoot":"","sources":["../src/webhook-message.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAExE,qFAAqF;AACrF,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,QAAQ,CAA+B;IAE/C,uEAAuE;IACvE,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAKpB,iCAAiC;IACjC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK3C,mCAAmC;IACnC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKxC,sCAAsC;IACtC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAK1C,4CAA4C;IAC5C,MAAM,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI;IAK9B,IAAI,QAAQ,IAAI,MAAM,GAAG,SAAS,CAEjC;IAED,IAAI,WAAW,IAAI,aAAa,CAE/B;IAED,IAAI,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEzC;IAED,IAAI,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE1C;IAED,6CAA6C;IAC7C,SAAS,IAAI,cAAc;CAQ5B"}
|
package/dist/webhook-message.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WebhookMessage = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* A fluent builder for a webhook request.
|
|
6
|
-
*
|
|
7
|
-
* ```ts
|
|
8
|
-
* new WebhookMessage()
|
|
9
|
-
* .url('https://example.com/hooks/orders')
|
|
10
|
-
* .header('X-Signature', sign(payload))
|
|
11
|
-
* .payload({ event: 'order.paid', id: 42 });
|
|
12
|
-
* ```
|
|
13
|
-
*/
|
|
14
|
-
class WebhookMessage {
|
|
15
|
-
_url;
|
|
16
|
-
_method = 'POST';
|
|
17
|
-
_headers = {};
|
|
18
|
-
_payload = {};
|
|
19
|
-
/** Set the target URL (overrides the route and configured default). */
|
|
20
|
-
url(u) {
|
|
21
|
-
this._url = u;
|
|
22
|
-
return this;
|
|
23
|
-
}
|
|
24
|
-
/** Set the JSON body to send. */
|
|
25
|
-
payload(obj) {
|
|
26
|
-
this._payload = obj;
|
|
27
|
-
return this;
|
|
28
|
-
}
|
|
29
|
-
/** Set a single request header. */
|
|
30
|
-
header(key, value) {
|
|
31
|
-
this._headers[key] = value;
|
|
32
|
-
return this;
|
|
33
|
-
}
|
|
34
|
-
/** Merge multiple request headers. */
|
|
35
|
-
headers(obj) {
|
|
36
|
-
this._headers = { ...this._headers, ...obj };
|
|
37
|
-
return this;
|
|
38
|
-
}
|
|
39
|
-
/** Set the HTTP method (default `POST`). */
|
|
40
|
-
method(m) {
|
|
41
|
-
this._method = m;
|
|
42
|
-
return this;
|
|
43
|
-
}
|
|
44
|
-
get urlValue() {
|
|
45
|
-
return this._url;
|
|
46
|
-
}
|
|
47
|
-
get methodValue() {
|
|
48
|
-
return this._method;
|
|
49
|
-
}
|
|
50
|
-
get headerValues() {
|
|
51
|
-
return this._headers;
|
|
52
|
-
}
|
|
53
|
-
get payloadValue() {
|
|
54
|
-
return this._payload;
|
|
55
|
-
}
|
|
56
|
-
/** Serialize to a {@link WebhookRequest}. */
|
|
57
|
-
toRequest() {
|
|
58
|
-
return {
|
|
59
|
-
url: this._url,
|
|
60
|
-
method: this._method,
|
|
61
|
-
headers: { ...this._headers },
|
|
62
|
-
body: this._payload,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
exports.WebhookMessage = WebhookMessage;
|
|
67
|
-
//# sourceMappingURL=webhook-message.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-message.js","sourceRoot":"","sources":["../src/webhook-message.ts"],"names":[],"mappings":";;;AAaA;;;;;;;;;GASG;AACH,MAAa,cAAc;IACjB,IAAI,CAAU;IACd,OAAO,GAAkB,MAAM,CAAC;IAChC,QAAQ,GAA2B,EAAE,CAAC;IACtC,QAAQ,GAA4B,EAAE,CAAC;IAE/C,uEAAuE;IACvE,GAAG,CAAC,CAAS;QACX,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iCAAiC;IACjC,OAAO,CAAC,GAA4B;QAClC,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mCAAmC;IACnC,MAAM,CAAC,GAAW,EAAE,KAAa;QAC/B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sCAAsC;IACtC,OAAO,CAAC,GAA2B;QACjC,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,GAAG,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4CAA4C;IAC5C,MAAM,CAAC,CAAgB;QACrB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,6CAA6C;IAC7C,SAAS;QACP,OAAO;YACL,GAAG,EAAE,IAAI,CAAC,IAAI;YACd,MAAM,EAAE,IAAI,CAAC,OAAO;YACpB,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE;YAC7B,IAAI,EAAE,IAAI,CAAC,QAAQ;SACpB,CAAC;IACJ,CAAC;CACF;AA7DD,wCA6DC"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { type ChannelDriver, type DeliveryContext, type Notifiable, type Notification } from '@dudousxd/nestjs-notifications-core';
|
|
2
|
-
import { WebhookMessage } from './webhook-message';
|
|
3
|
-
/** Channel handle: use as `@Webhook()` on a payload method, or as a token in `via()`. */
|
|
4
|
-
export declare const Webhook: import("@dudousxd/nestjs-notifications-core").ChannelHandle;
|
|
5
|
-
/** Resolved runtime options for the webhook channel. */
|
|
6
|
-
export interface WebhookChannelOptions {
|
|
7
|
-
/** Default URL used when neither the message nor the route supplies one. */
|
|
8
|
-
url?: string;
|
|
9
|
-
/** Default headers merged into every request. */
|
|
10
|
-
headers?: Record<string, string>;
|
|
11
|
-
/**
|
|
12
|
-
* HMAC-SHA256 secret. When set, the request is signed: the header (default
|
|
13
|
-
* `X-Signature-256`) is set to `sha256=<hex digest of the raw JSON body>`, so the receiver
|
|
14
|
-
* can verify authenticity. Per-tenant secrets work via `resolveOptions`.
|
|
15
|
-
*/
|
|
16
|
-
secret?: string;
|
|
17
|
-
/** Header name for the signature. Default `X-Signature-256`. */
|
|
18
|
-
signatureHeader?: string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Implement this on a notification to define its webhook payload. Returning a plain
|
|
22
|
-
* object is treated as the JSON body to POST; return a {@link WebhookMessage} for full
|
|
23
|
-
* control over the url, method and headers.
|
|
24
|
-
*/
|
|
25
|
-
export interface WebhookNotification extends Notification {
|
|
26
|
-
toWebhook(notifiable: Notifiable): WebhookMessage | Record<string, unknown>;
|
|
27
|
-
}
|
|
28
|
-
/** Resolves per-tenant {@link WebhookChannelOptions} from a tenant id. */
|
|
29
|
-
export type WebhookOptionsResolver = (tenant: string) => WebhookChannelOptions;
|
|
30
|
-
/**
|
|
31
|
-
* Delivers a notification by sending an HTTP request (JSON body) to a webhook endpoint.
|
|
32
|
-
* The target URL comes from the {@link WebhookMessage}, else from
|
|
33
|
-
* `routeNotificationFor('webhook')` (a URL string), else from the configured default.
|
|
34
|
-
*/
|
|
35
|
-
export declare class WebhookChannel implements ChannelDriver {
|
|
36
|
-
private readonly options;
|
|
37
|
-
private readonly resolveOptions?;
|
|
38
|
-
readonly channel = "webhook";
|
|
39
|
-
constructor(options: WebhookChannelOptions, resolveOptions?: WebhookOptionsResolver | undefined);
|
|
40
|
-
send(notifiable: Notifiable, notification: Notification, context?: DeliveryContext): Promise<void>;
|
|
41
|
-
private post;
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=webhook.channel.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.channel.d.ts","sourceRoot":"","sources":["../src/webhook.channel.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,eAAe,EAEpB,KAAK,UAAU,EACf,KAAK,YAAY,EAIlB,MAAM,qCAAqC,CAAC;AAG7C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKnD,yFAAyF;AACzF,eAAO,MAAM,OAAO,6DAA2B,CAAC;AAEhD,wDAAwD;AACxD,MAAM,WAAW,qBAAqB;IACpC,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7E;AAED,0EAA0E;AAC1E,MAAM,MAAM,sBAAsB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,qBAAqB,CAAC;AAE/E;;;;GAIG;AACH,qBACa,cAAe,YAAW,aAAa;IAKhD,OAAO,CAAC,QAAQ,CAAC,OAAO;IAGxB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAPlC,QAAQ,CAAC,OAAO,aAAa;gBAIV,OAAO,EAAE,qBAAqB,EAG9B,cAAc,CAAC,EAAE,sBAAsB,YAAA;IAGpD,IAAI,CACR,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,YAAY,EAC1B,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,IAAI,CAAC;YA8BF,IAAI;CA6BnB"}
|
package/dist/webhook.channel.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
-
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.WebhookChannel = exports.Webhook = void 0;
|
|
16
|
-
const node_crypto_1 = require("node:crypto");
|
|
17
|
-
const nestjs_notifications_core_1 = require("@dudousxd/nestjs-notifications-core");
|
|
18
|
-
const common_1 = require("@nestjs/common");
|
|
19
|
-
const tokens_1 = require("./tokens");
|
|
20
|
-
const webhook_message_1 = require("./webhook-message");
|
|
21
|
-
const DEFAULT_SIGNATURE_HEADER = 'X-Signature-256';
|
|
22
|
-
/** Channel handle: use as `@Webhook()` on a payload method, or as a token in `via()`. */
|
|
23
|
-
exports.Webhook = (0, nestjs_notifications_core_1.createChannel)('webhook');
|
|
24
|
-
/**
|
|
25
|
-
* Delivers a notification by sending an HTTP request (JSON body) to a webhook endpoint.
|
|
26
|
-
* The target URL comes from the {@link WebhookMessage}, else from
|
|
27
|
-
* `routeNotificationFor('webhook')` (a URL string), else from the configured default.
|
|
28
|
-
*/
|
|
29
|
-
let WebhookChannel = class WebhookChannel {
|
|
30
|
-
options;
|
|
31
|
-
resolveOptions;
|
|
32
|
-
channel = 'webhook';
|
|
33
|
-
constructor(options, resolveOptions) {
|
|
34
|
-
this.options = options;
|
|
35
|
-
this.resolveOptions = resolveOptions;
|
|
36
|
-
}
|
|
37
|
-
async send(notifiable, notification, context) {
|
|
38
|
-
const options = context?.tenant && this.resolveOptions ? this.resolveOptions(context.tenant) : this.options;
|
|
39
|
-
const handler = (0, nestjs_notifications_core_1.getHandler)(notification, 'webhook', 'toWebhook');
|
|
40
|
-
if (!handler) {
|
|
41
|
-
const name = notification.constructor.notificationName ??
|
|
42
|
-
notification.constructor.name;
|
|
43
|
-
throw new nestjs_notifications_core_1.MissingChannelMethodError('webhook', 'toWebhook()', name);
|
|
44
|
-
}
|
|
45
|
-
const result = handler(notifiable);
|
|
46
|
-
const message = result instanceof webhook_message_1.WebhookMessage ? result : new webhook_message_1.WebhookMessage().payload(result);
|
|
47
|
-
const request = message.toRequest();
|
|
48
|
-
const route = (0, nestjs_notifications_core_1.routeFor)(notifiable, 'webhook', notification);
|
|
49
|
-
const url = request.url ?? (typeof route === 'string' ? route : undefined) ?? options.url;
|
|
50
|
-
if (!url) {
|
|
51
|
-
throw new Error('The webhook channel needs a target URL. Set one on the WebhookMessage via .url(), ' +
|
|
52
|
-
'return one from routeNotificationFor("webhook"), or set url in forRoot().');
|
|
53
|
-
}
|
|
54
|
-
await this.post(options, url, request.method, request.body, request.headers);
|
|
55
|
-
}
|
|
56
|
-
async post(options, url, method, body, messageHeaders) {
|
|
57
|
-
const payload = JSON.stringify(body);
|
|
58
|
-
const headers = {
|
|
59
|
-
...options.headers,
|
|
60
|
-
...messageHeaders,
|
|
61
|
-
'Content-Type': 'application/json',
|
|
62
|
-
};
|
|
63
|
-
if (options.secret) {
|
|
64
|
-
const signature = (0, node_crypto_1.createHmac)('sha256', options.secret).update(payload).digest('hex');
|
|
65
|
-
headers[options.signatureHeader ?? DEFAULT_SIGNATURE_HEADER] = `sha256=${signature}`;
|
|
66
|
-
}
|
|
67
|
-
const response = await fetch(url, {
|
|
68
|
-
method,
|
|
69
|
-
headers,
|
|
70
|
-
body: payload,
|
|
71
|
-
});
|
|
72
|
-
if (!response.ok) {
|
|
73
|
-
throw new Error(`Webhook request to ${url} failed with status ${response.status}.`);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
exports.WebhookChannel = WebhookChannel;
|
|
78
|
-
exports.WebhookChannel = WebhookChannel = __decorate([
|
|
79
|
-
(0, common_1.Injectable)(),
|
|
80
|
-
__param(0, (0, common_1.Inject)(tokens_1.WEBHOOK_OPTIONS)),
|
|
81
|
-
__param(1, (0, common_1.Optional)()),
|
|
82
|
-
__param(1, (0, common_1.Inject)(tokens_1.WEBHOOK_OPTIONS_RESOLVER)),
|
|
83
|
-
__metadata("design:paramtypes", [Object, Function])
|
|
84
|
-
], WebhookChannel);
|
|
85
|
-
//# sourceMappingURL=webhook.channel.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.channel.js","sourceRoot":"","sources":["../src/webhook.channel.ts"],"names":[],"mappings":"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"}
|
package/dist/webhook.module.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { type DynamicModule } from '@nestjs/common';
|
|
2
|
-
import { type WebhookOptionsResolver } from './webhook.channel';
|
|
3
|
-
export interface WebhookChannelModuleOptions {
|
|
4
|
-
/** Default URL used when neither the message nor the route supplies one. */
|
|
5
|
-
url?: string;
|
|
6
|
-
/** Default headers merged into every request. */
|
|
7
|
-
headers?: Record<string, string>;
|
|
8
|
-
/**
|
|
9
|
-
* Optional per-tenant options resolver. When a notification is delivered with a
|
|
10
|
-
* `context.tenant`, the returned options are used instead of the defaults.
|
|
11
|
-
*/
|
|
12
|
-
resolveOptions?: WebhookOptionsResolver;
|
|
13
|
-
/** Register globally so the channel is discoverable app-wide. Default true. */
|
|
14
|
-
global?: boolean;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Registers the webhook channel.
|
|
18
|
-
*
|
|
19
|
-
* ```ts
|
|
20
|
-
* WebhookChannelModule.forRoot({ url: 'https://example.com/hooks/notifications' });
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
export declare class WebhookChannelModule {
|
|
24
|
-
static forRoot(options?: WebhookChannelModuleOptions): DynamicModule;
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=webhook.module.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.module.d.ts","sourceRoot":"","sources":["../src/webhook.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAyB,MAAM,gBAAgB,CAAC;AAE3E,OAAO,EAGL,KAAK,sBAAsB,EAC5B,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,2BAA2B;IAC1C,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;OAGG;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,qBACa,oBAAoB;IAC/B,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,2BAAgC,GAAG,aAAa;CAmBzE"}
|
package/dist/webhook.module.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var WebhookChannelModule_1;
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.WebhookChannelModule = void 0;
|
|
11
|
-
const common_1 = require("@nestjs/common");
|
|
12
|
-
const tokens_1 = require("./tokens");
|
|
13
|
-
const webhook_channel_1 = require("./webhook.channel");
|
|
14
|
-
/**
|
|
15
|
-
* Registers the webhook channel.
|
|
16
|
-
*
|
|
17
|
-
* ```ts
|
|
18
|
-
* WebhookChannelModule.forRoot({ url: 'https://example.com/hooks/notifications' });
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
let WebhookChannelModule = WebhookChannelModule_1 = class WebhookChannelModule {
|
|
22
|
-
static forRoot(options = {}) {
|
|
23
|
-
const webhookOptions = {
|
|
24
|
-
url: options.url,
|
|
25
|
-
headers: options.headers,
|
|
26
|
-
};
|
|
27
|
-
const providers = [
|
|
28
|
-
{ provide: tokens_1.WEBHOOK_OPTIONS, useValue: webhookOptions },
|
|
29
|
-
{ provide: tokens_1.WEBHOOK_OPTIONS_RESOLVER, useValue: options.resolveOptions ?? null },
|
|
30
|
-
webhook_channel_1.WebhookChannel,
|
|
31
|
-
];
|
|
32
|
-
return {
|
|
33
|
-
module: WebhookChannelModule_1,
|
|
34
|
-
global: options.global ?? true,
|
|
35
|
-
providers,
|
|
36
|
-
exports: [webhook_channel_1.WebhookChannel],
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
exports.WebhookChannelModule = WebhookChannelModule;
|
|
41
|
-
exports.WebhookChannelModule = WebhookChannelModule = WebhookChannelModule_1 = __decorate([
|
|
42
|
-
(0, common_1.Module)({})
|
|
43
|
-
], WebhookChannelModule);
|
|
44
|
-
//# sourceMappingURL=webhook.module.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.module.js","sourceRoot":"","sources":["../src/webhook.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAA2E;AAC3E,qCAAqE;AACrE,uDAI2B;AAgB3B;;;;;;GAMG;AAEI,IAAM,oBAAoB,4BAA1B,MAAM,oBAAoB;IAC/B,MAAM,CAAC,OAAO,CAAC,UAAuC,EAAE;QACtD,MAAM,cAAc,GAA0B;YAC5C,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC;QAEF,MAAM,SAAS,GAAe;YAC5B,EAAE,OAAO,EAAE,wBAAe,EAAE,QAAQ,EAAE,cAAc,EAAE;YACtD,EAAE,OAAO,EAAE,iCAAwB,EAAE,QAAQ,EAAE,OAAO,CAAC,cAAc,IAAI,IAAI,EAAE;YAC/E,gCAAc;SACf,CAAC;QAEF,OAAO;YACL,MAAM,EAAE,sBAAoB;YAC5B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;YAC9B,SAAS;YACT,OAAO,EAAE,CAAC,gCAAc,CAAC;SAC1B,CAAC;IACJ,CAAC;CACF,CAAA;AApBY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,oBAAoB,CAoBhC"}
|