@nestarc/webhook 0.12.1 → 0.13.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 +97 -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 +9 -3
- 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
|
|
@@ -62,6 +65,14 @@ psql -d your_database -f node_modules/@nestarc/webhook/src/sql/migrations/v0.9.0
|
|
|
62
65
|
|
|
63
66
|
See [CHANGELOG.md](./CHANGELOG.md) for release-specific migration notes.
|
|
64
67
|
|
|
68
|
+
### Upgrading from versions before 0.13.0
|
|
69
|
+
|
|
70
|
+
Existing databases need the v0.13.0 additive migration for idempotent publish keys, correlation IDs, and payload purge metadata:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
psql -d your_database -f node_modules/@nestarc/webhook/src/sql/migrations/v0.13.0.sql
|
|
74
|
+
```
|
|
75
|
+
|
|
65
76
|
## Quick Start
|
|
66
77
|
|
|
67
78
|
### 1. Register the module
|
|
@@ -143,6 +154,17 @@ export class OrderService {
|
|
|
143
154
|
}
|
|
144
155
|
```
|
|
145
156
|
|
|
157
|
+
Use an idempotency key when the producer may retry the same business operation:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
await this.webhooks.send(new OrderCreatedEvent(order.id, order.total), {
|
|
161
|
+
idempotencyKey: `order:${order.id}:created`,
|
|
162
|
+
correlationId: requestId,
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Duplicate sends with the same tenant, event type, and idempotency key return the existing event ID and do not enqueue duplicate deliveries.
|
|
167
|
+
|
|
146
168
|
### 4. Manage endpoints
|
|
147
169
|
|
|
148
170
|
```typescript
|
|
@@ -174,6 +196,8 @@ export class WebhookController {
|
|
|
174
196
|
| `sendToTenant(tenantId, event)` | Publish to tenant-specific endpoints only |
|
|
175
197
|
| `sendToEndpoints(endpointIds, event)` | Publish to specific endpoint IDs only |
|
|
176
198
|
|
|
199
|
+
All publish methods accept optional `WebhookPublishOptions` with `idempotencyKey` and `correlationId`.
|
|
200
|
+
|
|
177
201
|
### WebhookEndpointAdminService
|
|
178
202
|
|
|
179
203
|
| Method | Description |
|
|
@@ -193,6 +217,14 @@ export class WebhookController {
|
|
|
193
217
|
| `getDeliveryLogs(endpointId, filters?)` | Query delivery history |
|
|
194
218
|
| `getDeliveryAttempts(deliveryId)` | Query per-attempt audit records for a delivery |
|
|
195
219
|
| `retryDelivery(deliveryId)` | Manually retry a failed delivery |
|
|
220
|
+
| `retryFailedDeliveries(filters, options?)` | Requeue matching failed deliveries in bulk |
|
|
221
|
+
| `replayEvent(eventId, options?)` | Create new delivery rows for an existing event and currently active endpoints |
|
|
222
|
+
|
|
223
|
+
### WebhookRetentionAdminService
|
|
224
|
+
|
|
225
|
+
| Method | Description |
|
|
226
|
+
|--------|-------------|
|
|
227
|
+
| `purgeExpiredData(now?)` | Apply configured retention policy and return purge counts |
|
|
196
228
|
|
|
197
229
|
### WebhookSigner
|
|
198
230
|
|
|
@@ -201,6 +233,7 @@ export class WebhookController {
|
|
|
201
233
|
| `sign(eventId, timestamp, body, secret)` | Generate Standard Webhooks signature headers |
|
|
202
234
|
| `signAll(eventId, timestamp, body, secrets[])` | Generate multi-signature headers for secret rotation overlap |
|
|
203
235
|
| `verify(eventId, timestamp, body, secret, signature)` | Verify a webhook signature |
|
|
236
|
+
| `verifyWithTolerance(eventId, timestamp, body, secret, signature, options)` | Verify a signature and reject timestamps outside `options.toleranceSeconds` |
|
|
204
237
|
| `generateSecret()` | Generate a random base64 signing secret |
|
|
205
238
|
|
|
206
239
|
> **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 +257,11 @@ export class WebhookController {
|
|
|
224
257
|
| `polling.drainWhileBacklogged` | `false` | Keep claiming additional batches inside one poll while backlog and capacity remain |
|
|
225
258
|
| `polling.maxDrainLoopsPerPoll` | `1`, or `10` when drain mode is enabled | Max claim loops inside one poll cycle |
|
|
226
259
|
| `polling.drainLoopDelayMs` | `0` | Optional delay between drain loops |
|
|
260
|
+
| `retention.eventPayloadRetentionDays` | — | Replace terminal event payloads with `{}` after this many days. Disabled unless set. |
|
|
261
|
+
| `retention.deliveryResponseBodyRetentionDays` | — | Clear terminal delivery response bodies after this many days. Disabled unless set. |
|
|
262
|
+
| `retention.attemptResponseBodyRetentionDays` | — | Clear attempt response bodies after this many days. Disabled unless set. |
|
|
263
|
+
| `redaction.sanitizePayload` | — | Minimize payload before persistence and delivery. This changes delivered content. |
|
|
264
|
+
| `redaction.sanitizeResponseBody` | — | Sanitize or suppress response bodies before storing delivery and attempt rows. |
|
|
227
265
|
| `allowPrivateUrls` | `false` | Allow private/internal URLs (dev/test only) |
|
|
228
266
|
| `secretVault` | `PlaintextSecretVault` | Custom vault for encrypting/decrypting endpoint secrets at rest |
|
|
229
267
|
| `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 +312,52 @@ const summary = await deliveryRepository.getBacklogSummary?.();
|
|
|
274
312
|
|
|
275
313
|
The summary includes `pendingCount`, `sendingCount`, `runnablePendingCount`, `oldestPendingAgeMs`, and `oldestRunnableAgeMs`.
|
|
276
314
|
|
|
315
|
+
### Manual Retry, Bulk Retry, And Replay
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
await deliveryAdmin.retryDelivery(deliveryId, {
|
|
319
|
+
reason: 'customer requested retry',
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
await deliveryAdmin.retryFailedDeliveries({
|
|
323
|
+
endpointId,
|
|
324
|
+
eventType: 'order.created',
|
|
325
|
+
limit: 100,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
await deliveryAdmin.replayEvent(eventId, {
|
|
329
|
+
tenantId: 'tenant_123',
|
|
330
|
+
reason: 'customer support replay',
|
|
331
|
+
});
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
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.
|
|
335
|
+
|
|
336
|
+
### Retention And Redaction
|
|
337
|
+
|
|
338
|
+
Retention is disabled unless configured. Applications can call `WebhookRetentionAdminService.purgeExpiredData()` from their own scheduler:
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
WebhookModule.forRoot({
|
|
342
|
+
prisma,
|
|
343
|
+
retention: {
|
|
344
|
+
eventPayloadRetentionDays: 30,
|
|
345
|
+
deliveryResponseBodyRetentionDays: 14,
|
|
346
|
+
attemptResponseBodyRetentionDays: 7,
|
|
347
|
+
},
|
|
348
|
+
redaction: {
|
|
349
|
+
sanitizePayload(payload) {
|
|
350
|
+
return { ...payload, email: undefined };
|
|
351
|
+
},
|
|
352
|
+
sanitizeResponseBody() {
|
|
353
|
+
return null;
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
`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.
|
|
360
|
+
|
|
277
361
|
Webhook receiver responses are classified before scheduling another attempt:
|
|
278
362
|
|
|
279
363
|
| Response | Behavior |
|
|
@@ -412,6 +496,19 @@ const isValid = signer.verify(
|
|
|
412
496
|
);
|
|
413
497
|
```
|
|
414
498
|
|
|
499
|
+
To reject replayed signatures, use an explicit timestamp tolerance:
|
|
500
|
+
|
|
501
|
+
```ts
|
|
502
|
+
const isValidFreshRequest = signer.verifyWithTolerance(
|
|
503
|
+
headers['webhook-id'],
|
|
504
|
+
Number(headers['webhook-timestamp']),
|
|
505
|
+
rawBody,
|
|
506
|
+
signingSecret,
|
|
507
|
+
headers['webhook-signature'],
|
|
508
|
+
{ toleranceSeconds: 300 },
|
|
509
|
+
);
|
|
510
|
+
```
|
|
511
|
+
|
|
415
512
|
### Delivery audit data
|
|
416
513
|
|
|
417
514
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2CAA6B;AAC7B,6CAA+B;AAC/B,yCAA2B;AAM3B,4DAG8B;AAQvB,IAAM,eAAe,GAArB,MAAM,eAAe;IAC1B,KAAK,CAAC,IAAI,CACR,GAAW,EACX,OAA+B,EAC/B,IAAY,EACZ,OAAe,EACf,OAAyC;QAEzC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEzB,OAAO,IAAI,OAAO,CAAiB,CAAC,OAAO,EAAE,EAAE;YAC7C,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,IAAI,SAAqC,CAAC;YAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC;YAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;YACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YACzD,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;YAEhE,MAAM,MAAM,GAAG,CAAC,MAAsB,EAAQ,EAAE;gBAC9C,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO;gBACT,CAAC;gBACD,OAAO,GAAG,IAAI,CAAC;gBACf,IAAI,SAAS,EAAE,CAAC;oBACd,YAAY,CAAC,SAAS,CAAC,CAAC;gBAC1B,CAAC;gBACD,OAAO,CAAC,MAAM,CAAC,CAAC;YAClB,CAAC,CAAC;YAEF,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAC/B;gBACE,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,QAAQ;gBACR,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,GAAG,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE;gBAC1C,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,YAAY,EAAE,sCAAkB;oBAChC,GAAG,OAAO;iBACX;gBACD,MAAM;gBACN,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;aAC3C,EACD,CAAC,QAAQ,EAAE,EAAE;gBACX,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;gBACrC,uBAAuB,CAAC,QAAQ,CAAC;qBAC9B,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE;oBACrB,MAAM,CAAC;wBACL,OAAO,EAAE,QAAQ,CAAC,UAAU,KAAK,SAAS;4BACxC,QAAQ,CAAC,UAAU,IAAI,GAAG;4BAC1B,QAAQ,CAAC,UAAU,GAAG,GAAG;wBAC3B,UAAU,EAAE,QAAQ,CAAC,UAAU;wBAC/B,IAAI,EAAE,YAAY;wBAClB,SAAS;qBACV,CAAC,CAAC;gBACL,CAAC,CAAC;qBACD,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;oBACxB,MAAM,CAAC;wBACL,OAAO,EAAE,KAAK;wBACd,SAAS;wBACT,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;qBAC9D,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;YACP,CAAC,CACF,CAAC;YAEF,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC5B,MAAM,CAAC;oBACL,OAAO,EAAE,KAAK;oBACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;oBAC7B,KAAK,EAAE,KAAK,CAAC,OAAO;iBACrB,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,EAAE;gBAC/B,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;YAClD,CAAC,CAAC,CAAC;YACH,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC1B,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;YAClD,CAAC,EAAE,OAAO,CAAC,CAAC;YAEZ,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACpB,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAvFY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;GACA,eAAe,CAuF3B;AAED,SAAS,kBAAkB,CACzB,SAAoB;IAEpB,MAAM,MAAM,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;SAC7B,GAAG,CAAC,CAAC,OAAO,EAAwB,EAAE;QACrC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjC,OAAO,MAAM,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACnE,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,OAAO,EAA4B,EAAE,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC;IAEnE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,CAAC,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE;QAC5C,MAAM,OAAO,GAAG,OAAO,aAAa,KAAK,QAAQ;YAC/C,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,KAAK,EAAE;YACvC,CAAC,CAAC,aAAa,CAAC;QAClB,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,KAAK,CAAC,IAAI,OAAO,EAAE,MAAM,KAAK,CAAC;YAC3D,CAAC,CAAC,OAAO,CAAC,MAAM;YAChB,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,OAAO,GAAG,MAAM;YACpB,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC;YACvD,CAAC,CAAC,MAAM,CAAC;QAEX,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,kDAAkD,CACnD,CAAC;YACxB,KAAK,CAAC,IAAI,GAAG,WAAW,CAAC;YACzB,QAAQ,CAAC,KAAK,EAAE,EAAE,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC;YACjC,OAAO;QACT,CAAC;QAED,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAC/C,QAAQ,CAAC,IAAI,EAAE,OAAgB,EAAE,CAAC,CAAC,CAAC;YACpC,OAAO;QACT,CAAC;QAED,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACxD,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAAC,QAA8B;IAC7D,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC7C,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,MAAM,GAAG,CAAC,MAAc,EAAQ,EAAE;YACtC,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO;YACT,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CAAC;QAEF,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC7B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACpC,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO;YACT,CAAC;YACD,MAAM,SAAS,GAAG,4CAAwB,GAAG,IAAI,CAAC,MAAM,CAAC;YACzD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;gBAClB,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;YACpC,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,IAAI,4CAAwB,EAAE,CAAC;gBAC5C,MAAM,CAAC,IAAI,CAAC,CAAC;gBACb,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACvC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -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":"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,EACd,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,4BAA4B,EAC5B,2BAA2B,EAC3B,2BAA2B,EAC5B,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAMnE,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;AA+DF,qBACa,wBAAyB,YAAW,yBAAyB;IAEtE,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG;IAC9B,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB;IAC7C,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,uBAAuB;gBAFnC,MAAM,EAAE,GAAG,EACX,KAAK,CAAC,EAAE,kBAAkB,YAAA,EAC1B,SAAS,CAAC,EAAE,uBAAuB,YAAA;IAGlD,6BAA6B,CACjC,EAAE,EAAE,GAAG,EACP,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EAAE,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAyCV,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;IAerF,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBvF,SAAS,CACb,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,IAAI,CAAC;IAgBV,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA2D9D,iBAAiB,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAwEpD,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,CACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,OAAO,CAAC;IAUb,qBAAqB,CACzB,OAAO,EAAE,4BAA4B,EACrC,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,2BAA2B,CAAC;IAoEjC,WAAW,CACf,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,iBAAiB,CAAC;IA8EvB,gBAAgB,CACpB,OAAO,EAAE,uBAAuB,EAChC,GAAG,OAAa,GACf,OAAO,CAAC,2BAA2B,CAAC;IAmEjC,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;IA2BhB,OAAO,CAAC,sBAAsB;CAoB/B"}
|