@dudousxd/nestjs-notifications-webhook 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +191 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -48
- package/dist/index.d.ts +112 -48
- package/dist/index.js +186 -57
- package/dist/index.js.map +1 -1
- package/package.json +18 -5
package/dist/index.d.cts
CHANGED
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
import * as _dudousxd_nestjs_notifications_core from '@dudousxd/nestjs-notifications-core';
|
|
2
2
|
import { BaseChannel, Notifiable, Notification, DeliveryContext, ChannelContext } from '@dudousxd/nestjs-notifications-core';
|
|
3
|
+
import { OutboxInbox } from '@nestjs/outbox';
|
|
4
|
+
import { HttpWebhookTransportOptions, WebhookTransport, Webhooks, WebhookMessage as WebhookMessage$1 } from '@nestjs/webhooks';
|
|
3
5
|
import { DynamicModule } from '@nestjs/common';
|
|
4
6
|
|
|
5
|
-
/** The HTTP method used for a webhook request. */
|
|
6
|
-
type WebhookMethod = 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'GET';
|
|
7
7
|
/** The resolved request description produced by {@link WebhookMessage.toRequest}. */
|
|
8
8
|
interface WebhookRequest {
|
|
9
9
|
/** Target URL, when set on the message; otherwise resolved by the channel. */
|
|
10
10
|
url?: string | undefined;
|
|
11
|
-
method: WebhookMethod;
|
|
12
11
|
headers: Record<string, string>;
|
|
13
|
-
/** The JSON body to
|
|
12
|
+
/** The JSON body to POST (a plain object). */
|
|
14
13
|
body: Record<string, unknown>;
|
|
15
14
|
}
|
|
16
15
|
/**
|
|
17
|
-
* A fluent builder for a webhook request.
|
|
16
|
+
* A fluent builder for a direct webhook request (always a `POST` with a JSON body).
|
|
18
17
|
*
|
|
19
18
|
* ```ts
|
|
20
19
|
* new WebhookMessage()
|
|
21
20
|
* .url('https://example.com/hooks/orders')
|
|
22
|
-
* .header('X-
|
|
21
|
+
* .header('X-App', 'shop')
|
|
23
22
|
* .payload({ event: 'order.paid', id: 42 });
|
|
24
23
|
* ```
|
|
25
24
|
*/
|
|
26
25
|
declare class WebhookMessage {
|
|
27
26
|
private _url?;
|
|
28
|
-
private _method;
|
|
29
27
|
private _headers;
|
|
30
28
|
private _payload;
|
|
31
29
|
/** Set the target URL (overrides the route and configured default). */
|
|
@@ -36,84 +34,139 @@ declare class WebhookMessage {
|
|
|
36
34
|
header(key: string, value: string): this;
|
|
37
35
|
/** Merge multiple request headers. */
|
|
38
36
|
headers(obj: Record<string, string>): this;
|
|
39
|
-
/** Set the HTTP method (default `POST`). */
|
|
40
|
-
method(m: WebhookMethod): this;
|
|
41
37
|
get urlValue(): string | undefined;
|
|
42
|
-
get methodValue(): WebhookMethod;
|
|
43
38
|
get headerValues(): Record<string, string>;
|
|
44
39
|
get payloadValue(): Record<string, unknown>;
|
|
45
40
|
/** Serialize to a {@link WebhookRequest}. */
|
|
46
41
|
toRequest(): WebhookRequest;
|
|
47
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* A webhook event for subscription delivery: `@nestjs/webhooks` fans it out to every endpoint of
|
|
45
|
+
* the tenant subscribed to `type`, signs it (Standard Webhooks), retries and logs each delivery.
|
|
46
|
+
* Return one from `toWebhook(ctx)` instead of a {@link WebhookMessage}.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* toWebhook() { return new WebhookEvent('invoice.paid', { id: this.invoice.id }); }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
declare class WebhookEvent<D = unknown> {
|
|
53
|
+
/** Full-stop delimited: `invoice.paid`. In `WebhooksModule`'s `eventTypes` when that is set. */
|
|
54
|
+
readonly type: string;
|
|
55
|
+
/** JSON-serializable data, sent as the body's `data`. */
|
|
56
|
+
readonly data: D;
|
|
57
|
+
constructor(
|
|
58
|
+
/** Full-stop delimited: `invoice.paid`. In `WebhooksModule`'s `eventTypes` when that is set. */
|
|
59
|
+
type: string,
|
|
60
|
+
/** JSON-serializable data, sent as the body's `data`. */
|
|
61
|
+
data: D);
|
|
62
|
+
}
|
|
48
63
|
|
|
64
|
+
/**
|
|
65
|
+
* The outbox inbox consumer under which subscription deliveries record the `webhook-id`s they
|
|
66
|
+
* dispatched, so a retried notification doesn't dispatch the same event twice.
|
|
67
|
+
*/
|
|
68
|
+
declare const WEBHOOK_DISPATCH_CONSUMER = "aviary.notifications.webhook-dispatch";
|
|
49
69
|
/** Channel handle: use as `@Webhook()` on a payload method, or as a token in `via()`. */
|
|
50
70
|
declare const Webhook: _dudousxd_nestjs_notifications_core.ChannelHandle;
|
|
51
|
-
/**
|
|
71
|
+
/** Opens a transaction for subscription delivery, e.g. `(work) => db.transaction(work)`. */
|
|
72
|
+
type WebhookTransactionRunner = <T>(work: (tx: unknown) => Promise<T>) => Promise<T>;
|
|
73
|
+
/** Resolved runtime options for direct-URL delivery. */
|
|
52
74
|
interface WebhookChannelOptions {
|
|
53
75
|
/** Default URL used when neither the message nor the route supplies one. */
|
|
54
76
|
url?: string;
|
|
55
77
|
/** Default headers merged into every request. */
|
|
56
78
|
headers?: Record<string, string>;
|
|
79
|
+
/** Signing secret. Unset: requests are not signed. */
|
|
80
|
+
secret?: string;
|
|
57
81
|
/**
|
|
58
|
-
*
|
|
59
|
-
* `
|
|
60
|
-
*
|
|
82
|
+
* `'sha256'` (default): `<signatureHeader>: sha256=<hex HMAC of the body>`.
|
|
83
|
+
* `'standard'`: Standard Webhooks headers, verifiable with `@nestjs/webhooks`'
|
|
84
|
+
* `@VerifyWebhook()` and a `scheme: 'standard'` receiver; `secret` is then `whsec_…`.
|
|
61
85
|
*/
|
|
62
|
-
|
|
63
|
-
/** Header
|
|
86
|
+
signing?: 'sha256' | 'standard';
|
|
87
|
+
/** Header for the `'sha256'` signature. Default `X-Signature-256`. */
|
|
64
88
|
signatureHeader?: string;
|
|
65
|
-
/** Abort a request that takes longer than this (ms).
|
|
89
|
+
/** Abort a request that takes longer than this (ms). Default 15000. */
|
|
66
90
|
timeoutMs?: number;
|
|
67
91
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
92
|
+
* The SSRF policy of `@nestjs/webhooks`' `HttpWebhookTransport` (https only, every resolved
|
|
93
|
+
* address checked and pinned, redirects never followed): `allowHttp`, `allowPrivateNetworks`,
|
|
94
|
+
* `allowedAddresses`, `ca`, `maxResponseSize`, `lookup`.
|
|
95
|
+
*/
|
|
96
|
+
delivery?: HttpWebhookTransportOptions;
|
|
97
|
+
/**
|
|
98
|
+
* A `@nestjs/webhooks` transport instance to send through instead (a proxy, mTLS, or
|
|
99
|
+
* `InMemoryWebhookTransport` in tests). Takes precedence over `delivery`.
|
|
70
100
|
*/
|
|
71
|
-
|
|
101
|
+
transport?: WebhookTransport;
|
|
72
102
|
}
|
|
73
103
|
/**
|
|
74
|
-
* Implement this on a notification to define its webhook payload
|
|
75
|
-
*
|
|
76
|
-
*
|
|
104
|
+
* Implement this on a notification to define its webhook payload: a plain object (the JSON body),
|
|
105
|
+
* a {@link WebhookMessage} (url, headers, body), or a {@link WebhookEvent} for subscription
|
|
106
|
+
* delivery through `@nestjs/webhooks`.
|
|
77
107
|
*/
|
|
78
108
|
interface WebhookNotification extends Notification {
|
|
79
|
-
toWebhook(ctx: ChannelContext): WebhookMessage | Record<string, unknown>;
|
|
109
|
+
toWebhook(ctx: ChannelContext): WebhookMessage | WebhookEvent | Record<string, unknown>;
|
|
80
110
|
}
|
|
81
111
|
/** Resolves per-tenant {@link WebhookChannelOptions} from a tenant id. */
|
|
82
112
|
type WebhookOptionsResolver = (tenant: string) => WebhookChannelOptions;
|
|
113
|
+
/** What a direct delivery returns (the channel response). */
|
|
114
|
+
interface WebhookDeliveryResult {
|
|
115
|
+
/** The `webhook-id` (Standard signing) or a generated id. */
|
|
116
|
+
id: string;
|
|
117
|
+
statusCode: number;
|
|
118
|
+
}
|
|
83
119
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
|
|
120
|
+
* What a subscription delivery returns when its `webhook-id` was already dispatched by an earlier
|
|
121
|
+
* attempt of the same delivery: nothing was dispatched this time.
|
|
122
|
+
*/
|
|
123
|
+
interface WebhookAlreadyDispatched {
|
|
124
|
+
id: string;
|
|
125
|
+
duplicate: true;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Delivers a notification as a webhook, two ways:
|
|
129
|
+
*
|
|
130
|
+
* - **Direct**: a signed `POST` of the JSON body to one URL (the message's, else the
|
|
131
|
+
* `routeNotificationFor('webhook')` URL, else the configured default), through `@nestjs/webhooks`'
|
|
132
|
+
* SSRF-guarded `HttpWebhookTransport`. A non-2xx answer fails the delivery.
|
|
133
|
+
* - **Subscriptions**: a {@link WebhookEvent} is handed to `Webhooks.dispatch()`, which fans it
|
|
134
|
+
* out to the endpoints subscribed to its type (for the delivery's tenant), with retries and a
|
|
135
|
+
* delivery log. Needs `WebhooksModule` + `OutboxModule` and the `transaction` option. A
|
|
136
|
+
* delivery with an `idempotencyKey` (the outbox dispatcher's message id) records its
|
|
137
|
+
* `webhook-id` in the outbox inbox in the same transaction as the dispatch, so a retry of it
|
|
138
|
+
* dispatches nothing and returns {@link WebhookAlreadyDispatched}.
|
|
87
139
|
*/
|
|
88
140
|
declare class WebhookChannel extends BaseChannel {
|
|
89
141
|
private readonly options;
|
|
90
142
|
private readonly resolveOptions?;
|
|
143
|
+
private readonly transaction?;
|
|
144
|
+
private readonly webhooks?;
|
|
145
|
+
private readonly inbox?;
|
|
91
146
|
readonly channel = "webhook";
|
|
92
|
-
constructor(options: WebhookChannelOptions, resolveOptions?: WebhookOptionsResolver | undefined);
|
|
93
|
-
send(notifiable: Notifiable, notification: Notification, context?: DeliveryContext): Promise<void>;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
interface WebhookChannelModuleOptions {
|
|
97
|
-
/** Default URL used when neither the message nor the route supplies one. */
|
|
98
|
-
url?: string;
|
|
99
|
-
/** Default headers merged into every request. */
|
|
100
|
-
headers?: Record<string, string>;
|
|
101
147
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
148
|
+
* `HttpWebhookTransport`s by tenant (`''` without one), rebuilt only when that tenant's
|
|
149
|
+
* `delivery` options change. Keyed by tenant rather than by the resolved options object, since a
|
|
150
|
+
* resolver may return a fresh object on every call.
|
|
104
151
|
*/
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
152
|
+
private readonly transports;
|
|
153
|
+
constructor(options: WebhookChannelOptions, resolveOptions?: WebhookOptionsResolver | undefined, transaction?: WebhookTransactionRunner | undefined, webhooks?: Webhooks | undefined, inbox?: OutboxInbox | undefined);
|
|
154
|
+
send(notifiable: Notifiable, notification: Notification, context?: DeliveryContext): Promise<WebhookDeliveryResult | WebhookMessage$1 | WebhookAlreadyDispatched>;
|
|
155
|
+
private dispatch;
|
|
156
|
+
private transportFor;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
interface WebhookChannelModuleOptions extends WebhookChannelOptions {
|
|
112
160
|
/**
|
|
113
161
|
* Optional per-tenant options resolver. When a notification is delivered with a
|
|
114
162
|
* `context.tenant`, the returned options are used instead of the defaults.
|
|
115
163
|
*/
|
|
116
164
|
resolveOptions?: WebhookOptionsResolver;
|
|
165
|
+
/**
|
|
166
|
+
* Opens a transaction for subscription delivery ({@link WebhookEvent}): `@nestjs/webhooks`
|
|
167
|
+
* writes the event to the outbox through it, e.g. `(work) => db.transaction(work)`.
|
|
168
|
+
*/
|
|
169
|
+
transaction?: WebhookTransactionRunner;
|
|
117
170
|
/** Register globally so the channel is discoverable app-wide. Default true. */
|
|
118
171
|
global?: boolean;
|
|
119
172
|
}
|
|
@@ -121,16 +174,27 @@ interface WebhookChannelModuleOptions {
|
|
|
121
174
|
* Registers the webhook channel.
|
|
122
175
|
*
|
|
123
176
|
* ```ts
|
|
124
|
-
* WebhookChannelModule.forRoot({ url: 'https://example.com/hooks
|
|
177
|
+
* WebhookChannelModule.forRoot({ url: 'https://example.com/hooks', secret: 'whsec_…', signing: 'standard' });
|
|
125
178
|
* ```
|
|
126
179
|
*/
|
|
127
180
|
declare class WebhookChannelModule {
|
|
128
181
|
static forRoot(options?: WebhookChannelModuleOptions): DynamicModule;
|
|
129
182
|
}
|
|
130
183
|
|
|
131
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Standard Webhooks headers (`webhook-id`, `webhook-timestamp`, `webhook-signature: v1,…`) for
|
|
186
|
+
* `body`, verifiable by a `@nestjs/webhooks` receiver with `scheme: 'standard'` (`@VerifyWebhook`).
|
|
187
|
+
* `secret` is `whsec_` + base64 (the prefix is optional).
|
|
188
|
+
*/
|
|
189
|
+
declare function standardWebhookHeaders(secret: string, id: string, body: string, now?: Date): Record<string, string>;
|
|
190
|
+
/** The legacy `sha256=<hex HMAC of the body>` signature (GitHub-style). */
|
|
191
|
+
declare function sha256Signature(secret: string, body: string): string;
|
|
192
|
+
|
|
193
|
+
/** DI token for the resolved {@link WebhookChannelOptions}. */
|
|
132
194
|
declare const WEBHOOK_OPTIONS: unique symbol;
|
|
133
195
|
/** DI token for the optional per-tenant {@link WebhookChannelOptions} resolver. */
|
|
134
196
|
declare const WEBHOOK_OPTIONS_RESOLVER: unique symbol;
|
|
197
|
+
/** DI token for the transaction opener used by subscription delivery. */
|
|
198
|
+
declare const WEBHOOK_TRANSACTION: unique symbol;
|
|
135
199
|
|
|
136
|
-
export { WEBHOOK_OPTIONS, WEBHOOK_OPTIONS_RESOLVER, Webhook, WebhookChannel, WebhookChannelModule, type WebhookChannelModuleOptions, type WebhookChannelOptions,
|
|
200
|
+
export { WEBHOOK_DISPATCH_CONSUMER, WEBHOOK_OPTIONS, WEBHOOK_OPTIONS_RESOLVER, WEBHOOK_TRANSACTION, Webhook, type WebhookAlreadyDispatched, WebhookChannel, WebhookChannelModule, type WebhookChannelModuleOptions, type WebhookChannelOptions, type WebhookDeliveryResult, WebhookEvent, WebhookMessage, type WebhookNotification, type WebhookOptionsResolver, type WebhookRequest, type WebhookTransactionRunner, sha256Signature, standardWebhookHeaders };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
import * as _dudousxd_nestjs_notifications_core from '@dudousxd/nestjs-notifications-core';
|
|
2
2
|
import { BaseChannel, Notifiable, Notification, DeliveryContext, ChannelContext } from '@dudousxd/nestjs-notifications-core';
|
|
3
|
+
import { OutboxInbox } from '@nestjs/outbox';
|
|
4
|
+
import { HttpWebhookTransportOptions, WebhookTransport, Webhooks, WebhookMessage as WebhookMessage$1 } from '@nestjs/webhooks';
|
|
3
5
|
import { DynamicModule } from '@nestjs/common';
|
|
4
6
|
|
|
5
|
-
/** The HTTP method used for a webhook request. */
|
|
6
|
-
type WebhookMethod = 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'GET';
|
|
7
7
|
/** The resolved request description produced by {@link WebhookMessage.toRequest}. */
|
|
8
8
|
interface WebhookRequest {
|
|
9
9
|
/** Target URL, when set on the message; otherwise resolved by the channel. */
|
|
10
10
|
url?: string | undefined;
|
|
11
|
-
method: WebhookMethod;
|
|
12
11
|
headers: Record<string, string>;
|
|
13
|
-
/** The JSON body to
|
|
12
|
+
/** The JSON body to POST (a plain object). */
|
|
14
13
|
body: Record<string, unknown>;
|
|
15
14
|
}
|
|
16
15
|
/**
|
|
17
|
-
* A fluent builder for a webhook request.
|
|
16
|
+
* A fluent builder for a direct webhook request (always a `POST` with a JSON body).
|
|
18
17
|
*
|
|
19
18
|
* ```ts
|
|
20
19
|
* new WebhookMessage()
|
|
21
20
|
* .url('https://example.com/hooks/orders')
|
|
22
|
-
* .header('X-
|
|
21
|
+
* .header('X-App', 'shop')
|
|
23
22
|
* .payload({ event: 'order.paid', id: 42 });
|
|
24
23
|
* ```
|
|
25
24
|
*/
|
|
26
25
|
declare class WebhookMessage {
|
|
27
26
|
private _url?;
|
|
28
|
-
private _method;
|
|
29
27
|
private _headers;
|
|
30
28
|
private _payload;
|
|
31
29
|
/** Set the target URL (overrides the route and configured default). */
|
|
@@ -36,84 +34,139 @@ declare class WebhookMessage {
|
|
|
36
34
|
header(key: string, value: string): this;
|
|
37
35
|
/** Merge multiple request headers. */
|
|
38
36
|
headers(obj: Record<string, string>): this;
|
|
39
|
-
/** Set the HTTP method (default `POST`). */
|
|
40
|
-
method(m: WebhookMethod): this;
|
|
41
37
|
get urlValue(): string | undefined;
|
|
42
|
-
get methodValue(): WebhookMethod;
|
|
43
38
|
get headerValues(): Record<string, string>;
|
|
44
39
|
get payloadValue(): Record<string, unknown>;
|
|
45
40
|
/** Serialize to a {@link WebhookRequest}. */
|
|
46
41
|
toRequest(): WebhookRequest;
|
|
47
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* A webhook event for subscription delivery: `@nestjs/webhooks` fans it out to every endpoint of
|
|
45
|
+
* the tenant subscribed to `type`, signs it (Standard Webhooks), retries and logs each delivery.
|
|
46
|
+
* Return one from `toWebhook(ctx)` instead of a {@link WebhookMessage}.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* toWebhook() { return new WebhookEvent('invoice.paid', { id: this.invoice.id }); }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
declare class WebhookEvent<D = unknown> {
|
|
53
|
+
/** Full-stop delimited: `invoice.paid`. In `WebhooksModule`'s `eventTypes` when that is set. */
|
|
54
|
+
readonly type: string;
|
|
55
|
+
/** JSON-serializable data, sent as the body's `data`. */
|
|
56
|
+
readonly data: D;
|
|
57
|
+
constructor(
|
|
58
|
+
/** Full-stop delimited: `invoice.paid`. In `WebhooksModule`'s `eventTypes` when that is set. */
|
|
59
|
+
type: string,
|
|
60
|
+
/** JSON-serializable data, sent as the body's `data`. */
|
|
61
|
+
data: D);
|
|
62
|
+
}
|
|
48
63
|
|
|
64
|
+
/**
|
|
65
|
+
* The outbox inbox consumer under which subscription deliveries record the `webhook-id`s they
|
|
66
|
+
* dispatched, so a retried notification doesn't dispatch the same event twice.
|
|
67
|
+
*/
|
|
68
|
+
declare const WEBHOOK_DISPATCH_CONSUMER = "aviary.notifications.webhook-dispatch";
|
|
49
69
|
/** Channel handle: use as `@Webhook()` on a payload method, or as a token in `via()`. */
|
|
50
70
|
declare const Webhook: _dudousxd_nestjs_notifications_core.ChannelHandle;
|
|
51
|
-
/**
|
|
71
|
+
/** Opens a transaction for subscription delivery, e.g. `(work) => db.transaction(work)`. */
|
|
72
|
+
type WebhookTransactionRunner = <T>(work: (tx: unknown) => Promise<T>) => Promise<T>;
|
|
73
|
+
/** Resolved runtime options for direct-URL delivery. */
|
|
52
74
|
interface WebhookChannelOptions {
|
|
53
75
|
/** Default URL used when neither the message nor the route supplies one. */
|
|
54
76
|
url?: string;
|
|
55
77
|
/** Default headers merged into every request. */
|
|
56
78
|
headers?: Record<string, string>;
|
|
79
|
+
/** Signing secret. Unset: requests are not signed. */
|
|
80
|
+
secret?: string;
|
|
57
81
|
/**
|
|
58
|
-
*
|
|
59
|
-
* `
|
|
60
|
-
*
|
|
82
|
+
* `'sha256'` (default): `<signatureHeader>: sha256=<hex HMAC of the body>`.
|
|
83
|
+
* `'standard'`: Standard Webhooks headers, verifiable with `@nestjs/webhooks`'
|
|
84
|
+
* `@VerifyWebhook()` and a `scheme: 'standard'` receiver; `secret` is then `whsec_…`.
|
|
61
85
|
*/
|
|
62
|
-
|
|
63
|
-
/** Header
|
|
86
|
+
signing?: 'sha256' | 'standard';
|
|
87
|
+
/** Header for the `'sha256'` signature. Default `X-Signature-256`. */
|
|
64
88
|
signatureHeader?: string;
|
|
65
|
-
/** Abort a request that takes longer than this (ms).
|
|
89
|
+
/** Abort a request that takes longer than this (ms). Default 15000. */
|
|
66
90
|
timeoutMs?: number;
|
|
67
91
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
92
|
+
* The SSRF policy of `@nestjs/webhooks`' `HttpWebhookTransport` (https only, every resolved
|
|
93
|
+
* address checked and pinned, redirects never followed): `allowHttp`, `allowPrivateNetworks`,
|
|
94
|
+
* `allowedAddresses`, `ca`, `maxResponseSize`, `lookup`.
|
|
95
|
+
*/
|
|
96
|
+
delivery?: HttpWebhookTransportOptions;
|
|
97
|
+
/**
|
|
98
|
+
* A `@nestjs/webhooks` transport instance to send through instead (a proxy, mTLS, or
|
|
99
|
+
* `InMemoryWebhookTransport` in tests). Takes precedence over `delivery`.
|
|
70
100
|
*/
|
|
71
|
-
|
|
101
|
+
transport?: WebhookTransport;
|
|
72
102
|
}
|
|
73
103
|
/**
|
|
74
|
-
* Implement this on a notification to define its webhook payload
|
|
75
|
-
*
|
|
76
|
-
*
|
|
104
|
+
* Implement this on a notification to define its webhook payload: a plain object (the JSON body),
|
|
105
|
+
* a {@link WebhookMessage} (url, headers, body), or a {@link WebhookEvent} for subscription
|
|
106
|
+
* delivery through `@nestjs/webhooks`.
|
|
77
107
|
*/
|
|
78
108
|
interface WebhookNotification extends Notification {
|
|
79
|
-
toWebhook(ctx: ChannelContext): WebhookMessage | Record<string, unknown>;
|
|
109
|
+
toWebhook(ctx: ChannelContext): WebhookMessage | WebhookEvent | Record<string, unknown>;
|
|
80
110
|
}
|
|
81
111
|
/** Resolves per-tenant {@link WebhookChannelOptions} from a tenant id. */
|
|
82
112
|
type WebhookOptionsResolver = (tenant: string) => WebhookChannelOptions;
|
|
113
|
+
/** What a direct delivery returns (the channel response). */
|
|
114
|
+
interface WebhookDeliveryResult {
|
|
115
|
+
/** The `webhook-id` (Standard signing) or a generated id. */
|
|
116
|
+
id: string;
|
|
117
|
+
statusCode: number;
|
|
118
|
+
}
|
|
83
119
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
|
|
120
|
+
* What a subscription delivery returns when its `webhook-id` was already dispatched by an earlier
|
|
121
|
+
* attempt of the same delivery: nothing was dispatched this time.
|
|
122
|
+
*/
|
|
123
|
+
interface WebhookAlreadyDispatched {
|
|
124
|
+
id: string;
|
|
125
|
+
duplicate: true;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Delivers a notification as a webhook, two ways:
|
|
129
|
+
*
|
|
130
|
+
* - **Direct**: a signed `POST` of the JSON body to one URL (the message's, else the
|
|
131
|
+
* `routeNotificationFor('webhook')` URL, else the configured default), through `@nestjs/webhooks`'
|
|
132
|
+
* SSRF-guarded `HttpWebhookTransport`. A non-2xx answer fails the delivery.
|
|
133
|
+
* - **Subscriptions**: a {@link WebhookEvent} is handed to `Webhooks.dispatch()`, which fans it
|
|
134
|
+
* out to the endpoints subscribed to its type (for the delivery's tenant), with retries and a
|
|
135
|
+
* delivery log. Needs `WebhooksModule` + `OutboxModule` and the `transaction` option. A
|
|
136
|
+
* delivery with an `idempotencyKey` (the outbox dispatcher's message id) records its
|
|
137
|
+
* `webhook-id` in the outbox inbox in the same transaction as the dispatch, so a retry of it
|
|
138
|
+
* dispatches nothing and returns {@link WebhookAlreadyDispatched}.
|
|
87
139
|
*/
|
|
88
140
|
declare class WebhookChannel extends BaseChannel {
|
|
89
141
|
private readonly options;
|
|
90
142
|
private readonly resolveOptions?;
|
|
143
|
+
private readonly transaction?;
|
|
144
|
+
private readonly webhooks?;
|
|
145
|
+
private readonly inbox?;
|
|
91
146
|
readonly channel = "webhook";
|
|
92
|
-
constructor(options: WebhookChannelOptions, resolveOptions?: WebhookOptionsResolver | undefined);
|
|
93
|
-
send(notifiable: Notifiable, notification: Notification, context?: DeliveryContext): Promise<void>;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
interface WebhookChannelModuleOptions {
|
|
97
|
-
/** Default URL used when neither the message nor the route supplies one. */
|
|
98
|
-
url?: string;
|
|
99
|
-
/** Default headers merged into every request. */
|
|
100
|
-
headers?: Record<string, string>;
|
|
101
147
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
148
|
+
* `HttpWebhookTransport`s by tenant (`''` without one), rebuilt only when that tenant's
|
|
149
|
+
* `delivery` options change. Keyed by tenant rather than by the resolved options object, since a
|
|
150
|
+
* resolver may return a fresh object on every call.
|
|
104
151
|
*/
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
152
|
+
private readonly transports;
|
|
153
|
+
constructor(options: WebhookChannelOptions, resolveOptions?: WebhookOptionsResolver | undefined, transaction?: WebhookTransactionRunner | undefined, webhooks?: Webhooks | undefined, inbox?: OutboxInbox | undefined);
|
|
154
|
+
send(notifiable: Notifiable, notification: Notification, context?: DeliveryContext): Promise<WebhookDeliveryResult | WebhookMessage$1 | WebhookAlreadyDispatched>;
|
|
155
|
+
private dispatch;
|
|
156
|
+
private transportFor;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
interface WebhookChannelModuleOptions extends WebhookChannelOptions {
|
|
112
160
|
/**
|
|
113
161
|
* Optional per-tenant options resolver. When a notification is delivered with a
|
|
114
162
|
* `context.tenant`, the returned options are used instead of the defaults.
|
|
115
163
|
*/
|
|
116
164
|
resolveOptions?: WebhookOptionsResolver;
|
|
165
|
+
/**
|
|
166
|
+
* Opens a transaction for subscription delivery ({@link WebhookEvent}): `@nestjs/webhooks`
|
|
167
|
+
* writes the event to the outbox through it, e.g. `(work) => db.transaction(work)`.
|
|
168
|
+
*/
|
|
169
|
+
transaction?: WebhookTransactionRunner;
|
|
117
170
|
/** Register globally so the channel is discoverable app-wide. Default true. */
|
|
118
171
|
global?: boolean;
|
|
119
172
|
}
|
|
@@ -121,16 +174,27 @@ interface WebhookChannelModuleOptions {
|
|
|
121
174
|
* Registers the webhook channel.
|
|
122
175
|
*
|
|
123
176
|
* ```ts
|
|
124
|
-
* WebhookChannelModule.forRoot({ url: 'https://example.com/hooks
|
|
177
|
+
* WebhookChannelModule.forRoot({ url: 'https://example.com/hooks', secret: 'whsec_…', signing: 'standard' });
|
|
125
178
|
* ```
|
|
126
179
|
*/
|
|
127
180
|
declare class WebhookChannelModule {
|
|
128
181
|
static forRoot(options?: WebhookChannelModuleOptions): DynamicModule;
|
|
129
182
|
}
|
|
130
183
|
|
|
131
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Standard Webhooks headers (`webhook-id`, `webhook-timestamp`, `webhook-signature: v1,…`) for
|
|
186
|
+
* `body`, verifiable by a `@nestjs/webhooks` receiver with `scheme: 'standard'` (`@VerifyWebhook`).
|
|
187
|
+
* `secret` is `whsec_` + base64 (the prefix is optional).
|
|
188
|
+
*/
|
|
189
|
+
declare function standardWebhookHeaders(secret: string, id: string, body: string, now?: Date): Record<string, string>;
|
|
190
|
+
/** The legacy `sha256=<hex HMAC of the body>` signature (GitHub-style). */
|
|
191
|
+
declare function sha256Signature(secret: string, body: string): string;
|
|
192
|
+
|
|
193
|
+
/** DI token for the resolved {@link WebhookChannelOptions}. */
|
|
132
194
|
declare const WEBHOOK_OPTIONS: unique symbol;
|
|
133
195
|
/** DI token for the optional per-tenant {@link WebhookChannelOptions} resolver. */
|
|
134
196
|
declare const WEBHOOK_OPTIONS_RESOLVER: unique symbol;
|
|
197
|
+
/** DI token for the transaction opener used by subscription delivery. */
|
|
198
|
+
declare const WEBHOOK_TRANSACTION: unique symbol;
|
|
135
199
|
|
|
136
|
-
export { WEBHOOK_OPTIONS, WEBHOOK_OPTIONS_RESOLVER, Webhook, WebhookChannel, WebhookChannelModule, type WebhookChannelModuleOptions, type WebhookChannelOptions,
|
|
200
|
+
export { WEBHOOK_DISPATCH_CONSUMER, WEBHOOK_OPTIONS, WEBHOOK_OPTIONS_RESOLVER, WEBHOOK_TRANSACTION, Webhook, type WebhookAlreadyDispatched, WebhookChannel, WebhookChannelModule, type WebhookChannelModuleOptions, type WebhookChannelOptions, type WebhookDeliveryResult, WebhookEvent, WebhookMessage, type WebhookNotification, type WebhookOptionsResolver, type WebhookRequest, type WebhookTransactionRunner, sha256Signature, standardWebhookHeaders };
|