@nestarc/webhook 0.9.0 → 0.10.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.
Files changed (65) hide show
  1. package/README.md +76 -25
  2. package/dist/adapters/fetch-http-client.d.ts.map +1 -1
  3. package/dist/adapters/fetch-http-client.js +1 -1
  4. package/dist/adapters/fetch-http-client.js.map +1 -1
  5. package/dist/adapters/plaintext-secret-vault.d.ts.map +1 -1
  6. package/dist/adapters/plaintext-secret-vault.js +12 -2
  7. package/dist/adapters/plaintext-secret-vault.js.map +1 -1
  8. package/dist/adapters/prisma-delivery.repository.d.ts +8 -4
  9. package/dist/adapters/prisma-delivery.repository.d.ts.map +1 -1
  10. package/dist/adapters/prisma-delivery.repository.js +134 -77
  11. package/dist/adapters/prisma-delivery.repository.js.map +1 -1
  12. package/dist/adapters/prisma-endpoint.repository.d.ts +4 -3
  13. package/dist/adapters/prisma-endpoint.repository.d.ts.map +1 -1
  14. package/dist/adapters/prisma-endpoint.repository.js +39 -6
  15. package/dist/adapters/prisma-endpoint.repository.js.map +1 -1
  16. package/dist/index.d.ts +11 -6
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +9 -2
  19. package/dist/index.js.map +1 -1
  20. package/dist/interfaces/webhook-delivery.interface.d.ts +6 -2
  21. package/dist/interfaces/webhook-delivery.interface.d.ts.map +1 -1
  22. package/dist/interfaces/webhook-endpoint.interface.d.ts +11 -2
  23. package/dist/interfaces/webhook-endpoint.interface.d.ts.map +1 -1
  24. package/dist/interfaces/webhook-options.interface.d.ts +8 -6
  25. package/dist/interfaces/webhook-options.interface.d.ts.map +1 -1
  26. package/dist/ports/webhook-delivery.repository.d.ts +28 -10
  27. package/dist/ports/webhook-delivery.repository.d.ts.map +1 -1
  28. package/dist/ports/webhook-endpoint.repository.d.ts +24 -4
  29. package/dist/ports/webhook-endpoint.repository.d.ts.map +1 -1
  30. package/dist/ports/webhook-event.repository.d.ts +3 -1
  31. package/dist/ports/webhook-event.repository.d.ts.map +1 -1
  32. package/dist/ports/webhook-http-client.d.ts +4 -0
  33. package/dist/ports/webhook-http-client.d.ts.map +1 -1
  34. package/dist/ports/webhook-secret-vault.d.ts +1 -0
  35. package/dist/ports/webhook-secret-vault.d.ts.map +1 -1
  36. package/dist/webhook.admin.service.d.ts +4 -3
  37. package/dist/webhook.admin.service.d.ts.map +1 -1
  38. package/dist/webhook.admin.service.js +5 -2
  39. package/dist/webhook.admin.service.js.map +1 -1
  40. package/dist/webhook.circuit-breaker.d.ts +6 -4
  41. package/dist/webhook.circuit-breaker.d.ts.map +1 -1
  42. package/dist/webhook.circuit-breaker.js +10 -8
  43. package/dist/webhook.circuit-breaker.js.map +1 -1
  44. package/dist/webhook.constants.d.ts +9 -2
  45. package/dist/webhook.constants.d.ts.map +1 -1
  46. package/dist/webhook.constants.js +11 -4
  47. package/dist/webhook.constants.js.map +1 -1
  48. package/dist/webhook.delivery-worker.d.ts +4 -0
  49. package/dist/webhook.delivery-worker.d.ts.map +1 -1
  50. package/dist/webhook.delivery-worker.js +49 -22
  51. package/dist/webhook.delivery-worker.js.map +1 -1
  52. package/dist/webhook.dispatcher.d.ts +1 -0
  53. package/dist/webhook.dispatcher.d.ts.map +1 -1
  54. package/dist/webhook.dispatcher.js +13 -6
  55. package/dist/webhook.dispatcher.js.map +1 -1
  56. package/dist/webhook.endpoint-admin.service.d.ts +4 -1
  57. package/dist/webhook.endpoint-admin.service.d.ts.map +1 -1
  58. package/dist/webhook.endpoint-admin.service.js +36 -9
  59. package/dist/webhook.endpoint-admin.service.js.map +1 -1
  60. package/dist/webhook.module.d.ts.map +1 -1
  61. package/dist/webhook.module.js +22 -13
  62. package/dist/webhook.module.js.map +1 -1
  63. package/package.json +4 -4
  64. package/src/sql/create-webhook-tables.sql +1 -1
  65. package/src/sql/migrations/v0.9.0.sql +32 -0
package/README.md CHANGED
@@ -6,14 +6,21 @@ Outbound webhook delivery for NestJS — HMAC signing, exponential retry, circui
6
6
 
7
7
  [![CI](https://github.com/nestarc/webhook/actions/workflows/ci.yml/badge.svg)](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
@@ -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 three tables: `webhook_endpoints`, `webhook_events`, `webhook_deliveries`.
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,11 +72,12 @@ 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
79
  timeout: 10_000,
61
80
  maxRetries: 5,
62
- backoff: 'exponential',
63
81
  jitter: true,
64
82
  },
65
83
  circuitBreaker: {
@@ -126,7 +144,8 @@ export class WebhookController {
126
144
  return this.endpointAdmin.createEndpoint({
127
145
  url: 'https://customer.com/webhooks',
128
146
  events: ['order.created', 'order.paid'],
129
- secret: 'auto',
147
+ tenantId: 'tenant_123', // optional; omit for global endpoints (returned as null)
148
+ secret: 'auto', // case-sensitive; omit or pass "auto" to generate a secret
130
149
  });
131
150
  }
132
151
  }
@@ -149,7 +168,8 @@ export class WebhookController {
149
168
  | `createEndpoint(dto)` | Register a new webhook endpoint (returns secret) |
150
169
  | `listEndpoints(tenantId?)` | List all endpoints (secret excluded) |
151
170
  | `getEndpoint(id)` | Get endpoint details (secret excluded) |
152
- | `updateEndpoint(id, dto)` | Update endpoint URL, events, etc. |
171
+ | `updateEndpoint(id, dto)` | Update endpoint URL, events, description, metadata, or active status |
172
+ | `rotateSecret(endpointId, dto)` | Rotate the endpoint signing secret and keep the previous secret valid until `previousSecretExpiresAt` |
153
173
  | `deleteEndpoint(id)` | Delete an endpoint |
154
174
  | `sendTestEvent(endpointId)` | Send a `webhook.test` ping event |
155
175
 
@@ -158,6 +178,7 @@ export class WebhookController {
158
178
  | Method | Description |
159
179
  |--------|-------------|
160
180
  | `getDeliveryLogs(endpointId, filters?)` | Query delivery history |
181
+ | `getDeliveryAttempts(deliveryId)` | Query per-attempt audit records for a delivery |
161
182
  | `retryDelivery(deliveryId)` | Manually retry a failed delivery |
162
183
 
163
184
  ### WebhookSigner
@@ -165,10 +186,11 @@ export class WebhookController {
165
186
  | Method | Description |
166
187
  |--------|-------------|
167
188
  | `sign(eventId, timestamp, body, secret)` | Generate Standard Webhooks signature headers |
189
+ | `signAll(eventId, timestamp, body, secrets[])` | Generate multi-signature headers for secret rotation overlap |
168
190
  | `verify(eventId, timestamp, body, secret, signature)` | Verify a webhook signature |
169
191
  | `generateSecret()` | Generate a random base64 signing secret |
170
192
 
171
- > **Deprecated:** `WebhookAdminService` is a facade that delegates to `WebhookEndpointAdminService` and `WebhookDeliveryAdminService`. It will be removed in a future release.
193
+ > **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
194
 
173
195
  ## Configuration
174
196
 
@@ -189,6 +211,8 @@ export class WebhookController {
189
211
  | `onDeliveryFailed` | — | Fire-and-forget callback when a delivery exhausts all retries. Receives `DeliveryFailedContext` (`tenantId` is `null` for global endpoints). See **Delivery failure classification** below. |
190
212
  | `onEndpointDisabled` | — | Fire-and-forget callback when the circuit breaker disables an endpoint. Fires once at exact threshold crossing. |
191
213
 
214
+ The retry schedule is fixed exponential (`30s`, `5m`, `30m`, `2h`, `24h`). Use `delivery.jitter` to enable or disable random jitter.
215
+
192
216
  **Delivery failure classification.** `DeliveryFailedContext.failureKind` categorizes why a delivery was abandoned after all retries:
193
217
 
194
218
  | `failureKind` | When | Extra fields |
@@ -250,7 +274,7 @@ webhook-timestamp: <unix-seconds>
250
274
  webhook-signature: v1,<base64-hmac-sha256>
251
275
  ```
252
276
 
253
- **Secret format:** Secrets must be valid base64 strings decoding to at least 16 bytes. Use `"auto"` for automatic generation.
277
+ **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
278
 
255
279
  ### SSRF defense
256
280
 
@@ -284,6 +308,38 @@ try {
284
308
  - Secrets are only returned on `createEndpoint` (initial provisioning)
285
309
  - Delivery enrichment uses an internal path that does not expose secrets through admin APIs
286
310
  - **At-rest encryption** — provide a custom `WebhookSecretVault` to encrypt secrets before storage and decrypt before HMAC signing. The default `PlaintextSecretVault` passes values through unchanged.
311
+ - **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`.
312
+
313
+ ```ts
314
+ const rotated = await endpointAdmin.rotateSecret(endpointId, {
315
+ previousSecretExpiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
316
+ });
317
+
318
+ // Provision this value to the receiver immediately. It is returned only once.
319
+ console.log(rotated?.secret);
320
+ ```
321
+
322
+ `WebhookSigner.verify()` accepts a request when any signature in the space-separated `webhook-signature` header matches the provided secret.
323
+
324
+ ```ts
325
+ const signer = new WebhookSigner();
326
+ const isValid = signer.verify(
327
+ headers['webhook-id'],
328
+ Number(headers['webhook-timestamp']),
329
+ rawBody,
330
+ signingSecret,
331
+ headers['webhook-signature'],
332
+ );
333
+ ```
334
+
335
+ ### Delivery audit data
336
+
337
+ Delivery logs expose the snapshotted destination URL through `DeliveryRecord.destinationUrl`. Per-attempt audit records are available through `WebhookDeliveryAdminService.getDeliveryAttempts(deliveryId)`:
338
+
339
+ ```ts
340
+ const [delivery] = await deliveryAdmin.getDeliveryLogs(endpointId);
341
+ const attempts = await deliveryAdmin.getDeliveryAttempts(delivery.id);
342
+ ```
287
343
 
288
344
  ## Webhook Payload Format
289
345
 
@@ -326,34 +382,29 @@ export class WorkerModule {}
326
382
 
327
383
  // main.ts
328
384
  const app = await NestFactory.createApplicationContext(WorkerModule);
385
+ process.on('SIGTERM', () => void app.close());
386
+ process.on('SIGINT', () => void app.close());
329
387
  ```
330
388
 
331
389
  Both processes share the same PostgreSQL database. Workers scale horizontally — `FOR UPDATE SKIP LOCKED` prevents duplicate delivery.
390
+ 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
391
 
333
392
  ## Architecture
334
393
 
335
- ```
336
- ┌─────────────┐ ┌──────────────────┐ ┌───────────────────┐
337
- Your Service │────>│ WebhookService │────>│ PostgreSQL (tx) │
338
- └─────────────┘ └──────────────────┘ └───────────────────┘
339
-
340
- ┌───────┴────────┐
341
- │ DeliveryWorker │ (polls every N seconds)
342
- └───────┬────────┘
343
-
344
- ┌─────────────┼─────────────┐
345
- v v v
346
- ┌──────────┐ ┌──────────┐ ┌──────────┐
347
- │Dispatcher│ │RetryPolicy│ │CircuitBkr│
348
- └────┬─────┘ └──────────┘ └──────────┘
349
-
350
- ┌────┴─────┐
351
- │HttpClient│──> customer endpoints
352
- └──────────┘
394
+ ```mermaid
395
+ flowchart LR
396
+ A[Your Service] -->|publish| B[WebhookService]
397
+ B -->|transactional insert| C[(PostgreSQL)]
398
+ D[DeliveryWorker] -->|poll and claim| C
399
+ D --> E[Dispatcher]
400
+ D --> F[RetryPolicy]
401
+ D --> G[CircuitBreaker]
402
+ E --> H[HttpClient]
403
+ H --> I[Customer endpoints]
353
404
  ```
354
405
 
355
406
  All components depend on **port interfaces**, not concrete implementations. Default adapters use Prisma and Node.js fetch.
356
407
 
357
408
  ## License
358
409
 
359
- MIT
410
+ 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;AAG1E,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
+ {"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"}
@@ -19,7 +19,7 @@ let FetchHttpClient = class FetchHttpClient {
19
19
  method: 'POST',
20
20
  headers: {
21
21
  'Content-Type': 'application/json',
22
- 'User-Agent': '@nestarc/webhook',
22
+ 'User-Agent': webhook_constants_1.DEFAULT_USER_AGENT,
23
23
  ...headers,
24
24
  },
25
25
  body,
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-http-client.js","sourceRoot":"","sources":["../../src/adapters/fetch-http-client.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAG5C,4DAAgE;AAGzD,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,kBAAkB;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
+ {"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":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE;;;GAGG;AACH,qBAAa,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
+ {"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":";;;AAEA;;;GAGG;AACH,MAAa,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;AARD,oDAQC"}
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: unknown) => Promise<T>): Promise<T>;
10
- claimPendingDeliveries(batchSize: number): Promise<PendingDelivery[]>;
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,EAC1B,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;AAyBnE,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,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhE,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAqBrE,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAgCnE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAYrF,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAavF,SAAS,CACb,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,IAAI,CAAC;IAaV,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAY9D,eAAe,CACnB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,cAAc,EAAE,CAAC;IAkDtB,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,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,EACrC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,IAAI,CAAC;CAkCjB"}
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;IAevF,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"}
@@ -11,7 +11,8 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.PrismaDeliveryRepository = void 0;
13
13
  const common_1 = require("@nestjs/common");
14
- const MAX_ATTEMPT_RESPONSE_BODY_LENGTH = 4096;
14
+ const webhook_constants_1 = require("../webhook.constants");
15
+ const STALE_SENDING_RECOVERY_ERROR = 'Recovered stale SENDING delivery after worker lease expired';
15
16
  function truncateAttemptResponseBody(body) {
16
17
  if (body == null) {
17
18
  return {
@@ -19,14 +20,14 @@ function truncateAttemptResponseBody(body) {
19
20
  responseBodyTruncated: false,
20
21
  };
21
22
  }
22
- if (body.length <= MAX_ATTEMPT_RESPONSE_BODY_LENGTH) {
23
+ if (body.length <= webhook_constants_1.ATTEMPT_RESPONSE_BODY_MAX_LENGTH) {
23
24
  return {
24
25
  responseBody: body,
25
26
  responseBodyTruncated: false,
26
27
  };
27
28
  }
28
29
  return {
29
- responseBody: body.slice(0, MAX_ATTEMPT_RESPONSE_BODY_LENGTH),
30
+ responseBody: body.slice(0, webhook_constants_1.ATTEMPT_RESPONSE_BODY_MAX_LENGTH),
30
31
  responseBodyTruncated: true,
31
32
  };
32
33
  }
@@ -86,16 +87,20 @@ let PrismaDeliveryRepository = class PrismaDeliveryRepository {
86
87
  )
87
88
  RETURNING
88
89
  webhook_deliveries.id,
89
- webhook_deliveries.event_id,
90
- webhook_deliveries.endpoint_id,
90
+ webhook_deliveries.event_id AS "eventId",
91
+ webhook_deliveries.endpoint_id AS "endpointId",
91
92
  webhook_deliveries.attempts,
92
- webhook_deliveries.max_attempts`;
93
+ webhook_deliveries.max_attempts AS "maxAttempts"`;
93
94
  }
94
95
  async enrichDeliveries(deliveryIds) {
95
96
  const rows = await this.prisma.$queryRaw `
96
97
  SELECT
97
- d.id, d.event_id, d.endpoint_id, d.attempts, d.max_attempts,
98
- e.tenant_id::text AS tenant_id,
98
+ d.id,
99
+ d.event_id AS "eventId",
100
+ d.endpoint_id AS "endpointId",
101
+ d.attempts,
102
+ d.max_attempts AS "maxAttempts",
103
+ e.tenant_id::text AS "tenantId",
99
104
  COALESCE(d.endpoint_url_snapshot, e.url) AS url,
100
105
  COALESCE(d.signing_secret_snapshot, e.secret) AS secret,
101
106
  CASE
@@ -103,63 +108,121 @@ let PrismaDeliveryRepository = class PrismaDeliveryRepository {
103
108
  THEN ARRAY[]::text[]
104
109
  ELSE ARRAY[d.secondary_signing_secret_snapshot]
105
110
  END AS "additionalSecrets",
106
- ev.event_type, ev.payload
111
+ ev.event_type AS "eventType",
112
+ ev.payload
107
113
  FROM webhook_deliveries d
108
114
  JOIN webhook_endpoints e ON e.id = d.endpoint_id
109
115
  JOIN webhook_events ev ON ev.id = d.event_id
110
116
  WHERE d.id = ANY(${deliveryIds}::uuid[])`;
111
117
  if (this.vault) {
112
- for (const row of rows) {
113
- row.secret = await this.vault.decrypt(row.secret);
114
- row.additionalSecrets = await Promise.all((row.additionalSecrets ?? []).map((secret) => this.vault.decrypt(secret)));
115
- }
118
+ await Promise.all(rows.map(async (row) => {
119
+ const [secret, additionalSecrets] = await Promise.all([
120
+ this.vault.decrypt(row.secret),
121
+ Promise.all(row.additionalSecrets.map((secret) => this.vault.decrypt(secret))),
122
+ ]);
123
+ row.secret = secret;
124
+ row.additionalSecrets = additionalSecrets;
125
+ }));
116
126
  }
117
127
  return rows;
118
128
  }
119
129
  async markSent(deliveryId, attempts, result) {
120
- await this.prisma.$executeRaw `
121
- UPDATE webhook_deliveries
122
- SET status = 'SENT', attempts = ${attempts},
123
- last_attempt_at = NOW(), completed_at = NOW(),
124
- response_status = ${result.statusCode ?? null},
125
- response_body = ${result.body ?? null},
126
- latency_ms = ${result.latencyMs}
127
- WHERE id = ${deliveryId}::uuid`;
128
- await this.appendAttemptLog(deliveryId, attempts, 'SENT', result);
130
+ await this.prisma.$transaction(async (tx) => {
131
+ await tx.$executeRaw `
132
+ UPDATE webhook_deliveries
133
+ SET status = 'SENT', attempts = ${attempts},
134
+ last_attempt_at = NOW(), completed_at = NOW(),
135
+ response_status = ${result.statusCode ?? null},
136
+ response_body = ${result.body ?? null},
137
+ latency_ms = ${result.latencyMs}
138
+ WHERE id = ${deliveryId}::uuid`;
139
+ await this.appendAttemptLog(tx, deliveryId, attempts, 'SENT', result);
140
+ });
129
141
  }
130
142
  async markFailed(deliveryId, attempts, result) {
131
- await this.prisma.$executeRaw `
132
- UPDATE webhook_deliveries
133
- SET status = 'FAILED', attempts = ${attempts},
134
- last_attempt_at = NOW(), completed_at = NOW(),
135
- response_status = ${result.statusCode ?? null},
136
- response_body = ${result.body ?? null},
137
- latency_ms = ${result.latencyMs},
138
- last_error = ${result.error ?? null}
139
- WHERE id = ${deliveryId}::uuid`;
140
- await this.appendAttemptLog(deliveryId, attempts, 'FAILED', result);
143
+ await this.prisma.$transaction(async (tx) => {
144
+ await tx.$executeRaw `
145
+ UPDATE webhook_deliveries
146
+ SET status = 'FAILED', attempts = ${attempts},
147
+ last_attempt_at = NOW(), completed_at = NOW(),
148
+ response_status = ${result.statusCode ?? null},
149
+ response_body = ${result.body ?? null},
150
+ latency_ms = ${result.latencyMs},
151
+ last_error = ${result.error ?? null}
152
+ WHERE id = ${deliveryId}::uuid`;
153
+ await this.appendAttemptLog(tx, deliveryId, attempts, 'FAILED', result);
154
+ });
141
155
  }
142
156
  async markRetry(deliveryId, attempts, nextAt, result) {
143
- await this.prisma.$executeRaw `
144
- UPDATE webhook_deliveries
145
- SET status = 'PENDING', attempts = ${attempts},
146
- last_attempt_at = NOW(), next_attempt_at = ${nextAt},
147
- response_status = ${result.statusCode ?? null},
148
- response_body = ${result.body ?? null},
149
- latency_ms = ${result.latencyMs},
150
- last_error = ${result.error ?? null}
151
- WHERE id = ${deliveryId}::uuid`;
152
- await this.appendAttemptLog(deliveryId, attempts, 'PENDING', result);
157
+ await this.prisma.$transaction(async (tx) => {
158
+ await tx.$executeRaw `
159
+ UPDATE webhook_deliveries
160
+ SET status = 'PENDING', attempts = ${attempts},
161
+ last_attempt_at = NOW(), next_attempt_at = ${nextAt},
162
+ response_status = ${result.statusCode ?? null},
163
+ response_body = ${result.body ?? null},
164
+ latency_ms = ${result.latencyMs},
165
+ last_error = ${result.error ?? null}
166
+ WHERE id = ${deliveryId}::uuid`;
167
+ await this.appendAttemptLog(tx, deliveryId, attempts, 'PENDING', result);
168
+ });
153
169
  }
154
170
  async recoverStaleSending(stalenessMinutes) {
155
171
  const interval = `${stalenessMinutes} minutes`;
156
172
  const recovered = await this.prisma.$queryRaw `
157
- UPDATE webhook_deliveries
158
- SET status = 'PENDING', claimed_at = NULL
159
- WHERE status = 'SENDING'
160
- AND claimed_at IS NOT NULL
161
- AND claimed_at + ${interval}::interval < NOW()
162
- RETURNING id`;
173
+ WITH recovered AS (
174
+ UPDATE webhook_deliveries
175
+ SET attempts = attempts + 1,
176
+ status = CASE
177
+ WHEN attempts + 1 >= max_attempts THEN 'FAILED'
178
+ ELSE 'PENDING'
179
+ END,
180
+ claimed_at = NULL,
181
+ last_attempt_at = NOW(),
182
+ next_attempt_at = CASE
183
+ WHEN attempts + 1 >= max_attempts THEN NULL
184
+ ELSE NOW()
185
+ END,
186
+ completed_at = CASE
187
+ WHEN attempts + 1 >= max_attempts THEN NOW()
188
+ ELSE completed_at
189
+ END,
190
+ response_status = NULL,
191
+ response_body = NULL,
192
+ latency_ms = NULL,
193
+ last_error = ${STALE_SENDING_RECOVERY_ERROR}
194
+ WHERE status = 'SENDING'
195
+ AND claimed_at IS NOT NULL
196
+ AND claimed_at + ${interval}::interval < NOW()
197
+ RETURNING id, attempts, status
198
+ ),
199
+ attempt_log AS (
200
+ INSERT INTO webhook_delivery_attempts (
201
+ delivery_id,
202
+ attempt_number,
203
+ status,
204
+ response_status,
205
+ response_body,
206
+ response_body_truncated,
207
+ latency_ms,
208
+ last_error
209
+ )
210
+ SELECT
211
+ id,
212
+ attempts,
213
+ status,
214
+ NULL,
215
+ NULL,
216
+ FALSE,
217
+ NULL,
218
+ ${STALE_SENDING_RECOVERY_ERROR}
219
+ FROM recovered
220
+ ON CONFLICT (delivery_id, attempt_number) DO NOTHING
221
+ RETURNING delivery_id
222
+ )
223
+ SELECT recovered.id
224
+ FROM recovered
225
+ LEFT JOIN attempt_log ON attempt_log.delivery_id = recovered.id`;
163
226
  return recovered.length;
164
227
  }
165
228
  async getDeliveryLogs(endpointId, filters) {
@@ -189,6 +252,7 @@ let PrismaDeliveryRepository = class PrismaDeliveryRepository {
189
252
  d.event_id AS "eventId",
190
253
  d.endpoint_id AS "endpointId",
191
254
  COALESCE(d.endpoint_url_snapshot, ep.url) AS "destinationUrl",
255
+ ep.tenant_id::text AS "tenantId",
192
256
  d.max_attempts AS "maxAttempts",
193
257
  d.next_attempt_at AS "nextAttemptAt",
194
258
  d.last_attempt_at AS "lastAttemptAt",
@@ -262,36 +326,29 @@ let PrismaDeliveryRepository = class PrismaDeliveryRepository {
262
326
  FROM webhook_endpoints e
263
327
  WHERE e.id = ${endpointId}::uuid`;
264
328
  }
265
- async appendAttemptLog(deliveryId, attempts, status, result) {
329
+ async appendAttemptLog(client, deliveryId, attempts, status, result) {
266
330
  const { responseBody, responseBodyTruncated } = truncateAttemptResponseBody(result.body ?? null);
267
- try {
268
- await this.prisma.$executeRaw `
269
- INSERT INTO webhook_delivery_attempts (
270
- delivery_id,
271
- attempt_number,
272
- status,
273
- response_status,
274
- response_body,
275
- response_body_truncated,
276
- latency_ms,
277
- last_error
278
- )
279
- VALUES (
280
- ${deliveryId}::uuid,
281
- ${attempts},
282
- ${status},
283
- ${result.statusCode ?? null},
284
- ${responseBody},
285
- ${responseBodyTruncated},
286
- ${result.latencyMs},
287
- ${result.error ?? null}
288
- )`;
289
- }
290
- catch (error) {
291
- const message = error instanceof Error ? error.message : String(error);
292
- // Best-effort logging only. Delivery state has already been committed above.
293
- console.error(`[PrismaDeliveryRepository] Failed to append attempt log for ${deliveryId}#${attempts}: ${message}`);
294
- }
331
+ await client.$executeRaw `
332
+ INSERT INTO webhook_delivery_attempts (
333
+ delivery_id,
334
+ attempt_number,
335
+ status,
336
+ response_status,
337
+ response_body,
338
+ response_body_truncated,
339
+ latency_ms,
340
+ last_error
341
+ )
342
+ VALUES (
343
+ ${deliveryId}::uuid,
344
+ ${attempts},
345
+ ${status},
346
+ ${result.statusCode ?? null},
347
+ ${responseBody},
348
+ ${responseBodyTruncated},
349
+ ${result.latencyMs},
350
+ ${result.error ?? null}
351
+ )`;
295
352
  }
296
353
  };
297
354
  exports.PrismaDeliveryRepository = PrismaDeliveryRepository;
@@ -1 +1 @@
1
- {"version":3,"file":"prisma-delivery.repository.js","sourceRoot":"","sources":["../../src/adapters/prisma-delivery.repository.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prisma-delivery.repository.js","sourceRoot":"","sources":["../../src/adapters/prisma-delivery.repository.ts"],"names":[],"mappings":"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"}