@voltro/plugin-webhooks 0.28.0 → 0.30.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 +655 -0
- package/THIRD-PARTY-NOTICES.md +87 -1
- package/dist/index.d.ts +170 -16
- package/dist/index.js +256 -227
- package/dist/mixin.d.ts +79 -2
- package/dist/mixin.js +11 -3
- package/dist/providers/index.d.ts +2 -6
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,15 +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,
|
|
4
|
-
import { randomBytes as
|
|
5
|
-
import { Context as te, Effect as
|
|
6
|
-
import { and as
|
|
7
|
-
import { assertPublicUrl as
|
|
3
|
+
import { _voltroWebhookDeliveriesTable as m, _voltroWebhookEventStatsTable as h, _voltroWebhookRateWindowsTable as ee, _voltroWebhookTargetsTable as g, webhookTables as _ } from "./mixin.js";
|
|
4
|
+
import { randomBytes as v, randomUUID as y } from "node:crypto";
|
|
5
|
+
import { Context as te, Effect as b, Either as ne, 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
8
|
import { publishServerError as re } from "@voltro/protocol";
|
|
9
|
-
import { createLogger as
|
|
10
|
-
import { Activity as
|
|
9
|
+
import { createLogger as T } from "@voltro/logger";
|
|
10
|
+
import { Activity as E, DurableClock as D, Workflow as ie } from "@effect/workflow";
|
|
11
|
+
import { defineOutboxHandler as ae } from "@voltro/runtime";
|
|
11
12
|
//#region src/eventAudience.ts
|
|
12
|
-
var oe = (e) => e.webhook !== void 0,
|
|
13
|
+
var oe = (e) => e.webhook !== void 0, O = (e) => {
|
|
13
14
|
let t = e.webhook;
|
|
14
15
|
if (t !== void 0) return {
|
|
15
16
|
_tag: "outgoingEvent",
|
|
@@ -19,7 +20,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
19
20
|
...t.version === void 0 ? {} : { version: t.version },
|
|
20
21
|
...t.rateLimit === void 0 ? {} : { globalRateLimit: t.rateLimit }
|
|
21
22
|
};
|
|
22
|
-
}, se = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/,
|
|
23
|
+
}, se = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, k = (e) => {
|
|
23
24
|
let t = se.exec(e);
|
|
24
25
|
if (!t) throw Error(`invalid duration literal: ${e}`);
|
|
25
26
|
let n = Number(t[1]), r = t[2] ?? "s";
|
|
@@ -31,7 +32,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
31
32
|
case "d": return n * 864e5;
|
|
32
33
|
default: throw Error(`unreachable unit: ${r}`);
|
|
33
34
|
}
|
|
34
|
-
},
|
|
35
|
+
}, A = [
|
|
35
36
|
408,
|
|
36
37
|
425,
|
|
37
38
|
429,
|
|
@@ -39,16 +40,16 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
39
40
|
502,
|
|
40
41
|
503,
|
|
41
42
|
504
|
|
42
|
-
],
|
|
43
|
-
if (t >= e.maxAttempts || n?.status !== void 0 && !(e.retryOn ??
|
|
43
|
+
], ce = (e, t, n) => {
|
|
44
|
+
if (t >= e.maxAttempts || n?.status !== void 0 && !(e.retryOn ?? A).includes(n.status)) return null;
|
|
44
45
|
if (e.honourRetryAfter !== !1 && n?.retryAfterSeconds !== void 0) {
|
|
45
|
-
let t = n.retryAfterSeconds * 1e3, r =
|
|
46
|
+
let t = n.retryAfterSeconds * 1e3, r = k(e.maxDelay), i = Math.min(t, r);
|
|
46
47
|
return {
|
|
47
48
|
delayMs: i,
|
|
48
49
|
baseDelayMs: i
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
|
-
let r =
|
|
52
|
+
let r = k(e.initialDelay), i = k(e.maxDelay), a;
|
|
52
53
|
switch (e.strategy) {
|
|
53
54
|
case "fixed":
|
|
54
55
|
a = r;
|
|
@@ -66,23 +67,23 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
66
67
|
delayMs: Math.max(1, Math.floor(o)),
|
|
67
68
|
baseDelayMs: a
|
|
68
69
|
};
|
|
69
|
-
},
|
|
70
|
+
}, le = () => ({
|
|
70
71
|
strategy: "exponential",
|
|
71
72
|
maxAttempts: 8,
|
|
72
73
|
initialDelay: "5s",
|
|
73
74
|
maxDelay: "1h",
|
|
74
|
-
retryOn:
|
|
75
|
+
retryOn: A,
|
|
75
76
|
honourRetryAfter: !0,
|
|
76
77
|
jitter: "full"
|
|
77
|
-
}),
|
|
78
|
+
}), ue = () => ({
|
|
78
79
|
strategy: "fixed",
|
|
79
80
|
maxAttempts: 120,
|
|
80
81
|
initialDelay: "30s",
|
|
81
82
|
maxDelay: "30s",
|
|
82
|
-
retryOn:
|
|
83
|
+
retryOn: A,
|
|
83
84
|
honourRetryAfter: !0,
|
|
84
85
|
jitter: "full"
|
|
85
|
-
}), j = class extends te.Tag("@voltro/webhooks/WebhooksService")() {},
|
|
86
|
+
}), j = class extends te.Tag("@voltro/webhooks/WebhooksService")() {}, de = 32, M = 32, N = () => v(de).toString("hex"), P = (e) => e.events ?? (e.event === void 0 ? [] : [e.event]), F = (e) => {
|
|
86
87
|
let t;
|
|
87
88
|
try {
|
|
88
89
|
t = new URL(e.url);
|
|
@@ -97,14 +98,14 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
97
98
|
reason: `url protocol must be http or https, got ${t.protocol}`
|
|
98
99
|
});
|
|
99
100
|
if (process.env.NODE_ENV === "production") try {
|
|
100
|
-
|
|
101
|
+
w(e.url);
|
|
101
102
|
} catch (e) {
|
|
102
103
|
throw new p({
|
|
103
104
|
field: "url",
|
|
104
105
|
reason: e.message
|
|
105
106
|
});
|
|
106
107
|
}
|
|
107
|
-
let n =
|
|
108
|
+
let n = P(e);
|
|
108
109
|
if (n.length === 0) throw new p({
|
|
109
110
|
field: "event",
|
|
110
111
|
reason: "event id required"
|
|
@@ -121,9 +122,9 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
121
122
|
field: "events",
|
|
122
123
|
reason: "duplicate event — one row per event, so a repeat would deliver twice"
|
|
123
124
|
});
|
|
124
|
-
if (e.secret !== void 0 && e.secret.length <
|
|
125
|
+
if (e.secret !== void 0 && e.secret.length < M) throw new p({
|
|
125
126
|
field: "secret",
|
|
126
|
-
reason: `secret must be at least ${
|
|
127
|
+
reason: `secret must be at least ${M} chars`
|
|
127
128
|
});
|
|
128
129
|
if (e.retry !== void 0) {
|
|
129
130
|
if (e.retry.maxAttempts < 1) throw new p({
|
|
@@ -143,13 +144,14 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
143
144
|
field: "autoDisableAfter",
|
|
144
145
|
reason: `autoDisableAfter must be a positive integer, got ${e.autoDisableAfter}`
|
|
145
146
|
});
|
|
146
|
-
},
|
|
147
|
-
id:
|
|
147
|
+
}, I = (e, t, n, r) => ({
|
|
148
|
+
id: y(),
|
|
149
|
+
tenantId: e.tenantId ?? null,
|
|
148
150
|
event: n ?? e.event ?? "",
|
|
149
151
|
url: e.url,
|
|
150
|
-
secret: r ?? e.secret ??
|
|
152
|
+
secret: r ?? e.secret ?? N(),
|
|
151
153
|
signing: e.signing ?? t?.defaultSigning ?? c(),
|
|
152
|
-
retry: e.retry ?? t?.defaultRetry ??
|
|
154
|
+
retry: e.retry ?? t?.defaultRetry ?? le(),
|
|
153
155
|
filter: e.filter ?? null,
|
|
154
156
|
scope: e.scope ?? null,
|
|
155
157
|
headers: e.headers ?? null,
|
|
@@ -162,14 +164,14 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
162
164
|
autoDisableReason: null,
|
|
163
165
|
payloadVersion: e.payloadVersion ?? t?.version ?? 1,
|
|
164
166
|
description: e.description ?? null
|
|
165
|
-
}),
|
|
167
|
+
}), fe = (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, pe = (e) => {
|
|
166
168
|
if (typeof e != "string") return e;
|
|
167
169
|
try {
|
|
168
170
|
return JSON.parse(e);
|
|
169
171
|
} catch {
|
|
170
172
|
return e;
|
|
171
173
|
}
|
|
172
|
-
},
|
|
174
|
+
}, R = (e) => ({
|
|
173
175
|
id: String(e.id),
|
|
174
176
|
deliveryId: String(e.deliveryId),
|
|
175
177
|
targetId: String(e.targetId),
|
|
@@ -180,22 +182,22 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
180
182
|
responseStatus: e.responseStatus ?? null,
|
|
181
183
|
errorMessage: e.errorMessage ?? null,
|
|
182
184
|
latencyMs: e.latencyMs ?? null,
|
|
183
|
-
scheduledAt:
|
|
184
|
-
nextAttemptAt:
|
|
185
|
-
createdAt:
|
|
186
|
-
}),
|
|
185
|
+
scheduledAt: L(e.scheduledAt),
|
|
186
|
+
nextAttemptAt: L(e.nextAttemptAt),
|
|
187
|
+
createdAt: L(e.createdAt)
|
|
188
|
+
}), me = (e, t) => e === t ? "current" : e < t ? "behind" : "ahead", z = (e, t) => {
|
|
187
189
|
if (e === null) return !0;
|
|
188
190
|
let n = { payload: t };
|
|
189
|
-
for (let [t, r] of Object.entries(e)) if (!he(
|
|
191
|
+
for (let [t, r] of Object.entries(e)) if (!ge(he(n, t), r)) return !1;
|
|
190
192
|
return !0;
|
|
191
|
-
},
|
|
193
|
+
}, he = (e, t) => {
|
|
192
194
|
let n = t.split("."), r = e;
|
|
193
195
|
for (let e of n) {
|
|
194
196
|
if (typeof r != "object" || !r) return;
|
|
195
197
|
r = r[e];
|
|
196
198
|
}
|
|
197
199
|
return r;
|
|
198
|
-
},
|
|
200
|
+
}, ge = (e, t) => {
|
|
199
201
|
if (typeof t == "object" && t && !Array.isArray(t)) {
|
|
200
202
|
let n = t;
|
|
201
203
|
if ("eq" in n) return e === n.eq;
|
|
@@ -206,27 +208,60 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
206
208
|
if ("in" in n && Array.isArray(n.in)) return n.in.includes(e);
|
|
207
209
|
}
|
|
208
210
|
return e === t;
|
|
209
|
-
},
|
|
210
|
-
let n =
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
211
|
+
}, B = (e, t) => e !== null && Object.entries(t).every(([t, n]) => e[t] === n), V = 500, H = 200, U = async (e, t) => {
|
|
212
|
+
let n = [];
|
|
213
|
+
for (let r = 0; r < H; r += 1) {
|
|
214
|
+
let i = await e.store.query({
|
|
215
|
+
table: "_voltro_webhook_targets",
|
|
216
|
+
predicate: t,
|
|
217
|
+
order: [{
|
|
218
|
+
column: "id",
|
|
219
|
+
direction: "asc"
|
|
220
|
+
}],
|
|
221
|
+
take: V,
|
|
222
|
+
skip: r === 0 ? void 0 : r * V,
|
|
223
|
+
projection: void 0
|
|
224
|
+
});
|
|
225
|
+
if (n.push(...i), i.length < V) return n;
|
|
226
|
+
}
|
|
227
|
+
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
|
+
}, _e = async (e, t) => {
|
|
229
|
+
let n = (await U(e)).filter((e) => B(e.scope, t));
|
|
221
230
|
if (n.length === 0) return;
|
|
222
231
|
let r = new Set(n.map((e) => e.secret));
|
|
223
232
|
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
233
|
return [...r][0];
|
|
225
|
-
},
|
|
234
|
+
}, ve = async (e, t) => {
|
|
235
|
+
try {
|
|
236
|
+
let n = await e.store.query({
|
|
237
|
+
table: "_voltro_webhook_event_stats",
|
|
238
|
+
predicate: C("event", t),
|
|
239
|
+
order: [],
|
|
240
|
+
take: 1,
|
|
241
|
+
skip: void 0,
|
|
242
|
+
projection: void 0
|
|
243
|
+
}), r = /* @__PURE__ */ new Date(), i = n[0];
|
|
244
|
+
if (i === void 0) {
|
|
245
|
+
await e.store.insert("_voltro_webhook_event_stats", {
|
|
246
|
+
event: t,
|
|
247
|
+
emitCount: 1,
|
|
248
|
+
lastEmitAt: r,
|
|
249
|
+
createdAt: r,
|
|
250
|
+
updatedAt: r
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
await e.store.update("_voltro_webhook_event_stats", i.id, {
|
|
255
|
+
emitCount: (i.emitCount ?? 0) + 1,
|
|
256
|
+
lastEmitAt: r,
|
|
257
|
+
updatedAt: r
|
|
258
|
+
});
|
|
259
|
+
} catch {}
|
|
260
|
+
}, ye = (e, t, n = {}) => {
|
|
226
261
|
let r = new Map((n.events ?? []).map((e) => [e.id, e])), i = {
|
|
227
262
|
subscribe: async (t) => {
|
|
228
|
-
|
|
229
|
-
let n =
|
|
263
|
+
F(t);
|
|
264
|
+
let n = P(t), i = t.secret === void 0 && t.scope !== void 0 ? await _e(e, t.scope) : void 0, a = t.secret ?? i ?? N(), o = n.map((e) => I(t, r.get(e), e, a));
|
|
230
265
|
for (let t of o) await e.store.insert("_voltro_webhook_targets", {
|
|
231
266
|
...t,
|
|
232
267
|
createdAt: /* @__PURE__ */ new Date(),
|
|
@@ -247,34 +282,26 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
247
282
|
};
|
|
248
283
|
},
|
|
249
284
|
emit: async (n, i, a) => {
|
|
250
|
-
let o = typeof n == "object" && n.kind === "event" ?
|
|
285
|
+
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
286
|
if (s !== void 0) {
|
|
252
|
-
let e =
|
|
287
|
+
let e = x.decodeUnknownEither(s.payload)(i, { errors: "all" });
|
|
253
288
|
if (ne.isLeft(e)) throw new u({
|
|
254
289
|
event: c,
|
|
255
290
|
issues: e.left.message
|
|
256
291
|
});
|
|
257
292
|
}
|
|
258
|
-
let l =
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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);
|
|
269
|
-
for (let n of f) {
|
|
270
|
-
let r = v();
|
|
293
|
+
let l = y(), d = a?.tenantId, f = await U(e, d == null ? C("event", c) : S(C("event", c), C("tenantId", d)));
|
|
294
|
+
await ve(e, c);
|
|
295
|
+
let p = f.filter((e) => z(e.filter, i)), m = [], h = JSON.stringify(i);
|
|
296
|
+
for (let n of p) {
|
|
297
|
+
let r = y();
|
|
271
298
|
n.active ? (await t({
|
|
272
299
|
deliveryId: r,
|
|
273
300
|
targetId: n.id,
|
|
274
301
|
event: c,
|
|
275
302
|
eventId: l,
|
|
276
|
-
payloadJson:
|
|
277
|
-
}),
|
|
303
|
+
payloadJson: h
|
|
304
|
+
}), m.push({
|
|
278
305
|
targetId: n.id,
|
|
279
306
|
deliveryId: r,
|
|
280
307
|
status: "dispatched"
|
|
@@ -287,11 +314,11 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
287
314
|
eventId: l,
|
|
288
315
|
attempt: 1,
|
|
289
316
|
status: "pending",
|
|
290
|
-
payload:
|
|
317
|
+
payload: h,
|
|
291
318
|
scheduledAt: /* @__PURE__ */ new Date(),
|
|
292
319
|
createdAt: /* @__PURE__ */ new Date(),
|
|
293
320
|
updatedAt: /* @__PURE__ */ new Date()
|
|
294
|
-
}),
|
|
321
|
+
}), m.push({
|
|
295
322
|
targetId: n.id,
|
|
296
323
|
deliveryId: r,
|
|
297
324
|
status: "queued"
|
|
@@ -299,13 +326,13 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
299
326
|
}
|
|
300
327
|
return {
|
|
301
328
|
eventId: l,
|
|
302
|
-
deliveries:
|
|
329
|
+
deliveries: m
|
|
303
330
|
};
|
|
304
331
|
},
|
|
305
332
|
replay: async (n) => {
|
|
306
333
|
let r = (await e.store.query({
|
|
307
334
|
table: "_voltro_webhook_deliveries",
|
|
308
|
-
predicate:
|
|
335
|
+
predicate: C("deliveryId", n),
|
|
309
336
|
order: [{
|
|
310
337
|
column: "attempt",
|
|
311
338
|
direction: "asc"
|
|
@@ -319,21 +346,14 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
319
346
|
deliveryId: r.deliveryId,
|
|
320
347
|
targetId: r.targetId,
|
|
321
348
|
event: r.event,
|
|
322
|
-
eventId: r.eventId ??
|
|
349
|
+
eventId: r.eventId ?? y(),
|
|
323
350
|
payloadJson: typeof r.payload == "string" ? r.payload : JSON.stringify(r.payload),
|
|
324
351
|
attemptEpoch: Date.now()
|
|
325
352
|
});
|
|
326
353
|
},
|
|
327
354
|
resolveTargets: async (t) => {
|
|
328
355
|
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);
|
|
356
|
+
let n = (await U(e)).filter((e) => B(e.scope, t.scope)).map((e) => e.id);
|
|
337
357
|
if (n.length === 0) throw Error(`webhooks: no target matches scope ${JSON.stringify(t.scope)} — the operation would have silently done nothing`);
|
|
338
358
|
return n;
|
|
339
359
|
},
|
|
@@ -354,7 +374,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
354
374
|
});
|
|
355
375
|
let n = await e.store.query({
|
|
356
376
|
table: "_voltro_webhook_deliveries",
|
|
357
|
-
predicate:
|
|
377
|
+
predicate: S(C("targetId", r), C("status", "pending")),
|
|
358
378
|
order: [{
|
|
359
379
|
column: "createdAt",
|
|
360
380
|
direction: "asc"
|
|
@@ -367,17 +387,17 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
367
387
|
deliveryId: e.deliveryId,
|
|
368
388
|
targetId: r,
|
|
369
389
|
event: e.event,
|
|
370
|
-
eventId: e.eventId ??
|
|
390
|
+
eventId: e.eventId ?? y(),
|
|
371
391
|
payloadJson: typeof e.payload == "string" ? e.payload : JSON.stringify(e.payload),
|
|
372
392
|
attemptEpoch: i
|
|
373
393
|
});
|
|
374
394
|
}
|
|
375
395
|
},
|
|
376
396
|
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:
|
|
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: S(C("targetId", n), C("status", "pending")) });
|
|
378
398
|
},
|
|
379
399
|
rotateSecret: async (t) => {
|
|
380
|
-
let n =
|
|
400
|
+
let n = N();
|
|
381
401
|
for (let r of await i.resolveTargets(t)) await e.store.update("_voltro_webhook_targets", r, {
|
|
382
402
|
secret: n,
|
|
383
403
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -385,7 +405,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
385
405
|
return { secret: n };
|
|
386
406
|
},
|
|
387
407
|
updateTarget: async (t, n) => {
|
|
388
|
-
if (n.url !== void 0 &&
|
|
408
|
+
if (n.url !== void 0 && F({
|
|
389
409
|
event: "x",
|
|
390
410
|
url: n.url
|
|
391
411
|
}), 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 +425,14 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
405
425
|
testTarget: async (n, r) => {
|
|
406
426
|
let i = (await e.store.query({
|
|
407
427
|
table: "_voltro_webhook_targets",
|
|
408
|
-
predicate:
|
|
428
|
+
predicate: C("id", n),
|
|
409
429
|
order: void 0,
|
|
410
430
|
take: 1,
|
|
411
431
|
skip: void 0,
|
|
412
432
|
projection: void 0
|
|
413
433
|
}))[0];
|
|
414
434
|
if (i === void 0) throw Error(`testTarget: no target ${n}`);
|
|
415
|
-
let a =
|
|
435
|
+
let a = y(), o = JSON.stringify(r ?? {
|
|
416
436
|
test: !0,
|
|
417
437
|
targetId: n,
|
|
418
438
|
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -421,7 +441,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
421
441
|
deliveryId: a,
|
|
422
442
|
targetId: i.id,
|
|
423
443
|
event: i.event,
|
|
424
|
-
eventId:
|
|
444
|
+
eventId: y(),
|
|
425
445
|
payloadJson: o
|
|
426
446
|
}) : await e.store.insert("_voltro_webhook_deliveries", {
|
|
427
447
|
id: `${a}:1`,
|
|
@@ -441,7 +461,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
441
461
|
listDeliveries: async (t) => {
|
|
442
462
|
let n = t?.scope === void 0 ? void 0 : new Set(await i.resolveTargets({ scope: t.scope })), r = await e.store.query({
|
|
443
463
|
table: "_voltro_webhook_deliveries",
|
|
444
|
-
predicate: t?.targetId === void 0 ? void 0 :
|
|
464
|
+
predicate: t?.targetId === void 0 ? void 0 : C("targetId", t.targetId),
|
|
445
465
|
order: [{
|
|
446
466
|
column: "createdAt",
|
|
447
467
|
direction: "desc"
|
|
@@ -450,20 +470,20 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
450
470
|
skip: void 0,
|
|
451
471
|
projection: void 0
|
|
452
472
|
}), 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 ||
|
|
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 || fe(e.createdAt) >= a).slice(0, t?.limit ?? 200).map(R);
|
|
454
474
|
},
|
|
455
475
|
getDelivery: async (t) => {
|
|
456
476
|
let n = (await e.store.query({
|
|
457
477
|
table: "_voltro_webhook_deliveries",
|
|
458
|
-
predicate:
|
|
478
|
+
predicate: C("id", t),
|
|
459
479
|
order: void 0,
|
|
460
480
|
take: 1,
|
|
461
481
|
skip: void 0,
|
|
462
482
|
projection: void 0
|
|
463
483
|
}))[0];
|
|
464
484
|
return n === void 0 ? null : {
|
|
465
|
-
...
|
|
466
|
-
payload:
|
|
485
|
+
...R(n),
|
|
486
|
+
payload: pe(n.payload),
|
|
467
487
|
responseBody: n.responseBody ?? null
|
|
468
488
|
};
|
|
469
489
|
},
|
|
@@ -477,7 +497,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
477
497
|
listTargets: async (t, n) => {
|
|
478
498
|
let r = await e.store.query({
|
|
479
499
|
table: "_voltro_webhook_targets",
|
|
480
|
-
predicate: t === void 0 ? void 0 :
|
|
500
|
+
predicate: t === void 0 ? void 0 : C("event", t),
|
|
481
501
|
order: [{
|
|
482
502
|
column: "createdAt",
|
|
483
503
|
direction: "desc"
|
|
@@ -503,15 +523,15 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
503
523
|
}
|
|
504
524
|
};
|
|
505
525
|
return i;
|
|
506
|
-
},
|
|
526
|
+
}, be = b.gen(function* () {
|
|
507
527
|
return yield* j;
|
|
508
|
-
}),
|
|
528
|
+
}), xe = (e) => {
|
|
509
529
|
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
530
|
return e.webhooks;
|
|
511
|
-
},
|
|
531
|
+
}, Se = 1e4, W = class {
|
|
512
532
|
capacity;
|
|
513
533
|
cache = /* @__PURE__ */ new Map();
|
|
514
|
-
constructor(e =
|
|
534
|
+
constructor(e = Se) {
|
|
515
535
|
this.capacity = e;
|
|
516
536
|
}
|
|
517
537
|
evictExpired() {
|
|
@@ -546,20 +566,20 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
546
566
|
clear() {
|
|
547
567
|
this.cache.clear();
|
|
548
568
|
}
|
|
549
|
-
},
|
|
569
|
+
}, G = null, K = () => (G === null && (G = new W()), G), q = {
|
|
550
570
|
"5m": 5 * 6e4,
|
|
551
571
|
"1h": 60 * 6e4,
|
|
552
572
|
"6h": 360 * 6e4,
|
|
553
573
|
"1d": 1440 * 6e4,
|
|
554
574
|
"7d": 10080 * 6e4,
|
|
555
575
|
"30d": 720 * 60 * 6e4
|
|
556
|
-
},
|
|
576
|
+
}, J = (e) => e === void 0 ? q["7d"] : q[e] ?? q["7d"], Y = (e, t) => ({
|
|
557
577
|
status: e,
|
|
558
578
|
contentType: "application/json; charset=utf-8",
|
|
559
579
|
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
|
-
|
|
580
|
+
}), Ce = (e, t) => {
|
|
581
|
+
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
|
+
T({ scope: `webhook:${e.webhookId}` }).debug("incoming webhook", {
|
|
563
583
|
status: e.status,
|
|
564
584
|
signatureOk: e.signatureOk,
|
|
565
585
|
idempotency: e.idempotency,
|
|
@@ -570,7 +590,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
570
590
|
return async (n) => {
|
|
571
591
|
let u = Date.now();
|
|
572
592
|
if (n.method !== "POST") {
|
|
573
|
-
let t =
|
|
593
|
+
let t = Y(405, {
|
|
574
594
|
error: "method not allowed",
|
|
575
595
|
expected: "POST"
|
|
576
596
|
});
|
|
@@ -591,7 +611,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
591
611
|
r._tag === "custom" && n.headers["x-slack-request-timestamp"] && (o = `${n.headers["x-slack-request-timestamp"]}|${t ?? ""}`);
|
|
592
612
|
let s = a(r, n.rawBody, i, o);
|
|
593
613
|
if (d = s.ok, !s.ok) {
|
|
594
|
-
let t =
|
|
614
|
+
let t = Y(401, {
|
|
595
615
|
error: "invalid signature",
|
|
596
616
|
reason: s.reason
|
|
597
617
|
});
|
|
@@ -617,7 +637,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
617
637
|
f = r;
|
|
618
638
|
} else f = n.rawBody;
|
|
619
639
|
} catch (t) {
|
|
620
|
-
let n =
|
|
640
|
+
let n = Y(400, {
|
|
621
641
|
error: "malformed body",
|
|
622
642
|
reason: t.message
|
|
623
643
|
});
|
|
@@ -632,9 +652,9 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
632
652
|
}
|
|
633
653
|
let p;
|
|
634
654
|
try {
|
|
635
|
-
p =
|
|
655
|
+
p = x.decodeUnknownSync(e.payload)(f);
|
|
636
656
|
} catch (t) {
|
|
637
|
-
let n =
|
|
657
|
+
let n = Y(422, {
|
|
638
658
|
error: "schema validation failed",
|
|
639
659
|
reason: t.message
|
|
640
660
|
});
|
|
@@ -653,7 +673,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
653
673
|
if (m = typeof t == "string" ? n.headers[t.toLowerCase()] : t(n.headers, n.rawBody), m !== void 0 && m.length > 0) {
|
|
654
674
|
let t = s.claim(m, c);
|
|
655
675
|
if (h = t, t === "duplicate") {
|
|
656
|
-
let t =
|
|
676
|
+
let t = Y(200, {
|
|
657
677
|
received: !0,
|
|
658
678
|
duplicate: !0
|
|
659
679
|
});
|
|
@@ -666,7 +686,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
666
686
|
}), t;
|
|
667
687
|
}
|
|
668
688
|
if (t === "inflight") {
|
|
669
|
-
let t =
|
|
689
|
+
let t = Y(409, {
|
|
670
690
|
error: "request already in flight",
|
|
671
691
|
idempotencyKey: m
|
|
672
692
|
});
|
|
@@ -689,7 +709,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
689
709
|
idempotencyKey: m ?? "",
|
|
690
710
|
...r ? { workflows: r } : {}
|
|
691
711
|
}), m !== void 0 && s.commit(m);
|
|
692
|
-
let i =
|
|
712
|
+
let i = Y(200, { received: !0 });
|
|
693
713
|
return l({
|
|
694
714
|
webhookId: e.id,
|
|
695
715
|
status: 200,
|
|
@@ -699,7 +719,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
699
719
|
}), i;
|
|
700
720
|
} catch (t) {
|
|
701
721
|
m !== void 0 && s.release(m);
|
|
702
|
-
let n =
|
|
722
|
+
let n = Y(500, {
|
|
703
723
|
error: "handler threw",
|
|
704
724
|
reason: t.message
|
|
705
725
|
});
|
|
@@ -718,20 +738,20 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
718
738
|
}), n;
|
|
719
739
|
}
|
|
720
740
|
};
|
|
721
|
-
},
|
|
741
|
+
}, we = (e) => `/webhooks/${e}`, Te = "_voltro_webhook_rate_windows", Ee = 6e4, De = (e, t) => `${e}@${t}`, Oe = async (e, t, n) => (await e.query({
|
|
722
742
|
table: "_voltro_webhook_rate_windows",
|
|
723
|
-
predicate:
|
|
743
|
+
predicate: S(C("scope", t), C("bucket", n)),
|
|
724
744
|
order: [],
|
|
725
745
|
take: 1,
|
|
726
746
|
skip: void 0,
|
|
727
747
|
projection: void 0
|
|
728
|
-
}))[0] ?? null,
|
|
729
|
-
for (let r = 0; r <
|
|
748
|
+
}))[0] ?? null, X = 32, ke = async (e, t, n) => {
|
|
749
|
+
for (let r = 0; r < X; r++) {
|
|
730
750
|
r > 0 && await new Promise((e) => setTimeout(e, r));
|
|
731
|
-
let i = await
|
|
751
|
+
let i = await Oe(e, t.key, n);
|
|
732
752
|
if (i === null) {
|
|
733
|
-
await e.insertIgnore(
|
|
734
|
-
id:
|
|
753
|
+
await e.insertIgnore(Te, {
|
|
754
|
+
id: De(t.key, n),
|
|
735
755
|
scope: t.key,
|
|
736
756
|
bucket: n,
|
|
737
757
|
count: 0,
|
|
@@ -744,31 +764,31 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
744
764
|
if (await e.updateMany("_voltro_webhook_rate_windows", {
|
|
745
765
|
count: i.count + 1,
|
|
746
766
|
updatedAt: /* @__PURE__ */ new Date()
|
|
747
|
-
}, { where:
|
|
767
|
+
}, { where: S(C("id", i.id), C("count", i.count)) }) === 1) return !0;
|
|
748
768
|
}
|
|
749
|
-
throw Error(`webhook rate window: contention on (${t.key}, ${n}) exceeded ${
|
|
750
|
-
},
|
|
751
|
-
for (let r = 0; r <
|
|
769
|
+
throw Error(`webhook rate window: contention on (${t.key}, ${n}) exceeded ${X} attempts`);
|
|
770
|
+
}, Ae = async (e, t, n) => {
|
|
771
|
+
for (let r = 0; r < X; r++) {
|
|
752
772
|
r > 0 && await new Promise((e) => setTimeout(e, r));
|
|
753
|
-
let i = await
|
|
773
|
+
let i = await Oe(e, t, n);
|
|
754
774
|
if (i === null || i.count <= 0 || await e.updateMany("_voltro_webhook_rate_windows", {
|
|
755
775
|
count: i.count - 1,
|
|
756
776
|
updatedAt: /* @__PURE__ */ new Date()
|
|
757
|
-
}, { where:
|
|
777
|
+
}, { where: S(C("id", i.id), C("count", i.count)) }) === 1) return;
|
|
758
778
|
}
|
|
759
|
-
throw Error(`webhook rate window: release contention on (${t}) exceeded ${
|
|
760
|
-
},
|
|
779
|
+
throw Error(`webhook rate window: release contention on (${t}) exceeded ${X} attempts`);
|
|
780
|
+
}, je = async (e, t, n, r = Ee) => {
|
|
761
781
|
if (t.length === 0) return {
|
|
762
782
|
acquired: !0,
|
|
763
783
|
retryInMs: 0
|
|
764
784
|
};
|
|
765
785
|
let i = Math.floor(n / r), a = [];
|
|
766
786
|
for (let o of t) {
|
|
767
|
-
if (await
|
|
787
|
+
if (await ke(e, o, i)) {
|
|
768
788
|
a.push(o);
|
|
769
789
|
continue;
|
|
770
790
|
}
|
|
771
|
-
for (let t of a) await
|
|
791
|
+
for (let t of a) await Ae(e, t.key, i);
|
|
772
792
|
return {
|
|
773
793
|
acquired: !1,
|
|
774
794
|
retryInMs: (i + 1) * r - n
|
|
@@ -778,7 +798,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
778
798
|
acquired: !0,
|
|
779
799
|
retryInMs: 0
|
|
780
800
|
};
|
|
781
|
-
},
|
|
801
|
+
}, Me = (e, t) => {
|
|
782
802
|
if (e === "json") return {
|
|
783
803
|
contentType: "application/json",
|
|
784
804
|
bytes: Buffer.from(t, "utf8")
|
|
@@ -794,15 +814,15 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
794
814
|
}
|
|
795
815
|
return e === "form" ? {
|
|
796
816
|
contentType: "application/x-www-form-urlencoded",
|
|
797
|
-
bytes: Buffer.from(
|
|
817
|
+
bytes: Buffer.from(Ne(n), "utf8")
|
|
798
818
|
} : {
|
|
799
819
|
contentType: "application/xml",
|
|
800
|
-
bytes: Buffer.from(
|
|
820
|
+
bytes: Buffer.from(Fe(n), "utf8")
|
|
801
821
|
};
|
|
802
|
-
},
|
|
822
|
+
}, Ne = (e) => {
|
|
803
823
|
if (typeof e != "object" || !e || Array.isArray(e)) throw new d({
|
|
804
824
|
format: "form",
|
|
805
|
-
reason: `form encoding requires a JSON object at the top level, got ${
|
|
825
|
+
reason: `form encoding requires a JSON object at the top level, got ${Re(e)}`
|
|
806
826
|
});
|
|
807
827
|
let t = new URLSearchParams();
|
|
808
828
|
for (let [n, r] of Object.entries(e)) Z(t, n, r);
|
|
@@ -821,13 +841,13 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
821
841
|
return;
|
|
822
842
|
}
|
|
823
843
|
e.append(t, String(n));
|
|
824
|
-
},
|
|
825
|
-
if (!
|
|
844
|
+
}, Pe = /^[A-Za-z_][A-Za-z0-9_.-]*$/, Fe = (e) => `<?xml version="1.0" encoding="UTF-8"?>${Q("webhook", e)}`, Q = (e, t) => {
|
|
845
|
+
if (!Pe.test(e)) throw new d({
|
|
826
846
|
format: "xml",
|
|
827
847
|
reason: `object key ${JSON.stringify(e)} is not a valid XML element name`
|
|
828
848
|
});
|
|
829
|
-
return `<${e}>${
|
|
830
|
-
},
|
|
849
|
+
return `<${e}>${Ie(e, t)}</${e}>`;
|
|
850
|
+
}, Ie = (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("") : Le(String(t)), Le = (e) => e.replace(/[&<>"']/g, (e) => {
|
|
831
851
|
switch (e) {
|
|
832
852
|
case "&": return "&";
|
|
833
853
|
case "<": return "<";
|
|
@@ -835,17 +855,17 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
835
855
|
case "\"": return """;
|
|
836
856
|
default: return "'";
|
|
837
857
|
}
|
|
838
|
-
}),
|
|
858
|
+
}), Re = (e) => e === null ? "null" : Array.isArray(e) ? "array" : typeof e, ze = "_voltro_webhook_targets", Be = 32, Ve = async (e, t) => (await e.query({
|
|
839
859
|
table: "_voltro_webhook_targets",
|
|
840
|
-
predicate:
|
|
860
|
+
predicate: C("id", t),
|
|
841
861
|
order: [],
|
|
842
862
|
take: 1,
|
|
843
863
|
skip: void 0,
|
|
844
864
|
projection: void 0
|
|
845
|
-
}))[0] ?? null,
|
|
846
|
-
for (let a = 0; a <
|
|
865
|
+
}))[0] ?? null, He = async (e, t, n, r, i = /* @__PURE__ */ new Date()) => {
|
|
866
|
+
for (let a = 0; a < Be; a++) {
|
|
847
867
|
a > 0 && await new Promise((e) => setTimeout(e, a));
|
|
848
|
-
let o = await
|
|
868
|
+
let o = await Ve(e, t);
|
|
849
869
|
if (o === null) return {
|
|
850
870
|
consecutiveFailures: 0,
|
|
851
871
|
autoDisabled: !1
|
|
@@ -855,7 +875,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
855
875
|
if (s === 0 || await e.updateMany("_voltro_webhook_targets", {
|
|
856
876
|
consecutiveFailures: 0,
|
|
857
877
|
updatedAt: i
|
|
858
|
-
}, { where:
|
|
878
|
+
}, { where: S(C("id", t), C("consecutiveFailures", s)) }) === 1) return {
|
|
859
879
|
consecutiveFailures: 0,
|
|
860
880
|
autoDisabled: !1
|
|
861
881
|
};
|
|
@@ -865,68 +885,68 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
865
885
|
consecutiveFailures: c,
|
|
866
886
|
updatedAt: i
|
|
867
887
|
};
|
|
868
|
-
if (u && (d.active = !1, d.autoDisabledAt = i, d.autoDisableReason = r.slice(0, 500)), await e.updateMany("_voltro_webhook_targets", d, { where:
|
|
888
|
+
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
889
|
consecutiveFailures: c,
|
|
870
890
|
autoDisabled: u
|
|
871
891
|
};
|
|
872
892
|
}
|
|
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
|
-
}),
|
|
893
|
+
throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${Be} attempts`);
|
|
894
|
+
}, Ue = x.Struct({
|
|
895
|
+
status: x.Number,
|
|
896
|
+
body: x.String,
|
|
897
|
+
latencyMs: x.Number,
|
|
898
|
+
retryAfterSec: x.Union(x.Number, x.Undefined),
|
|
899
|
+
transportError: x.Union(x.String, x.Undefined),
|
|
900
|
+
encodeError: x.Union(x.String, x.Undefined)
|
|
901
|
+
}), We = x.Struct({
|
|
902
|
+
outcome: x.Literal("succeeded", "failed", "retry"),
|
|
903
|
+
delayMs: x.Union(x.Number, x.Undefined),
|
|
904
|
+
reason: x.Union(x.String, x.Undefined),
|
|
905
|
+
autoDisabled: x.Union(x.Boolean, x.Undefined),
|
|
906
|
+
consecutiveFailures: x.Union(x.Number, x.Undefined)
|
|
907
|
+
}), Ge = x.Struct({
|
|
908
|
+
acquired: x.Boolean,
|
|
909
|
+
retryInMs: x.Number
|
|
910
|
+
}), Ke = ie.make({
|
|
891
911
|
name: "voltro.deliverWebhook",
|
|
892
912
|
payload: {
|
|
893
|
-
deliveryId:
|
|
894
|
-
targetId:
|
|
895
|
-
event:
|
|
896
|
-
eventId:
|
|
897
|
-
payloadJson:
|
|
898
|
-
attemptEpoch:
|
|
913
|
+
deliveryId: x.String,
|
|
914
|
+
targetId: x.String,
|
|
915
|
+
event: x.String,
|
|
916
|
+
eventId: x.String,
|
|
917
|
+
payloadJson: x.String,
|
|
918
|
+
attemptEpoch: x.optionalWith(x.Number, { default: () => 0 })
|
|
899
919
|
},
|
|
900
|
-
success:
|
|
901
|
-
finalStatus:
|
|
902
|
-
attempts:
|
|
920
|
+
success: x.Struct({
|
|
921
|
+
finalStatus: x.Literal("succeeded", "failed", "deferred"),
|
|
922
|
+
attempts: x.Number
|
|
903
923
|
}),
|
|
904
924
|
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*
|
|
925
|
+
}), qe = (e, t = {}) => (n, r) => {
|
|
926
|
+
let { deliveryId: i, targetId: a, event: s, eventId: c, payloadJson: l } = n, u = T({ scope: `webhook:${i}` });
|
|
927
|
+
return b.gen(function* () {
|
|
928
|
+
let n = yield* E.make({
|
|
909
929
|
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:
|
|
930
|
+
success: x.Union(x.Null, x.Struct({
|
|
931
|
+
id: x.String,
|
|
932
|
+
event: x.String,
|
|
933
|
+
url: x.String,
|
|
934
|
+
secret: x.String,
|
|
935
|
+
signing: x.Any,
|
|
936
|
+
retry: x.Any,
|
|
937
|
+
headers: x.Union(x.Record({
|
|
938
|
+
key: x.String,
|
|
939
|
+
value: x.String
|
|
940
|
+
}), x.Null),
|
|
941
|
+
rateLimitPerMinute: x.Union(x.Number, x.Null),
|
|
942
|
+
active: x.Boolean,
|
|
943
|
+
format: x.Literal("json", "form", "xml"),
|
|
944
|
+
tenantId: x.Union(x.String, x.Null)
|
|
925
945
|
})),
|
|
926
|
-
execute:
|
|
946
|
+
execute: b.tryPromise({
|
|
927
947
|
try: async () => (await e.store.query({
|
|
928
948
|
table: "_voltro_webhook_targets",
|
|
929
|
-
predicate:
|
|
949
|
+
predicate: C("id", a),
|
|
930
950
|
order: [{
|
|
931
951
|
column: "createdAt",
|
|
932
952
|
direction: "asc"
|
|
@@ -940,7 +960,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
940
960
|
event: s,
|
|
941
961
|
cause: String(e)
|
|
942
962
|
}), /* @__PURE__ */ Error(`fetch-target failed: ${e.message}`))
|
|
943
|
-
}).pipe(
|
|
963
|
+
}).pipe(b.orDie)
|
|
944
964
|
});
|
|
945
965
|
if (n === null) return {
|
|
946
966
|
finalStatus: "failed",
|
|
@@ -965,13 +985,13 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
965
985
|
...o
|
|
966
986
|
});
|
|
967
987
|
};
|
|
968
|
-
if (n.active !== !0) return yield*
|
|
988
|
+
if (n.active !== !0) return yield* E.make({
|
|
969
989
|
name: "record-paused-pending",
|
|
970
|
-
success:
|
|
971
|
-
execute:
|
|
990
|
+
success: x.Void,
|
|
991
|
+
execute: b.tryPromise({
|
|
972
992
|
try: () => r(`${i}:1`, 1, { status: "pending" }),
|
|
973
993
|
catch: (e) => /* @__PURE__ */ Error(`record-paused-pending failed: ${e.message}`)
|
|
974
|
-
}).pipe(
|
|
994
|
+
}).pipe(b.orDie)
|
|
975
995
|
}), {
|
|
976
996
|
finalStatus: "deferred",
|
|
977
997
|
attempts: 0
|
|
@@ -986,54 +1006,54 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
986
1006
|
for (let t = 1; t <= p.maxAttempts; t++) {
|
|
987
1007
|
let f = `${i}:${t}`;
|
|
988
1008
|
if (_.length > 0) for (let n = 0;; n++) {
|
|
989
|
-
let i = yield*
|
|
1009
|
+
let i = yield* E.make({
|
|
990
1010
|
name: `rate-acquire-${t}-${n}`,
|
|
991
|
-
success:
|
|
992
|
-
execute:
|
|
993
|
-
try: () =>
|
|
1011
|
+
success: Ge,
|
|
1012
|
+
execute: b.tryPromise({
|
|
1013
|
+
try: () => je(e.store, _, Date.now(), ee),
|
|
994
1014
|
catch: (e) => (u.debug("rate-acquire raw error", {
|
|
995
1015
|
targetId: a,
|
|
996
1016
|
event: s,
|
|
997
1017
|
attempt: t,
|
|
998
1018
|
cause: String(e)
|
|
999
1019
|
}), /* @__PURE__ */ Error(`rate-acquire failed: ${e.message}`))
|
|
1000
|
-
}).pipe(
|
|
1020
|
+
}).pipe(b.orDie)
|
|
1001
1021
|
});
|
|
1002
1022
|
if (i.acquired) break;
|
|
1003
|
-
yield*
|
|
1023
|
+
yield* E.make({
|
|
1004
1024
|
name: `record-rate-deferral-${t}-${n}`,
|
|
1005
|
-
success:
|
|
1006
|
-
execute:
|
|
1025
|
+
success: x.Void,
|
|
1026
|
+
execute: b.tryPromise({
|
|
1007
1027
|
try: () => r(f, t, {
|
|
1008
1028
|
status: "pending",
|
|
1009
1029
|
nextAttemptAt: new Date(Date.now() + i.retryInMs)
|
|
1010
1030
|
}),
|
|
1011
1031
|
catch: (e) => /* @__PURE__ */ Error(`record-rate-deferral failed: ${e.message}`)
|
|
1012
|
-
}).pipe(
|
|
1013
|
-
}), yield*
|
|
1032
|
+
}).pipe(b.orDie)
|
|
1033
|
+
}), yield* D.sleep({
|
|
1014
1034
|
name: `rate-window-sleep-${t}-${n}`,
|
|
1015
1035
|
duration: `${i.retryInMs} millis`
|
|
1016
1036
|
});
|
|
1017
1037
|
}
|
|
1018
|
-
yield*
|
|
1038
|
+
yield* E.make({
|
|
1019
1039
|
name: `record-attempt-start-${t}`,
|
|
1020
|
-
success:
|
|
1021
|
-
execute:
|
|
1040
|
+
success: x.Void,
|
|
1041
|
+
execute: b.tryPromise({
|
|
1022
1042
|
try: () => r(f, t, {
|
|
1023
1043
|
status: "inFlight",
|
|
1024
1044
|
nextAttemptAt: null,
|
|
1025
1045
|
scheduledAt: /* @__PURE__ */ new Date()
|
|
1026
1046
|
}),
|
|
1027
1047
|
catch: (e) => /* @__PURE__ */ Error(`record-attempt-start failed: ${e.message}`)
|
|
1028
|
-
}).pipe(
|
|
1048
|
+
}).pipe(b.orDie)
|
|
1029
1049
|
});
|
|
1030
|
-
let g = yield*
|
|
1050
|
+
let g = yield* E.make({
|
|
1031
1051
|
name: `sign-and-post-${t}`,
|
|
1032
|
-
success:
|
|
1033
|
-
execute:
|
|
1052
|
+
success: Ue,
|
|
1053
|
+
execute: b.promise(async () => {
|
|
1034
1054
|
let e = Date.now(), r;
|
|
1035
1055
|
try {
|
|
1036
|
-
r =
|
|
1056
|
+
r = Me(n.format, l);
|
|
1037
1057
|
} catch (t) {
|
|
1038
1058
|
if (t instanceof d) return {
|
|
1039
1059
|
status: 0,
|
|
@@ -1046,7 +1066,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1046
1066
|
throw t;
|
|
1047
1067
|
}
|
|
1048
1068
|
let a = Buffer.from(r.bytes), { headerName: u, headerValue: f } = o(m, a, n.secret);
|
|
1049
|
-
process.env.NODE_ENV === "production" &&
|
|
1069
|
+
process.env.NODE_ENV === "production" && w(n.url);
|
|
1050
1070
|
try {
|
|
1051
1071
|
let o = await fetch(n.url, {
|
|
1052
1072
|
method: "POST",
|
|
@@ -1081,12 +1101,12 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1081
1101
|
};
|
|
1082
1102
|
}
|
|
1083
1103
|
})
|
|
1084
|
-
}), v = yield*
|
|
1104
|
+
}), v = yield* E.make({
|
|
1085
1105
|
name: `classify-and-record-${t}`,
|
|
1086
|
-
success:
|
|
1087
|
-
execute:
|
|
1106
|
+
success: We,
|
|
1107
|
+
execute: b.tryPromise({
|
|
1088
1108
|
try: async () => {
|
|
1089
|
-
let r = g.status >= 200 && g.status < 300, i = g.transportError !== void 0, o = g.encodeError !== void 0, s = (t, n) =>
|
|
1109
|
+
let r = g.status >= 200 && g.status < 300, i = g.transportError !== void 0, o = g.encodeError !== void 0, s = (t, n) => He(e.store, a, t, n);
|
|
1090
1110
|
if (r) {
|
|
1091
1111
|
await e.store.update("_voltro_webhook_deliveries", f, {
|
|
1092
1112
|
status: "succeeded",
|
|
@@ -1104,7 +1124,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1104
1124
|
consecutiveFailures: t.consecutiveFailures
|
|
1105
1125
|
};
|
|
1106
1126
|
}
|
|
1107
|
-
let c = o ? null :
|
|
1127
|
+
let c = o ? null : ce(p, t, {
|
|
1108
1128
|
...i ? {} : { status: g.status },
|
|
1109
1129
|
...g.retryAfterSec === void 0 ? {} : { retryAfterSeconds: g.retryAfterSec }
|
|
1110
1130
|
});
|
|
@@ -1149,7 +1169,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1149
1169
|
attempt: t,
|
|
1150
1170
|
cause: String(e)
|
|
1151
1171
|
}), /* @__PURE__ */ Error(`classify-and-record failed: ${e.message}`))
|
|
1152
|
-
}).pipe(
|
|
1172
|
+
}).pipe(b.orDie)
|
|
1153
1173
|
});
|
|
1154
1174
|
if (v.autoDisabled === !0 && u.warn("target auto-disabled after consecutive failures", {
|
|
1155
1175
|
targetId: a,
|
|
@@ -1164,7 +1184,7 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1164
1184
|
finalStatus: "failed",
|
|
1165
1185
|
attempts: t
|
|
1166
1186
|
};
|
|
1167
|
-
v.delayMs !== void 0 && (yield*
|
|
1187
|
+
v.delayMs !== void 0 && (yield* D.sleep({
|
|
1168
1188
|
name: `retry-sleep-${t}`,
|
|
1169
1189
|
duration: `${v.delayMs} millis`
|
|
1170
1190
|
}));
|
|
@@ -1174,6 +1194,15 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
|
|
|
1174
1194
|
attempts: p.maxAttempts
|
|
1175
1195
|
};
|
|
1176
1196
|
});
|
|
1177
|
-
}
|
|
1197
|
+
}, $ = "voltro.webhook.emit", Je = (e) => typeof e.event == "string", Ye = (e) => ae({
|
|
1198
|
+
effect: $,
|
|
1199
|
+
maxAttempts: 1,
|
|
1200
|
+
handler: async ({ payload: t }) => {
|
|
1201
|
+
if (!Je(t)) throw Error(`${$}: row payload has no \`event\` name — cannot resolve a descriptor. Got keys: ${Object.keys(t).join(", ") || "(none)"}`);
|
|
1202
|
+
let n = e();
|
|
1203
|
+
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
|
+
await n.emit(t.event, t.payload, { tenantId: t.tenantId });
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1178
1207
|
//#endregion
|
|
1179
|
-
export {
|
|
1208
|
+
export { W as IdempotencyCache, Ee as RATE_WINDOW_MS, Te as RATE_WINDOW_TABLE, ze as TARGETS_TABLE, $ as WEBHOOK_EMIT_EFFECT, l as WebhookDeliveryNotFound, u as WebhookPayloadInvalid, d as WebhookPayloadUnrepresentable, f as WebhookPayloadVersionInvalid, p as WebhookSubscribeInvalid, j as WebhooksService, m as _voltroWebhookDeliveriesTable, h as _voltroWebhookEventStatsTable, ee as _voltroWebhookRateWindowsTable, g as _voltroWebhookTargetsTable, je as acquireRateSlots, O as asOutgoingEvent, qe as buildDeliverWebhookExecute, ye as buildWebhooksService, me as compareVersions, ke as consumeRateSlot, we as defaultIncomingPath, c as defaultOutgoingSignature, le as defaultRetryPolicy, s as defineIncomingWebhook, t as defineWebhookProvider, Ke as deliverWebhookWorkflow, Me as encodePayload, P as eventsOf, ue as fastRetryPolicy, N as generateSecret, K as getIdempotencyCache, i as githubSignature, oe as hasWebhookAudience, r as isWebhookDescriptor, Ye as makeDeferredEmitHandler, z as matchesFilter, Ce as mountIncomingWebhook, ce as nextRetry, k as parseDuration, J as parseTtl, He as recordDeliveryOutcome, Ae as releaseRateSlot, I as resolveSubscribe, o as signPayload, n as slackSignature, e as stripeSignature, xe as useWebhooks, be as useWebhooksEffect, F as validateSubscribe, a as verifySignature, _ as webhookTables };
|