@voltro/plugin-webhooks 0.26.0 → 0.28.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/dist/index.js CHANGED
@@ -6,10 +6,10 @@ import { Context as te, Effect as y, Either as ne, Schema as b } from "effect";
6
6
  import { and as x, eq as S } from "@voltro/database";
7
7
  import { assertPublicUrl as C } from "@voltro/integration-http";
8
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";
9
+ import { createLogger as ie } from "@voltro/logger";
10
+ import { Activity as w, DurableClock as T, Workflow as ae } from "@effect/workflow";
11
11
  //#region src/eventAudience.ts
12
- var ae = (e) => e.webhook !== void 0, D = (e) => {
12
+ var oe = (e) => e.webhook !== void 0, E = (e) => {
13
13
  let t = e.webhook;
14
14
  if (t !== void 0) return {
15
15
  _tag: "outgoingEvent",
@@ -19,8 +19,8 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
19
19
  ...t.version === void 0 ? {} : { version: t.version },
20
20
  ...t.rateLimit === void 0 ? {} : { globalRateLimit: t.rateLimit }
21
21
  };
22
- }, oe = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, O = (e) => {
23
- let t = oe.exec(e);
22
+ }, se = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, D = (e) => {
23
+ let t = se.exec(e);
24
24
  if (!t) throw Error(`invalid duration literal: ${e}`);
25
25
  let n = Number(t[1]), r = t[2] ?? "s";
26
26
  switch (r) {
@@ -31,7 +31,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
31
31
  case "d": return n * 864e5;
32
32
  default: throw Error(`unreachable unit: ${r}`);
33
33
  }
34
- }, k = [
34
+ }, O = [
35
35
  408,
36
36
  425,
37
37
  429,
@@ -39,16 +39,16 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
39
39
  502,
40
40
  503,
41
41
  504
42
- ], A = (e, t, n) => {
43
- if (t >= e.maxAttempts || n?.status !== void 0 && !(e.retryOn ?? k).includes(n.status)) return null;
42
+ ], k = (e, t, n) => {
43
+ if (t >= e.maxAttempts || n?.status !== void 0 && !(e.retryOn ?? O).includes(n.status)) return null;
44
44
  if (e.honourRetryAfter !== !1 && n?.retryAfterSeconds !== void 0) {
45
- let t = n.retryAfterSeconds * 1e3, r = O(e.maxDelay), i = Math.min(t, r);
45
+ let t = n.retryAfterSeconds * 1e3, r = D(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 = O(e.initialDelay), i = O(e.maxDelay), a;
51
+ let r = D(e.initialDelay), i = D(e.maxDelay), a;
52
52
  switch (e.strategy) {
53
53
  case "fixed":
54
54
  a = r;
@@ -66,23 +66,23 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
66
66
  delayMs: Math.max(1, Math.floor(o)),
67
67
  baseDelayMs: a
68
68
  };
69
- }, j = () => ({
69
+ }, A = () => ({
70
70
  strategy: "exponential",
71
71
  maxAttempts: 8,
72
72
  initialDelay: "5s",
73
73
  maxDelay: "1h",
74
- retryOn: k,
74
+ retryOn: O,
75
75
  honourRetryAfter: !0,
76
76
  jitter: "full"
77
- }), se = () => ({
77
+ }), ce = () => ({
78
78
  strategy: "fixed",
79
79
  maxAttempts: 120,
80
80
  initialDelay: "30s",
81
81
  maxDelay: "30s",
82
- retryOn: k,
82
+ retryOn: O,
83
83
  honourRetryAfter: !0,
84
84
  jitter: "full"
85
- }), M = class extends te.Tag("@voltro/webhooks/WebhooksService")() {}, ce = 32, N = 32, P = () => _(ce).toString("hex"), F = (e) => {
85
+ }), j = class extends te.Tag("@voltro/webhooks/WebhooksService")() {}, le = 32, ue = 32, M = () => _(le).toString("hex"), N = (e) => e.events ?? (e.event === void 0 ? [] : [e.event]), P = (e) => {
86
86
  let t;
87
87
  try {
88
88
  t = new URL(e.url);
@@ -104,13 +104,26 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
104
104
  reason: e.message
105
105
  });
106
106
  }
107
- if (e.event.length === 0) throw new p({
107
+ let n = N(e);
108
+ if (n.length === 0) throw new p({
108
109
  field: "event",
109
110
  reason: "event id required"
110
111
  });
111
- if (e.secret !== void 0 && e.secret.length < N) throw new p({
112
+ if (e.event !== void 0 && e.events !== void 0) throw new p({
113
+ field: "event",
114
+ reason: "pass `event` OR `events`, not both — the row set would be ambiguous"
115
+ });
116
+ if (n.some((e) => e.length === 0)) throw new p({
117
+ field: "events",
118
+ reason: "event id required"
119
+ });
120
+ if (new Set(n).size !== n.length) throw new p({
121
+ field: "events",
122
+ reason: "duplicate event — one row per event, so a repeat would deliver twice"
123
+ });
124
+ if (e.secret !== void 0 && e.secret.length < ue) throw new p({
112
125
  field: "secret",
113
- reason: `secret must be at least ${N} chars`
126
+ reason: `secret must be at least ${ue} chars`
114
127
  });
115
128
  if (e.retry !== void 0) {
116
129
  if (e.retry.maxAttempts < 1) throw new p({
@@ -130,13 +143,13 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
130
143
  field: "autoDisableAfter",
131
144
  reason: `autoDisableAfter must be a positive integer, got ${e.autoDisableAfter}`
132
145
  });
133
- }, I = (e, t) => ({
146
+ }, F = (e, t, n, r) => ({
134
147
  id: v(),
135
- event: e.event,
148
+ event: n ?? e.event ?? "",
136
149
  url: e.url,
137
- secret: e.secret ?? P(),
150
+ secret: r ?? e.secret ?? M(),
138
151
  signing: e.signing ?? t?.defaultSigning ?? c(),
139
- retry: e.retry ?? t?.defaultRetry ?? j(),
152
+ retry: e.retry ?? t?.defaultRetry ?? A(),
140
153
  filter: e.filter ?? null,
141
154
  scope: e.scope ?? null,
142
155
  headers: e.headers ?? null,
@@ -149,14 +162,14 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
149
162
  autoDisableReason: null,
150
163
  payloadVersion: e.payloadVersion ?? t?.version ?? 1,
151
164
  description: e.description ?? null
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) => {
165
+ }), de = (e) => e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : 0, I = (e) => e instanceof Date ? e.toISOString() : typeof e == "string" ? e : null, fe = (e) => {
153
166
  if (typeof e != "string") return e;
154
167
  try {
155
168
  return JSON.parse(e);
156
169
  } catch {
157
170
  return e;
158
171
  }
159
- }, R = (e) => ({
172
+ }, L = (e) => ({
160
173
  id: String(e.id),
161
174
  deliveryId: String(e.deliveryId),
162
175
  targetId: String(e.targetId),
@@ -167,22 +180,22 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
167
180
  responseStatus: e.responseStatus ?? null,
168
181
  errorMessage: e.errorMessage ?? null,
169
182
  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) => {
183
+ scheduledAt: I(e.scheduledAt),
184
+ nextAttemptAt: I(e.nextAttemptAt),
185
+ createdAt: I(e.createdAt)
186
+ }), pe = (e, t) => e === t ? "current" : e < t ? "behind" : "ahead", R = (e, t) => {
174
187
  if (e === null) return !0;
175
188
  let n = { payload: t };
176
- for (let [t, r] of Object.entries(e)) if (!pe(fe(n, t), r)) return !1;
189
+ for (let [t, r] of Object.entries(e)) if (!he(me(n, t), r)) return !1;
177
190
  return !0;
178
- }, fe = (e, t) => {
191
+ }, me = (e, t) => {
179
192
  let n = t.split("."), r = e;
180
193
  for (let e of n) {
181
194
  if (typeof r != "object" || !r) return;
182
195
  r = r[e];
183
196
  }
184
197
  return r;
185
- }, pe = (e, t) => {
198
+ }, he = (e, t) => {
186
199
  if (typeof t == "object" && t && !Array.isArray(t)) {
187
200
  let n = t;
188
201
  if ("eq" in n) return e === n.eq;
@@ -193,37 +206,58 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
193
206
  if ("in" in n && Array.isArray(n.in)) return n.in.includes(e);
194
207
  }
195
208
  return e === t;
196
- }, me = (e, t, n = {}) => {
197
- let r = new Map((n.events ?? []).map((e) => [e.id, e]));
198
- return {
209
+ }, z = (e, t) => e !== null && Object.entries(t).every(([t, n]) => e[t] === n), ge = async (e, t) => {
210
+ let n = (await e.store.query({
211
+ table: "_voltro_webhook_targets",
212
+ predicate: void 0,
213
+ order: [{
214
+ column: "createdAt",
215
+ direction: "asc"
216
+ }],
217
+ take: 1e3,
218
+ skip: void 0,
219
+ projection: void 0
220
+ })).filter((e) => z(e.scope, t));
221
+ if (n.length === 0) return;
222
+ let r = new Set(n.map((e) => e.secret));
223
+ 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
+ return [...r][0];
225
+ }, _e = (e, t, n = {}) => {
226
+ let r = new Map((n.events ?? []).map((e) => [e.id, e])), i = {
199
227
  subscribe: async (t) => {
200
- F(t);
201
- let n = I(t, r.get(t.event));
202
- return await e.store.insert("_voltro_webhook_targets", {
203
- ...n,
228
+ P(t);
229
+ let n = N(t), i = t.secret === void 0 && t.scope !== void 0 ? await ge(e, t.scope) : void 0, a = t.secret ?? i ?? M(), o = n.map((e) => F(t, r.get(e), e, a));
230
+ for (let t of o) await e.store.insert("_voltro_webhook_targets", {
231
+ ...t,
204
232
  createdAt: /* @__PURE__ */ new Date(),
205
233
  updatedAt: /* @__PURE__ */ new Date()
206
- }), {
207
- id: n.id,
208
- event: n.event,
209
- url: n.url,
210
- secret: n.secret,
211
- signing: n.signing,
212
- retry: n.retry
234
+ });
235
+ let s = o[0];
236
+ return {
237
+ id: s.id,
238
+ event: s.event,
239
+ url: s.url,
240
+ secret: s.secret,
241
+ signing: s.signing,
242
+ retry: s.retry,
243
+ ...o.length > 1 ? { targets: o.map((e) => ({
244
+ id: e.id,
245
+ event: e.event
246
+ })) } : {}
213
247
  };
214
248
  },
215
- emit: async (n, i) => {
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" });
249
+ emit: async (n, i, a) => {
250
+ let o = typeof n == "object" && n.kind === "event" ? E(n) : void 0, s = typeof n == "string" ? r.get(n) : o ?? n, c = typeof n == "string" ? n : n.id ?? n.name;
251
+ if (s !== void 0) {
252
+ let e = b.decodeUnknownEither(s.payload)(i, { errors: "all" });
219
253
  if (ne.isLeft(e)) throw new u({
220
- event: s,
254
+ event: c,
221
255
  issues: e.left.message
222
256
  });
223
257
  }
224
- let c = v(), l = (await e.store.query({
258
+ let l = v(), d = a?.tenantId, f = (await e.store.query({
225
259
  table: "_voltro_webhook_targets",
226
- predicate: S("event", s),
260
+ predicate: d == null ? S("event", c) : x(S("event", c), S("tenantId", d)),
227
261
  order: [{
228
262
  column: "createdAt",
229
263
  direction: "asc"
@@ -231,16 +265,16 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
231
265
  take: 1e3,
232
266
  skip: void 0,
233
267
  projection: void 0
234
- })).filter((e) => z(e.filter, i)), d = [], f = JSON.stringify(i);
235
- for (let n of l) {
268
+ })).filter((e) => R(e.filter, i)), p = [], m = JSON.stringify(i);
269
+ for (let n of f) {
236
270
  let r = v();
237
271
  n.active ? (await t({
238
272
  deliveryId: r,
239
273
  targetId: n.id,
240
- event: s,
241
- eventId: c,
242
- payloadJson: f
243
- }), d.push({
274
+ event: c,
275
+ eventId: l,
276
+ payloadJson: m
277
+ }), p.push({
244
278
  targetId: n.id,
245
279
  deliveryId: r,
246
280
  status: "dispatched"
@@ -249,23 +283,23 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
249
283
  deliveryId: r,
250
284
  targetId: n.id,
251
285
  tenantId: n.tenantId ?? null,
252
- event: s,
253
- eventId: c,
286
+ event: c,
287
+ eventId: l,
254
288
  attempt: 1,
255
289
  status: "pending",
256
- payload: f,
290
+ payload: m,
257
291
  scheduledAt: /* @__PURE__ */ new Date(),
258
292
  createdAt: /* @__PURE__ */ new Date(),
259
293
  updatedAt: /* @__PURE__ */ new Date()
260
- }), d.push({
294
+ }), p.push({
261
295
  targetId: n.id,
262
296
  deliveryId: r,
263
297
  status: "queued"
264
298
  }));
265
299
  }
266
300
  return {
267
- eventId: c,
268
- deliveries: d
301
+ eventId: l,
302
+ deliveries: p
269
303
  };
270
304
  },
271
305
  replay: async (n) => {
@@ -290,52 +324,68 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
290
324
  attemptEpoch: Date.now()
291
325
  });
292
326
  },
327
+ resolveTargets: async (t) => {
328
+ if (typeof t == "string") return [t];
329
+ let n = (await e.store.query({
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);
337
+ if (n.length === 0) throw Error(`webhooks: no target matches scope ${JSON.stringify(t.scope)} — the operation would have silently done nothing`);
338
+ return n;
339
+ },
293
340
  pauseTarget: async (t) => {
294
- await e.store.update("_voltro_webhook_targets", t, {
341
+ for (let n of await i.resolveTargets(t)) await e.store.update("_voltro_webhook_targets", n, {
295
342
  active: !1,
296
343
  updatedAt: /* @__PURE__ */ new Date()
297
344
  });
298
345
  },
299
346
  resumeTarget: async (n) => {
300
- await e.store.update("_voltro_webhook_targets", n, {
301
- active: !0,
302
- consecutiveFailures: 0,
303
- autoDisabledAt: null,
304
- autoDisableReason: null,
305
- updatedAt: /* @__PURE__ */ new Date()
306
- });
307
- let r = await e.store.query({
308
- table: "_voltro_webhook_deliveries",
309
- predicate: x(S("targetId", n), S("status", "pending")),
310
- order: [{
311
- column: "createdAt",
312
- direction: "asc"
313
- }],
314
- take: 1e4,
315
- skip: void 0,
316
- projection: void 0
317
- }), i = Date.now();
318
- for (let e of r) await t({
319
- deliveryId: e.deliveryId,
320
- targetId: n,
321
- event: e.event,
322
- eventId: e.eventId ?? v(),
323
- payloadJson: typeof e.payload == "string" ? e.payload : JSON.stringify(e.payload),
324
- attemptEpoch: i
325
- });
347
+ for (let r of await i.resolveTargets(n)) {
348
+ await e.store.update("_voltro_webhook_targets", r, {
349
+ active: !0,
350
+ consecutiveFailures: 0,
351
+ autoDisabledAt: null,
352
+ autoDisableReason: null,
353
+ updatedAt: /* @__PURE__ */ new Date()
354
+ });
355
+ let n = await e.store.query({
356
+ table: "_voltro_webhook_deliveries",
357
+ predicate: x(S("targetId", r), S("status", "pending")),
358
+ order: [{
359
+ column: "createdAt",
360
+ direction: "asc"
361
+ }],
362
+ take: 1e4,
363
+ skip: void 0,
364
+ projection: void 0
365
+ }), i = Date.now();
366
+ for (let e of n) await t({
367
+ deliveryId: e.deliveryId,
368
+ targetId: r,
369
+ event: e.event,
370
+ eventId: e.eventId ?? v(),
371
+ payloadJson: typeof e.payload == "string" ? e.payload : JSON.stringify(e.payload),
372
+ attemptEpoch: i
373
+ });
374
+ }
326
375
  },
327
376
  deleteTarget: async (t) => {
328
- await e.store.delete("_voltro_webhook_targets", t), await e.store.deleteMany("_voltro_webhook_deliveries", { where: x(S("targetId", t), S("status", "pending")) });
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: x(S("targetId", n), S("status", "pending")) });
329
378
  },
330
379
  rotateSecret: async (t) => {
331
- let n = P();
332
- return await e.store.update("_voltro_webhook_targets", t, {
380
+ let n = M();
381
+ for (let r of await i.resolveTargets(t)) await e.store.update("_voltro_webhook_targets", r, {
333
382
  secret: n,
334
383
  updatedAt: /* @__PURE__ */ new Date()
335
- }), { secret: n };
384
+ });
385
+ return { secret: n };
336
386
  },
337
387
  updateTarget: async (t, n) => {
338
- if (n.url !== void 0 && F({
388
+ if (n.url !== void 0 && P({
339
389
  event: "x",
340
390
  url: n.url
341
391
  }), 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");
@@ -350,7 +400,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
350
400
  "autoDisableAfter",
351
401
  "format"
352
402
  ]) n[e] !== void 0 && (r[e] = n[e]);
353
- await e.store.update("_voltro_webhook_targets", t, r);
403
+ for (let n of await i.resolveTargets(t)) await e.store.update("_voltro_webhook_targets", n, r);
354
404
  },
355
405
  testTarget: async (n, r) => {
356
406
  let i = (await e.store.query({
@@ -389,7 +439,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
389
439
  }), { deliveryId: a };
390
440
  },
391
441
  listDeliveries: async (t) => {
392
- let n = await e.store.query({
442
+ let n = t?.scope === void 0 ? void 0 : new Set(await i.resolveTargets({ scope: t.scope })), r = await e.store.query({
393
443
  table: "_voltro_webhook_deliveries",
394
444
  predicate: t?.targetId === void 0 ? void 0 : S("targetId", t.targetId),
395
445
  order: [{
@@ -399,8 +449,8 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
399
449
  take: Math.min((t?.limit ?? 200) * (t?.status !== void 0 || t?.since !== void 0 ? 10 : 1), 5e3),
400
450
  skip: void 0,
401
451
  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);
452
+ }), 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 || de(e.createdAt) >= a).slice(0, t?.limit ?? 200).map(L);
404
454
  },
405
455
  getDelivery: async (t) => {
406
456
  let n = (await e.store.query({
@@ -412,8 +462,8 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
412
462
  projection: void 0
413
463
  }))[0];
414
464
  return n === void 0 ? null : {
415
- ...R(n),
416
- payload: ue(n.payload),
465
+ ...L(n),
466
+ payload: fe(n.payload),
417
467
  responseBody: n.responseBody ?? null
418
468
  };
419
469
  },
@@ -452,15 +502,16 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
452
502
  }));
453
503
  }
454
504
  };
455
- }, he = y.gen(function* () {
456
- return yield* M;
457
- }), ge = (e) => {
505
+ return i;
506
+ }, ve = y.gen(function* () {
507
+ return yield* j;
508
+ }), ye = (e) => {
458
509
  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.");
459
510
  return e.webhooks;
460
- }, _e = 1e4, B = class {
511
+ }, be = 1e4, B = class {
461
512
  capacity;
462
513
  cache = /* @__PURE__ */ new Map();
463
- constructor(e = _e) {
514
+ constructor(e = be) {
464
515
  this.capacity = e;
465
516
  }
466
517
  evictExpired() {
@@ -506,9 +557,9 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
506
557
  status: e,
507
558
  contentType: "application/json; charset=utf-8",
508
559
  body: JSON.stringify(t)
509
- }), ve = (e, t) => {
560
+ }), xe = (e, t) => {
510
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 ?? H(), c = W(i?.ttl), l = t.log ?? ((e) => {
511
- w({ scope: `webhook:${e.webhookId}` }).debug("incoming webhook", {
562
+ ie({ scope: `webhook:${e.webhookId}` }).debug("incoming webhook", {
512
563
  status: e.status,
513
564
  signatureOk: e.signatureOk,
514
565
  idempotency: e.idempotency,
@@ -667,7 +718,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
667
718
  }), n;
668
719
  }
669
720
  };
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({
721
+ }, Se = (e) => `/webhooks/${e}`, K = "_voltro_webhook_rate_windows", q = 6e4, Ce = (e, t) => `${e}@${t}`, J = async (e, t, n) => (await e.query({
671
722
  table: "_voltro_webhook_rate_windows",
672
723
  predicate: x(S("scope", t), S("bucket", n)),
673
724
  order: [],
@@ -680,7 +731,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
680
731
  let i = await J(e, t.key, n);
681
732
  if (i === null) {
682
733
  await e.insertIgnore(K, {
683
- id: be(t.key, n),
734
+ id: Ce(t.key, n),
684
735
  scope: t.key,
685
736
  bucket: n,
686
737
  count: 0,
@@ -696,7 +747,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
696
747
  }, { where: x(S("id", i.id), S("count", i.count)) }) === 1) return !0;
697
748
  }
698
749
  throw Error(`webhook rate window: contention on (${t.key}, ${n}) exceeded ${Y} attempts`);
699
- }, Z = async (e, t, n) => {
750
+ }, we = async (e, t, n) => {
700
751
  for (let r = 0; r < Y; r++) {
701
752
  r > 0 && await new Promise((e) => setTimeout(e, r));
702
753
  let i = await J(e, t, n);
@@ -706,7 +757,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
706
757
  }, { where: x(S("id", i.id), S("count", i.count)) }) === 1) return;
707
758
  }
708
759
  throw Error(`webhook rate window: release contention on (${t}) exceeded ${Y} attempts`);
709
- }, xe = async (e, t, n, r = q) => {
760
+ }, Te = async (e, t, n, r = q) => {
710
761
  if (t.length === 0) return {
711
762
  acquired: !0,
712
763
  retryInMs: 0
@@ -717,7 +768,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
717
768
  a.push(o);
718
769
  continue;
719
770
  }
720
- for (let t of a) await Z(e, t.key, i);
771
+ for (let t of a) await we(e, t.key, i);
721
772
  return {
722
773
  acquired: !1,
723
774
  retryInMs: (i + 1) * r - n
@@ -727,7 +778,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
727
778
  acquired: !0,
728
779
  retryInMs: 0
729
780
  };
730
- }, Se = (e, t) => {
781
+ }, Ee = (e, t) => {
731
782
  if (e === "json") return {
732
783
  contentType: "application/json",
733
784
  bytes: Buffer.from(t, "utf8")
@@ -743,40 +794,40 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
743
794
  }
744
795
  return e === "form" ? {
745
796
  contentType: "application/x-www-form-urlencoded",
746
- bytes: Buffer.from(Ce(n), "utf8")
797
+ bytes: Buffer.from(De(n), "utf8")
747
798
  } : {
748
799
  contentType: "application/xml",
749
- bytes: Buffer.from(Te(n), "utf8")
800
+ bytes: Buffer.from(ke(n), "utf8")
750
801
  };
751
- }, Ce = (e) => {
802
+ }, De = (e) => {
752
803
  if (typeof e != "object" || !e || Array.isArray(e)) throw new d({
753
804
  format: "form",
754
- reason: `form encoding requires a JSON object at the top level, got ${Oe(e)}`
805
+ reason: `form encoding requires a JSON object at the top level, got ${Me(e)}`
755
806
  });
756
807
  let t = new URLSearchParams();
757
- for (let [n, r] of Object.entries(e)) Q(t, n, r);
808
+ for (let [n, r] of Object.entries(e)) Z(t, n, r);
758
809
  return t.toString();
759
- }, Q = (e, t, n) => {
810
+ }, Z = (e, t, n) => {
760
811
  if (n === null) {
761
812
  e.append(t, "");
762
813
  return;
763
814
  }
764
815
  if (Array.isArray(n)) {
765
- n.forEach((n, r) => Q(e, `${t}[${r}]`, n));
816
+ n.forEach((n, r) => Z(e, `${t}[${r}]`, n));
766
817
  return;
767
818
  }
768
819
  if (typeof n == "object") {
769
- for (let [r, i] of Object.entries(n)) Q(e, `${t}[${r}]`, i);
820
+ for (let [r, i] of Object.entries(n)) Z(e, `${t}[${r}]`, i);
770
821
  return;
771
822
  }
772
823
  e.append(t, String(n));
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({
824
+ }, Oe = /^[A-Za-z_][A-Za-z0-9_.-]*$/, ke = (e) => `<?xml version="1.0" encoding="UTF-8"?>${Q("webhook", e)}`, Q = (e, t) => {
825
+ if (!Oe.test(e)) throw new d({
775
826
  format: "xml",
776
827
  reason: `object key ${JSON.stringify(e)} is not a valid XML element name`
777
828
  });
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) => {
829
+ return `<${e}>${Ae(e, t)}</${e}>`;
830
+ }, Ae = (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("") : je(String(t)), je = (e) => e.replace(/[&<>"']/g, (e) => {
780
831
  switch (e) {
781
832
  case "&": return "&amp;";
782
833
  case "<": return "&lt;";
@@ -784,17 +835,17 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
784
835
  case "\"": return "&quot;";
785
836
  default: return "&apos;";
786
837
  }
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({
838
+ }), Me = (e) => e === null ? "null" : Array.isArray(e) ? "array" : typeof e, Ne = "_voltro_webhook_targets", Pe = 32, Fe = async (e, t) => (await e.query({
788
839
  table: "_voltro_webhook_targets",
789
840
  predicate: S("id", t),
790
841
  order: [],
791
842
  take: 1,
792
843
  skip: void 0,
793
844
  projection: void 0
794
- }))[0] ?? null, Me = async (e, t, n, r, i = /* @__PURE__ */ new Date()) => {
795
- for (let a = 0; a < Ae; a++) {
845
+ }))[0] ?? null, $ = async (e, t, n, r, i = /* @__PURE__ */ new Date()) => {
846
+ for (let a = 0; a < Pe; a++) {
796
847
  a > 0 && await new Promise((e) => setTimeout(e, a));
797
- let o = await je(e, t);
848
+ let o = await Fe(e, t);
798
849
  if (o === null) return {
799
850
  consecutiveFailures: 0,
800
851
  autoDisabled: !1
@@ -819,24 +870,24 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
819
870
  autoDisabled: u
820
871
  };
821
872
  }
822
- throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${Ae} attempts`);
823
- }, Ne = b.Struct({
873
+ throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${Pe} attempts`);
874
+ }, Ie = b.Struct({
824
875
  status: b.Number,
825
876
  body: b.String,
826
877
  latencyMs: b.Number,
827
878
  retryAfterSec: b.Union(b.Number, b.Undefined),
828
879
  transportError: b.Union(b.String, b.Undefined),
829
880
  encodeError: b.Union(b.String, b.Undefined)
830
- }), Pe = b.Struct({
881
+ }), Le = b.Struct({
831
882
  outcome: b.Literal("succeeded", "failed", "retry"),
832
883
  delayMs: b.Union(b.Number, b.Undefined),
833
884
  reason: b.Union(b.String, b.Undefined),
834
885
  autoDisabled: b.Union(b.Boolean, b.Undefined),
835
886
  consecutiveFailures: b.Union(b.Number, b.Undefined)
836
- }), Fe = b.Struct({
887
+ }), Re = b.Struct({
837
888
  acquired: b.Boolean,
838
889
  retryInMs: b.Number
839
- }), Ie = ie.make({
890
+ }), ze = ae.make({
840
891
  name: "voltro.deliverWebhook",
841
892
  payload: {
842
893
  deliveryId: b.String,
@@ -851,10 +902,10 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
851
902
  attempts: b.Number
852
903
  }),
853
904
  idempotencyKey: ({ deliveryId: e, attemptEpoch: t }) => `voltro.deliverWebhook:${e}:${t ?? 0}`
854
- }), Le = (e, t = {}) => (n, r) => {
855
- let { deliveryId: i, targetId: a, event: s, eventId: c, payloadJson: l } = n, u = w({ scope: `webhook:${i}` });
905
+ }), Be = (e, t = {}) => (n, r) => {
906
+ let { deliveryId: i, targetId: a, event: s, eventId: c, payloadJson: l } = n, u = ie({ scope: `webhook:${i}` });
856
907
  return y.gen(function* () {
857
- let n = yield* T.make({
908
+ let n = yield* w.make({
858
909
  name: "fetch-target",
859
910
  success: b.Union(b.Null, b.Struct({
860
911
  id: b.String,
@@ -914,7 +965,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
914
965
  ...o
915
966
  });
916
967
  };
917
- if (n.active !== !0) return yield* T.make({
968
+ if (n.active !== !0) return yield* w.make({
918
969
  name: "record-paused-pending",
919
970
  success: b.Void,
920
971
  execute: y.tryPromise({
@@ -935,11 +986,11 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
935
986
  for (let t = 1; t <= p.maxAttempts; t++) {
936
987
  let f = `${i}:${t}`;
937
988
  if (_.length > 0) for (let n = 0;; n++) {
938
- let i = yield* T.make({
989
+ let i = yield* w.make({
939
990
  name: `rate-acquire-${t}-${n}`,
940
- success: Fe,
991
+ success: Re,
941
992
  execute: y.tryPromise({
942
- try: () => xe(e.store, _, Date.now(), ee),
993
+ try: () => Te(e.store, _, Date.now(), ee),
943
994
  catch: (e) => (u.debug("rate-acquire raw error", {
944
995
  targetId: a,
945
996
  event: s,
@@ -949,7 +1000,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
949
1000
  }).pipe(y.orDie)
950
1001
  });
951
1002
  if (i.acquired) break;
952
- yield* T.make({
1003
+ yield* w.make({
953
1004
  name: `record-rate-deferral-${t}-${n}`,
954
1005
  success: b.Void,
955
1006
  execute: y.tryPromise({
@@ -959,12 +1010,12 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
959
1010
  }),
960
1011
  catch: (e) => /* @__PURE__ */ Error(`record-rate-deferral failed: ${e.message}`)
961
1012
  }).pipe(y.orDie)
962
- }), yield* E.sleep({
1013
+ }), yield* T.sleep({
963
1014
  name: `rate-window-sleep-${t}-${n}`,
964
1015
  duration: `${i.retryInMs} millis`
965
1016
  });
966
1017
  }
967
- yield* T.make({
1018
+ yield* w.make({
968
1019
  name: `record-attempt-start-${t}`,
969
1020
  success: b.Void,
970
1021
  execute: y.tryPromise({
@@ -976,13 +1027,13 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
976
1027
  catch: (e) => /* @__PURE__ */ Error(`record-attempt-start failed: ${e.message}`)
977
1028
  }).pipe(y.orDie)
978
1029
  });
979
- let g = yield* T.make({
1030
+ let g = yield* w.make({
980
1031
  name: `sign-and-post-${t}`,
981
- success: Ne,
1032
+ success: Ie,
982
1033
  execute: y.promise(async () => {
983
1034
  let e = Date.now(), r;
984
1035
  try {
985
- r = Se(n.format, l);
1036
+ r = Ee(n.format, l);
986
1037
  } catch (t) {
987
1038
  if (t instanceof d) return {
988
1039
  status: 0,
@@ -1030,12 +1081,12 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
1030
1081
  };
1031
1082
  }
1032
1083
  })
1033
- }), v = yield* T.make({
1084
+ }), v = yield* w.make({
1034
1085
  name: `classify-and-record-${t}`,
1035
- success: Pe,
1086
+ success: Le,
1036
1087
  execute: y.tryPromise({
1037
1088
  try: async () => {
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);
1089
+ let r = g.status >= 200 && g.status < 300, i = g.transportError !== void 0, o = g.encodeError !== void 0, s = (t, n) => $(e.store, a, t, n);
1039
1090
  if (r) {
1040
1091
  await e.store.update("_voltro_webhook_deliveries", f, {
1041
1092
  status: "succeeded",
@@ -1053,7 +1104,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
1053
1104
  consecutiveFailures: t.consecutiveFailures
1054
1105
  };
1055
1106
  }
1056
- let c = o ? null : A(p, t, {
1107
+ let c = o ? null : k(p, t, {
1057
1108
  ...i ? {} : { status: g.status },
1058
1109
  ...g.retryAfterSec === void 0 ? {} : { retryAfterSeconds: g.retryAfterSec }
1059
1110
  });
@@ -1113,7 +1164,7 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
1113
1164
  finalStatus: "failed",
1114
1165
  attempts: t
1115
1166
  };
1116
- v.delayMs !== void 0 && (yield* E.sleep({
1167
+ v.delayMs !== void 0 && (yield* T.sleep({
1117
1168
  name: `retry-sleep-${t}`,
1118
1169
  duration: `${v.delayMs} millis`
1119
1170
  }));
@@ -1125,4 +1176,4 @@ var ae = (e) => e.webhook !== void 0, D = (e) => {
1125
1176
  });
1126
1177
  };
1127
1178
  //#endregion
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 };
1179
+ export { B as IdempotencyCache, q as RATE_WINDOW_MS, K as RATE_WINDOW_TABLE, Ne 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, ee as _voltroWebhookTargetsTable, Te as acquireRateSlots, E as asOutgoingEvent, Be as buildDeliverWebhookExecute, _e as buildWebhooksService, pe as compareVersions, X as consumeRateSlot, Se as defaultIncomingPath, c as defaultOutgoingSignature, A as defaultRetryPolicy, s as defineIncomingWebhook, t as defineWebhookProvider, ze as deliverWebhookWorkflow, Ee as encodePayload, N as eventsOf, ce as fastRetryPolicy, M as generateSecret, H as getIdempotencyCache, i as githubSignature, oe as hasWebhookAudience, r as isWebhookDescriptor, R as matchesFilter, xe as mountIncomingWebhook, k as nextRetry, D as parseDuration, W as parseTtl, $ as recordDeliveryOutcome, we as releaseRateSlot, F as resolveSubscribe, o as signPayload, n as slackSignature, e as stripeSignature, ye as useWebhooks, ve as useWebhooksEffect, P as validateSubscribe, a as verifySignature, g as webhookTables };