@voltro/plugin-webhooks 0.22.1 → 0.24.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.d.ts CHANGED
@@ -666,6 +666,183 @@ export declare const verifySignature: (scheme: SignatureScheme, rawBody: Uint8Ar
666
666
  */
667
667
  export declare type VersionState = 'current' | 'behind' | 'ahead';
668
668
 
669
+ /**
670
+ * One row per delivery attempt — NOT per emit. An emit fans out to
671
+ * N targets; each target then runs ≤ `maxAttempts` deliveries. The
672
+ * primary key is `(deliveryId, attempt)`; `deliveryId` is shared
673
+ * across retries of the SAME (event, payload, target) tuple so the
674
+ * dashboard groups them.
675
+ *
676
+ * Status lifecycle: `pending` (queued — the target was paused at emit
677
+ * time, or the attempt is rate-deferred to the next window) →
678
+ * `inFlight` → `succeeded` | `failed` | `retryScheduled`. On retry the
679
+ * workflow creates a new `(deliveryId, attempt+1)` row; on
680
+ * resume/deferral the SAME attempt-1 row transitions out of `pending`.
681
+ */
682
+ export declare const _voltroWebhookDeliveriesTable: Table<"_voltro_webhook_deliveries", FieldDefinitions<{
683
+ readonly id: ColumnBuilder<string, "id", boolean>;
684
+ /** Grouping key for retries of the same delivery. See above. */
685
+ readonly deliveryId: ColumnBuilder<string, "text", boolean>;
686
+ /** FK to `_voltro_webhook_targets.id`. */
687
+ readonly targetId: ColumnBuilder<string, "text", boolean>;
688
+ /** Event id at emit time — denormalised so dashboard listings
689
+ * don't need to JOIN through `_voltro_webhook_targets` (which
690
+ * may have been deleted by the time someone audits this row). */
691
+ readonly event: ColumnBuilder<string, "text", boolean>;
692
+ /** The emit's `eventId` (shared by every target fan-out of one
693
+ * emit) — lets `resumeTarget`'s flush re-trigger a queued delivery
694
+ * with its ORIGINAL event id, and correlates rows across targets. */
695
+ readonly eventId: ColumnBuilder<string | null, "text", boolean>;
696
+ /** Attempt counter (1-indexed). */
697
+ readonly attempt: ColumnBuilder<number, "integer", boolean>;
698
+ readonly status: ColumnBuilder<"failed" | "succeeded" | "pending" | "inFlight" | "retryScheduled", "text", boolean>;
699
+ /** Payload as sent over the wire. Stored verbatim — re-rendering
700
+ * from a referenced event row would lose the snapshot if the
701
+ * source event was deleted. */
702
+ readonly payload: ColumnBuilder<unknown, "json", boolean>;
703
+ /** HTTP status code returned. `null` for transport errors (DNS,
704
+ * TLS, timeout) — `errorMessage` carries the detail. */
705
+ readonly responseStatus: ColumnBuilder<number | null, "integer", boolean>;
706
+ /** Response body sample (clipped to 8 KB). Lets the dashboard
707
+ * show the recipient's error reply inline. */
708
+ readonly responseBody: ColumnBuilder<string | null, "text", boolean>;
709
+ /** Transport-layer error message ("ENOTFOUND", "ETIMEDOUT", TLS
710
+ * handshake failure). Null on HTTP-layer errors (those carry
711
+ * `responseStatus`). */
712
+ readonly errorMessage: ColumnBuilder<string | null, "text", boolean>;
713
+ /** End-to-end attempt latency in ms — includes DNS, TLS, request,
714
+ * response read. Useful for the dashboard's "slowest endpoint"
715
+ * ranking. */
716
+ readonly latencyMs: ColumnBuilder<number | null, "integer", boolean>;
717
+ /** When this attempt was scheduled (NOT when it was sent — sent
718
+ * time is approximately `scheduledAt + queueDelay`). */
719
+ readonly scheduledAt: ColumnBuilder<Date, "timestamp", boolean>;
720
+ /** When the next retry is due (set ONLY when `status =
721
+ * retryScheduled`). Lets the workflow's sleep block read its
722
+ * wake time from the persisted row across restarts. */
723
+ readonly nextAttemptAt: ColumnBuilder<Date | null, "timestamp", boolean>;
724
+ }> & {
725
+ tenantId: ColumnDefinition<string>;
726
+ } & {
727
+ readonly createdAt: ColumnDefinition<Date, "timestamp", true>;
728
+ readonly updatedAt: ColumnDefinition<Date, "timestamp", true>;
729
+ readonly createdBy: ColumnDefinition<string | null, "reference", boolean>;
730
+ readonly updatedBy: ColumnDefinition<string | null, "reference", boolean>;
731
+ }, true, never>;
732
+
733
+ /**
734
+ * Fixed-window rate-limit counters — one row per (scope, minute
735
+ * bucket), where scope is `target:<targetId>` (per-target
736
+ * `rateLimitPerMinute`) or `event:<eventId>` (an outgoing event's
737
+ * `globalRateLimit`). The delivery workflow claims a slot via a CAS
738
+ * loop over `count` (see `rateLimit.ts`) BEFORE every wire POST, so
739
+ * the cap holds across replicas — the counter lives here, never in
740
+ * process memory.
741
+ *
742
+ * Deliberately NOT tenant-scoped: rows carry only a scope key + an
743
+ * integer count (no payload, no secret, no tenant data), and the
744
+ * background delivery workflow that writes them has no request
745
+ * subject. The deterministic PK `<scope>@<bucket>` is the
746
+ * `insertIgnore` conflict target for the first-in-window create race.
747
+ */
748
+ export declare const _voltroWebhookRateWindowsTable: Table<"_voltro_webhook_rate_windows", FieldDefinitions<{
749
+ /** Deterministic `<scope>@<bucket>` — always supplied explicitly by
750
+ * the CAS writer (the prefix scheme only fires for omitted ids,
751
+ * which never happens here). */
752
+ readonly id: ColumnBuilder<string, "id", boolean>;
753
+ /** `target:<targetId>` | `event:<eventId>`. */
754
+ readonly scope: ColumnBuilder<string, "text", boolean>;
755
+ /** Epoch-minute bucket (`floor(now / windowMs)`). */
756
+ readonly bucket: ColumnBuilder<number, "integer", boolean>;
757
+ /** Slots consumed in this window. */
758
+ readonly count: ColumnBuilder<number, "integer", true>;
759
+ readonly createdAt: ColumnBuilder<Date, "timestamp", boolean>;
760
+ readonly updatedAt: ColumnBuilder<Date, "timestamp", boolean>;
761
+ }>, true, never>;
762
+
763
+ /**
764
+ * One row per subscribed delivery target. Created via
765
+ * `webhooks.subscribe(...)`. Read-only from app code; mutate via
766
+ * the `webhooks` service so the framework can run validation +
767
+ * generate secrets + invalidate caches.
768
+ */
769
+ export declare const _voltroWebhookTargetsTable: Table<"_voltro_webhook_targets", FieldDefinitions<{
770
+ readonly id: ColumnBuilder<string, "id", boolean>;
771
+ /** Event id from the outgoing-event descriptor (e.g. `'order.completed'`). */
772
+ readonly event: ColumnBuilder<string, "text", boolean>;
773
+ /** Active subscription URL the delivery posts to. */
774
+ readonly url: ColumnBuilder<string, "text", boolean>;
775
+ /** Per-target signing secret. Generated at subscribe time when not
776
+ * supplied. Stored as-is — encryption is a deployment concern
777
+ * (Postgres-at-rest, KMS-wrapped column, vault sidecar). */
778
+ readonly secret: ColumnBuilder<string, "text", boolean>;
779
+ /** Serialised `SignatureScheme` discriminated union — see `signing.ts`.
780
+ * Stored as JSON so future schemes don't require a schema migration. */
781
+ readonly signing: ColumnBuilder<unknown, "json", boolean>;
782
+ /** Serialised `RetryPolicy` — see `retry.ts`. */
783
+ readonly retry: ColumnBuilder<unknown, "json", boolean>;
784
+ /** Optional predicate filter (subset of `Predicate`) — the engine
785
+ * evaluates this against each emit's payload to decide whether
786
+ * this target receives the delivery. */
787
+ readonly filter: ColumnBuilder<unknown, "json", boolean>;
788
+ /** Optional custom headers merged with the framework's
789
+ * Content-Type + signature header. Values larger than 2 KB are
790
+ * rejected at subscribe time. */
791
+ readonly headers: ColumnBuilder<unknown, "json", boolean>;
792
+ /** Per-target rate limit — at most N wire POSTs per minute to this
793
+ * target, enforced by the delivery workflow via a shared-store
794
+ * fixed-window counter (`_voltro_webhook_rate_windows`, so the cap
795
+ * holds across replicas). Excess deliveries are DEFERRED: parked as
796
+ * `status='pending'` rows and durable-slept until the next window
797
+ * opens — they're never dropped silently. */
798
+ readonly rateLimitPerMinute: ColumnBuilder<number | null, "integer", boolean>;
799
+ /** Soft-disable without deleting the row — the dashboard's
800
+ * "Pause" affordance flips this. While paused, emits against this
801
+ * target accumulate as `_voltro_webhook_deliveries` rows with
802
+ * status `'pending'` (no POST happens); `resumeTarget` flushes
803
+ * them through the delivery workflow in emit order. */
804
+ readonly active: ColumnBuilder<boolean, "boolean", true>;
805
+ /** Format the payload is delivered as. `json` is the default and
806
+ * what every modern integration expects. `form`
807
+ * (`application/x-www-form-urlencoded`, bracketed-key flattening)
808
+ * and `xml` (`application/xml`, `<webhook>`-rooted) exist for
809
+ * SOAP-era partners; the delivery workflow re-encodes the payload
810
+ * into this format and signs the re-encoded bytes. */
811
+ readonly format: ColumnBuilder<"json" | "form" | "xml", "text", true>;
812
+ /** Auto-disable threshold — after this many CONSECUTIVE terminal
813
+ * delivery failures the target is auto-paused (dead-letter guard).
814
+ * `null` (the default) disables the feature. When it trips, the
815
+ * target flips to `active=false` and subsequent emits QUEUE as
816
+ * `status='pending'` rows (same as a manual pause — nothing is
817
+ * dropped); a manual `resumeTarget` re-activates, flushes the queue,
818
+ * and clears the streak. */
819
+ readonly autoDisableAfter: ColumnBuilder<number | null, "integer", boolean>;
820
+ /** Consecutive terminal-failure streak. Incremented on each terminal
821
+ * `failed` delivery, reset to 0 on any `succeeded`. Drives
822
+ * `autoDisableAfter`. Multi-replica-correct via a CAS loop
823
+ * (`autoDisable.ts`). */
824
+ readonly consecutiveFailures: ColumnBuilder<number, "integer", true>;
825
+ /** When the auto-disable last fired (`null` = never / cleared by a
826
+ * manual resume). Surfaced on the inspect panel. */
827
+ readonly autoDisabledAt: ColumnBuilder<Date | null, "timestamp", boolean>;
828
+ /** The terminal failure reason that tripped the auto-disable
829
+ * (`null` = not auto-disabled). Surfaced on the inspect panel. */
830
+ readonly autoDisableReason: ColumnBuilder<string | null, "text", boolean>;
831
+ /** Schema version bound at subscribe time. Lets the dashboard
832
+ * show which targets are still pinned to an older event version
833
+ * after the producer bumps it. */
834
+ readonly payloadVersion: ColumnBuilder<number, "integer", true>;
835
+ /** Human-readable label surfaced in the dashboard listing. */
836
+ readonly description: ColumnBuilder<string | null, "text", boolean>;
837
+ }> & {
838
+ tenantId: ColumnDefinition<string>;
839
+ } & {
840
+ readonly createdAt: ColumnDefinition<Date, "timestamp", true>;
841
+ readonly updatedAt: ColumnDefinition<Date, "timestamp", true>;
842
+ readonly createdBy: ColumnDefinition<string | null, "reference", boolean>;
843
+ readonly updatedBy: ColumnDefinition<string | null, "reference", boolean>;
844
+ }, true, never>;
845
+
669
846
  /**
670
847
  * Thrown by `WebhooksService.replay` when no delivery row exists for the
671
848
  * given `deliveryId` — the row can't be re-triggered because the