mailery 0.4.0 → 0.7.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.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, M as MailProvider, S as SendArgs, b as SendResult, c as Mailer, T as TemplateDoc, d as SuppressionScope, F as FlowStep, e as SegmentFilter, P as Predicate } from './null-CGaNDDlQ.cjs';
2
- export { f as AuditLogDoc, B as BroadcastDoc, g as BroadcastStatus, h as CircuitBreakerThresholds, i as Collections, j as ContactTagDoc, E as EventDoc, k as FlowDoc, l as FlowGoal, m as FlowRunDoc, n as FlowRunStatus, o as FlowVersionDoc, H as HealthDoc, p as HealthStatus, L as LeadDoc, q as MailerConfig, r as NullProvider, O as OutboxDoc, s as RedisOptions, t as SegmentDefinition, u as SendDoc, v as SendStatus, w as SenderDomainConfig, x as SenderDomainRegistry, y as SenderDomainValidation, z as SubscriptionDoc, D as SubscriptionStatus, G as SuppressionDoc, I as SuppressionReason, J as TemplateKind, K as TemplateVersionDoc, W as WebhookEventDoc, Q as ensureIndexes, U as getCollections, V as validateSenderDomain } from './null-CGaNDDlQ.cjs';
1
+ import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, M as MailProvider, S as SendArgs, b as SendResult, c as MailTesterFeedback, d as Mailer, T as TemplateDoc, e as SuppressionScope, D as DeliveryWindow, F as FlowStep, f as SegmentFilter, P as Predicate } from './null-B0rPgE5_.cjs';
2
+ export { g as AuditLogDoc, B as BroadcastDoc, h as BroadcastStatus, i as CircuitBreakerThresholds, j as Collections, k as ContactTagDoc, E as EventDoc, l as FlowDoc, m as FlowGoal, n as FlowRunDoc, o as FlowRunStatus, p as FlowVersionDoc, H as HealthDoc, q as HealthStatus, L as LeadDoc, r as MailerConfig, s as NullProvider, O as OutboxDoc, t as RESERVED_VAR_KEYS, u as RedisOptions, v as SegmentDefinition, w as SendDoc, x as SendStatus, y as SenderDomainConfig, z as SenderDomainRegistry, G as SenderDomainValidation, I as SubscriptionDoc, J as SubscriptionStatus, K as SuppressionDoc, Q as SuppressionReason, U as TemplateKind, V as TemplateVersionDoc, W as VarsAdapter, X as VarsResolveInfo, Y as WebhookEventDoc, Z as defineVars, _ as ensureIndexes, $ as getCollections, a0 as validateSenderDomain, a1 as varsJsonSchema } from './null-B0rPgE5_.cjs';
3
3
  import { ObjectId, Db, Filter } from 'mongodb';
4
4
  import { Request, Router } from 'express';
5
5
  import Handlebars from 'handlebars';
@@ -140,6 +140,37 @@ declare class SendGridProvider implements MailProvider {
140
140
  parseWebhookEvents(payload: unknown): NormalizedEvent[];
141
141
  }
142
142
 
143
+ /**
144
+ * Mail-Tester deliverability check.
145
+ *
146
+ * 1. Operator clicks "Run deliverability check" in the template editor.
147
+ * 2. Mailery provisions a check (gets a unique test address from Mail-Tester).
148
+ * 3. Mailery sends the rendered draft to that address via the operator's
149
+ * default provider.
150
+ * 4. Mailery polls Mail-Tester for the score (typically arrives 10-30s after
151
+ * receipt) and caches it keyed on a content fingerprint.
152
+ * 5. If the score is below `minScore` the publish endpoint refuses unless
153
+ * the operator passes `bypassMailTester: true` in the publish request.
154
+ *
155
+ * The client interface is pluggable so tests inject a stub. Real
156
+ * implementation hits https://mail-tester.com/api/.
157
+ */
158
+
159
+ interface ProvisionedCheck {
160
+ checkId: string;
161
+ emailAddress: string;
162
+ }
163
+ interface MailTesterResult {
164
+ ready: boolean;
165
+ score: number;
166
+ feedback: MailTesterFeedback[];
167
+ rawSummary: string | null;
168
+ }
169
+ interface MailTesterClient {
170
+ provisionCheck(): Promise<ProvisionedCheck>;
171
+ fetchResult(checkId: string): Promise<MailTesterResult>;
172
+ }
173
+
143
174
  /**
144
175
  * Admin router — serves the prebuilt React SPA + REST endpoints the SPA
145
176
  * consumes. Mount inside a host Express app, gated by host auth.
@@ -154,6 +185,11 @@ interface AdminRouterOptions {
154
185
  spaDir?: string;
155
186
  /** Resolve actor metadata from a Request. Defaults to `human:${req.user?.email || 'anonymous'}`. */
156
187
  getActor?: (req: Request) => string;
188
+ /**
189
+ * Inject a Mail-Tester client (tests use a stub). When omitted, a real
190
+ * client is created from `mailer.config.mailTester`.
191
+ */
192
+ mailTesterClient?: MailTesterClient;
157
193
  }
158
194
  declare function createAdminRouter(mailer: Mailer, opts?: AdminRouterOptions): Router;
159
195
 
@@ -214,8 +250,16 @@ declare function compileMailyTemplate(content: unknown): Promise<CompileResult>;
214
250
  /** Auto-derive plain text from compiled HTML. */
215
251
  declare function derivePlaintext(html: string): string;
216
252
  interface RenderContext {
253
+ /**
254
+ * Host-resolved variables (varsAdapter) live at the context root, so a
255
+ * schema key `user` renders as `{{user.name}}`. Reserved keys below always
256
+ * win over resolved keys.
257
+ */
258
+ [resolvedVar: string]: unknown;
217
259
  contact: Contact;
218
260
  vars: Record<string, unknown>;
261
+ /** Properties of the event that triggered the flow run ({{event.*}}). Empty outside flow sends. */
262
+ event?: Record<string, unknown>;
219
263
  /** URL the recipient hits to one-click unsubscribe. */
220
264
  unsubscribeUrl: string;
221
265
  /** URL to view this email in a browser (when implemented). */
@@ -289,6 +333,20 @@ declare function verifyUnsubscribeToken(token: string, secret: string, now?: Dat
289
333
  */
290
334
  declare function sha256Hex(input: string): string;
291
335
 
336
+ /**
337
+ * Delivery-window math. Pure — no I/O, no Date.now(); callers pass `now`.
338
+ *
339
+ * Given the moment a send step becomes runnable, `computeDeliveryTime`
340
+ * returns the earliest instant the email is allowed to go out. It only ever
341
+ * moves forward: a send already inside its window returns `now` unchanged.
342
+ *
343
+ * Timezone handling uses the Intl API (no dependency). DST transitions are
344
+ * resolved with a two-pass offset fix; the worst case during a transition is
345
+ * being off by the shifted hour, which is fine for email delivery.
346
+ */
347
+
348
+ declare function computeDeliveryTime(now: Date, window: DeliveryWindow, contactTimezone?: string): Date;
349
+
292
350
  /**
293
351
  * Enumerable option lists for the union types in `./types.ts`. Used by the
294
352
  * admin client to render dropdowns + by tooling to enumerate the surface.
@@ -340,6 +398,6 @@ declare const DEDUPE_POLICIES: readonly DedupePolicyOption[];
340
398
  /**
341
399
  * Public exports for the `mailery` package.
342
400
  */
343
- declare const VERSION = "0.1.0";
401
+ declare const VERSION = "0.7.0";
344
402
 
345
- export { AdapterFilter, type AdminRouterOptions, Contact, ContactAdapter, DEDUPE_POLICIES, type DedupePolicyOption, FLOW_STEP_KINDS, FlowStep, type FlowStepKindOption, MailProvider, Mailer, MongoContactAdapter, type MongoContactAdapterOptions, NormalizedEvent, PREDICATE_KINDS, Predicate, type PredicateKind, type PredicateKindOption, type PublicRouterOptions, SEGMENT_FILTER_KINDS, SegmentFilter, type SegmentFilterKind, type SegmentFilterKindOption, SendArgs, SendGridProvider, type SendGridProviderOptions, SendResult, SuppressionScope, TemplateDoc, VERSION, applyTracking, applyWebhookEvent, compileMailyTemplate, compileTemplate, createAdminRouter, createPublicRouter, defaultFlowStep, defaultPredicate, defaultSegmentFilter, derivePlaintext, dispatchSend, predicateKind, processNewlyFiredEventTriggers, processOneRunStep, renderTemplate, runTick, sha256Hex, signUnsubscribeToken, sweepStrandedFlowRuns, verifyUnsubscribeToken };
403
+ export { AdapterFilter, type AdminRouterOptions, Contact, ContactAdapter, DEDUPE_POLICIES, type DedupePolicyOption, DeliveryWindow, FLOW_STEP_KINDS, FlowStep, type FlowStepKindOption, MailProvider, Mailer, MongoContactAdapter, type MongoContactAdapterOptions, NormalizedEvent, PREDICATE_KINDS, Predicate, type PredicateKind, type PredicateKindOption, type PublicRouterOptions, SEGMENT_FILTER_KINDS, SegmentFilter, type SegmentFilterKind, type SegmentFilterKindOption, SendArgs, SendGridProvider, type SendGridProviderOptions, SendResult, SuppressionScope, TemplateDoc, VERSION, applyTracking, applyWebhookEvent, compileMailyTemplate, compileTemplate, computeDeliveryTime, createAdminRouter, createPublicRouter, defaultFlowStep, defaultPredicate, defaultSegmentFilter, derivePlaintext, dispatchSend, predicateKind, processNewlyFiredEventTriggers, processOneRunStep, renderTemplate, runTick, sha256Hex, signUnsubscribeToken, sweepStrandedFlowRuns, verifyUnsubscribeToken };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, M as MailProvider, S as SendArgs, b as SendResult, c as Mailer, T as TemplateDoc, d as SuppressionScope, F as FlowStep, e as SegmentFilter, P as Predicate } from './null-CGaNDDlQ.js';
2
- export { f as AuditLogDoc, B as BroadcastDoc, g as BroadcastStatus, h as CircuitBreakerThresholds, i as Collections, j as ContactTagDoc, E as EventDoc, k as FlowDoc, l as FlowGoal, m as FlowRunDoc, n as FlowRunStatus, o as FlowVersionDoc, H as HealthDoc, p as HealthStatus, L as LeadDoc, q as MailerConfig, r as NullProvider, O as OutboxDoc, s as RedisOptions, t as SegmentDefinition, u as SendDoc, v as SendStatus, w as SenderDomainConfig, x as SenderDomainRegistry, y as SenderDomainValidation, z as SubscriptionDoc, D as SubscriptionStatus, G as SuppressionDoc, I as SuppressionReason, J as TemplateKind, K as TemplateVersionDoc, W as WebhookEventDoc, Q as ensureIndexes, U as getCollections, V as validateSenderDomain } from './null-CGaNDDlQ.js';
1
+ import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, M as MailProvider, S as SendArgs, b as SendResult, c as MailTesterFeedback, d as Mailer, T as TemplateDoc, e as SuppressionScope, D as DeliveryWindow, F as FlowStep, f as SegmentFilter, P as Predicate } from './null-B0rPgE5_.js';
2
+ export { g as AuditLogDoc, B as BroadcastDoc, h as BroadcastStatus, i as CircuitBreakerThresholds, j as Collections, k as ContactTagDoc, E as EventDoc, l as FlowDoc, m as FlowGoal, n as FlowRunDoc, o as FlowRunStatus, p as FlowVersionDoc, H as HealthDoc, q as HealthStatus, L as LeadDoc, r as MailerConfig, s as NullProvider, O as OutboxDoc, t as RESERVED_VAR_KEYS, u as RedisOptions, v as SegmentDefinition, w as SendDoc, x as SendStatus, y as SenderDomainConfig, z as SenderDomainRegistry, G as SenderDomainValidation, I as SubscriptionDoc, J as SubscriptionStatus, K as SuppressionDoc, Q as SuppressionReason, U as TemplateKind, V as TemplateVersionDoc, W as VarsAdapter, X as VarsResolveInfo, Y as WebhookEventDoc, Z as defineVars, _ as ensureIndexes, $ as getCollections, a0 as validateSenderDomain, a1 as varsJsonSchema } from './null-B0rPgE5_.js';
3
3
  import { ObjectId, Db, Filter } from 'mongodb';
4
4
  import { Request, Router } from 'express';
5
5
  import Handlebars from 'handlebars';
@@ -140,6 +140,37 @@ declare class SendGridProvider implements MailProvider {
140
140
  parseWebhookEvents(payload: unknown): NormalizedEvent[];
141
141
  }
142
142
 
143
+ /**
144
+ * Mail-Tester deliverability check.
145
+ *
146
+ * 1. Operator clicks "Run deliverability check" in the template editor.
147
+ * 2. Mailery provisions a check (gets a unique test address from Mail-Tester).
148
+ * 3. Mailery sends the rendered draft to that address via the operator's
149
+ * default provider.
150
+ * 4. Mailery polls Mail-Tester for the score (typically arrives 10-30s after
151
+ * receipt) and caches it keyed on a content fingerprint.
152
+ * 5. If the score is below `minScore` the publish endpoint refuses unless
153
+ * the operator passes `bypassMailTester: true` in the publish request.
154
+ *
155
+ * The client interface is pluggable so tests inject a stub. Real
156
+ * implementation hits https://mail-tester.com/api/.
157
+ */
158
+
159
+ interface ProvisionedCheck {
160
+ checkId: string;
161
+ emailAddress: string;
162
+ }
163
+ interface MailTesterResult {
164
+ ready: boolean;
165
+ score: number;
166
+ feedback: MailTesterFeedback[];
167
+ rawSummary: string | null;
168
+ }
169
+ interface MailTesterClient {
170
+ provisionCheck(): Promise<ProvisionedCheck>;
171
+ fetchResult(checkId: string): Promise<MailTesterResult>;
172
+ }
173
+
143
174
  /**
144
175
  * Admin router — serves the prebuilt React SPA + REST endpoints the SPA
145
176
  * consumes. Mount inside a host Express app, gated by host auth.
@@ -154,6 +185,11 @@ interface AdminRouterOptions {
154
185
  spaDir?: string;
155
186
  /** Resolve actor metadata from a Request. Defaults to `human:${req.user?.email || 'anonymous'}`. */
156
187
  getActor?: (req: Request) => string;
188
+ /**
189
+ * Inject a Mail-Tester client (tests use a stub). When omitted, a real
190
+ * client is created from `mailer.config.mailTester`.
191
+ */
192
+ mailTesterClient?: MailTesterClient;
157
193
  }
158
194
  declare function createAdminRouter(mailer: Mailer, opts?: AdminRouterOptions): Router;
159
195
 
@@ -214,8 +250,16 @@ declare function compileMailyTemplate(content: unknown): Promise<CompileResult>;
214
250
  /** Auto-derive plain text from compiled HTML. */
215
251
  declare function derivePlaintext(html: string): string;
216
252
  interface RenderContext {
253
+ /**
254
+ * Host-resolved variables (varsAdapter) live at the context root, so a
255
+ * schema key `user` renders as `{{user.name}}`. Reserved keys below always
256
+ * win over resolved keys.
257
+ */
258
+ [resolvedVar: string]: unknown;
217
259
  contact: Contact;
218
260
  vars: Record<string, unknown>;
261
+ /** Properties of the event that triggered the flow run ({{event.*}}). Empty outside flow sends. */
262
+ event?: Record<string, unknown>;
219
263
  /** URL the recipient hits to one-click unsubscribe. */
220
264
  unsubscribeUrl: string;
221
265
  /** URL to view this email in a browser (when implemented). */
@@ -289,6 +333,20 @@ declare function verifyUnsubscribeToken(token: string, secret: string, now?: Dat
289
333
  */
290
334
  declare function sha256Hex(input: string): string;
291
335
 
336
+ /**
337
+ * Delivery-window math. Pure — no I/O, no Date.now(); callers pass `now`.
338
+ *
339
+ * Given the moment a send step becomes runnable, `computeDeliveryTime`
340
+ * returns the earliest instant the email is allowed to go out. It only ever
341
+ * moves forward: a send already inside its window returns `now` unchanged.
342
+ *
343
+ * Timezone handling uses the Intl API (no dependency). DST transitions are
344
+ * resolved with a two-pass offset fix; the worst case during a transition is
345
+ * being off by the shifted hour, which is fine for email delivery.
346
+ */
347
+
348
+ declare function computeDeliveryTime(now: Date, window: DeliveryWindow, contactTimezone?: string): Date;
349
+
292
350
  /**
293
351
  * Enumerable option lists for the union types in `./types.ts`. Used by the
294
352
  * admin client to render dropdowns + by tooling to enumerate the surface.
@@ -340,6 +398,6 @@ declare const DEDUPE_POLICIES: readonly DedupePolicyOption[];
340
398
  /**
341
399
  * Public exports for the `mailery` package.
342
400
  */
343
- declare const VERSION = "0.1.0";
401
+ declare const VERSION = "0.7.0";
344
402
 
345
- export { AdapterFilter, type AdminRouterOptions, Contact, ContactAdapter, DEDUPE_POLICIES, type DedupePolicyOption, FLOW_STEP_KINDS, FlowStep, type FlowStepKindOption, MailProvider, Mailer, MongoContactAdapter, type MongoContactAdapterOptions, NormalizedEvent, PREDICATE_KINDS, Predicate, type PredicateKind, type PredicateKindOption, type PublicRouterOptions, SEGMENT_FILTER_KINDS, SegmentFilter, type SegmentFilterKind, type SegmentFilterKindOption, SendArgs, SendGridProvider, type SendGridProviderOptions, SendResult, SuppressionScope, TemplateDoc, VERSION, applyTracking, applyWebhookEvent, compileMailyTemplate, compileTemplate, createAdminRouter, createPublicRouter, defaultFlowStep, defaultPredicate, defaultSegmentFilter, derivePlaintext, dispatchSend, predicateKind, processNewlyFiredEventTriggers, processOneRunStep, renderTemplate, runTick, sha256Hex, signUnsubscribeToken, sweepStrandedFlowRuns, verifyUnsubscribeToken };
403
+ export { AdapterFilter, type AdminRouterOptions, Contact, ContactAdapter, DEDUPE_POLICIES, type DedupePolicyOption, DeliveryWindow, FLOW_STEP_KINDS, FlowStep, type FlowStepKindOption, MailProvider, Mailer, MongoContactAdapter, type MongoContactAdapterOptions, NormalizedEvent, PREDICATE_KINDS, Predicate, type PredicateKind, type PredicateKindOption, type PublicRouterOptions, SEGMENT_FILTER_KINDS, SegmentFilter, type SegmentFilterKind, type SegmentFilterKindOption, SendArgs, SendGridProvider, type SendGridProviderOptions, SendResult, SuppressionScope, TemplateDoc, VERSION, applyTracking, applyWebhookEvent, compileMailyTemplate, compileTemplate, computeDeliveryTime, createAdminRouter, createPublicRouter, defaultFlowStep, defaultPredicate, defaultSegmentFilter, derivePlaintext, dispatchSend, predicateKind, processNewlyFiredEventTriggers, processOneRunStep, renderTemplate, runTick, sha256Hex, signUnsubscribeToken, sweepStrandedFlowRuns, verifyUnsubscribeToken };