@voltro/plugin-webhooks 0.28.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 +278 -0
- package/THIRD-PARTY-NOTICES.md +87 -1
- package/dist/index.js +240 -250
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
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, _voltroWebhookRateWindowsTable as h, _voltroWebhookTargetsTable 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
|
|
3
|
+
import { _voltroWebhookDeliveriesTable as m, _voltroWebhookRateWindowsTable as h, _voltroWebhookTargetsTable as g, webhookTables as _ } from "./mixin.js";
|
|
4
|
+
import { randomBytes as v, randomUUID as y } from "node:crypto";
|
|
5
|
+
import { Context as ee, Effect as b, Either as te, Schema as x } from "effect";
|
|
6
|
+
import { and as S, eq as C } from "@voltro/database";
|
|
7
|
+
import { assertPublicUrl as w } from "@voltro/integration-http";
|
|
8
|
+
import { publishServerError as ne } from "@voltro/protocol";
|
|
9
|
+
import { createLogger as T } from "@voltro/logger";
|
|
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 oe = (e) => e.webhook !== void 0, E = (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 oe = (e) => e.webhook !== void 0, E = (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 oe = (e) => e.webhook !== void 0, E = (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 oe = (e) => e.webhook !== void 0, E = (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,10 +79,10 @@ var oe = (e) => e.webhook !== void 0, E = (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
|
-
}), j = class extends
|
|
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) => {
|
|
86
86
|
let t;
|
|
87
87
|
try {
|
|
88
88
|
t = new URL(e.url);
|
|
@@ -97,14 +97,14 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
97
97
|
reason: `url protocol must be http or https, got ${t.protocol}`
|
|
98
98
|
});
|
|
99
99
|
if (process.env.NODE_ENV === "production") try {
|
|
100
|
-
|
|
100
|
+
w(e.url);
|
|
101
101
|
} catch (e) {
|
|
102
102
|
throw new p({
|
|
103
103
|
field: "url",
|
|
104
104
|
reason: e.message
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
|
-
let n =
|
|
107
|
+
let n = P(e);
|
|
108
108
|
if (n.length === 0) throw new p({
|
|
109
109
|
field: "event",
|
|
110
110
|
reason: "event id required"
|
|
@@ -121,9 +121,9 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
121
121
|
field: "events",
|
|
122
122
|
reason: "duplicate event — one row per event, so a repeat would deliver twice"
|
|
123
123
|
});
|
|
124
|
-
if (e.secret !== void 0 && e.secret.length <
|
|
124
|
+
if (e.secret !== void 0 && e.secret.length < M) throw new p({
|
|
125
125
|
field: "secret",
|
|
126
|
-
reason: `secret must be at least ${
|
|
126
|
+
reason: `secret must be at least ${M} chars`
|
|
127
127
|
});
|
|
128
128
|
if (e.retry !== void 0) {
|
|
129
129
|
if (e.retry.maxAttempts < 1) throw new p({
|
|
@@ -143,13 +143,13 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
143
143
|
field: "autoDisableAfter",
|
|
144
144
|
reason: `autoDisableAfter must be a positive integer, got ${e.autoDisableAfter}`
|
|
145
145
|
});
|
|
146
|
-
},
|
|
147
|
-
id:
|
|
146
|
+
}, I = (e, t, n, r) => ({
|
|
147
|
+
id: y(),
|
|
148
148
|
event: n ?? e.event ?? "",
|
|
149
149
|
url: e.url,
|
|
150
|
-
secret: r ?? e.secret ??
|
|
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,
|
|
@@ -162,14 +162,14 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
162
162
|
autoDisableReason: null,
|
|
163
163
|
payloadVersion: e.payloadVersion ?? t?.version ?? 1,
|
|
164
164
|
description: e.description ?? null
|
|
165
|
-
}),
|
|
165
|
+
}), ue = (e) => e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : 0, L = (e) => e instanceof Date ? e.toISOString() : typeof e == "string" ? e : null, de = (e) => {
|
|
166
166
|
if (typeof e != "string") return e;
|
|
167
167
|
try {
|
|
168
168
|
return JSON.parse(e);
|
|
169
169
|
} catch {
|
|
170
170
|
return e;
|
|
171
171
|
}
|
|
172
|
-
},
|
|
172
|
+
}, R = (e) => ({
|
|
173
173
|
id: String(e.id),
|
|
174
174
|
deliveryId: String(e.deliveryId),
|
|
175
175
|
targetId: String(e.targetId),
|
|
@@ -180,22 +180,22 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
180
180
|
responseStatus: e.responseStatus ?? null,
|
|
181
181
|
errorMessage: e.errorMessage ?? null,
|
|
182
182
|
latencyMs: e.latencyMs ?? null,
|
|
183
|
-
scheduledAt:
|
|
184
|
-
nextAttemptAt:
|
|
185
|
-
createdAt:
|
|
186
|
-
}),
|
|
183
|
+
scheduledAt: L(e.scheduledAt),
|
|
184
|
+
nextAttemptAt: L(e.nextAttemptAt),
|
|
185
|
+
createdAt: L(e.createdAt)
|
|
186
|
+
}), fe = (e, t) => e === t ? "current" : e < t ? "behind" : "ahead", z = (e, t) => {
|
|
187
187
|
if (e === null) return !0;
|
|
188
188
|
let n = { payload: t };
|
|
189
|
-
for (let [t, r] of Object.entries(e)) if (!
|
|
189
|
+
for (let [t, r] of Object.entries(e)) if (!me(pe(n, t), r)) return !1;
|
|
190
190
|
return !0;
|
|
191
|
-
},
|
|
191
|
+
}, pe = (e, t) => {
|
|
192
192
|
let n = t.split("."), r = e;
|
|
193
193
|
for (let e of n) {
|
|
194
194
|
if (typeof r != "object" || !r) return;
|
|
195
195
|
r = r[e];
|
|
196
196
|
}
|
|
197
197
|
return r;
|
|
198
|
-
},
|
|
198
|
+
}, me = (e, t) => {
|
|
199
199
|
if (typeof t == "object" && t && !Array.isArray(t)) {
|
|
200
200
|
let n = t;
|
|
201
201
|
if ("eq" in n) return e === n.eq;
|
|
@@ -206,27 +206,34 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
206
206
|
if ("in" in n && Array.isArray(n.in)) return n.in.includes(e);
|
|
207
207
|
}
|
|
208
208
|
return e === t;
|
|
209
|
-
},
|
|
210
|
-
let n =
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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));
|
|
221
228
|
if (n.length === 0) return;
|
|
222
229
|
let r = new Set(n.map((e) => e.secret));
|
|
223
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.`);
|
|
224
231
|
return [...r][0];
|
|
225
|
-
},
|
|
232
|
+
}, ge = (e, t, n = {}) => {
|
|
226
233
|
let r = new Map((n.events ?? []).map((e) => [e.id, e])), i = {
|
|
227
234
|
subscribe: async (t) => {
|
|
228
|
-
|
|
229
|
-
let n =
|
|
235
|
+
F(t);
|
|
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));
|
|
230
237
|
for (let t of o) await e.store.insert("_voltro_webhook_targets", {
|
|
231
238
|
...t,
|
|
232
239
|
createdAt: /* @__PURE__ */ new Date(),
|
|
@@ -247,27 +254,17 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
247
254
|
};
|
|
248
255
|
},
|
|
249
256
|
emit: async (n, i, a) => {
|
|
250
|
-
let o = typeof n == "object" && n.kind === "event" ?
|
|
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;
|
|
251
258
|
if (s !== void 0) {
|
|
252
|
-
let e =
|
|
253
|
-
if (
|
|
259
|
+
let e = x.decodeUnknownEither(s.payload)(i, { errors: "all" });
|
|
260
|
+
if (te.isLeft(e)) throw new u({
|
|
254
261
|
event: c,
|
|
255
262
|
issues: e.left.message
|
|
256
263
|
});
|
|
257
264
|
}
|
|
258
|
-
let l =
|
|
259
|
-
table: "_voltro_webhook_targets",
|
|
260
|
-
predicate: d == null ? S("event", c) : x(S("event", c), S("tenantId", d)),
|
|
261
|
-
order: [{
|
|
262
|
-
column: "createdAt",
|
|
263
|
-
direction: "asc"
|
|
264
|
-
}],
|
|
265
|
-
take: 1e3,
|
|
266
|
-
skip: void 0,
|
|
267
|
-
projection: void 0
|
|
268
|
-
})).filter((e) => R(e.filter, i)), p = [], m = JSON.stringify(i);
|
|
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);
|
|
269
266
|
for (let n of f) {
|
|
270
|
-
let r =
|
|
267
|
+
let r = y();
|
|
271
268
|
n.active ? (await t({
|
|
272
269
|
deliveryId: r,
|
|
273
270
|
targetId: n.id,
|
|
@@ -305,7 +302,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
305
302
|
replay: async (n) => {
|
|
306
303
|
let r = (await e.store.query({
|
|
307
304
|
table: "_voltro_webhook_deliveries",
|
|
308
|
-
predicate:
|
|
305
|
+
predicate: C("deliveryId", n),
|
|
309
306
|
order: [{
|
|
310
307
|
column: "attempt",
|
|
311
308
|
direction: "asc"
|
|
@@ -319,21 +316,14 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
319
316
|
deliveryId: r.deliveryId,
|
|
320
317
|
targetId: r.targetId,
|
|
321
318
|
event: r.event,
|
|
322
|
-
eventId: r.eventId ??
|
|
319
|
+
eventId: r.eventId ?? y(),
|
|
323
320
|
payloadJson: typeof r.payload == "string" ? r.payload : JSON.stringify(r.payload),
|
|
324
321
|
attemptEpoch: Date.now()
|
|
325
322
|
});
|
|
326
323
|
},
|
|
327
324
|
resolveTargets: async (t) => {
|
|
328
325
|
if (typeof t == "string") return [t];
|
|
329
|
-
let n = (await e.
|
|
330
|
-
table: "_voltro_webhook_targets",
|
|
331
|
-
predicate: void 0,
|
|
332
|
-
order: void 0,
|
|
333
|
-
take: 1e3,
|
|
334
|
-
skip: void 0,
|
|
335
|
-
projection: void 0
|
|
336
|
-
})).filter((e) => z(e.scope, t.scope)).map((e) => e.id);
|
|
326
|
+
let n = (await U(e)).filter((e) => B(e.scope, t.scope)).map((e) => e.id);
|
|
337
327
|
if (n.length === 0) throw Error(`webhooks: no target matches scope ${JSON.stringify(t.scope)} — the operation would have silently done nothing`);
|
|
338
328
|
return n;
|
|
339
329
|
},
|
|
@@ -354,7 +344,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
354
344
|
});
|
|
355
345
|
let n = await e.store.query({
|
|
356
346
|
table: "_voltro_webhook_deliveries",
|
|
357
|
-
predicate:
|
|
347
|
+
predicate: S(C("targetId", r), C("status", "pending")),
|
|
358
348
|
order: [{
|
|
359
349
|
column: "createdAt",
|
|
360
350
|
direction: "asc"
|
|
@@ -367,17 +357,17 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
367
357
|
deliveryId: e.deliveryId,
|
|
368
358
|
targetId: r,
|
|
369
359
|
event: e.event,
|
|
370
|
-
eventId: e.eventId ??
|
|
360
|
+
eventId: e.eventId ?? y(),
|
|
371
361
|
payloadJson: typeof e.payload == "string" ? e.payload : JSON.stringify(e.payload),
|
|
372
362
|
attemptEpoch: i
|
|
373
363
|
});
|
|
374
364
|
}
|
|
375
365
|
},
|
|
376
366
|
deleteTarget: async (t) => {
|
|
377
|
-
for (let n of await i.resolveTargets(t)) await e.store.delete("_voltro_webhook_targets", n), await e.store.deleteMany("_voltro_webhook_deliveries", { where:
|
|
367
|
+
for (let n of await i.resolveTargets(t)) await e.store.delete("_voltro_webhook_targets", n), await e.store.deleteMany("_voltro_webhook_deliveries", { where: S(C("targetId", n), C("status", "pending")) });
|
|
378
368
|
},
|
|
379
369
|
rotateSecret: async (t) => {
|
|
380
|
-
let n =
|
|
370
|
+
let n = N();
|
|
381
371
|
for (let r of await i.resolveTargets(t)) await e.store.update("_voltro_webhook_targets", r, {
|
|
382
372
|
secret: n,
|
|
383
373
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -385,7 +375,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
385
375
|
return { secret: n };
|
|
386
376
|
},
|
|
387
377
|
updateTarget: async (t, n) => {
|
|
388
|
-
if (n.url !== void 0 &&
|
|
378
|
+
if (n.url !== void 0 && F({
|
|
389
379
|
event: "x",
|
|
390
380
|
url: n.url
|
|
391
381
|
}), 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");
|
|
@@ -405,14 +395,14 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
405
395
|
testTarget: async (n, r) => {
|
|
406
396
|
let i = (await e.store.query({
|
|
407
397
|
table: "_voltro_webhook_targets",
|
|
408
|
-
predicate:
|
|
398
|
+
predicate: C("id", n),
|
|
409
399
|
order: void 0,
|
|
410
400
|
take: 1,
|
|
411
401
|
skip: void 0,
|
|
412
402
|
projection: void 0
|
|
413
403
|
}))[0];
|
|
414
404
|
if (i === void 0) throw Error(`testTarget: no target ${n}`);
|
|
415
|
-
let a =
|
|
405
|
+
let a = y(), o = JSON.stringify(r ?? {
|
|
416
406
|
test: !0,
|
|
417
407
|
targetId: n,
|
|
418
408
|
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -421,7 +411,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
421
411
|
deliveryId: a,
|
|
422
412
|
targetId: i.id,
|
|
423
413
|
event: i.event,
|
|
424
|
-
eventId:
|
|
414
|
+
eventId: y(),
|
|
425
415
|
payloadJson: o
|
|
426
416
|
}) : await e.store.insert("_voltro_webhook_deliveries", {
|
|
427
417
|
id: `${a}:1`,
|
|
@@ -441,7 +431,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
441
431
|
listDeliveries: async (t) => {
|
|
442
432
|
let n = t?.scope === void 0 ? void 0 : new Set(await i.resolveTargets({ scope: t.scope })), r = await e.store.query({
|
|
443
433
|
table: "_voltro_webhook_deliveries",
|
|
444
|
-
predicate: t?.targetId === void 0 ? void 0 :
|
|
434
|
+
predicate: t?.targetId === void 0 ? void 0 : C("targetId", t.targetId),
|
|
445
435
|
order: [{
|
|
446
436
|
column: "createdAt",
|
|
447
437
|
direction: "desc"
|
|
@@ -450,20 +440,20 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
450
440
|
skip: void 0,
|
|
451
441
|
projection: void 0
|
|
452
442
|
}), a = t?.since?.getTime();
|
|
453
|
-
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 ||
|
|
443
|
+
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 || ue(e.createdAt) >= a).slice(0, t?.limit ?? 200).map(R);
|
|
454
444
|
},
|
|
455
445
|
getDelivery: async (t) => {
|
|
456
446
|
let n = (await e.store.query({
|
|
457
447
|
table: "_voltro_webhook_deliveries",
|
|
458
|
-
predicate:
|
|
448
|
+
predicate: C("id", t),
|
|
459
449
|
order: void 0,
|
|
460
450
|
take: 1,
|
|
461
451
|
skip: void 0,
|
|
462
452
|
projection: void 0
|
|
463
453
|
}))[0];
|
|
464
454
|
return n === void 0 ? null : {
|
|
465
|
-
...
|
|
466
|
-
payload:
|
|
455
|
+
...R(n),
|
|
456
|
+
payload: de(n.payload),
|
|
467
457
|
responseBody: n.responseBody ?? null
|
|
468
458
|
};
|
|
469
459
|
},
|
|
@@ -477,7 +467,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
477
467
|
listTargets: async (t, n) => {
|
|
478
468
|
let r = await e.store.query({
|
|
479
469
|
table: "_voltro_webhook_targets",
|
|
480
|
-
predicate: t === void 0 ? void 0 :
|
|
470
|
+
predicate: t === void 0 ? void 0 : C("event", t),
|
|
481
471
|
order: [{
|
|
482
472
|
column: "createdAt",
|
|
483
473
|
direction: "desc"
|
|
@@ -503,15 +493,15 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
503
493
|
}
|
|
504
494
|
};
|
|
505
495
|
return i;
|
|
506
|
-
},
|
|
496
|
+
}, _e = b.gen(function* () {
|
|
507
497
|
return yield* j;
|
|
508
|
-
}),
|
|
498
|
+
}), ve = (e) => {
|
|
509
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.");
|
|
510
500
|
return e.webhooks;
|
|
511
|
-
},
|
|
501
|
+
}, ye = 1e4, W = class {
|
|
512
502
|
capacity;
|
|
513
503
|
cache = /* @__PURE__ */ new Map();
|
|
514
|
-
constructor(e =
|
|
504
|
+
constructor(e = ye) {
|
|
515
505
|
this.capacity = e;
|
|
516
506
|
}
|
|
517
507
|
evictExpired() {
|
|
@@ -546,20 +536,20 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
546
536
|
clear() {
|
|
547
537
|
this.cache.clear();
|
|
548
538
|
}
|
|
549
|
-
},
|
|
539
|
+
}, G = null, K = () => (G === null && (G = new W()), G), q = {
|
|
550
540
|
"5m": 5 * 6e4,
|
|
551
541
|
"1h": 60 * 6e4,
|
|
552
542
|
"6h": 360 * 6e4,
|
|
553
543
|
"1d": 1440 * 6e4,
|
|
554
544
|
"7d": 10080 * 6e4,
|
|
555
545
|
"30d": 720 * 60 * 6e4
|
|
556
|
-
},
|
|
546
|
+
}, J = (e) => e === void 0 ? q["7d"] : q[e] ?? q["7d"], Y = (e, t) => ({
|
|
557
547
|
status: e,
|
|
558
548
|
contentType: "application/json; charset=utf-8",
|
|
559
549
|
body: JSON.stringify(t)
|
|
560
|
-
}),
|
|
561
|
-
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 ??
|
|
562
|
-
|
|
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) => {
|
|
552
|
+
T({ scope: `webhook:${e.webhookId}` }).debug("incoming webhook", {
|
|
563
553
|
status: e.status,
|
|
564
554
|
signatureOk: e.signatureOk,
|
|
565
555
|
idempotency: e.idempotency,
|
|
@@ -570,7 +560,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
570
560
|
return async (n) => {
|
|
571
561
|
let u = Date.now();
|
|
572
562
|
if (n.method !== "POST") {
|
|
573
|
-
let t =
|
|
563
|
+
let t = Y(405, {
|
|
574
564
|
error: "method not allowed",
|
|
575
565
|
expected: "POST"
|
|
576
566
|
});
|
|
@@ -591,7 +581,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
591
581
|
r._tag === "custom" && n.headers["x-slack-request-timestamp"] && (o = `${n.headers["x-slack-request-timestamp"]}|${t ?? ""}`);
|
|
592
582
|
let s = a(r, n.rawBody, i, o);
|
|
593
583
|
if (d = s.ok, !s.ok) {
|
|
594
|
-
let t =
|
|
584
|
+
let t = Y(401, {
|
|
595
585
|
error: "invalid signature",
|
|
596
586
|
reason: s.reason
|
|
597
587
|
});
|
|
@@ -617,7 +607,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
617
607
|
f = r;
|
|
618
608
|
} else f = n.rawBody;
|
|
619
609
|
} catch (t) {
|
|
620
|
-
let n =
|
|
610
|
+
let n = Y(400, {
|
|
621
611
|
error: "malformed body",
|
|
622
612
|
reason: t.message
|
|
623
613
|
});
|
|
@@ -632,9 +622,9 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
632
622
|
}
|
|
633
623
|
let p;
|
|
634
624
|
try {
|
|
635
|
-
p =
|
|
625
|
+
p = x.decodeUnknownSync(e.payload)(f);
|
|
636
626
|
} catch (t) {
|
|
637
|
-
let n =
|
|
627
|
+
let n = Y(422, {
|
|
638
628
|
error: "schema validation failed",
|
|
639
629
|
reason: t.message
|
|
640
630
|
});
|
|
@@ -653,7 +643,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
653
643
|
if (m = typeof t == "string" ? n.headers[t.toLowerCase()] : t(n.headers, n.rawBody), m !== void 0 && m.length > 0) {
|
|
654
644
|
let t = s.claim(m, c);
|
|
655
645
|
if (h = t, t === "duplicate") {
|
|
656
|
-
let t =
|
|
646
|
+
let t = Y(200, {
|
|
657
647
|
received: !0,
|
|
658
648
|
duplicate: !0
|
|
659
649
|
});
|
|
@@ -666,7 +656,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
666
656
|
}), t;
|
|
667
657
|
}
|
|
668
658
|
if (t === "inflight") {
|
|
669
|
-
let t =
|
|
659
|
+
let t = Y(409, {
|
|
670
660
|
error: "request already in flight",
|
|
671
661
|
idempotencyKey: m
|
|
672
662
|
});
|
|
@@ -689,7 +679,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
689
679
|
idempotencyKey: m ?? "",
|
|
690
680
|
...r ? { workflows: r } : {}
|
|
691
681
|
}), m !== void 0 && s.commit(m);
|
|
692
|
-
let i =
|
|
682
|
+
let i = Y(200, { received: !0 });
|
|
693
683
|
return l({
|
|
694
684
|
webhookId: e.id,
|
|
695
685
|
status: 200,
|
|
@@ -699,7 +689,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
699
689
|
}), i;
|
|
700
690
|
} catch (t) {
|
|
701
691
|
m !== void 0 && s.release(m);
|
|
702
|
-
let n =
|
|
692
|
+
let n = Y(500, {
|
|
703
693
|
error: "handler threw",
|
|
704
694
|
reason: t.message
|
|
705
695
|
});
|
|
@@ -710,7 +700,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
710
700
|
idempotency: h,
|
|
711
701
|
durationMs: Date.now() - u,
|
|
712
702
|
errorMessage: t.message
|
|
713
|
-
}),
|
|
703
|
+
}), ne({
|
|
714
704
|
error: t,
|
|
715
705
|
source: "webhook",
|
|
716
706
|
name: e.id,
|
|
@@ -718,19 +708,19 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
718
708
|
}), n;
|
|
719
709
|
}
|
|
720
710
|
};
|
|
721
|
-
},
|
|
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({
|
|
722
712
|
table: "_voltro_webhook_rate_windows",
|
|
723
|
-
predicate:
|
|
713
|
+
predicate: S(C("scope", t), C("bucket", n)),
|
|
724
714
|
order: [],
|
|
725
715
|
take: 1,
|
|
726
716
|
skip: void 0,
|
|
727
717
|
projection: void 0
|
|
728
|
-
}))[0] ?? null,
|
|
729
|
-
for (let r = 0; r <
|
|
718
|
+
}))[0] ?? null, Z = 32, Te = async (e, t, n) => {
|
|
719
|
+
for (let r = 0; r < Z; r++) {
|
|
730
720
|
r > 0 && await new Promise((e) => setTimeout(e, r));
|
|
731
|
-
let i = await
|
|
721
|
+
let i = await we(e, t.key, n);
|
|
732
722
|
if (i === null) {
|
|
733
|
-
await e.insertIgnore(
|
|
723
|
+
await e.insertIgnore(X, {
|
|
734
724
|
id: Ce(t.key, n),
|
|
735
725
|
scope: t.key,
|
|
736
726
|
bucket: n,
|
|
@@ -744,31 +734,31 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
744
734
|
if (await e.updateMany("_voltro_webhook_rate_windows", {
|
|
745
735
|
count: i.count + 1,
|
|
746
736
|
updatedAt: /* @__PURE__ */ new Date()
|
|
747
|
-
}, { where:
|
|
737
|
+
}, { where: S(C("id", i.id), C("count", i.count)) }) === 1) return !0;
|
|
748
738
|
}
|
|
749
|
-
throw Error(`webhook rate window: contention on (${t.key}, ${n}) exceeded ${
|
|
750
|
-
},
|
|
751
|
-
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++) {
|
|
752
742
|
r > 0 && await new Promise((e) => setTimeout(e, r));
|
|
753
|
-
let i = await
|
|
743
|
+
let i = await we(e, t, n);
|
|
754
744
|
if (i === null || i.count <= 0 || await e.updateMany("_voltro_webhook_rate_windows", {
|
|
755
745
|
count: i.count - 1,
|
|
756
746
|
updatedAt: /* @__PURE__ */ new Date()
|
|
757
|
-
}, { where:
|
|
747
|
+
}, { where: S(C("id", i.id), C("count", i.count)) }) === 1) return;
|
|
758
748
|
}
|
|
759
|
-
throw Error(`webhook rate window: release contention on (${t}) exceeded ${
|
|
760
|
-
},
|
|
749
|
+
throw Error(`webhook rate window: release contention on (${t}) exceeded ${Z} attempts`);
|
|
750
|
+
}, De = async (e, t, n, r = Se) => {
|
|
761
751
|
if (t.length === 0) return {
|
|
762
752
|
acquired: !0,
|
|
763
753
|
retryInMs: 0
|
|
764
754
|
};
|
|
765
755
|
let i = Math.floor(n / r), a = [];
|
|
766
756
|
for (let o of t) {
|
|
767
|
-
if (await
|
|
757
|
+
if (await Te(e, o, i)) {
|
|
768
758
|
a.push(o);
|
|
769
759
|
continue;
|
|
770
760
|
}
|
|
771
|
-
for (let t of a) await
|
|
761
|
+
for (let t of a) await Ee(e, t.key, i);
|
|
772
762
|
return {
|
|
773
763
|
acquired: !1,
|
|
774
764
|
retryInMs: (i + 1) * r - n
|
|
@@ -778,7 +768,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
778
768
|
acquired: !0,
|
|
779
769
|
retryInMs: 0
|
|
780
770
|
};
|
|
781
|
-
},
|
|
771
|
+
}, Oe = (e, t) => {
|
|
782
772
|
if (e === "json") return {
|
|
783
773
|
contentType: "application/json",
|
|
784
774
|
bytes: Buffer.from(t, "utf8")
|
|
@@ -794,40 +784,40 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
794
784
|
}
|
|
795
785
|
return e === "form" ? {
|
|
796
786
|
contentType: "application/x-www-form-urlencoded",
|
|
797
|
-
bytes: Buffer.from(
|
|
787
|
+
bytes: Buffer.from(ke(n), "utf8")
|
|
798
788
|
} : {
|
|
799
789
|
contentType: "application/xml",
|
|
800
|
-
bytes: Buffer.from(
|
|
790
|
+
bytes: Buffer.from(je(n), "utf8")
|
|
801
791
|
};
|
|
802
|
-
},
|
|
792
|
+
}, ke = (e) => {
|
|
803
793
|
if (typeof e != "object" || !e || Array.isArray(e)) throw new d({
|
|
804
794
|
format: "form",
|
|
805
|
-
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)}`
|
|
806
796
|
});
|
|
807
797
|
let t = new URLSearchParams();
|
|
808
|
-
for (let [n, r] of Object.entries(e))
|
|
798
|
+
for (let [n, r] of Object.entries(e)) Q(t, n, r);
|
|
809
799
|
return t.toString();
|
|
810
|
-
},
|
|
800
|
+
}, Q = (e, t, n) => {
|
|
811
801
|
if (n === null) {
|
|
812
802
|
e.append(t, "");
|
|
813
803
|
return;
|
|
814
804
|
}
|
|
815
805
|
if (Array.isArray(n)) {
|
|
816
|
-
n.forEach((n, r) =>
|
|
806
|
+
n.forEach((n, r) => Q(e, `${t}[${r}]`, n));
|
|
817
807
|
return;
|
|
818
808
|
}
|
|
819
809
|
if (typeof n == "object") {
|
|
820
|
-
for (let [r, i] of Object.entries(n))
|
|
810
|
+
for (let [r, i] of Object.entries(n)) Q(e, `${t}[${r}]`, i);
|
|
821
811
|
return;
|
|
822
812
|
}
|
|
823
813
|
e.append(t, String(n));
|
|
824
|
-
},
|
|
825
|
-
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({
|
|
826
816
|
format: "xml",
|
|
827
817
|
reason: `object key ${JSON.stringify(e)} is not a valid XML element name`
|
|
828
818
|
});
|
|
829
|
-
return `<${e}>${
|
|
830
|
-
},
|
|
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) => {
|
|
831
821
|
switch (e) {
|
|
832
822
|
case "&": return "&";
|
|
833
823
|
case "<": return "<";
|
|
@@ -835,17 +825,17 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
835
825
|
case "\"": return """;
|
|
836
826
|
default: return "'";
|
|
837
827
|
}
|
|
838
|
-
}),
|
|
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({
|
|
839
829
|
table: "_voltro_webhook_targets",
|
|
840
|
-
predicate:
|
|
830
|
+
predicate: C("id", t),
|
|
841
831
|
order: [],
|
|
842
832
|
take: 1,
|
|
843
833
|
skip: void 0,
|
|
844
834
|
projection: void 0
|
|
845
|
-
}))[0] ?? null,
|
|
846
|
-
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++) {
|
|
847
837
|
a > 0 && await new Promise((e) => setTimeout(e, a));
|
|
848
|
-
let o = await
|
|
838
|
+
let o = await Le(e, t);
|
|
849
839
|
if (o === null) return {
|
|
850
840
|
consecutiveFailures: 0,
|
|
851
841
|
autoDisabled: !1
|
|
@@ -855,7 +845,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
855
845
|
if (s === 0 || await e.updateMany("_voltro_webhook_targets", {
|
|
856
846
|
consecutiveFailures: 0,
|
|
857
847
|
updatedAt: i
|
|
858
|
-
}, { where:
|
|
848
|
+
}, { where: S(C("id", t), C("consecutiveFailures", s)) }) === 1) return {
|
|
859
849
|
consecutiveFailures: 0,
|
|
860
850
|
autoDisabled: !1
|
|
861
851
|
};
|
|
@@ -865,68 +855,68 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
865
855
|
consecutiveFailures: c,
|
|
866
856
|
updatedAt: i
|
|
867
857
|
};
|
|
868
|
-
if (u && (d.active = !1, d.autoDisabledAt = i, d.autoDisableReason = r.slice(0, 500)), await e.updateMany("_voltro_webhook_targets", d, { where:
|
|
858
|
+
if (u && (d.active = !1, d.autoDisabledAt = i, d.autoDisableReason = r.slice(0, 500)), await e.updateMany("_voltro_webhook_targets", d, { where: S(C("id", t), C("consecutiveFailures", s)) }) === 1) return {
|
|
869
859
|
consecutiveFailures: c,
|
|
870
860
|
autoDisabled: u
|
|
871
861
|
};
|
|
872
862
|
}
|
|
873
|
-
throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${
|
|
874
|
-
},
|
|
875
|
-
status:
|
|
876
|
-
body:
|
|
877
|
-
latencyMs:
|
|
878
|
-
retryAfterSec:
|
|
879
|
-
transportError:
|
|
880
|
-
encodeError:
|
|
881
|
-
}),
|
|
882
|
-
outcome:
|
|
883
|
-
delayMs:
|
|
884
|
-
reason:
|
|
885
|
-
autoDisabled:
|
|
886
|
-
consecutiveFailures:
|
|
887
|
-
}),
|
|
888
|
-
acquired:
|
|
889
|
-
retryInMs:
|
|
890
|
-
}),
|
|
863
|
+
throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${Ie} attempts`);
|
|
864
|
+
}, ze = x.Struct({
|
|
865
|
+
status: x.Number,
|
|
866
|
+
body: x.String,
|
|
867
|
+
latencyMs: x.Number,
|
|
868
|
+
retryAfterSec: x.Union(x.Number, x.Undefined),
|
|
869
|
+
transportError: x.Union(x.String, x.Undefined),
|
|
870
|
+
encodeError: x.Union(x.String, x.Undefined)
|
|
871
|
+
}), Be = x.Struct({
|
|
872
|
+
outcome: x.Literal("succeeded", "failed", "retry"),
|
|
873
|
+
delayMs: x.Union(x.Number, x.Undefined),
|
|
874
|
+
reason: x.Union(x.String, x.Undefined),
|
|
875
|
+
autoDisabled: x.Union(x.Boolean, x.Undefined),
|
|
876
|
+
consecutiveFailures: x.Union(x.Number, x.Undefined)
|
|
877
|
+
}), Ve = x.Struct({
|
|
878
|
+
acquired: x.Boolean,
|
|
879
|
+
retryInMs: x.Number
|
|
880
|
+
}), He = re.make({
|
|
891
881
|
name: "voltro.deliverWebhook",
|
|
892
882
|
payload: {
|
|
893
|
-
deliveryId:
|
|
894
|
-
targetId:
|
|
895
|
-
event:
|
|
896
|
-
eventId:
|
|
897
|
-
payloadJson:
|
|
898
|
-
attemptEpoch:
|
|
883
|
+
deliveryId: x.String,
|
|
884
|
+
targetId: x.String,
|
|
885
|
+
event: x.String,
|
|
886
|
+
eventId: x.String,
|
|
887
|
+
payloadJson: x.String,
|
|
888
|
+
attemptEpoch: x.optionalWith(x.Number, { default: () => 0 })
|
|
899
889
|
},
|
|
900
|
-
success:
|
|
901
|
-
finalStatus:
|
|
902
|
-
attempts:
|
|
890
|
+
success: x.Struct({
|
|
891
|
+
finalStatus: x.Literal("succeeded", "failed", "deferred"),
|
|
892
|
+
attempts: x.Number
|
|
903
893
|
}),
|
|
904
894
|
idempotencyKey: ({ deliveryId: e, attemptEpoch: t }) => `voltro.deliverWebhook:${e}:${t ?? 0}`
|
|
905
|
-
}),
|
|
906
|
-
let { deliveryId: i, targetId: a, event: s, eventId: c, payloadJson: l } = n, u =
|
|
907
|
-
return
|
|
908
|
-
let n = yield*
|
|
895
|
+
}), Ue = (e, t = {}) => (n, r) => {
|
|
896
|
+
let { deliveryId: i, targetId: a, event: s, eventId: c, payloadJson: l } = n, u = T({ scope: `webhook:${i}` });
|
|
897
|
+
return b.gen(function* () {
|
|
898
|
+
let n = yield* E.make({
|
|
909
899
|
name: "fetch-target",
|
|
910
|
-
success:
|
|
911
|
-
id:
|
|
912
|
-
event:
|
|
913
|
-
url:
|
|
914
|
-
secret:
|
|
915
|
-
signing:
|
|
916
|
-
retry:
|
|
917
|
-
headers:
|
|
918
|
-
key:
|
|
919
|
-
value:
|
|
920
|
-
}),
|
|
921
|
-
rateLimitPerMinute:
|
|
922
|
-
active:
|
|
923
|
-
format:
|
|
924
|
-
tenantId:
|
|
900
|
+
success: x.Union(x.Null, x.Struct({
|
|
901
|
+
id: x.String,
|
|
902
|
+
event: x.String,
|
|
903
|
+
url: x.String,
|
|
904
|
+
secret: x.String,
|
|
905
|
+
signing: x.Any,
|
|
906
|
+
retry: x.Any,
|
|
907
|
+
headers: x.Union(x.Record({
|
|
908
|
+
key: x.String,
|
|
909
|
+
value: x.String
|
|
910
|
+
}), x.Null),
|
|
911
|
+
rateLimitPerMinute: x.Union(x.Number, x.Null),
|
|
912
|
+
active: x.Boolean,
|
|
913
|
+
format: x.Literal("json", "form", "xml"),
|
|
914
|
+
tenantId: x.Union(x.String, x.Null)
|
|
925
915
|
})),
|
|
926
|
-
execute:
|
|
916
|
+
execute: b.tryPromise({
|
|
927
917
|
try: async () => (await e.store.query({
|
|
928
918
|
table: "_voltro_webhook_targets",
|
|
929
|
-
predicate:
|
|
919
|
+
predicate: C("id", a),
|
|
930
920
|
order: [{
|
|
931
921
|
column: "createdAt",
|
|
932
922
|
direction: "asc"
|
|
@@ -940,7 +930,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
940
930
|
event: s,
|
|
941
931
|
cause: String(e)
|
|
942
932
|
}), /* @__PURE__ */ Error(`fetch-target failed: ${e.message}`))
|
|
943
|
-
}).pipe(
|
|
933
|
+
}).pipe(b.orDie)
|
|
944
934
|
});
|
|
945
935
|
if (n === null) return {
|
|
946
936
|
finalStatus: "failed",
|
|
@@ -965,75 +955,75 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
965
955
|
...o
|
|
966
956
|
});
|
|
967
957
|
};
|
|
968
|
-
if (n.active !== !0) return yield*
|
|
958
|
+
if (n.active !== !0) return yield* E.make({
|
|
969
959
|
name: "record-paused-pending",
|
|
970
|
-
success:
|
|
971
|
-
execute:
|
|
960
|
+
success: x.Void,
|
|
961
|
+
execute: b.tryPromise({
|
|
972
962
|
try: () => r(`${i}:1`, 1, { status: "pending" }),
|
|
973
963
|
catch: (e) => /* @__PURE__ */ Error(`record-paused-pending failed: ${e.message}`)
|
|
974
|
-
}).pipe(
|
|
964
|
+
}).pipe(b.orDie)
|
|
975
965
|
}), {
|
|
976
966
|
finalStatus: "deferred",
|
|
977
967
|
attempts: 0
|
|
978
968
|
};
|
|
979
|
-
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),
|
|
969
|
+
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 ? [{
|
|
980
970
|
key: `target:${n.id}`,
|
|
981
971
|
limit: n.rateLimitPerMinute
|
|
982
|
-
}] : [], ...
|
|
972
|
+
}] : [], ..._ !== void 0 && _.perMinute > 0 ? [{
|
|
983
973
|
key: `event:${s}`,
|
|
984
|
-
limit:
|
|
974
|
+
limit: _.perMinute
|
|
985
975
|
}] : []];
|
|
986
976
|
for (let t = 1; t <= p.maxAttempts; t++) {
|
|
987
977
|
let f = `${i}:${t}`;
|
|
988
|
-
if (
|
|
989
|
-
let i = yield*
|
|
978
|
+
if (v.length > 0) for (let n = 0;; n++) {
|
|
979
|
+
let i = yield* E.make({
|
|
990
980
|
name: `rate-acquire-${t}-${n}`,
|
|
991
|
-
success:
|
|
992
|
-
execute:
|
|
993
|
-
try: () =>
|
|
981
|
+
success: Ve,
|
|
982
|
+
execute: b.tryPromise({
|
|
983
|
+
try: () => De(e.store, v, Date.now(), g),
|
|
994
984
|
catch: (e) => (u.debug("rate-acquire raw error", {
|
|
995
985
|
targetId: a,
|
|
996
986
|
event: s,
|
|
997
987
|
attempt: t,
|
|
998
988
|
cause: String(e)
|
|
999
989
|
}), /* @__PURE__ */ Error(`rate-acquire failed: ${e.message}`))
|
|
1000
|
-
}).pipe(
|
|
990
|
+
}).pipe(b.orDie)
|
|
1001
991
|
});
|
|
1002
992
|
if (i.acquired) break;
|
|
1003
|
-
yield*
|
|
993
|
+
yield* E.make({
|
|
1004
994
|
name: `record-rate-deferral-${t}-${n}`,
|
|
1005
|
-
success:
|
|
1006
|
-
execute:
|
|
995
|
+
success: x.Void,
|
|
996
|
+
execute: b.tryPromise({
|
|
1007
997
|
try: () => r(f, t, {
|
|
1008
998
|
status: "pending",
|
|
1009
999
|
nextAttemptAt: new Date(Date.now() + i.retryInMs)
|
|
1010
1000
|
}),
|
|
1011
1001
|
catch: (e) => /* @__PURE__ */ Error(`record-rate-deferral failed: ${e.message}`)
|
|
1012
|
-
}).pipe(
|
|
1013
|
-
}), yield*
|
|
1002
|
+
}).pipe(b.orDie)
|
|
1003
|
+
}), yield* D.sleep({
|
|
1014
1004
|
name: `rate-window-sleep-${t}-${n}`,
|
|
1015
1005
|
duration: `${i.retryInMs} millis`
|
|
1016
1006
|
});
|
|
1017
1007
|
}
|
|
1018
|
-
yield*
|
|
1008
|
+
yield* E.make({
|
|
1019
1009
|
name: `record-attempt-start-${t}`,
|
|
1020
|
-
success:
|
|
1021
|
-
execute:
|
|
1010
|
+
success: x.Void,
|
|
1011
|
+
execute: b.tryPromise({
|
|
1022
1012
|
try: () => r(f, t, {
|
|
1023
1013
|
status: "inFlight",
|
|
1024
1014
|
nextAttemptAt: null,
|
|
1025
1015
|
scheduledAt: /* @__PURE__ */ new Date()
|
|
1026
1016
|
}),
|
|
1027
1017
|
catch: (e) => /* @__PURE__ */ Error(`record-attempt-start failed: ${e.message}`)
|
|
1028
|
-
}).pipe(
|
|
1018
|
+
}).pipe(b.orDie)
|
|
1029
1019
|
});
|
|
1030
|
-
let
|
|
1020
|
+
let _ = yield* E.make({
|
|
1031
1021
|
name: `sign-and-post-${t}`,
|
|
1032
|
-
success:
|
|
1033
|
-
execute:
|
|
1022
|
+
success: ze,
|
|
1023
|
+
execute: b.promise(async () => {
|
|
1034
1024
|
let e = Date.now(), r;
|
|
1035
1025
|
try {
|
|
1036
|
-
r =
|
|
1026
|
+
r = Oe(n.format, l);
|
|
1037
1027
|
} catch (t) {
|
|
1038
1028
|
if (t instanceof d) return {
|
|
1039
1029
|
status: 0,
|
|
@@ -1046,7 +1036,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1046
1036
|
throw t;
|
|
1047
1037
|
}
|
|
1048
1038
|
let a = Buffer.from(r.bytes), { headerName: u, headerValue: f } = o(m, a, n.secret);
|
|
1049
|
-
process.env.NODE_ENV === "production" &&
|
|
1039
|
+
process.env.NODE_ENV === "production" && w(n.url);
|
|
1050
1040
|
try {
|
|
1051
1041
|
let o = await fetch(n.url, {
|
|
1052
1042
|
method: "POST",
|
|
@@ -1081,18 +1071,18 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1081
1071
|
};
|
|
1082
1072
|
}
|
|
1083
1073
|
})
|
|
1084
|
-
}),
|
|
1074
|
+
}), y = yield* E.make({
|
|
1085
1075
|
name: `classify-and-record-${t}`,
|
|
1086
|
-
success:
|
|
1087
|
-
execute:
|
|
1076
|
+
success: Be,
|
|
1077
|
+
execute: b.tryPromise({
|
|
1088
1078
|
try: async () => {
|
|
1089
|
-
let r =
|
|
1079
|
+
let r = _.status >= 200 && _.status < 300, i = _.transportError !== void 0, o = _.encodeError !== void 0, s = (t, n) => Re(e.store, a, t, n);
|
|
1090
1080
|
if (r) {
|
|
1091
1081
|
await e.store.update("_voltro_webhook_deliveries", f, {
|
|
1092
1082
|
status: "succeeded",
|
|
1093
|
-
responseStatus:
|
|
1094
|
-
responseBody:
|
|
1095
|
-
latencyMs:
|
|
1083
|
+
responseStatus: _.status,
|
|
1084
|
+
responseBody: _.body,
|
|
1085
|
+
latencyMs: _.latencyMs,
|
|
1096
1086
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1097
1087
|
});
|
|
1098
1088
|
let t = await s(!0, "");
|
|
@@ -1104,18 +1094,18 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1104
1094
|
consecutiveFailures: t.consecutiveFailures
|
|
1105
1095
|
};
|
|
1106
1096
|
}
|
|
1107
|
-
let c = o ? null :
|
|
1108
|
-
...i ? {} : { status:
|
|
1109
|
-
...
|
|
1097
|
+
let c = o ? null : oe(p, t, {
|
|
1098
|
+
...i ? {} : { status: _.status },
|
|
1099
|
+
..._.retryAfterSec === void 0 ? {} : { retryAfterSeconds: _.retryAfterSec }
|
|
1110
1100
|
});
|
|
1111
1101
|
if (c === null) {
|
|
1112
|
-
let t = o ? `encode (${n.format}): ${
|
|
1102
|
+
let t = o ? `encode (${n.format}): ${_.encodeError}` : i ? `transport: ${_.transportError}` : `http ${_.status}: ${_.body.slice(0, 200)}`;
|
|
1113
1103
|
await e.store.update("_voltro_webhook_deliveries", f, {
|
|
1114
1104
|
status: "failed",
|
|
1115
|
-
responseStatus: i || o ? null :
|
|
1116
|
-
responseBody: i || o ? null :
|
|
1117
|
-
errorMessage: o ? t :
|
|
1118
|
-
latencyMs:
|
|
1105
|
+
responseStatus: i || o ? null : _.status,
|
|
1106
|
+
responseBody: i || o ? null : _.body,
|
|
1107
|
+
errorMessage: o ? t : _.transportError ?? null,
|
|
1108
|
+
latencyMs: _.latencyMs,
|
|
1119
1109
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1120
1110
|
});
|
|
1121
1111
|
let r = await s(!1, t);
|
|
@@ -1129,10 +1119,10 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1129
1119
|
}
|
|
1130
1120
|
return await e.store.update("_voltro_webhook_deliveries", f, {
|
|
1131
1121
|
status: "retryScheduled",
|
|
1132
|
-
responseStatus: i ? null :
|
|
1133
|
-
responseBody: i ? null :
|
|
1134
|
-
errorMessage:
|
|
1135
|
-
latencyMs:
|
|
1122
|
+
responseStatus: i ? null : _.status,
|
|
1123
|
+
responseBody: i ? null : _.body,
|
|
1124
|
+
errorMessage: _.transportError ?? null,
|
|
1125
|
+
latencyMs: _.latencyMs,
|
|
1136
1126
|
nextAttemptAt: new Date(Date.now() + c.delayMs),
|
|
1137
1127
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1138
1128
|
}), {
|
|
@@ -1149,24 +1139,24 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1149
1139
|
attempt: t,
|
|
1150
1140
|
cause: String(e)
|
|
1151
1141
|
}), /* @__PURE__ */ Error(`classify-and-record failed: ${e.message}`))
|
|
1152
|
-
}).pipe(
|
|
1142
|
+
}).pipe(b.orDie)
|
|
1153
1143
|
});
|
|
1154
|
-
if (
|
|
1144
|
+
if (y.autoDisabled === !0 && u.warn("target auto-disabled after consecutive failures", {
|
|
1155
1145
|
targetId: a,
|
|
1156
1146
|
event: s,
|
|
1157
|
-
consecutiveFailures:
|
|
1158
|
-
reason:
|
|
1159
|
-
}),
|
|
1147
|
+
consecutiveFailures: y.consecutiveFailures ?? null,
|
|
1148
|
+
reason: y.reason ?? null
|
|
1149
|
+
}), y.outcome === "succeeded") return {
|
|
1160
1150
|
finalStatus: "succeeded",
|
|
1161
1151
|
attempts: t
|
|
1162
1152
|
};
|
|
1163
|
-
if (
|
|
1153
|
+
if (y.outcome === "failed") return {
|
|
1164
1154
|
finalStatus: "failed",
|
|
1165
1155
|
attempts: t
|
|
1166
1156
|
};
|
|
1167
|
-
|
|
1157
|
+
y.delayMs !== void 0 && (yield* D.sleep({
|
|
1168
1158
|
name: `retry-sleep-${t}`,
|
|
1169
|
-
duration: `${
|
|
1159
|
+
duration: `${y.delayMs} millis`
|
|
1170
1160
|
}));
|
|
1171
1161
|
}
|
|
1172
1162
|
return {
|
|
@@ -1176,4 +1166,4 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1176
1166
|
});
|
|
1177
1167
|
};
|
|
1178
1168
|
//#endregion
|
|
1179
|
-
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 };
|