@voltro/plugin-webhooks 0.81.0 → 0.82.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,23 +1,29 @@
1
1
  import { C as e, E as t, S as n, T as r, _ as i, a, b as o, c as s, d as c, f as l, g as u, h as d, i as f, l as p, m, n as h, o as g, p as _, r as v, s as y, t as ee, u as te, v as ne, w as re, x as ie, y as ae } from "./signing-DeHObNv6.js";
2
- import { WebhookDeliveryNotFound as b, WebhookPayloadInvalid as oe, WebhookPayloadUnrepresentable as x, WebhookPayloadVersionInvalid as se, WebhookSubscribeInvalid as S, WebhookTargetNotFound as C } from "./errors.js";
3
- import { _voltroWebhookDeliveriesTable as ce, _voltroWebhookEventStatsTable as le, _voltroWebhookRateWindowsTable as ue, _voltroWebhookTargetsTable as de, webhookTables as fe } from "./mixin.js";
4
- import { Context as pe, Effect as w, Either as me, Schema as T } from "effect";
5
- import { definePlugin as he, publishServerError as ge } from "@voltro/protocol";
6
- import { randomBytes as _e, randomUUID as E } from "node:crypto";
7
- import { and as D, eq as O } from "@voltro/database";
8
- import { assertPublicUrl as ve } from "@voltro/integration-http";
9
- import { createLogger as k } from "@voltro/logger";
10
- import { Activity as A, DurableClock as j, Workflow as ye } from "@effect/workflow";
11
- import { defineOutboxHandler as be } from "@voltro/runtime";
2
+ import { WebhookDeliveryNotFound as oe, WebhookPayloadInvalid as se, WebhookPayloadUnrepresentable as b, WebhookPayloadVersionInvalid as ce, WebhookSubscribeInvalid as x, WebhookTargetNotFound as S } from "./errors.js";
3
+ import { _voltroWebhookDeliveriesTable as le, _voltroWebhookEventStatsTable as ue, _voltroWebhookRateWindowsTable as de, _voltroWebhookTargetsTable as fe, webhookTables as pe } from "./mixin.js";
4
+ import { Context as me, Effect as C, Either as he, Schema as w } from "effect";
5
+ import { definePlugin as ge, publishServerError as _e } from "@voltro/protocol";
6
+ import { randomBytes as ve, randomUUID as T } from "node:crypto";
7
+ import { and as E, eq as D } from "@voltro/database";
8
+ import { assertPublicUrl as ye } from "@voltro/integration-http";
9
+ import { createLogger as O } from "@voltro/logger";
10
+ import { Activity as k, DurableClock as A, Workflow as be } from "@effect/workflow";
11
+ import { defineOutboxHandler as xe } from "@voltro/runtime";
12
12
  //#region src/plugin.ts
13
- var M = ["network:outbound:*"], xe = (e = {}) => he({
13
+ var j = ["network:outbound:*"], M = [{
14
+ destination: "runtime:registered webhook targets (_voltro_webhook_targets)",
15
+ purpose: "event deliveries, HMAC-signed",
16
+ data: ["content", "metadata"],
17
+ disable: "remove or pause the targets; an emit then delivers nowhere"
18
+ }], Se = (e = {}) => ge({
14
19
  name: e.name ? `@voltro/plugin-webhooks#${e.name}` : "@voltro/plugin-webhooks",
15
20
  description: "Outgoing webhooks (multi-target, durable-workflow delivery, HMAC signing, retry policies, rate limits) + incoming (signature verification, idempotency, provider templates). Declares wildcard outbound network access because targets are user-configured URLs.",
16
- permissions: M,
17
- onActivate: (e) => w.sync(() => {
21
+ permissions: j,
22
+ egress: M,
23
+ onActivate: (e) => C.sync(() => {
18
24
  e.logger.info("webhooks active", { outbound: "network:outbound:* (subscriber-configured target URLs — audit visibility, not a restriction)" });
19
25
  })
20
- }), Se = (e) => e.webhook !== void 0, Ce = (e) => {
26
+ }), Ce = (e) => e.webhook !== void 0, we = (e) => {
21
27
  let t = e.webhook;
22
28
  if (t !== void 0) return {
23
29
  _tag: "outgoingEvent",
@@ -27,8 +33,8 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
27
33
  ...t.version === void 0 ? {} : { version: t.version },
28
34
  ...t.rateLimit === void 0 ? {} : { globalRateLimit: t.rateLimit }
29
35
  };
30
- }, we = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, N = (e) => {
31
- let t = we.exec(e);
36
+ }, Te = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, N = (e) => {
37
+ let t = Te.exec(e);
32
38
  if (!t) throw Error(`invalid duration literal: ${e}`);
33
39
  let n = Number(t[1]), r = t[2] ?? "s";
34
40
  switch (r) {
@@ -47,7 +53,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
47
53
  502,
48
54
  503,
49
55
  504
50
- ], Te = (e, t, n) => {
56
+ ], Ee = (e, t, n) => {
51
57
  if (t >= e.maxAttempts || n?.status !== void 0 && !(e.retryOn ?? P).includes(n.status)) return null;
52
58
  if (e.honourRetryAfter !== !1 && n?.retryAfterSeconds !== void 0) {
53
59
  let t = n.retryAfterSeconds * 1e3, r = N(e.maxDelay), i = Math.min(t, r);
@@ -72,7 +78,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
72
78
  delayMs: Math.max(1, Math.floor(o)),
73
79
  baseDelayMs: a
74
80
  };
75
- }, Ee = () => ({
81
+ }, De = () => ({
76
82
  strategy: "exponential",
77
83
  maxAttempts: 8,
78
84
  initialDelay: "5s",
@@ -80,7 +86,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
80
86
  retryOn: P,
81
87
  honourRetryAfter: !0,
82
88
  jitter: "full"
83
- }), De = () => ({
89
+ }), Oe = () => ({
84
90
  strategy: "fixed",
85
91
  maxAttempts: 120,
86
92
  initialDelay: "30s",
@@ -88,82 +94,82 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
88
94
  retryOn: P,
89
95
  honourRetryAfter: !0,
90
96
  jitter: "full"
91
- }), Oe = class extends pe.Tag("@voltro/webhooks/WebhooksService")() {}, ke = 32, F = 32, I = () => _e(ke).toString("hex"), L = (e) => e.events ?? (e.event === void 0 ? [] : [e.event]), R = (e, t) => {
92
- if (e !== void 0 && t?._tag === "standardWebhooks" && !ne(e)) throw new S({
97
+ }), ke = class extends me.Tag("@voltro/webhooks/WebhooksService")() {}, Ae = 32, je = 32, Me = () => ve(Ae).toString("hex"), F = (e) => e.events ?? (e.event === void 0 ? [] : [e.event]), I = (e, t) => {
98
+ if (e !== void 0 && t?._tag === "standardWebhooks" && !ne(e)) throw new x({
93
99
  field: "secret",
94
100
  reason: "a standard-webhooks target needs the spec's secret serialization (\"whsec_\" + base64 of 24..64 random bytes) — mint one with generateStandardWebhooksSecret(), or omit `secret` and let subscribe mint it. If the incompatible key is inherited, rotate the endpoint secret and update the receiver before adding the event"
95
101
  });
96
- }, z = (e) => {
102
+ }, L = (e) => {
97
103
  let t;
98
104
  try {
99
105
  t = new URL(e.url);
100
106
  } catch (e) {
101
- throw new S({
107
+ throw new x({
102
108
  field: "url",
103
109
  reason: `invalid subscriber url: ${e.message}`
104
110
  });
105
111
  }
106
- if (t.protocol !== "http:" && t.protocol !== "https:") throw new S({
112
+ if (t.protocol !== "http:" && t.protocol !== "https:") throw new x({
107
113
  field: "url",
108
114
  reason: `url protocol must be http or https, got ${t.protocol}`
109
115
  });
110
116
  if (process.env.NODE_ENV === "production") try {
111
- ve(e.url);
117
+ ye(e.url);
112
118
  } catch (e) {
113
- throw new S({
119
+ throw new x({
114
120
  field: "url",
115
121
  reason: e.message
116
122
  });
117
123
  }
118
- let n = L(e);
119
- if (n.length === 0) throw new S({
124
+ let n = F(e);
125
+ if (n.length === 0) throw new x({
120
126
  field: "event",
121
127
  reason: "event id required"
122
128
  });
123
- if (e.event !== void 0 && e.events !== void 0) throw new S({
129
+ if (e.event !== void 0 && e.events !== void 0) throw new x({
124
130
  field: "event",
125
131
  reason: "pass `event` OR `events`, not both — the row set would be ambiguous"
126
132
  });
127
- if (n.some((e) => e.length === 0)) throw new S({
133
+ if (n.some((e) => e.length === 0)) throw new x({
128
134
  field: "events",
129
135
  reason: "event id required"
130
136
  });
131
- if (new Set(n).size !== n.length) throw new S({
137
+ if (new Set(n).size !== n.length) throw new x({
132
138
  field: "events",
133
139
  reason: "duplicate event — one row per event, so a repeat would deliver twice"
134
140
  });
135
- if (e.secret !== void 0 && e.secret.length < F) throw new S({
141
+ if (e.secret !== void 0 && e.secret.length < je) throw new x({
136
142
  field: "secret",
137
- reason: `secret must be at least ${F} chars`
143
+ reason: `secret must be at least ${je} chars`
138
144
  });
139
- if (R(e.secret, e.signing), e.retry !== void 0) {
140
- if (e.retry.maxAttempts < 1) throw new S({
145
+ if (I(e.secret, e.signing), e.retry !== void 0) {
146
+ if (e.retry.maxAttempts < 1) throw new x({
141
147
  field: "retry",
142
148
  reason: "retry.maxAttempts must be ≥ 1"
143
149
  });
144
- if (e.retry.maxAttempts > 1e3) throw new S({
150
+ if (e.retry.maxAttempts > 1e3) throw new x({
145
151
  field: "retry",
146
152
  reason: "retry.maxAttempts must be ≤ 1000"
147
153
  });
148
154
  }
149
- if (e.rateLimitPerMinute !== void 0 && e.rateLimitPerMinute < 1) throw new S({
155
+ if (e.rateLimitPerMinute !== void 0 && e.rateLimitPerMinute < 1) throw new x({
150
156
  field: "rateLimitPerMinute",
151
157
  reason: "rateLimitPerMinute must be ≥ 1"
152
158
  });
153
- if (e.autoDisableAfter !== void 0 && (e.autoDisableAfter < 1 || !Number.isInteger(e.autoDisableAfter))) throw new S({
159
+ if (e.autoDisableAfter !== void 0 && (e.autoDisableAfter < 1 || !Number.isInteger(e.autoDisableAfter))) throw new x({
154
160
  field: "autoDisableAfter",
155
161
  reason: `autoDisableAfter must be a positive integer, got ${e.autoDisableAfter}`
156
162
  });
157
- }, B = (e, t, n, r) => {
158
- let a = e.signing ?? t?.defaultSigning ?? ee(), o = a._tag === "standardWebhooks" ? i : I, s = r ?? e.secret ?? o();
159
- return R(s, a), {
160
- id: E(),
163
+ }, R = (e, t, n, r) => {
164
+ let a = e.signing ?? t?.defaultSigning ?? ee(), o = a._tag === "standardWebhooks" ? i : Me, s = r ?? e.secret ?? o();
165
+ return I(s, a), {
166
+ id: T(),
161
167
  tenantId: e.tenantId ?? null,
162
168
  event: n ?? e.event ?? "",
163
169
  url: e.url,
164
170
  secret: s,
165
171
  signing: a,
166
- retry: e.retry ?? t?.defaultRetry ?? Ee(),
172
+ retry: e.retry ?? t?.defaultRetry ?? De(),
167
173
  filter: e.filter ?? null,
168
174
  scope: e.scope ?? null,
169
175
  headers: e.headers ?? null,
@@ -177,14 +183,14 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
177
183
  payloadVersion: e.payloadVersion ?? t?.version ?? 1,
178
184
  description: e.description ?? null
179
185
  };
180
- }, Ae = (e) => e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : 0, V = (e) => e instanceof Date ? e.toISOString() : typeof e == "string" ? e : null, je = (e) => {
186
+ }, Ne = (e) => e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : 0, z = (e) => e instanceof Date ? e.toISOString() : typeof e == "string" ? e : null, Pe = (e) => {
181
187
  if (typeof e != "string") return e;
182
188
  try {
183
189
  return JSON.parse(e);
184
190
  } catch {
185
191
  return e;
186
192
  }
187
- }, H = (e) => ({
193
+ }, B = (e) => ({
188
194
  id: String(e.id),
189
195
  deliveryId: String(e.deliveryId),
190
196
  targetId: String(e.targetId),
@@ -195,28 +201,28 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
195
201
  responseStatus: e.responseStatus ?? null,
196
202
  errorMessage: e.errorMessage ?? null,
197
203
  latencyMs: e.latencyMs ?? null,
198
- scheduledAt: V(e.scheduledAt),
199
- nextAttemptAt: V(e.nextAttemptAt),
200
- createdAt: V(e.createdAt)
201
- }), Me = (e, t) => e === t ? "current" : e < t ? "behind" : "ahead", Ne = (e, t) => {
204
+ scheduledAt: z(e.scheduledAt),
205
+ nextAttemptAt: z(e.nextAttemptAt),
206
+ createdAt: z(e.createdAt)
207
+ }), Fe = (e, t) => e === t ? "current" : e < t ? "behind" : "ahead", V = (e, t) => {
202
208
  if (e === null) return !0;
203
209
  let n = { payload: t };
204
- for (let [t, r] of Object.entries(e)) if (!Fe(Pe(n, t), r)) return !1;
210
+ for (let [t, r] of Object.entries(e)) if (!Le(Ie(n, t), r)) return !1;
205
211
  return !0;
206
- }, Pe = (e, t) => {
212
+ }, Ie = (e, t) => {
207
213
  let n = t.split("."), r = e;
208
214
  for (let e of n) {
209
215
  if (typeof r != "object" || !r) return;
210
216
  r = r[e];
211
217
  }
212
218
  return r;
213
- }, U = (e, t) => {
219
+ }, H = (e, t) => {
214
220
  if (typeof e == "number" && typeof t == "number") return e - t;
215
221
  if (typeof e == "string" && typeof t == "string") return e < t ? -1 : +(e > t);
216
- }, Fe = (e, t) => {
222
+ }, Le = (e, t) => {
217
223
  if (typeof t == "object" && t && !Array.isArray(t)) {
218
224
  let n = t;
219
- return "eq" in n && e !== n.eq || "in" in n && (!Array.isArray(n.in) || !n.in.includes(e)) || "gt" in n && !((U(e, n.gt) ?? 0) > 0) || "gte" in n && !((U(e, n.gte) ?? -1) >= 0) || "lt" in n && !((U(e, n.lt) ?? 0) < 0) || "lte" in n && !((U(e, n.lte) ?? 1) <= 0) ? !1 : [
225
+ return "eq" in n && e !== n.eq || "in" in n && (!Array.isArray(n.in) || !n.in.includes(e)) || "gt" in n && !((H(e, n.gt) ?? 0) > 0) || "gte" in n && !((H(e, n.gte) ?? -1) >= 0) || "lt" in n && !((H(e, n.lt) ?? 0) < 0) || "lte" in n && !((H(e, n.lte) ?? 1) <= 0) ? !1 : [
220
226
  "eq",
221
227
  "in",
222
228
  "gt",
@@ -226,9 +232,9 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
226
232
  ].some((e) => e in n);
227
233
  }
228
234
  return e === t;
229
- }, Ie = (e, t) => e !== null && Object.entries(t).every(([t, n]) => e[t] === n), W = 500, Le = 200, G = async (e, t) => {
235
+ }, U = (e, t) => e !== null && Object.entries(t).every(([t, n]) => e[t] === n), W = 500, Re = 200, G = async (e, t) => {
230
236
  let n = [];
231
- for (let r = 0; r < Le; r += 1) {
237
+ for (let r = 0; r < Re; r += 1) {
232
238
  let i = await e.store.query({
233
239
  table: "_voltro_webhook_targets",
234
240
  predicate: t,
@@ -242,18 +248,18 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
242
248
  });
243
249
  if (n.push(...i), i.length < W) return n;
244
250
  }
245
- throw Error(`webhooks: more than ${Le * W} 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.`);
246
- }, Re = async (e, t) => {
247
- let n = (await G(e)).filter((e) => Ie(e.scope, t));
251
+ throw Error(`webhooks: more than ${Re * W} 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.`);
252
+ }, ze = async (e, t) => {
253
+ let n = (await G(e)).filter((e) => U(e.scope, t));
248
254
  if (n.length === 0) return;
249
255
  let r = new Set(n.map((e) => e.secret));
250
256
  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.`);
251
257
  return [...r][0];
252
- }, ze = async (e, t) => {
258
+ }, Be = async (e, t) => {
253
259
  try {
254
260
  let n = await e.store.query({
255
261
  table: "_voltro_webhook_event_stats",
256
- predicate: O("event", t),
262
+ predicate: D("event", t),
257
263
  order: [],
258
264
  take: 1,
259
265
  skip: void 0,
@@ -275,11 +281,11 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
275
281
  updatedAt: r
276
282
  });
277
283
  } catch {}
278
- }, Be = (e, t, n = {}) => {
284
+ }, Ve = (e, t, n = {}) => {
279
285
  let r = new Map((n.events ?? []).map((e) => [e.id, e])), a = {
280
286
  subscribe: async (t) => {
281
- z(t);
282
- let n = L(t), a = t.secret === void 0 && t.scope !== void 0 ? await Re(e, t.scope) : void 0, o = t.secret ?? a ?? i(), s = n.map((e) => B(t, r.get(e), e, o));
287
+ L(t);
288
+ let n = F(t), a = t.secret === void 0 && t.scope !== void 0 ? await ze(e, t.scope) : void 0, o = t.secret ?? a ?? i(), s = n.map((e) => R(t, r.get(e), e, o));
283
289
  for (let t of s) await e.store.insert("_voltro_webhook_targets", {
284
290
  ...t,
285
291
  createdAt: /* @__PURE__ */ new Date(),
@@ -300,19 +306,19 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
300
306
  };
301
307
  },
302
308
  emit: async (n, i, a) => {
303
- let o = typeof n == "object" && n.kind === "event" ? Ce(n) : void 0, s = typeof n == "string" ? r.get(n) : o ?? n, c = typeof n == "string" ? n : n.id ?? n.name;
309
+ let o = typeof n == "object" && n.kind === "event" ? we(n) : void 0, s = typeof n == "string" ? r.get(n) : o ?? n, c = typeof n == "string" ? n : n.id ?? n.name;
304
310
  if (s !== void 0) {
305
- let e = T.decodeUnknownEither(s.payload)(i, { errors: "all" });
306
- if (me.isLeft(e)) throw new oe({
311
+ let e = w.decodeUnknownEither(s.payload)(i, { errors: "all" });
312
+ if (he.isLeft(e)) throw new se({
307
313
  event: c,
308
314
  issues: e.left.message
309
315
  });
310
316
  }
311
- let l = E(), u = a?.tenantId, d = await G(e, u == null ? O("event", c) : D(O("event", c), O("tenantId", u)));
312
- await ze(e, c);
313
- let f = d.filter((e) => Ne(e.filter, i)), p = [], m = JSON.stringify(i);
317
+ let l = T(), u = a?.tenantId, d = await G(e, u == null ? D("event", c) : E(D("event", c), D("tenantId", u)));
318
+ await Be(e, c);
319
+ let f = d.filter((e) => V(e.filter, i)), p = [], m = JSON.stringify(i);
314
320
  for (let n of f) {
315
- let r = E();
321
+ let r = T();
316
322
  n.active ? (await t({
317
323
  deliveryId: r,
318
324
  targetId: n.id,
@@ -350,7 +356,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
350
356
  replay: async (n) => {
351
357
  let r = (await e.store.query({
352
358
  table: "_voltro_webhook_deliveries",
353
- predicate: O("deliveryId", n),
359
+ predicate: D("deliveryId", n),
354
360
  order: [{
355
361
  column: "attempt",
356
362
  direction: "asc"
@@ -359,12 +365,12 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
359
365
  skip: void 0,
360
366
  projection: void 0
361
367
  }))[0];
362
- if (r === void 0) throw new b({ deliveryId: n });
368
+ if (r === void 0) throw new oe({ deliveryId: n });
363
369
  await t({
364
370
  deliveryId: r.deliveryId,
365
371
  targetId: r.targetId,
366
372
  event: r.event,
367
- eventId: r.eventId ?? E(),
373
+ eventId: r.eventId ?? T(),
368
374
  payloadJson: typeof r.payload == "string" ? r.payload : JSON.stringify(r.payload),
369
375
  attemptEpoch: Date.now()
370
376
  });
@@ -373,15 +379,15 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
373
379
  if (typeof t == "string") {
374
380
  if ((await e.store.query({
375
381
  table: "_voltro_webhook_targets",
376
- predicate: O("id", t),
382
+ predicate: D("id", t),
377
383
  order: [],
378
384
  take: 1,
379
385
  skip: void 0,
380
386
  projection: ["id"]
381
- })).length === 0) throw new C({ targetId: t });
387
+ })).length === 0) throw new S({ targetId: t });
382
388
  return [t];
383
389
  }
384
- let n = (await G(e)).filter((e) => Ie(e.scope, t.scope)).map((e) => e.id);
390
+ let n = (await G(e)).filter((e) => U(e.scope, t.scope)).map((e) => e.id);
385
391
  if (n.length === 0) throw Error(`webhooks: no target matches scope ${JSON.stringify(t.scope)} — the operation would have silently done nothing`);
386
392
  return n;
387
393
  },
@@ -402,7 +408,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
402
408
  });
403
409
  let n = await e.store.query({
404
410
  table: "_voltro_webhook_deliveries",
405
- predicate: D(O("targetId", r), O("status", "pending")),
411
+ predicate: E(D("targetId", r), D("status", "pending")),
406
412
  order: [{
407
413
  column: "createdAt",
408
414
  direction: "asc"
@@ -415,14 +421,14 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
415
421
  deliveryId: e.deliveryId,
416
422
  targetId: r,
417
423
  event: e.event,
418
- eventId: e.eventId ?? E(),
424
+ eventId: e.eventId ?? T(),
419
425
  payloadJson: typeof e.payload == "string" ? e.payload : JSON.stringify(e.payload),
420
426
  attemptEpoch: i
421
427
  });
422
428
  }
423
429
  },
424
430
  deleteTarget: async (t) => {
425
- for (let n of await a.resolveTargets(t)) await e.store.delete("_voltro_webhook_targets", n), await e.store.deleteMany("_voltro_webhook_deliveries", { where: D(O("targetId", n), O("status", "pending")) });
431
+ for (let n of await a.resolveTargets(t)) await e.store.delete("_voltro_webhook_targets", n), await e.store.deleteMany("_voltro_webhook_deliveries", { where: E(D("targetId", n), D("status", "pending")) });
426
432
  },
427
433
  rotateSecret: async (t) => {
428
434
  let n = i();
@@ -433,7 +439,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
433
439
  return { secret: n };
434
440
  },
435
441
  updateTarget: async (t, n) => {
436
- if (n.url !== void 0 && z({
442
+ if (n.url !== void 0 && L({
437
443
  event: "x",
438
444
  url: n.url
439
445
  }), 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");
@@ -453,14 +459,14 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
453
459
  testTarget: async (n, r) => {
454
460
  let i = (await e.store.query({
455
461
  table: "_voltro_webhook_targets",
456
- predicate: O("id", n),
462
+ predicate: D("id", n),
457
463
  order: [],
458
464
  take: 1,
459
465
  skip: void 0,
460
466
  projection: void 0
461
467
  }))[0];
462
- if (i === void 0) throw new C({ targetId: n });
463
- let a = E(), o = JSON.stringify(r ?? {
468
+ if (i === void 0) throw new S({ targetId: n });
469
+ let a = T(), o = JSON.stringify(r ?? {
464
470
  test: !0,
465
471
  targetId: n,
466
472
  at: (/* @__PURE__ */ new Date()).toISOString()
@@ -469,7 +475,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
469
475
  deliveryId: a,
470
476
  targetId: i.id,
471
477
  event: i.event,
472
- eventId: E(),
478
+ eventId: T(),
473
479
  payloadJson: o
474
480
  }) : await e.store.insert("_voltro_webhook_deliveries", {
475
481
  id: `${a}:1`,
@@ -489,7 +495,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
489
495
  listDeliveries: async (t) => {
490
496
  let n = t?.scope === void 0 ? void 0 : new Set(await a.resolveTargets({ scope: t.scope })), r = await e.store.query({
491
497
  table: "_voltro_webhook_deliveries",
492
- predicate: t?.targetId === void 0 ? void 0 : O("targetId", t.targetId),
498
+ predicate: t?.targetId === void 0 ? void 0 : D("targetId", t.targetId),
493
499
  order: [{
494
500
  column: "createdAt",
495
501
  direction: "desc"
@@ -498,25 +504,25 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
498
504
  skip: void 0,
499
505
  projection: void 0
500
506
  }), i = t?.since?.getTime();
501
- return r.filter((e) => n === void 0 || n.has(String(e.targetId))).filter((e) => t?.status === void 0 || e.status === t.status).filter((e) => i === void 0 || Ae(e.createdAt) >= i).slice(0, t?.limit ?? 200).map(H);
507
+ return r.filter((e) => n === void 0 || n.has(String(e.targetId))).filter((e) => t?.status === void 0 || e.status === t.status).filter((e) => i === void 0 || Ne(e.createdAt) >= i).slice(0, t?.limit ?? 200).map(B);
502
508
  },
503
509
  getDelivery: async (t) => {
504
510
  let n = (await e.store.query({
505
511
  table: "_voltro_webhook_deliveries",
506
- predicate: O("id", t),
512
+ predicate: D("id", t),
507
513
  order: [],
508
514
  take: 1,
509
515
  skip: void 0,
510
516
  projection: void 0
511
517
  }))[0];
512
518
  return n === void 0 ? null : {
513
- ...H(n),
514
- payload: je(n.payload),
519
+ ...B(n),
520
+ payload: Pe(n.payload),
515
521
  responseBody: n.responseBody ?? null
516
522
  };
517
523
  },
518
524
  updateTargetPayloadVersion: async (t, n) => {
519
- if (n < 1 || !Number.isInteger(n)) throw new se({ version: n });
525
+ if (n < 1 || !Number.isInteger(n)) throw new ce({ version: n });
520
526
  await a.resolveTargets(t), await e.store.update("_voltro_webhook_targets", t, {
521
527
  payloadVersion: n,
522
528
  updatedAt: /* @__PURE__ */ new Date()
@@ -525,7 +531,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
525
531
  listTargets: async (t, n) => {
526
532
  let r = await e.store.query({
527
533
  table: "_voltro_webhook_targets",
528
- predicate: t === void 0 ? void 0 : O("event", t),
534
+ predicate: t === void 0 ? void 0 : D("event", t),
529
535
  order: [{
530
536
  column: "createdAt",
531
537
  direction: "desc"
@@ -546,20 +552,21 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
546
552
  consecutiveFailures: e.consecutiveFailures ?? 0,
547
553
  autoDisabledAt: e.autoDisabledAt === null || e.autoDisabledAt === void 0 ? null : e.autoDisabledAt instanceof Date ? e.autoDisabledAt.toISOString() : String(e.autoDisabledAt),
548
554
  autoDisableReason: e.autoDisableReason,
549
- scope: e.scope
555
+ scope: e.scope,
556
+ filter: e.filter ?? null
550
557
  }));
551
558
  }
552
559
  };
553
560
  return a;
554
- }, Ve = w.gen(function* () {
555
- return yield* Oe;
556
- }), He = (e) => {
561
+ }, He = C.gen(function* () {
562
+ return yield* ke;
563
+ }), Ue = (e) => {
557
564
  if (e.webhooks === void 0) throw Error("useWebhooks: `ctx.webhooks` is not set. Declare an outgoing event with a `webhook:` block in a *.event.ts file, or an incoming endpoint in a *.webhook.tsx file — the service is wired the moment the app has either.");
558
565
  return e.webhooks;
559
- }, Ue = 1e4, We = class {
566
+ }, We = 1e4, Ge = class {
560
567
  capacity;
561
568
  cache = /* @__PURE__ */ new Map();
562
- constructor(e = Ue) {
569
+ constructor(e = We) {
563
570
  this.capacity = e;
564
571
  }
565
572
  evictExpired() {
@@ -594,36 +601,36 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
594
601
  clear() {
595
602
  this.cache.clear();
596
603
  }
597
- }, K = null, Ge = () => (K === null && (K = new We()), K), q = {
604
+ }, K = null, Ke = () => (K === null && (K = new Ge()), K), q = {
598
605
  "5m": 3e5,
599
606
  "1h": 36e5,
600
607
  "6h": 216e5,
601
608
  "1d": 864e5,
602
609
  "7d": 6048e5,
603
610
  "30d": 2592e6
604
- }, Ke = (e) => e === void 0 ? q["7d"] : q[e] ?? q["7d"], J = (e, t) => ({
611
+ }, qe = (e) => e === void 0 ? q["7d"] : q[e] ?? q["7d"], J = (e, t) => ({
605
612
  status: e,
606
613
  contentType: "application/json; charset=utf-8",
607
614
  body: JSON.stringify(t)
608
- }), qe = "voltroWebhookVerification", Je = class extends Error {
615
+ }), Je = "voltroWebhookVerification", Ye = class extends Error {
609
616
  webhookId;
610
617
  name = "UnverifiedIncomingWebhook";
611
618
  constructor(e) {
612
619
  super(`incoming webhook '${e}' declares no verification. It would be mounted as a public, unauthenticated POST that runs your application code, so the framework refuses to mount it. Pick one on the descriptor:\n · provider: stripeWebhookProvider() — a preset (HMAC scheme + idempotency + body type)\n · signature: { _tag: 'hmac', algorithm: 'hmacSha256', header: 'X-Signature', encoding: 'hex', includeTimestamp: true }\n · verification: 'provider' — the handler verifies with the provider's own SDK\n · verification: 'none' — deliberately public; a gateway / IP allow-list owns it\nA signature-verified webhook reads its shared secret from VOLTRO_WEBHOOK_SECRET_${e.toUpperCase().replace(/[^A-Z0-9]/g, "_")}. The framework never invents that value — the sender holds the other half of it.`), this.webhookId = e;
613
620
  }
614
- }, Ye = (e) => `VOLTRO_WEBHOOK_SECRET_${e.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`, Xe = (e) => {
621
+ }, Xe = (e) => `VOLTRO_WEBHOOK_SECRET_${e.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`, Ze = (e) => {
615
622
  let t = e.signature ?? e.provider?.signature ?? null;
616
623
  return (e.verification === void 0 || e.verification === "signature") && t === null ? null : e.verification ?? "signature";
617
- }, Ze = (e, t) => {
618
- let n = e.provider, r = e.signature ?? n?.signature ?? null, i = e.idempotency ?? n?.idempotency ?? null, a = e.bodyType ?? n?.bodyType ?? "json", o = Xe(e);
619
- if (o === null) throw new Je(e.id);
624
+ }, Qe = (e, t) => {
625
+ let n = e.provider, r = e.signature ?? n?.signature ?? null, i = e.idempotency ?? n?.idempotency ?? null, a = e.bodyType ?? n?.bodyType ?? "json", o = Ze(e);
626
+ if (o === null) throw new Ye(e.id);
620
627
  let c = o;
621
- c === "none" && k({ scope: `webhook:${e.id}` }).warn("incoming webhook mounted WITHOUT verification — anyone who can reach this URL can run its handler", {
628
+ c === "none" && O({ scope: `webhook:${e.id}` }).warn("incoming webhook mounted WITHOUT verification — anyone who can reach this URL can run its handler", {
622
629
  webhookId: e.id,
623
630
  verification: "none"
624
631
  });
625
- let l = t.idempotencyCache ?? Ge(), u = Ke(i?.ttl), d = t.log ?? ((e) => {
626
- k({ scope: `webhook:${e.webhookId}` }).debug("incoming webhook", {
632
+ let l = t.idempotencyCache ?? Ke(), u = qe(i?.ttl), d = t.log ?? ((e) => {
633
+ O({ scope: `webhook:${e.webhookId}` }).debug("incoming webhook", {
627
634
  status: e.status,
628
635
  signatureOk: e.signatureOk,
629
636
  idempotency: e.idempotency,
@@ -650,7 +657,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
650
657
  if (r !== null && c === "signature") {
651
658
  let i = await t.resolveSecret(e.id);
652
659
  if (i === null) {
653
- let t = `no signing secret configured — set ${Ye(e.id)}`, n = J(503, {
660
+ let t = `no signing secret configured — set ${Xe(e.id)}`, n = J(503, {
654
661
  error: "webhook not configured",
655
662
  reason: t
656
663
  });
@@ -711,7 +718,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
711
718
  }
712
719
  let m;
713
720
  try {
714
- m = T.decodeUnknownSync(e.payload)(p);
721
+ m = w.decodeUnknownSync(e.payload)(p);
715
722
  } catch (t) {
716
723
  let n = J(422, {
717
724
  error: "schema validation failed",
@@ -789,28 +796,28 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
789
796
  idempotency: g,
790
797
  durationMs: Date.now() - o,
791
798
  errorMessage: t.message
792
- }), ge({
799
+ }), _e({
793
800
  error: t,
794
801
  source: "webhook",
795
802
  name: e.id,
796
803
  fields: { status: 500 }
797
804
  }), n;
798
805
  }
799
- }, { [qe]: c });
800
- }, Qe = (e) => `/webhooks/${e}`, $e = "_voltro_webhook_rate_windows", et = 6e4, tt = (e, t) => `${e}@${t}`, nt = async (e, t, n) => (await e.query({
806
+ }, { [Je]: c });
807
+ }, $e = (e) => `/webhooks/${e}`, et = "_voltro_webhook_rate_windows", tt = 6e4, nt = (e, t) => `${e}@${t}`, rt = async (e, t, n) => (await e.query({
801
808
  table: "_voltro_webhook_rate_windows",
802
- predicate: D(O("scope", t), O("bucket", n)),
809
+ predicate: E(D("scope", t), D("bucket", n)),
803
810
  order: [],
804
811
  take: 1,
805
812
  skip: void 0,
806
813
  projection: void 0
807
- }))[0] ?? null, Y = 32, rt = async (e, t, n) => {
814
+ }))[0] ?? null, Y = 32, X = async (e, t, n) => {
808
815
  for (let r = 0; r < Y; r++) {
809
816
  r > 0 && await new Promise((e) => setTimeout(e, r));
810
- let i = await nt(e, t.key, n);
817
+ let i = await rt(e, t.key, n);
811
818
  if (i === null) {
812
- await e.insertIgnore($e, {
813
- id: tt(t.key, n),
819
+ await e.insertIgnore(et, {
820
+ id: nt(t.key, n),
814
821
  scope: t.key,
815
822
  bucket: n,
816
823
  count: 0,
@@ -823,27 +830,27 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
823
830
  if (await e.updateMany("_voltro_webhook_rate_windows", {
824
831
  count: i.count + 1,
825
832
  updatedAt: /* @__PURE__ */ new Date()
826
- }, { where: D(O("id", i.id), O("count", i.count)) }) === 1) return !0;
833
+ }, { where: E(D("id", i.id), D("count", i.count)) }) === 1) return !0;
827
834
  }
828
835
  throw Error(`webhook rate window: contention on (${t.key}, ${n}) exceeded ${Y} attempts`);
829
836
  }, it = async (e, t, n) => {
830
837
  for (let r = 0; r < Y; r++) {
831
838
  r > 0 && await new Promise((e) => setTimeout(e, r));
832
- let i = await nt(e, t, n);
839
+ let i = await rt(e, t, n);
833
840
  if (i === null || i.count <= 0 || await e.updateMany("_voltro_webhook_rate_windows", {
834
841
  count: i.count - 1,
835
842
  updatedAt: /* @__PURE__ */ new Date()
836
- }, { where: D(O("id", i.id), O("count", i.count)) }) === 1) return;
843
+ }, { where: E(D("id", i.id), D("count", i.count)) }) === 1) return;
837
844
  }
838
845
  throw Error(`webhook rate window: release contention on (${t}) exceeded ${Y} attempts`);
839
- }, at = async (e, t, n, r = et) => {
846
+ }, at = async (e, t, n, r = tt) => {
840
847
  if (t.length === 0) return {
841
848
  acquired: !0,
842
849
  retryInMs: 0
843
850
  };
844
851
  let i = Math.floor(n / r), a = [];
845
852
  for (let o of t) {
846
- if (await rt(e, o, i)) {
853
+ if (await X(e, o, i)) {
847
854
  a.push(o);
848
855
  continue;
849
856
  }
@@ -866,7 +873,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
866
873
  try {
867
874
  n = JSON.parse(t);
868
875
  } catch (t) {
869
- throw new x({
876
+ throw new b({
870
877
  format: e,
871
878
  reason: `payload is not valid JSON: ${t.message}`
872
879
  });
@@ -879,34 +886,34 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
879
886
  bytes: Buffer.from(lt(n), "utf8")
880
887
  };
881
888
  }, st = (e) => {
882
- if (typeof e != "object" || !e || Array.isArray(e)) throw new x({
889
+ if (typeof e != "object" || !e || Array.isArray(e)) throw new b({
883
890
  format: "form",
884
891
  reason: `form encoding requires a JSON object at the top level, got ${ft(e)}`
885
892
  });
886
893
  let t = new URLSearchParams();
887
- for (let [n, r] of Object.entries(e)) X(t, n, r);
894
+ for (let [n, r] of Object.entries(e)) Z(t, n, r);
888
895
  return t.toString();
889
- }, X = (e, t, n) => {
896
+ }, Z = (e, t, n) => {
890
897
  if (n === null) {
891
898
  e.append(t, "");
892
899
  return;
893
900
  }
894
901
  if (Array.isArray(n)) {
895
- n.forEach((n, r) => X(e, `${t}[${r}]`, n));
902
+ n.forEach((n, r) => Z(e, `${t}[${r}]`, n));
896
903
  return;
897
904
  }
898
905
  if (typeof n == "object") {
899
- for (let [r, i] of Object.entries(n)) X(e, `${t}[${r}]`, i);
906
+ for (let [r, i] of Object.entries(n)) Z(e, `${t}[${r}]`, i);
900
907
  return;
901
908
  }
902
909
  e.append(t, String(n));
903
- }, ct = /^[A-Za-z_][A-Za-z0-9_.-]*$/, lt = (e) => `<?xml version="1.0" encoding="UTF-8"?>${Z("webhook", e)}`, Z = (e, t) => {
904
- if (!ct.test(e)) throw new x({
910
+ }, ct = /^[A-Za-z_][A-Za-z0-9_.-]*$/, lt = (e) => `<?xml version="1.0" encoding="UTF-8"?>${Q("webhook", e)}`, Q = (e, t) => {
911
+ if (!ct.test(e)) throw new b({
905
912
  format: "xml",
906
913
  reason: `object key ${JSON.stringify(e)} is not a valid XML element name`
907
914
  });
908
915
  return `<${e}>${ut(e, t)}</${e}>`;
909
- }, ut = (e, t) => t == null ? "" : Array.isArray(t) ? t.map((e) => Z("item", e)).join("") : typeof t == "object" ? Object.entries(t).map(([e, t]) => Z(e, t)).join("") : dt(String(t)), dt = (e) => e.replace(/[&<>"']/g, (e) => {
916
+ }, ut = (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("") : dt(String(t)), dt = (e) => e.replace(/[&<>"']/g, (e) => {
910
917
  switch (e) {
911
918
  case "&": return "&amp;";
912
919
  case "<": return "&lt;";
@@ -916,12 +923,12 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
916
923
  }
917
924
  }), ft = (e) => e === null ? "null" : Array.isArray(e) ? "array" : typeof e, pt = "_voltro_webhook_targets", mt = 32, ht = async (e, t) => (await e.query({
918
925
  table: "_voltro_webhook_targets",
919
- predicate: O("id", t),
926
+ predicate: D("id", t),
920
927
  order: [],
921
928
  take: 1,
922
929
  skip: void 0,
923
930
  projection: void 0
924
- }))[0] ?? null, Q = async (e, t, n, r, i = /* @__PURE__ */ new Date(), a = !1) => {
931
+ }))[0] ?? null, gt = async (e, t, n, r, i = /* @__PURE__ */ new Date(), a = !1) => {
925
932
  for (let o = 0; o < mt; o++) {
926
933
  o > 0 && await new Promise((e) => setTimeout(e, o));
927
934
  let s = await ht(e, t);
@@ -934,7 +941,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
934
941
  if (c === 0 || await e.updateMany("_voltro_webhook_targets", {
935
942
  consecutiveFailures: 0,
936
943
  updatedAt: i
937
- }, { where: D(O("id", t), O("consecutiveFailures", c)) }) === 1) return {
944
+ }, { where: E(D("id", t), D("consecutiveFailures", c)) }) === 1) return {
938
945
  consecutiveFailures: 0,
939
946
  autoDisabled: !1
940
947
  };
@@ -944,72 +951,72 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
944
951
  consecutiveFailures: l,
945
952
  updatedAt: i
946
953
  };
947
- if (d && (f.active = !1, f.autoDisabledAt = i, f.autoDisableReason = r.slice(0, 500)), await e.updateMany("_voltro_webhook_targets", f, { where: D(O("id", t), O("consecutiveFailures", c)) }) === 1) return {
954
+ if (d && (f.active = !1, f.autoDisabledAt = i, f.autoDisableReason = r.slice(0, 500)), await e.updateMany("_voltro_webhook_targets", f, { where: E(D("id", t), D("consecutiveFailures", c)) }) === 1) return {
948
955
  consecutiveFailures: l,
949
956
  autoDisabled: d
950
957
  };
951
958
  }
952
959
  throw Error(`webhook auto-disable: streak CAS contention on target ${t} exceeded ${mt} attempts`);
953
- }, gt = T.Struct({
954
- status: T.Number,
955
- body: T.String,
956
- latencyMs: T.Number,
957
- retryAfterSec: T.Union(T.Number, T.Undefined),
958
- transportError: T.Union(T.String, T.Undefined),
959
- encodeError: T.Union(T.String, T.Undefined)
960
- }), _t = T.Struct({
961
- outcome: T.Literal("succeeded", "failed", "retry"),
962
- delayMs: T.Union(T.Number, T.Undefined),
963
- reason: T.Union(T.String, T.Undefined),
964
- autoDisabled: T.Union(T.Boolean, T.Undefined),
965
- consecutiveFailures: T.Union(T.Number, T.Undefined)
966
- }), vt = T.Struct({
967
- acquired: T.Boolean,
968
- retryInMs: T.Number
969
- }), yt = {
970
- deliveryId: T.String,
971
- targetId: T.String,
972
- event: T.String,
973
- eventId: T.String,
974
- payloadJson: T.String,
975
- attemptEpoch: T.optionalWith(T.Number, { default: () => 0 })
976
- }, bt = ye.make({
960
+ }, _t = w.Struct({
961
+ status: w.Number,
962
+ body: w.String,
963
+ latencyMs: w.Number,
964
+ retryAfterSec: w.Union(w.Number, w.Undefined),
965
+ transportError: w.Union(w.String, w.Undefined),
966
+ encodeError: w.Union(w.String, w.Undefined)
967
+ }), vt = w.Struct({
968
+ outcome: w.Literal("succeeded", "failed", "retry"),
969
+ delayMs: w.Union(w.Number, w.Undefined),
970
+ reason: w.Union(w.String, w.Undefined),
971
+ autoDisabled: w.Union(w.Boolean, w.Undefined),
972
+ consecutiveFailures: w.Union(w.Number, w.Undefined)
973
+ }), yt = w.Struct({
974
+ acquired: w.Boolean,
975
+ retryInMs: w.Number
976
+ }), bt = {
977
+ deliveryId: w.String,
978
+ targetId: w.String,
979
+ event: w.String,
980
+ eventId: w.String,
981
+ payloadJson: w.String,
982
+ attemptEpoch: w.optionalWith(w.Number, { default: () => 0 })
983
+ }, xt = be.make({
977
984
  name: "voltro.deliverWebhook",
978
- payload: yt,
979
- success: T.Struct({
980
- finalStatus: T.Literal("succeeded", "failed", "deferred"),
981
- attempts: T.Number
985
+ payload: bt,
986
+ success: w.Struct({
987
+ finalStatus: w.Literal("succeeded", "failed", "deferred"),
988
+ attempts: w.Number
982
989
  }),
983
990
  idempotencyKey: ({ deliveryId: e, attemptEpoch: t }) => `voltro.deliverWebhook:${e}:${t ?? 0}`
984
- }), xt = 3e4, St = (e, t) => {
991
+ }), St = 3e4, Ct = (e, t) => {
985
992
  if (e !== void 0) return e;
986
993
  let n = Number(t.VOLTRO_WEBHOOK_TIMEOUT_MS);
987
- return Number.isFinite(n) && n > 0 ? n : xt;
988
- }, Ct = (e, t = {}) => (n, r) => {
989
- let { deliveryId: i, targetId: a, event: o, eventId: s, payloadJson: c } = n, l = k({ scope: `webhook:${i}` });
990
- return w.gen(function* () {
991
- let n = yield* A.make({
994
+ return Number.isFinite(n) && n > 0 ? n : St;
995
+ }, wt = (e, t = {}) => (n, r) => {
996
+ let { deliveryId: i, targetId: a, event: o, eventId: s, payloadJson: c } = n, l = O({ scope: `webhook:${i}` });
997
+ return C.gen(function* () {
998
+ let n = yield* k.make({
992
999
  name: "fetch-target",
993
- success: T.Union(T.Null, T.Struct({
994
- id: T.String,
995
- event: T.String,
996
- url: T.String,
997
- secret: T.String,
998
- signing: T.Any,
999
- retry: T.Any,
1000
- headers: T.Union(T.Record({
1001
- key: T.String,
1002
- value: T.String
1003
- }), T.Null),
1004
- rateLimitPerMinute: T.Union(T.Number, T.Null),
1005
- active: T.Boolean,
1006
- format: T.Literal("json", "form", "xml"),
1007
- tenantId: T.Union(T.String, T.Null)
1000
+ success: w.Union(w.Null, w.Struct({
1001
+ id: w.String,
1002
+ event: w.String,
1003
+ url: w.String,
1004
+ secret: w.String,
1005
+ signing: w.Any,
1006
+ retry: w.Any,
1007
+ headers: w.Union(w.Record({
1008
+ key: w.String,
1009
+ value: w.String
1010
+ }), w.Null),
1011
+ rateLimitPerMinute: w.Union(w.Number, w.Null),
1012
+ active: w.Boolean,
1013
+ format: w.Literal("json", "form", "xml"),
1014
+ tenantId: w.Union(w.String, w.Null)
1008
1015
  })),
1009
- execute: w.tryPromise({
1016
+ execute: C.tryPromise({
1010
1017
  try: async () => (await e.store.query({
1011
1018
  table: "_voltro_webhook_targets",
1012
- predicate: O("id", a),
1019
+ predicate: D("id", a),
1013
1020
  order: [{
1014
1021
  column: "createdAt",
1015
1022
  direction: "asc"
@@ -1023,7 +1030,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
1023
1030
  event: o,
1024
1031
  cause: String(e)
1025
1032
  }), /* @__PURE__ */ Error(`fetch-target failed: ${e.message}`))
1026
- }).pipe(w.orDie)
1033
+ }).pipe(C.orDie)
1027
1034
  });
1028
1035
  if (n === null) return {
1029
1036
  finalStatus: "failed",
@@ -1048,18 +1055,18 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
1048
1055
  ...l
1049
1056
  });
1050
1057
  };
1051
- if (n.active !== !0) return yield* A.make({
1058
+ if (n.active !== !0) return yield* k.make({
1052
1059
  name: "record-paused-pending",
1053
- success: T.Void,
1054
- execute: w.tryPromise({
1060
+ success: w.Void,
1061
+ execute: C.tryPromise({
1055
1062
  try: () => r(`${i}:1`, 1, { status: "pending" }),
1056
1063
  catch: (e) => /* @__PURE__ */ Error(`record-paused-pending failed: ${e.message}`)
1057
- }).pipe(w.orDie)
1064
+ }).pipe(C.orDie)
1058
1065
  }), {
1059
1066
  finalStatus: "deferred",
1060
1067
  attempts: 0
1061
1068
  };
1062
- let u = (e) => typeof e == "string" ? JSON.parse(e) : e, d = u(n.retry), p = u(n.signing), m = n.headers === null ? null : u(n.headers), h = t.rateWindowMs ?? 6e4, g = St(t.timeoutMs, process.env), _ = t.events?.find((e) => e.id === o)?.globalRateLimit, v = [...typeof n.rateLimitPerMinute == "number" && n.rateLimitPerMinute > 0 ? [{
1069
+ let u = (e) => typeof e == "string" ? JSON.parse(e) : e, d = u(n.retry), p = u(n.signing), m = n.headers === null ? null : u(n.headers), h = t.rateWindowMs ?? 6e4, g = Ct(t.timeoutMs, process.env), _ = t.events?.find((e) => e.id === o)?.globalRateLimit, v = [...typeof n.rateLimitPerMinute == "number" && n.rateLimitPerMinute > 0 ? [{
1063
1070
  key: `target:${n.id}`,
1064
1071
  limit: n.rateLimitPerMinute
1065
1072
  }] : [], ..._ !== void 0 && _.perMinute > 0 ? [{
@@ -1069,10 +1076,10 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
1069
1076
  for (let t = 1; t <= d.maxAttempts; t++) {
1070
1077
  let u = `${i}:${t}`;
1071
1078
  if (v.length > 0) for (let n = 0;; n++) {
1072
- let i = yield* A.make({
1079
+ let i = yield* k.make({
1073
1080
  name: `rate-acquire-${t}-${n}`,
1074
- success: vt,
1075
- execute: w.tryPromise({
1081
+ success: yt,
1082
+ execute: C.tryPromise({
1076
1083
  try: () => at(e.store, v, Date.now(), h),
1077
1084
  catch: (e) => (l.debug("rate-acquire raw error", {
1078
1085
  targetId: a,
@@ -1080,45 +1087,45 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
1080
1087
  attempt: t,
1081
1088
  cause: String(e)
1082
1089
  }), /* @__PURE__ */ Error(`rate-acquire failed: ${e.message}`))
1083
- }).pipe(w.orDie)
1090
+ }).pipe(C.orDie)
1084
1091
  });
1085
1092
  if (i.acquired) break;
1086
- yield* A.make({
1093
+ yield* k.make({
1087
1094
  name: `record-rate-deferral-${t}-${n}`,
1088
- success: T.Void,
1089
- execute: w.tryPromise({
1095
+ success: w.Void,
1096
+ execute: C.tryPromise({
1090
1097
  try: () => r(u, t, {
1091
1098
  status: "pending",
1092
1099
  nextAttemptAt: new Date(Date.now() + i.retryInMs)
1093
1100
  }),
1094
1101
  catch: (e) => /* @__PURE__ */ Error(`record-rate-deferral failed: ${e.message}`)
1095
- }).pipe(w.orDie)
1096
- }), yield* j.sleep({
1102
+ }).pipe(C.orDie)
1103
+ }), yield* A.sleep({
1097
1104
  name: `rate-window-sleep-${t}-${n}`,
1098
1105
  duration: `${i.retryInMs} millis`
1099
1106
  });
1100
1107
  }
1101
- yield* A.make({
1108
+ yield* k.make({
1102
1109
  name: `record-attempt-start-${t}`,
1103
- success: T.Void,
1104
- execute: w.tryPromise({
1110
+ success: w.Void,
1111
+ execute: C.tryPromise({
1105
1112
  try: () => r(u, t, {
1106
1113
  status: "inFlight",
1107
1114
  nextAttemptAt: null,
1108
1115
  scheduledAt: /* @__PURE__ */ new Date()
1109
1116
  }),
1110
1117
  catch: (e) => /* @__PURE__ */ Error(`record-attempt-start failed: ${e.message}`)
1111
- }).pipe(w.orDie)
1118
+ }).pipe(C.orDie)
1112
1119
  });
1113
- let _ = yield* A.make({
1120
+ let _ = yield* k.make({
1114
1121
  name: `sign-and-post-${t}`,
1115
- success: gt,
1116
- execute: w.promise(async () => {
1122
+ success: _t,
1123
+ execute: C.promise(async () => {
1117
1124
  let e = Date.now(), r;
1118
1125
  try {
1119
1126
  r = ot(n.format, c);
1120
1127
  } catch (t) {
1121
- if (t instanceof x) return {
1128
+ if (t instanceof b) return {
1122
1129
  status: 0,
1123
1130
  body: "",
1124
1131
  latencyMs: Date.now() - e,
@@ -1133,7 +1140,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
1133
1140
  secret: n.secret,
1134
1141
  messageId: i
1135
1142
  });
1136
- process.env.NODE_ENV === "production" && ve(n.url);
1143
+ process.env.NODE_ENV === "production" && ye(n.url);
1137
1144
  try {
1138
1145
  let c = await fetch(n.url, {
1139
1146
  method: "POST",
@@ -1170,12 +1177,12 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
1170
1177
  };
1171
1178
  }
1172
1179
  })
1173
- }), y = yield* A.make({
1180
+ }), y = yield* k.make({
1174
1181
  name: `classify-and-record-${t}`,
1175
- success: _t,
1176
- execute: w.tryPromise({
1182
+ success: vt,
1183
+ execute: C.tryPromise({
1177
1184
  try: async () => {
1178
- let r = _.status >= 200 && _.status < 300, i = _.transportError !== void 0, o = _.encodeError !== void 0, s = _.status === 410, c = (t, n) => Q(e.store, a, t, n, /* @__PURE__ */ new Date(), !t && s);
1185
+ let r = _.status >= 200 && _.status < 300, i = _.transportError !== void 0, o = _.encodeError !== void 0, s = _.status === 410, c = (t, n) => gt(e.store, a, t, n, /* @__PURE__ */ new Date(), !t && s);
1179
1186
  if (r) {
1180
1187
  await e.store.update("_voltro_webhook_deliveries", u, {
1181
1188
  status: "succeeded",
@@ -1193,7 +1200,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
1193
1200
  consecutiveFailures: t.consecutiveFailures
1194
1201
  };
1195
1202
  }
1196
- let l = o ? null : Te(d, t, {
1203
+ let l = o ? null : Ee(d, t, {
1197
1204
  ...i ? {} : { status: _.status },
1198
1205
  ..._.retryAfterSec === void 0 ? {} : { retryAfterSeconds: _.retryAfterSec }
1199
1206
  });
@@ -1238,7 +1245,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
1238
1245
  attempt: t,
1239
1246
  cause: String(e)
1240
1247
  }), /* @__PURE__ */ Error(`classify-and-record failed: ${e.message}`))
1241
- }).pipe(w.orDie)
1248
+ }).pipe(C.orDie)
1242
1249
  });
1243
1250
  if (y.autoDisabled === !0 && l.warn("target auto-disabled after consecutive failures", {
1244
1251
  targetId: a,
@@ -1253,7 +1260,7 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
1253
1260
  finalStatus: "failed",
1254
1261
  attempts: t
1255
1262
  };
1256
- y.delayMs !== void 0 && (yield* j.sleep({
1263
+ y.delayMs !== void 0 && (yield* A.sleep({
1257
1264
  name: `retry-sleep-${t}`,
1258
1265
  duration: `${y.delayMs} millis`
1259
1266
  }));
@@ -1263,15 +1270,15 @@ var M = ["network:outbound:*"], xe = (e = {}) => he({
1263
1270
  attempts: d.maxAttempts
1264
1271
  };
1265
1272
  });
1266
- }, $ = "voltro.webhook.emit", wt = (e) => typeof e.event == "string", Tt = (e) => be({
1273
+ }, $ = "voltro.webhook.emit", Tt = (e) => typeof e.event == "string", Et = (e) => xe({
1267
1274
  effect: $,
1268
1275
  maxAttempts: 1,
1269
1276
  handler: async ({ payload: t }) => {
1270
- if (!wt(t)) throw Error(`${$}: row payload has no \`event\` name — cannot resolve a descriptor. Got keys: ${Object.keys(t).join(", ") || "(none)"}`);
1277
+ if (!Tt(t)) throw Error(`${$}: row payload has no \`event\` name — cannot resolve a descriptor. Got keys: ${Object.keys(t).join(", ") || "(none)"}`);
1271
1278
  let n = e();
1272
1279
  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.`);
1273
1280
  await n.emit(t.event, t.payload, { tenantId: t.tenantId });
1274
1281
  }
1275
1282
  });
1276
1283
  //#endregion
1277
- export { We as IdempotencyCache, et as RATE_WINDOW_MS, $e as RATE_WINDOW_TABLE, p as STANDARD_WEBHOOKS_DEFAULT_TOLERANCE_SECONDS, te as STANDARD_WEBHOOKS_ID_HEADER, c as STANDARD_WEBHOOKS_MAX_KEY_BYTES, l as STANDARD_WEBHOOKS_MIN_KEY_BYTES, _ as STANDARD_WEBHOOKS_SECRET_PREFIX, m as STANDARD_WEBHOOKS_SIGNATURE_HEADER, d as STANDARD_WEBHOOKS_TIMESTAMP_HEADER, u as STANDARD_WEBHOOKS_VERSION, pt as TARGETS_TABLE, Je as UnverifiedIncomingWebhook, M as WEBHOOKS_PLUGIN_PERMISSIONS, $ as WEBHOOK_EMIT_EFFECT, qe as WEBHOOK_VERIFICATION_PROPERTY, b as WebhookDeliveryNotFound, oe as WebhookPayloadInvalid, x as WebhookPayloadUnrepresentable, se as WebhookPayloadVersionInvalid, S as WebhookSubscribeInvalid, C as WebhookTargetNotFound, Oe as WebhooksService, ce as _voltroWebhookDeliveriesTable, le as _voltroWebhookEventStatsTable, ue as _voltroWebhookRateWindowsTable, de as _voltroWebhookTargetsTable, at as acquireRateSlots, Ce as asOutgoingEvent, Ct as buildDeliverWebhookExecute, Be as buildWebhooksService, Me as compareVersions, rt as consumeRateSlot, Qe as defaultIncomingPath, ee as defaultOutgoingSignature, Ee as defaultRetryPolicy, re as defineIncomingWebhook, r as defineWebhookProvider, bt as deliverWebhookWorkflow, ot as encodePayload, L as eventsOf, De as fastRetryPolicy, I as generateSecret, i as generateStandardWebhooksSecret, h as genericHmacSignature, Ge as getIdempotencyCache, v as githubSignature, Se as hasWebhookAudience, Xe as incomingWebhookVerification, ne as isStandardWebhooksSecret, t as isWebhookDescriptor, Tt as makeDeferredEmitHandler, Ne as matchesFilter, Ze as mountIncomingWebhook, Te as nextRetry, N as parseDuration, Ke as parseTtl, Q as recordDeliveryOutcome, it as releaseRateSlot, B as resolveSubscribe, f as signRequest, a as slackSignature, ae as standardWebhooksHeaders, o as standardWebhooksKey, g as standardWebhooksSignature, ie as standardWebhooksSignatureToken, n as standardWebhooksSignedContent, y as stripeSignature, He as useWebhooks, Ve as useWebhooksEffect, z as validateSubscribe, s as verifyRequest, e as verifyStandardWebhooks, fe as webhookTables, xe as webhooksPlugin };
1284
+ export { Ge as IdempotencyCache, tt as RATE_WINDOW_MS, et as RATE_WINDOW_TABLE, p as STANDARD_WEBHOOKS_DEFAULT_TOLERANCE_SECONDS, te as STANDARD_WEBHOOKS_ID_HEADER, c as STANDARD_WEBHOOKS_MAX_KEY_BYTES, l as STANDARD_WEBHOOKS_MIN_KEY_BYTES, _ as STANDARD_WEBHOOKS_SECRET_PREFIX, m as STANDARD_WEBHOOKS_SIGNATURE_HEADER, d as STANDARD_WEBHOOKS_TIMESTAMP_HEADER, u as STANDARD_WEBHOOKS_VERSION, pt as TARGETS_TABLE, Ye as UnverifiedIncomingWebhook, M as WEBHOOKS_EGRESS, j as WEBHOOKS_PLUGIN_PERMISSIONS, $ as WEBHOOK_EMIT_EFFECT, Je as WEBHOOK_VERIFICATION_PROPERTY, oe as WebhookDeliveryNotFound, se as WebhookPayloadInvalid, b as WebhookPayloadUnrepresentable, ce as WebhookPayloadVersionInvalid, x as WebhookSubscribeInvalid, S as WebhookTargetNotFound, ke as WebhooksService, le as _voltroWebhookDeliveriesTable, ue as _voltroWebhookEventStatsTable, de as _voltroWebhookRateWindowsTable, fe as _voltroWebhookTargetsTable, at as acquireRateSlots, we as asOutgoingEvent, wt as buildDeliverWebhookExecute, Ve as buildWebhooksService, Fe as compareVersions, X as consumeRateSlot, $e as defaultIncomingPath, ee as defaultOutgoingSignature, De as defaultRetryPolicy, re as defineIncomingWebhook, r as defineWebhookProvider, xt as deliverWebhookWorkflow, ot as encodePayload, F as eventsOf, Oe as fastRetryPolicy, Me as generateSecret, i as generateStandardWebhooksSecret, h as genericHmacSignature, Ke as getIdempotencyCache, v as githubSignature, Ce as hasWebhookAudience, Ze as incomingWebhookVerification, ne as isStandardWebhooksSecret, t as isWebhookDescriptor, Et as makeDeferredEmitHandler, V as matchesFilter, Qe as mountIncomingWebhook, Ee as nextRetry, N as parseDuration, qe as parseTtl, gt as recordDeliveryOutcome, it as releaseRateSlot, R as resolveSubscribe, f as signRequest, a as slackSignature, ae as standardWebhooksHeaders, o as standardWebhooksKey, g as standardWebhooksSignature, ie as standardWebhooksSignatureToken, n as standardWebhooksSignedContent, y as stripeSignature, Ue as useWebhooks, He as useWebhooksEffect, L as validateSubscribe, s as verifyRequest, e as verifyStandardWebhooks, pe as webhookTables, Se as webhooksPlugin };