@voltro/plugin-webhooks 0.30.2 → 0.32.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 +278 -0
- package/dist/index.d.ts +28 -4
- package/dist/index.js +225 -220
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c } from "./signing-DUG4JWwk.js";
|
|
2
2
|
import { WebhookDeliveryNotFound as l, WebhookPayloadInvalid as u, WebhookPayloadUnrepresentable as d, WebhookPayloadVersionInvalid as f, WebhookSubscribeInvalid as p } from "./errors.js";
|
|
3
|
-
import { _voltroWebhookDeliveriesTable as m, _voltroWebhookEventStatsTable as h, _voltroWebhookRateWindowsTable as
|
|
4
|
-
import { randomBytes as
|
|
5
|
-
import { Context as
|
|
6
|
-
import { and as
|
|
7
|
-
import { assertPublicUrl as
|
|
8
|
-
import { publishServerError as
|
|
9
|
-
import { createLogger as
|
|
10
|
-
import { Activity as
|
|
11
|
-
import { defineOutboxHandler as
|
|
3
|
+
import { _voltroWebhookDeliveriesTable as m, _voltroWebhookEventStatsTable as h, _voltroWebhookRateWindowsTable as g, _voltroWebhookTargetsTable as _, webhookTables as v } from "./mixin.js";
|
|
4
|
+
import { randomBytes as y, randomUUID as b } from "node:crypto";
|
|
5
|
+
import { Context as ee, Effect as x, Either as te, Schema as S } from "effect";
|
|
6
|
+
import { and as C, eq as w } from "@voltro/database";
|
|
7
|
+
import { assertPublicUrl as T } from "@voltro/integration-http";
|
|
8
|
+
import { publishServerError as ne } from "@voltro/protocol";
|
|
9
|
+
import { createLogger as E } from "@voltro/logger";
|
|
10
|
+
import { Activity as D, DurableClock as O, Workflow as re } from "@effect/workflow";
|
|
11
|
+
import { defineOutboxHandler as ie } from "@voltro/runtime";
|
|
12
12
|
//#region src/eventAudience.ts
|
|
13
|
-
var
|
|
13
|
+
var ae = (e) => e.webhook !== void 0, k = (e) => {
|
|
14
14
|
let t = e.webhook;
|
|
15
15
|
if (t !== void 0) return {
|
|
16
16
|
_tag: "outgoingEvent",
|
|
@@ -20,8 +20,8 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
20
20
|
...t.version === void 0 ? {} : { version: t.version },
|
|
21
21
|
...t.rateLimit === void 0 ? {} : { globalRateLimit: t.rateLimit }
|
|
22
22
|
};
|
|
23
|
-
},
|
|
24
|
-
let t =
|
|
23
|
+
}, oe = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, A = (e) => {
|
|
24
|
+
let t = oe.exec(e);
|
|
25
25
|
if (!t) throw Error(`invalid duration literal: ${e}`);
|
|
26
26
|
let n = Number(t[1]), r = t[2] ?? "s";
|
|
27
27
|
switch (r) {
|
|
@@ -32,7 +32,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
32
32
|
case "d": return n * 864e5;
|
|
33
33
|
default: throw Error(`unreachable unit: ${r}`);
|
|
34
34
|
}
|
|
35
|
-
},
|
|
35
|
+
}, j = [
|
|
36
36
|
408,
|
|
37
37
|
425,
|
|
38
38
|
429,
|
|
@@ -40,16 +40,16 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
40
40
|
502,
|
|
41
41
|
503,
|
|
42
42
|
504
|
|
43
|
-
],
|
|
44
|
-
if (t >= e.maxAttempts || n?.status !== void 0 && !(e.retryOn ??
|
|
43
|
+
], M = (e, t, n) => {
|
|
44
|
+
if (t >= e.maxAttempts || n?.status !== void 0 && !(e.retryOn ?? j).includes(n.status)) return null;
|
|
45
45
|
if (e.honourRetryAfter !== !1 && n?.retryAfterSeconds !== void 0) {
|
|
46
|
-
let t = n.retryAfterSeconds * 1e3, r =
|
|
46
|
+
let t = n.retryAfterSeconds * 1e3, r = A(e.maxDelay), i = Math.min(t, r);
|
|
47
47
|
return {
|
|
48
48
|
delayMs: i,
|
|
49
49
|
baseDelayMs: i
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
-
let r =
|
|
52
|
+
let r = A(e.initialDelay), i = A(e.maxDelay), a;
|
|
53
53
|
switch (e.strategy) {
|
|
54
54
|
case "fixed":
|
|
55
55
|
a = r;
|
|
@@ -67,23 +67,23 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
67
67
|
delayMs: Math.max(1, Math.floor(o)),
|
|
68
68
|
baseDelayMs: a
|
|
69
69
|
};
|
|
70
|
-
},
|
|
70
|
+
}, se = () => ({
|
|
71
71
|
strategy: "exponential",
|
|
72
72
|
maxAttempts: 8,
|
|
73
73
|
initialDelay: "5s",
|
|
74
74
|
maxDelay: "1h",
|
|
75
|
-
retryOn:
|
|
75
|
+
retryOn: j,
|
|
76
76
|
honourRetryAfter: !0,
|
|
77
77
|
jitter: "full"
|
|
78
|
-
}),
|
|
78
|
+
}), ce = () => ({
|
|
79
79
|
strategy: "fixed",
|
|
80
80
|
maxAttempts: 120,
|
|
81
81
|
initialDelay: "30s",
|
|
82
82
|
maxDelay: "30s",
|
|
83
|
-
retryOn:
|
|
83
|
+
retryOn: j,
|
|
84
84
|
honourRetryAfter: !0,
|
|
85
85
|
jitter: "full"
|
|
86
|
-
}),
|
|
86
|
+
}), N = class extends ee.Tag("@voltro/webhooks/WebhooksService")() {}, le = 32, P = 32, F = () => y(le).toString("hex"), I = (e) => e.events ?? (e.event === void 0 ? [] : [e.event]), L = (e) => {
|
|
87
87
|
let t;
|
|
88
88
|
try {
|
|
89
89
|
t = new URL(e.url);
|
|
@@ -98,14 +98,14 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
98
98
|
reason: `url protocol must be http or https, got ${t.protocol}`
|
|
99
99
|
});
|
|
100
100
|
if (process.env.NODE_ENV === "production") try {
|
|
101
|
-
|
|
101
|
+
T(e.url);
|
|
102
102
|
} catch (e) {
|
|
103
103
|
throw new p({
|
|
104
104
|
field: "url",
|
|
105
105
|
reason: e.message
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
|
-
let n =
|
|
108
|
+
let n = I(e);
|
|
109
109
|
if (n.length === 0) throw new p({
|
|
110
110
|
field: "event",
|
|
111
111
|
reason: "event id required"
|
|
@@ -122,9 +122,9 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
122
122
|
field: "events",
|
|
123
123
|
reason: "duplicate event — one row per event, so a repeat would deliver twice"
|
|
124
124
|
});
|
|
125
|
-
if (e.secret !== void 0 && e.secret.length <
|
|
125
|
+
if (e.secret !== void 0 && e.secret.length < P) throw new p({
|
|
126
126
|
field: "secret",
|
|
127
|
-
reason: `secret must be at least ${
|
|
127
|
+
reason: `secret must be at least ${P} chars`
|
|
128
128
|
});
|
|
129
129
|
if (e.retry !== void 0) {
|
|
130
130
|
if (e.retry.maxAttempts < 1) throw new p({
|
|
@@ -144,14 +144,14 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
144
144
|
field: "autoDisableAfter",
|
|
145
145
|
reason: `autoDisableAfter must be a positive integer, got ${e.autoDisableAfter}`
|
|
146
146
|
});
|
|
147
|
-
},
|
|
148
|
-
id:
|
|
147
|
+
}, ue = (e, t, n, r) => ({
|
|
148
|
+
id: b(),
|
|
149
149
|
tenantId: e.tenantId ?? null,
|
|
150
150
|
event: n ?? e.event ?? "",
|
|
151
151
|
url: e.url,
|
|
152
|
-
secret: r ?? e.secret ??
|
|
152
|
+
secret: r ?? e.secret ?? F(),
|
|
153
153
|
signing: e.signing ?? t?.defaultSigning ?? c(),
|
|
154
|
-
retry: e.retry ?? t?.defaultRetry ??
|
|
154
|
+
retry: e.retry ?? t?.defaultRetry ?? se(),
|
|
155
155
|
filter: e.filter ?? null,
|
|
156
156
|
scope: e.scope ?? null,
|
|
157
157
|
headers: e.headers ?? null,
|
|
@@ -164,14 +164,14 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
164
164
|
autoDisableReason: null,
|
|
165
165
|
payloadVersion: e.payloadVersion ?? t?.version ?? 1,
|
|
166
166
|
description: e.description ?? null
|
|
167
|
-
}),
|
|
167
|
+
}), de = (e) => e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : 0, R = (e) => e instanceof Date ? e.toISOString() : typeof e == "string" ? e : null, fe = (e) => {
|
|
168
168
|
if (typeof e != "string") return e;
|
|
169
169
|
try {
|
|
170
170
|
return JSON.parse(e);
|
|
171
171
|
} catch {
|
|
172
172
|
return e;
|
|
173
173
|
}
|
|
174
|
-
},
|
|
174
|
+
}, pe = (e) => ({
|
|
175
175
|
id: String(e.id),
|
|
176
176
|
deliveryId: String(e.deliveryId),
|
|
177
177
|
targetId: String(e.targetId),
|
|
@@ -182,30 +182,35 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
182
182
|
responseStatus: e.responseStatus ?? null,
|
|
183
183
|
errorMessage: e.errorMessage ?? null,
|
|
184
184
|
latencyMs: e.latencyMs ?? null,
|
|
185
|
-
scheduledAt:
|
|
186
|
-
nextAttemptAt:
|
|
187
|
-
createdAt:
|
|
188
|
-
}), me = (e, t) => e === t ? "current" : e < t ? "behind" : "ahead",
|
|
185
|
+
scheduledAt: R(e.scheduledAt),
|
|
186
|
+
nextAttemptAt: R(e.nextAttemptAt),
|
|
187
|
+
createdAt: R(e.createdAt)
|
|
188
|
+
}), me = (e, t) => e === t ? "current" : e < t ? "behind" : "ahead", he = (e, t) => {
|
|
189
189
|
if (e === null) return !0;
|
|
190
190
|
let n = { payload: t };
|
|
191
|
-
for (let [t, r] of Object.entries(e)) if (!ge(
|
|
191
|
+
for (let [t, r] of Object.entries(e)) if (!_e(ge(n, t), r)) return !1;
|
|
192
192
|
return !0;
|
|
193
|
-
},
|
|
193
|
+
}, ge = (e, t) => {
|
|
194
194
|
let n = t.split("."), r = e;
|
|
195
195
|
for (let e of n) {
|
|
196
196
|
if (typeof r != "object" || !r) return;
|
|
197
197
|
r = r[e];
|
|
198
198
|
}
|
|
199
199
|
return r;
|
|
200
|
-
},
|
|
200
|
+
}, z = (e, t) => {
|
|
201
|
+
if (typeof e == "number" && typeof t == "number") return e - t;
|
|
202
|
+
if (typeof e == "string" && typeof t == "string") return e < t ? -1 : +(e > t);
|
|
203
|
+
}, _e = (e, t) => {
|
|
201
204
|
if (typeof t == "object" && t && !Array.isArray(t)) {
|
|
202
205
|
let n = t;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
206
|
+
return "eq" in n && e !== n.eq || "in" in n && (!Array.isArray(n.in) || !n.in.includes(e)) || "gt" in n && !((z(e, n.gt) ?? 0) > 0) || "gte" in n && !((z(e, n.gte) ?? -1) >= 0) || "lt" in n && !((z(e, n.lt) ?? 0) < 0) || "lte" in n && !((z(e, n.lte) ?? 1) <= 0) ? !1 : [
|
|
207
|
+
"eq",
|
|
208
|
+
"in",
|
|
209
|
+
"gt",
|
|
210
|
+
"gte",
|
|
211
|
+
"lt",
|
|
212
|
+
"lte"
|
|
213
|
+
].some((e) => e in n);
|
|
209
214
|
}
|
|
210
215
|
return e === t;
|
|
211
216
|
}, B = (e, t) => e !== null && Object.entries(t).every(([t, n]) => e[t] === n), V = 500, H = 200, U = async (e, t) => {
|
|
@@ -225,17 +230,17 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
225
230
|
if (n.push(...i), i.length < V) return n;
|
|
226
231
|
}
|
|
227
232
|
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.`);
|
|
228
|
-
},
|
|
233
|
+
}, ve = async (e, t) => {
|
|
229
234
|
let n = (await U(e)).filter((e) => B(e.scope, t));
|
|
230
235
|
if (n.length === 0) return;
|
|
231
236
|
let r = new Set(n.map((e) => e.secret));
|
|
232
237
|
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.`);
|
|
233
238
|
return [...r][0];
|
|
234
|
-
},
|
|
239
|
+
}, ye = async (e, t) => {
|
|
235
240
|
try {
|
|
236
241
|
let n = await e.store.query({
|
|
237
242
|
table: "_voltro_webhook_event_stats",
|
|
238
|
-
predicate:
|
|
243
|
+
predicate: w("event", t),
|
|
239
244
|
order: [],
|
|
240
245
|
take: 1,
|
|
241
246
|
skip: void 0,
|
|
@@ -257,11 +262,11 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
257
262
|
updatedAt: r
|
|
258
263
|
});
|
|
259
264
|
} catch {}
|
|
260
|
-
},
|
|
265
|
+
}, be = (e, t, n = {}) => {
|
|
261
266
|
let r = new Map((n.events ?? []).map((e) => [e.id, e])), i = {
|
|
262
267
|
subscribe: async (t) => {
|
|
263
|
-
|
|
264
|
-
let n =
|
|
268
|
+
L(t);
|
|
269
|
+
let n = I(t), i = t.secret === void 0 && t.scope !== void 0 ? await ve(e, t.scope) : void 0, a = t.secret ?? i ?? F(), o = n.map((e) => ue(t, r.get(e), e, a));
|
|
265
270
|
for (let t of o) await e.store.insert("_voltro_webhook_targets", {
|
|
266
271
|
...t,
|
|
267
272
|
createdAt: /* @__PURE__ */ new Date(),
|
|
@@ -282,19 +287,19 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
282
287
|
};
|
|
283
288
|
},
|
|
284
289
|
emit: async (n, i, a) => {
|
|
285
|
-
let o = typeof n == "object" && n.kind === "event" ?
|
|
290
|
+
let o = typeof n == "object" && n.kind === "event" ? k(n) : void 0, s = typeof n == "string" ? r.get(n) : o ?? n, c = typeof n == "string" ? n : n.id ?? n.name;
|
|
286
291
|
if (s !== void 0) {
|
|
287
|
-
let e =
|
|
288
|
-
if (
|
|
292
|
+
let e = S.decodeUnknownEither(s.payload)(i, { errors: "all" });
|
|
293
|
+
if (te.isLeft(e)) throw new u({
|
|
289
294
|
event: c,
|
|
290
295
|
issues: e.left.message
|
|
291
296
|
});
|
|
292
297
|
}
|
|
293
|
-
let l =
|
|
294
|
-
await
|
|
295
|
-
let p = f.filter((e) =>
|
|
298
|
+
let l = b(), d = a?.tenantId, f = await U(e, d == null ? w("event", c) : C(w("event", c), w("tenantId", d)));
|
|
299
|
+
await ye(e, c);
|
|
300
|
+
let p = f.filter((e) => he(e.filter, i)), m = [], h = JSON.stringify(i);
|
|
296
301
|
for (let n of p) {
|
|
297
|
-
let r =
|
|
302
|
+
let r = b();
|
|
298
303
|
n.active ? (await t({
|
|
299
304
|
deliveryId: r,
|
|
300
305
|
targetId: n.id,
|
|
@@ -332,7 +337,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
332
337
|
replay: async (n) => {
|
|
333
338
|
let r = (await e.store.query({
|
|
334
339
|
table: "_voltro_webhook_deliveries",
|
|
335
|
-
predicate:
|
|
340
|
+
predicate: w("deliveryId", n),
|
|
336
341
|
order: [{
|
|
337
342
|
column: "attempt",
|
|
338
343
|
direction: "asc"
|
|
@@ -346,7 +351,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
346
351
|
deliveryId: r.deliveryId,
|
|
347
352
|
targetId: r.targetId,
|
|
348
353
|
event: r.event,
|
|
349
|
-
eventId: r.eventId ??
|
|
354
|
+
eventId: r.eventId ?? b(),
|
|
350
355
|
payloadJson: typeof r.payload == "string" ? r.payload : JSON.stringify(r.payload),
|
|
351
356
|
attemptEpoch: Date.now()
|
|
352
357
|
});
|
|
@@ -374,7 +379,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
374
379
|
});
|
|
375
380
|
let n = await e.store.query({
|
|
376
381
|
table: "_voltro_webhook_deliveries",
|
|
377
|
-
predicate:
|
|
382
|
+
predicate: C(w("targetId", r), w("status", "pending")),
|
|
378
383
|
order: [{
|
|
379
384
|
column: "createdAt",
|
|
380
385
|
direction: "asc"
|
|
@@ -387,17 +392,17 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
387
392
|
deliveryId: e.deliveryId,
|
|
388
393
|
targetId: r,
|
|
389
394
|
event: e.event,
|
|
390
|
-
eventId: e.eventId ??
|
|
395
|
+
eventId: e.eventId ?? b(),
|
|
391
396
|
payloadJson: typeof e.payload == "string" ? e.payload : JSON.stringify(e.payload),
|
|
392
397
|
attemptEpoch: i
|
|
393
398
|
});
|
|
394
399
|
}
|
|
395
400
|
},
|
|
396
401
|
deleteTarget: async (t) => {
|
|
397
|
-
for (let n of await i.resolveTargets(t)) await e.store.delete("_voltro_webhook_targets", n), await e.store.deleteMany("_voltro_webhook_deliveries", { where:
|
|
402
|
+
for (let n of await i.resolveTargets(t)) await e.store.delete("_voltro_webhook_targets", n), await e.store.deleteMany("_voltro_webhook_deliveries", { where: C(w("targetId", n), w("status", "pending")) });
|
|
398
403
|
},
|
|
399
404
|
rotateSecret: async (t) => {
|
|
400
|
-
let n =
|
|
405
|
+
let n = F();
|
|
401
406
|
for (let r of await i.resolveTargets(t)) await e.store.update("_voltro_webhook_targets", r, {
|
|
402
407
|
secret: n,
|
|
403
408
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -405,7 +410,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
405
410
|
return { secret: n };
|
|
406
411
|
},
|
|
407
412
|
updateTarget: async (t, n) => {
|
|
408
|
-
if (n.url !== void 0 &&
|
|
413
|
+
if (n.url !== void 0 && L({
|
|
409
414
|
event: "x",
|
|
410
415
|
url: n.url
|
|
411
416
|
}), n.rateLimitPerMinute !== void 0 && n.rateLimitPerMinute !== null && (!Number.isInteger(n.rateLimitPerMinute) || n.rateLimitPerMinute < 1)) throw Error("updateTarget: rateLimitPerMinute must be a positive integer or null");
|
|
@@ -425,14 +430,14 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
425
430
|
testTarget: async (n, r) => {
|
|
426
431
|
let i = (await e.store.query({
|
|
427
432
|
table: "_voltro_webhook_targets",
|
|
428
|
-
predicate:
|
|
429
|
-
order:
|
|
433
|
+
predicate: w("id", n),
|
|
434
|
+
order: [],
|
|
430
435
|
take: 1,
|
|
431
436
|
skip: void 0,
|
|
432
437
|
projection: void 0
|
|
433
438
|
}))[0];
|
|
434
439
|
if (i === void 0) throw Error(`testTarget: no target ${n}`);
|
|
435
|
-
let a =
|
|
440
|
+
let a = b(), o = JSON.stringify(r ?? {
|
|
436
441
|
test: !0,
|
|
437
442
|
targetId: n,
|
|
438
443
|
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -441,7 +446,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
441
446
|
deliveryId: a,
|
|
442
447
|
targetId: i.id,
|
|
443
448
|
event: i.event,
|
|
444
|
-
eventId:
|
|
449
|
+
eventId: b(),
|
|
445
450
|
payloadJson: o
|
|
446
451
|
}) : await e.store.insert("_voltro_webhook_deliveries", {
|
|
447
452
|
id: `${a}:1`,
|
|
@@ -461,7 +466,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
461
466
|
listDeliveries: async (t) => {
|
|
462
467
|
let n = t?.scope === void 0 ? void 0 : new Set(await i.resolveTargets({ scope: t.scope })), r = await e.store.query({
|
|
463
468
|
table: "_voltro_webhook_deliveries",
|
|
464
|
-
predicate: t?.targetId === void 0 ? void 0 :
|
|
469
|
+
predicate: t?.targetId === void 0 ? void 0 : w("targetId", t.targetId),
|
|
465
470
|
order: [{
|
|
466
471
|
column: "createdAt",
|
|
467
472
|
direction: "desc"
|
|
@@ -470,20 +475,20 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
470
475
|
skip: void 0,
|
|
471
476
|
projection: void 0
|
|
472
477
|
}), a = t?.since?.getTime();
|
|
473
|
-
return r.filter((e) => n === void 0 || n.has(String(e.targetId))).filter((e) => t?.status === void 0 || e.status === t.status).filter((e) => a === void 0 ||
|
|
478
|
+
return r.filter((e) => n === void 0 || n.has(String(e.targetId))).filter((e) => t?.status === void 0 || e.status === t.status).filter((e) => a === void 0 || de(e.createdAt) >= a).slice(0, t?.limit ?? 200).map(pe);
|
|
474
479
|
},
|
|
475
480
|
getDelivery: async (t) => {
|
|
476
481
|
let n = (await e.store.query({
|
|
477
482
|
table: "_voltro_webhook_deliveries",
|
|
478
|
-
predicate:
|
|
479
|
-
order:
|
|
483
|
+
predicate: w("id", t),
|
|
484
|
+
order: [],
|
|
480
485
|
take: 1,
|
|
481
486
|
skip: void 0,
|
|
482
487
|
projection: void 0
|
|
483
488
|
}))[0];
|
|
484
489
|
return n === void 0 ? null : {
|
|
485
|
-
...
|
|
486
|
-
payload:
|
|
490
|
+
...pe(n),
|
|
491
|
+
payload: fe(n.payload),
|
|
487
492
|
responseBody: n.responseBody ?? null
|
|
488
493
|
};
|
|
489
494
|
},
|
|
@@ -497,7 +502,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
497
502
|
listTargets: async (t, n) => {
|
|
498
503
|
let r = await e.store.query({
|
|
499
504
|
table: "_voltro_webhook_targets",
|
|
500
|
-
predicate: t === void 0 ? void 0 :
|
|
505
|
+
predicate: t === void 0 ? void 0 : w("event", t),
|
|
501
506
|
order: [{
|
|
502
507
|
column: "createdAt",
|
|
503
508
|
direction: "desc"
|
|
@@ -523,15 +528,15 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
523
528
|
}
|
|
524
529
|
};
|
|
525
530
|
return i;
|
|
526
|
-
},
|
|
527
|
-
return yield*
|
|
528
|
-
}),
|
|
531
|
+
}, xe = x.gen(function* () {
|
|
532
|
+
return yield* N;
|
|
533
|
+
}), Se = (e) => {
|
|
529
534
|
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.");
|
|
530
535
|
return e.webhooks;
|
|
531
|
-
},
|
|
536
|
+
}, Ce = 1e4, W = class {
|
|
532
537
|
capacity;
|
|
533
538
|
cache = /* @__PURE__ */ new Map();
|
|
534
|
-
constructor(e =
|
|
539
|
+
constructor(e = Ce) {
|
|
535
540
|
this.capacity = e;
|
|
536
541
|
}
|
|
537
542
|
evictExpired() {
|
|
@@ -577,9 +582,9 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
577
582
|
status: e,
|
|
578
583
|
contentType: "application/json; charset=utf-8",
|
|
579
584
|
body: JSON.stringify(t)
|
|
580
|
-
}),
|
|
585
|
+
}), we = (e, t) => {
|
|
581
586
|
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) => {
|
|
582
|
-
|
|
587
|
+
E({ scope: `webhook:${e.webhookId}` }).debug("incoming webhook", {
|
|
583
588
|
status: e.status,
|
|
584
589
|
signatureOk: e.signatureOk,
|
|
585
590
|
idempotency: e.idempotency,
|
|
@@ -652,7 +657,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
652
657
|
}
|
|
653
658
|
let p;
|
|
654
659
|
try {
|
|
655
|
-
p =
|
|
660
|
+
p = S.decodeUnknownSync(e.payload)(f);
|
|
656
661
|
} catch (t) {
|
|
657
662
|
let n = Y(422, {
|
|
658
663
|
error: "schema validation failed",
|
|
@@ -730,7 +735,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
730
735
|
idempotency: h,
|
|
731
736
|
durationMs: Date.now() - u,
|
|
732
737
|
errorMessage: t.message
|
|
733
|
-
}),
|
|
738
|
+
}), ne({
|
|
734
739
|
error: t,
|
|
735
740
|
source: "webhook",
|
|
736
741
|
name: e.id,
|
|
@@ -738,20 +743,20 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
738
743
|
}), n;
|
|
739
744
|
}
|
|
740
745
|
};
|
|
741
|
-
},
|
|
746
|
+
}, Te = (e) => `/webhooks/${e}`, Ee = "_voltro_webhook_rate_windows", De = 6e4, Oe = (e, t) => `${e}@${t}`, ke = async (e, t, n) => (await e.query({
|
|
742
747
|
table: "_voltro_webhook_rate_windows",
|
|
743
|
-
predicate:
|
|
748
|
+
predicate: C(w("scope", t), w("bucket", n)),
|
|
744
749
|
order: [],
|
|
745
750
|
take: 1,
|
|
746
751
|
skip: void 0,
|
|
747
752
|
projection: void 0
|
|
748
|
-
}))[0] ?? null, X = 32,
|
|
753
|
+
}))[0] ?? null, X = 32, Ae = async (e, t, n) => {
|
|
749
754
|
for (let r = 0; r < X; r++) {
|
|
750
755
|
r > 0 && await new Promise((e) => setTimeout(e, r));
|
|
751
|
-
let i = await
|
|
756
|
+
let i = await ke(e, t.key, n);
|
|
752
757
|
if (i === null) {
|
|
753
|
-
await e.insertIgnore(
|
|
754
|
-
id:
|
|
758
|
+
await e.insertIgnore(Ee, {
|
|
759
|
+
id: Oe(t.key, n),
|
|
755
760
|
scope: t.key,
|
|
756
761
|
bucket: n,
|
|
757
762
|
count: 0,
|
|
@@ -764,31 +769,31 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
764
769
|
if (await e.updateMany("_voltro_webhook_rate_windows", {
|
|
765
770
|
count: i.count + 1,
|
|
766
771
|
updatedAt: /* @__PURE__ */ new Date()
|
|
767
|
-
}, { where:
|
|
772
|
+
}, { where: C(w("id", i.id), w("count", i.count)) }) === 1) return !0;
|
|
768
773
|
}
|
|
769
774
|
throw Error(`webhook rate window: contention on (${t.key}, ${n}) exceeded ${X} attempts`);
|
|
770
|
-
},
|
|
775
|
+
}, je = async (e, t, n) => {
|
|
771
776
|
for (let r = 0; r < X; r++) {
|
|
772
777
|
r > 0 && await new Promise((e) => setTimeout(e, r));
|
|
773
|
-
let i = await
|
|
778
|
+
let i = await ke(e, t, n);
|
|
774
779
|
if (i === null || i.count <= 0 || await e.updateMany("_voltro_webhook_rate_windows", {
|
|
775
780
|
count: i.count - 1,
|
|
776
781
|
updatedAt: /* @__PURE__ */ new Date()
|
|
777
|
-
}, { where:
|
|
782
|
+
}, { where: C(w("id", i.id), w("count", i.count)) }) === 1) return;
|
|
778
783
|
}
|
|
779
784
|
throw Error(`webhook rate window: release contention on (${t}) exceeded ${X} attempts`);
|
|
780
|
-
},
|
|
785
|
+
}, Me = async (e, t, n, r = De) => {
|
|
781
786
|
if (t.length === 0) return {
|
|
782
787
|
acquired: !0,
|
|
783
788
|
retryInMs: 0
|
|
784
789
|
};
|
|
785
790
|
let i = Math.floor(n / r), a = [];
|
|
786
791
|
for (let o of t) {
|
|
787
|
-
if (await
|
|
792
|
+
if (await Ae(e, o, i)) {
|
|
788
793
|
a.push(o);
|
|
789
794
|
continue;
|
|
790
795
|
}
|
|
791
|
-
for (let t of a) await
|
|
796
|
+
for (let t of a) await je(e, t.key, i);
|
|
792
797
|
return {
|
|
793
798
|
acquired: !1,
|
|
794
799
|
retryInMs: (i + 1) * r - n
|
|
@@ -798,7 +803,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
798
803
|
acquired: !0,
|
|
799
804
|
retryInMs: 0
|
|
800
805
|
};
|
|
801
|
-
},
|
|
806
|
+
}, Ne = (e, t) => {
|
|
802
807
|
if (e === "json") return {
|
|
803
808
|
contentType: "application/json",
|
|
804
809
|
bytes: Buffer.from(t, "utf8")
|
|
@@ -814,15 +819,15 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
814
819
|
}
|
|
815
820
|
return e === "form" ? {
|
|
816
821
|
contentType: "application/x-www-form-urlencoded",
|
|
817
|
-
bytes: Buffer.from(
|
|
822
|
+
bytes: Buffer.from(Pe(n), "utf8")
|
|
818
823
|
} : {
|
|
819
824
|
contentType: "application/xml",
|
|
820
|
-
bytes: Buffer.from(
|
|
825
|
+
bytes: Buffer.from(Ie(n), "utf8")
|
|
821
826
|
};
|
|
822
|
-
},
|
|
827
|
+
}, Pe = (e) => {
|
|
823
828
|
if (typeof e != "object" || !e || Array.isArray(e)) throw new d({
|
|
824
829
|
format: "form",
|
|
825
|
-
reason: `form encoding requires a JSON object at the top level, got ${
|
|
830
|
+
reason: `form encoding requires a JSON object at the top level, got ${ze(e)}`
|
|
826
831
|
});
|
|
827
832
|
let t = new URLSearchParams();
|
|
828
833
|
for (let [n, r] of Object.entries(e)) Z(t, n, r);
|
|
@@ -841,13 +846,13 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
841
846
|
return;
|
|
842
847
|
}
|
|
843
848
|
e.append(t, String(n));
|
|
844
|
-
},
|
|
845
|
-
if (!
|
|
849
|
+
}, Fe = /^[A-Za-z_][A-Za-z0-9_.-]*$/, Ie = (e) => `<?xml version="1.0" encoding="UTF-8"?>${Q("webhook", e)}`, Q = (e, t) => {
|
|
850
|
+
if (!Fe.test(e)) throw new d({
|
|
846
851
|
format: "xml",
|
|
847
852
|
reason: `object key ${JSON.stringify(e)} is not a valid XML element name`
|
|
848
853
|
});
|
|
849
|
-
return `<${e}>${
|
|
850
|
-
},
|
|
854
|
+
return `<${e}>${Le(e, t)}</${e}>`;
|
|
855
|
+
}, Le = (e, t) => t == null ? "" : Array.isArray(t) ? t.map((e) => Q("item", e)).join("") : typeof t == "object" ? Object.entries(t).map(([e, t]) => Q(e, t)).join("") : Re(String(t)), Re = (e) => e.replace(/[&<>"']/g, (e) => {
|
|
851
856
|
switch (e) {
|
|
852
857
|
case "&": return "&";
|
|
853
858
|
case "<": return "<";
|
|
@@ -855,17 +860,17 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
855
860
|
case "\"": return """;
|
|
856
861
|
default: return "'";
|
|
857
862
|
}
|
|
858
|
-
}),
|
|
863
|
+
}), ze = (e) => e === null ? "null" : Array.isArray(e) ? "array" : typeof e, Be = "_voltro_webhook_targets", Ve = 32, He = async (e, t) => (await e.query({
|
|
859
864
|
table: "_voltro_webhook_targets",
|
|
860
|
-
predicate:
|
|
865
|
+
predicate: w("id", t),
|
|
861
866
|
order: [],
|
|
862
867
|
take: 1,
|
|
863
868
|
skip: void 0,
|
|
864
869
|
projection: void 0
|
|
865
|
-
}))[0] ?? null,
|
|
866
|
-
for (let a = 0; a <
|
|
870
|
+
}))[0] ?? null, Ue = async (e, t, n, r, i = /* @__PURE__ */ new Date()) => {
|
|
871
|
+
for (let a = 0; a < Ve; a++) {
|
|
867
872
|
a > 0 && await new Promise((e) => setTimeout(e, a));
|
|
868
|
-
let o = await
|
|
873
|
+
let o = await He(e, t);
|
|
869
874
|
if (o === null) return {
|
|
870
875
|
consecutiveFailures: 0,
|
|
871
876
|
autoDisabled: !1
|
|
@@ -875,7 +880,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
875
880
|
if (s === 0 || await e.updateMany("_voltro_webhook_targets", {
|
|
876
881
|
consecutiveFailures: 0,
|
|
877
882
|
updatedAt: i
|
|
878
|
-
}, { where:
|
|
883
|
+
}, { where: C(w("id", t), w("consecutiveFailures", s)) }) === 1) return {
|
|
879
884
|
consecutiveFailures: 0,
|
|
880
885
|
autoDisabled: !1
|
|
881
886
|
};
|
|
@@ -885,68 +890,68 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
885
890
|
consecutiveFailures: c,
|
|
886
891
|
updatedAt: i
|
|
887
892
|
};
|
|
888
|
-
if (u && (d.active = !1, d.autoDisabledAt = i, d.autoDisableReason = r.slice(0, 500)), await e.updateMany("_voltro_webhook_targets", d, { where:
|
|
893
|
+
if (u && (d.active = !1, d.autoDisabledAt = i, d.autoDisableReason = r.slice(0, 500)), await e.updateMany("_voltro_webhook_targets", d, { where: C(w("id", t), w("consecutiveFailures", s)) }) === 1) return {
|
|
889
894
|
consecutiveFailures: c,
|
|
890
895
|
autoDisabled: u
|
|
891
896
|
};
|
|
892
897
|
}
|
|
893
|
-
throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${
|
|
894
|
-
},
|
|
895
|
-
status:
|
|
896
|
-
body:
|
|
897
|
-
latencyMs:
|
|
898
|
-
retryAfterSec:
|
|
899
|
-
transportError:
|
|
900
|
-
encodeError:
|
|
901
|
-
}),
|
|
902
|
-
outcome:
|
|
903
|
-
delayMs:
|
|
904
|
-
reason:
|
|
905
|
-
autoDisabled:
|
|
906
|
-
consecutiveFailures:
|
|
907
|
-
}),
|
|
908
|
-
acquired:
|
|
909
|
-
retryInMs:
|
|
910
|
-
}),
|
|
898
|
+
throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${Ve} attempts`);
|
|
899
|
+
}, We = S.Struct({
|
|
900
|
+
status: S.Number,
|
|
901
|
+
body: S.String,
|
|
902
|
+
latencyMs: S.Number,
|
|
903
|
+
retryAfterSec: S.Union(S.Number, S.Undefined),
|
|
904
|
+
transportError: S.Union(S.String, S.Undefined),
|
|
905
|
+
encodeError: S.Union(S.String, S.Undefined)
|
|
906
|
+
}), Ge = S.Struct({
|
|
907
|
+
outcome: S.Literal("succeeded", "failed", "retry"),
|
|
908
|
+
delayMs: S.Union(S.Number, S.Undefined),
|
|
909
|
+
reason: S.Union(S.String, S.Undefined),
|
|
910
|
+
autoDisabled: S.Union(S.Boolean, S.Undefined),
|
|
911
|
+
consecutiveFailures: S.Union(S.Number, S.Undefined)
|
|
912
|
+
}), Ke = S.Struct({
|
|
913
|
+
acquired: S.Boolean,
|
|
914
|
+
retryInMs: S.Number
|
|
915
|
+
}), qe = re.make({
|
|
911
916
|
name: "voltro.deliverWebhook",
|
|
912
917
|
payload: {
|
|
913
|
-
deliveryId:
|
|
914
|
-
targetId:
|
|
915
|
-
event:
|
|
916
|
-
eventId:
|
|
917
|
-
payloadJson:
|
|
918
|
-
attemptEpoch:
|
|
918
|
+
deliveryId: S.String,
|
|
919
|
+
targetId: S.String,
|
|
920
|
+
event: S.String,
|
|
921
|
+
eventId: S.String,
|
|
922
|
+
payloadJson: S.String,
|
|
923
|
+
attemptEpoch: S.optionalWith(S.Number, { default: () => 0 })
|
|
919
924
|
},
|
|
920
|
-
success:
|
|
921
|
-
finalStatus:
|
|
922
|
-
attempts:
|
|
925
|
+
success: S.Struct({
|
|
926
|
+
finalStatus: S.Literal("succeeded", "failed", "deferred"),
|
|
927
|
+
attempts: S.Number
|
|
923
928
|
}),
|
|
924
929
|
idempotencyKey: ({ deliveryId: e, attemptEpoch: t }) => `voltro.deliverWebhook:${e}:${t ?? 0}`
|
|
925
|
-
}),
|
|
926
|
-
let { deliveryId: i, targetId: a, event: s, eventId: c, payloadJson: l } = n, u =
|
|
927
|
-
return
|
|
928
|
-
let n = yield*
|
|
930
|
+
}), Je = (e, t = {}) => (n, r) => {
|
|
931
|
+
let { deliveryId: i, targetId: a, event: s, eventId: c, payloadJson: l } = n, u = E({ scope: `webhook:${i}` });
|
|
932
|
+
return x.gen(function* () {
|
|
933
|
+
let n = yield* D.make({
|
|
929
934
|
name: "fetch-target",
|
|
930
|
-
success:
|
|
931
|
-
id:
|
|
932
|
-
event:
|
|
933
|
-
url:
|
|
934
|
-
secret:
|
|
935
|
-
signing:
|
|
936
|
-
retry:
|
|
937
|
-
headers:
|
|
938
|
-
key:
|
|
939
|
-
value:
|
|
940
|
-
}),
|
|
941
|
-
rateLimitPerMinute:
|
|
942
|
-
active:
|
|
943
|
-
format:
|
|
944
|
-
tenantId:
|
|
935
|
+
success: S.Union(S.Null, S.Struct({
|
|
936
|
+
id: S.String,
|
|
937
|
+
event: S.String,
|
|
938
|
+
url: S.String,
|
|
939
|
+
secret: S.String,
|
|
940
|
+
signing: S.Any,
|
|
941
|
+
retry: S.Any,
|
|
942
|
+
headers: S.Union(S.Record({
|
|
943
|
+
key: S.String,
|
|
944
|
+
value: S.String
|
|
945
|
+
}), S.Null),
|
|
946
|
+
rateLimitPerMinute: S.Union(S.Number, S.Null),
|
|
947
|
+
active: S.Boolean,
|
|
948
|
+
format: S.Literal("json", "form", "xml"),
|
|
949
|
+
tenantId: S.Union(S.String, S.Null)
|
|
945
950
|
})),
|
|
946
|
-
execute:
|
|
951
|
+
execute: x.tryPromise({
|
|
947
952
|
try: async () => (await e.store.query({
|
|
948
953
|
table: "_voltro_webhook_targets",
|
|
949
|
-
predicate:
|
|
954
|
+
predicate: w("id", a),
|
|
950
955
|
order: [{
|
|
951
956
|
column: "createdAt",
|
|
952
957
|
direction: "asc"
|
|
@@ -960,7 +965,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
960
965
|
event: s,
|
|
961
966
|
cause: String(e)
|
|
962
967
|
}), /* @__PURE__ */ Error(`fetch-target failed: ${e.message}`))
|
|
963
|
-
}).pipe(
|
|
968
|
+
}).pipe(x.orDie)
|
|
964
969
|
});
|
|
965
970
|
if (n === null) return {
|
|
966
971
|
finalStatus: "failed",
|
|
@@ -985,75 +990,75 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
985
990
|
...o
|
|
986
991
|
});
|
|
987
992
|
};
|
|
988
|
-
if (n.active !== !0) return yield*
|
|
993
|
+
if (n.active !== !0) return yield* D.make({
|
|
989
994
|
name: "record-paused-pending",
|
|
990
|
-
success:
|
|
991
|
-
execute:
|
|
995
|
+
success: S.Void,
|
|
996
|
+
execute: x.tryPromise({
|
|
992
997
|
try: () => r(`${i}:1`, 1, { status: "pending" }),
|
|
993
998
|
catch: (e) => /* @__PURE__ */ Error(`record-paused-pending failed: ${e.message}`)
|
|
994
|
-
}).pipe(
|
|
999
|
+
}).pipe(x.orDie)
|
|
995
1000
|
}), {
|
|
996
1001
|
finalStatus: "deferred",
|
|
997
1002
|
attempts: 0
|
|
998
1003
|
};
|
|
999
|
-
let f = (e) => typeof e == "string" ? JSON.parse(e) : e, p = f(n.retry), m = f(n.signing), h = n.headers === null ? null : f(n.headers),
|
|
1004
|
+
let f = (e) => typeof e == "string" ? JSON.parse(e) : e, p = f(n.retry), m = f(n.signing), h = n.headers === null ? null : f(n.headers), g = t.rateWindowMs ?? 6e4, _ = t.events?.find((e) => e.id === s)?.globalRateLimit, v = [...typeof n.rateLimitPerMinute == "number" && n.rateLimitPerMinute > 0 ? [{
|
|
1000
1005
|
key: `target:${n.id}`,
|
|
1001
1006
|
limit: n.rateLimitPerMinute
|
|
1002
|
-
}] : [], ...
|
|
1007
|
+
}] : [], ..._ !== void 0 && _.perMinute > 0 ? [{
|
|
1003
1008
|
key: `event:${s}`,
|
|
1004
|
-
limit:
|
|
1009
|
+
limit: _.perMinute
|
|
1005
1010
|
}] : []];
|
|
1006
1011
|
for (let t = 1; t <= p.maxAttempts; t++) {
|
|
1007
1012
|
let f = `${i}:${t}`;
|
|
1008
|
-
if (
|
|
1009
|
-
let i = yield*
|
|
1013
|
+
if (v.length > 0) for (let n = 0;; n++) {
|
|
1014
|
+
let i = yield* D.make({
|
|
1010
1015
|
name: `rate-acquire-${t}-${n}`,
|
|
1011
|
-
success:
|
|
1012
|
-
execute:
|
|
1013
|
-
try: () =>
|
|
1016
|
+
success: Ke,
|
|
1017
|
+
execute: x.tryPromise({
|
|
1018
|
+
try: () => Me(e.store, v, Date.now(), g),
|
|
1014
1019
|
catch: (e) => (u.debug("rate-acquire raw error", {
|
|
1015
1020
|
targetId: a,
|
|
1016
1021
|
event: s,
|
|
1017
1022
|
attempt: t,
|
|
1018
1023
|
cause: String(e)
|
|
1019
1024
|
}), /* @__PURE__ */ Error(`rate-acquire failed: ${e.message}`))
|
|
1020
|
-
}).pipe(
|
|
1025
|
+
}).pipe(x.orDie)
|
|
1021
1026
|
});
|
|
1022
1027
|
if (i.acquired) break;
|
|
1023
|
-
yield*
|
|
1028
|
+
yield* D.make({
|
|
1024
1029
|
name: `record-rate-deferral-${t}-${n}`,
|
|
1025
|
-
success:
|
|
1026
|
-
execute:
|
|
1030
|
+
success: S.Void,
|
|
1031
|
+
execute: x.tryPromise({
|
|
1027
1032
|
try: () => r(f, t, {
|
|
1028
1033
|
status: "pending",
|
|
1029
1034
|
nextAttemptAt: new Date(Date.now() + i.retryInMs)
|
|
1030
1035
|
}),
|
|
1031
1036
|
catch: (e) => /* @__PURE__ */ Error(`record-rate-deferral failed: ${e.message}`)
|
|
1032
|
-
}).pipe(
|
|
1033
|
-
}), yield*
|
|
1037
|
+
}).pipe(x.orDie)
|
|
1038
|
+
}), yield* O.sleep({
|
|
1034
1039
|
name: `rate-window-sleep-${t}-${n}`,
|
|
1035
1040
|
duration: `${i.retryInMs} millis`
|
|
1036
1041
|
});
|
|
1037
1042
|
}
|
|
1038
|
-
yield*
|
|
1043
|
+
yield* D.make({
|
|
1039
1044
|
name: `record-attempt-start-${t}`,
|
|
1040
|
-
success:
|
|
1041
|
-
execute:
|
|
1045
|
+
success: S.Void,
|
|
1046
|
+
execute: x.tryPromise({
|
|
1042
1047
|
try: () => r(f, t, {
|
|
1043
1048
|
status: "inFlight",
|
|
1044
1049
|
nextAttemptAt: null,
|
|
1045
1050
|
scheduledAt: /* @__PURE__ */ new Date()
|
|
1046
1051
|
}),
|
|
1047
1052
|
catch: (e) => /* @__PURE__ */ Error(`record-attempt-start failed: ${e.message}`)
|
|
1048
|
-
}).pipe(
|
|
1053
|
+
}).pipe(x.orDie)
|
|
1049
1054
|
});
|
|
1050
|
-
let
|
|
1055
|
+
let _ = yield* D.make({
|
|
1051
1056
|
name: `sign-and-post-${t}`,
|
|
1052
|
-
success:
|
|
1053
|
-
execute:
|
|
1057
|
+
success: We,
|
|
1058
|
+
execute: x.promise(async () => {
|
|
1054
1059
|
let e = Date.now(), r;
|
|
1055
1060
|
try {
|
|
1056
|
-
r =
|
|
1061
|
+
r = Ne(n.format, l);
|
|
1057
1062
|
} catch (t) {
|
|
1058
1063
|
if (t instanceof d) return {
|
|
1059
1064
|
status: 0,
|
|
@@ -1066,7 +1071,7 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
1066
1071
|
throw t;
|
|
1067
1072
|
}
|
|
1068
1073
|
let a = Buffer.from(r.bytes), { headerName: u, headerValue: f } = o(m, a, n.secret);
|
|
1069
|
-
process.env.NODE_ENV === "production" &&
|
|
1074
|
+
process.env.NODE_ENV === "production" && T(n.url);
|
|
1070
1075
|
try {
|
|
1071
1076
|
let o = await fetch(n.url, {
|
|
1072
1077
|
method: "POST",
|
|
@@ -1101,18 +1106,18 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
1101
1106
|
};
|
|
1102
1107
|
}
|
|
1103
1108
|
})
|
|
1104
|
-
}),
|
|
1109
|
+
}), y = yield* D.make({
|
|
1105
1110
|
name: `classify-and-record-${t}`,
|
|
1106
|
-
success:
|
|
1107
|
-
execute:
|
|
1111
|
+
success: Ge,
|
|
1112
|
+
execute: x.tryPromise({
|
|
1108
1113
|
try: async () => {
|
|
1109
|
-
let r =
|
|
1114
|
+
let r = _.status >= 200 && _.status < 300, i = _.transportError !== void 0, o = _.encodeError !== void 0, s = (t, n) => Ue(e.store, a, t, n);
|
|
1110
1115
|
if (r) {
|
|
1111
1116
|
await e.store.update("_voltro_webhook_deliveries", f, {
|
|
1112
1117
|
status: "succeeded",
|
|
1113
|
-
responseStatus:
|
|
1114
|
-
responseBody:
|
|
1115
|
-
latencyMs:
|
|
1118
|
+
responseStatus: _.status,
|
|
1119
|
+
responseBody: _.body,
|
|
1120
|
+
latencyMs: _.latencyMs,
|
|
1116
1121
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1117
1122
|
});
|
|
1118
1123
|
let t = await s(!0, "");
|
|
@@ -1124,18 +1129,18 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
1124
1129
|
consecutiveFailures: t.consecutiveFailures
|
|
1125
1130
|
};
|
|
1126
1131
|
}
|
|
1127
|
-
let c = o ? null :
|
|
1128
|
-
...i ? {} : { status:
|
|
1129
|
-
...
|
|
1132
|
+
let c = o ? null : M(p, t, {
|
|
1133
|
+
...i ? {} : { status: _.status },
|
|
1134
|
+
..._.retryAfterSec === void 0 ? {} : { retryAfterSeconds: _.retryAfterSec }
|
|
1130
1135
|
});
|
|
1131
1136
|
if (c === null) {
|
|
1132
|
-
let t = o ? `encode (${n.format}): ${
|
|
1137
|
+
let t = o ? `encode (${n.format}): ${_.encodeError}` : i ? `transport: ${_.transportError}` : `http ${_.status}: ${_.body.slice(0, 200)}`;
|
|
1133
1138
|
await e.store.update("_voltro_webhook_deliveries", f, {
|
|
1134
1139
|
status: "failed",
|
|
1135
|
-
responseStatus: i || o ? null :
|
|
1136
|
-
responseBody: i || o ? null :
|
|
1137
|
-
errorMessage: o ? t :
|
|
1138
|
-
latencyMs:
|
|
1140
|
+
responseStatus: i || o ? null : _.status,
|
|
1141
|
+
responseBody: i || o ? null : _.body,
|
|
1142
|
+
errorMessage: o ? t : _.transportError ?? null,
|
|
1143
|
+
latencyMs: _.latencyMs,
|
|
1139
1144
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1140
1145
|
});
|
|
1141
1146
|
let r = await s(!1, t);
|
|
@@ -1149,10 +1154,10 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
1149
1154
|
}
|
|
1150
1155
|
return await e.store.update("_voltro_webhook_deliveries", f, {
|
|
1151
1156
|
status: "retryScheduled",
|
|
1152
|
-
responseStatus: i ? null :
|
|
1153
|
-
responseBody: i ? null :
|
|
1154
|
-
errorMessage:
|
|
1155
|
-
latencyMs:
|
|
1157
|
+
responseStatus: i ? null : _.status,
|
|
1158
|
+
responseBody: i ? null : _.body,
|
|
1159
|
+
errorMessage: _.transportError ?? null,
|
|
1160
|
+
latencyMs: _.latencyMs,
|
|
1156
1161
|
nextAttemptAt: new Date(Date.now() + c.delayMs),
|
|
1157
1162
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1158
1163
|
}), {
|
|
@@ -1169,24 +1174,24 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
1169
1174
|
attempt: t,
|
|
1170
1175
|
cause: String(e)
|
|
1171
1176
|
}), /* @__PURE__ */ Error(`classify-and-record failed: ${e.message}`))
|
|
1172
|
-
}).pipe(
|
|
1177
|
+
}).pipe(x.orDie)
|
|
1173
1178
|
});
|
|
1174
|
-
if (
|
|
1179
|
+
if (y.autoDisabled === !0 && u.warn("target auto-disabled after consecutive failures", {
|
|
1175
1180
|
targetId: a,
|
|
1176
1181
|
event: s,
|
|
1177
|
-
consecutiveFailures:
|
|
1178
|
-
reason:
|
|
1179
|
-
}),
|
|
1182
|
+
consecutiveFailures: y.consecutiveFailures ?? null,
|
|
1183
|
+
reason: y.reason ?? null
|
|
1184
|
+
}), y.outcome === "succeeded") return {
|
|
1180
1185
|
finalStatus: "succeeded",
|
|
1181
1186
|
attempts: t
|
|
1182
1187
|
};
|
|
1183
|
-
if (
|
|
1188
|
+
if (y.outcome === "failed") return {
|
|
1184
1189
|
finalStatus: "failed",
|
|
1185
1190
|
attempts: t
|
|
1186
1191
|
};
|
|
1187
|
-
|
|
1192
|
+
y.delayMs !== void 0 && (yield* O.sleep({
|
|
1188
1193
|
name: `retry-sleep-${t}`,
|
|
1189
|
-
duration: `${
|
|
1194
|
+
duration: `${y.delayMs} millis`
|
|
1190
1195
|
}));
|
|
1191
1196
|
}
|
|
1192
1197
|
return {
|
|
@@ -1194,15 +1199,15 @@ var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
|
1194
1199
|
attempts: p.maxAttempts
|
|
1195
1200
|
};
|
|
1196
1201
|
});
|
|
1197
|
-
}, $ = "voltro.webhook.emit",
|
|
1202
|
+
}, $ = "voltro.webhook.emit", Ye = (e) => typeof e.event == "string", Xe = (e) => ie({
|
|
1198
1203
|
effect: $,
|
|
1199
1204
|
maxAttempts: 1,
|
|
1200
1205
|
handler: async ({ payload: t }) => {
|
|
1201
|
-
if (!
|
|
1206
|
+
if (!Ye(t)) throw Error(`${$}: row payload has no \`event\` name — cannot resolve a descriptor. Got keys: ${Object.keys(t).join(", ") || "(none)"}`);
|
|
1202
1207
|
let n = e();
|
|
1203
1208
|
if (n === void 0) throw Error(`${$}: the outgoing-webhooks service is not available in this process. An emit was enqueued by a build that had the plugin registered; this one does not.`);
|
|
1204
1209
|
await n.emit(t.event, t.payload, { tenantId: t.tenantId });
|
|
1205
1210
|
}
|
|
1206
1211
|
});
|
|
1207
1212
|
//#endregion
|
|
1208
|
-
export { W as IdempotencyCache,
|
|
1213
|
+
export { W as IdempotencyCache, De as RATE_WINDOW_MS, Ee as RATE_WINDOW_TABLE, Be as TARGETS_TABLE, $ as WEBHOOK_EMIT_EFFECT, l as WebhookDeliveryNotFound, u as WebhookPayloadInvalid, d as WebhookPayloadUnrepresentable, f as WebhookPayloadVersionInvalid, p as WebhookSubscribeInvalid, N as WebhooksService, m as _voltroWebhookDeliveriesTable, h as _voltroWebhookEventStatsTable, g as _voltroWebhookRateWindowsTable, _ as _voltroWebhookTargetsTable, Me as acquireRateSlots, k as asOutgoingEvent, Je as buildDeliverWebhookExecute, be as buildWebhooksService, me as compareVersions, Ae as consumeRateSlot, Te as defaultIncomingPath, c as defaultOutgoingSignature, se as defaultRetryPolicy, s as defineIncomingWebhook, t as defineWebhookProvider, qe as deliverWebhookWorkflow, Ne as encodePayload, I as eventsOf, ce as fastRetryPolicy, F as generateSecret, K as getIdempotencyCache, i as githubSignature, ae as hasWebhookAudience, r as isWebhookDescriptor, Xe as makeDeferredEmitHandler, he as matchesFilter, we as mountIncomingWebhook, M as nextRetry, A as parseDuration, J as parseTtl, Ue as recordDeliveryOutcome, je as releaseRateSlot, ue as resolveSubscribe, o as signPayload, n as slackSignature, e as stripeSignature, Se as useWebhooks, xe as useWebhooksEffect, L as validateSubscribe, a as verifySignature, v as webhookTables };
|