@voltro/plugin-webhooks 0.86.0 → 0.88.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.
@@ -1,5 +1,5 @@
1
1
  import { and as e, eq as t } from "@voltro/database";
2
- //#region src/autoDisable.ts
2
+ // src/autoDisable.ts
3
3
  var n = "_voltro_webhook_targets", r = 32, i = async (e, n) => (await e.query({
4
4
  table: "_voltro_webhook_targets",
5
5
  predicate: t("id", n),
@@ -37,5 +37,4 @@ var n = "_voltro_webhook_targets", r = 32, i = async (e, n) => (await e.query({
37
37
  }
38
38
  throw Error(`webhook auto-disable: streak CAS contention on target ${a} exceeded ${r} attempts`);
39
39
  };
40
- //#endregion
41
40
  export { n as TARGETS_TABLE, a as recordDeliveryOutcome };
@@ -1,5 +1,5 @@
1
1
  import { defineOutboxHandler as e } from "@voltro/runtime";
2
- //#region src/deferredEmit.ts
2
+ // src/deferredEmit.ts
3
3
  var t = "voltro.webhook.emit", n = (e) => typeof e.event == "string", r = (r) => e({
4
4
  effect: t,
5
5
  maxAttempts: 1,
@@ -13,5 +13,4 @@ var t = "voltro.webhook.emit", n = (e) => typeof e.event == "string", r = (r) =>
13
13
  });
14
14
  }
15
15
  });
16
- //#endregion
17
16
  export { t as WEBHOOK_EMIT_EFFECT, r as makeDeferredEmitHandler };
@@ -9,7 +9,7 @@ import { eq as c } from "@voltro/database";
9
9
  import { assertPublicUrl as l } from "@voltro/integration-http";
10
10
  import { createLogger as u } from "@voltro/logger";
11
11
  import { Activity as d, DurableClock as f, Workflow as p } from "effect/workflow";
12
- //#region src/deliverWorkflow.ts
12
+ // src/deliverWorkflow.ts
13
13
  var m = s.Struct({
14
14
  status: s.Number,
15
15
  body: s.String,
@@ -62,6 +62,8 @@ var m = s.Struct({
62
62
  event: s.String,
63
63
  url: s.String,
64
64
  secret: s.String,
65
+ previousSecret: s.optional(s.Union([s.String, s.Null])),
66
+ previousSecretUntil: s.optional(s.Union([s.String, s.Null])),
65
67
  signing: s.Any,
66
68
  retry: s.Any,
67
69
  headers: s.Union([s.Record(s.String, s.String), s.Null]),
@@ -75,17 +77,26 @@ var m = s.Struct({
75
77
  tenantId: s.Union([s.String, s.Null])
76
78
  })]),
77
79
  execute: o.tryPromise({
78
- try: async () => (await p.store.query({
79
- table: "_voltro_webhook_targets",
80
- predicate: c("id", S),
81
- order: [{
82
- column: "createdAt",
83
- direction: "asc"
84
- }],
85
- take: 1,
86
- skip: void 0,
87
- projection: void 0
88
- }))[0] ?? null,
80
+ try: async () => {
81
+ let e = (await p.store.query({
82
+ table: "_voltro_webhook_targets",
83
+ predicate: c("id", S),
84
+ order: [{
85
+ column: "createdAt",
86
+ direction: "asc"
87
+ }],
88
+ take: 1,
89
+ skip: void 0,
90
+ projection: void 0
91
+ }))[0];
92
+ if (e === void 0) return null;
93
+ let t = e.previousSecretUntil;
94
+ return {
95
+ ...e,
96
+ previousSecret: e.previousSecret ?? null,
97
+ previousSecretUntil: t instanceof Date ? t.toISOString() : t ?? null
98
+ };
99
+ },
89
100
  catch: (e) => (E.debug("fetch-target raw error", {
90
101
  targetId: S,
91
102
  event: C,
@@ -196,7 +207,10 @@ var m = s.Struct({
196
207
  };
197
208
  throw e;
198
209
  }
199
- let a = Buffer.from(r.bytes), { headers: o } = e(O, {
210
+ let a = Buffer.from(r.bytes), o = u.previousSecret != null && u.previousSecretUntil != null && Date.parse(u.previousSecretUntil) > Date.now() && O._tag === "standardWebhooks" && O.previousSecret === void 0 ? {
211
+ ...O,
212
+ previousSecret: u.previousSecret
213
+ } : O, { headers: s } = e(o, {
200
214
  rawBody: a,
201
215
  secret: u.secret,
202
216
  messageId: x
@@ -207,7 +221,7 @@ var m = s.Struct({
207
221
  method: "POST",
208
222
  headers: {
209
223
  "content-type": r.contentType,
210
- ...o,
224
+ ...s,
211
225
  "x-voltro-event": C,
212
226
  "x-voltro-event-id": w,
213
227
  "x-voltro-delivery-id": x,
@@ -332,5 +346,4 @@ var m = s.Struct({
332
346
  };
333
347
  });
334
348
  };
335
- //#endregion
336
349
  export { y as DEFAULT_WEBHOOK_TIMEOUT_MS, x as buildDeliverWebhookExecute, v as deliverWebhookWorkflow };
package/dist/dsl.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/dsl.ts
1
+ // src/dsl.ts
2
2
  var e = (e) => ({
3
3
  _tag: "incomingWebhook",
4
4
  ...e
@@ -10,5 +10,4 @@ var e = (e) => ({
10
10
  let t = e._tag;
11
11
  return t === "outgoingEvent" || t === "incomingWebhook" || t === "webhookProvider";
12
12
  };
13
- //#endregion
14
13
  export { e as defineIncomingWebhook, t as defineWebhookProvider, n as isWebhookDescriptor };
package/dist/encode.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { WebhookPayloadUnrepresentable as e } from "./errors.js";
2
- //#region src/encode.ts
2
+ // src/encode.ts
3
3
  var t = (t, r) => {
4
4
  if (t === "json") return {
5
5
  contentType: "application/json",
@@ -49,5 +49,4 @@ var t = (t, r) => {
49
49
  default: return "'";
50
50
  }
51
51
  }), l = (e) => e === null ? "null" : Array.isArray(e) ? "array" : typeof e;
52
- //#endregion
53
52
  export { t as encodePayload };
package/dist/errors.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Schema as e } from "effect";
2
- //#region src/errors.ts
2
+ // src/errors.ts
3
3
  var t = class extends e.TaggedError()("WebhookSubscribeInvalid", {
4
4
  field: e.String,
5
5
  reason: e.String
@@ -18,5 +18,4 @@ var t = class extends e.TaggedError()("WebhookSubscribeInvalid", {
18
18
  ]),
19
19
  reason: e.String
20
20
  }) {};
21
- //#endregion
22
21
  export { n as WebhookDeliveryNotFound, a as WebhookPayloadInvalid, o as WebhookPayloadUnrepresentable, i as WebhookPayloadVersionInvalid, t as WebhookSubscribeInvalid, r as WebhookTargetNotFound };
@@ -1,4 +1,4 @@
1
- //#region src/eventAudience.ts
1
+ // src/eventAudience.ts
2
2
  var e = (e) => e.webhook !== void 0, t = (e) => {
3
3
  let t = e.webhook;
4
4
  if (t !== void 0) return {
@@ -10,5 +10,4 @@ var e = (e) => e.webhook !== void 0, t = (e) => {
10
10
  ...t.rateLimit === void 0 ? {} : { globalRateLimit: t.rateLimit }
11
11
  };
12
12
  };
13
- //#endregion
14
13
  export { t as asOutgoingEvent, e as hasWebhookAudience };
@@ -1,4 +1,4 @@
1
- //#region src/idempotency.ts
1
+ // src/idempotency.ts
2
2
  var e = class {
3
3
  capacity;
4
4
  cache = /* @__PURE__ */ new Map();
@@ -45,5 +45,4 @@ var e = class {
45
45
  "7d": 6048e5,
46
46
  "30d": 2592e6
47
47
  }, i = (e) => e === void 0 ? r["7d"] : r[e] ?? r["7d"];
48
- //#endregion
49
48
  export { e as IdempotencyCache, n as getIdempotencyCache, i as parseTtl };
package/dist/incoming.js CHANGED
@@ -3,7 +3,7 @@ import { getIdempotencyCache as t, parseTtl as n } from "./idempotency.js";
3
3
  import { Schema as r } from "effect";
4
4
  import { publishServerError as i } from "@voltro/protocol";
5
5
  import { createLogger as a } from "@voltro/logger";
6
- //#region src/incoming.ts
6
+ // src/incoming.ts
7
7
  var o = (e, t) => ({
8
8
  status: e,
9
9
  contentType: "application/json; charset=utf-8",
@@ -201,5 +201,4 @@ var o = (e, t) => ({
201
201
  }
202
202
  }, { [s]: v });
203
203
  }, f = (e) => `/webhooks/${e}`;
204
- //#endregion
205
204
  export { c as UnverifiedIncomingWebhook, s as WEBHOOK_VERIFICATION_PROPERTY, f as defaultIncomingPath, u as incomingWebhookVerification, d as mountIncomingWebhook };
package/dist/index.d.ts CHANGED
@@ -617,6 +617,9 @@ emitter: () => Pick<WebhooksServiceShape, "emit"> | undefined) => OutboxHandlerD
617
617
  */
618
618
  export declare const matchesFilter: (filter: WebhookFilter | Readonly<Record<string, unknown>> | null, payload: unknown) => boolean;
619
619
 
620
+ /** The longest overlap `rotateSecret` accepts, and its default: 30 days, unless ended earlier. */
621
+ export declare const MAX_SECRET_OVERLAP_SECONDS: number;
622
+
620
623
  /** A mounted handler, carrying its verification declaration. */
621
624
  export declare type MountedIncomingWebhook = ((request: IncomingRequest) => Promise<IncomingResponse>) & {
622
625
  readonly [WEBHOOK_VERIFICATION_PROPERTY]: IncomingWebhookVerification;
@@ -822,6 +825,28 @@ export declare interface RetryPolicy {
822
825
 
823
826
  export declare type RetryStrategy = 'fixed' | 'linear' | 'exponential';
824
827
 
828
+ /** What `rotateSecret` returns. */
829
+ export declare interface RotatedSecret {
830
+ /** The new secret — returned once; hand it to the receiver. */
831
+ readonly secret: string;
832
+ /** Until when deliveries are signed under the previous secret too. `null`:
833
+ * no overlap — asked for none, or a scheme with one signature per message. */
834
+ readonly previousSignedUntil: Date | null;
835
+ }
836
+
837
+ /** How long a rotated webhook secret keeps signing beside the new one. */
838
+ export declare interface RotateSecretOptions {
839
+ /**
840
+ * Seconds every delivery still carries a signature under the previous
841
+ * secret, so the receiver can switch when it is ready. The length is the
842
+ * RECEIVER's switch-over time — nothing on the sending side can measure it —
843
+ * so by default the overlap lasts until {@link WebhooksServiceShape.endSecretOverlap}
844
+ * ends it (once the receiver has switched), and at most 30 days. Pass a
845
+ * number to bound it yourself; `0` switches at once (after a leak).
846
+ */
847
+ readonly overlapSeconds?: number;
848
+ }
849
+
825
850
  export declare type SignatureScheme = HmacSignatureScheme | CustomSignatureScheme | StandardWebhooksSignatureScheme;
826
851
 
827
852
  export declare interface SignedRequest {
@@ -1250,7 +1275,7 @@ export declare const _voltroWebhookDeliveriesTable: Table<"_voltro_webhook_deliv
1250
1275
  readonly eventId: ColumnBuilder<string | null, "text", boolean>;
1251
1276
  /** Attempt counter (1-indexed). */
1252
1277
  readonly attempt: ColumnBuilder<number, "integer", boolean>;
1253
- readonly status: ColumnBuilder<"failed" | "pending" | "succeeded" | "inFlight" | "retryScheduled", "text", boolean>;
1278
+ readonly status: ColumnBuilder<"failed" | "succeeded" | "pending" | "inFlight" | "retryScheduled", "text", boolean>;
1254
1279
  /** Payload as sent over the wire. Stored verbatim — re-rendering
1255
1280
  * from a referenced event row would lose the snapshot if the
1256
1281
  * source event was deleted. */
@@ -1389,6 +1414,13 @@ export declare const _voltroWebhookTargetsTable: Table<"_voltro_webhook_targets"
1389
1414
  * (a non-ciphertext value passes through); `voltro db encrypt-column
1390
1415
  * _voltro_webhook_targets.secret` converts them in place. */
1391
1416
  readonly secret: ColumnBuilder<string, "text", boolean>;
1417
+ /** During a rotation (`rotateSecret`): the secret the receiver may still
1418
+ * verify with. Every delivery until {@link previousSecretUntil} carries a
1419
+ * signature under it beside the new one (Standard Webhooks' signature list),
1420
+ * so the receiver switches when it is ready. Encrypted like `secret`. */
1421
+ readonly previousSecret: ColumnBuilder<string | null, "text", boolean>;
1422
+ /** When deliveries stop signing under `previousSecret`. */
1423
+ readonly previousSecretUntil: ColumnBuilder<Date | null, "timestamp", boolean>;
1392
1424
  /** Serialised `SignatureScheme` discriminated union — see `signing.ts`.
1393
1425
  * Stored as JSON so future schemes don't require a schema migration. */
1394
1426
  readonly signing: ColumnBuilder<unknown, "json", boolean>;
@@ -1702,8 +1734,11 @@ export declare interface WebhooksServiceShape {
1702
1734
  * must sign identically, and the previous way to achieve it was delete +
1703
1735
  * re-subscribe, minting new ids and orphaning the delivery history.
1704
1736
  */
1705
- readonly rotateSecret: (target: TargetSelector) => Promise<{
1706
- readonly secret: string;
1737
+ readonly rotateSecret: (target: TargetSelector, options?: RotateSecretOptions) => Promise<RotatedSecret>;
1738
+ /** End a rotation's overlap once the receiver verifies with the new secret: the
1739
+ * previous secret stops signing at once. Returns how many targets were overlapping. */
1740
+ readonly endSecretOverlap: (target: TargetSelector) => Promise<{
1741
+ readonly ended: number;
1707
1742
  }>;
1708
1743
  /** Re-pin a target's `payloadVersion` to the event's current
1709
1744
  * version. Called from the dashboard's "Re-pin" action after
@@ -1805,6 +1840,13 @@ export declare const webhookTables: () => readonly [ Table<"_voltro_webhook_targ
1805
1840
  * (a non-ciphertext value passes through); `voltro db encrypt-column
1806
1841
  * _voltro_webhook_targets.secret` converts them in place. */
1807
1842
  readonly secret: ColumnBuilder<string, "text", boolean>;
1843
+ /** During a rotation (`rotateSecret`): the secret the receiver may still
1844
+ * verify with. Every delivery until {@link previousSecretUntil} carries a
1845
+ * signature under it beside the new one (Standard Webhooks' signature list),
1846
+ * so the receiver switches when it is ready. Encrypted like `secret`. */
1847
+ readonly previousSecret: ColumnBuilder<string | null, "text", boolean>;
1848
+ /** When deliveries stop signing under `previousSecret`. */
1849
+ readonly previousSecretUntil: ColumnBuilder<Date | null, "timestamp", boolean>;
1808
1850
  /** Serialised `SignatureScheme` discriminated union — see `signing.ts`.
1809
1851
  * Stored as JSON so future schemes don't require a schema migration. */
1810
1852
  readonly signing: ColumnBuilder<unknown, "json", boolean>;
@@ -1910,7 +1952,7 @@ export declare const webhookTables: () => readonly [ Table<"_voltro_webhook_targ
1910
1952
  readonly eventId: ColumnBuilder<string | null, "text", boolean>;
1911
1953
  /** Attempt counter (1-indexed). */
1912
1954
  readonly attempt: ColumnBuilder<number, "integer", boolean>;
1913
- readonly status: ColumnBuilder<"failed" | "pending" | "succeeded" | "inFlight" | "retryScheduled", "text", boolean>;
1955
+ readonly status: ColumnBuilder<"failed" | "succeeded" | "pending" | "inFlight" | "retryScheduled", "text", boolean>;
1914
1956
  /** Payload as sent over the wire. Stored verbatim — re-rendering
1915
1957
  * from a referenced event row would lose the snapshot if the
1916
1958
  * source event was deleted. */
package/dist/index.js CHANGED
@@ -5,13 +5,13 @@ import { STANDARD_WEBHOOKS_DEFAULT_TOLERANCE_SECONDS as c, STANDARD_WEBHOOKS_ID_
5
5
  import { defaultOutgoingSignature as C, genericHmacSignature as w, githubSignature as T, signRequest as E, slackSignature as D, standardWebhooksSignature as O, stripeSignature as k, verifyRequest as A } from "./signing.js";
6
6
  import { defaultRetryPolicy as j, fastRetryPolicy as M, nextRetry as N, parseDuration as P } from "./retry.js";
7
7
  import { WebhookDeliveryNotFound as F, WebhookPayloadInvalid as I, WebhookPayloadUnrepresentable as L, WebhookPayloadVersionInvalid as R, WebhookSubscribeInvalid as z, WebhookTargetNotFound as B } from "./errors.js";
8
- import { WebhooksService as V, buildWebhooksService as H, compareVersions as U, eventsOf as W, generateSecret as G, matchesFilter as K, resolveSubscribe as q, useWebhooks as J, useWebhooksEffect as Y, validateSubscribe as X } from "./service.js";
9
- import { IdempotencyCache as Z, getIdempotencyCache as Q, parseTtl as $ } from "./idempotency.js";
10
- import { UnverifiedIncomingWebhook as ee, WEBHOOK_VERIFICATION_PROPERTY as te, defaultIncomingPath as ne, incomingWebhookVerification as re, mountIncomingWebhook as ie } from "./incoming.js";
11
- import { RATE_WINDOW_MS as ae, RATE_WINDOW_TABLE as oe, acquireRateSlots as se, consumeRateSlot as ce, releaseRateSlot as le } from "./rateLimit.js";
12
- import { encodePayload as ue } from "./encode.js";
13
- import { TARGETS_TABLE as de, recordDeliveryOutcome as fe } from "./autoDisable.js";
14
- import { buildDeliverWebhookExecute as pe, deliverWebhookWorkflow as me } from "./deliverWorkflow.js";
15
- import { _voltroWebhookDeliveriesTable as he, _voltroWebhookEventStatsTable as ge, _voltroWebhookRateWindowsTable as _e, _voltroWebhookTargetsTable as ve, webhookTables as ye } from "./mixin.js";
16
- import { WEBHOOK_EMIT_EFFECT as be, makeDeferredEmitHandler as xe } from "./deferredEmit.js";
17
- export { Z as IdempotencyCache, ae as RATE_WINDOW_MS, oe as RATE_WINDOW_TABLE, c as STANDARD_WEBHOOKS_DEFAULT_TOLERANCE_SECONDS, l as STANDARD_WEBHOOKS_ID_HEADER, u as STANDARD_WEBHOOKS_MAX_KEY_BYTES, d as STANDARD_WEBHOOKS_MIN_KEY_BYTES, f as STANDARD_WEBHOOKS_SECRET_PREFIX, p as STANDARD_WEBHOOKS_SIGNATURE_HEADER, m as STANDARD_WEBHOOKS_TIMESTAMP_HEADER, h as STANDARD_WEBHOOKS_VERSION, de as TARGETS_TABLE, ee as UnverifiedIncomingWebhook, e as WEBHOOKS_EGRESS, t as WEBHOOKS_PLUGIN_PERMISSIONS, be as WEBHOOK_EMIT_EFFECT, te as WEBHOOK_VERIFICATION_PROPERTY, F as WebhookDeliveryNotFound, I as WebhookPayloadInvalid, L as WebhookPayloadUnrepresentable, R as WebhookPayloadVersionInvalid, z as WebhookSubscribeInvalid, B as WebhookTargetNotFound, V as WebhooksService, he as _voltroWebhookDeliveriesTable, ge as _voltroWebhookEventStatsTable, _e as _voltroWebhookRateWindowsTable, ve as _voltroWebhookTargetsTable, se as acquireRateSlots, o as asOutgoingEvent, pe as buildDeliverWebhookExecute, H as buildWebhooksService, U as compareVersions, ce as consumeRateSlot, ne as defaultIncomingPath, C as defaultOutgoingSignature, j as defaultRetryPolicy, r as defineIncomingWebhook, i as defineWebhookProvider, me as deliverWebhookWorkflow, ue as encodePayload, W as eventsOf, M as fastRetryPolicy, G as generateSecret, g as generateStandardWebhooksSecret, w as genericHmacSignature, Q as getIdempotencyCache, T as githubSignature, s as hasWebhookAudience, re as incomingWebhookVerification, _ as isStandardWebhooksSecret, a as isWebhookDescriptor, xe as makeDeferredEmitHandler, K as matchesFilter, ie as mountIncomingWebhook, N as nextRetry, P as parseDuration, $ as parseTtl, fe as recordDeliveryOutcome, le as releaseRateSlot, q as resolveSubscribe, E as signRequest, D as slackSignature, v as standardWebhooksHeaders, y as standardWebhooksKey, O as standardWebhooksSignature, b as standardWebhooksSignatureToken, x as standardWebhooksSignedContent, k as stripeSignature, J as useWebhooks, Y as useWebhooksEffect, X as validateSubscribe, A as verifyRequest, S as verifyStandardWebhooks, ye as webhookTables, n as webhooksPlugin };
8
+ import { MAX_SECRET_OVERLAP_SECONDS as V, WebhooksService as H, buildWebhooksService as U, compareVersions as W, eventsOf as G, generateSecret as K, matchesFilter as q, resolveSubscribe as J, useWebhooks as Y, useWebhooksEffect as X, validateSubscribe as Z } from "./service.js";
9
+ import { IdempotencyCache as Q, getIdempotencyCache as $, parseTtl as ee } from "./idempotency.js";
10
+ import { UnverifiedIncomingWebhook as te, WEBHOOK_VERIFICATION_PROPERTY as ne, defaultIncomingPath as re, incomingWebhookVerification as ie, mountIncomingWebhook as ae } from "./incoming.js";
11
+ import { RATE_WINDOW_MS as oe, RATE_WINDOW_TABLE as se, acquireRateSlots as ce, consumeRateSlot as le, releaseRateSlot as ue } from "./rateLimit.js";
12
+ import { encodePayload as de } from "./encode.js";
13
+ import { TARGETS_TABLE as fe, recordDeliveryOutcome as pe } from "./autoDisable.js";
14
+ import { buildDeliverWebhookExecute as me, deliverWebhookWorkflow as he } from "./deliverWorkflow.js";
15
+ import { _voltroWebhookDeliveriesTable as ge, _voltroWebhookEventStatsTable as _e, _voltroWebhookRateWindowsTable as ve, _voltroWebhookTargetsTable as ye, webhookTables as be } from "./mixin.js";
16
+ import { WEBHOOK_EMIT_EFFECT as xe, makeDeferredEmitHandler as Se } from "./deferredEmit.js";
17
+ export { Q as IdempotencyCache, V as MAX_SECRET_OVERLAP_SECONDS, oe as RATE_WINDOW_MS, se as RATE_WINDOW_TABLE, c as STANDARD_WEBHOOKS_DEFAULT_TOLERANCE_SECONDS, l as STANDARD_WEBHOOKS_ID_HEADER, u as STANDARD_WEBHOOKS_MAX_KEY_BYTES, d as STANDARD_WEBHOOKS_MIN_KEY_BYTES, f as STANDARD_WEBHOOKS_SECRET_PREFIX, p as STANDARD_WEBHOOKS_SIGNATURE_HEADER, m as STANDARD_WEBHOOKS_TIMESTAMP_HEADER, h as STANDARD_WEBHOOKS_VERSION, fe as TARGETS_TABLE, te as UnverifiedIncomingWebhook, e as WEBHOOKS_EGRESS, t as WEBHOOKS_PLUGIN_PERMISSIONS, xe as WEBHOOK_EMIT_EFFECT, ne as WEBHOOK_VERIFICATION_PROPERTY, F as WebhookDeliveryNotFound, I as WebhookPayloadInvalid, L as WebhookPayloadUnrepresentable, R as WebhookPayloadVersionInvalid, z as WebhookSubscribeInvalid, B as WebhookTargetNotFound, H as WebhooksService, ge as _voltroWebhookDeliveriesTable, _e as _voltroWebhookEventStatsTable, ve as _voltroWebhookRateWindowsTable, ye as _voltroWebhookTargetsTable, ce as acquireRateSlots, o as asOutgoingEvent, me as buildDeliverWebhookExecute, U as buildWebhooksService, W as compareVersions, le as consumeRateSlot, re as defaultIncomingPath, C as defaultOutgoingSignature, j as defaultRetryPolicy, r as defineIncomingWebhook, i as defineWebhookProvider, he as deliverWebhookWorkflow, de as encodePayload, G as eventsOf, M as fastRetryPolicy, K as generateSecret, g as generateStandardWebhooksSecret, w as genericHmacSignature, $ as getIdempotencyCache, T as githubSignature, s as hasWebhookAudience, ie as incomingWebhookVerification, _ as isStandardWebhooksSecret, a as isWebhookDescriptor, Se as makeDeferredEmitHandler, q as matchesFilter, ae as mountIncomingWebhook, N as nextRetry, P as parseDuration, ee as parseTtl, pe as recordDeliveryOutcome, ue as releaseRateSlot, J as resolveSubscribe, E as signRequest, D as slackSignature, v as standardWebhooksHeaders, y as standardWebhooksKey, O as standardWebhooksSignature, b as standardWebhooksSignatureToken, x as standardWebhooksSignedContent, k as stripeSignature, Y as useWebhooks, X as useWebhooksEffect, Z as validateSubscribe, A as verifyRequest, S as verifyStandardWebhooks, be as webhookTables, n as webhooksPlugin };
package/dist/mixin.d.ts CHANGED
@@ -32,7 +32,7 @@ export declare const _voltroWebhookDeliveriesTable: Table<"_voltro_webhook_deliv
32
32
  readonly eventId: ColumnBuilder<string | null, "text", boolean>;
33
33
  /** Attempt counter (1-indexed). */
34
34
  readonly attempt: ColumnBuilder<number, "integer", boolean>;
35
- readonly status: ColumnBuilder<"failed" | "pending" | "succeeded" | "inFlight" | "retryScheduled", "text", boolean>;
35
+ readonly status: ColumnBuilder<"failed" | "succeeded" | "pending" | "inFlight" | "retryScheduled", "text", boolean>;
36
36
  /** Payload as sent over the wire. Stored verbatim — re-rendering
37
37
  * from a referenced event row would lose the snapshot if the
38
38
  * source event was deleted. */
@@ -171,6 +171,13 @@ export declare const _voltroWebhookTargetsTable: Table<"_voltro_webhook_targets"
171
171
  * (a non-ciphertext value passes through); `voltro db encrypt-column
172
172
  * _voltro_webhook_targets.secret` converts them in place. */
173
173
  readonly secret: ColumnBuilder<string, "text", boolean>;
174
+ /** During a rotation (`rotateSecret`): the secret the receiver may still
175
+ * verify with. Every delivery until {@link previousSecretUntil} carries a
176
+ * signature under it beside the new one (Standard Webhooks' signature list),
177
+ * so the receiver switches when it is ready. Encrypted like `secret`. */
178
+ readonly previousSecret: ColumnBuilder<string | null, "text", boolean>;
179
+ /** When deliveries stop signing under `previousSecret`. */
180
+ readonly previousSecretUntil: ColumnBuilder<Date | null, "timestamp", boolean>;
174
181
  /** Serialised `SignatureScheme` discriminated union — see `signing.ts`.
175
182
  * Stored as JSON so future schemes don't require a schema migration. */
176
183
  readonly signing: ColumnBuilder<unknown, "json", boolean>;
@@ -287,6 +294,13 @@ export declare const webhookTables: () => readonly [ Table<"_voltro_webhook_targ
287
294
  * (a non-ciphertext value passes through); `voltro db encrypt-column
288
295
  * _voltro_webhook_targets.secret` converts them in place. */
289
296
  readonly secret: ColumnBuilder<string, "text", boolean>;
297
+ /** During a rotation (`rotateSecret`): the secret the receiver may still
298
+ * verify with. Every delivery until {@link previousSecretUntil} carries a
299
+ * signature under it beside the new one (Standard Webhooks' signature list),
300
+ * so the receiver switches when it is ready. Encrypted like `secret`. */
301
+ readonly previousSecret: ColumnBuilder<string | null, "text", boolean>;
302
+ /** When deliveries stop signing under `previousSecret`. */
303
+ readonly previousSecretUntil: ColumnBuilder<Date | null, "timestamp", boolean>;
290
304
  /** Serialised `SignatureScheme` discriminated union — see `signing.ts`.
291
305
  * Stored as JSON so future schemes don't require a schema migration. */
292
306
  readonly signing: ColumnBuilder<unknown, "json", boolean>;
@@ -392,7 +406,7 @@ export declare const webhookTables: () => readonly [ Table<"_voltro_webhook_targ
392
406
  readonly eventId: ColumnBuilder<string | null, "text", boolean>;
393
407
  /** Attempt counter (1-indexed). */
394
408
  readonly attempt: ColumnBuilder<number, "integer", boolean>;
395
- readonly status: ColumnBuilder<"failed" | "pending" | "succeeded" | "inFlight" | "retryScheduled", "text", boolean>;
409
+ readonly status: ColumnBuilder<"failed" | "succeeded" | "pending" | "inFlight" | "retryScheduled", "text", boolean>;
396
410
  /** Payload as sent over the wire. Stored verbatim — re-rendering
397
411
  * from a referenced event row would lose the snapshot if the
398
412
  * source event was deleted. */
package/dist/mixin.js CHANGED
@@ -1,11 +1,13 @@
1
1
  import { boolean as e, id as t, integer as n, json as r, registerRetention as i, retentionTtlMsFromEnv as a, table as o, text as s, timestamp as c } from "@voltro/database";
2
2
  import { tenant as l } from "@voltro/plugin-multitenancy/mixin";
3
- //#region src/mixin.ts
3
+ // src/mixin.ts
4
4
  var u = o("_voltro_webhook_targets", {
5
5
  id: t({ prefix: "whtarget" }),
6
6
  event: s(),
7
7
  url: s(),
8
8
  secret: s().encrypted().serverOnly(),
9
+ previousSecret: s().encrypted().serverOnly().nullable(),
10
+ previousSecretUntil: c().nullable(),
9
11
  signing: r(),
10
12
  retry: r(),
11
13
  scope: r().nullable(),
@@ -75,5 +77,4 @@ var u = o("_voltro_webhook_targets", {
75
77
  f,
76
78
  p
77
79
  ]);
78
- //#endregion
79
80
  export { d as _voltroWebhookDeliveriesTable, p as _voltroWebhookEventStatsTable, f as _voltroWebhookRateWindowsTable, u as _voltroWebhookTargetsTable, m as webhookTables };
package/dist/plugin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Effect as e } from "effect";
2
2
  import { definePlugin as t } from "@voltro/protocol";
3
- //#region src/plugin.ts
3
+ // src/plugin.ts
4
4
  var n = ["network:outbound:*"], r = [{
5
5
  destination: "runtime:registered webhook targets (_voltro_webhook_targets)",
6
6
  purpose: "event deliveries, HMAC-signed",
@@ -15,5 +15,4 @@ var n = ["network:outbound:*"], r = [{
15
15
  t.logger.info("webhooks active", { outbound: "network:outbound:* (subscriber-configured target URLs — audit visibility, not a restriction)" });
16
16
  })
17
17
  });
18
- //#endregion
19
18
  export { r as WEBHOOKS_EGRESS, n as WEBHOOKS_PLUGIN_PERMISSIONS, i as webhooksPlugin };
@@ -1,6 +1,6 @@
1
1
  import { defineWebhookProvider as e } from "../dsl.js";
2
2
  import { genericHmacSignature as t } from "../signing.js";
3
- //#region src/providers/generic.ts
3
+ // src/providers/generic.ts
4
4
  var n = (n) => e({
5
5
  id: "generic",
6
6
  name: "Generic",
@@ -15,5 +15,4 @@ var n = (n) => e({
15
15
  },
16
16
  bodyType: "json"
17
17
  });
18
- //#endregion
19
18
  export { n as genericProvider };
@@ -1,6 +1,6 @@
1
1
  import { defineWebhookProvider as e } from "../dsl.js";
2
2
  import { githubSignature as t } from "../signing.js";
3
- //#region src/providers/github.ts
3
+ // src/providers/github.ts
4
4
  var n = () => e({
5
5
  id: "github",
6
6
  name: "GitHub",
@@ -12,5 +12,4 @@ var n = () => e({
12
12
  bodyType: "json",
13
13
  eventTypeFrom: (e) => {}
14
14
  });
15
- //#endregion
16
15
  export { n as githubProvider };
@@ -1,6 +1,6 @@
1
1
  import { defineWebhookProvider as e } from "../dsl.js";
2
2
  import { createHmac as t, timingSafeEqual as n } from "node:crypto";
3
- //#region src/providers/slack.ts
3
+ // src/providers/slack.ts
4
4
  var r = () => ({
5
5
  _tag: "custom",
6
6
  header: "X-Slack-Signature",
@@ -35,5 +35,4 @@ var r = () => ({
35
35
  }
36
36
  }
37
37
  });
38
- //#endregion
39
38
  export { i as slackProvider };
@@ -1,6 +1,6 @@
1
1
  import { defineWebhookProvider as e } from "../dsl.js";
2
2
  import { stripeSignature as t } from "../signing.js";
3
- //#region src/providers/stripe.ts
3
+ // src/providers/stripe.ts
4
4
  var n = (n) => e({
5
5
  id: "stripe",
6
6
  name: "Stripe",
@@ -26,5 +26,4 @@ var n = (n) => e({
26
26
  }
27
27
  }
28
28
  });
29
- //#endregion
30
29
  export { n as stripeProvider };
package/dist/rateLimit.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { and as e, eq as t } from "@voltro/database";
2
- //#region src/rateLimit.ts
2
+ // src/rateLimit.ts
3
3
  var n = "_voltro_webhook_rate_windows", r = 6e4, i = (e, t) => `${e}@${t}`, a = async (n, r, i) => (await n.query({
4
4
  table: "_voltro_webhook_rate_windows",
5
5
  predicate: e(t("scope", r), t("bucket", i)),
@@ -57,5 +57,4 @@ var n = "_voltro_webhook_rate_windows", r = 6e4, i = (e, t) => `${e}@${t}`, a =
57
57
  retryInMs: 0
58
58
  };
59
59
  };
60
- //#endregion
61
60
  export { r as RATE_WINDOW_MS, n as RATE_WINDOW_TABLE, l as acquireRateSlots, s as consumeRateSlot, c as releaseRateSlot };
package/dist/retry.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/retry.ts
1
+ // src/retry.ts
2
2
  var e = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, t = (t) => {
3
3
  let n = e.exec(t);
4
4
  if (!n) throw Error(`invalid duration literal: ${t}`);
@@ -61,5 +61,4 @@ var e = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/, t = (t) => {
61
61
  honourRetryAfter: !0,
62
62
  jitter: "full"
63
63
  });
64
- //#endregion
65
64
  export { i as defaultRetryPolicy, a as fastRetryPolicy, r as nextRetry, t as parseDuration };