@marktiderman/genesis-forms 0.8.0 → 0.9.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.
Files changed (36) hide show
  1. package/README.md +299 -10
  2. package/dist/destinations.cjs +331 -114
  3. package/dist/destinations.cjs.map +1 -1
  4. package/dist/destinations.d.cts +67 -45
  5. package/dist/destinations.d.ts +67 -45
  6. package/dist/destinations.js +327 -111
  7. package/dist/destinations.js.map +1 -1
  8. package/dist/{engine-Bj23YJlB.d.cts → engine-BaHPC2-1.d.cts} +2 -2
  9. package/dist/{engine-CzinTJph.d.ts → engine-Da99Tv-E.d.ts} +2 -2
  10. package/dist/index.cjs +996 -167
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +288 -11
  13. package/dist/index.d.ts +288 -11
  14. package/dist/index.js +975 -168
  15. package/dist/index.js.map +1 -1
  16. package/dist/server.cjs +563 -173
  17. package/dist/server.cjs.map +1 -1
  18. package/dist/server.d.cts +3 -474
  19. package/dist/server.d.ts +3 -474
  20. package/dist/server.js +558 -174
  21. package/dist/server.js.map +1 -1
  22. package/dist/submit-protocol-CFi-0Qd0.d.ts +840 -0
  23. package/dist/submit-protocol-D0cOckmK.d.cts +840 -0
  24. package/dist/testing.cjs +250 -154
  25. package/dist/testing.cjs.map +1 -1
  26. package/dist/testing.d.cts +4 -4
  27. package/dist/testing.d.ts +4 -4
  28. package/dist/testing.js +250 -154
  29. package/dist/testing.js.map +1 -1
  30. package/dist/{types-jqDKcmP4.d.cts → types-C0pUfew3.d.cts} +7 -1
  31. package/dist/{types-jqDKcmP4.d.ts → types-C0pUfew3.d.ts} +7 -1
  32. package/dist/{validate-DvfQHYnu.d.ts → validate-DpmBssFk.d.ts} +258 -140
  33. package/dist/{validate-irIPubxc.d.cts → validate-U5LoW3eR.d.cts} +258 -140
  34. package/package.json +3 -2
  35. package/dist/upload-protocol-BOe018kL.d.cts +0 -122
  36. package/dist/upload-protocol-BUO74WkS.d.ts +0 -122
@@ -1,4 +1,4 @@
1
- import { J as Json, F as FormDefinition, a as FieldBlock, C as Condition } from './types-jqDKcmP4.cjs';
1
+ import { J as Json, F as FormDefinition, a as FieldBlock, C as Condition } from './types-C0pUfew3.cjs';
2
2
 
3
3
  /**
4
4
  * The contract every destination keeps. A destination sends one checked
@@ -103,6 +103,12 @@ interface DeliveryFailure {
103
103
  retryAfterSeconds?: number;
104
104
  /** The HTTP status that refused it, if one did. */
105
105
  status?: number;
106
+ /**
107
+ * The receiver asked to be switched off (a webhook answered 410 Gone, as
108
+ * Standard Webhooks specifies): disable the destination rather than keep
109
+ * sending to it. Always with `retryable: false`.
110
+ */
111
+ disable?: true;
106
112
  /** Safe to store and log: a status and a short reason, never secrets, answers or contact details. */
107
113
  error: string;
108
114
  }
@@ -129,10 +135,13 @@ declare class DeliveryError extends Error {
129
135
  readonly retryable: boolean;
130
136
  readonly retryAfterSeconds: number | undefined;
131
137
  readonly status: number | undefined;
138
+ /** The receiver asked to be switched off (`DeliveryFailure.disable`); never retryable. */
139
+ readonly disable: boolean;
132
140
  constructor(message: string, options?: {
133
141
  retryable?: boolean | undefined;
134
142
  retryAfterSeconds?: number | undefined;
135
143
  status?: number | undefined;
144
+ disable?: boolean | undefined;
136
145
  });
137
146
  }
138
147
  /** Every outbound request gives up after this long, unless the destination is given another `timeoutMs`. */
@@ -467,15 +476,17 @@ interface WebhookConfig {
467
476
  }
468
477
  interface WebhookDestinationOptions extends HttpDeps {
469
478
  /**
470
- * The signing secret: base64 of at least 16 random bytes, usually with
471
- * the `whsec_` prefix Standard Webhooks tools issue (`whsec_` plus base64
472
- * of 24 to 64 bytes is what the spec recommends). The prefix is optional;
473
- * the rest is always base64-decoded, as every Standard Webhooks library
474
- * does, so a receiver verifies with the same string. A secret that is not
475
- * base64 fails permanently. Or a function that finds the one for a
476
- * destination's config. Never put it in config.
479
+ * The signing secret: base64 of at least 24 random bytes (the spec
480
+ * recommends 24 to 64), usually with the `whsec_` prefix Standard Webhooks
481
+ * tools issue. The prefix is optional; the rest is always base64-decoded,
482
+ * as every Standard Webhooks library does, so a receiver verifies with the
483
+ * same string. A secret that is not base64, or is shorter, fails
484
+ * permanently. Several secrets (new and old, during a rotation) sign with
485
+ * each, space-separated in `webhook-signature`, so the receiver verifies
486
+ * with either. Or a function that finds them for a destination's config.
487
+ * Never put it in config.
477
488
  */
478
- secret: string | OptionResolver<string>;
489
+ secret: string | readonly string[] | OptionResolver<string | readonly string[]>;
479
490
  /** Allow `http:` URLs. Default false. */
480
491
  allowInsecureUrls?: boolean | undefined;
481
492
  /** Allow localhost and private, loopback and link-local address literals. Default false. */
@@ -502,7 +513,10 @@ interface WebhookDestinationOptions extends HttpDeps {
502
513
  /** The JSON body a webhook receives. */
503
514
  interface WebhookPayload {
504
515
  type: string;
505
- /** ISO time the delivery was sent. */
516
+ /**
517
+ * ISO time the event happened: the submission's `submittedAt`, or the send
518
+ * time when it has none. The `webhook-timestamp` header is the send time.
519
+ */
506
520
  timestamp: string;
507
521
  data: {
508
522
  form: {
@@ -524,17 +538,26 @@ interface WebhookPayload {
524
538
  row: Record<string, Json>;
525
539
  };
526
540
  }
541
+ /** The shortest signing secret, decoded: Standard Webhooks recommends 24 to 64 bytes. */
542
+ declare const MIN_WEBHOOK_SECRET_BYTES = 24;
527
543
  /**
528
544
  * A new signing secret: `whsec_` and standard base64 of 32 random bytes, the
529
545
  * form Standard Webhooks libraries read. Show it to the receiver once; keep it
530
546
  * out of config.
531
547
  */
532
548
  declare function newWebhookSecret(): string;
533
- /** The `webhook-signature` value for a message: `v1,<base64>`. */
534
- declare function signWebhook(secret: string, id: string, timestamp: number, body: string): Promise<string>;
549
+ /**
550
+ * The `webhook-signature` value for a message: `v1,<base64>`, or one such
551
+ * signature per secret, space-separated, when given several (a rotation).
552
+ */
553
+ declare function signWebhook(secret: string | readonly string[], id: string, timestamp: number, body: string): Promise<string>;
535
554
  interface VerifyWebhookInput {
536
- /** The same secret the sender signs with (base64, optionally `whsec_`-prefixed). */
537
- secret: string;
555
+ /**
556
+ * The same secret the sender signs with (base64, optionally
557
+ * `whsec_`-prefixed), or several (new and old, during a rotation): any one
558
+ * matching signature passes.
559
+ */
560
+ secret: string | readonly string[];
538
561
  /** The request's headers: a `Headers` or a plain object. */
539
562
  headers: Headers | Record<string, string | string[] | undefined>;
540
563
  /** The raw body, exactly as received. */
@@ -682,12 +705,25 @@ declare function googleOAuthAccessToken(client: GoogleOAuthClient, refreshToken:
682
705
  * problem, never the key.
683
706
  */
684
707
  declare function secretKeyBytes(key: string | Uint8Array): Uint8Array<ArrayBuffer>;
708
+ /** Options for `deriveSecretKey`. */
709
+ interface DeriveSecretKeyOptions {
710
+ /**
711
+ * The HKDF salt: bytes, or text taken as its UTF-8 bytes. Default empty,
712
+ * which is what every key derived before this option existed used, so
713
+ * leave it out to open those. For new keys a fixed, app-wide random value
714
+ * (say 32 bytes from `openssl rand -base64 32`, kept as config) is
715
+ * recommended (RFC 5869). It need not be secret, but must never change
716
+ * once anything is sealed with it.
717
+ */
718
+ salt?: string | Uint8Array | undefined;
719
+ }
685
720
  /**
686
721
  * An AES-GCM key for one purpose, derived from the app secret with HKDF-SHA-256
687
- * (empty salt, `info` naming the purpose). Use a different `info` for each
688
- * kind of secret; keep it fixed once anything is sealed with it.
722
+ * (`info` naming the purpose; the salt empty unless `options.salt` is given).
723
+ * Use a different `info` for each kind of secret; keep it, and the salt,
724
+ * fixed once anything is sealed with them.
689
725
  */
690
- declare function deriveSecretKey(key: string | Uint8Array, info: string): Promise<WebCryptoKey>;
726
+ declare function deriveSecretKey(key: string | Uint8Array, info: string, options?: DeriveSecretKeyOptions): Promise<WebCryptoKey>;
691
727
  /** Encrypts `plaintext` bound to `aad` (the row it belongs to): `v1.<iv>.<ciphertext>`. */
692
728
  declare function sealSecret(key: WebCryptoKey, plaintext: string, aad: string): Promise<string>;
693
729
  /**
@@ -697,28 +733,6 @@ declare function sealSecret(key: WebCryptoKey, plaintext: string, aad: string):
697
733
  */
698
734
  declare function openSecret(key: WebCryptoKey, sealed: string, aad: string): Promise<string>;
699
735
 
700
- /**
701
- * The outbox runner: claim a batch of due deliveries, send each to its
702
- * destination, and record what happened. The store is the app's (a SQL
703
- * outbox, a queue, a KV list): it hands over claimed deliveries and takes
704
- * back outcomes, and decides how claims and leases work. The runner owns the
705
- * rest: the destination's condition (`config.when`), batching deliveries to
706
- * one destination into one request where the destination can, keeping its
707
- * state, and turning each result into an outcome with a retry time.
708
- *
709
- * Outcomes:
710
- * - `sent`: delivered (or already there, on a redelivery).
711
- * - `filtered`: the submission doesn't match the destination's condition; nothing was sent, nothing to retry.
712
- * - `skipped`: the destination deliberately sent nothing (`notSent`); never retried.
713
- * - `retry`: a retryable failure. With the service's own Retry-After it is
714
- * deferred without counting an attempt (`counted: false`); otherwise it backs
715
- * off by attempt (`counted: true`), until `maxAttempts`.
716
- * - `failed`: permanent, an unknown kind, or out of attempts.
717
- *
718
- * It never throws for a delivery's failure, and never logs answers, contact
719
- * details or secrets: only ids, kinds and the destinations' safe messages.
720
- */
721
-
722
736
  /** One claimed delivery, as the app's store hands it over. */
723
737
  interface ClaimedDelivery {
724
738
  /** The delivery's id: stable across its retries (the email idempotency key, the webhook-id). */
@@ -772,6 +786,8 @@ type DeliveryOutcome = {
772
786
  status: "failed";
773
787
  error: string;
774
788
  httpStatus?: number;
789
+ /** The receiver asked to be switched off (a webhook's 410 Gone): disable the destination. */
790
+ disable?: true;
775
791
  };
776
792
  interface ClaimRequest<Filter> {
777
793
  /** Most deliveries to claim. */
@@ -834,11 +850,17 @@ interface DeliveryRunner<Filter = undefined> {
834
850
  /** One claim, its sends and their records. Never rejects for a delivery's failure; a failing `claim` rejects. */
835
851
  run(options?: RunOptions<Filter>): Promise<DeliveryReport>;
836
852
  }
837
- declare const DEFAULT_MAX_ATTEMPTS = 8;
838
- declare const DEFAULT_MAX_DEFERRALS = 24;
839
- declare const DEFAULT_BATCH_SIZE = 25;
840
- /** 1 minute, doubling each attempt, at most 6 hours: 1m, 2m, 4m … 6h. */
841
- declare function defaultBackoff(attempt: number): number;
853
+ declare const DEFAULT_MAX_ATTEMPTS: number;
854
+ declare const DEFAULT_MAX_DEFERRALS: number;
855
+ declare const DEFAULT_BATCH_SIZE: number;
856
+ /**
857
+ * 5 minutes, quadrupling each attempt, at most 24 hours: 5m, 20m, 80m, 5h20m,
858
+ * 21h20m, then 24h. Across the default 8 attempts that is about 3 days, as
859
+ * Standard Webhooks suggests. Each wait is jittered by up to ±20%, so
860
+ * deliveries that failed together (a receiver's outage) don't all come back
861
+ * at once. `random` (default `Math.random`) is for tests.
862
+ */
863
+ declare const defaultBackoff: (attempt: number, random?: () => number) => number;
842
864
  /** Turns a destination's result into an outcome for a delivery on its `attempt`. */
843
865
  declare function deliveryOutcome(result: DeliveryResult, attempt: number, options?: {
844
866
  now?: number | undefined;
@@ -850,4 +872,4 @@ declare function deliveryOutcome(result: DeliveryResult, attempt: number, option
850
872
  }): DeliveryOutcome;
851
873
  declare function createDeliveryRunner<Filter = undefined>(options: DeliveryRunnerOptions<Filter>): DeliveryRunner<Filter>;
852
874
 
853
- export { type Cell, type ClaimRequest, type ClaimedDelivery, DEDUPE_WINDOW, DEFAULT_BATCH_SIZE, DEFAULT_MAX_ATTEMPTS, DEFAULT_MAX_DEFERRALS, DEFAULT_SHEET_NAME, DRIVE_FILE_SCOPE, DeliveryError, type DeliveryFailure, type DeliveryItem, type DeliveryOptions, type DeliveryOutcome, type DeliveryReport, type DeliveryResult, type DeliveryRunner, type DeliveryRunnerOptions, type DeliverySubmission, type Destination, type DestinationConfig, type EmailConfig, type EmailContent, type EmailDestinationOptions, type EmailMessage, type EmailProvider, type EmailRenderInput, GOOGLE_TOKEN_URL, type GoogleOAuthClient, type GoogleOAuthTokenOptions, type GoogleServiceAccount, type GoogleTokenOptions, HEADER_SCAN_ROWS, type HookKind, HookUrlError, type HttpDeps, MAX_CONDITION_DEPTH, MAX_HOOK_URL_LENGTH, MAX_RECIPIENTS, type OptionResolver, REQUEST_TIMEOUT_MS, RESEND_API, type RefreshedGoogleToken, type ResendOptions, type ResolveContext, type ResolveHost, type ResponseRow, type RowOptions, type RunOptions, SENSITIVE_MASK, SHEETS_API, SHEETS_SCOPE, SLACK_HOSTS, SLACK_VALUE_LIMIT, type SheetConfig, type SheetState, type SheetsDestinationOptions, type SlackDestinationOptions, type SlackMessageInput, type SlackMessageOptions, type VerifyWebhookInput, type WebCryptoKey, type WebhookConfig, type WebhookDestinationOptions, type WebhookPayload, answerCell, checkHookHost, checkWebhookUrl, clearGoogleOAuthTokenCache, clearGoogleTokenCache, columnLetter, conditionOf, createDeliveryRunner, defaultBackoff, deliveryFailure, deliveryOutcome, deriveSecretKey, emailBody, emailDestination, escapeFormula, escapeHtml, escapeSlack, googleAccessToken, googleOAuthAccessToken, headerSafe, hookHost, httpError, idempotencyKey, isCondition, isPrivateHost, isPublicAddress, isRetryableStatus, isSafeAddress, isSafeFrom, mergeHeader, newWebhookSecret, openSecret, parseHookUrl, passesCondition, refreshGoogleAccessToken, renderEmail, resendProvider, responseRow, retryAfterSeconds, rowFor, sealSecret, secretKeyBytes, sheetsDestination, signJwt, signWebhook, slackDestination, slackMessage, verifyWebhook, visibleInputs, webhookDestination, webhookPayload };
875
+ export { type Cell, type ClaimRequest, type ClaimedDelivery, DEDUPE_WINDOW, DEFAULT_BATCH_SIZE, DEFAULT_MAX_ATTEMPTS, DEFAULT_MAX_DEFERRALS, DEFAULT_SHEET_NAME, DRIVE_FILE_SCOPE, DeliveryError, type DeliveryFailure, type DeliveryItem, type DeliveryOptions, type DeliveryOutcome, type DeliveryReport, type DeliveryResult, type DeliveryRunner, type DeliveryRunnerOptions, type DeliverySubmission, type DeriveSecretKeyOptions, type Destination, type DestinationConfig, type EmailConfig, type EmailContent, type EmailDestinationOptions, type EmailMessage, type EmailProvider, type EmailRenderInput, GOOGLE_TOKEN_URL, type GoogleOAuthClient, type GoogleOAuthTokenOptions, type GoogleServiceAccount, type GoogleTokenOptions, HEADER_SCAN_ROWS, type HookKind, HookUrlError, type HttpDeps, MAX_CONDITION_DEPTH, MAX_HOOK_URL_LENGTH, MAX_RECIPIENTS, MIN_WEBHOOK_SECRET_BYTES, type OptionResolver, REQUEST_TIMEOUT_MS, RESEND_API, type RefreshedGoogleToken, type ResendOptions, type ResolveContext, type ResolveHost, type ResponseRow, type RowOptions, type RunOptions, SENSITIVE_MASK, SHEETS_API, SHEETS_SCOPE, SLACK_HOSTS, SLACK_VALUE_LIMIT, type SheetConfig, type SheetState, type SheetsDestinationOptions, type SlackDestinationOptions, type SlackMessageInput, type SlackMessageOptions, type VerifyWebhookInput, type WebCryptoKey, type WebhookConfig, type WebhookDestinationOptions, type WebhookPayload, answerCell, checkHookHost, checkWebhookUrl, clearGoogleOAuthTokenCache, clearGoogleTokenCache, columnLetter, conditionOf, createDeliveryRunner, defaultBackoff, deliveryFailure, deliveryOutcome, deriveSecretKey, emailBody, emailDestination, escapeFormula, escapeHtml, escapeSlack, googleAccessToken, googleOAuthAccessToken, headerSafe, hookHost, httpError, idempotencyKey, isCondition, isPrivateHost, isPublicAddress, isRetryableStatus, isSafeAddress, isSafeFrom, mergeHeader, newWebhookSecret, openSecret, parseHookUrl, passesCondition, refreshGoogleAccessToken, renderEmail, resendProvider, responseRow, retryAfterSeconds, rowFor, sealSecret, secretKeyBytes, sheetsDestination, signJwt, signWebhook, slackDestination, slackMessage, verifyWebhook, visibleInputs, webhookDestination, webhookPayload };
@@ -1,4 +1,4 @@
1
- import { J as Json, F as FormDefinition, a as FieldBlock, C as Condition } from './types-jqDKcmP4.js';
1
+ import { J as Json, F as FormDefinition, a as FieldBlock, C as Condition } from './types-C0pUfew3.js';
2
2
 
3
3
  /**
4
4
  * The contract every destination keeps. A destination sends one checked
@@ -103,6 +103,12 @@ interface DeliveryFailure {
103
103
  retryAfterSeconds?: number;
104
104
  /** The HTTP status that refused it, if one did. */
105
105
  status?: number;
106
+ /**
107
+ * The receiver asked to be switched off (a webhook answered 410 Gone, as
108
+ * Standard Webhooks specifies): disable the destination rather than keep
109
+ * sending to it. Always with `retryable: false`.
110
+ */
111
+ disable?: true;
106
112
  /** Safe to store and log: a status and a short reason, never secrets, answers or contact details. */
107
113
  error: string;
108
114
  }
@@ -129,10 +135,13 @@ declare class DeliveryError extends Error {
129
135
  readonly retryable: boolean;
130
136
  readonly retryAfterSeconds: number | undefined;
131
137
  readonly status: number | undefined;
138
+ /** The receiver asked to be switched off (`DeliveryFailure.disable`); never retryable. */
139
+ readonly disable: boolean;
132
140
  constructor(message: string, options?: {
133
141
  retryable?: boolean | undefined;
134
142
  retryAfterSeconds?: number | undefined;
135
143
  status?: number | undefined;
144
+ disable?: boolean | undefined;
136
145
  });
137
146
  }
138
147
  /** Every outbound request gives up after this long, unless the destination is given another `timeoutMs`. */
@@ -467,15 +476,17 @@ interface WebhookConfig {
467
476
  }
468
477
  interface WebhookDestinationOptions extends HttpDeps {
469
478
  /**
470
- * The signing secret: base64 of at least 16 random bytes, usually with
471
- * the `whsec_` prefix Standard Webhooks tools issue (`whsec_` plus base64
472
- * of 24 to 64 bytes is what the spec recommends). The prefix is optional;
473
- * the rest is always base64-decoded, as every Standard Webhooks library
474
- * does, so a receiver verifies with the same string. A secret that is not
475
- * base64 fails permanently. Or a function that finds the one for a
476
- * destination's config. Never put it in config.
479
+ * The signing secret: base64 of at least 24 random bytes (the spec
480
+ * recommends 24 to 64), usually with the `whsec_` prefix Standard Webhooks
481
+ * tools issue. The prefix is optional; the rest is always base64-decoded,
482
+ * as every Standard Webhooks library does, so a receiver verifies with the
483
+ * same string. A secret that is not base64, or is shorter, fails
484
+ * permanently. Several secrets (new and old, during a rotation) sign with
485
+ * each, space-separated in `webhook-signature`, so the receiver verifies
486
+ * with either. Or a function that finds them for a destination's config.
487
+ * Never put it in config.
477
488
  */
478
- secret: string | OptionResolver<string>;
489
+ secret: string | readonly string[] | OptionResolver<string | readonly string[]>;
479
490
  /** Allow `http:` URLs. Default false. */
480
491
  allowInsecureUrls?: boolean | undefined;
481
492
  /** Allow localhost and private, loopback and link-local address literals. Default false. */
@@ -502,7 +513,10 @@ interface WebhookDestinationOptions extends HttpDeps {
502
513
  /** The JSON body a webhook receives. */
503
514
  interface WebhookPayload {
504
515
  type: string;
505
- /** ISO time the delivery was sent. */
516
+ /**
517
+ * ISO time the event happened: the submission's `submittedAt`, or the send
518
+ * time when it has none. The `webhook-timestamp` header is the send time.
519
+ */
506
520
  timestamp: string;
507
521
  data: {
508
522
  form: {
@@ -524,17 +538,26 @@ interface WebhookPayload {
524
538
  row: Record<string, Json>;
525
539
  };
526
540
  }
541
+ /** The shortest signing secret, decoded: Standard Webhooks recommends 24 to 64 bytes. */
542
+ declare const MIN_WEBHOOK_SECRET_BYTES = 24;
527
543
  /**
528
544
  * A new signing secret: `whsec_` and standard base64 of 32 random bytes, the
529
545
  * form Standard Webhooks libraries read. Show it to the receiver once; keep it
530
546
  * out of config.
531
547
  */
532
548
  declare function newWebhookSecret(): string;
533
- /** The `webhook-signature` value for a message: `v1,<base64>`. */
534
- declare function signWebhook(secret: string, id: string, timestamp: number, body: string): Promise<string>;
549
+ /**
550
+ * The `webhook-signature` value for a message: `v1,<base64>`, or one such
551
+ * signature per secret, space-separated, when given several (a rotation).
552
+ */
553
+ declare function signWebhook(secret: string | readonly string[], id: string, timestamp: number, body: string): Promise<string>;
535
554
  interface VerifyWebhookInput {
536
- /** The same secret the sender signs with (base64, optionally `whsec_`-prefixed). */
537
- secret: string;
555
+ /**
556
+ * The same secret the sender signs with (base64, optionally
557
+ * `whsec_`-prefixed), or several (new and old, during a rotation): any one
558
+ * matching signature passes.
559
+ */
560
+ secret: string | readonly string[];
538
561
  /** The request's headers: a `Headers` or a plain object. */
539
562
  headers: Headers | Record<string, string | string[] | undefined>;
540
563
  /** The raw body, exactly as received. */
@@ -682,12 +705,25 @@ declare function googleOAuthAccessToken(client: GoogleOAuthClient, refreshToken:
682
705
  * problem, never the key.
683
706
  */
684
707
  declare function secretKeyBytes(key: string | Uint8Array): Uint8Array<ArrayBuffer>;
708
+ /** Options for `deriveSecretKey`. */
709
+ interface DeriveSecretKeyOptions {
710
+ /**
711
+ * The HKDF salt: bytes, or text taken as its UTF-8 bytes. Default empty,
712
+ * which is what every key derived before this option existed used, so
713
+ * leave it out to open those. For new keys a fixed, app-wide random value
714
+ * (say 32 bytes from `openssl rand -base64 32`, kept as config) is
715
+ * recommended (RFC 5869). It need not be secret, but must never change
716
+ * once anything is sealed with it.
717
+ */
718
+ salt?: string | Uint8Array | undefined;
719
+ }
685
720
  /**
686
721
  * An AES-GCM key for one purpose, derived from the app secret with HKDF-SHA-256
687
- * (empty salt, `info` naming the purpose). Use a different `info` for each
688
- * kind of secret; keep it fixed once anything is sealed with it.
722
+ * (`info` naming the purpose; the salt empty unless `options.salt` is given).
723
+ * Use a different `info` for each kind of secret; keep it, and the salt,
724
+ * fixed once anything is sealed with them.
689
725
  */
690
- declare function deriveSecretKey(key: string | Uint8Array, info: string): Promise<WebCryptoKey>;
726
+ declare function deriveSecretKey(key: string | Uint8Array, info: string, options?: DeriveSecretKeyOptions): Promise<WebCryptoKey>;
691
727
  /** Encrypts `plaintext` bound to `aad` (the row it belongs to): `v1.<iv>.<ciphertext>`. */
692
728
  declare function sealSecret(key: WebCryptoKey, plaintext: string, aad: string): Promise<string>;
693
729
  /**
@@ -697,28 +733,6 @@ declare function sealSecret(key: WebCryptoKey, plaintext: string, aad: string):
697
733
  */
698
734
  declare function openSecret(key: WebCryptoKey, sealed: string, aad: string): Promise<string>;
699
735
 
700
- /**
701
- * The outbox runner: claim a batch of due deliveries, send each to its
702
- * destination, and record what happened. The store is the app's (a SQL
703
- * outbox, a queue, a KV list): it hands over claimed deliveries and takes
704
- * back outcomes, and decides how claims and leases work. The runner owns the
705
- * rest: the destination's condition (`config.when`), batching deliveries to
706
- * one destination into one request where the destination can, keeping its
707
- * state, and turning each result into an outcome with a retry time.
708
- *
709
- * Outcomes:
710
- * - `sent`: delivered (or already there, on a redelivery).
711
- * - `filtered`: the submission doesn't match the destination's condition; nothing was sent, nothing to retry.
712
- * - `skipped`: the destination deliberately sent nothing (`notSent`); never retried.
713
- * - `retry`: a retryable failure. With the service's own Retry-After it is
714
- * deferred without counting an attempt (`counted: false`); otherwise it backs
715
- * off by attempt (`counted: true`), until `maxAttempts`.
716
- * - `failed`: permanent, an unknown kind, or out of attempts.
717
- *
718
- * It never throws for a delivery's failure, and never logs answers, contact
719
- * details or secrets: only ids, kinds and the destinations' safe messages.
720
- */
721
-
722
736
  /** One claimed delivery, as the app's store hands it over. */
723
737
  interface ClaimedDelivery {
724
738
  /** The delivery's id: stable across its retries (the email idempotency key, the webhook-id). */
@@ -772,6 +786,8 @@ type DeliveryOutcome = {
772
786
  status: "failed";
773
787
  error: string;
774
788
  httpStatus?: number;
789
+ /** The receiver asked to be switched off (a webhook's 410 Gone): disable the destination. */
790
+ disable?: true;
775
791
  };
776
792
  interface ClaimRequest<Filter> {
777
793
  /** Most deliveries to claim. */
@@ -834,11 +850,17 @@ interface DeliveryRunner<Filter = undefined> {
834
850
  /** One claim, its sends and their records. Never rejects for a delivery's failure; a failing `claim` rejects. */
835
851
  run(options?: RunOptions<Filter>): Promise<DeliveryReport>;
836
852
  }
837
- declare const DEFAULT_MAX_ATTEMPTS = 8;
838
- declare const DEFAULT_MAX_DEFERRALS = 24;
839
- declare const DEFAULT_BATCH_SIZE = 25;
840
- /** 1 minute, doubling each attempt, at most 6 hours: 1m, 2m, 4m … 6h. */
841
- declare function defaultBackoff(attempt: number): number;
853
+ declare const DEFAULT_MAX_ATTEMPTS: number;
854
+ declare const DEFAULT_MAX_DEFERRALS: number;
855
+ declare const DEFAULT_BATCH_SIZE: number;
856
+ /**
857
+ * 5 minutes, quadrupling each attempt, at most 24 hours: 5m, 20m, 80m, 5h20m,
858
+ * 21h20m, then 24h. Across the default 8 attempts that is about 3 days, as
859
+ * Standard Webhooks suggests. Each wait is jittered by up to ±20%, so
860
+ * deliveries that failed together (a receiver's outage) don't all come back
861
+ * at once. `random` (default `Math.random`) is for tests.
862
+ */
863
+ declare const defaultBackoff: (attempt: number, random?: () => number) => number;
842
864
  /** Turns a destination's result into an outcome for a delivery on its `attempt`. */
843
865
  declare function deliveryOutcome(result: DeliveryResult, attempt: number, options?: {
844
866
  now?: number | undefined;
@@ -850,4 +872,4 @@ declare function deliveryOutcome(result: DeliveryResult, attempt: number, option
850
872
  }): DeliveryOutcome;
851
873
  declare function createDeliveryRunner<Filter = undefined>(options: DeliveryRunnerOptions<Filter>): DeliveryRunner<Filter>;
852
874
 
853
- export { type Cell, type ClaimRequest, type ClaimedDelivery, DEDUPE_WINDOW, DEFAULT_BATCH_SIZE, DEFAULT_MAX_ATTEMPTS, DEFAULT_MAX_DEFERRALS, DEFAULT_SHEET_NAME, DRIVE_FILE_SCOPE, DeliveryError, type DeliveryFailure, type DeliveryItem, type DeliveryOptions, type DeliveryOutcome, type DeliveryReport, type DeliveryResult, type DeliveryRunner, type DeliveryRunnerOptions, type DeliverySubmission, type Destination, type DestinationConfig, type EmailConfig, type EmailContent, type EmailDestinationOptions, type EmailMessage, type EmailProvider, type EmailRenderInput, GOOGLE_TOKEN_URL, type GoogleOAuthClient, type GoogleOAuthTokenOptions, type GoogleServiceAccount, type GoogleTokenOptions, HEADER_SCAN_ROWS, type HookKind, HookUrlError, type HttpDeps, MAX_CONDITION_DEPTH, MAX_HOOK_URL_LENGTH, MAX_RECIPIENTS, type OptionResolver, REQUEST_TIMEOUT_MS, RESEND_API, type RefreshedGoogleToken, type ResendOptions, type ResolveContext, type ResolveHost, type ResponseRow, type RowOptions, type RunOptions, SENSITIVE_MASK, SHEETS_API, SHEETS_SCOPE, SLACK_HOSTS, SLACK_VALUE_LIMIT, type SheetConfig, type SheetState, type SheetsDestinationOptions, type SlackDestinationOptions, type SlackMessageInput, type SlackMessageOptions, type VerifyWebhookInput, type WebCryptoKey, type WebhookConfig, type WebhookDestinationOptions, type WebhookPayload, answerCell, checkHookHost, checkWebhookUrl, clearGoogleOAuthTokenCache, clearGoogleTokenCache, columnLetter, conditionOf, createDeliveryRunner, defaultBackoff, deliveryFailure, deliveryOutcome, deriveSecretKey, emailBody, emailDestination, escapeFormula, escapeHtml, escapeSlack, googleAccessToken, googleOAuthAccessToken, headerSafe, hookHost, httpError, idempotencyKey, isCondition, isPrivateHost, isPublicAddress, isRetryableStatus, isSafeAddress, isSafeFrom, mergeHeader, newWebhookSecret, openSecret, parseHookUrl, passesCondition, refreshGoogleAccessToken, renderEmail, resendProvider, responseRow, retryAfterSeconds, rowFor, sealSecret, secretKeyBytes, sheetsDestination, signJwt, signWebhook, slackDestination, slackMessage, verifyWebhook, visibleInputs, webhookDestination, webhookPayload };
875
+ export { type Cell, type ClaimRequest, type ClaimedDelivery, DEDUPE_WINDOW, DEFAULT_BATCH_SIZE, DEFAULT_MAX_ATTEMPTS, DEFAULT_MAX_DEFERRALS, DEFAULT_SHEET_NAME, DRIVE_FILE_SCOPE, DeliveryError, type DeliveryFailure, type DeliveryItem, type DeliveryOptions, type DeliveryOutcome, type DeliveryReport, type DeliveryResult, type DeliveryRunner, type DeliveryRunnerOptions, type DeliverySubmission, type DeriveSecretKeyOptions, type Destination, type DestinationConfig, type EmailConfig, type EmailContent, type EmailDestinationOptions, type EmailMessage, type EmailProvider, type EmailRenderInput, GOOGLE_TOKEN_URL, type GoogleOAuthClient, type GoogleOAuthTokenOptions, type GoogleServiceAccount, type GoogleTokenOptions, HEADER_SCAN_ROWS, type HookKind, HookUrlError, type HttpDeps, MAX_CONDITION_DEPTH, MAX_HOOK_URL_LENGTH, MAX_RECIPIENTS, MIN_WEBHOOK_SECRET_BYTES, type OptionResolver, REQUEST_TIMEOUT_MS, RESEND_API, type RefreshedGoogleToken, type ResendOptions, type ResolveContext, type ResolveHost, type ResponseRow, type RowOptions, type RunOptions, SENSITIVE_MASK, SHEETS_API, SHEETS_SCOPE, SLACK_HOSTS, SLACK_VALUE_LIMIT, type SheetConfig, type SheetState, type SheetsDestinationOptions, type SlackDestinationOptions, type SlackMessageInput, type SlackMessageOptions, type VerifyWebhookInput, type WebCryptoKey, type WebhookConfig, type WebhookDestinationOptions, type WebhookPayload, answerCell, checkHookHost, checkWebhookUrl, clearGoogleOAuthTokenCache, clearGoogleTokenCache, columnLetter, conditionOf, createDeliveryRunner, defaultBackoff, deliveryFailure, deliveryOutcome, deriveSecretKey, emailBody, emailDestination, escapeFormula, escapeHtml, escapeSlack, googleAccessToken, googleOAuthAccessToken, headerSafe, hookHost, httpError, idempotencyKey, isCondition, isPrivateHost, isPublicAddress, isRetryableStatus, isSafeAddress, isSafeFrom, mergeHeader, newWebhookSecret, openSecret, parseHookUrl, passesCondition, refreshGoogleAccessToken, renderEmail, resendProvider, responseRow, retryAfterSeconds, rowFor, sealSecret, secretKeyBytes, sheetsDestination, signJwt, signWebhook, slackDestination, slackMessage, verifyWebhook, visibleInputs, webhookDestination, webhookPayload };