@nestarc/webhook 0.9.0 → 0.11.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/README.md +117 -31
- package/dist/adapters/fetch-http-client.d.ts.map +1 -1
- package/dist/adapters/fetch-http-client.js +1 -1
- package/dist/adapters/fetch-http-client.js.map +1 -1
- package/dist/adapters/plaintext-secret-vault.d.ts.map +1 -1
- package/dist/adapters/plaintext-secret-vault.js +12 -2
- package/dist/adapters/plaintext-secret-vault.js.map +1 -1
- package/dist/adapters/prisma-delivery.repository.d.ts +8 -4
- package/dist/adapters/prisma-delivery.repository.d.ts.map +1 -1
- package/dist/adapters/prisma-delivery.repository.js +135 -77
- package/dist/adapters/prisma-delivery.repository.js.map +1 -1
- package/dist/adapters/prisma-endpoint.repository.d.ts +4 -3
- package/dist/adapters/prisma-endpoint.repository.d.ts.map +1 -1
- package/dist/adapters/prisma-endpoint.repository.js +39 -6
- package/dist/adapters/prisma-endpoint.repository.js.map +1 -1
- package/dist/index.d.ts +12 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/interfaces/webhook-delivery.interface.d.ts +6 -2
- package/dist/interfaces/webhook-delivery.interface.d.ts.map +1 -1
- package/dist/interfaces/webhook-endpoint.interface.d.ts +11 -2
- package/dist/interfaces/webhook-endpoint.interface.d.ts.map +1 -1
- package/dist/interfaces/webhook-options.interface.d.ts +46 -8
- package/dist/interfaces/webhook-options.interface.d.ts.map +1 -1
- package/dist/ports/webhook-delivery.repository.d.ts +28 -10
- package/dist/ports/webhook-delivery.repository.d.ts.map +1 -1
- package/dist/ports/webhook-endpoint.repository.d.ts +24 -4
- package/dist/ports/webhook-endpoint.repository.d.ts.map +1 -1
- package/dist/ports/webhook-event.repository.d.ts +3 -1
- package/dist/ports/webhook-event.repository.d.ts.map +1 -1
- package/dist/ports/webhook-http-client.d.ts +4 -0
- package/dist/ports/webhook-http-client.d.ts.map +1 -1
- package/dist/ports/webhook-secret-vault.d.ts +1 -0
- package/dist/ports/webhook-secret-vault.d.ts.map +1 -1
- package/dist/webhook.admin.service.d.ts +4 -3
- package/dist/webhook.admin.service.d.ts.map +1 -1
- package/dist/webhook.admin.service.js +5 -2
- package/dist/webhook.admin.service.js.map +1 -1
- package/dist/webhook.circuit-breaker.d.ts +11 -4
- package/dist/webhook.circuit-breaker.d.ts.map +1 -1
- package/dist/webhook.circuit-breaker.js +80 -19
- package/dist/webhook.circuit-breaker.js.map +1 -1
- package/dist/webhook.constants.d.ts +9 -2
- package/dist/webhook.constants.d.ts.map +1 -1
- package/dist/webhook.constants.js +11 -4
- package/dist/webhook.constants.js.map +1 -1
- package/dist/webhook.delivery-worker.d.ts +7 -0
- package/dist/webhook.delivery-worker.d.ts.map +1 -1
- package/dist/webhook.delivery-worker.js +122 -42
- package/dist/webhook.delivery-worker.js.map +1 -1
- package/dist/webhook.dispatcher.d.ts +1 -0
- package/dist/webhook.dispatcher.d.ts.map +1 -1
- package/dist/webhook.dispatcher.js +13 -6
- package/dist/webhook.dispatcher.js.map +1 -1
- package/dist/webhook.endpoint-admin.service.d.ts +4 -1
- package/dist/webhook.endpoint-admin.service.d.ts.map +1 -1
- package/dist/webhook.endpoint-admin.service.js +36 -9
- package/dist/webhook.endpoint-admin.service.js.map +1 -1
- package/dist/webhook.module.d.ts.map +1 -1
- package/dist/webhook.module.js +22 -13
- package/dist/webhook.module.js.map +1 -1
- package/dist/webhook.retry-classifier.d.ts +3 -0
- package/dist/webhook.retry-classifier.d.ts.map +1 -0
- package/dist/webhook.retry-classifier.js +17 -0
- package/dist/webhook.retry-classifier.js.map +1 -0
- package/package.json +4 -4
- package/src/sql/create-webhook-tables.sql +1 -1
- package/src/sql/migrations/v0.9.0.sql +32 -0
package/README.md
CHANGED
|
@@ -6,20 +6,27 @@ Outbound webhook delivery for NestJS — HMAC signing, exponential retry, circui
|
|
|
6
6
|
|
|
7
7
|
[](https://github.com/nestarc/webhook/actions/workflows/ci.yml)
|
|
8
8
|
|
|
9
|
+
[Changelog](./CHANGELOG.md) · [Security policy](./SECURITY.md)
|
|
10
|
+
|
|
11
|
+
> **Pre-1.0:** minor version bumps may include breaking changes. Pin exact versions in production until `1.0.0`.
|
|
12
|
+
|
|
9
13
|
## Features
|
|
10
14
|
|
|
11
15
|
- **Fan-out delivery** — one event to many endpoints
|
|
12
16
|
- **HMAC-SHA256 signing** — Standard Webhooks compatible headers
|
|
17
|
+
- **Secret rotation overlap** — sign with both old and new secrets during rotation windows
|
|
13
18
|
- **Exponential backoff** — 30s, 5m, 30m, 2h, 24h (with jitter)
|
|
14
19
|
- **Circuit breaker** — auto-disable failing endpoints, auto-recover after cooldown
|
|
15
20
|
- **Dead letter queue** — failed deliveries tracked for manual retry
|
|
16
21
|
- **Delivery logs** — full audit trail (status code, latency, response body)
|
|
22
|
+
- **Per-attempt audit log** — every attempt recorded with status, latency, response body, and errors
|
|
23
|
+
- **Endpoint snapshotting** — queued deliveries keep their original URL and signing secret during retries
|
|
17
24
|
- **Multi-instance safe** — `FOR UPDATE SKIP LOCKED` prevents duplicate delivery
|
|
18
25
|
- **Graceful shutdown** — waits for in-flight deliveries on process exit
|
|
19
26
|
- **SSRF defense** — DNS resolution validation at registration and dispatch time
|
|
20
27
|
- **Ports/adapters architecture** — swap Prisma or fetch with custom implementations
|
|
21
28
|
- **Stale delivery recovery** — lease-based reaper recovers crashed worker deliveries
|
|
22
|
-
- **Notification hooks** —
|
|
29
|
+
- **Notification hooks** — retry, degraded, failed, and disabled callbacks for observability and alerting
|
|
23
30
|
|
|
24
31
|
## Installation
|
|
25
32
|
|
|
@@ -41,10 +48,20 @@ Run the migration SQL against your PostgreSQL database:
|
|
|
41
48
|
psql -d your_database -f node_modules/@nestarc/webhook/src/sql/create-webhook-tables.sql
|
|
42
49
|
```
|
|
43
50
|
|
|
44
|
-
This creates
|
|
51
|
+
This creates four tables: `webhook_endpoints`, `webhook_events`, `webhook_deliveries`, and `webhook_delivery_attempts`.
|
|
45
52
|
|
|
46
53
|
The migration includes `CREATE EXTENSION IF NOT EXISTS pgcrypto` for PostgreSQL < 13 compatibility.
|
|
47
54
|
|
|
55
|
+
### Upgrading from versions before 0.9.0
|
|
56
|
+
|
|
57
|
+
Existing databases need the v0.9.0 additive migration for per-attempt audit logs, endpoint snapshots, and secret rotation overlap:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
psql -d your_database -f node_modules/@nestarc/webhook/src/sql/migrations/v0.9.0.sql
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
See [CHANGELOG.md](./CHANGELOG.md) for release-specific migration notes.
|
|
64
|
+
|
|
48
65
|
## Quick Start
|
|
49
66
|
|
|
50
67
|
### 1. Register the module
|
|
@@ -55,14 +72,16 @@ import { WebhookModule } from '@nestarc/webhook';
|
|
|
55
72
|
@Module({
|
|
56
73
|
imports: [
|
|
57
74
|
WebhookModule.forRoot({
|
|
75
|
+
// prismaService is your PrismaClient/PrismaService instance.
|
|
76
|
+
// See "Async configuration" below for NestJS DI wiring.
|
|
58
77
|
prisma: prismaService,
|
|
59
78
|
delivery: {
|
|
60
|
-
timeout:
|
|
61
|
-
maxRetries:
|
|
62
|
-
backoff: 'exponential',
|
|
79
|
+
timeout: 30_000,
|
|
80
|
+
maxRetries: 6,
|
|
63
81
|
jitter: true,
|
|
64
82
|
},
|
|
65
83
|
circuitBreaker: {
|
|
84
|
+
degradedThreshold: 3,
|
|
66
85
|
failureThreshold: 5,
|
|
67
86
|
cooldownMinutes: 60,
|
|
68
87
|
},
|
|
@@ -70,6 +89,18 @@ import { WebhookModule } from '@nestarc/webhook';
|
|
|
70
89
|
interval: 5000,
|
|
71
90
|
batchSize: 50,
|
|
72
91
|
},
|
|
92
|
+
onDeliveryRetryScheduled: (ctx) => {
|
|
93
|
+
// Internal observability: a retry was persisted with ctx.nextAttemptAt.
|
|
94
|
+
},
|
|
95
|
+
onEndpointDegraded: (ctx) => {
|
|
96
|
+
// Alert candidate: endpoint reached the degraded threshold before disablement.
|
|
97
|
+
},
|
|
98
|
+
onDeliveryFailed: (ctx) => {
|
|
99
|
+
// Terminal delivery failure only.
|
|
100
|
+
},
|
|
101
|
+
onEndpointDisabled: (ctx) => {
|
|
102
|
+
// Endpoint transitioned from active to inactive.
|
|
103
|
+
},
|
|
73
104
|
}),
|
|
74
105
|
],
|
|
75
106
|
})
|
|
@@ -126,7 +157,8 @@ export class WebhookController {
|
|
|
126
157
|
return this.endpointAdmin.createEndpoint({
|
|
127
158
|
url: 'https://customer.com/webhooks',
|
|
128
159
|
events: ['order.created', 'order.paid'],
|
|
129
|
-
|
|
160
|
+
tenantId: 'tenant_123', // optional; omit for global endpoints (returned as null)
|
|
161
|
+
secret: 'auto', // case-sensitive; omit or pass "auto" to generate a secret
|
|
130
162
|
});
|
|
131
163
|
}
|
|
132
164
|
}
|
|
@@ -149,7 +181,8 @@ export class WebhookController {
|
|
|
149
181
|
| `createEndpoint(dto)` | Register a new webhook endpoint (returns secret) |
|
|
150
182
|
| `listEndpoints(tenantId?)` | List all endpoints (secret excluded) |
|
|
151
183
|
| `getEndpoint(id)` | Get endpoint details (secret excluded) |
|
|
152
|
-
| `updateEndpoint(id, dto)` | Update endpoint URL, events,
|
|
184
|
+
| `updateEndpoint(id, dto)` | Update endpoint URL, events, description, metadata, or active status |
|
|
185
|
+
| `rotateSecret(endpointId, dto)` | Rotate the endpoint signing secret and keep the previous secret valid until `previousSecretExpiresAt` |
|
|
153
186
|
| `deleteEndpoint(id)` | Delete an endpoint |
|
|
154
187
|
| `sendTestEvent(endpointId)` | Send a `webhook.test` ping event |
|
|
155
188
|
|
|
@@ -158,6 +191,7 @@ export class WebhookController {
|
|
|
158
191
|
| Method | Description |
|
|
159
192
|
|--------|-------------|
|
|
160
193
|
| `getDeliveryLogs(endpointId, filters?)` | Query delivery history |
|
|
194
|
+
| `getDeliveryAttempts(deliveryId)` | Query per-attempt audit records for a delivery |
|
|
161
195
|
| `retryDelivery(deliveryId)` | Manually retry a failed delivery |
|
|
162
196
|
|
|
163
197
|
### WebhookSigner
|
|
@@ -165,10 +199,11 @@ export class WebhookController {
|
|
|
165
199
|
| Method | Description |
|
|
166
200
|
|--------|-------------|
|
|
167
201
|
| `sign(eventId, timestamp, body, secret)` | Generate Standard Webhooks signature headers |
|
|
202
|
+
| `signAll(eventId, timestamp, body, secrets[])` | Generate multi-signature headers for secret rotation overlap |
|
|
168
203
|
| `verify(eventId, timestamp, body, secret, signature)` | Verify a webhook signature |
|
|
169
204
|
| `generateSecret()` | Generate a random base64 signing secret |
|
|
170
205
|
|
|
171
|
-
> **Deprecated:** `WebhookAdminService` is a facade that delegates to `WebhookEndpointAdminService` and `WebhookDeliveryAdminService`. It will be removed in
|
|
206
|
+
> **Deprecated:** `WebhookAdminService` is a facade that delegates to `WebhookEndpointAdminService` and `WebhookDeliveryAdminService`. It has been deprecated since `v0.2.0` and will be removed in `v1.0.0`.
|
|
172
207
|
|
|
173
208
|
## Configuration
|
|
174
209
|
|
|
@@ -179,6 +214,7 @@ export class WebhookController {
|
|
|
179
214
|
| `delivery.maxRetries` | `5` | Maximum delivery attempts |
|
|
180
215
|
| `delivery.jitter` | `true` | Add random jitter to retry delays |
|
|
181
216
|
| `circuitBreaker.failureThreshold` | `5` | Consecutive failures before disabling endpoint |
|
|
217
|
+
| `circuitBreaker.degradedThreshold` | — | Consecutive failures before firing `onEndpointDegraded`. Disabled unless configured. Must be lower than `failureThreshold`. |
|
|
182
218
|
| `circuitBreaker.cooldownMinutes` | `60` | Minutes before attempting recovery |
|
|
183
219
|
| `polling.enabled` | `true` | Set to `false` to disable the polling loop (API-only mode) |
|
|
184
220
|
| `polling.interval` | `5000` | Delivery worker poll interval (ms) |
|
|
@@ -186,10 +222,27 @@ export class WebhookController {
|
|
|
186
222
|
| `polling.staleSendingMinutes` | `5` | Minutes before a stuck SENDING delivery is recovered |
|
|
187
223
|
| `allowPrivateUrls` | `false` | Allow private/internal URLs (dev/test only) |
|
|
188
224
|
| `secretVault` | `PlaintextSecretVault` | Custom vault for encrypting/decrypting endpoint secrets at rest |
|
|
189
|
-
| `onDeliveryFailed` | — | Fire-and-forget callback when a delivery exhausts
|
|
190
|
-
| `
|
|
225
|
+
| `onDeliveryFailed` | — | Fire-and-forget callback when a delivery exhausts retries or receives a non-retryable response. Receives `DeliveryFailedContext` (`tenantId` is `null` for global endpoints). See **Delivery failure classification** below. |
|
|
226
|
+
| `onDeliveryRetryScheduled` | — | Fire-and-forget callback after a retriable failed attempt is persisted with `nextAttemptAt`. Receives `DeliveryRetryScheduledContext`. Does not fire for terminal failures. |
|
|
227
|
+
| `onEndpointDegraded` | — | Fire-and-forget callback when consecutive failures reach `circuitBreaker.degradedThreshold` before endpoint disablement. Receives `EndpointDegradedContext`. |
|
|
228
|
+
| `onEndpointDisabled` | — | Fire-and-forget callback when the circuit breaker disables an endpoint. Fires only on active-to-inactive transition after failures meet or exceed `failureThreshold`. |
|
|
229
|
+
|
|
230
|
+
The retry schedule is fixed exponential (`30s`, `5m`, `30m`, `2h`, `24h`). Use `delivery.jitter` to enable or disable random jitter.
|
|
191
231
|
|
|
192
|
-
|
|
232
|
+
Webhook receiver responses are classified before scheduling another attempt:
|
|
233
|
+
|
|
234
|
+
| Response | Behavior |
|
|
235
|
+
|---|---|
|
|
236
|
+
| `2xx` | Mark delivery `SENT` |
|
|
237
|
+
| `3xx` | Retry while attempts remain |
|
|
238
|
+
| `408`, `409`, `425`, `429` | Retry while attempts remain |
|
|
239
|
+
| Other `4xx` | Mark delivery `FAILED` after the current attempt |
|
|
240
|
+
| `5xx` | Retry while attempts remain |
|
|
241
|
+
| Network, DNS, timeout, or dispatch error | Retry while attempts remain |
|
|
242
|
+
|
|
243
|
+
Permanent `4xx` failures still record the response status/body, append a failed attempt log, count as a circuit-breaker failure, clear `nextAttemptAt`, and trigger `onDeliveryFailed`.
|
|
244
|
+
|
|
245
|
+
**Delivery failure classification.** `DeliveryFailedContext.failureKind` categorizes why a delivery was abandoned after retries are exhausted or a non-retryable receiver response is observed:
|
|
193
246
|
|
|
194
247
|
| `failureKind` | When | Extra fields |
|
|
195
248
|
|---|---|---|
|
|
@@ -197,6 +250,12 @@ export class WebhookController {
|
|
|
197
250
|
| `dispatch_error` | Dispatcher threw (DNS failure, ECONNREFUSED, timeout) | — |
|
|
198
251
|
| `http_error` | Endpoint responded with non-2xx status | `responseStatus` |
|
|
199
252
|
|
|
253
|
+
Retryable HTTP responses only trigger `onDeliveryFailed` after the attempt budget is exhausted. Non-retryable receiver responses trigger it after the current attempt. Callback errors are logged and never change persisted delivery state.
|
|
254
|
+
|
|
255
|
+
`onDeliveryRetryScheduled` fires earlier, after a retryable failed attempt has been persisted with its next attempt time. It is intended for internal observability and includes the same failure classification fields as `DeliveryFailedContext`, plus `nextAttemptAt`.
|
|
256
|
+
|
|
257
|
+
`onEndpointDegraded` fires only when `circuitBreaker.degradedThreshold` is configured, the endpoint is still active, and the consecutive failure count exactly reaches that degraded threshold. It does not replace `onEndpointDisabled`, which still fires only when the endpoint transitions from active to inactive at `failureThreshold`.
|
|
258
|
+
|
|
200
259
|
```ts
|
|
201
260
|
onDeliveryFailed: (ctx) => {
|
|
202
261
|
if (ctx.failureKind === 'url_validation') {
|
|
@@ -250,7 +309,7 @@ webhook-timestamp: <unix-seconds>
|
|
|
250
309
|
webhook-signature: v1,<base64-hmac-sha256>
|
|
251
310
|
```
|
|
252
311
|
|
|
253
|
-
**Secret format:** Secrets must be valid base64 strings decoding to at least 16 bytes. Use `"auto"` for automatic generation.
|
|
312
|
+
**Secret format:** Secrets must be valid base64 strings decoding to at least 16 bytes. Use `"auto"` (case-sensitive) or omit `secret` for automatic generation.
|
|
254
313
|
|
|
255
314
|
### SSRF defense
|
|
256
315
|
|
|
@@ -284,6 +343,38 @@ try {
|
|
|
284
343
|
- Secrets are only returned on `createEndpoint` (initial provisioning)
|
|
285
344
|
- Delivery enrichment uses an internal path that does not expose secrets through admin APIs
|
|
286
345
|
- **At-rest encryption** — provide a custom `WebhookSecretVault` to encrypt secrets before storage and decrypt before HMAC signing. The default `PlaintextSecretVault` passes values through unchanged.
|
|
346
|
+
- **Rotation overlap** — use `rotateSecret()` to move the current stored secret to `previous_secret`, set a new current secret, and sign queued deliveries with both secrets until `previousSecretExpiresAt`.
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
const rotated = await endpointAdmin.rotateSecret(endpointId, {
|
|
350
|
+
previousSecretExpiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// Provision this value to the receiver immediately. It is returned only once.
|
|
354
|
+
console.log(rotated?.secret);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
`WebhookSigner.verify()` accepts a request when any signature in the space-separated `webhook-signature` header matches the provided secret.
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
const signer = new WebhookSigner();
|
|
361
|
+
const isValid = signer.verify(
|
|
362
|
+
headers['webhook-id'],
|
|
363
|
+
Number(headers['webhook-timestamp']),
|
|
364
|
+
rawBody,
|
|
365
|
+
signingSecret,
|
|
366
|
+
headers['webhook-signature'],
|
|
367
|
+
);
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Delivery audit data
|
|
371
|
+
|
|
372
|
+
Delivery logs expose the snapshotted destination URL through `DeliveryRecord.destinationUrl`. Per-attempt audit records are available through `WebhookDeliveryAdminService.getDeliveryAttempts(deliveryId)`:
|
|
373
|
+
|
|
374
|
+
```ts
|
|
375
|
+
const [delivery] = await deliveryAdmin.getDeliveryLogs(endpointId);
|
|
376
|
+
const attempts = await deliveryAdmin.getDeliveryAttempts(delivery.id);
|
|
377
|
+
```
|
|
287
378
|
|
|
288
379
|
## Webhook Payload Format
|
|
289
380
|
|
|
@@ -326,34 +417,29 @@ export class WorkerModule {}
|
|
|
326
417
|
|
|
327
418
|
// main.ts
|
|
328
419
|
const app = await NestFactory.createApplicationContext(WorkerModule);
|
|
420
|
+
process.on('SIGTERM', () => void app.close());
|
|
421
|
+
process.on('SIGINT', () => void app.close());
|
|
329
422
|
```
|
|
330
423
|
|
|
331
424
|
Both processes share the same PostgreSQL database. Workers scale horizontally — `FOR UPDATE SKIP LOCKED` prevents duplicate delivery.
|
|
425
|
+
During shutdown, the worker waits up to 30 seconds for the active poll cycle and in-flight deliveries. If the process exits earlier, the next worker recovers stuck `SENDING` rows after `polling.staleSendingMinutes`.
|
|
332
426
|
|
|
333
427
|
## Architecture
|
|
334
428
|
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
v v v
|
|
346
|
-
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
347
|
-
│Dispatcher│ │RetryPolicy│ │CircuitBkr│
|
|
348
|
-
└────┬─────┘ └──────────┘ └──────────┘
|
|
349
|
-
│
|
|
350
|
-
┌────┴─────┐
|
|
351
|
-
│HttpClient│──> customer endpoints
|
|
352
|
-
└──────────┘
|
|
429
|
+
```mermaid
|
|
430
|
+
flowchart LR
|
|
431
|
+
A[Your Service] -->|publish| B[WebhookService]
|
|
432
|
+
B -->|transactional insert| C[(PostgreSQL)]
|
|
433
|
+
D[DeliveryWorker] -->|poll and claim| C
|
|
434
|
+
D --> E[Dispatcher]
|
|
435
|
+
D --> F[RetryPolicy]
|
|
436
|
+
D --> G[CircuitBreaker]
|
|
437
|
+
E --> H[HttpClient]
|
|
438
|
+
H --> I[Customer endpoints]
|
|
353
439
|
```
|
|
354
440
|
|
|
355
441
|
All components depend on **port interfaces**, not concrete implementations. Default adapters use Prisma and Node.js fetch.
|
|
356
442
|
|
|
357
443
|
## License
|
|
358
444
|
|
|
359
|
-
MIT
|
|
445
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-http-client.d.ts","sourceRoot":"","sources":["../../src/adapters/fetch-http-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch-http-client.d.ts","sourceRoot":"","sources":["../../src/adapters/fetch-http-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAM1E,qBACa,eAAgB,YAAW,iBAAiB;IACjD,IAAI,CACR,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC;CAuC3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-http-client.js","sourceRoot":"","sources":["../../src/adapters/fetch-http-client.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAG5C,
|
|
1
|
+
{"version":3,"file":"fetch-http-client.js","sourceRoot":"","sources":["../../src/adapters/fetch-http-client.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAG5C,4DAG8B;AAGvB,IAAM,eAAe,GAArB,MAAM,eAAe;IAC1B,KAAK,CAAC,IAAI,CACR,GAAW,EACX,OAA+B,EAC/B,IAAY,EACZ,OAAe;QAEf,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEzB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,YAAY,EAAE,sCAAkB;oBAChC,GAAG,OAAO;iBACX;gBACD,IAAI;gBACJ,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,QAAQ,EAAE,QAAQ;aACnB,CAAC,CAAC;YAEH,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YACrC,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAE3C,OAAO;gBACL,OAAO,EAAE,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG;gBACxD,UAAU,EAAE,QAAQ,CAAC,MAAM;gBAC3B,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,4CAAwB,CAAC;gBACrD,SAAS;aACV,CAAC;QACJ,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YACrC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,SAAS;gBACT,KAAK,EAAE,OAAO;aACf,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;CACF,CAAA;AA7CY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;GACA,eAAe,CA6C3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plaintext-secret-vault.d.ts","sourceRoot":"","sources":["../../src/adapters/plaintext-secret-vault.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plaintext-secret-vault.d.ts","sourceRoot":"","sources":["../../src/adapters/plaintext-secret-vault.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE;;;GAGG;AACH,qBACa,oBAAqB,YAAW,kBAAkB;IACvD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIxC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAG/C"}
|
|
@@ -1,17 +1,27 @@
|
|
|
1
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
|
+
};
|
|
2
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
9
|
exports.PlaintextSecretVault = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
4
11
|
/**
|
|
5
12
|
* Default no-op vault — secrets are stored and retrieved as-is.
|
|
6
13
|
* Replace with a real implementation (e.g. AES-256-GCM) for production.
|
|
7
14
|
*/
|
|
8
|
-
class PlaintextSecretVault {
|
|
15
|
+
let PlaintextSecretVault = class PlaintextSecretVault {
|
|
9
16
|
async encrypt(secret) {
|
|
10
17
|
return secret;
|
|
11
18
|
}
|
|
12
19
|
async decrypt(secret) {
|
|
13
20
|
return secret;
|
|
14
21
|
}
|
|
15
|
-
}
|
|
22
|
+
};
|
|
16
23
|
exports.PlaintextSecretVault = PlaintextSecretVault;
|
|
24
|
+
exports.PlaintextSecretVault = PlaintextSecretVault = __decorate([
|
|
25
|
+
(0, common_1.Injectable)()
|
|
26
|
+
], PlaintextSecretVault);
|
|
17
27
|
//# sourceMappingURL=plaintext-secret-vault.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plaintext-secret-vault.js","sourceRoot":"","sources":["../../src/adapters/plaintext-secret-vault.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"plaintext-secret-vault.js","sourceRoot":"","sources":["../../src/adapters/plaintext-secret-vault.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAG5C;;;GAGG;AAEI,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAC/B,KAAK,CAAC,OAAO,CAAC,MAAc;QAC1B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,MAAc;QAC1B,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAA;AARY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;GACA,oBAAoB,CAQhC"}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import { PendingDelivery, WebhookDeliveryRepository } from '../ports/webhook-delivery.repository';
|
|
1
|
+
import { ClaimedDelivery, PendingDelivery, WebhookDeliveryRepository, WebhookTransaction } from '../ports/webhook-delivery.repository';
|
|
2
2
|
import { DeliveryAttemptRecord, DeliveryLogFilters, DeliveryRecord, DeliveryResult } from '../interfaces/webhook-delivery.interface';
|
|
3
3
|
import { WebhookSecretVault } from '../ports/webhook-secret-vault';
|
|
4
|
+
type AttemptLogClient = {
|
|
5
|
+
$executeRaw: <T = unknown>(strings: TemplateStringsArray, ...values: unknown[]) => Promise<T>;
|
|
6
|
+
};
|
|
4
7
|
export declare class PrismaDeliveryRepository implements WebhookDeliveryRepository {
|
|
5
8
|
protected readonly prisma: any;
|
|
6
9
|
protected readonly vault?: WebhookSecretVault | undefined;
|
|
7
10
|
constructor(prisma: any, vault?: WebhookSecretVault | undefined);
|
|
8
11
|
createDeliveriesInTransaction(tx: any, eventId: string, endpointIds: string[], maxAttempts: number): Promise<void>;
|
|
9
|
-
runInTransaction<T>(fn: (tx:
|
|
10
|
-
claimPendingDeliveries(batchSize: number): Promise<
|
|
12
|
+
runInTransaction<T>(fn: (tx: WebhookTransaction) => Promise<T>): Promise<T>;
|
|
13
|
+
claimPendingDeliveries(batchSize: number): Promise<ClaimedDelivery[]>;
|
|
11
14
|
enrichDeliveries(deliveryIds: string[]): Promise<PendingDelivery[]>;
|
|
12
15
|
markSent(deliveryId: string, attempts: number, result: DeliveryResult): Promise<void>;
|
|
13
16
|
markFailed(deliveryId: string, attempts: number, result: DeliveryResult): Promise<void>;
|
|
@@ -17,6 +20,7 @@ export declare class PrismaDeliveryRepository implements WebhookDeliveryReposito
|
|
|
17
20
|
getDeliveryAttempts(deliveryId: string): Promise<DeliveryAttemptRecord[]>;
|
|
18
21
|
retryDelivery(deliveryId: string): Promise<boolean>;
|
|
19
22
|
createTestDelivery(eventId: string, endpointId: string): Promise<void>;
|
|
20
|
-
protected appendAttemptLog(deliveryId: string, attempts: number, status: 'PENDING' | 'SENT' | 'FAILED', result: DeliveryResult): Promise<void>;
|
|
23
|
+
protected appendAttemptLog(client: AttemptLogClient, deliveryId: string, attempts: number, status: 'PENDING' | 'SENT' | 'FAILED', result: DeliveryResult): Promise<void>;
|
|
21
24
|
}
|
|
25
|
+
export {};
|
|
22
26
|
//# sourceMappingURL=prisma-delivery.repository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma-delivery.repository.d.ts","sourceRoot":"","sources":["../../src/adapters/prisma-delivery.repository.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,yBAAyB,
|
|
1
|
+
{"version":3,"file":"prisma-delivery.repository.d.ts","sourceRoot":"","sources":["../../src/adapters/prisma-delivery.repository.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,eAAe,EACf,yBAAyB,EACzB,kBAAkB,EACnB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACd,cAAc,EACf,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGnE,KAAK,gBAAgB,GAAG;IACtB,WAAW,EAAE,CAAC,CAAC,GAAG,OAAO,EACvB,OAAO,EAAE,oBAAoB,EAC7B,GAAG,MAAM,EAAE,OAAO,EAAE,KACjB,OAAO,CAAC,CAAC,CAAC,CAAC;CACjB,CAAC;AA0BF,qBACa,wBAAyB,YAAW,yBAAyB;IAEtE,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG;IAC9B,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB;gBAD1B,MAAM,EAAE,GAAG,EACX,KAAK,CAAC,EAAE,kBAAkB,YAAA;IAGzC,6BAA6B,CACjC,EAAE,EAAE,GAAG,EACP,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EAAE,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAqCV,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,kBAAkB,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAI3E,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAqBrE,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA4CnE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrF,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBvF,SAAS,CACb,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,IAAI,CAAC;IAeV,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA2D9D,eAAe,CACnB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,cAAc,EAAE,CAAC;IAmDtB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAkBzE,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQnD,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cA+B5D,gBAAgB,CAC9B,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,EACrC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,IAAI,CAAC;CA0BjB"}
|