@voltro/plugin-webhooks 0.27.0 → 0.29.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/CHANGELOG.md +513 -0
- package/THIRD-PARTY-NOTICES.md +87 -1
- package/dist/index.d.ts +52 -7
- package/dist/index.js +124 -118
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -7,9 +7,9 @@ import { and as S, eq as C } from "@voltro/database";
|
|
|
7
7
|
import { assertPublicUrl as w } from "@voltro/integration-http";
|
|
8
8
|
import { publishServerError as ne } from "@voltro/protocol";
|
|
9
9
|
import { createLogger as T } from "@voltro/logger";
|
|
10
|
-
import { Activity as E, DurableClock as
|
|
10
|
+
import { Activity as E, DurableClock as D, Workflow as re } from "@effect/workflow";
|
|
11
11
|
//#region src/eventAudience.ts
|
|
12
|
-
var
|
|
12
|
+
var ie = (e) => e.webhook !== void 0, O = (e) => {
|
|
13
13
|
let t = e.webhook;
|
|
14
14
|
if (t !== void 0) return {
|
|
15
15
|
_tag: "outgoingEvent",
|
|
@@ -19,8 +19,8 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
19
19
|
...t.version === void 0 ? {} : { version: t.version },
|
|
20
20
|
...t.rateLimit === void 0 ? {} : { globalRateLimit: t.rateLimit }
|
|
21
21
|
};
|
|
22
|
-
},
|
|
23
|
-
let t =
|
|
22
|
+
}, ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, k = (e) => {
|
|
23
|
+
let t = ae.exec(e);
|
|
24
24
|
if (!t) throw Error(`invalid duration literal: ${e}`);
|
|
25
25
|
let n = Number(t[1]), r = t[2] ?? "s";
|
|
26
26
|
switch (r) {
|
|
@@ -31,7 +31,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
31
31
|
case "d": return n * 864e5;
|
|
32
32
|
default: throw Error(`unreachable unit: ${r}`);
|
|
33
33
|
}
|
|
34
|
-
},
|
|
34
|
+
}, A = [
|
|
35
35
|
408,
|
|
36
36
|
425,
|
|
37
37
|
429,
|
|
@@ -39,16 +39,16 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
39
39
|
502,
|
|
40
40
|
503,
|
|
41
41
|
504
|
|
42
|
-
],
|
|
43
|
-
if (t >= e.maxAttempts || n?.status !== void 0 && !(e.retryOn ??
|
|
42
|
+
], oe = (e, t, n) => {
|
|
43
|
+
if (t >= e.maxAttempts || n?.status !== void 0 && !(e.retryOn ?? A).includes(n.status)) return null;
|
|
44
44
|
if (e.honourRetryAfter !== !1 && n?.retryAfterSeconds !== void 0) {
|
|
45
|
-
let t = n.retryAfterSeconds * 1e3, r =
|
|
45
|
+
let t = n.retryAfterSeconds * 1e3, r = k(e.maxDelay), i = Math.min(t, r);
|
|
46
46
|
return {
|
|
47
47
|
delayMs: i,
|
|
48
48
|
baseDelayMs: i
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
-
let r =
|
|
51
|
+
let r = k(e.initialDelay), i = k(e.maxDelay), a;
|
|
52
52
|
switch (e.strategy) {
|
|
53
53
|
case "fixed":
|
|
54
54
|
a = r;
|
|
@@ -66,12 +66,12 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
66
66
|
delayMs: Math.max(1, Math.floor(o)),
|
|
67
67
|
baseDelayMs: a
|
|
68
68
|
};
|
|
69
|
-
},
|
|
69
|
+
}, se = () => ({
|
|
70
70
|
strategy: "exponential",
|
|
71
71
|
maxAttempts: 8,
|
|
72
72
|
initialDelay: "5s",
|
|
73
73
|
maxDelay: "1h",
|
|
74
|
-
retryOn:
|
|
74
|
+
retryOn: A,
|
|
75
75
|
honourRetryAfter: !0,
|
|
76
76
|
jitter: "full"
|
|
77
77
|
}), ce = () => ({
|
|
@@ -79,7 +79,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
79
79
|
maxAttempts: 120,
|
|
80
80
|
initialDelay: "30s",
|
|
81
81
|
maxDelay: "30s",
|
|
82
|
-
retryOn:
|
|
82
|
+
retryOn: A,
|
|
83
83
|
honourRetryAfter: !0,
|
|
84
84
|
jitter: "full"
|
|
85
85
|
}), j = class extends ee.Tag("@voltro/webhooks/WebhooksService")() {}, le = 32, M = 32, N = () => v(le).toString("hex"), P = (e) => e.events ?? (e.event === void 0 ? [] : [e.event]), F = (e) => {
|
|
@@ -149,7 +149,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
149
149
|
url: e.url,
|
|
150
150
|
secret: r ?? e.secret ?? N(),
|
|
151
151
|
signing: e.signing ?? t?.defaultSigning ?? c(),
|
|
152
|
-
retry: e.retry ?? t?.defaultRetry ??
|
|
152
|
+
retry: e.retry ?? t?.defaultRetry ?? se(),
|
|
153
153
|
filter: e.filter ?? null,
|
|
154
154
|
scope: e.scope ?? null,
|
|
155
155
|
headers: e.headers ?? null,
|
|
@@ -206,59 +206,72 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
206
206
|
if ("in" in n && Array.isArray(n.in)) return n.in.includes(e);
|
|
207
207
|
}
|
|
208
208
|
return e === t;
|
|
209
|
-
},
|
|
209
|
+
}, B = (e, t) => e !== null && Object.entries(t).every(([t, n]) => e[t] === n), V = 500, H = 200, U = async (e, t) => {
|
|
210
|
+
let n = [];
|
|
211
|
+
for (let r = 0; r < H; r += 1) {
|
|
212
|
+
let i = await e.store.query({
|
|
213
|
+
table: "_voltro_webhook_targets",
|
|
214
|
+
predicate: t,
|
|
215
|
+
order: [{
|
|
216
|
+
column: "id",
|
|
217
|
+
direction: "asc"
|
|
218
|
+
}],
|
|
219
|
+
take: V,
|
|
220
|
+
skip: r === 0 ? void 0 : r * V,
|
|
221
|
+
projection: void 0
|
|
222
|
+
});
|
|
223
|
+
if (n.push(...i), i.length < V) return n;
|
|
224
|
+
}
|
|
225
|
+
throw Error(`webhooks: more than ${H * V} rows in \`_voltro_webhook_targets\` — refusing to answer a scope lookup from a partial scan. A truncated scan mints a fresh secret for a live endpoint (breaking its signatures) or reports that a real scope matches nothing.`);
|
|
226
|
+
}, he = async (e, t) => {
|
|
227
|
+
let n = (await U(e)).filter((e) => B(e.scope, t));
|
|
228
|
+
if (n.length === 0) return;
|
|
229
|
+
let r = new Set(n.map((e) => e.secret));
|
|
230
|
+
if (r.size > 1) throw Error(`webhooks.subscribe: the scope ${JSON.stringify(t)} matches ${n.length} targets with ${r.size} different secrets, so it is not one endpoint. Pass \`secret\` explicitly, or narrow the scope.`);
|
|
231
|
+
return [...r][0];
|
|
232
|
+
}, ge = (e, t, n = {}) => {
|
|
210
233
|
let r = new Map((n.events ?? []).map((e) => [e.id, e])), i = {
|
|
211
234
|
subscribe: async (t) => {
|
|
212
235
|
F(t);
|
|
213
|
-
let n = P(t), i = t.secret ?? N(),
|
|
214
|
-
for (let t of
|
|
236
|
+
let n = P(t), i = t.secret === void 0 && t.scope !== void 0 ? await he(e, t.scope) : void 0, a = t.secret ?? i ?? N(), o = n.map((e) => I(t, r.get(e), e, a));
|
|
237
|
+
for (let t of o) await e.store.insert("_voltro_webhook_targets", {
|
|
215
238
|
...t,
|
|
216
239
|
createdAt: /* @__PURE__ */ new Date(),
|
|
217
240
|
updatedAt: /* @__PURE__ */ new Date()
|
|
218
241
|
});
|
|
219
|
-
let
|
|
242
|
+
let s = o[0];
|
|
220
243
|
return {
|
|
221
|
-
id:
|
|
222
|
-
event:
|
|
223
|
-
url:
|
|
224
|
-
secret:
|
|
225
|
-
signing:
|
|
226
|
-
retry:
|
|
227
|
-
...
|
|
244
|
+
id: s.id,
|
|
245
|
+
event: s.event,
|
|
246
|
+
url: s.url,
|
|
247
|
+
secret: s.secret,
|
|
248
|
+
signing: s.signing,
|
|
249
|
+
retry: s.retry,
|
|
250
|
+
...o.length > 1 ? { targets: o.map((e) => ({
|
|
228
251
|
id: e.id,
|
|
229
252
|
event: e.event
|
|
230
253
|
})) } : {}
|
|
231
254
|
};
|
|
232
255
|
},
|
|
233
|
-
emit: async (n, i) => {
|
|
234
|
-
let
|
|
235
|
-
if (
|
|
236
|
-
let e = x.decodeUnknownEither(
|
|
256
|
+
emit: async (n, i, a) => {
|
|
257
|
+
let o = typeof n == "object" && n.kind === "event" ? O(n) : void 0, s = typeof n == "string" ? r.get(n) : o ?? n, c = typeof n == "string" ? n : n.id ?? n.name;
|
|
258
|
+
if (s !== void 0) {
|
|
259
|
+
let e = x.decodeUnknownEither(s.payload)(i, { errors: "all" });
|
|
237
260
|
if (te.isLeft(e)) throw new u({
|
|
238
|
-
event:
|
|
261
|
+
event: c,
|
|
239
262
|
issues: e.left.message
|
|
240
263
|
});
|
|
241
264
|
}
|
|
242
|
-
let
|
|
243
|
-
|
|
244
|
-
predicate: C("event", s),
|
|
245
|
-
order: [{
|
|
246
|
-
column: "createdAt",
|
|
247
|
-
direction: "asc"
|
|
248
|
-
}],
|
|
249
|
-
take: 1e3,
|
|
250
|
-
skip: void 0,
|
|
251
|
-
projection: void 0
|
|
252
|
-
})).filter((e) => z(e.filter, i)), d = [], f = JSON.stringify(i);
|
|
253
|
-
for (let n of l) {
|
|
265
|
+
let l = y(), d = a?.tenantId, f = (await U(e, d == null ? C("event", c) : S(C("event", c), C("tenantId", d)))).filter((e) => z(e.filter, i)), p = [], m = JSON.stringify(i);
|
|
266
|
+
for (let n of f) {
|
|
254
267
|
let r = y();
|
|
255
268
|
n.active ? (await t({
|
|
256
269
|
deliveryId: r,
|
|
257
270
|
targetId: n.id,
|
|
258
|
-
event:
|
|
259
|
-
eventId:
|
|
260
|
-
payloadJson:
|
|
261
|
-
}),
|
|
271
|
+
event: c,
|
|
272
|
+
eventId: l,
|
|
273
|
+
payloadJson: m
|
|
274
|
+
}), p.push({
|
|
262
275
|
targetId: n.id,
|
|
263
276
|
deliveryId: r,
|
|
264
277
|
status: "dispatched"
|
|
@@ -267,23 +280,23 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
267
280
|
deliveryId: r,
|
|
268
281
|
targetId: n.id,
|
|
269
282
|
tenantId: n.tenantId ?? null,
|
|
270
|
-
event:
|
|
271
|
-
eventId:
|
|
283
|
+
event: c,
|
|
284
|
+
eventId: l,
|
|
272
285
|
attempt: 1,
|
|
273
286
|
status: "pending",
|
|
274
|
-
payload:
|
|
287
|
+
payload: m,
|
|
275
288
|
scheduledAt: /* @__PURE__ */ new Date(),
|
|
276
289
|
createdAt: /* @__PURE__ */ new Date(),
|
|
277
290
|
updatedAt: /* @__PURE__ */ new Date()
|
|
278
|
-
}),
|
|
291
|
+
}), p.push({
|
|
279
292
|
targetId: n.id,
|
|
280
293
|
deliveryId: r,
|
|
281
294
|
status: "queued"
|
|
282
295
|
}));
|
|
283
296
|
}
|
|
284
297
|
return {
|
|
285
|
-
eventId:
|
|
286
|
-
deliveries:
|
|
298
|
+
eventId: l,
|
|
299
|
+
deliveries: p
|
|
287
300
|
};
|
|
288
301
|
},
|
|
289
302
|
replay: async (n) => {
|
|
@@ -310,14 +323,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
310
323
|
},
|
|
311
324
|
resolveTargets: async (t) => {
|
|
312
325
|
if (typeof t == "string") return [t];
|
|
313
|
-
let n = (await e.
|
|
314
|
-
table: "_voltro_webhook_targets",
|
|
315
|
-
predicate: void 0,
|
|
316
|
-
order: void 0,
|
|
317
|
-
take: 1e3,
|
|
318
|
-
skip: void 0,
|
|
319
|
-
projection: void 0
|
|
320
|
-
})).filter((e) => e.scope !== null && Object.entries(t.scope).every(([t, n]) => e.scope?.[t] === n)).map((e) => e.id);
|
|
326
|
+
let n = (await U(e)).filter((e) => B(e.scope, t.scope)).map((e) => e.id);
|
|
321
327
|
if (n.length === 0) throw Error(`webhooks: no target matches scope ${JSON.stringify(t.scope)} — the operation would have silently done nothing`);
|
|
322
328
|
return n;
|
|
323
329
|
},
|
|
@@ -487,15 +493,15 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
487
493
|
}
|
|
488
494
|
};
|
|
489
495
|
return i;
|
|
490
|
-
},
|
|
496
|
+
}, _e = b.gen(function* () {
|
|
491
497
|
return yield* j;
|
|
492
|
-
}),
|
|
498
|
+
}), ve = (e) => {
|
|
493
499
|
if (e.webhooks === void 0) throw Error("useWebhooks: `ctx.webhooks` is not set. Add @voltro/plugin-webhooks to your app.config.ts plugin list and declare at least one *.webhook.tsx file.");
|
|
494
500
|
return e.webhooks;
|
|
495
|
-
},
|
|
501
|
+
}, ye = 1e4, W = class {
|
|
496
502
|
capacity;
|
|
497
503
|
cache = /* @__PURE__ */ new Map();
|
|
498
|
-
constructor(e =
|
|
504
|
+
constructor(e = ye) {
|
|
499
505
|
this.capacity = e;
|
|
500
506
|
}
|
|
501
507
|
evictExpired() {
|
|
@@ -530,19 +536,19 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
530
536
|
clear() {
|
|
531
537
|
this.cache.clear();
|
|
532
538
|
}
|
|
533
|
-
},
|
|
539
|
+
}, G = null, K = () => (G === null && (G = new W()), G), q = {
|
|
534
540
|
"5m": 5 * 6e4,
|
|
535
541
|
"1h": 60 * 6e4,
|
|
536
542
|
"6h": 360 * 6e4,
|
|
537
543
|
"1d": 1440 * 6e4,
|
|
538
544
|
"7d": 10080 * 6e4,
|
|
539
545
|
"30d": 720 * 60 * 6e4
|
|
540
|
-
},
|
|
546
|
+
}, J = (e) => e === void 0 ? q["7d"] : q[e] ?? q["7d"], Y = (e, t) => ({
|
|
541
547
|
status: e,
|
|
542
548
|
contentType: "application/json; charset=utf-8",
|
|
543
549
|
body: JSON.stringify(t)
|
|
544
|
-
}),
|
|
545
|
-
let n = e.provider, r = e.signature ?? n?.signature ?? null, i = e.idempotency ?? n?.idempotency ?? null, o = e.bodyType ?? n?.bodyType ?? "json", s = t.idempotencyCache ??
|
|
550
|
+
}), be = (e, t) => {
|
|
551
|
+
let n = e.provider, r = e.signature ?? n?.signature ?? null, i = e.idempotency ?? n?.idempotency ?? null, o = e.bodyType ?? n?.bodyType ?? "json", s = t.idempotencyCache ?? K(), c = J(i?.ttl), l = t.log ?? ((e) => {
|
|
546
552
|
T({ scope: `webhook:${e.webhookId}` }).debug("incoming webhook", {
|
|
547
553
|
status: e.status,
|
|
548
554
|
signatureOk: e.signatureOk,
|
|
@@ -554,7 +560,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
554
560
|
return async (n) => {
|
|
555
561
|
let u = Date.now();
|
|
556
562
|
if (n.method !== "POST") {
|
|
557
|
-
let t =
|
|
563
|
+
let t = Y(405, {
|
|
558
564
|
error: "method not allowed",
|
|
559
565
|
expected: "POST"
|
|
560
566
|
});
|
|
@@ -575,7 +581,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
575
581
|
r._tag === "custom" && n.headers["x-slack-request-timestamp"] && (o = `${n.headers["x-slack-request-timestamp"]}|${t ?? ""}`);
|
|
576
582
|
let s = a(r, n.rawBody, i, o);
|
|
577
583
|
if (d = s.ok, !s.ok) {
|
|
578
|
-
let t =
|
|
584
|
+
let t = Y(401, {
|
|
579
585
|
error: "invalid signature",
|
|
580
586
|
reason: s.reason
|
|
581
587
|
});
|
|
@@ -601,7 +607,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
601
607
|
f = r;
|
|
602
608
|
} else f = n.rawBody;
|
|
603
609
|
} catch (t) {
|
|
604
|
-
let n =
|
|
610
|
+
let n = Y(400, {
|
|
605
611
|
error: "malformed body",
|
|
606
612
|
reason: t.message
|
|
607
613
|
});
|
|
@@ -618,7 +624,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
618
624
|
try {
|
|
619
625
|
p = x.decodeUnknownSync(e.payload)(f);
|
|
620
626
|
} catch (t) {
|
|
621
|
-
let n =
|
|
627
|
+
let n = Y(422, {
|
|
622
628
|
error: "schema validation failed",
|
|
623
629
|
reason: t.message
|
|
624
630
|
});
|
|
@@ -637,7 +643,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
637
643
|
if (m = typeof t == "string" ? n.headers[t.toLowerCase()] : t(n.headers, n.rawBody), m !== void 0 && m.length > 0) {
|
|
638
644
|
let t = s.claim(m, c);
|
|
639
645
|
if (h = t, t === "duplicate") {
|
|
640
|
-
let t =
|
|
646
|
+
let t = Y(200, {
|
|
641
647
|
received: !0,
|
|
642
648
|
duplicate: !0
|
|
643
649
|
});
|
|
@@ -650,7 +656,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
650
656
|
}), t;
|
|
651
657
|
}
|
|
652
658
|
if (t === "inflight") {
|
|
653
|
-
let t =
|
|
659
|
+
let t = Y(409, {
|
|
654
660
|
error: "request already in flight",
|
|
655
661
|
idempotencyKey: m
|
|
656
662
|
});
|
|
@@ -673,7 +679,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
673
679
|
idempotencyKey: m ?? "",
|
|
674
680
|
...r ? { workflows: r } : {}
|
|
675
681
|
}), m !== void 0 && s.commit(m);
|
|
676
|
-
let i =
|
|
682
|
+
let i = Y(200, { received: !0 });
|
|
677
683
|
return l({
|
|
678
684
|
webhookId: e.id,
|
|
679
685
|
status: 200,
|
|
@@ -683,7 +689,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
683
689
|
}), i;
|
|
684
690
|
} catch (t) {
|
|
685
691
|
m !== void 0 && s.release(m);
|
|
686
|
-
let n =
|
|
692
|
+
let n = Y(500, {
|
|
687
693
|
error: "handler threw",
|
|
688
694
|
reason: t.message
|
|
689
695
|
});
|
|
@@ -702,20 +708,20 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
702
708
|
}), n;
|
|
703
709
|
}
|
|
704
710
|
};
|
|
705
|
-
},
|
|
711
|
+
}, xe = (e) => `/webhooks/${e}`, X = "_voltro_webhook_rate_windows", Se = 6e4, Ce = (e, t) => `${e}@${t}`, we = async (e, t, n) => (await e.query({
|
|
706
712
|
table: "_voltro_webhook_rate_windows",
|
|
707
713
|
predicate: S(C("scope", t), C("bucket", n)),
|
|
708
714
|
order: [],
|
|
709
715
|
take: 1,
|
|
710
716
|
skip: void 0,
|
|
711
717
|
projection: void 0
|
|
712
|
-
}))[0] ?? null,
|
|
713
|
-
for (let r = 0; r <
|
|
718
|
+
}))[0] ?? null, Z = 32, Te = async (e, t, n) => {
|
|
719
|
+
for (let r = 0; r < Z; r++) {
|
|
714
720
|
r > 0 && await new Promise((e) => setTimeout(e, r));
|
|
715
|
-
let i = await
|
|
721
|
+
let i = await we(e, t.key, n);
|
|
716
722
|
if (i === null) {
|
|
717
|
-
await e.insertIgnore(
|
|
718
|
-
id:
|
|
723
|
+
await e.insertIgnore(X, {
|
|
724
|
+
id: Ce(t.key, n),
|
|
719
725
|
scope: t.key,
|
|
720
726
|
bucket: n,
|
|
721
727
|
count: 0,
|
|
@@ -730,29 +736,29 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
730
736
|
updatedAt: /* @__PURE__ */ new Date()
|
|
731
737
|
}, { where: S(C("id", i.id), C("count", i.count)) }) === 1) return !0;
|
|
732
738
|
}
|
|
733
|
-
throw Error(`webhook rate window: contention on (${t.key}, ${n}) exceeded ${
|
|
734
|
-
},
|
|
735
|
-
for (let r = 0; r <
|
|
739
|
+
throw Error(`webhook rate window: contention on (${t.key}, ${n}) exceeded ${Z} attempts`);
|
|
740
|
+
}, Ee = async (e, t, n) => {
|
|
741
|
+
for (let r = 0; r < Z; r++) {
|
|
736
742
|
r > 0 && await new Promise((e) => setTimeout(e, r));
|
|
737
|
-
let i = await
|
|
743
|
+
let i = await we(e, t, n);
|
|
738
744
|
if (i === null || i.count <= 0 || await e.updateMany("_voltro_webhook_rate_windows", {
|
|
739
745
|
count: i.count - 1,
|
|
740
746
|
updatedAt: /* @__PURE__ */ new Date()
|
|
741
747
|
}, { where: S(C("id", i.id), C("count", i.count)) }) === 1) return;
|
|
742
748
|
}
|
|
743
|
-
throw Error(`webhook rate window: release contention on (${t}) exceeded ${
|
|
744
|
-
},
|
|
749
|
+
throw Error(`webhook rate window: release contention on (${t}) exceeded ${Z} attempts`);
|
|
750
|
+
}, De = async (e, t, n, r = Se) => {
|
|
745
751
|
if (t.length === 0) return {
|
|
746
752
|
acquired: !0,
|
|
747
753
|
retryInMs: 0
|
|
748
754
|
};
|
|
749
755
|
let i = Math.floor(n / r), a = [];
|
|
750
756
|
for (let o of t) {
|
|
751
|
-
if (await
|
|
757
|
+
if (await Te(e, o, i)) {
|
|
752
758
|
a.push(o);
|
|
753
759
|
continue;
|
|
754
760
|
}
|
|
755
|
-
for (let t of a) await
|
|
761
|
+
for (let t of a) await Ee(e, t.key, i);
|
|
756
762
|
return {
|
|
757
763
|
acquired: !1,
|
|
758
764
|
retryInMs: (i + 1) * r - n
|
|
@@ -762,7 +768,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
762
768
|
acquired: !0,
|
|
763
769
|
retryInMs: 0
|
|
764
770
|
};
|
|
765
|
-
},
|
|
771
|
+
}, Oe = (e, t) => {
|
|
766
772
|
if (e === "json") return {
|
|
767
773
|
contentType: "application/json",
|
|
768
774
|
bytes: Buffer.from(t, "utf8")
|
|
@@ -778,15 +784,15 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
778
784
|
}
|
|
779
785
|
return e === "form" ? {
|
|
780
786
|
contentType: "application/x-www-form-urlencoded",
|
|
781
|
-
bytes: Buffer.from(
|
|
787
|
+
bytes: Buffer.from(ke(n), "utf8")
|
|
782
788
|
} : {
|
|
783
789
|
contentType: "application/xml",
|
|
784
|
-
bytes: Buffer.from(
|
|
790
|
+
bytes: Buffer.from(je(n), "utf8")
|
|
785
791
|
};
|
|
786
|
-
},
|
|
792
|
+
}, ke = (e) => {
|
|
787
793
|
if (typeof e != "object" || !e || Array.isArray(e)) throw new d({
|
|
788
794
|
format: "form",
|
|
789
|
-
reason: `form encoding requires a JSON object at the top level, got ${
|
|
795
|
+
reason: `form encoding requires a JSON object at the top level, got ${Pe(e)}`
|
|
790
796
|
});
|
|
791
797
|
let t = new URLSearchParams();
|
|
792
798
|
for (let [n, r] of Object.entries(e)) Q(t, n, r);
|
|
@@ -805,13 +811,13 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
805
811
|
return;
|
|
806
812
|
}
|
|
807
813
|
e.append(t, String(n));
|
|
808
|
-
},
|
|
809
|
-
if (!
|
|
814
|
+
}, Ae = /^[A-Za-z_][A-Za-z0-9_.-]*$/, je = (e) => `<?xml version="1.0" encoding="UTF-8"?>${$("webhook", e)}`, $ = (e, t) => {
|
|
815
|
+
if (!Ae.test(e)) throw new d({
|
|
810
816
|
format: "xml",
|
|
811
817
|
reason: `object key ${JSON.stringify(e)} is not a valid XML element name`
|
|
812
818
|
});
|
|
813
|
-
return `<${e}>${
|
|
814
|
-
},
|
|
819
|
+
return `<${e}>${Me(e, t)}</${e}>`;
|
|
820
|
+
}, Me = (e, t) => t == null ? "" : Array.isArray(t) ? t.map((e) => $("item", e)).join("") : typeof t == "object" ? Object.entries(t).map(([e, t]) => $(e, t)).join("") : Ne(String(t)), Ne = (e) => e.replace(/[&<>"']/g, (e) => {
|
|
815
821
|
switch (e) {
|
|
816
822
|
case "&": return "&";
|
|
817
823
|
case "<": return "<";
|
|
@@ -819,17 +825,17 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
819
825
|
case "\"": return """;
|
|
820
826
|
default: return "'";
|
|
821
827
|
}
|
|
822
|
-
}),
|
|
828
|
+
}), Pe = (e) => e === null ? "null" : Array.isArray(e) ? "array" : typeof e, Fe = "_voltro_webhook_targets", Ie = 32, Le = async (e, t) => (await e.query({
|
|
823
829
|
table: "_voltro_webhook_targets",
|
|
824
830
|
predicate: C("id", t),
|
|
825
831
|
order: [],
|
|
826
832
|
take: 1,
|
|
827
833
|
skip: void 0,
|
|
828
834
|
projection: void 0
|
|
829
|
-
}))[0] ?? null,
|
|
830
|
-
for (let a = 0; a <
|
|
835
|
+
}))[0] ?? null, Re = async (e, t, n, r, i = /* @__PURE__ */ new Date()) => {
|
|
836
|
+
for (let a = 0; a < Ie; a++) {
|
|
831
837
|
a > 0 && await new Promise((e) => setTimeout(e, a));
|
|
832
|
-
let o = await
|
|
838
|
+
let o = await Le(e, t);
|
|
833
839
|
if (o === null) return {
|
|
834
840
|
consecutiveFailures: 0,
|
|
835
841
|
autoDisabled: !1
|
|
@@ -854,24 +860,24 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
854
860
|
autoDisabled: u
|
|
855
861
|
};
|
|
856
862
|
}
|
|
857
|
-
throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${
|
|
858
|
-
},
|
|
863
|
+
throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${Ie} attempts`);
|
|
864
|
+
}, ze = x.Struct({
|
|
859
865
|
status: x.Number,
|
|
860
866
|
body: x.String,
|
|
861
867
|
latencyMs: x.Number,
|
|
862
868
|
retryAfterSec: x.Union(x.Number, x.Undefined),
|
|
863
869
|
transportError: x.Union(x.String, x.Undefined),
|
|
864
870
|
encodeError: x.Union(x.String, x.Undefined)
|
|
865
|
-
}),
|
|
871
|
+
}), Be = x.Struct({
|
|
866
872
|
outcome: x.Literal("succeeded", "failed", "retry"),
|
|
867
873
|
delayMs: x.Union(x.Number, x.Undefined),
|
|
868
874
|
reason: x.Union(x.String, x.Undefined),
|
|
869
875
|
autoDisabled: x.Union(x.Boolean, x.Undefined),
|
|
870
876
|
consecutiveFailures: x.Union(x.Number, x.Undefined)
|
|
871
|
-
}),
|
|
877
|
+
}), Ve = x.Struct({
|
|
872
878
|
acquired: x.Boolean,
|
|
873
879
|
retryInMs: x.Number
|
|
874
|
-
}),
|
|
880
|
+
}), He = re.make({
|
|
875
881
|
name: "voltro.deliverWebhook",
|
|
876
882
|
payload: {
|
|
877
883
|
deliveryId: x.String,
|
|
@@ -886,7 +892,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
886
892
|
attempts: x.Number
|
|
887
893
|
}),
|
|
888
894
|
idempotencyKey: ({ deliveryId: e, attemptEpoch: t }) => `voltro.deliverWebhook:${e}:${t ?? 0}`
|
|
889
|
-
}),
|
|
895
|
+
}), Ue = (e, t = {}) => (n, r) => {
|
|
890
896
|
let { deliveryId: i, targetId: a, event: s, eventId: c, payloadJson: l } = n, u = T({ scope: `webhook:${i}` });
|
|
891
897
|
return b.gen(function* () {
|
|
892
898
|
let n = yield* E.make({
|
|
@@ -972,9 +978,9 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
972
978
|
if (v.length > 0) for (let n = 0;; n++) {
|
|
973
979
|
let i = yield* E.make({
|
|
974
980
|
name: `rate-acquire-${t}-${n}`,
|
|
975
|
-
success:
|
|
981
|
+
success: Ve,
|
|
976
982
|
execute: b.tryPromise({
|
|
977
|
-
try: () =>
|
|
983
|
+
try: () => De(e.store, v, Date.now(), g),
|
|
978
984
|
catch: (e) => (u.debug("rate-acquire raw error", {
|
|
979
985
|
targetId: a,
|
|
980
986
|
event: s,
|
|
@@ -994,7 +1000,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
994
1000
|
}),
|
|
995
1001
|
catch: (e) => /* @__PURE__ */ Error(`record-rate-deferral failed: ${e.message}`)
|
|
996
1002
|
}).pipe(b.orDie)
|
|
997
|
-
}), yield*
|
|
1003
|
+
}), yield* D.sleep({
|
|
998
1004
|
name: `rate-window-sleep-${t}-${n}`,
|
|
999
1005
|
duration: `${i.retryInMs} millis`
|
|
1000
1006
|
});
|
|
@@ -1013,11 +1019,11 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
1013
1019
|
});
|
|
1014
1020
|
let _ = yield* E.make({
|
|
1015
1021
|
name: `sign-and-post-${t}`,
|
|
1016
|
-
success:
|
|
1022
|
+
success: ze,
|
|
1017
1023
|
execute: b.promise(async () => {
|
|
1018
1024
|
let e = Date.now(), r;
|
|
1019
1025
|
try {
|
|
1020
|
-
r =
|
|
1026
|
+
r = Oe(n.format, l);
|
|
1021
1027
|
} catch (t) {
|
|
1022
1028
|
if (t instanceof d) return {
|
|
1023
1029
|
status: 0,
|
|
@@ -1067,10 +1073,10 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
1067
1073
|
})
|
|
1068
1074
|
}), y = yield* E.make({
|
|
1069
1075
|
name: `classify-and-record-${t}`,
|
|
1070
|
-
success:
|
|
1076
|
+
success: Be,
|
|
1071
1077
|
execute: b.tryPromise({
|
|
1072
1078
|
try: async () => {
|
|
1073
|
-
let r = _.status >= 200 && _.status < 300, i = _.transportError !== void 0, o = _.encodeError !== void 0, s = (t, n) =>
|
|
1079
|
+
let r = _.status >= 200 && _.status < 300, i = _.transportError !== void 0, o = _.encodeError !== void 0, s = (t, n) => Re(e.store, a, t, n);
|
|
1074
1080
|
if (r) {
|
|
1075
1081
|
await e.store.update("_voltro_webhook_deliveries", f, {
|
|
1076
1082
|
status: "succeeded",
|
|
@@ -1088,7 +1094,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
1088
1094
|
consecutiveFailures: t.consecutiveFailures
|
|
1089
1095
|
};
|
|
1090
1096
|
}
|
|
1091
|
-
let c = o ? null :
|
|
1097
|
+
let c = o ? null : oe(p, t, {
|
|
1092
1098
|
...i ? {} : { status: _.status },
|
|
1093
1099
|
..._.retryAfterSec === void 0 ? {} : { retryAfterSeconds: _.retryAfterSec }
|
|
1094
1100
|
});
|
|
@@ -1148,7 +1154,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
1148
1154
|
finalStatus: "failed",
|
|
1149
1155
|
attempts: t
|
|
1150
1156
|
};
|
|
1151
|
-
y.delayMs !== void 0 && (yield*
|
|
1157
|
+
y.delayMs !== void 0 && (yield* D.sleep({
|
|
1152
1158
|
name: `retry-sleep-${t}`,
|
|
1153
1159
|
duration: `${y.delayMs} millis`
|
|
1154
1160
|
}));
|
|
@@ -1160,4 +1166,4 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
|
1160
1166
|
});
|
|
1161
1167
|
};
|
|
1162
1168
|
//#endregion
|
|
1163
|
-
export {
|
|
1169
|
+
export { W as IdempotencyCache, Se as RATE_WINDOW_MS, X as RATE_WINDOW_TABLE, Fe as TARGETS_TABLE, l as WebhookDeliveryNotFound, u as WebhookPayloadInvalid, d as WebhookPayloadUnrepresentable, f as WebhookPayloadVersionInvalid, p as WebhookSubscribeInvalid, j as WebhooksService, m as _voltroWebhookDeliveriesTable, h as _voltroWebhookRateWindowsTable, g as _voltroWebhookTargetsTable, De as acquireRateSlots, O as asOutgoingEvent, Ue as buildDeliverWebhookExecute, ge as buildWebhooksService, fe as compareVersions, Te as consumeRateSlot, xe as defaultIncomingPath, c as defaultOutgoingSignature, se as defaultRetryPolicy, s as defineIncomingWebhook, t as defineWebhookProvider, He as deliverWebhookWorkflow, Oe as encodePayload, P as eventsOf, ce as fastRetryPolicy, N as generateSecret, K as getIdempotencyCache, i as githubSignature, ie as hasWebhookAudience, r as isWebhookDescriptor, z as matchesFilter, be as mountIncomingWebhook, oe as nextRetry, k as parseDuration, J as parseTtl, Re as recordDeliveryOutcome, Ee as releaseRateSlot, I as resolveSubscribe, o as signPayload, n as slackSignature, e as stripeSignature, ve as useWebhooks, _e as useWebhooksEffect, F as validateSubscribe, a as verifySignature, _ as webhookTables };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/plugin-webhooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Webhooks plugin — first-class outgoing (multi-target, durable-workflow delivery, HMAC signing, retry policies, rate limits) + incoming (signature verification, idempotency, provider templates for Stripe / GitHub / Slack / generic).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@effect/workflow": "^0.19.0",
|
|
51
|
-
"@voltro/database": "0.
|
|
52
|
-
"@voltro/integration-http": "0.
|
|
53
|
-
"@voltro/logger": "0.
|
|
54
|
-
"@voltro/plugin-multitenancy": "0.
|
|
55
|
-
"@voltro/protocol": "0.
|
|
56
|
-
"@voltro/runtime": "0.
|
|
51
|
+
"@voltro/database": "0.29.0",
|
|
52
|
+
"@voltro/integration-http": "0.29.0",
|
|
53
|
+
"@voltro/logger": "0.29.0",
|
|
54
|
+
"@voltro/plugin-multitenancy": "0.29.0",
|
|
55
|
+
"@voltro/protocol": "0.29.0",
|
|
56
|
+
"@voltro/runtime": "0.29.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"effect": "^3.22.0"
|