mailery 0.9.0 → 0.10.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.
- package/dist/admin/spa/index-BTww-4AA.js +41 -0
- package/dist/admin/spa/index-BTww-4AA.js.map +1 -0
- package/dist/admin/spa/index.html +1 -1
- package/dist/admin/spa/{template-editor-DwMKy5sw.js → template-editor-CrOKLpSw.js} +2 -2
- package/dist/admin/spa/{template-editor-DwMKy5sw.js.map → template-editor-CrOKLpSw.js.map} +1 -1
- package/dist/index.cjs +65 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +65 -6
- package/dist/index.js.map +1 -1
- package/dist/{null-BRSHAxvg.d.cts → null-CU_EApC6.d.cts} +16 -0
- package/dist/{null-BRSHAxvg.d.ts → null-CU_EApC6.d.ts} +16 -0
- package/dist/testing.cjs +58 -5
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +58 -5
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
- package/dist/admin/spa/index-B3ndbezm.js +0 -41
- package/dist/admin/spa/index-B3ndbezm.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -414,7 +414,25 @@ var tagInputSchema = zod.z.object({
|
|
|
414
414
|
var abortFlowInputSchema = zod.z.object({
|
|
415
415
|
flowSlug: slugSchema,
|
|
416
416
|
externalId: externalIdSchema,
|
|
417
|
-
reason: zod.z.string().min(1).max(200).optional()
|
|
417
|
+
reason: zod.z.string().min(1).max(200).optional(),
|
|
418
|
+
/**
|
|
419
|
+
* Restrict the abort to runs whose trigger event carried these properties —
|
|
420
|
+
* e.g. `{ accountId }` to cancel one account's series while the same
|
|
421
|
+
* contact's other accounts keep running. Omit to abort every active run for
|
|
422
|
+
* the contact on this flow.
|
|
423
|
+
*
|
|
424
|
+
* Keys and values are both constrained because these go straight into a
|
|
425
|
+
* Mongo query. Values are primitives only: an object value like
|
|
426
|
+
* `{ $ne: null }` would reach the query as an OPERATOR and match every
|
|
427
|
+
* scoped run, turning a one-account abort into abort-everything. Hosts
|
|
428
|
+
* typically pass an id from a request body, so treat it as untrusted. The
|
|
429
|
+
* key regex likewise blocks `$`-prefixed keys and dots (a dot would silently
|
|
430
|
+
* extend the path and change match semantics).
|
|
431
|
+
*/
|
|
432
|
+
matchTriggerProperties: zod.z.record(
|
|
433
|
+
zod.z.string().regex(/^[A-Za-z0-9_]+$/),
|
|
434
|
+
zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.null()])
|
|
435
|
+
).optional()
|
|
418
436
|
});
|
|
419
437
|
var abortAllFlowsInputSchema = abortFlowInputSchema.omit({ flowSlug: true });
|
|
420
438
|
var sendOneOffInputSchema = zod.z.object({
|
|
@@ -483,6 +501,13 @@ var predicateSchema = zod.z.lazy(
|
|
|
483
501
|
zod.z.object({ notHasTag: zod.z.string() }),
|
|
484
502
|
zod.z.object({ fieldEquals: zod.z.object({ field: zod.z.string(), value: zod.z.unknown() }) }),
|
|
485
503
|
zod.z.object({ fieldExists: zod.z.string() }),
|
|
504
|
+
zod.z.object({
|
|
505
|
+
triggerPropertyEquals: zod.z.object({
|
|
506
|
+
key: zod.z.string().min(1),
|
|
507
|
+
value: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.null()])
|
|
508
|
+
})
|
|
509
|
+
}),
|
|
510
|
+
zod.z.object({ triggerPropertyTruthy: zod.z.string().min(1) }),
|
|
486
511
|
zod.z.object({
|
|
487
512
|
hasFiredEvent: zod.z.string(),
|
|
488
513
|
sinceFlowStart: zod.z.boolean().optional(),
|
|
@@ -1666,6 +1691,12 @@ async function evaluatePredicate(predicate, ctx) {
|
|
|
1666
1691
|
if ("fieldExists" in p) {
|
|
1667
1692
|
return ctx.contact.fields[p.fieldExists] !== void 0;
|
|
1668
1693
|
}
|
|
1694
|
+
if ("triggerPropertyEquals" in p) {
|
|
1695
|
+
return (ctx.run.triggerEvent?.properties ?? {})[p.triggerPropertyEquals.key] === p.triggerPropertyEquals.value;
|
|
1696
|
+
}
|
|
1697
|
+
if ("triggerPropertyTruthy" in p) {
|
|
1698
|
+
return Boolean((ctx.run.triggerEvent?.properties ?? {})[p.triggerPropertyTruthy]);
|
|
1699
|
+
}
|
|
1669
1700
|
if ("subscriptionStatus" in p) {
|
|
1670
1701
|
const sub = await ctx.collections.subscriptions.findOne({ externalId: ctx.contact.externalId });
|
|
1671
1702
|
return sub?.status === p.subscriptionStatus;
|
|
@@ -2272,9 +2303,11 @@ function sha256(s) {
|
|
|
2272
2303
|
}
|
|
2273
2304
|
|
|
2274
2305
|
// src/server/runner/step.ts
|
|
2306
|
+
var DUE_SKEW_MS = 1e3;
|
|
2275
2307
|
async function processOneRunStep(runId, ctx) {
|
|
2276
2308
|
const run = await ctx.collections.flowRuns.findOne({ _id: runId });
|
|
2277
2309
|
if (!run || run.status !== "active") return;
|
|
2310
|
+
if (run.nextActionAt && run.nextActionAt.getTime() > Date.now() + DUE_SKEW_MS) return;
|
|
2278
2311
|
const flow = await ctx.collections.flows.findOne({ _id: run.flowId });
|
|
2279
2312
|
if (!flow) {
|
|
2280
2313
|
await failFlowRun(run, "flow_missing", ctx);
|
|
@@ -2418,7 +2451,14 @@ async function handleFireEvent(run, step, ctx) {
|
|
|
2418
2451
|
await ctx.collections.events.insertOne({
|
|
2419
2452
|
externalId: run.externalId,
|
|
2420
2453
|
name: step.eventName,
|
|
2421
|
-
|
|
2454
|
+
// Inherit the triggering event's properties so a handoff carries the
|
|
2455
|
+
// context that identifies what the run is ABOUT (which account, order,
|
|
2456
|
+
// subscription, ...). A step's `properties` are static — authored once in
|
|
2457
|
+
// the flow definition — so without this a fired event can only ever say
|
|
2458
|
+
// "this contact", losing the scope the originating event supplied, and
|
|
2459
|
+
// the receiving flow has nothing to resolve variables against. Explicit
|
|
2460
|
+
// step.properties win on conflict.
|
|
2461
|
+
properties: { ...run.triggerEvent?.properties ?? {}, ...step.properties ?? {} },
|
|
2422
2462
|
dedupeKey,
|
|
2423
2463
|
occurredAt: /* @__PURE__ */ new Date(),
|
|
2424
2464
|
createdAt: /* @__PURE__ */ new Date()
|
|
@@ -4253,14 +4293,25 @@ var Mailer = class _Mailer {
|
|
|
4253
4293
|
* same handler that processes the business event ("user upgraded").
|
|
4254
4294
|
*/
|
|
4255
4295
|
async abortFlow(flowSlug, externalId, opts = {}) {
|
|
4256
|
-
const parsed = abortFlowInputSchema.parse({
|
|
4296
|
+
const parsed = abortFlowInputSchema.parse({
|
|
4297
|
+
flowSlug,
|
|
4298
|
+
externalId,
|
|
4299
|
+
reason: opts.reason,
|
|
4300
|
+
matchTriggerProperties: opts.matchTriggerProperties
|
|
4301
|
+
});
|
|
4257
4302
|
const flow = await this.collections.flows.findOne(
|
|
4258
4303
|
{ slug: parsed.flowSlug },
|
|
4259
4304
|
{ projection: { _id: 1 } }
|
|
4260
4305
|
);
|
|
4261
4306
|
if (!flow) throw new Error(`abortFlow: unknown flow slug "${parsed.flowSlug}"`);
|
|
4307
|
+
const triggerMatch = Object.fromEntries(
|
|
4308
|
+
Object.entries(parsed.matchTriggerProperties ?? {}).map(([k, v]) => [
|
|
4309
|
+
`triggerEvent.properties.${k}`,
|
|
4310
|
+
v
|
|
4311
|
+
])
|
|
4312
|
+
);
|
|
4262
4313
|
const result = await this.abortActiveRuns(
|
|
4263
|
-
{ externalId: parsed.externalId, flowId: flow._id },
|
|
4314
|
+
{ externalId: parsed.externalId, flowId: flow._id, ...triggerMatch },
|
|
4264
4315
|
parsed.reason ? `aborted_by_host:${parsed.reason}` : "aborted_by_host"
|
|
4265
4316
|
);
|
|
4266
4317
|
if (result.abortedRuns > 0 || result.cancelledSends > 0) {
|
|
@@ -4268,7 +4319,9 @@ var Mailer = class _Mailer {
|
|
|
4268
4319
|
actor: "host",
|
|
4269
4320
|
action: "flow.abort",
|
|
4270
4321
|
resource: { collection: "mailer_flow_runs", slug: parsed.flowSlug },
|
|
4271
|
-
|
|
4322
|
+
// Record the scope: without it a one-account abort and an abort-every-
|
|
4323
|
+
// run-for-this-contact are indistinguishable in the audit trail.
|
|
4324
|
+
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}` : ""}`
|
|
4272
4325
|
});
|
|
4273
4326
|
}
|
|
4274
4327
|
return result;
|
|
@@ -7434,6 +7487,8 @@ var PREDICATE_KINDS = [
|
|
|
7434
7487
|
{ value: "notHasTag", label: "does NOT have tag" },
|
|
7435
7488
|
{ value: "fieldEquals", label: "field equals" },
|
|
7436
7489
|
{ value: "fieldExists", label: "field exists" },
|
|
7490
|
+
{ value: "triggerPropertyEquals", label: "trigger property equals" },
|
|
7491
|
+
{ value: "triggerPropertyTruthy", label: "trigger property is set" },
|
|
7437
7492
|
{ value: "hasFiredEvent", label: "fired event" },
|
|
7438
7493
|
{ value: "notHasFiredEvent", label: "has NOT fired event" },
|
|
7439
7494
|
{ value: "subscriptionStatus", label: "subscription status" },
|
|
@@ -7461,6 +7516,10 @@ function defaultPredicate(kind) {
|
|
|
7461
7516
|
return { fieldEquals: { field: "tier", value: "Pro" } };
|
|
7462
7517
|
case "fieldExists":
|
|
7463
7518
|
return { fieldExists: "tier" };
|
|
7519
|
+
case "triggerPropertyEquals":
|
|
7520
|
+
return { triggerPropertyEquals: { key: "plan", value: "pro" } };
|
|
7521
|
+
case "triggerPropertyTruthy":
|
|
7522
|
+
return { triggerPropertyTruthy: "wasReferred" };
|
|
7464
7523
|
case "hasFiredEvent":
|
|
7465
7524
|
return { hasFiredEvent: "Activated app" };
|
|
7466
7525
|
case "notHasFiredEvent":
|
|
@@ -7551,7 +7610,7 @@ var DEDUPE_POLICIES = [
|
|
|
7551
7610
|
];
|
|
7552
7611
|
|
|
7553
7612
|
// src/server/index.ts
|
|
7554
|
-
var VERSION = "0.
|
|
7613
|
+
var VERSION = "0.10.1" ;
|
|
7555
7614
|
|
|
7556
7615
|
exports.DEDUPE_POLICIES = DEDUPE_POLICIES;
|
|
7557
7616
|
exports.FLOW_STEP_KINDS = FLOW_STEP_KINDS;
|