@voltro/plugin-webhooks 0.24.0 → 0.26.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 +670 -0
- package/dist/errors.d.ts +1 -1
- package/dist/index.d.ts +196 -9
- package/dist/index.js +368 -249
- package/dist/mixin.d.ts +48 -0
- package/dist/mixin.js +1 -0
- package/dist/providers/index.js +1 -1
- package/dist/{signing-BOUxHdAo.js → signing-DUG4JWwk.js} +29 -32
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
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
|
|
2
|
-
import { WebhookDeliveryNotFound as
|
|
3
|
-
import { _voltroWebhookDeliveriesTable 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
|
-
//#region src/
|
|
12
|
-
var ae =
|
|
13
|
-
let t =
|
|
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
|
+
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 ee, webhookTables as g } from "./mixin.js";
|
|
4
|
+
import { randomBytes as _, randomUUID as v } from "node:crypto";
|
|
5
|
+
import { Context as te, Effect as y, Either as ne, Schema as b } from "effect";
|
|
6
|
+
import { and as x, eq as S } from "@voltro/database";
|
|
7
|
+
import { assertPublicUrl as C } from "@voltro/integration-http";
|
|
8
|
+
import { publishServerError as re } from "@voltro/protocol";
|
|
9
|
+
import { createLogger as w } from "@voltro/logger";
|
|
10
|
+
import { Activity as T, DurableClock as E, Workflow as ie } from "@effect/workflow";
|
|
11
|
+
//#region src/eventAudience.ts
|
|
12
|
+
var ae = (e) => e.webhook !== void 0, D = (e) => {
|
|
13
|
+
let t = e.webhook;
|
|
14
|
+
if (t !== void 0) return {
|
|
15
|
+
_tag: "outgoingEvent",
|
|
16
|
+
id: e.name,
|
|
17
|
+
payload: e.payload,
|
|
18
|
+
...t.description === void 0 ? {} : { description: t.description },
|
|
19
|
+
...t.version === void 0 ? {} : { version: t.version },
|
|
20
|
+
...t.rateLimit === void 0 ? {} : { globalRateLimit: t.rateLimit }
|
|
21
|
+
};
|
|
22
|
+
}, oe = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
23
|
+
let t = oe.exec(e);
|
|
14
24
|
if (!t) throw Error(`invalid duration literal: ${e}`);
|
|
15
25
|
let n = Number(t[1]), r = t[2] ?? "s";
|
|
16
26
|
switch (r) {
|
|
@@ -64,7 +74,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
64
74
|
retryOn: k,
|
|
65
75
|
honourRetryAfter: !0,
|
|
66
76
|
jitter: "full"
|
|
67
|
-
}),
|
|
77
|
+
}), se = () => ({
|
|
68
78
|
strategy: "fixed",
|
|
69
79
|
maxAttempts: 120,
|
|
70
80
|
initialDelay: "30s",
|
|
@@ -72,62 +82,63 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
72
82
|
retryOn: k,
|
|
73
83
|
honourRetryAfter: !0,
|
|
74
84
|
jitter: "full"
|
|
75
|
-
}), M = class extends
|
|
85
|
+
}), M = class extends te.Tag("@voltro/webhooks/WebhooksService")() {}, ce = 32, N = 32, P = () => _(ce).toString("hex"), F = (e) => {
|
|
76
86
|
let t;
|
|
77
87
|
try {
|
|
78
88
|
t = new URL(e.url);
|
|
79
89
|
} catch (e) {
|
|
80
|
-
throw new
|
|
90
|
+
throw new p({
|
|
81
91
|
field: "url",
|
|
82
92
|
reason: `invalid subscriber url: ${e.message}`
|
|
83
93
|
});
|
|
84
94
|
}
|
|
85
|
-
if (t.protocol !== "http:" && t.protocol !== "https:") throw new
|
|
95
|
+
if (t.protocol !== "http:" && t.protocol !== "https:") throw new p({
|
|
86
96
|
field: "url",
|
|
87
97
|
reason: `url protocol must be http or https, got ${t.protocol}`
|
|
88
98
|
});
|
|
89
99
|
if (process.env.NODE_ENV === "production") try {
|
|
90
|
-
|
|
100
|
+
C(e.url);
|
|
91
101
|
} catch (e) {
|
|
92
|
-
throw new
|
|
102
|
+
throw new p({
|
|
93
103
|
field: "url",
|
|
94
104
|
reason: e.message
|
|
95
105
|
});
|
|
96
106
|
}
|
|
97
|
-
if (e.event.length === 0) throw new
|
|
107
|
+
if (e.event.length === 0) throw new p({
|
|
98
108
|
field: "event",
|
|
99
109
|
reason: "event id required"
|
|
100
110
|
});
|
|
101
|
-
if (e.secret !== void 0 && e.secret.length < N) throw new
|
|
111
|
+
if (e.secret !== void 0 && e.secret.length < N) throw new p({
|
|
102
112
|
field: "secret",
|
|
103
113
|
reason: `secret must be at least ${N} chars`
|
|
104
114
|
});
|
|
105
115
|
if (e.retry !== void 0) {
|
|
106
|
-
if (e.retry.maxAttempts < 1) throw new
|
|
116
|
+
if (e.retry.maxAttempts < 1) throw new p({
|
|
107
117
|
field: "retry",
|
|
108
118
|
reason: "retry.maxAttempts must be ≥ 1"
|
|
109
119
|
});
|
|
110
|
-
if (e.retry.maxAttempts > 1e3) throw new
|
|
120
|
+
if (e.retry.maxAttempts > 1e3) throw new p({
|
|
111
121
|
field: "retry",
|
|
112
122
|
reason: "retry.maxAttempts must be ≤ 1000"
|
|
113
123
|
});
|
|
114
124
|
}
|
|
115
|
-
if (e.rateLimitPerMinute !== void 0 && e.rateLimitPerMinute < 1) throw new
|
|
125
|
+
if (e.rateLimitPerMinute !== void 0 && e.rateLimitPerMinute < 1) throw new p({
|
|
116
126
|
field: "rateLimitPerMinute",
|
|
117
127
|
reason: "rateLimitPerMinute must be ≥ 1"
|
|
118
128
|
});
|
|
119
|
-
if (e.autoDisableAfter !== void 0 && (e.autoDisableAfter < 1 || !Number.isInteger(e.autoDisableAfter))) throw new
|
|
129
|
+
if (e.autoDisableAfter !== void 0 && (e.autoDisableAfter < 1 || !Number.isInteger(e.autoDisableAfter))) throw new p({
|
|
120
130
|
field: "autoDisableAfter",
|
|
121
131
|
reason: `autoDisableAfter must be a positive integer, got ${e.autoDisableAfter}`
|
|
122
132
|
});
|
|
123
133
|
}, I = (e, t) => ({
|
|
124
|
-
id:
|
|
134
|
+
id: v(),
|
|
125
135
|
event: e.event,
|
|
126
136
|
url: e.url,
|
|
127
137
|
secret: e.secret ?? P(),
|
|
128
138
|
signing: e.signing ?? t?.defaultSigning ?? c(),
|
|
129
139
|
retry: e.retry ?? t?.defaultRetry ?? j(),
|
|
130
140
|
filter: e.filter ?? null,
|
|
141
|
+
scope: e.scope ?? null,
|
|
131
142
|
headers: e.headers ?? null,
|
|
132
143
|
rateLimitPerMinute: e.rateLimitPerMinute ?? null,
|
|
133
144
|
active: !0,
|
|
@@ -138,19 +149,40 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
138
149
|
autoDisableReason: null,
|
|
139
150
|
payloadVersion: e.payloadVersion ?? t?.version ?? 1,
|
|
140
151
|
description: e.description ?? null
|
|
141
|
-
}),
|
|
152
|
+
}), le = (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, ue = (e) => {
|
|
153
|
+
if (typeof e != "string") return e;
|
|
154
|
+
try {
|
|
155
|
+
return JSON.parse(e);
|
|
156
|
+
} catch {
|
|
157
|
+
return e;
|
|
158
|
+
}
|
|
159
|
+
}, R = (e) => ({
|
|
160
|
+
id: String(e.id),
|
|
161
|
+
deliveryId: String(e.deliveryId),
|
|
162
|
+
targetId: String(e.targetId),
|
|
163
|
+
event: String(e.event),
|
|
164
|
+
eventId: e.eventId ?? null,
|
|
165
|
+
attempt: Number(e.attempt ?? 0),
|
|
166
|
+
status: e.status,
|
|
167
|
+
responseStatus: e.responseStatus ?? null,
|
|
168
|
+
errorMessage: e.errorMessage ?? null,
|
|
169
|
+
latencyMs: e.latencyMs ?? null,
|
|
170
|
+
scheduledAt: L(e.scheduledAt),
|
|
171
|
+
nextAttemptAt: L(e.nextAttemptAt),
|
|
172
|
+
createdAt: L(e.createdAt)
|
|
173
|
+
}), de = (e, t) => e === t ? "current" : e < t ? "behind" : "ahead", z = (e, t) => {
|
|
142
174
|
if (e === null) return !0;
|
|
143
175
|
let n = { payload: t };
|
|
144
|
-
for (let [t, r] of Object.entries(e)) if (!
|
|
176
|
+
for (let [t, r] of Object.entries(e)) if (!pe(fe(n, t), r)) return !1;
|
|
145
177
|
return !0;
|
|
146
|
-
},
|
|
178
|
+
}, fe = (e, t) => {
|
|
147
179
|
let n = t.split("."), r = e;
|
|
148
180
|
for (let e of n) {
|
|
149
181
|
if (typeof r != "object" || !r) return;
|
|
150
182
|
r = r[e];
|
|
151
183
|
}
|
|
152
184
|
return r;
|
|
153
|
-
},
|
|
185
|
+
}, pe = (e, t) => {
|
|
154
186
|
if (typeof t == "object" && t && !Array.isArray(t)) {
|
|
155
187
|
let n = t;
|
|
156
188
|
if ("eq" in n) return e === n.eq;
|
|
@@ -161,7 +193,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
161
193
|
if ("in" in n && Array.isArray(n.in)) return n.in.includes(e);
|
|
162
194
|
}
|
|
163
195
|
return e === t;
|
|
164
|
-
},
|
|
196
|
+
}, me = (e, t, n = {}) => {
|
|
165
197
|
let r = new Map((n.events ?? []).map((e) => [e.id, e]));
|
|
166
198
|
return {
|
|
167
199
|
subscribe: async (t) => {
|
|
@@ -181,17 +213,17 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
181
213
|
};
|
|
182
214
|
},
|
|
183
215
|
emit: async (n, i) => {
|
|
184
|
-
let a = typeof n == "string" ? r.get(n) : n,
|
|
185
|
-
if (
|
|
186
|
-
let e =
|
|
187
|
-
if (
|
|
188
|
-
event:
|
|
216
|
+
let a = typeof n == "object" && n.kind === "event" ? D(n) : void 0, o = typeof n == "string" ? r.get(n) : a ?? n, s = typeof n == "string" ? n : n.id ?? n.name;
|
|
217
|
+
if (o !== void 0) {
|
|
218
|
+
let e = b.decodeUnknownEither(o.payload)(i, { errors: "all" });
|
|
219
|
+
if (ne.isLeft(e)) throw new u({
|
|
220
|
+
event: s,
|
|
189
221
|
issues: e.left.message
|
|
190
222
|
});
|
|
191
223
|
}
|
|
192
|
-
let
|
|
224
|
+
let c = v(), l = (await e.store.query({
|
|
193
225
|
table: "_voltro_webhook_targets",
|
|
194
|
-
predicate:
|
|
226
|
+
predicate: S("event", s),
|
|
195
227
|
order: [{
|
|
196
228
|
column: "createdAt",
|
|
197
229
|
direction: "asc"
|
|
@@ -199,16 +231,16 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
199
231
|
take: 1e3,
|
|
200
232
|
skip: void 0,
|
|
201
233
|
projection: void 0
|
|
202
|
-
})).filter((e) =>
|
|
203
|
-
for (let n of
|
|
204
|
-
let r =
|
|
234
|
+
})).filter((e) => z(e.filter, i)), d = [], f = JSON.stringify(i);
|
|
235
|
+
for (let n of l) {
|
|
236
|
+
let r = v();
|
|
205
237
|
n.active ? (await t({
|
|
206
238
|
deliveryId: r,
|
|
207
239
|
targetId: n.id,
|
|
208
|
-
event:
|
|
209
|
-
eventId:
|
|
210
|
-
payloadJson:
|
|
211
|
-
}),
|
|
240
|
+
event: s,
|
|
241
|
+
eventId: c,
|
|
242
|
+
payloadJson: f
|
|
243
|
+
}), d.push({
|
|
212
244
|
targetId: n.id,
|
|
213
245
|
deliveryId: r,
|
|
214
246
|
status: "dispatched"
|
|
@@ -217,29 +249,29 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
217
249
|
deliveryId: r,
|
|
218
250
|
targetId: n.id,
|
|
219
251
|
tenantId: n.tenantId ?? null,
|
|
220
|
-
event:
|
|
221
|
-
eventId:
|
|
252
|
+
event: s,
|
|
253
|
+
eventId: c,
|
|
222
254
|
attempt: 1,
|
|
223
255
|
status: "pending",
|
|
224
|
-
payload:
|
|
256
|
+
payload: f,
|
|
225
257
|
scheduledAt: /* @__PURE__ */ new Date(),
|
|
226
258
|
createdAt: /* @__PURE__ */ new Date(),
|
|
227
259
|
updatedAt: /* @__PURE__ */ new Date()
|
|
228
|
-
}),
|
|
260
|
+
}), d.push({
|
|
229
261
|
targetId: n.id,
|
|
230
262
|
deliveryId: r,
|
|
231
263
|
status: "queued"
|
|
232
264
|
}));
|
|
233
265
|
}
|
|
234
266
|
return {
|
|
235
|
-
eventId:
|
|
236
|
-
deliveries:
|
|
267
|
+
eventId: c,
|
|
268
|
+
deliveries: d
|
|
237
269
|
};
|
|
238
270
|
},
|
|
239
271
|
replay: async (n) => {
|
|
240
272
|
let r = (await e.store.query({
|
|
241
273
|
table: "_voltro_webhook_deliveries",
|
|
242
|
-
predicate:
|
|
274
|
+
predicate: S("deliveryId", n),
|
|
243
275
|
order: [{
|
|
244
276
|
column: "attempt",
|
|
245
277
|
direction: "asc"
|
|
@@ -248,12 +280,12 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
248
280
|
skip: void 0,
|
|
249
281
|
projection: void 0
|
|
250
282
|
}))[0];
|
|
251
|
-
if (r === void 0) throw new
|
|
283
|
+
if (r === void 0) throw new l({ deliveryId: n });
|
|
252
284
|
await t({
|
|
253
285
|
deliveryId: r.deliveryId,
|
|
254
286
|
targetId: r.targetId,
|
|
255
287
|
event: r.event,
|
|
256
|
-
eventId: r.eventId ??
|
|
288
|
+
eventId: r.eventId ?? v(),
|
|
257
289
|
payloadJson: typeof r.payload == "string" ? r.payload : JSON.stringify(r.payload),
|
|
258
290
|
attemptEpoch: Date.now()
|
|
259
291
|
});
|
|
@@ -274,7 +306,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
274
306
|
});
|
|
275
307
|
let r = await e.store.query({
|
|
276
308
|
table: "_voltro_webhook_deliveries",
|
|
277
|
-
predicate:
|
|
309
|
+
predicate: x(S("targetId", n), S("status", "pending")),
|
|
278
310
|
order: [{
|
|
279
311
|
column: "createdAt",
|
|
280
312
|
direction: "asc"
|
|
@@ -287,13 +319,13 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
287
319
|
deliveryId: e.deliveryId,
|
|
288
320
|
targetId: n,
|
|
289
321
|
event: e.event,
|
|
290
|
-
eventId: e.eventId ??
|
|
322
|
+
eventId: e.eventId ?? v(),
|
|
291
323
|
payloadJson: typeof e.payload == "string" ? e.payload : JSON.stringify(e.payload),
|
|
292
324
|
attemptEpoch: i
|
|
293
325
|
});
|
|
294
326
|
},
|
|
295
327
|
deleteTarget: async (t) => {
|
|
296
|
-
await e.store.delete("_voltro_webhook_targets", t), await e.store.deleteMany("_voltro_webhook_deliveries", { where:
|
|
328
|
+
await e.store.delete("_voltro_webhook_targets", t), await e.store.deleteMany("_voltro_webhook_deliveries", { where: x(S("targetId", t), S("status", "pending")) });
|
|
297
329
|
},
|
|
298
330
|
rotateSecret: async (t) => {
|
|
299
331
|
let n = P();
|
|
@@ -302,46 +334,133 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
302
334
|
updatedAt: /* @__PURE__ */ new Date()
|
|
303
335
|
}), { secret: n };
|
|
304
336
|
},
|
|
337
|
+
updateTarget: async (t, n) => {
|
|
338
|
+
if (n.url !== void 0 && F({
|
|
339
|
+
event: "x",
|
|
340
|
+
url: n.url
|
|
341
|
+
}), 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");
|
|
342
|
+
let r = { updatedAt: /* @__PURE__ */ new Date() };
|
|
343
|
+
for (let e of [
|
|
344
|
+
"url",
|
|
345
|
+
"description",
|
|
346
|
+
"filter",
|
|
347
|
+
"scope",
|
|
348
|
+
"headers",
|
|
349
|
+
"rateLimitPerMinute",
|
|
350
|
+
"autoDisableAfter",
|
|
351
|
+
"format"
|
|
352
|
+
]) n[e] !== void 0 && (r[e] = n[e]);
|
|
353
|
+
await e.store.update("_voltro_webhook_targets", t, r);
|
|
354
|
+
},
|
|
355
|
+
testTarget: async (n, r) => {
|
|
356
|
+
let i = (await e.store.query({
|
|
357
|
+
table: "_voltro_webhook_targets",
|
|
358
|
+
predicate: S("id", n),
|
|
359
|
+
order: void 0,
|
|
360
|
+
take: 1,
|
|
361
|
+
skip: void 0,
|
|
362
|
+
projection: void 0
|
|
363
|
+
}))[0];
|
|
364
|
+
if (i === void 0) throw Error(`testTarget: no target ${n}`);
|
|
365
|
+
let a = v(), o = JSON.stringify(r ?? {
|
|
366
|
+
test: !0,
|
|
367
|
+
targetId: n,
|
|
368
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
369
|
+
});
|
|
370
|
+
return i.active ? await t({
|
|
371
|
+
deliveryId: a,
|
|
372
|
+
targetId: i.id,
|
|
373
|
+
event: i.event,
|
|
374
|
+
eventId: v(),
|
|
375
|
+
payloadJson: o
|
|
376
|
+
}) : await e.store.insert("_voltro_webhook_deliveries", {
|
|
377
|
+
id: `${a}:1`,
|
|
378
|
+
deliveryId: a,
|
|
379
|
+
targetId: i.id,
|
|
380
|
+
tenantId: i.tenantId ?? null,
|
|
381
|
+
event: i.event,
|
|
382
|
+
eventId: null,
|
|
383
|
+
attempt: 1,
|
|
384
|
+
status: "pending",
|
|
385
|
+
payload: o,
|
|
386
|
+
scheduledAt: /* @__PURE__ */ new Date(),
|
|
387
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
388
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
389
|
+
}), { deliveryId: a };
|
|
390
|
+
},
|
|
391
|
+
listDeliveries: async (t) => {
|
|
392
|
+
let n = await e.store.query({
|
|
393
|
+
table: "_voltro_webhook_deliveries",
|
|
394
|
+
predicate: t?.targetId === void 0 ? void 0 : S("targetId", t.targetId),
|
|
395
|
+
order: [{
|
|
396
|
+
column: "createdAt",
|
|
397
|
+
direction: "desc"
|
|
398
|
+
}],
|
|
399
|
+
take: Math.min((t?.limit ?? 200) * (t?.status !== void 0 || t?.since !== void 0 ? 10 : 1), 5e3),
|
|
400
|
+
skip: void 0,
|
|
401
|
+
projection: void 0
|
|
402
|
+
}), r = t?.since?.getTime();
|
|
403
|
+
return n.filter((e) => t?.status === void 0 || e.status === t.status).filter((e) => r === void 0 || le(e.createdAt) >= r).slice(0, t?.limit ?? 200).map(R);
|
|
404
|
+
},
|
|
405
|
+
getDelivery: async (t) => {
|
|
406
|
+
let n = (await e.store.query({
|
|
407
|
+
table: "_voltro_webhook_deliveries",
|
|
408
|
+
predicate: S("id", t),
|
|
409
|
+
order: void 0,
|
|
410
|
+
take: 1,
|
|
411
|
+
skip: void 0,
|
|
412
|
+
projection: void 0
|
|
413
|
+
}))[0];
|
|
414
|
+
return n === void 0 ? null : {
|
|
415
|
+
...R(n),
|
|
416
|
+
payload: ue(n.payload),
|
|
417
|
+
responseBody: n.responseBody ?? null
|
|
418
|
+
};
|
|
419
|
+
},
|
|
305
420
|
updateTargetPayloadVersion: async (t, n) => {
|
|
306
|
-
if (n < 1 || !Number.isInteger(n)) throw new
|
|
421
|
+
if (n < 1 || !Number.isInteger(n)) throw new f({ version: n });
|
|
307
422
|
await e.store.update("_voltro_webhook_targets", t, {
|
|
308
423
|
payloadVersion: n,
|
|
309
424
|
updatedAt: /* @__PURE__ */ new Date()
|
|
310
425
|
});
|
|
311
426
|
},
|
|
312
|
-
listTargets: async (t) =>
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
427
|
+
listTargets: async (t, n) => {
|
|
428
|
+
let r = await e.store.query({
|
|
429
|
+
table: "_voltro_webhook_targets",
|
|
430
|
+
predicate: t === void 0 ? void 0 : S("event", t),
|
|
431
|
+
order: [{
|
|
432
|
+
column: "createdAt",
|
|
433
|
+
direction: "desc"
|
|
434
|
+
}],
|
|
435
|
+
take: 500,
|
|
436
|
+
skip: void 0,
|
|
437
|
+
projection: void 0
|
|
438
|
+
});
|
|
439
|
+
return (n === void 0 ? r : r.filter((e) => e.scope !== null && Object.entries(n).every(([t, n]) => e.scope?.[t] === n))).map((e) => ({
|
|
440
|
+
id: e.id,
|
|
441
|
+
event: e.event,
|
|
442
|
+
url: e.url,
|
|
443
|
+
active: e.active,
|
|
444
|
+
rateLimitPerMinute: e.rateLimitPerMinute,
|
|
445
|
+
description: e.description,
|
|
446
|
+
payloadVersion: e.payloadVersion,
|
|
447
|
+
autoDisableAfter: e.autoDisableAfter,
|
|
448
|
+
consecutiveFailures: e.consecutiveFailures ?? 0,
|
|
449
|
+
autoDisabledAt: e.autoDisabledAt === null || e.autoDisabledAt === void 0 ? null : e.autoDisabledAt instanceof Date ? e.autoDisabledAt.toISOString() : String(e.autoDisabledAt),
|
|
450
|
+
autoDisableReason: e.autoDisableReason,
|
|
451
|
+
scope: e.scope
|
|
452
|
+
}));
|
|
453
|
+
}
|
|
335
454
|
};
|
|
336
|
-
},
|
|
455
|
+
}, he = y.gen(function* () {
|
|
337
456
|
return yield* M;
|
|
338
|
-
}),
|
|
457
|
+
}), ge = (e) => {
|
|
339
458
|
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.");
|
|
340
459
|
return e.webhooks;
|
|
341
|
-
},
|
|
460
|
+
}, _e = 1e4, B = class {
|
|
342
461
|
capacity;
|
|
343
462
|
cache = /* @__PURE__ */ new Map();
|
|
344
|
-
constructor(e =
|
|
463
|
+
constructor(e = _e) {
|
|
345
464
|
this.capacity = e;
|
|
346
465
|
}
|
|
347
466
|
evictExpired() {
|
|
@@ -376,20 +495,20 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
376
495
|
clear() {
|
|
377
496
|
this.cache.clear();
|
|
378
497
|
}
|
|
379
|
-
},
|
|
498
|
+
}, V = null, H = () => (V === null && (V = new B()), V), U = {
|
|
380
499
|
"5m": 5 * 6e4,
|
|
381
500
|
"1h": 60 * 6e4,
|
|
382
501
|
"6h": 360 * 6e4,
|
|
383
502
|
"1d": 1440 * 6e4,
|
|
384
503
|
"7d": 10080 * 6e4,
|
|
385
504
|
"30d": 720 * 60 * 6e4
|
|
386
|
-
},
|
|
505
|
+
}, W = (e) => e === void 0 ? U["7d"] : U[e] ?? U["7d"], G = (e, t) => ({
|
|
387
506
|
status: e,
|
|
388
507
|
contentType: "application/json; charset=utf-8",
|
|
389
508
|
body: JSON.stringify(t)
|
|
390
|
-
}),
|
|
391
|
-
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 ??
|
|
392
|
-
|
|
509
|
+
}), ve = (e, t) => {
|
|
510
|
+
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 ?? H(), c = W(i?.ttl), l = t.log ?? ((e) => {
|
|
511
|
+
w({ scope: `webhook:${e.webhookId}` }).debug("incoming webhook", {
|
|
393
512
|
status: e.status,
|
|
394
513
|
signatureOk: e.signatureOk,
|
|
395
514
|
idempotency: e.idempotency,
|
|
@@ -400,7 +519,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
400
519
|
return async (n) => {
|
|
401
520
|
let u = Date.now();
|
|
402
521
|
if (n.method !== "POST") {
|
|
403
|
-
let t =
|
|
522
|
+
let t = G(405, {
|
|
404
523
|
error: "method not allowed",
|
|
405
524
|
expected: "POST"
|
|
406
525
|
});
|
|
@@ -421,7 +540,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
421
540
|
r._tag === "custom" && n.headers["x-slack-request-timestamp"] && (o = `${n.headers["x-slack-request-timestamp"]}|${t ?? ""}`);
|
|
422
541
|
let s = a(r, n.rawBody, i, o);
|
|
423
542
|
if (d = s.ok, !s.ok) {
|
|
424
|
-
let t =
|
|
543
|
+
let t = G(401, {
|
|
425
544
|
error: "invalid signature",
|
|
426
545
|
reason: s.reason
|
|
427
546
|
});
|
|
@@ -447,7 +566,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
447
566
|
f = r;
|
|
448
567
|
} else f = n.rawBody;
|
|
449
568
|
} catch (t) {
|
|
450
|
-
let n =
|
|
569
|
+
let n = G(400, {
|
|
451
570
|
error: "malformed body",
|
|
452
571
|
reason: t.message
|
|
453
572
|
});
|
|
@@ -462,9 +581,9 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
462
581
|
}
|
|
463
582
|
let p;
|
|
464
583
|
try {
|
|
465
|
-
p =
|
|
584
|
+
p = b.decodeUnknownSync(e.payload)(f);
|
|
466
585
|
} catch (t) {
|
|
467
|
-
let n =
|
|
586
|
+
let n = G(422, {
|
|
468
587
|
error: "schema validation failed",
|
|
469
588
|
reason: t.message
|
|
470
589
|
});
|
|
@@ -483,7 +602,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
483
602
|
if (m = typeof t == "string" ? n.headers[t.toLowerCase()] : t(n.headers, n.rawBody), m !== void 0 && m.length > 0) {
|
|
484
603
|
let t = s.claim(m, c);
|
|
485
604
|
if (h = t, t === "duplicate") {
|
|
486
|
-
let t =
|
|
605
|
+
let t = G(200, {
|
|
487
606
|
received: !0,
|
|
488
607
|
duplicate: !0
|
|
489
608
|
});
|
|
@@ -496,7 +615,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
496
615
|
}), t;
|
|
497
616
|
}
|
|
498
617
|
if (t === "inflight") {
|
|
499
|
-
let t =
|
|
618
|
+
let t = G(409, {
|
|
500
619
|
error: "request already in flight",
|
|
501
620
|
idempotencyKey: m
|
|
502
621
|
});
|
|
@@ -519,7 +638,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
519
638
|
idempotencyKey: m ?? "",
|
|
520
639
|
...r ? { workflows: r } : {}
|
|
521
640
|
}), m !== void 0 && s.commit(m);
|
|
522
|
-
let i =
|
|
641
|
+
let i = G(200, { received: !0 });
|
|
523
642
|
return l({
|
|
524
643
|
webhookId: e.id,
|
|
525
644
|
status: 200,
|
|
@@ -529,7 +648,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
529
648
|
}), i;
|
|
530
649
|
} catch (t) {
|
|
531
650
|
m !== void 0 && s.release(m);
|
|
532
|
-
let n =
|
|
651
|
+
let n = G(500, {
|
|
533
652
|
error: "handler threw",
|
|
534
653
|
reason: t.message
|
|
535
654
|
});
|
|
@@ -540,7 +659,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
540
659
|
idempotency: h,
|
|
541
660
|
durationMs: Date.now() - u,
|
|
542
661
|
errorMessage: t.message
|
|
543
|
-
}),
|
|
662
|
+
}), re({
|
|
544
663
|
error: t,
|
|
545
664
|
source: "webhook",
|
|
546
665
|
name: e.id,
|
|
@@ -548,20 +667,20 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
548
667
|
}), n;
|
|
549
668
|
}
|
|
550
669
|
};
|
|
551
|
-
},
|
|
670
|
+
}, ye = (e) => `/webhooks/${e}`, K = "_voltro_webhook_rate_windows", q = 6e4, be = (e, t) => `${e}@${t}`, J = async (e, t, n) => (await e.query({
|
|
552
671
|
table: "_voltro_webhook_rate_windows",
|
|
553
|
-
predicate:
|
|
672
|
+
predicate: x(S("scope", t), S("bucket", n)),
|
|
554
673
|
order: [],
|
|
555
674
|
take: 1,
|
|
556
675
|
skip: void 0,
|
|
557
676
|
projection: void 0
|
|
558
|
-
}))[0] ?? null,
|
|
559
|
-
for (let r = 0; r <
|
|
677
|
+
}))[0] ?? null, Y = 32, X = async (e, t, n) => {
|
|
678
|
+
for (let r = 0; r < Y; r++) {
|
|
560
679
|
r > 0 && await new Promise((e) => setTimeout(e, r));
|
|
561
|
-
let i = await
|
|
680
|
+
let i = await J(e, t.key, n);
|
|
562
681
|
if (i === null) {
|
|
563
|
-
await e.insertIgnore(
|
|
564
|
-
id:
|
|
682
|
+
await e.insertIgnore(K, {
|
|
683
|
+
id: be(t.key, n),
|
|
565
684
|
scope: t.key,
|
|
566
685
|
bucket: n,
|
|
567
686
|
count: 0,
|
|
@@ -574,31 +693,31 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
574
693
|
if (await e.updateMany("_voltro_webhook_rate_windows", {
|
|
575
694
|
count: i.count + 1,
|
|
576
695
|
updatedAt: /* @__PURE__ */ new Date()
|
|
577
|
-
}, { where:
|
|
696
|
+
}, { where: x(S("id", i.id), S("count", i.count)) }) === 1) return !0;
|
|
578
697
|
}
|
|
579
|
-
throw Error(`webhook rate window: contention on (${t.key}, ${n}) exceeded ${
|
|
580
|
-
},
|
|
581
|
-
for (let r = 0; r <
|
|
698
|
+
throw Error(`webhook rate window: contention on (${t.key}, ${n}) exceeded ${Y} attempts`);
|
|
699
|
+
}, Z = async (e, t, n) => {
|
|
700
|
+
for (let r = 0; r < Y; r++) {
|
|
582
701
|
r > 0 && await new Promise((e) => setTimeout(e, r));
|
|
583
|
-
let i = await
|
|
702
|
+
let i = await J(e, t, n);
|
|
584
703
|
if (i === null || i.count <= 0 || await e.updateMany("_voltro_webhook_rate_windows", {
|
|
585
704
|
count: i.count - 1,
|
|
586
705
|
updatedAt: /* @__PURE__ */ new Date()
|
|
587
|
-
}, { where:
|
|
706
|
+
}, { where: x(S("id", i.id), S("count", i.count)) }) === 1) return;
|
|
588
707
|
}
|
|
589
|
-
throw Error(`webhook rate window: release contention on (${t}) exceeded ${
|
|
590
|
-
},
|
|
708
|
+
throw Error(`webhook rate window: release contention on (${t}) exceeded ${Y} attempts`);
|
|
709
|
+
}, xe = async (e, t, n, r = q) => {
|
|
591
710
|
if (t.length === 0) return {
|
|
592
711
|
acquired: !0,
|
|
593
712
|
retryInMs: 0
|
|
594
713
|
};
|
|
595
714
|
let i = Math.floor(n / r), a = [];
|
|
596
715
|
for (let o of t) {
|
|
597
|
-
if (await
|
|
716
|
+
if (await X(e, o, i)) {
|
|
598
717
|
a.push(o);
|
|
599
718
|
continue;
|
|
600
719
|
}
|
|
601
|
-
for (let t of a) await
|
|
720
|
+
for (let t of a) await Z(e, t.key, i);
|
|
602
721
|
return {
|
|
603
722
|
acquired: !1,
|
|
604
723
|
retryInMs: (i + 1) * r - n
|
|
@@ -608,7 +727,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
608
727
|
acquired: !0,
|
|
609
728
|
retryInMs: 0
|
|
610
729
|
};
|
|
611
|
-
},
|
|
730
|
+
}, Se = (e, t) => {
|
|
612
731
|
if (e === "json") return {
|
|
613
732
|
contentType: "application/json",
|
|
614
733
|
bytes: Buffer.from(t, "utf8")
|
|
@@ -617,22 +736,22 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
617
736
|
try {
|
|
618
737
|
n = JSON.parse(t);
|
|
619
738
|
} catch (t) {
|
|
620
|
-
throw new
|
|
739
|
+
throw new d({
|
|
621
740
|
format: e,
|
|
622
741
|
reason: `payload is not valid JSON: ${t.message}`
|
|
623
742
|
});
|
|
624
743
|
}
|
|
625
744
|
return e === "form" ? {
|
|
626
745
|
contentType: "application/x-www-form-urlencoded",
|
|
627
|
-
bytes: Buffer.from(
|
|
746
|
+
bytes: Buffer.from(Ce(n), "utf8")
|
|
628
747
|
} : {
|
|
629
748
|
contentType: "application/xml",
|
|
630
|
-
bytes: Buffer.from(
|
|
749
|
+
bytes: Buffer.from(Te(n), "utf8")
|
|
631
750
|
};
|
|
632
|
-
},
|
|
633
|
-
if (typeof e != "object" || !e || Array.isArray(e)) throw new
|
|
751
|
+
}, Ce = (e) => {
|
|
752
|
+
if (typeof e != "object" || !e || Array.isArray(e)) throw new d({
|
|
634
753
|
format: "form",
|
|
635
|
-
reason: `form encoding requires a JSON object at the top level, got ${
|
|
754
|
+
reason: `form encoding requires a JSON object at the top level, got ${Oe(e)}`
|
|
636
755
|
});
|
|
637
756
|
let t = new URLSearchParams();
|
|
638
757
|
for (let [n, r] of Object.entries(e)) Q(t, n, r);
|
|
@@ -651,13 +770,13 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
651
770
|
return;
|
|
652
771
|
}
|
|
653
772
|
e.append(t, String(n));
|
|
654
|
-
},
|
|
655
|
-
if (!
|
|
773
|
+
}, we = /^[A-Za-z_][A-Za-z0-9_.-]*$/, Te = (e) => `<?xml version="1.0" encoding="UTF-8"?>${$("webhook", e)}`, $ = (e, t) => {
|
|
774
|
+
if (!we.test(e)) throw new d({
|
|
656
775
|
format: "xml",
|
|
657
776
|
reason: `object key ${JSON.stringify(e)} is not a valid XML element name`
|
|
658
777
|
});
|
|
659
|
-
return `<${e}>${
|
|
660
|
-
},
|
|
778
|
+
return `<${e}>${Ee(e, t)}</${e}>`;
|
|
779
|
+
}, Ee = (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("") : De(String(t)), De = (e) => e.replace(/[&<>"']/g, (e) => {
|
|
661
780
|
switch (e) {
|
|
662
781
|
case "&": return "&";
|
|
663
782
|
case "<": return "<";
|
|
@@ -665,17 +784,17 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
665
784
|
case "\"": return """;
|
|
666
785
|
default: return "'";
|
|
667
786
|
}
|
|
668
|
-
}),
|
|
787
|
+
}), Oe = (e) => e === null ? "null" : Array.isArray(e) ? "array" : typeof e, ke = "_voltro_webhook_targets", Ae = 32, je = async (e, t) => (await e.query({
|
|
669
788
|
table: "_voltro_webhook_targets",
|
|
670
|
-
predicate:
|
|
789
|
+
predicate: S("id", t),
|
|
671
790
|
order: [],
|
|
672
791
|
take: 1,
|
|
673
792
|
skip: void 0,
|
|
674
793
|
projection: void 0
|
|
675
|
-
}))[0] ?? null,
|
|
676
|
-
for (let a = 0; a <
|
|
794
|
+
}))[0] ?? null, Me = async (e, t, n, r, i = /* @__PURE__ */ new Date()) => {
|
|
795
|
+
for (let a = 0; a < Ae; a++) {
|
|
677
796
|
a > 0 && await new Promise((e) => setTimeout(e, a));
|
|
678
|
-
let o = await
|
|
797
|
+
let o = await je(e, t);
|
|
679
798
|
if (o === null) return {
|
|
680
799
|
consecutiveFailures: 0,
|
|
681
800
|
autoDisabled: !1
|
|
@@ -685,7 +804,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
685
804
|
if (s === 0 || await e.updateMany("_voltro_webhook_targets", {
|
|
686
805
|
consecutiveFailures: 0,
|
|
687
806
|
updatedAt: i
|
|
688
|
-
}, { where:
|
|
807
|
+
}, { where: x(S("id", t), S("consecutiveFailures", s)) }) === 1) return {
|
|
689
808
|
consecutiveFailures: 0,
|
|
690
809
|
autoDisabled: !1
|
|
691
810
|
};
|
|
@@ -695,68 +814,68 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
695
814
|
consecutiveFailures: c,
|
|
696
815
|
updatedAt: i
|
|
697
816
|
};
|
|
698
|
-
if (u && (d.active = !1, d.autoDisabledAt = i, d.autoDisableReason = r.slice(0, 500)), await e.updateMany("_voltro_webhook_targets", d, { where:
|
|
817
|
+
if (u && (d.active = !1, d.autoDisabledAt = i, d.autoDisableReason = r.slice(0, 500)), await e.updateMany("_voltro_webhook_targets", d, { where: x(S("id", t), S("consecutiveFailures", s)) }) === 1) return {
|
|
699
818
|
consecutiveFailures: c,
|
|
700
819
|
autoDisabled: u
|
|
701
820
|
};
|
|
702
821
|
}
|
|
703
|
-
throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${
|
|
704
|
-
},
|
|
705
|
-
status:
|
|
706
|
-
body:
|
|
707
|
-
latencyMs:
|
|
708
|
-
retryAfterSec:
|
|
709
|
-
transportError:
|
|
710
|
-
encodeError:
|
|
711
|
-
}),
|
|
712
|
-
outcome:
|
|
713
|
-
delayMs:
|
|
714
|
-
reason:
|
|
715
|
-
autoDisabled:
|
|
716
|
-
consecutiveFailures:
|
|
717
|
-
}),
|
|
718
|
-
acquired:
|
|
719
|
-
retryInMs:
|
|
720
|
-
}),
|
|
822
|
+
throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${Ae} attempts`);
|
|
823
|
+
}, Ne = b.Struct({
|
|
824
|
+
status: b.Number,
|
|
825
|
+
body: b.String,
|
|
826
|
+
latencyMs: b.Number,
|
|
827
|
+
retryAfterSec: b.Union(b.Number, b.Undefined),
|
|
828
|
+
transportError: b.Union(b.String, b.Undefined),
|
|
829
|
+
encodeError: b.Union(b.String, b.Undefined)
|
|
830
|
+
}), Pe = b.Struct({
|
|
831
|
+
outcome: b.Literal("succeeded", "failed", "retry"),
|
|
832
|
+
delayMs: b.Union(b.Number, b.Undefined),
|
|
833
|
+
reason: b.Union(b.String, b.Undefined),
|
|
834
|
+
autoDisabled: b.Union(b.Boolean, b.Undefined),
|
|
835
|
+
consecutiveFailures: b.Union(b.Number, b.Undefined)
|
|
836
|
+
}), Fe = b.Struct({
|
|
837
|
+
acquired: b.Boolean,
|
|
838
|
+
retryInMs: b.Number
|
|
839
|
+
}), Ie = ie.make({
|
|
721
840
|
name: "voltro.deliverWebhook",
|
|
722
841
|
payload: {
|
|
723
|
-
deliveryId:
|
|
724
|
-
targetId:
|
|
725
|
-
event:
|
|
726
|
-
eventId:
|
|
727
|
-
payloadJson:
|
|
728
|
-
attemptEpoch:
|
|
842
|
+
deliveryId: b.String,
|
|
843
|
+
targetId: b.String,
|
|
844
|
+
event: b.String,
|
|
845
|
+
eventId: b.String,
|
|
846
|
+
payloadJson: b.String,
|
|
847
|
+
attemptEpoch: b.optionalWith(b.Number, { default: () => 0 })
|
|
729
848
|
},
|
|
730
|
-
success:
|
|
731
|
-
finalStatus:
|
|
732
|
-
attempts:
|
|
849
|
+
success: b.Struct({
|
|
850
|
+
finalStatus: b.Literal("succeeded", "failed", "deferred"),
|
|
851
|
+
attempts: b.Number
|
|
733
852
|
}),
|
|
734
853
|
idempotencyKey: ({ deliveryId: e, attemptEpoch: t }) => `voltro.deliverWebhook:${e}:${t ?? 0}`
|
|
735
|
-
}),
|
|
736
|
-
let { deliveryId: i, targetId: a, event: s, eventId: c, payloadJson: l } = n, u =
|
|
737
|
-
return
|
|
738
|
-
let n = yield*
|
|
854
|
+
}), Le = (e, t = {}) => (n, r) => {
|
|
855
|
+
let { deliveryId: i, targetId: a, event: s, eventId: c, payloadJson: l } = n, u = w({ scope: `webhook:${i}` });
|
|
856
|
+
return y.gen(function* () {
|
|
857
|
+
let n = yield* T.make({
|
|
739
858
|
name: "fetch-target",
|
|
740
|
-
success:
|
|
741
|
-
id:
|
|
742
|
-
event:
|
|
743
|
-
url:
|
|
744
|
-
secret:
|
|
745
|
-
signing:
|
|
746
|
-
retry:
|
|
747
|
-
headers:
|
|
748
|
-
key:
|
|
749
|
-
value:
|
|
750
|
-
}),
|
|
751
|
-
rateLimitPerMinute:
|
|
752
|
-
active:
|
|
753
|
-
format:
|
|
754
|
-
tenantId:
|
|
859
|
+
success: b.Union(b.Null, b.Struct({
|
|
860
|
+
id: b.String,
|
|
861
|
+
event: b.String,
|
|
862
|
+
url: b.String,
|
|
863
|
+
secret: b.String,
|
|
864
|
+
signing: b.Any,
|
|
865
|
+
retry: b.Any,
|
|
866
|
+
headers: b.Union(b.Record({
|
|
867
|
+
key: b.String,
|
|
868
|
+
value: b.String
|
|
869
|
+
}), b.Null),
|
|
870
|
+
rateLimitPerMinute: b.Union(b.Number, b.Null),
|
|
871
|
+
active: b.Boolean,
|
|
872
|
+
format: b.Literal("json", "form", "xml"),
|
|
873
|
+
tenantId: b.Union(b.String, b.Null)
|
|
755
874
|
})),
|
|
756
|
-
execute:
|
|
875
|
+
execute: y.tryPromise({
|
|
757
876
|
try: async () => (await e.store.query({
|
|
758
877
|
table: "_voltro_webhook_targets",
|
|
759
|
-
predicate:
|
|
878
|
+
predicate: S("id", a),
|
|
760
879
|
order: [{
|
|
761
880
|
column: "createdAt",
|
|
762
881
|
direction: "asc"
|
|
@@ -770,7 +889,7 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
770
889
|
event: s,
|
|
771
890
|
cause: String(e)
|
|
772
891
|
}), /* @__PURE__ */ Error(`fetch-target failed: ${e.message}`))
|
|
773
|
-
}).pipe(
|
|
892
|
+
}).pipe(y.orDie)
|
|
774
893
|
});
|
|
775
894
|
if (n === null) return {
|
|
776
895
|
finalStatus: "failed",
|
|
@@ -795,77 +914,77 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
795
914
|
...o
|
|
796
915
|
});
|
|
797
916
|
};
|
|
798
|
-
if (n.active !== !0) return yield*
|
|
917
|
+
if (n.active !== !0) return yield* T.make({
|
|
799
918
|
name: "record-paused-pending",
|
|
800
|
-
success:
|
|
801
|
-
execute:
|
|
919
|
+
success: b.Void,
|
|
920
|
+
execute: y.tryPromise({
|
|
802
921
|
try: () => r(`${i}:1`, 1, { status: "pending" }),
|
|
803
922
|
catch: (e) => /* @__PURE__ */ Error(`record-paused-pending failed: ${e.message}`)
|
|
804
|
-
}).pipe(
|
|
923
|
+
}).pipe(y.orDie)
|
|
805
924
|
}), {
|
|
806
925
|
finalStatus: "deferred",
|
|
807
926
|
attempts: 0
|
|
808
927
|
};
|
|
809
|
-
let
|
|
928
|
+
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), ee = t.rateWindowMs ?? 6e4, g = t.events?.find((e) => e.id === s)?.globalRateLimit, _ = [...typeof n.rateLimitPerMinute == "number" && n.rateLimitPerMinute > 0 ? [{
|
|
810
929
|
key: `target:${n.id}`,
|
|
811
930
|
limit: n.rateLimitPerMinute
|
|
812
|
-
}] : [], ...
|
|
931
|
+
}] : [], ...g !== void 0 && g.perMinute > 0 ? [{
|
|
813
932
|
key: `event:${s}`,
|
|
814
|
-
limit:
|
|
933
|
+
limit: g.perMinute
|
|
815
934
|
}] : []];
|
|
816
935
|
for (let t = 1; t <= p.maxAttempts; t++) {
|
|
817
|
-
let
|
|
818
|
-
if (
|
|
819
|
-
let i = yield*
|
|
936
|
+
let f = `${i}:${t}`;
|
|
937
|
+
if (_.length > 0) for (let n = 0;; n++) {
|
|
938
|
+
let i = yield* T.make({
|
|
820
939
|
name: `rate-acquire-${t}-${n}`,
|
|
821
|
-
success:
|
|
822
|
-
execute:
|
|
823
|
-
try: () =>
|
|
940
|
+
success: Fe,
|
|
941
|
+
execute: y.tryPromise({
|
|
942
|
+
try: () => xe(e.store, _, Date.now(), ee),
|
|
824
943
|
catch: (e) => (u.debug("rate-acquire raw error", {
|
|
825
944
|
targetId: a,
|
|
826
945
|
event: s,
|
|
827
946
|
attempt: t,
|
|
828
947
|
cause: String(e)
|
|
829
948
|
}), /* @__PURE__ */ Error(`rate-acquire failed: ${e.message}`))
|
|
830
|
-
}).pipe(
|
|
949
|
+
}).pipe(y.orDie)
|
|
831
950
|
});
|
|
832
951
|
if (i.acquired) break;
|
|
833
|
-
yield*
|
|
952
|
+
yield* T.make({
|
|
834
953
|
name: `record-rate-deferral-${t}-${n}`,
|
|
835
|
-
success:
|
|
836
|
-
execute:
|
|
837
|
-
try: () => r(
|
|
954
|
+
success: b.Void,
|
|
955
|
+
execute: y.tryPromise({
|
|
956
|
+
try: () => r(f, t, {
|
|
838
957
|
status: "pending",
|
|
839
958
|
nextAttemptAt: new Date(Date.now() + i.retryInMs)
|
|
840
959
|
}),
|
|
841
960
|
catch: (e) => /* @__PURE__ */ Error(`record-rate-deferral failed: ${e.message}`)
|
|
842
|
-
}).pipe(
|
|
843
|
-
}), yield*
|
|
961
|
+
}).pipe(y.orDie)
|
|
962
|
+
}), yield* E.sleep({
|
|
844
963
|
name: `rate-window-sleep-${t}-${n}`,
|
|
845
964
|
duration: `${i.retryInMs} millis`
|
|
846
965
|
});
|
|
847
966
|
}
|
|
848
|
-
yield*
|
|
967
|
+
yield* T.make({
|
|
849
968
|
name: `record-attempt-start-${t}`,
|
|
850
|
-
success:
|
|
851
|
-
execute:
|
|
852
|
-
try: () => r(
|
|
969
|
+
success: b.Void,
|
|
970
|
+
execute: y.tryPromise({
|
|
971
|
+
try: () => r(f, t, {
|
|
853
972
|
status: "inFlight",
|
|
854
973
|
nextAttemptAt: null,
|
|
855
974
|
scheduledAt: /* @__PURE__ */ new Date()
|
|
856
975
|
}),
|
|
857
976
|
catch: (e) => /* @__PURE__ */ Error(`record-attempt-start failed: ${e.message}`)
|
|
858
|
-
}).pipe(
|
|
977
|
+
}).pipe(y.orDie)
|
|
859
978
|
});
|
|
860
|
-
let
|
|
979
|
+
let g = yield* T.make({
|
|
861
980
|
name: `sign-and-post-${t}`,
|
|
862
|
-
success:
|
|
863
|
-
execute:
|
|
981
|
+
success: Ne,
|
|
982
|
+
execute: y.promise(async () => {
|
|
864
983
|
let e = Date.now(), r;
|
|
865
984
|
try {
|
|
866
|
-
r =
|
|
985
|
+
r = Se(n.format, l);
|
|
867
986
|
} catch (t) {
|
|
868
|
-
if (t instanceof
|
|
987
|
+
if (t instanceof d) return {
|
|
869
988
|
status: 0,
|
|
870
989
|
body: "",
|
|
871
990
|
latencyMs: Date.now() - e,
|
|
@@ -875,14 +994,14 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
875
994
|
};
|
|
876
995
|
throw t;
|
|
877
996
|
}
|
|
878
|
-
let a = Buffer.from(r.bytes), { headerName: u, headerValue:
|
|
879
|
-
process.env.NODE_ENV === "production" &&
|
|
997
|
+
let a = Buffer.from(r.bytes), { headerName: u, headerValue: f } = o(m, a, n.secret);
|
|
998
|
+
process.env.NODE_ENV === "production" && C(n.url);
|
|
880
999
|
try {
|
|
881
1000
|
let o = await fetch(n.url, {
|
|
882
1001
|
method: "POST",
|
|
883
1002
|
headers: {
|
|
884
1003
|
"content-type": r.contentType,
|
|
885
|
-
[u]:
|
|
1004
|
+
[u]: f,
|
|
886
1005
|
"x-voltro-event": s,
|
|
887
1006
|
"x-voltro-event-id": c,
|
|
888
1007
|
"x-voltro-delivery-id": i,
|
|
@@ -891,12 +1010,12 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
891
1010
|
...h ?? {}
|
|
892
1011
|
},
|
|
893
1012
|
body: a
|
|
894
|
-
}), l = (await o.text()).slice(0, 8192),
|
|
1013
|
+
}), l = (await o.text()).slice(0, 8192), d = o.headers.get("retry-after");
|
|
895
1014
|
return {
|
|
896
1015
|
status: o.status,
|
|
897
1016
|
body: l,
|
|
898
1017
|
latencyMs: Date.now() - e,
|
|
899
|
-
retryAfterSec:
|
|
1018
|
+
retryAfterSec: d !== null && /^\d+$/.test(d) ? Number(d) : void 0,
|
|
900
1019
|
transportError: void 0,
|
|
901
1020
|
encodeError: void 0
|
|
902
1021
|
};
|
|
@@ -911,18 +1030,18 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
911
1030
|
};
|
|
912
1031
|
}
|
|
913
1032
|
})
|
|
914
|
-
}),
|
|
1033
|
+
}), v = yield* T.make({
|
|
915
1034
|
name: `classify-and-record-${t}`,
|
|
916
|
-
success:
|
|
917
|
-
execute:
|
|
1035
|
+
success: Pe,
|
|
1036
|
+
execute: y.tryPromise({
|
|
918
1037
|
try: async () => {
|
|
919
|
-
let r =
|
|
1038
|
+
let r = g.status >= 200 && g.status < 300, i = g.transportError !== void 0, o = g.encodeError !== void 0, s = (t, n) => Me(e.store, a, t, n);
|
|
920
1039
|
if (r) {
|
|
921
|
-
await e.store.update("_voltro_webhook_deliveries",
|
|
1040
|
+
await e.store.update("_voltro_webhook_deliveries", f, {
|
|
922
1041
|
status: "succeeded",
|
|
923
|
-
responseStatus:
|
|
924
|
-
responseBody:
|
|
925
|
-
latencyMs:
|
|
1042
|
+
responseStatus: g.status,
|
|
1043
|
+
responseBody: g.body,
|
|
1044
|
+
latencyMs: g.latencyMs,
|
|
926
1045
|
updatedAt: /* @__PURE__ */ new Date()
|
|
927
1046
|
});
|
|
928
1047
|
let t = await s(!0, "");
|
|
@@ -935,17 +1054,17 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
935
1054
|
};
|
|
936
1055
|
}
|
|
937
1056
|
let c = o ? null : A(p, t, {
|
|
938
|
-
...i ? {} : { status:
|
|
939
|
-
...
|
|
1057
|
+
...i ? {} : { status: g.status },
|
|
1058
|
+
...g.retryAfterSec === void 0 ? {} : { retryAfterSeconds: g.retryAfterSec }
|
|
940
1059
|
});
|
|
941
1060
|
if (c === null) {
|
|
942
|
-
let t = o ? `encode (${n.format}): ${
|
|
943
|
-
await e.store.update("_voltro_webhook_deliveries",
|
|
1061
|
+
let t = o ? `encode (${n.format}): ${g.encodeError}` : i ? `transport: ${g.transportError}` : `http ${g.status}: ${g.body.slice(0, 200)}`;
|
|
1062
|
+
await e.store.update("_voltro_webhook_deliveries", f, {
|
|
944
1063
|
status: "failed",
|
|
945
|
-
responseStatus: i || o ? null :
|
|
946
|
-
responseBody: i || o ? null :
|
|
947
|
-
errorMessage: o ? t :
|
|
948
|
-
latencyMs:
|
|
1064
|
+
responseStatus: i || o ? null : g.status,
|
|
1065
|
+
responseBody: i || o ? null : g.body,
|
|
1066
|
+
errorMessage: o ? t : g.transportError ?? null,
|
|
1067
|
+
latencyMs: g.latencyMs,
|
|
949
1068
|
updatedAt: /* @__PURE__ */ new Date()
|
|
950
1069
|
});
|
|
951
1070
|
let r = await s(!1, t);
|
|
@@ -957,12 +1076,12 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
957
1076
|
consecutiveFailures: r.consecutiveFailures
|
|
958
1077
|
};
|
|
959
1078
|
}
|
|
960
|
-
return await e.store.update("_voltro_webhook_deliveries",
|
|
1079
|
+
return await e.store.update("_voltro_webhook_deliveries", f, {
|
|
961
1080
|
status: "retryScheduled",
|
|
962
|
-
responseStatus: i ? null :
|
|
963
|
-
responseBody: i ? null :
|
|
964
|
-
errorMessage:
|
|
965
|
-
latencyMs:
|
|
1081
|
+
responseStatus: i ? null : g.status,
|
|
1082
|
+
responseBody: i ? null : g.body,
|
|
1083
|
+
errorMessage: g.transportError ?? null,
|
|
1084
|
+
latencyMs: g.latencyMs,
|
|
966
1085
|
nextAttemptAt: new Date(Date.now() + c.delayMs),
|
|
967
1086
|
updatedAt: /* @__PURE__ */ new Date()
|
|
968
1087
|
}), {
|
|
@@ -979,24 +1098,24 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
979
1098
|
attempt: t,
|
|
980
1099
|
cause: String(e)
|
|
981
1100
|
}), /* @__PURE__ */ Error(`classify-and-record failed: ${e.message}`))
|
|
982
|
-
}).pipe(
|
|
1101
|
+
}).pipe(y.orDie)
|
|
983
1102
|
});
|
|
984
|
-
if (
|
|
1103
|
+
if (v.autoDisabled === !0 && u.warn("target auto-disabled after consecutive failures", {
|
|
985
1104
|
targetId: a,
|
|
986
1105
|
event: s,
|
|
987
|
-
consecutiveFailures:
|
|
988
|
-
reason:
|
|
989
|
-
}),
|
|
1106
|
+
consecutiveFailures: v.consecutiveFailures ?? null,
|
|
1107
|
+
reason: v.reason ?? null
|
|
1108
|
+
}), v.outcome === "succeeded") return {
|
|
990
1109
|
finalStatus: "succeeded",
|
|
991
1110
|
attempts: t
|
|
992
1111
|
};
|
|
993
|
-
if (
|
|
1112
|
+
if (v.outcome === "failed") return {
|
|
994
1113
|
finalStatus: "failed",
|
|
995
1114
|
attempts: t
|
|
996
1115
|
};
|
|
997
|
-
|
|
1116
|
+
v.delayMs !== void 0 && (yield* E.sleep({
|
|
998
1117
|
name: `retry-sleep-${t}`,
|
|
999
|
-
duration: `${
|
|
1118
|
+
duration: `${v.delayMs} millis`
|
|
1000
1119
|
}));
|
|
1001
1120
|
}
|
|
1002
1121
|
return {
|
|
@@ -1006,4 +1125,4 @@ var ae = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
|
|
|
1006
1125
|
});
|
|
1007
1126
|
};
|
|
1008
1127
|
//#endregion
|
|
1009
|
-
export {
|
|
1128
|
+
export { B as IdempotencyCache, q as RATE_WINDOW_MS, K as RATE_WINDOW_TABLE, ke as TARGETS_TABLE, l as WebhookDeliveryNotFound, u as WebhookPayloadInvalid, d as WebhookPayloadUnrepresentable, f as WebhookPayloadVersionInvalid, p as WebhookSubscribeInvalid, M as WebhooksService, m as _voltroWebhookDeliveriesTable, h as _voltroWebhookRateWindowsTable, ee as _voltroWebhookTargetsTable, xe as acquireRateSlots, D as asOutgoingEvent, Le as buildDeliverWebhookExecute, me as buildWebhooksService, de as compareVersions, X as consumeRateSlot, ye as defaultIncomingPath, c as defaultOutgoingSignature, j as defaultRetryPolicy, s as defineIncomingWebhook, t as defineWebhookProvider, Ie as deliverWebhookWorkflow, Se as encodePayload, se as fastRetryPolicy, P as generateSecret, H as getIdempotencyCache, i as githubSignature, ae as hasWebhookAudience, r as isWebhookDescriptor, z as matchesFilter, ve as mountIncomingWebhook, A as nextRetry, O as parseDuration, W as parseTtl, Me as recordDeliveryOutcome, Z as releaseRateSlot, I as resolveSubscribe, o as signPayload, n as slackSignature, e as stripeSignature, ge as useWebhooks, he as useWebhooksEffect, F as validateSubscribe, a as verifySignature, g as webhookTables };
|