mailery 0.9.0 → 0.10.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 MailTesterFeedback, d as Mailer, T as TemplateDoc, e as SuppressionScope, D as DeliveryWindow, F as FlowStep, f as SegmentFilter, P as Predicate } from './null-BRSHAxvg.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-BRSHAxvg.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-CU_EApC6.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-CU_EApC6.cjs';
3
3
  import { ObjectId, Db, Filter } from 'mongodb';
4
4
  import { Request, Router } from 'express';
5
5
  import Handlebars from 'handlebars';
@@ -367,7 +367,7 @@ interface FlowStepKindOption {
367
367
  }
368
368
  declare const FLOW_STEP_KINDS: readonly FlowStepKindOption[];
369
369
  declare function defaultFlowStep(type: FlowStep['type']): FlowStep;
370
- type PredicateKind = 'hasTag' | 'notHasTag' | 'fieldEquals' | 'fieldExists' | 'hasFiredEvent' | 'notHasFiredEvent' | 'subscriptionStatus' | 'hasOpened' | 'hasClicked' | 'hasOpenedExcludingBots' | 'hasClickedExcludingBots' | 'openedAtLeastN' | 'clickedAtLeastN';
370
+ type PredicateKind = 'hasTag' | 'notHasTag' | 'fieldEquals' | 'fieldExists' | 'triggerPropertyEquals' | 'triggerPropertyTruthy' | 'hasFiredEvent' | 'notHasFiredEvent' | 'subscriptionStatus' | 'hasOpened' | 'hasClicked' | 'hasOpenedExcludingBots' | 'hasClickedExcludingBots' | 'openedAtLeastN' | 'clickedAtLeastN';
371
371
  interface PredicateKindOption {
372
372
  value: PredicateKind;
373
373
  label: string;
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 MailTesterFeedback, d as Mailer, T as TemplateDoc, e as SuppressionScope, D as DeliveryWindow, F as FlowStep, f as SegmentFilter, P as Predicate } from './null-BRSHAxvg.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-BRSHAxvg.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-CU_EApC6.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-CU_EApC6.js';
3
3
  import { ObjectId, Db, Filter } from 'mongodb';
4
4
  import { Request, Router } from 'express';
5
5
  import Handlebars from 'handlebars';
@@ -367,7 +367,7 @@ interface FlowStepKindOption {
367
367
  }
368
368
  declare const FLOW_STEP_KINDS: readonly FlowStepKindOption[];
369
369
  declare function defaultFlowStep(type: FlowStep['type']): FlowStep;
370
- type PredicateKind = 'hasTag' | 'notHasTag' | 'fieldEquals' | 'fieldExists' | 'hasFiredEvent' | 'notHasFiredEvent' | 'subscriptionStatus' | 'hasOpened' | 'hasClicked' | 'hasOpenedExcludingBots' | 'hasClickedExcludingBots' | 'openedAtLeastN' | 'clickedAtLeastN';
370
+ type PredicateKind = 'hasTag' | 'notHasTag' | 'fieldEquals' | 'fieldExists' | 'triggerPropertyEquals' | 'triggerPropertyTruthy' | 'hasFiredEvent' | 'notHasFiredEvent' | 'subscriptionStatus' | 'hasOpened' | 'hasClicked' | 'hasOpenedExcludingBots' | 'hasClickedExcludingBots' | 'openedAtLeastN' | 'clickedAtLeastN';
371
371
  interface PredicateKindOption {
372
372
  value: PredicateKind;
373
373
  label: string;
package/dist/index.js CHANGED
@@ -396,7 +396,25 @@ var tagInputSchema = z.object({
396
396
  var abortFlowInputSchema = z.object({
397
397
  flowSlug: slugSchema,
398
398
  externalId: externalIdSchema,
399
- reason: z.string().min(1).max(200).optional()
399
+ reason: z.string().min(1).max(200).optional(),
400
+ /**
401
+ * Restrict the abort to runs whose trigger event carried these properties —
402
+ * e.g. `{ accountId }` to cancel one account's series while the same
403
+ * contact's other accounts keep running. Omit to abort every active run for
404
+ * the contact on this flow.
405
+ *
406
+ * Keys and values are both constrained because these go straight into a
407
+ * Mongo query. Values are primitives only: an object value like
408
+ * `{ $ne: null }` would reach the query as an OPERATOR and match every
409
+ * scoped run, turning a one-account abort into abort-everything. Hosts
410
+ * typically pass an id from a request body, so treat it as untrusted. The
411
+ * key regex likewise blocks `$`-prefixed keys and dots (a dot would silently
412
+ * extend the path and change match semantics).
413
+ */
414
+ matchTriggerProperties: z.record(
415
+ z.string().regex(/^[A-Za-z0-9_]+$/),
416
+ z.union([z.string(), z.number(), z.boolean(), z.null()])
417
+ ).optional()
400
418
  });
401
419
  var abortAllFlowsInputSchema = abortFlowInputSchema.omit({ flowSlug: true });
402
420
  var sendOneOffInputSchema = z.object({
@@ -465,6 +483,13 @@ var predicateSchema = z.lazy(
465
483
  z.object({ notHasTag: z.string() }),
466
484
  z.object({ fieldEquals: z.object({ field: z.string(), value: z.unknown() }) }),
467
485
  z.object({ fieldExists: z.string() }),
486
+ z.object({
487
+ triggerPropertyEquals: z.object({
488
+ key: z.string().min(1),
489
+ value: z.union([z.string(), z.number(), z.boolean(), z.null()])
490
+ })
491
+ }),
492
+ z.object({ triggerPropertyTruthy: z.string().min(1) }),
468
493
  z.object({
469
494
  hasFiredEvent: z.string(),
470
495
  sinceFlowStart: z.boolean().optional(),
@@ -1648,6 +1673,12 @@ async function evaluatePredicate(predicate, ctx) {
1648
1673
  if ("fieldExists" in p) {
1649
1674
  return ctx.contact.fields[p.fieldExists] !== void 0;
1650
1675
  }
1676
+ if ("triggerPropertyEquals" in p) {
1677
+ return (ctx.run.triggerEvent?.properties ?? {})[p.triggerPropertyEquals.key] === p.triggerPropertyEquals.value;
1678
+ }
1679
+ if ("triggerPropertyTruthy" in p) {
1680
+ return Boolean((ctx.run.triggerEvent?.properties ?? {})[p.triggerPropertyTruthy]);
1681
+ }
1651
1682
  if ("subscriptionStatus" in p) {
1652
1683
  const sub = await ctx.collections.subscriptions.findOne({ externalId: ctx.contact.externalId });
1653
1684
  return sub?.status === p.subscriptionStatus;
@@ -2400,7 +2431,14 @@ async function handleFireEvent(run, step, ctx) {
2400
2431
  await ctx.collections.events.insertOne({
2401
2432
  externalId: run.externalId,
2402
2433
  name: step.eventName,
2403
- properties: step.properties ?? {},
2434
+ // Inherit the triggering event's properties so a handoff carries the
2435
+ // context that identifies what the run is ABOUT (which account, order,
2436
+ // subscription, ...). A step's `properties` are static — authored once in
2437
+ // the flow definition — so without this a fired event can only ever say
2438
+ // "this contact", losing the scope the originating event supplied, and
2439
+ // the receiving flow has nothing to resolve variables against. Explicit
2440
+ // step.properties win on conflict.
2441
+ properties: { ...run.triggerEvent?.properties ?? {}, ...step.properties ?? {} },
2404
2442
  dedupeKey,
2405
2443
  occurredAt: /* @__PURE__ */ new Date(),
2406
2444
  createdAt: /* @__PURE__ */ new Date()
@@ -4235,14 +4273,25 @@ var Mailer = class _Mailer {
4235
4273
  * same handler that processes the business event ("user upgraded").
4236
4274
  */
4237
4275
  async abortFlow(flowSlug, externalId, opts = {}) {
4238
- const parsed = abortFlowInputSchema.parse({ flowSlug, externalId, reason: opts.reason });
4276
+ const parsed = abortFlowInputSchema.parse({
4277
+ flowSlug,
4278
+ externalId,
4279
+ reason: opts.reason,
4280
+ matchTriggerProperties: opts.matchTriggerProperties
4281
+ });
4239
4282
  const flow = await this.collections.flows.findOne(
4240
4283
  { slug: parsed.flowSlug },
4241
4284
  { projection: { _id: 1 } }
4242
4285
  );
4243
4286
  if (!flow) throw new Error(`abortFlow: unknown flow slug "${parsed.flowSlug}"`);
4287
+ const triggerMatch = Object.fromEntries(
4288
+ Object.entries(parsed.matchTriggerProperties ?? {}).map(([k, v]) => [
4289
+ `triggerEvent.properties.${k}`,
4290
+ v
4291
+ ])
4292
+ );
4244
4293
  const result = await this.abortActiveRuns(
4245
- { externalId: parsed.externalId, flowId: flow._id },
4294
+ { externalId: parsed.externalId, flowId: flow._id, ...triggerMatch },
4246
4295
  parsed.reason ? `aborted_by_host:${parsed.reason}` : "aborted_by_host"
4247
4296
  );
4248
4297
  if (result.abortedRuns > 0 || result.cancelledSends > 0) {
@@ -4250,7 +4299,9 @@ var Mailer = class _Mailer {
4250
4299
  actor: "host",
4251
4300
  action: "flow.abort",
4252
4301
  resource: { collection: "mailer_flow_runs", slug: parsed.flowSlug },
4253
- diffSummary: `abortFlow slug=${parsed.flowSlug} externalId=${parsed.externalId} runs=${result.abortedRuns} sends=${result.cancelledSends}${parsed.reason ? ` reason=${parsed.reason}` : ""}`
4302
+ // Record the scope: without it a one-account abort and an abort-every-
4303
+ // run-for-this-contact are indistinguishable in the audit trail.
4304
+ diffSummary: `abortFlow slug=${parsed.flowSlug} externalId=${parsed.externalId} scope=${parsed.matchTriggerProperties ? JSON.stringify(parsed.matchTriggerProperties) : "all"} runs=${result.abortedRuns} sends=${result.cancelledSends}${parsed.reason ? ` reason=${parsed.reason}` : ""}`
4254
4305
  });
4255
4306
  }
4256
4307
  return result;
@@ -7416,6 +7467,8 @@ var PREDICATE_KINDS = [
7416
7467
  { value: "notHasTag", label: "does NOT have tag" },
7417
7468
  { value: "fieldEquals", label: "field equals" },
7418
7469
  { value: "fieldExists", label: "field exists" },
7470
+ { value: "triggerPropertyEquals", label: "trigger property equals" },
7471
+ { value: "triggerPropertyTruthy", label: "trigger property is set" },
7419
7472
  { value: "hasFiredEvent", label: "fired event" },
7420
7473
  { value: "notHasFiredEvent", label: "has NOT fired event" },
7421
7474
  { value: "subscriptionStatus", label: "subscription status" },
@@ -7443,6 +7496,10 @@ function defaultPredicate(kind) {
7443
7496
  return { fieldEquals: { field: "tier", value: "Pro" } };
7444
7497
  case "fieldExists":
7445
7498
  return { fieldExists: "tier" };
7499
+ case "triggerPropertyEquals":
7500
+ return { triggerPropertyEquals: { key: "plan", value: "pro" } };
7501
+ case "triggerPropertyTruthy":
7502
+ return { triggerPropertyTruthy: "wasReferred" };
7446
7503
  case "hasFiredEvent":
7447
7504
  return { hasFiredEvent: "Activated app" };
7448
7505
  case "notHasFiredEvent":
@@ -7533,7 +7590,7 @@ var DEDUPE_POLICIES = [
7533
7590
  ];
7534
7591
 
7535
7592
  // src/server/index.ts
7536
- var VERSION = "0.9.0" ;
7593
+ var VERSION = "0.10.0" ;
7537
7594
 
7538
7595
  export { DEDUPE_POLICIES, FLOW_STEP_KINDS, Mailer, MongoContactAdapter, NullProvider, PREDICATE_KINDS, RESERVED_VAR_KEYS, SEGMENT_FILTER_KINDS, SendGridProvider, VERSION, applyTracking, applyWebhookEvent, compileMailyTemplate, compileTemplate, computeDeliveryTime, createAdminRouter, createPublicRouter, defaultFlowStep, defaultPredicate, defaultSegmentFilter, defineVars, derivePlaintext, dispatchSend, ensureIndexes, getCollections, predicateKind, processNewlyFiredEventTriggers, processOneRunStep, renderTemplate, runTick, sha256Hex, signUnsubscribeToken, sweepStrandedFlowRuns, validateSenderDomain, varsJsonSchema, verifyUnsubscribeToken };
7539
7596
  //# sourceMappingURL=index.js.map