mailery 0.5.1 → 0.8.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.
@@ -47,6 +47,24 @@ interface ContactAdapter {
47
47
  addTags?(externalId: string, tags: string[]): Promise<void>;
48
48
  removeTags?(externalId: string, tags: string[]): Promise<void>;
49
49
  }
50
+ /**
51
+ * Constrains WHEN a send step's email may go out. The flow's waits decide the
52
+ * earliest moment (T + N days); the window then pushes that moment forward —
53
+ * never backward — to the next allowed slot:
54
+ *
55
+ * - `timeOfDay` — deliver at this local wall-clock time ('HH:mm'). A send
56
+ * arriving after that time waits for the next day's slot (with a short
57
+ * grace period so tick jitter doesn't add 24h).
58
+ * - `weekdaysOnly` — a slot landing on Saturday/Sunday moves to Monday.
59
+ * - `useContactTimezone` — interpret times in `contact.timezone` when set,
60
+ * else fall back to `timezone` (IANA name, default UTC).
61
+ */
62
+ interface DeliveryWindow {
63
+ weekdaysOnly?: boolean;
64
+ timeOfDay?: string;
65
+ useContactTimezone?: boolean;
66
+ timezone?: string;
67
+ }
50
68
  type FlowStep = {
51
69
  type: 'wait';
52
70
  value: number;
@@ -65,6 +83,7 @@ type FlowStep = {
65
83
  templateSlug: string;
66
84
  providerOverride?: string;
67
85
  vars?: Record<string, unknown>;
86
+ delivery?: DeliveryWindow;
68
87
  } | {
69
88
  type: 'tag';
70
89
  addTags?: string[];
@@ -375,7 +394,7 @@ type QueueDriverConfig = {
375
394
  * server-shaped interfaces.
376
395
  */
377
396
  type SubscriptionStatus = 'subscribed' | 'pending_doi' | 'unsubscribed' | 'bounced' | 'complained';
378
- type SendStatus = 'queued' | 'sending' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'failed' | 'suppressed';
397
+ type SendStatus = 'queued' | 'sending' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'failed' | 'suppressed' | 'cancelled';
379
398
  type TemplateKind = 'transactional' | 'marketing';
380
399
  type SuppressionScope = 'all' | 'marketing' | 'transactional';
381
400
  type SuppressionReason = 'unsubscribed' | 'hard_bounce' | 'complaint' | 'manual' | 'list_cleaning' | 'gdpr_forget';
@@ -417,6 +436,63 @@ type SenderDomainValidation = {
417
436
  };
418
437
  declare function validateSenderDomain(fromEmail: string, templateKind: TemplateKind, registry: SenderDomainRegistry | undefined): SenderDomainValidation;
419
438
 
439
+ /**
440
+ * VarsAdapter — host-provided template variables, resolved at render time.
441
+ *
442
+ * The host declares a zod schema (the contract templates can rely on) plus a
443
+ * `resolve` function that loads those values from the host's own database for
444
+ * a given contact. Resolved keys are merged into the render context root, so
445
+ * a schema key `user` is referenced in templates as `{{user.name}}`.
446
+ *
447
+ * The schema does double duty: it is exposed to the admin SPA as JSON Schema
448
+ * (GET /vars-schema) to drive editor autocomplete, and the content linter
449
+ * uses it to flag `{{paths}}` that don't exist.
450
+ */
451
+
452
+ interface VarsResolveInfo {
453
+ /** Why the vars are being resolved. Previews and tests should be side-effect free. */
454
+ reason: 'send' | 'preview' | 'test';
455
+ /** Slug of the template being rendered, when known. */
456
+ templateSlug?: string;
457
+ /** Slug of the flow the send belongs to, when the render is part of a flow run. */
458
+ flowSlug?: string;
459
+ /** Name of the event that triggered the flow run, when applicable. */
460
+ eventName?: string;
461
+ /**
462
+ * Properties of the triggering event (`mailer.fire(name, id, properties)`).
463
+ * This is how a resolver scopes lookups for account/topic flows:
464
+ * `info.eventProperties?.accountId` tells it WHICH account the run is about.
465
+ * Also available raw in templates as `{{event.*}}`.
466
+ */
467
+ eventProperties?: Record<string, unknown>;
468
+ }
469
+ interface VarsAdapter<S extends z.ZodType = z.ZodType> {
470
+ /** Contract for what `resolve` returns. Root keys become root template variables. */
471
+ schema: S;
472
+ /** Load the variables for one contact from the host's data. */
473
+ resolve(contact: Contact, info: VarsResolveInfo): Promise<z.infer<S>> | z.infer<S>;
474
+ }
475
+ /**
476
+ * Identity helper that pins `resolve`'s return type to `z.infer<schema>` —
477
+ * without it, TypeScript widens the schema generic and the return type is
478
+ * unchecked.
479
+ *
480
+ * const varsAdapter = defineVars({
481
+ * schema: z.object({ user: z.object({ name: z.string() }) }),
482
+ * async resolve(contact) {
483
+ * return { user: { name: await lookupName(contact.externalId) } }
484
+ * },
485
+ * })
486
+ */
487
+ declare function defineVars<S extends z.ZodType>(adapter: VarsAdapter<S>): VarsAdapter<S>;
488
+ /**
489
+ * Render-context keys mailery owns. Resolved vars never override these — a
490
+ * schema that declares one is rejected at Mailer.init.
491
+ */
492
+ declare const RESERVED_VAR_KEYS: readonly ["contact", "vars", "event", "unsubscribeUrl", "viewInBrowserUrl", "preferenceCenterUrl", "senderAddress"];
493
+ /** JSON Schema for the adapter's zod schema (wire format for the admin SPA + linter). */
494
+ declare function varsJsonSchema(adapter: VarsAdapter): Record<string, unknown>;
495
+
420
496
  /**
421
497
  * Mailer configuration shape. Required + optional surfaces with sane defaults.
422
498
  */
@@ -451,6 +527,14 @@ interface MailTesterConfig {
451
527
  apiKey: string;
452
528
  /** Minimum score (0-10) below which publish is blocked. Default 8.0. */
453
529
  minScore?: number;
530
+ /**
531
+ * When true, publishing content that has no cached score is blocked too —
532
+ * the operator must run a deliverability check for that exact content first.
533
+ * Default false, which only blocks content already known to score below
534
+ * `minScore`; in that mode any edit (even whitespace) changes the content
535
+ * key, misses the cache, and publishes freely.
536
+ */
537
+ requireScore?: boolean;
454
538
  /**
455
539
  * How long a successful score remains cached for the same content. Default 24.
456
540
  * Re-publishing the same body within the window does not burn a credit.
@@ -531,6 +615,14 @@ interface MailerConfig {
531
615
  db: Db;
532
616
  collectionPrefix?: string;
533
617
  adapter: ContactAdapter;
618
+ /**
619
+ * Optional host-provided template variables. `resolve(contact)` runs at
620
+ * send/preview/test render time; its result is merged into the template
621
+ * context root (schema key `user` → `{{user.name}}`). The zod schema
622
+ * drives admin-editor autocomplete and the `unknown_variable` lint rule.
623
+ * Build one with `defineVars({ schema, resolve })`.
624
+ */
625
+ varsAdapter?: VarsAdapter<any>;
534
626
  /**
535
627
  * Queue driver selection. One of:
536
628
  * - `{ driver: 'bull', redis: ... }` — BullMQ (default for prod; requires Redis)
@@ -734,7 +826,7 @@ interface FlowVersionDoc {
734
826
  }
735
827
  interface FlowRunHistoryEntry {
736
828
  stepIndex: number;
737
- action: 'entered' | 'wait_started' | 'wait_completed' | 'condition_evaluated' | 'branch_taken' | 'sent' | 'send_skipped' | 'tagged' | 'event_fired' | 'webhook_called' | 'exited' | 'failed';
829
+ action: 'entered' | 'wait_started' | 'wait_completed' | 'condition_evaluated' | 'branch_taken' | 'sent' | 'send_skipped' | 'send_deferred' | 'tagged' | 'event_fired' | 'webhook_called' | 'exited' | 'failed';
738
830
  at: Date;
739
831
  details?: Record<string, unknown>;
740
832
  }
@@ -745,6 +837,17 @@ interface FlowRunDoc {
745
837
  flowSlug: string;
746
838
  flowVersion: number;
747
839
  emailAtEntry: string;
840
+ /**
841
+ * Snapshot of the event that triggered this run. Properties surface in
842
+ * templates as `{{event.*}}` and in `varsAdapter.resolve` via
843
+ * `info.eventProperties` — this is how account/topic-scoped flows know
844
+ * which account or topic the run is about. Null for non-event entries.
845
+ */
846
+ triggerEvent?: {
847
+ name: string;
848
+ properties: Record<string, unknown>;
849
+ occurredAt: Date;
850
+ } | null;
748
851
  enteredAt: Date;
749
852
  status: FlowRunStatus;
750
853
  currentStepIndex: number;
@@ -1224,6 +1327,7 @@ interface RunnerContext {
1224
1327
  db: Db;
1225
1328
  collections: Collections;
1226
1329
  adapter: ContactAdapter;
1330
+ varsAdapter?: VarsAdapter;
1227
1331
  providers: Record<string, MailProvider>;
1228
1332
  queues: Queues;
1229
1333
  config: ResolvedConfig;
@@ -1297,6 +1401,33 @@ declare class Mailer {
1297
1401
  suppress(email: string, opts: Omit<SuppressInput, 'email'>): Promise<void>;
1298
1402
  tag(externalId: string, tag: string): Promise<void>;
1299
1403
  untag(externalId: string, tag: string): Promise<void>;
1404
+ /**
1405
+ * Abort every active run of one flow for a contact, immediately. Runs parked
1406
+ * in a `wait` exit too — their delayed wake-up jobs find the run exited and
1407
+ * no-op. Also cancels any of the flow's emails still sitting in the send
1408
+ * queue for this contact (queued or awaiting retry), so an abort means no
1409
+ * further mail, not just no further steps.
1410
+ *
1411
+ * No-op (returns zero counts) when nothing is active. Safe to call from the
1412
+ * same handler that processes the business event ("user upgraded").
1413
+ */
1414
+ abortFlow(flowSlug: string, externalId: string, opts?: {
1415
+ reason?: string;
1416
+ }): Promise<{
1417
+ abortedRuns: number;
1418
+ cancelledSends: number;
1419
+ }>;
1420
+ /**
1421
+ * Abort every active flow run for a contact across all flows. Same semantics
1422
+ * as `abortFlow` — for "stop everything" events (account deleted, churned).
1423
+ */
1424
+ abortAllFlows(externalId: string, opts?: {
1425
+ reason?: string;
1426
+ }): Promise<{
1427
+ abortedRuns: number;
1428
+ cancelledSends: number;
1429
+ }>;
1430
+ private abortActiveRuns;
1300
1431
  /**
1301
1432
  * GDPR right-to-erasure. Hard-deletes the contact's PII and leaves a hashed
1302
1433
  * suppression row to block re-import. INVARIANT 9.
@@ -1346,4 +1477,4 @@ declare class NullProvider implements MailProvider {
1346
1477
  reset(): void;
1347
1478
  }
1348
1479
 
1349
- export { type AdapterFilter as A, type BroadcastDoc as B, type ContactAdapter as C, type SubscriptionDoc as D, type EventDoc as E, type FlowStep as F, type SubscriptionStatus as G, type HealthDoc as H, type SuppressionDoc as I, type SuppressionReason as J, type TemplateKind as K, type LeadDoc as L, type MailProvider as M, type NormalizedEvent as N, type OutboxDoc as O, type Predicate as P, type TemplateVersionDoc as Q, type RunnerContext as R, type SendArgs as S, type TemplateDoc as T, ensureIndexes as U, getCollections as V, type WebhookEventDoc as W, validateSenderDomain as X, type Contact as a, type SendResult as b, type MailTesterFeedback as c, Mailer as d, type SuppressionScope as e, type SegmentFilter as f, type AuditLogDoc as g, type BroadcastStatus as h, type CircuitBreakerThresholds as i, type Collections as j, type ContactTagDoc as k, type FlowDoc as l, type FlowGoal as m, type FlowRunDoc as n, type FlowRunStatus as o, type FlowVersionDoc as p, type HealthStatus as q, type MailerConfig as r, NullProvider as s, type RedisOptions as t, type SegmentDefinition as u, type SendDoc as v, type SendStatus as w, type SenderDomainConfig as x, type SenderDomainRegistry as y, type SenderDomainValidation as z };
1480
+ export { getCollections as $, type AdapterFilter as A, type BroadcastDoc as B, type ContactAdapter as C, type DeliveryWindow as D, type EventDoc as E, type FlowStep as F, type SenderDomainValidation as G, type HealthDoc as H, type SubscriptionDoc as I, type SubscriptionStatus as J, type SuppressionDoc as K, type LeadDoc as L, type MailProvider as M, type NormalizedEvent as N, type OutboxDoc as O, type Predicate as P, type SuppressionReason as Q, type RunnerContext as R, type SendArgs as S, type TemplateDoc as T, type TemplateKind as U, type TemplateVersionDoc as V, type VarsAdapter as W, type VarsResolveInfo as X, type WebhookEventDoc as Y, defineVars as Z, ensureIndexes as _, type Contact as a, validateSenderDomain as a0, varsJsonSchema as a1, type FlowTrigger as a2, type QueueDriverConfig as a3, type SendResult as b, type MailTesterFeedback as c, Mailer as d, type SuppressionScope as e, type SegmentFilter as f, type AuditLogDoc as g, type BroadcastStatus as h, type CircuitBreakerThresholds as i, type Collections as j, type ContactTagDoc as k, type FlowDoc as l, type FlowGoal as m, type FlowRunDoc as n, type FlowRunStatus as o, type FlowVersionDoc as p, type HealthStatus as q, type MailerConfig as r, NullProvider as s, RESERVED_VAR_KEYS as t, type RedisOptions as u, type SegmentDefinition as v, type SendDoc as w, type SendStatus as x, type SenderDomainConfig as y, type SenderDomainRegistry as z };
@@ -47,6 +47,24 @@ interface ContactAdapter {
47
47
  addTags?(externalId: string, tags: string[]): Promise<void>;
48
48
  removeTags?(externalId: string, tags: string[]): Promise<void>;
49
49
  }
50
+ /**
51
+ * Constrains WHEN a send step's email may go out. The flow's waits decide the
52
+ * earliest moment (T + N days); the window then pushes that moment forward —
53
+ * never backward — to the next allowed slot:
54
+ *
55
+ * - `timeOfDay` — deliver at this local wall-clock time ('HH:mm'). A send
56
+ * arriving after that time waits for the next day's slot (with a short
57
+ * grace period so tick jitter doesn't add 24h).
58
+ * - `weekdaysOnly` — a slot landing on Saturday/Sunday moves to Monday.
59
+ * - `useContactTimezone` — interpret times in `contact.timezone` when set,
60
+ * else fall back to `timezone` (IANA name, default UTC).
61
+ */
62
+ interface DeliveryWindow {
63
+ weekdaysOnly?: boolean;
64
+ timeOfDay?: string;
65
+ useContactTimezone?: boolean;
66
+ timezone?: string;
67
+ }
50
68
  type FlowStep = {
51
69
  type: 'wait';
52
70
  value: number;
@@ -65,6 +83,7 @@ type FlowStep = {
65
83
  templateSlug: string;
66
84
  providerOverride?: string;
67
85
  vars?: Record<string, unknown>;
86
+ delivery?: DeliveryWindow;
68
87
  } | {
69
88
  type: 'tag';
70
89
  addTags?: string[];
@@ -375,7 +394,7 @@ type QueueDriverConfig = {
375
394
  * server-shaped interfaces.
376
395
  */
377
396
  type SubscriptionStatus = 'subscribed' | 'pending_doi' | 'unsubscribed' | 'bounced' | 'complained';
378
- type SendStatus = 'queued' | 'sending' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'failed' | 'suppressed';
397
+ type SendStatus = 'queued' | 'sending' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'failed' | 'suppressed' | 'cancelled';
379
398
  type TemplateKind = 'transactional' | 'marketing';
380
399
  type SuppressionScope = 'all' | 'marketing' | 'transactional';
381
400
  type SuppressionReason = 'unsubscribed' | 'hard_bounce' | 'complaint' | 'manual' | 'list_cleaning' | 'gdpr_forget';
@@ -417,6 +436,63 @@ type SenderDomainValidation = {
417
436
  };
418
437
  declare function validateSenderDomain(fromEmail: string, templateKind: TemplateKind, registry: SenderDomainRegistry | undefined): SenderDomainValidation;
419
438
 
439
+ /**
440
+ * VarsAdapter — host-provided template variables, resolved at render time.
441
+ *
442
+ * The host declares a zod schema (the contract templates can rely on) plus a
443
+ * `resolve` function that loads those values from the host's own database for
444
+ * a given contact. Resolved keys are merged into the render context root, so
445
+ * a schema key `user` is referenced in templates as `{{user.name}}`.
446
+ *
447
+ * The schema does double duty: it is exposed to the admin SPA as JSON Schema
448
+ * (GET /vars-schema) to drive editor autocomplete, and the content linter
449
+ * uses it to flag `{{paths}}` that don't exist.
450
+ */
451
+
452
+ interface VarsResolveInfo {
453
+ /** Why the vars are being resolved. Previews and tests should be side-effect free. */
454
+ reason: 'send' | 'preview' | 'test';
455
+ /** Slug of the template being rendered, when known. */
456
+ templateSlug?: string;
457
+ /** Slug of the flow the send belongs to, when the render is part of a flow run. */
458
+ flowSlug?: string;
459
+ /** Name of the event that triggered the flow run, when applicable. */
460
+ eventName?: string;
461
+ /**
462
+ * Properties of the triggering event (`mailer.fire(name, id, properties)`).
463
+ * This is how a resolver scopes lookups for account/topic flows:
464
+ * `info.eventProperties?.accountId` tells it WHICH account the run is about.
465
+ * Also available raw in templates as `{{event.*}}`.
466
+ */
467
+ eventProperties?: Record<string, unknown>;
468
+ }
469
+ interface VarsAdapter<S extends z.ZodType = z.ZodType> {
470
+ /** Contract for what `resolve` returns. Root keys become root template variables. */
471
+ schema: S;
472
+ /** Load the variables for one contact from the host's data. */
473
+ resolve(contact: Contact, info: VarsResolveInfo): Promise<z.infer<S>> | z.infer<S>;
474
+ }
475
+ /**
476
+ * Identity helper that pins `resolve`'s return type to `z.infer<schema>` —
477
+ * without it, TypeScript widens the schema generic and the return type is
478
+ * unchecked.
479
+ *
480
+ * const varsAdapter = defineVars({
481
+ * schema: z.object({ user: z.object({ name: z.string() }) }),
482
+ * async resolve(contact) {
483
+ * return { user: { name: await lookupName(contact.externalId) } }
484
+ * },
485
+ * })
486
+ */
487
+ declare function defineVars<S extends z.ZodType>(adapter: VarsAdapter<S>): VarsAdapter<S>;
488
+ /**
489
+ * Render-context keys mailery owns. Resolved vars never override these — a
490
+ * schema that declares one is rejected at Mailer.init.
491
+ */
492
+ declare const RESERVED_VAR_KEYS: readonly ["contact", "vars", "event", "unsubscribeUrl", "viewInBrowserUrl", "preferenceCenterUrl", "senderAddress"];
493
+ /** JSON Schema for the adapter's zod schema (wire format for the admin SPA + linter). */
494
+ declare function varsJsonSchema(adapter: VarsAdapter): Record<string, unknown>;
495
+
420
496
  /**
421
497
  * Mailer configuration shape. Required + optional surfaces with sane defaults.
422
498
  */
@@ -451,6 +527,14 @@ interface MailTesterConfig {
451
527
  apiKey: string;
452
528
  /** Minimum score (0-10) below which publish is blocked. Default 8.0. */
453
529
  minScore?: number;
530
+ /**
531
+ * When true, publishing content that has no cached score is blocked too —
532
+ * the operator must run a deliverability check for that exact content first.
533
+ * Default false, which only blocks content already known to score below
534
+ * `minScore`; in that mode any edit (even whitespace) changes the content
535
+ * key, misses the cache, and publishes freely.
536
+ */
537
+ requireScore?: boolean;
454
538
  /**
455
539
  * How long a successful score remains cached for the same content. Default 24.
456
540
  * Re-publishing the same body within the window does not burn a credit.
@@ -531,6 +615,14 @@ interface MailerConfig {
531
615
  db: Db;
532
616
  collectionPrefix?: string;
533
617
  adapter: ContactAdapter;
618
+ /**
619
+ * Optional host-provided template variables. `resolve(contact)` runs at
620
+ * send/preview/test render time; its result is merged into the template
621
+ * context root (schema key `user` → `{{user.name}}`). The zod schema
622
+ * drives admin-editor autocomplete and the `unknown_variable` lint rule.
623
+ * Build one with `defineVars({ schema, resolve })`.
624
+ */
625
+ varsAdapter?: VarsAdapter<any>;
534
626
  /**
535
627
  * Queue driver selection. One of:
536
628
  * - `{ driver: 'bull', redis: ... }` — BullMQ (default for prod; requires Redis)
@@ -734,7 +826,7 @@ interface FlowVersionDoc {
734
826
  }
735
827
  interface FlowRunHistoryEntry {
736
828
  stepIndex: number;
737
- action: 'entered' | 'wait_started' | 'wait_completed' | 'condition_evaluated' | 'branch_taken' | 'sent' | 'send_skipped' | 'tagged' | 'event_fired' | 'webhook_called' | 'exited' | 'failed';
829
+ action: 'entered' | 'wait_started' | 'wait_completed' | 'condition_evaluated' | 'branch_taken' | 'sent' | 'send_skipped' | 'send_deferred' | 'tagged' | 'event_fired' | 'webhook_called' | 'exited' | 'failed';
738
830
  at: Date;
739
831
  details?: Record<string, unknown>;
740
832
  }
@@ -745,6 +837,17 @@ interface FlowRunDoc {
745
837
  flowSlug: string;
746
838
  flowVersion: number;
747
839
  emailAtEntry: string;
840
+ /**
841
+ * Snapshot of the event that triggered this run. Properties surface in
842
+ * templates as `{{event.*}}` and in `varsAdapter.resolve` via
843
+ * `info.eventProperties` — this is how account/topic-scoped flows know
844
+ * which account or topic the run is about. Null for non-event entries.
845
+ */
846
+ triggerEvent?: {
847
+ name: string;
848
+ properties: Record<string, unknown>;
849
+ occurredAt: Date;
850
+ } | null;
748
851
  enteredAt: Date;
749
852
  status: FlowRunStatus;
750
853
  currentStepIndex: number;
@@ -1224,6 +1327,7 @@ interface RunnerContext {
1224
1327
  db: Db;
1225
1328
  collections: Collections;
1226
1329
  adapter: ContactAdapter;
1330
+ varsAdapter?: VarsAdapter;
1227
1331
  providers: Record<string, MailProvider>;
1228
1332
  queues: Queues;
1229
1333
  config: ResolvedConfig;
@@ -1297,6 +1401,33 @@ declare class Mailer {
1297
1401
  suppress(email: string, opts: Omit<SuppressInput, 'email'>): Promise<void>;
1298
1402
  tag(externalId: string, tag: string): Promise<void>;
1299
1403
  untag(externalId: string, tag: string): Promise<void>;
1404
+ /**
1405
+ * Abort every active run of one flow for a contact, immediately. Runs parked
1406
+ * in a `wait` exit too — their delayed wake-up jobs find the run exited and
1407
+ * no-op. Also cancels any of the flow's emails still sitting in the send
1408
+ * queue for this contact (queued or awaiting retry), so an abort means no
1409
+ * further mail, not just no further steps.
1410
+ *
1411
+ * No-op (returns zero counts) when nothing is active. Safe to call from the
1412
+ * same handler that processes the business event ("user upgraded").
1413
+ */
1414
+ abortFlow(flowSlug: string, externalId: string, opts?: {
1415
+ reason?: string;
1416
+ }): Promise<{
1417
+ abortedRuns: number;
1418
+ cancelledSends: number;
1419
+ }>;
1420
+ /**
1421
+ * Abort every active flow run for a contact across all flows. Same semantics
1422
+ * as `abortFlow` — for "stop everything" events (account deleted, churned).
1423
+ */
1424
+ abortAllFlows(externalId: string, opts?: {
1425
+ reason?: string;
1426
+ }): Promise<{
1427
+ abortedRuns: number;
1428
+ cancelledSends: number;
1429
+ }>;
1430
+ private abortActiveRuns;
1300
1431
  /**
1301
1432
  * GDPR right-to-erasure. Hard-deletes the contact's PII and leaves a hashed
1302
1433
  * suppression row to block re-import. INVARIANT 9.
@@ -1346,4 +1477,4 @@ declare class NullProvider implements MailProvider {
1346
1477
  reset(): void;
1347
1478
  }
1348
1479
 
1349
- export { type AdapterFilter as A, type BroadcastDoc as B, type ContactAdapter as C, type SubscriptionDoc as D, type EventDoc as E, type FlowStep as F, type SubscriptionStatus as G, type HealthDoc as H, type SuppressionDoc as I, type SuppressionReason as J, type TemplateKind as K, type LeadDoc as L, type MailProvider as M, type NormalizedEvent as N, type OutboxDoc as O, type Predicate as P, type TemplateVersionDoc as Q, type RunnerContext as R, type SendArgs as S, type TemplateDoc as T, ensureIndexes as U, getCollections as V, type WebhookEventDoc as W, validateSenderDomain as X, type Contact as a, type SendResult as b, type MailTesterFeedback as c, Mailer as d, type SuppressionScope as e, type SegmentFilter as f, type AuditLogDoc as g, type BroadcastStatus as h, type CircuitBreakerThresholds as i, type Collections as j, type ContactTagDoc as k, type FlowDoc as l, type FlowGoal as m, type FlowRunDoc as n, type FlowRunStatus as o, type FlowVersionDoc as p, type HealthStatus as q, type MailerConfig as r, NullProvider as s, type RedisOptions as t, type SegmentDefinition as u, type SendDoc as v, type SendStatus as w, type SenderDomainConfig as x, type SenderDomainRegistry as y, type SenderDomainValidation as z };
1480
+ export { getCollections as $, type AdapterFilter as A, type BroadcastDoc as B, type ContactAdapter as C, type DeliveryWindow as D, type EventDoc as E, type FlowStep as F, type SenderDomainValidation as G, type HealthDoc as H, type SubscriptionDoc as I, type SubscriptionStatus as J, type SuppressionDoc as K, type LeadDoc as L, type MailProvider as M, type NormalizedEvent as N, type OutboxDoc as O, type Predicate as P, type SuppressionReason as Q, type RunnerContext as R, type SendArgs as S, type TemplateDoc as T, type TemplateKind as U, type TemplateVersionDoc as V, type VarsAdapter as W, type VarsResolveInfo as X, type WebhookEventDoc as Y, defineVars as Z, ensureIndexes as _, type Contact as a, validateSenderDomain as a0, varsJsonSchema as a1, type FlowTrigger as a2, type QueueDriverConfig as a3, type SendResult as b, type MailTesterFeedback as c, Mailer as d, type SuppressionScope as e, type SegmentFilter as f, type AuditLogDoc as g, type BroadcastStatus as h, type CircuitBreakerThresholds as i, type Collections as j, type ContactTagDoc as k, type FlowDoc as l, type FlowGoal as m, type FlowRunDoc as n, type FlowRunStatus as o, type FlowVersionDoc as p, type HealthStatus as q, type MailerConfig as r, NullProvider as s, RESERVED_VAR_KEYS as t, type RedisOptions as u, type SegmentDefinition as v, type SendDoc as w, type SendStatus as x, type SenderDomainConfig as y, type SenderDomainRegistry as z };