@nestarc/webhook 0.12.1 → 0.13.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/README.md +154 -0
- package/dist/adapters/fetch-http-client.d.ts +2 -2
- package/dist/adapters/fetch-http-client.d.ts.map +1 -1
- package/dist/adapters/fetch-http-client.js +165 -29
- package/dist/adapters/fetch-http-client.js.map +1 -1
- package/dist/adapters/prisma-delivery.repository.d.ts +9 -3
- package/dist/adapters/prisma-delivery.repository.d.ts.map +1 -1
- package/dist/adapters/prisma-delivery.repository.js +228 -19
- package/dist/adapters/prisma-delivery.repository.js.map +1 -1
- package/dist/adapters/prisma-endpoint.repository.js +1 -1
- package/dist/adapters/prisma-event.repository.d.ts +3 -1
- package/dist/adapters/prisma-event.repository.d.ts.map +1 -1
- package/dist/adapters/prisma-event.repository.js +36 -2
- package/dist/adapters/prisma-event.repository.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/webhook-delivery.interface.d.ts +26 -0
- package/dist/interfaces/webhook-delivery.interface.d.ts.map +1 -1
- package/dist/interfaces/webhook-options.interface.d.ts +27 -0
- package/dist/interfaces/webhook-options.interface.d.ts.map +1 -1
- package/dist/ports/webhook-delivery.repository.d.ts +6 -2
- package/dist/ports/webhook-delivery.repository.d.ts.map +1 -1
- package/dist/ports/webhook-event.repository.d.ts +6 -0
- package/dist/ports/webhook-event.repository.d.ts.map +1 -1
- package/dist/ports/webhook-http-client.d.ts +8 -1
- package/dist/ports/webhook-http-client.d.ts.map +1 -1
- package/dist/webhook.admin.service.d.ts +4 -2
- package/dist/webhook.admin.service.d.ts.map +1 -1
- package/dist/webhook.admin.service.js +8 -2
- package/dist/webhook.admin.service.js.map +1 -1
- package/dist/webhook.delivery-admin.service.d.ts +4 -2
- package/dist/webhook.delivery-admin.service.d.ts.map +1 -1
- package/dist/webhook.delivery-admin.service.js +14 -2
- package/dist/webhook.delivery-admin.service.js.map +1 -1
- package/dist/webhook.dispatcher.d.ts.map +1 -1
- package/dist/webhook.dispatcher.js +5 -2
- package/dist/webhook.dispatcher.js.map +1 -1
- package/dist/webhook.module.d.ts.map +1 -1
- package/dist/webhook.module.js +6 -2
- package/dist/webhook.module.js.map +1 -1
- package/dist/webhook.retention-admin.service.d.ts +10 -0
- package/dist/webhook.retention-admin.service.d.ts.map +1 -0
- package/dist/webhook.retention-admin.service.js +50 -0
- package/dist/webhook.retention-admin.service.js.map +1 -0
- package/dist/webhook.service.d.ts +7 -4
- package/dist/webhook.service.d.ts.map +1 -1
- package/dist/webhook.service.js +40 -10
- package/dist/webhook.service.js.map +1 -1
- package/dist/webhook.signer.d.ts +5 -0
- package/dist/webhook.signer.d.ts.map +1 -1
- package/dist/webhook.signer.js +14 -0
- package/dist/webhook.signer.js.map +1 -1
- package/dist/webhook.url-validator.d.ts +1 -1
- package/dist/webhook.url-validator.d.ts.map +1 -1
- package/dist/webhook.url-validator.js +145 -51
- package/dist/webhook.url-validator.js.map +1 -1
- package/package.json +23 -12
- package/src/sql/create-webhook-tables.sql +11 -5
- package/src/sql/migrations/v0.13.0.sql +11 -0
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ Outbound webhook delivery for NestJS — HMAC signing, exponential retry, circui
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
15
|
- **Fan-out delivery** — one event to many endpoints
|
|
16
|
+
- **Idempotent publish** — deduplicate producer retries with application keys
|
|
16
17
|
- **HMAC-SHA256 signing** — Standard Webhooks compatible headers
|
|
17
18
|
- **Secret rotation overlap** — sign with both old and new secrets during rotation windows
|
|
18
19
|
- **Exponential backoff** — 30s, 5m, 30m, 2h, 24h (with jitter)
|
|
@@ -20,6 +21,8 @@ Outbound webhook delivery for NestJS — HMAC signing, exponential retry, circui
|
|
|
20
21
|
- **Dead letter queue** — failed deliveries tracked for manual retry
|
|
21
22
|
- **Delivery logs** — full audit trail (status code, latency, response body)
|
|
22
23
|
- **Per-attempt audit log** — every attempt recorded with status, latency, response body, and errors
|
|
24
|
+
- **Replay and bulk retry** — requeue failed deliveries or replay an event to active endpoints
|
|
25
|
+
- **Retention and redaction controls** — purge stored payloads/response bodies and sanitize data before persistence
|
|
23
26
|
- **Endpoint snapshotting** — queued deliveries keep their original URL and signing secret during retries
|
|
24
27
|
- **Multi-instance safe** — `FOR UPDATE SKIP LOCKED` prevents duplicate delivery
|
|
25
28
|
- **Graceful shutdown** — waits for in-flight deliveries on process exit
|
|
@@ -40,6 +43,63 @@ npm install @nestarc/webhook
|
|
|
40
43
|
npm install @nestjs/common @nestjs/core @nestjs/schedule @prisma/client
|
|
41
44
|
```
|
|
42
45
|
|
|
46
|
+
Supported peer ranges are NestJS 10 or 11, `@nestjs/schedule` 4 or 5, and
|
|
47
|
+
Prisma Client 5, 6, or 7. The CI compatibility lanes keep the established
|
|
48
|
+
NestJS 10.4.20/Prisma 6.19.3 path and separately exercise the modern
|
|
49
|
+
NestJS 11.2.1/Prisma 7.10.0 path against PostgreSQL 16. A packed-artifact
|
|
50
|
+
consumer also installs the modern tuple with `--strict-peer-deps` and verifies
|
|
51
|
+
artifact integrity/provenance, public types, and CommonJS runtime loading.
|
|
52
|
+
The Prisma 6 lane deliberately retains `prisma-client-js` and native
|
|
53
|
+
`new PrismaClient()` construction; only the Prisma 7 lane uses a driver adapter.
|
|
54
|
+
|
|
55
|
+
### Prisma 7 client setup
|
|
56
|
+
|
|
57
|
+
Prisma 7 requires a database driver adapter. Configure and generate the client
|
|
58
|
+
in your application, then pass that client to `WebhookModule`; this package
|
|
59
|
+
does not create or own your Prisma connection.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install @prisma/adapter-pg pg
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```prisma
|
|
66
|
+
// prisma/schema.prisma
|
|
67
|
+
generator client {
|
|
68
|
+
provider = "prisma-client"
|
|
69
|
+
output = "../src/generated/prisma"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
datasource db {
|
|
73
|
+
provider = "postgresql"
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// prisma.config.ts
|
|
79
|
+
import { defineConfig, env } from 'prisma/config';
|
|
80
|
+
|
|
81
|
+
export default defineConfig({
|
|
82
|
+
schema: 'prisma/schema.prisma',
|
|
83
|
+
datasource: { url: env('DATABASE_URL') },
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { PrismaPg } from '@prisma/adapter-pg';
|
|
89
|
+
import { PrismaClient } from './generated/prisma/client';
|
|
90
|
+
|
|
91
|
+
const adapter = new PrismaPg({
|
|
92
|
+
connectionString: process.env.DATABASE_URL!,
|
|
93
|
+
});
|
|
94
|
+
const prisma = new PrismaClient({ adapter });
|
|
95
|
+
|
|
96
|
+
WebhookModule.forRoot({ prisma });
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Prisma 5 and 6 consumers can keep their existing client construction. The
|
|
100
|
+
default webhook repositories only require Prisma's raw-query and transaction
|
|
101
|
+
surface, so applications may also provide a compatible custom Prisma service.
|
|
102
|
+
|
|
43
103
|
## Database Setup
|
|
44
104
|
|
|
45
105
|
Run the migration SQL against your PostgreSQL database:
|
|
@@ -62,6 +122,14 @@ psql -d your_database -f node_modules/@nestarc/webhook/src/sql/migrations/v0.9.0
|
|
|
62
122
|
|
|
63
123
|
See [CHANGELOG.md](./CHANGELOG.md) for release-specific migration notes.
|
|
64
124
|
|
|
125
|
+
### Upgrading from versions before 0.13.0
|
|
126
|
+
|
|
127
|
+
Existing databases need the v0.13.0 additive migration for idempotent publish keys, correlation IDs, and payload purge metadata:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
psql -d your_database -f node_modules/@nestarc/webhook/src/sql/migrations/v0.13.0.sql
|
|
131
|
+
```
|
|
132
|
+
|
|
65
133
|
## Quick Start
|
|
66
134
|
|
|
67
135
|
### 1. Register the module
|
|
@@ -143,6 +211,17 @@ export class OrderService {
|
|
|
143
211
|
}
|
|
144
212
|
```
|
|
145
213
|
|
|
214
|
+
Use an idempotency key when the producer may retry the same business operation:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
await this.webhooks.send(new OrderCreatedEvent(order.id, order.total), {
|
|
218
|
+
idempotencyKey: `order:${order.id}:created`,
|
|
219
|
+
correlationId: requestId,
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Duplicate sends with the same tenant, event type, and idempotency key return the existing event ID and do not enqueue duplicate deliveries.
|
|
224
|
+
|
|
146
225
|
### 4. Manage endpoints
|
|
147
226
|
|
|
148
227
|
```typescript
|
|
@@ -174,6 +253,8 @@ export class WebhookController {
|
|
|
174
253
|
| `sendToTenant(tenantId, event)` | Publish to tenant-specific endpoints only |
|
|
175
254
|
| `sendToEndpoints(endpointIds, event)` | Publish to specific endpoint IDs only |
|
|
176
255
|
|
|
256
|
+
All publish methods accept optional `WebhookPublishOptions` with `idempotencyKey` and `correlationId`.
|
|
257
|
+
|
|
177
258
|
### WebhookEndpointAdminService
|
|
178
259
|
|
|
179
260
|
| Method | Description |
|
|
@@ -193,6 +274,14 @@ export class WebhookController {
|
|
|
193
274
|
| `getDeliveryLogs(endpointId, filters?)` | Query delivery history |
|
|
194
275
|
| `getDeliveryAttempts(deliveryId)` | Query per-attempt audit records for a delivery |
|
|
195
276
|
| `retryDelivery(deliveryId)` | Manually retry a failed delivery |
|
|
277
|
+
| `retryFailedDeliveries(filters, options?)` | Requeue matching failed deliveries in bulk |
|
|
278
|
+
| `replayEvent(eventId, options?)` | Create new delivery rows for an existing event and currently active endpoints |
|
|
279
|
+
|
|
280
|
+
### WebhookRetentionAdminService
|
|
281
|
+
|
|
282
|
+
| Method | Description |
|
|
283
|
+
|--------|-------------|
|
|
284
|
+
| `purgeExpiredData(now?)` | Apply configured retention policy and return purge counts |
|
|
196
285
|
|
|
197
286
|
### WebhookSigner
|
|
198
287
|
|
|
@@ -201,6 +290,7 @@ export class WebhookController {
|
|
|
201
290
|
| `sign(eventId, timestamp, body, secret)` | Generate Standard Webhooks signature headers |
|
|
202
291
|
| `signAll(eventId, timestamp, body, secrets[])` | Generate multi-signature headers for secret rotation overlap |
|
|
203
292
|
| `verify(eventId, timestamp, body, secret, signature)` | Verify a webhook signature |
|
|
293
|
+
| `verifyWithTolerance(eventId, timestamp, body, secret, signature, options)` | Verify a signature and reject timestamps outside `options.toleranceSeconds` |
|
|
204
294
|
| `generateSecret()` | Generate a random base64 signing secret |
|
|
205
295
|
|
|
206
296
|
> **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`.
|
|
@@ -224,6 +314,11 @@ export class WebhookController {
|
|
|
224
314
|
| `polling.drainWhileBacklogged` | `false` | Keep claiming additional batches inside one poll while backlog and capacity remain |
|
|
225
315
|
| `polling.maxDrainLoopsPerPoll` | `1`, or `10` when drain mode is enabled | Max claim loops inside one poll cycle |
|
|
226
316
|
| `polling.drainLoopDelayMs` | `0` | Optional delay between drain loops |
|
|
317
|
+
| `retention.eventPayloadRetentionDays` | — | Replace terminal event payloads with `{}` after this many days. Disabled unless set. |
|
|
318
|
+
| `retention.deliveryResponseBodyRetentionDays` | — | Clear terminal delivery response bodies after this many days. Disabled unless set. |
|
|
319
|
+
| `retention.attemptResponseBodyRetentionDays` | — | Clear attempt response bodies after this many days. Disabled unless set. |
|
|
320
|
+
| `redaction.sanitizePayload` | — | Minimize payload before persistence and delivery. This changes delivered content. |
|
|
321
|
+
| `redaction.sanitizeResponseBody` | — | Sanitize or suppress response bodies before storing delivery and attempt rows. |
|
|
227
322
|
| `allowPrivateUrls` | `false` | Allow private/internal URLs (dev/test only) |
|
|
228
323
|
| `secretVault` | `PlaintextSecretVault` | Custom vault for encrypting/decrypting endpoint secrets at rest |
|
|
229
324
|
| `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. |
|
|
@@ -274,6 +369,52 @@ const summary = await deliveryRepository.getBacklogSummary?.();
|
|
|
274
369
|
|
|
275
370
|
The summary includes `pendingCount`, `sendingCount`, `runnablePendingCount`, `oldestPendingAgeMs`, and `oldestRunnableAgeMs`.
|
|
276
371
|
|
|
372
|
+
### Manual Retry, Bulk Retry, And Replay
|
|
373
|
+
|
|
374
|
+
```ts
|
|
375
|
+
await deliveryAdmin.retryDelivery(deliveryId, {
|
|
376
|
+
reason: 'customer requested retry',
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
await deliveryAdmin.retryFailedDeliveries({
|
|
380
|
+
endpointId,
|
|
381
|
+
eventType: 'order.created',
|
|
382
|
+
limit: 100,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
await deliveryAdmin.replayEvent(eventId, {
|
|
386
|
+
tenantId: 'tenant_123',
|
|
387
|
+
reason: 'customer support replay',
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Manual retries only requeue failed deliveries. Event replay reuses the original event ID and creates new delivery rows for currently active matching endpoints using their current URL and signing secret snapshots.
|
|
392
|
+
|
|
393
|
+
### Retention And Redaction
|
|
394
|
+
|
|
395
|
+
Retention is disabled unless configured. Applications can call `WebhookRetentionAdminService.purgeExpiredData()` from their own scheduler:
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
WebhookModule.forRoot({
|
|
399
|
+
prisma,
|
|
400
|
+
retention: {
|
|
401
|
+
eventPayloadRetentionDays: 30,
|
|
402
|
+
deliveryResponseBodyRetentionDays: 14,
|
|
403
|
+
attemptResponseBodyRetentionDays: 7,
|
|
404
|
+
},
|
|
405
|
+
redaction: {
|
|
406
|
+
sanitizePayload(payload) {
|
|
407
|
+
return { ...payload, email: undefined };
|
|
408
|
+
},
|
|
409
|
+
sanitizeResponseBody() {
|
|
410
|
+
return null;
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
`sanitizePayload` runs before persistence and dispatch, so it changes the delivered webhook payload. Review retention and redaction policies with your security and legal teams before storing sensitive data.
|
|
417
|
+
|
|
277
418
|
Webhook receiver responses are classified before scheduling another attempt:
|
|
278
419
|
|
|
279
420
|
| Response | Behavior |
|
|
@@ -412,6 +553,19 @@ const isValid = signer.verify(
|
|
|
412
553
|
);
|
|
413
554
|
```
|
|
414
555
|
|
|
556
|
+
To reject replayed signatures, use an explicit timestamp tolerance:
|
|
557
|
+
|
|
558
|
+
```ts
|
|
559
|
+
const isValidFreshRequest = signer.verifyWithTolerance(
|
|
560
|
+
headers['webhook-id'],
|
|
561
|
+
Number(headers['webhook-timestamp']),
|
|
562
|
+
rawBody,
|
|
563
|
+
signingSecret,
|
|
564
|
+
headers['webhook-signature'],
|
|
565
|
+
{ toleranceSeconds: 300 },
|
|
566
|
+
);
|
|
567
|
+
```
|
|
568
|
+
|
|
415
569
|
### Delivery audit data
|
|
416
570
|
|
|
417
571
|
Delivery logs expose the snapshotted destination URL through `DeliveryRecord.destinationUrl`. Per-attempt audit records are available through `WebhookDeliveryAdminService.getDeliveryAttempts(deliveryId)`:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { WebhookHttpClient } from '../ports/webhook-http-client';
|
|
1
|
+
import { WebhookHttpClient, WebhookHttpClientRequestOptions } from '../ports/webhook-http-client';
|
|
2
2
|
import { DeliveryResult } from '../interfaces/webhook-delivery.interface';
|
|
3
3
|
export declare class FetchHttpClient implements WebhookHttpClient {
|
|
4
|
-
post(url: string, headers: Record<string, string>, body: string, timeout: number): Promise<DeliveryResult>;
|
|
4
|
+
post(url: string, headers: Record<string, string>, body: string, timeout: number, options?: WebhookHttpClientRequestOptions): Promise<DeliveryResult>;
|
|
5
5
|
}
|
|
6
6
|
//# sourceMappingURL=fetch-http-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-http-client.d.ts","sourceRoot":"","sources":["../../src/adapters/fetch-http-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fetch-http-client.d.ts","sourceRoot":"","sources":["../../src/adapters/fetch-http-client.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,iBAAiB,EACjB,+BAA+B,EAChC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAW1E,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,EACf,OAAO,CAAC,EAAE,+BAA+B,GACxC,OAAO,CAAC,cAAc,CAAC;CAgF3B"}
|
|
@@ -1,56 +1,192 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
2
18
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
19
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
20
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
21
|
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
22
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
23
|
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
8
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
42
|
exports.FetchHttpClient = void 0;
|
|
10
43
|
const common_1 = require("@nestjs/common");
|
|
44
|
+
const http = __importStar(require("http"));
|
|
45
|
+
const https = __importStar(require("https"));
|
|
46
|
+
const net = __importStar(require("net"));
|
|
11
47
|
const webhook_constants_1 = require("../webhook.constants");
|
|
12
48
|
let FetchHttpClient = class FetchHttpClient {
|
|
13
|
-
async post(url, headers, body, timeout) {
|
|
14
|
-
const controller = new AbortController();
|
|
15
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
49
|
+
async post(url, headers, body, timeout, options) {
|
|
16
50
|
const start = Date.now();
|
|
17
|
-
|
|
18
|
-
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
let settled = false;
|
|
53
|
+
let timeoutId;
|
|
54
|
+
const parsed = new URL(url);
|
|
55
|
+
const isHttps = parsed.protocol === 'https:';
|
|
56
|
+
const transport = isHttps ? https : http;
|
|
57
|
+
const hostname = parsed.hostname.replace(/^\[|\]$/g, '');
|
|
58
|
+
const lookup = createPinnedLookup(options?.resolvedIpAddresses);
|
|
59
|
+
const finish = (result) => {
|
|
60
|
+
if (settled) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
settled = true;
|
|
64
|
+
if (timeoutId) {
|
|
65
|
+
clearTimeout(timeoutId);
|
|
66
|
+
}
|
|
67
|
+
resolve(result);
|
|
68
|
+
};
|
|
69
|
+
const request = transport.request({
|
|
70
|
+
protocol: parsed.protocol,
|
|
71
|
+
hostname,
|
|
72
|
+
port: parsed.port,
|
|
73
|
+
path: `${parsed.pathname}${parsed.search}`,
|
|
19
74
|
method: 'POST',
|
|
20
75
|
headers: {
|
|
21
76
|
'Content-Type': 'application/json',
|
|
22
77
|
'User-Agent': webhook_constants_1.DEFAULT_USER_AGENT,
|
|
23
78
|
...headers,
|
|
24
79
|
},
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
80
|
+
lookup,
|
|
81
|
+
servername: isHttps ? hostname : undefined,
|
|
82
|
+
}, (response) => {
|
|
83
|
+
const latencyMs = Date.now() - start;
|
|
84
|
+
readLimitedResponseBody(response)
|
|
85
|
+
.then((responseBody) => {
|
|
86
|
+
finish({
|
|
87
|
+
success: response.statusCode !== undefined &&
|
|
88
|
+
response.statusCode >= 200 &&
|
|
89
|
+
response.statusCode < 300,
|
|
90
|
+
statusCode: response.statusCode,
|
|
91
|
+
body: responseBody,
|
|
92
|
+
latencyMs,
|
|
93
|
+
});
|
|
94
|
+
})
|
|
95
|
+
.catch((error) => {
|
|
96
|
+
finish({
|
|
97
|
+
success: false,
|
|
98
|
+
latencyMs,
|
|
99
|
+
error: error instanceof Error ? error.message : String(error),
|
|
100
|
+
});
|
|
101
|
+
});
|
|
28
102
|
});
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
finally {
|
|
48
|
-
clearTimeout(timeoutId);
|
|
49
|
-
}
|
|
103
|
+
request.on('error', (error) => {
|
|
104
|
+
finish({
|
|
105
|
+
success: false,
|
|
106
|
+
latencyMs: Date.now() - start,
|
|
107
|
+
error: error.message,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
request.setTimeout(timeout, () => {
|
|
111
|
+
request.destroy(new Error('Request timed out'));
|
|
112
|
+
});
|
|
113
|
+
timeoutId = setTimeout(() => {
|
|
114
|
+
request.destroy(new Error('Request timed out'));
|
|
115
|
+
}, timeout);
|
|
116
|
+
request.write(body);
|
|
117
|
+
request.end();
|
|
118
|
+
});
|
|
50
119
|
}
|
|
51
120
|
};
|
|
52
121
|
exports.FetchHttpClient = FetchHttpClient;
|
|
53
122
|
exports.FetchHttpClient = FetchHttpClient = __decorate([
|
|
54
123
|
(0, common_1.Injectable)()
|
|
55
124
|
], FetchHttpClient);
|
|
125
|
+
function createPinnedLookup(addresses) {
|
|
126
|
+
const pinned = (addresses ?? [])
|
|
127
|
+
.map((address) => {
|
|
128
|
+
const family = net.isIP(address);
|
|
129
|
+
return family === 4 || family === 6 ? { address, family } : null;
|
|
130
|
+
})
|
|
131
|
+
.filter((address) => address !== null);
|
|
132
|
+
if (pinned.length === 0) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
return (_hostname, lookupOptions, callback) => {
|
|
136
|
+
const options = typeof lookupOptions === 'number'
|
|
137
|
+
? { family: lookupOptions, all: false }
|
|
138
|
+
: lookupOptions;
|
|
139
|
+
const family = options?.family === 4 || options?.family === 6
|
|
140
|
+
? options.family
|
|
141
|
+
: undefined;
|
|
142
|
+
const matches = family
|
|
143
|
+
? pinned.filter((address) => address.family === family)
|
|
144
|
+
: pinned;
|
|
145
|
+
if (matches.length === 0) {
|
|
146
|
+
const error = new Error('No prevalidated address matches requested family');
|
|
147
|
+
error.code = 'ENOTFOUND';
|
|
148
|
+
callback(error, '', family ?? 0);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (typeof options === 'object' && options.all) {
|
|
152
|
+
callback(null, matches, 0);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
callback(null, matches[0].address, matches[0].family);
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function readLimitedResponseBody(response) {
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
let body = '';
|
|
161
|
+
let settled = false;
|
|
162
|
+
const finish = (result) => {
|
|
163
|
+
if (settled) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
settled = true;
|
|
167
|
+
resolve(result);
|
|
168
|
+
};
|
|
169
|
+
response.setEncoding('utf8');
|
|
170
|
+
response.on('data', (chunk) => {
|
|
171
|
+
if (settled) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const remaining = webhook_constants_1.RESPONSE_BODY_MAX_LENGTH - body.length;
|
|
175
|
+
if (remaining > 0) {
|
|
176
|
+
body += chunk.slice(0, remaining);
|
|
177
|
+
}
|
|
178
|
+
if (body.length >= webhook_constants_1.RESPONSE_BODY_MAX_LENGTH) {
|
|
179
|
+
finish(body);
|
|
180
|
+
response.destroy();
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
response.on('end', () => finish(body));
|
|
184
|
+
response.on('error', (error) => {
|
|
185
|
+
if (!settled) {
|
|
186
|
+
settled = true;
|
|
187
|
+
reject(error);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
56
192
|
//# sourceMappingURL=fetch-http-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-http-client.js","sourceRoot":"","sources":["../../src/adapters/fetch-http-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fetch-http-client.js","sourceRoot":"","sources":["../../src/adapters/fetch-http-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ClaimedDelivery, DeliveryBacklogSummary, PendingDelivery, WebhookDeliveryRepository, WebhookTransaction } from '../ports/webhook-delivery.repository';
|
|
2
|
-
import { DeliveryAttemptRecord, DeliveryLogFilters, DeliveryRecord, DeliveryResult } from '../interfaces/webhook-delivery.interface';
|
|
2
|
+
import { DeliveryAttemptRecord, DeliveryLogFilters, DeliveryRecord, DeliveryResult, ReplayEventOptions, ReplayEventResult, RetryDeliveryOptions, RetryFailedDeliveriesFilters, RetryFailedDeliveriesResult, WebhookRetentionPurgeResult } from '../interfaces/webhook-delivery.interface';
|
|
3
|
+
import { WebhookRedactionOptions, WebhookRetentionOptions } from '../interfaces/webhook-options.interface';
|
|
3
4
|
import { WebhookSecretVault } from '../ports/webhook-secret-vault';
|
|
4
5
|
type AttemptLogClient = {
|
|
5
6
|
$executeRaw: <T = unknown>(strings: TemplateStringsArray, ...values: unknown[]) => Promise<T>;
|
|
@@ -7,7 +8,8 @@ type AttemptLogClient = {
|
|
|
7
8
|
export declare class PrismaDeliveryRepository implements WebhookDeliveryRepository {
|
|
8
9
|
protected readonly prisma: any;
|
|
9
10
|
protected readonly vault?: WebhookSecretVault | undefined;
|
|
10
|
-
|
|
11
|
+
protected readonly redaction?: WebhookRedactionOptions | undefined;
|
|
12
|
+
constructor(prisma: any, vault?: WebhookSecretVault | undefined, redaction?: WebhookRedactionOptions | undefined);
|
|
11
13
|
createDeliveriesInTransaction(tx: any, eventId: string, endpointIds: string[], maxAttempts: number): Promise<void>;
|
|
12
14
|
runInTransaction<T>(fn: (tx: WebhookTransaction) => Promise<T>): Promise<T>;
|
|
13
15
|
claimPendingDeliveries(batchSize: number): Promise<ClaimedDelivery[]>;
|
|
@@ -19,9 +21,13 @@ export declare class PrismaDeliveryRepository implements WebhookDeliveryReposito
|
|
|
19
21
|
getBacklogSummary(): Promise<DeliveryBacklogSummary>;
|
|
20
22
|
getDeliveryLogs(endpointId: string, filters?: DeliveryLogFilters): Promise<DeliveryRecord[]>;
|
|
21
23
|
getDeliveryAttempts(deliveryId: string): Promise<DeliveryAttemptRecord[]>;
|
|
22
|
-
retryDelivery(deliveryId: string): Promise<boolean>;
|
|
24
|
+
retryDelivery(deliveryId: string, _options?: RetryDeliveryOptions): Promise<boolean>;
|
|
25
|
+
retryFailedDeliveries(filters: RetryFailedDeliveriesFilters, _options?: RetryDeliveryOptions): Promise<RetryFailedDeliveriesResult>;
|
|
26
|
+
replayEvent(eventId: string, options?: ReplayEventOptions): Promise<ReplayEventResult>;
|
|
27
|
+
purgeExpiredData(options: WebhookRetentionOptions, now?: Date): Promise<WebhookRetentionPurgeResult>;
|
|
23
28
|
createTestDelivery(eventId: string, endpointId: string): Promise<void>;
|
|
24
29
|
protected appendAttemptLog(client: AttemptLogClient, deliveryId: string, attempts: number, status: 'PENDING' | 'SENT' | 'FAILED', result: DeliveryResult): Promise<void>;
|
|
30
|
+
private sanitizeDeliveryResult;
|
|
25
31
|
}
|
|
26
32
|
export {};
|
|
27
33
|
//# 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,sBAAsB,EACtB,eAAe,EACf,yBAAyB,EACzB,kBAAkB,EACnB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACd,cAAc,
|
|
1
|
+
{"version":3,"file":"prisma-delivery.repository.d.ts","sourceRoot":"","sources":["../../src/adapters/prisma-delivery.repository.ts"],"names":[],"mappings":"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"}
|