@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/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, _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";
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 ie } from "@voltro/logger";
10
- import { Activity as w, DurableClock as T, Workflow as ae } from "@effect/workflow";
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, E = (e) => {
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)?$/, D = (e) => {
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
- }, O = [
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
- ], k = (e, t, n) => {
43
- if (t >= e.maxAttempts || n?.status !== void 0 && !(e.retryOn ?? O).includes(n.status)) return null;
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 = D(e.maxDelay), i = Math.min(t, 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 = D(e.initialDelay), i = D(e.maxDelay), a;
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
- }, A = () => ({
70
+ }, le = () => ({
70
71
  strategy: "exponential",
71
72
  maxAttempts: 8,
72
73
  initialDelay: "5s",
73
74
  maxDelay: "1h",
74
- retryOn: O,
75
+ retryOn: A,
75
76
  honourRetryAfter: !0,
76
77
  jitter: "full"
77
- }), ce = () => ({
78
+ }), ue = () => ({
78
79
  strategy: "fixed",
79
80
  maxAttempts: 120,
80
81
  initialDelay: "30s",
81
82
  maxDelay: "30s",
82
- retryOn: O,
83
+ retryOn: A,
83
84
  honourRetryAfter: !0,
84
85
  jitter: "full"
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
+ }), 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
- C(e.url);
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 = N(e);
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 < ue) throw new p({
125
+ if (e.secret !== void 0 && e.secret.length < M) throw new p({
125
126
  field: "secret",
126
- reason: `secret must be at least ${ue} chars`
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
- }, F = (e, t, n, r) => ({
147
- id: v(),
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 ?? M(),
152
+ secret: r ?? e.secret ?? N(),
151
153
  signing: e.signing ?? t?.defaultSigning ?? c(),
152
- retry: e.retry ?? t?.defaultRetry ?? A(),
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
- }), 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) => {
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
- }, L = (e) => ({
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: 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) => {
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(me(n, t), r)) return !1;
191
+ for (let [t, r] of Object.entries(e)) if (!ge(he(n, t), r)) return !1;
190
192
  return !0;
191
- }, me = (e, t) => {
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
- }, he = (e, t) => {
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
- }, 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));
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
- }, _e = (e, t, n = {}) => {
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
- 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));
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" ? E(n) : void 0, s = typeof n == "string" ? r.get(n) : o ?? n, c = typeof n == "string" ? n : n.id ?? n.name;
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 = b.decodeUnknownEither(s.payload)(i, { errors: "all" });
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 = v(), d = a?.tenantId, f = (await e.store.query({
259
- table: "_voltro_webhook_targets",
260
- predicate: d == null ? S("event", c) : x(S("event", c), S("tenantId", d)),
261
- order: [{
262
- column: "createdAt",
263
- direction: "asc"
264
- }],
265
- take: 1e3,
266
- skip: void 0,
267
- projection: void 0
268
- })).filter((e) => R(e.filter, i)), p = [], m = JSON.stringify(i);
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: m
277
- }), p.push({
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: m,
317
+ payload: h,
291
318
  scheduledAt: /* @__PURE__ */ new Date(),
292
319
  createdAt: /* @__PURE__ */ new Date(),
293
320
  updatedAt: /* @__PURE__ */ new Date()
294
- }), p.push({
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: p
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: S("deliveryId", n),
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 ?? v(),
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.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);
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: x(S("targetId", r), S("status", "pending")),
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 ?? v(),
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: x(S("targetId", n), S("status", "pending")) });
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 = M();
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 && P({
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: S("id", n),
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 = v(), o = JSON.stringify(r ?? {
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: v(),
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 : S("targetId", t.targetId),
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 || de(e.createdAt) >= a).slice(0, t?.limit ?? 200).map(L);
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: S("id", t),
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
- ...L(n),
466
- payload: fe(n.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 : S("event", t),
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
- }, ve = y.gen(function* () {
526
+ }, be = b.gen(function* () {
507
527
  return yield* j;
508
- }), ye = (e) => {
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
- }, be = 1e4, B = class {
531
+ }, Se = 1e4, W = class {
512
532
  capacity;
513
533
  cache = /* @__PURE__ */ new Map();
514
- constructor(e = be) {
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
- }, V = null, H = () => (V === null && (V = new B()), V), U = {
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
- }, W = (e) => e === void 0 ? U["7d"] : U[e] ?? U["7d"], G = (e, t) => ({
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
- }), xe = (e, t) => {
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) => {
562
- ie({ scope: `webhook:${e.webhookId}` }).debug("incoming webhook", {
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 = G(405, {
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 = G(401, {
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 = G(400, {
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 = b.decodeUnknownSync(e.payload)(f);
655
+ p = x.decodeUnknownSync(e.payload)(f);
636
656
  } catch (t) {
637
- let n = G(422, {
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 = G(200, {
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 = G(409, {
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 = G(200, { received: !0 });
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 = G(500, {
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
- }, Se = (e) => `/webhooks/${e}`, K = "_voltro_webhook_rate_windows", q = 6e4, Ce = (e, t) => `${e}@${t}`, J = async (e, t, n) => (await e.query({
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: x(S("scope", t), S("bucket", n)),
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, Y = 32, X = async (e, t, n) => {
729
- for (let r = 0; r < Y; 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 J(e, t.key, n);
751
+ let i = await Oe(e, t.key, n);
732
752
  if (i === null) {
733
- await e.insertIgnore(K, {
734
- id: Ce(t.key, n),
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: x(S("id", i.id), S("count", i.count)) }) === 1) return !0;
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 ${Y} attempts`);
750
- }, we = async (e, t, n) => {
751
- for (let r = 0; r < Y; 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 J(e, t, n);
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: x(S("id", i.id), S("count", i.count)) }) === 1) return;
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 ${Y} attempts`);
760
- }, Te = async (e, t, n, r = q) => {
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 X(e, o, i)) {
787
+ if (await ke(e, o, i)) {
768
788
  a.push(o);
769
789
  continue;
770
790
  }
771
- for (let t of a) await we(e, t.key, i);
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
- }, Ee = (e, t) => {
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(De(n), "utf8")
817
+ bytes: Buffer.from(Ne(n), "utf8")
798
818
  } : {
799
819
  contentType: "application/xml",
800
- bytes: Buffer.from(ke(n), "utf8")
820
+ bytes: Buffer.from(Fe(n), "utf8")
801
821
  };
802
- }, De = (e) => {
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 ${Me(e)}`
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
- }, 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({
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}>${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) => {
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 "&amp;";
833
853
  case "<": return "&lt;";
@@ -835,17 +855,17 @@ var oe = (e) => e.webhook !== void 0, E = (e) => {
835
855
  case "\"": return "&quot;";
836
856
  default: return "&apos;";
837
857
  }
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({
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: S("id", t),
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, $ = async (e, t, n, r, i = /* @__PURE__ */ new Date()) => {
846
- for (let a = 0; a < Pe; 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 Fe(e, t);
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: x(S("id", t), S("consecutiveFailures", s)) }) === 1) return {
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: x(S("id", t), S("consecutiveFailures", s)) }) === 1) return {
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 ${Pe} attempts`);
874
- }, Ie = b.Struct({
875
- status: b.Number,
876
- body: b.String,
877
- latencyMs: b.Number,
878
- retryAfterSec: b.Union(b.Number, b.Undefined),
879
- transportError: b.Union(b.String, b.Undefined),
880
- encodeError: b.Union(b.String, b.Undefined)
881
- }), Le = b.Struct({
882
- outcome: b.Literal("succeeded", "failed", "retry"),
883
- delayMs: b.Union(b.Number, b.Undefined),
884
- reason: b.Union(b.String, b.Undefined),
885
- autoDisabled: b.Union(b.Boolean, b.Undefined),
886
- consecutiveFailures: b.Union(b.Number, b.Undefined)
887
- }), Re = b.Struct({
888
- acquired: b.Boolean,
889
- retryInMs: b.Number
890
- }), ze = ae.make({
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: b.String,
894
- targetId: b.String,
895
- event: b.String,
896
- eventId: b.String,
897
- payloadJson: b.String,
898
- attemptEpoch: b.optionalWith(b.Number, { default: () => 0 })
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: b.Struct({
901
- finalStatus: b.Literal("succeeded", "failed", "deferred"),
902
- attempts: b.Number
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
- }), Be = (e, t = {}) => (n, r) => {
906
- let { deliveryId: i, targetId: a, event: s, eventId: c, payloadJson: l } = n, u = ie({ scope: `webhook:${i}` });
907
- return y.gen(function* () {
908
- let n = yield* w.make({
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: b.Union(b.Null, b.Struct({
911
- id: b.String,
912
- event: b.String,
913
- url: b.String,
914
- secret: b.String,
915
- signing: b.Any,
916
- retry: b.Any,
917
- headers: b.Union(b.Record({
918
- key: b.String,
919
- value: b.String
920
- }), b.Null),
921
- rateLimitPerMinute: b.Union(b.Number, b.Null),
922
- active: b.Boolean,
923
- format: b.Literal("json", "form", "xml"),
924
- tenantId: b.Union(b.String, b.Null)
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: y.tryPromise({
946
+ execute: b.tryPromise({
927
947
  try: async () => (await e.store.query({
928
948
  table: "_voltro_webhook_targets",
929
- predicate: S("id", a),
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(y.orDie)
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* w.make({
988
+ if (n.active !== !0) return yield* E.make({
969
989
  name: "record-paused-pending",
970
- success: b.Void,
971
- execute: y.tryPromise({
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(y.orDie)
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* w.make({
1009
+ let i = yield* E.make({
990
1010
  name: `rate-acquire-${t}-${n}`,
991
- success: Re,
992
- execute: y.tryPromise({
993
- try: () => Te(e.store, _, Date.now(), ee),
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(y.orDie)
1020
+ }).pipe(b.orDie)
1001
1021
  });
1002
1022
  if (i.acquired) break;
1003
- yield* w.make({
1023
+ yield* E.make({
1004
1024
  name: `record-rate-deferral-${t}-${n}`,
1005
- success: b.Void,
1006
- execute: y.tryPromise({
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(y.orDie)
1013
- }), yield* T.sleep({
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* w.make({
1038
+ yield* E.make({
1019
1039
  name: `record-attempt-start-${t}`,
1020
- success: b.Void,
1021
- execute: y.tryPromise({
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(y.orDie)
1048
+ }).pipe(b.orDie)
1029
1049
  });
1030
- let g = yield* w.make({
1050
+ let g = yield* E.make({
1031
1051
  name: `sign-and-post-${t}`,
1032
- success: Ie,
1033
- execute: y.promise(async () => {
1052
+ success: Ue,
1053
+ execute: b.promise(async () => {
1034
1054
  let e = Date.now(), r;
1035
1055
  try {
1036
- r = Ee(n.format, l);
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" && C(n.url);
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* w.make({
1104
+ }), v = yield* E.make({
1085
1105
  name: `classify-and-record-${t}`,
1086
- success: Le,
1087
- execute: y.tryPromise({
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) => $(e.store, a, 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 : k(p, t, {
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(y.orDie)
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* T.sleep({
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 { 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 };
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 };