@voltro/plugin-webhooks 0.29.0 → 0.30.1

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/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" | "succeeded" | "pending" | "inFlight" | "retryScheduled", "text", boolean>;
35
+ readonly status: ColumnBuilder<"failed" | "succeeded" | "inFlight" | "pending" | "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. */
@@ -67,6 +67,60 @@ export declare const _voltroWebhookDeliveriesTable: Table<"_voltro_webhook_deliv
67
67
  readonly updatedBy: ColumnDefinition<string | null, "reference", boolean>;
68
68
  }, true, never>;
69
69
 
70
+ /**
71
+ * ONE row per declared event, stamped every time `emit` runs.
72
+ *
73
+ * **Why this is not derivable from `_voltro_webhook_deliveries`, which is the
74
+ * whole reason the table exists.** A delivery row is written when an emit MATCHES
75
+ * a target. So "no delivery rows" conflates three different facts:
76
+ *
77
+ * 1. no `emit(...)` call site exists, or none ever ran ← the defect
78
+ * 2. it ran, but nobody was subscribed yet
79
+ * 3. it ran, but every target's `filter` excluded the payload, or every
80
+ * target was paused
81
+ *
82
+ * Only (1) is a bug, and it is the one a consumer spent a week finding by hand:
83
+ * seven of eleven advertised events had no emit call site anywhere. Reading (2)
84
+ * or (3) as (1) turns a working integration into a false alarm; reading (1) as
85
+ * (2) hides it. Delivery history also ages out — `_voltro_webhook_deliveries`
86
+ * carries a 90-day retention — so an event emitted correctly and quietly can
87
+ * decay into looking dead.
88
+ *
89
+ * This row is written REGARDLESS of whether any target matched, which is
90
+ * precisely the axis history cannot see. The dashboard shows both, labelled,
91
+ * and their disagreement is itself the useful signal: emitted but never
92
+ * delivered means every target is paused, filtered out, or failing.
93
+ *
94
+ * Deliberately NOT tenant-scoped. The question is "does this event have a live
95
+ * call site in this deployment", which is a property of the CODE, not of a
96
+ * tenant's data — and scoping it per tenant would make an event look dead for
97
+ * every tenant that has not happened to trigger it yet.
98
+ */
99
+ export declare const _voltroWebhookEventStatsTable: Table<"_voltro_webhook_event_stats", FieldDefinitions<{
100
+ /**
101
+ * A GENERATED id. The event name is the natural key and it is deliberately
102
+ * NOT reused here.
103
+ *
104
+ * An `id()` column is `VARCHAR(64)` on MySQL/MariaDB (`applier.ts`), while an
105
+ * event name is allowed 191. Using the name as the id would make a namespaced
106
+ * event longer than 64 characters fail its insert — and because the stats
107
+ * write is best-effort and swallows every error, it would fail SILENTLY and
108
+ * the dashboard would report "never emitted" for a live event. That is
109
+ * precisely the false positive this table exists to remove, reintroduced by
110
+ * its own primary key, on exactly the dialect that reported the original
111
+ * defect.
112
+ */
113
+ readonly id: ColumnBuilder<string, "id", boolean>;
114
+ /** The natural key. Unique, so a concurrent double-insert loses rather than
115
+ * duplicating the row. */
116
+ readonly event: ColumnBuilder<string, "text", boolean>;
117
+ /** Total emits seen, including those that matched no target. */
118
+ readonly emitCount: ColumnBuilder<number, "integer", true>;
119
+ readonly lastEmitAt: ColumnBuilder<Date, "timestamp", boolean>;
120
+ readonly createdAt: ColumnBuilder<Date, "timestamp", boolean>;
121
+ readonly updatedAt: ColumnBuilder<Date, "timestamp", boolean>;
122
+ }>, true, never>;
123
+
70
124
  /**
71
125
  * Fixed-window rate-limit counters — one row per (scope, minute
72
126
  * bucket), where scope is `target:<targetId>` (per-target
@@ -330,7 +384,7 @@ export declare const webhookTables: () => readonly [ Table<"_voltro_webhook_targ
330
384
  readonly eventId: ColumnBuilder<string | null, "text", boolean>;
331
385
  /** Attempt counter (1-indexed). */
332
386
  readonly attempt: ColumnBuilder<number, "integer", boolean>;
333
- readonly status: ColumnBuilder<"failed" | "succeeded" | "pending" | "inFlight" | "retryScheduled", "text", boolean>;
387
+ readonly status: ColumnBuilder<"failed" | "succeeded" | "inFlight" | "pending" | "retryScheduled", "text", boolean>;
334
388
  /** Payload as sent over the wire. Stored verbatim — re-rendering
335
389
  * from a referenced event row would lose the snapshot if the
336
390
  * source event was deleted. */
@@ -376,6 +430,29 @@ export declare const webhookTables: () => readonly [ Table<"_voltro_webhook_targ
376
430
  readonly count: ColumnBuilder<number, "integer", true>;
377
431
  readonly createdAt: ColumnBuilder<Date, "timestamp", boolean>;
378
432
  readonly updatedAt: ColumnBuilder<Date, "timestamp", boolean>;
433
+ }>, true, never>, Table<"_voltro_webhook_event_stats", FieldDefinitions<{
434
+ /**
435
+ * A GENERATED id. The event name is the natural key and it is deliberately
436
+ * NOT reused here.
437
+ *
438
+ * An `id()` column is `VARCHAR(64)` on MySQL/MariaDB (`applier.ts`), while an
439
+ * event name is allowed 191. Using the name as the id would make a namespaced
440
+ * event longer than 64 characters fail its insert — and because the stats
441
+ * write is best-effort and swallows every error, it would fail SILENTLY and
442
+ * the dashboard would report "never emitted" for a live event. That is
443
+ * precisely the false positive this table exists to remove, reintroduced by
444
+ * its own primary key, on exactly the dialect that reported the original
445
+ * defect.
446
+ */
447
+ readonly id: ColumnBuilder<string, "id", boolean>;
448
+ /** The natural key. Unique, so a concurrent double-insert loses rather than
449
+ * duplicating the row. */
450
+ readonly event: ColumnBuilder<string, "text", boolean>;
451
+ /** Total emits seen, including those that matched no target. */
452
+ readonly emitCount: ColumnBuilder<number, "integer", true>;
453
+ readonly lastEmitAt: ColumnBuilder<Date, "timestamp", boolean>;
454
+ readonly createdAt: ColumnBuilder<Date, "timestamp", boolean>;
455
+ readonly updatedAt: ColumnBuilder<Date, "timestamp", boolean>;
379
456
  }>, true, never>];
380
457
 
381
458
  export { }
package/dist/mixin.js CHANGED
@@ -52,7 +52,14 @@ var u = o("_voltro_webhook_targets", {
52
52
  count: n().default(0),
53
53
  createdAt: c(),
54
54
  updatedAt: c()
55
- }), p = () => (i({
55
+ }), p = o("_voltro_webhook_event_stats", {
56
+ id: t({ prefix: "whstat" }),
57
+ event: s().maxLength(191).unique(),
58
+ emitCount: n().default(0),
59
+ lastEmitAt: c(),
60
+ createdAt: c(),
61
+ updatedAt: c()
62
+ }), m = () => (i({
56
63
  table: "_voltro_webhook_deliveries",
57
64
  timeColumn: "createdAt",
58
65
  ttlMs: a(process.env.VOLTRO_WEBHOOK_DELIVERIES_TTL_HOURS, 2160)
@@ -63,7 +70,8 @@ var u = o("_voltro_webhook_targets", {
63
70
  }), [
64
71
  u,
65
72
  d,
66
- f
73
+ f,
74
+ p
67
75
  ]);
68
76
  //#endregion
69
- export { d as _voltroWebhookDeliveriesTable, f as _voltroWebhookRateWindowsTable, u as _voltroWebhookTargetsTable, p as webhookTables };
77
+ export { d as _voltroWebhookDeliveriesTable, p as _voltroWebhookEventStatsTable, f as _voltroWebhookRateWindowsTable, u as _voltroWebhookTargetsTable, m as webhookTables };
@@ -1,4 +1,5 @@
1
1
  import { Schema } from 'effect';
2
+ import { WorkflowRunHandle } from '@voltro/protocol';
2
3
 
3
4
  declare interface CustomSignatureScheme {
4
5
  readonly _tag: 'custom';
@@ -106,12 +107,7 @@ declare interface IncomingWebhookDescriptor<Body> {
106
107
  }
107
108
 
108
109
  declare interface IncomingWorkflowFacade {
109
- start<Payload = unknown>(workflowName: string, payload: Payload): Promise<{
110
- readonly id: string;
111
- readonly workflowName: string;
112
- readonly executionId: string;
113
- readonly status: 'running';
114
- }>;
110
+ start<Payload = unknown>(workflowName: string, payload: Payload): Promise<WorkflowRunHandle>;
115
111
  signal(target: {
116
112
  readonly id?: string;
117
113
  readonly executionId?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-webhooks",
3
- "version": "0.29.0",
3
+ "version": "0.30.1",
4
4
  "description": "Webhooks plugin — first-class outgoing (multi-target, durable-workflow delivery, HMAC signing, retry policies, rate limits) + incoming (signature verification, idempotency, provider templates for Stripe / GitHub / Slack / generic).",
5
5
  "keywords": [
6
6
  "voltro",
@@ -48,12 +48,12 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@effect/workflow": "^0.19.0",
51
- "@voltro/database": "0.29.0",
52
- "@voltro/integration-http": "0.29.0",
53
- "@voltro/logger": "0.29.0",
54
- "@voltro/plugin-multitenancy": "0.29.0",
55
- "@voltro/protocol": "0.29.0",
56
- "@voltro/runtime": "0.29.0"
51
+ "@voltro/database": "0.30.1",
52
+ "@voltro/integration-http": "0.30.1",
53
+ "@voltro/logger": "0.30.1",
54
+ "@voltro/plugin-multitenancy": "0.30.1",
55
+ "@voltro/protocol": "0.30.1",
56
+ "@voltro/runtime": "0.30.1"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "effect": "^3.22.0"