@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.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 send (a plain object). */
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-Signature', sign(payload))
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
- /** Resolved runtime options for the webhook channel. */
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
- * 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`.
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
- secret?: string;
63
- /** Header name for the signature. Default `X-Signature-256`. */
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). Unset = no timeout. */
89
+ /** Abort a request that takes longer than this (ms). Default 15000. */
66
90
  timeoutMs?: number;
67
91
  /**
68
- * Redirect handling. Default `'follow'` (the `fetch` default). Use `'error'` when webhook URLs
69
- * are user-supplied, so a 3xx can't bounce the (possibly signed) request somewhere else.
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
- redirect?: 'follow' | 'error' | 'manual';
101
+ transport?: WebhookTransport;
72
102
  }
73
103
  /**
74
- * Implement this on a notification to define its webhook payload. Returning a plain
75
- * object is treated as the JSON body to POST; return a {@link WebhookMessage} for full
76
- * control over the url, method and headers.
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
- * Delivers a notification by sending an HTTP request (JSON body) to a webhook endpoint.
85
- * The target URL comes from the {@link WebhookMessage}, else from
86
- * `routeNotificationFor('webhook')` (a URL string), else from the configured default.
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
- * HMAC-SHA256 signing secret. When set, every request carries
103
- * `<signatureHeader>: sha256=<hex digest of the JSON body>`.
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
- secret?: string;
106
- /** Header name for the signature. Default `X-Signature-256`. */
107
- signatureHeader?: string;
108
- /** Abort a request that takes longer than this (ms). Unset = no timeout. */
109
- timeoutMs?: number;
110
- /** Redirect handling: `'follow'` (default), `'error'` or `'manual'`. */
111
- redirect?: 'follow' | 'error' | 'manual';
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/notifications' });
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
- /** DI token for the resolved {@link WebhookChannelOptions} (default url, headers). */
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, WebhookMessage, type WebhookMethod, type WebhookNotification, type WebhookOptionsResolver, type WebhookRequest };
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 send (a plain object). */
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-Signature', sign(payload))
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
- /** Resolved runtime options for the webhook channel. */
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
- * 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`.
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
- secret?: string;
63
- /** Header name for the signature. Default `X-Signature-256`. */
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). Unset = no timeout. */
89
+ /** Abort a request that takes longer than this (ms). Default 15000. */
66
90
  timeoutMs?: number;
67
91
  /**
68
- * Redirect handling. Default `'follow'` (the `fetch` default). Use `'error'` when webhook URLs
69
- * are user-supplied, so a 3xx can't bounce the (possibly signed) request somewhere else.
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
- redirect?: 'follow' | 'error' | 'manual';
101
+ transport?: WebhookTransport;
72
102
  }
73
103
  /**
74
- * Implement this on a notification to define its webhook payload. Returning a plain
75
- * object is treated as the JSON body to POST; return a {@link WebhookMessage} for full
76
- * control over the url, method and headers.
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
- * Delivers a notification by sending an HTTP request (JSON body) to a webhook endpoint.
85
- * The target URL comes from the {@link WebhookMessage}, else from
86
- * `routeNotificationFor('webhook')` (a URL string), else from the configured default.
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
- * HMAC-SHA256 signing secret. When set, every request carries
103
- * `<signatureHeader>: sha256=<hex digest of the JSON body>`.
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
- secret?: string;
106
- /** Header name for the signature. Default `X-Signature-256`. */
107
- signatureHeader?: string;
108
- /** Abort a request that takes longer than this (ms). Unset = no timeout. */
109
- timeoutMs?: number;
110
- /** Redirect handling: `'follow'` (default), `'error'` or `'manual'`. */
111
- redirect?: 'follow' | 'error' | 'manual';
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/notifications' });
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
- /** DI token for the resolved {@link WebhookChannelOptions} (default url, headers). */
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, WebhookMessage, type WebhookMethod, type WebhookNotification, type WebhookOptionsResolver, type WebhookRequest };
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 };