mailery 0.16.5 → 0.16.7
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.cjs +135 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +135 -14
- package/dist/index.js.map +1 -1
- package/dist/{null-CDlseQxO.d.cts → null-oKPS74Gr.d.cts} +43 -0
- package/dist/{null-CDlseQxO.d.ts → null-oKPS74Gr.d.ts} +43 -0
- package/dist/testing.cjs +50 -0
- 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 +50 -0
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
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, F as FlowStep, e as Collections, f as FlowDoc, g as SuppressionScope, D as DeliveryWindow, h as SegmentFilter, P as Predicate } from './null-
|
|
2
|
-
export { i as AuditLogDoc, B as BotFilterConfig, j as BroadcastDoc, k as BroadcastStatus, l as CircuitBreakerThresholds, m as ContactTagDoc, E as EventDoc, n as FlowGoal, o as FlowRunDoc, p as FlowRunStatus, q as FlowVersionDoc, H as HealthDoc, r as HealthStatus, L as LeadDoc, s as MailerConfig, t as NullProvider, O as OutboxDoc, u as RESERVED_VAR_KEYS, v as RedisOptions, w as SegmentDefinition, x as SendDoc, y as SendStatus, z as SenderDomainConfig, G as SenderDomainRegistry, I as SenderDomainValidation, J as SubscriptionDoc, K as SubscriptionStatus, Q as SuppressionDoc, U as SuppressionReason, V as TemplateKind, W as TemplateVersionDoc, X as VarsAdapter, Y as VarsResolveInfo, Z as WebhookEventDoc, _ as defineVars, $ as ensureIndexes, a0 as getCollections, a1 as validateSenderDomain, a2 as varsJsonSchema } from './null-
|
|
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, F as FlowStep, e as Collections, f as FlowDoc, g as SuppressionScope, D as DeliveryWindow, h as SegmentFilter, P as Predicate } from './null-oKPS74Gr.cjs';
|
|
2
|
+
export { i as AuditLogDoc, B as BotFilterConfig, j as BroadcastDoc, k as BroadcastStatus, l as CircuitBreakerThresholds, m as ContactTagDoc, E as EventDoc, n as FlowGoal, o as FlowRunDoc, p as FlowRunStatus, q as FlowVersionDoc, H as HealthDoc, r as HealthStatus, L as LeadDoc, s as MailerConfig, t as NullProvider, O as OutboxDoc, u as RESERVED_VAR_KEYS, v as RedisOptions, w as SegmentDefinition, x as SendDoc, y as SendStatus, z as SenderDomainConfig, G as SenderDomainRegistry, I as SenderDomainValidation, J as SubscriptionDoc, K as SubscriptionStatus, Q as SuppressionDoc, U as SuppressionReason, V as TemplateKind, W as TemplateVersionDoc, X as VarsAdapter, Y as VarsResolveInfo, Z as WebhookEventDoc, _ as defineVars, $ as ensureIndexes, a0 as getCollections, a1 as validateSenderDomain, a2 as varsJsonSchema } from './null-oKPS74Gr.cjs';
|
|
3
3
|
import { ObjectId, Db, Filter } from 'mongodb';
|
|
4
4
|
import { Request, Router } from 'express';
|
|
5
5
|
import Handlebars from 'handlebars';
|
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, F as FlowStep, e as Collections, f as FlowDoc, g as SuppressionScope, D as DeliveryWindow, h as SegmentFilter, P as Predicate } from './null-
|
|
2
|
-
export { i as AuditLogDoc, B as BotFilterConfig, j as BroadcastDoc, k as BroadcastStatus, l as CircuitBreakerThresholds, m as ContactTagDoc, E as EventDoc, n as FlowGoal, o as FlowRunDoc, p as FlowRunStatus, q as FlowVersionDoc, H as HealthDoc, r as HealthStatus, L as LeadDoc, s as MailerConfig, t as NullProvider, O as OutboxDoc, u as RESERVED_VAR_KEYS, v as RedisOptions, w as SegmentDefinition, x as SendDoc, y as SendStatus, z as SenderDomainConfig, G as SenderDomainRegistry, I as SenderDomainValidation, J as SubscriptionDoc, K as SubscriptionStatus, Q as SuppressionDoc, U as SuppressionReason, V as TemplateKind, W as TemplateVersionDoc, X as VarsAdapter, Y as VarsResolveInfo, Z as WebhookEventDoc, _ as defineVars, $ as ensureIndexes, a0 as getCollections, a1 as validateSenderDomain, a2 as varsJsonSchema } from './null-
|
|
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, F as FlowStep, e as Collections, f as FlowDoc, g as SuppressionScope, D as DeliveryWindow, h as SegmentFilter, P as Predicate } from './null-oKPS74Gr.js';
|
|
2
|
+
export { i as AuditLogDoc, B as BotFilterConfig, j as BroadcastDoc, k as BroadcastStatus, l as CircuitBreakerThresholds, m as ContactTagDoc, E as EventDoc, n as FlowGoal, o as FlowRunDoc, p as FlowRunStatus, q as FlowVersionDoc, H as HealthDoc, r as HealthStatus, L as LeadDoc, s as MailerConfig, t as NullProvider, O as OutboxDoc, u as RESERVED_VAR_KEYS, v as RedisOptions, w as SegmentDefinition, x as SendDoc, y as SendStatus, z as SenderDomainConfig, G as SenderDomainRegistry, I as SenderDomainValidation, J as SubscriptionDoc, K as SubscriptionStatus, Q as SuppressionDoc, U as SuppressionReason, V as TemplateKind, W as TemplateVersionDoc, X as VarsAdapter, Y as VarsResolveInfo, Z as WebhookEventDoc, _ as defineVars, $ as ensureIndexes, a0 as getCollections, a1 as validateSenderDomain, a2 as varsJsonSchema } from './null-oKPS74Gr.js';
|
|
3
3
|
import { ObjectId, Db, Filter } from 'mongodb';
|
|
4
4
|
import { Request, Router } from 'express';
|
|
5
5
|
import Handlebars from 'handlebars';
|
package/dist/index.js
CHANGED
|
@@ -453,6 +453,15 @@ var unsubscribeInputSchema = z.object({
|
|
|
453
453
|
source: z.string().max(256).default("manual"),
|
|
454
454
|
notes: z.string().max(1024).optional()
|
|
455
455
|
});
|
|
456
|
+
var resubscribeInputSchema = z.object({
|
|
457
|
+
externalId: externalIdSchema,
|
|
458
|
+
/** `marketing` clears marketing + all-scope opt-outs; `all` clears every scope. */
|
|
459
|
+
scope: z.enum(["marketing", "all"]).default("marketing"),
|
|
460
|
+
source: z.string().min(1).max(256),
|
|
461
|
+
consentTimestamp: z.date().optional(),
|
|
462
|
+
consentIp: z.string().optional(),
|
|
463
|
+
consentUserAgent: z.string().optional()
|
|
464
|
+
});
|
|
456
465
|
var suppressInputSchema = z.object({
|
|
457
466
|
email: emailSchema,
|
|
458
467
|
scope: unsubscribeScopeSchema,
|
|
@@ -1014,6 +1023,15 @@ function verifyDoiToken(token, secret, now = /* @__PURE__ */ new Date()) {
|
|
|
1014
1023
|
}
|
|
1015
1024
|
|
|
1016
1025
|
// src/server/unsubscribe.ts
|
|
1026
|
+
async function clearUnsubscribeSuppressions(collections, email, scope) {
|
|
1027
|
+
const scopes = scope === "all" ? ["all", "marketing", "transactional"] : ["marketing", "all"];
|
|
1028
|
+
const result = await collections.suppressions.deleteMany({
|
|
1029
|
+
email,
|
|
1030
|
+
reason: "unsubscribed",
|
|
1031
|
+
scope: { $in: scopes }
|
|
1032
|
+
});
|
|
1033
|
+
return result.deletedCount ?? 0;
|
|
1034
|
+
}
|
|
1017
1035
|
async function applyUnsubscribe(collections, input, now = /* @__PURE__ */ new Date()) {
|
|
1018
1036
|
const normalized = input.email;
|
|
1019
1037
|
await collections.suppressions.updateOne(
|
|
@@ -4717,6 +4735,38 @@ var Mailer = class _Mailer {
|
|
|
4717
4735
|
}
|
|
4718
4736
|
}
|
|
4719
4737
|
}
|
|
4738
|
+
/**
|
|
4739
|
+
* An explicit opt-in from a contact who unsubscribed before.
|
|
4740
|
+
*
|
|
4741
|
+
* `upsertSubscription` alone is not enough: an unsubscribe also writes a
|
|
4742
|
+
* `mailer_suppressions` row, and the suppression check runs at enqueue
|
|
4743
|
+
* time regardless of subscription status — so a contact re-subscribed
|
|
4744
|
+
* through `upsertSubscription` reads as subscribed while every send comes
|
|
4745
|
+
* back `suppressed`. This clears the opt-out rows (only those: a bounce or
|
|
4746
|
+
* complaint is not the contact's to reverse), then upserts the subscription
|
|
4747
|
+
* through the normal path, double opt-in included.
|
|
4748
|
+
*
|
|
4749
|
+
* Deliberately a separate method rather than a side effect of
|
|
4750
|
+
* `upsertSubscription`, so a backfill or a model hook that re-upserts every
|
|
4751
|
+
* account cannot silently resurrect addresses that opted out.
|
|
4752
|
+
*/
|
|
4753
|
+
async resubscribe(input) {
|
|
4754
|
+
const parsed = resubscribeInputSchema.parse(input);
|
|
4755
|
+
const contact = await this.adapter.getById(parsed.externalId);
|
|
4756
|
+
if (!contact) throw new Error(`adapter has no contact for externalId ${parsed.externalId}`);
|
|
4757
|
+
const removedSuppressions = await clearUnsubscribeSuppressions(this.collections, contact.email, parsed.scope);
|
|
4758
|
+
const { scope: _scope, ...subscription } = parsed;
|
|
4759
|
+
await this.upsertSubscription(subscription);
|
|
4760
|
+
if (removedSuppressions > 0) {
|
|
4761
|
+
await this.audit({
|
|
4762
|
+
actor: `host:${parsed.source}`,
|
|
4763
|
+
action: "contact.resubscribe",
|
|
4764
|
+
resource: { collection: "mailer_suppressions", id: parsed.externalId },
|
|
4765
|
+
diffSummary: `${contact.email}: removed ${removedSuppressions} unsubscribed suppression${removedSuppressions === 1 ? "" : "s"} (${parsed.scope})`
|
|
4766
|
+
});
|
|
4767
|
+
}
|
|
4768
|
+
return { removedSuppressions };
|
|
4769
|
+
}
|
|
4720
4770
|
/**
|
|
4721
4771
|
* The writes live in `server/unsubscribe.ts` so the pending-unsubscribe
|
|
4722
4772
|
* drain (INVARIANT 8) replays a journaled opt-out through exactly this path
|
|
@@ -5208,7 +5258,7 @@ ${input.plainText}`);
|
|
|
5208
5258
|
});
|
|
5209
5259
|
}
|
|
5210
5260
|
}
|
|
5211
|
-
const offDomain = findOffDomainLinkHosts(input.html, input.fromEmail);
|
|
5261
|
+
const offDomain = findOffDomainLinkHosts(input.html, input.fromEmail, ownHosts(config));
|
|
5212
5262
|
if (offDomain.majority && offDomain.hosts.length > 0) {
|
|
5213
5263
|
issues.push({
|
|
5214
5264
|
rule: "offdomain_links",
|
|
@@ -5293,12 +5343,23 @@ function hostnameOf(url) {
|
|
|
5293
5343
|
}
|
|
5294
5344
|
}
|
|
5295
5345
|
function hasBareUrlInVisibleText(html) {
|
|
5296
|
-
const stripped = html.replace(/<a\b[^>]*>.*?<\/a>/gis, " ").replace(/<style\b[^>]*>.*?<\/style>/gis, " ").replace(/<script\b[^>]*>.*?<\/script>/gis, " ").replace(/<head\b[^>]*>.*?<\/head>/gis, " ");
|
|
5346
|
+
const stripped = html.replace(/<a\b[^>]*>.*?<\/a>/gis, " ").replace(/<style\b[^>]*>.*?<\/style>/gis, " ").replace(/<script\b[^>]*>.*?<\/script>/gis, " ").replace(/<head\b[^>]*>.*?<\/head>/gis, " ").replace(/<[^>]*>/g, " ");
|
|
5297
5347
|
return /https?:\/\/[^\s<>"']+/i.test(stripped);
|
|
5298
5348
|
}
|
|
5299
|
-
function
|
|
5349
|
+
function ownHosts(config) {
|
|
5350
|
+
const hosts = [];
|
|
5351
|
+
const publicHost = config.publicUrl ? hostnameOf(config.publicUrl) : null;
|
|
5352
|
+
if (publicHost) hosts.push(publicHost);
|
|
5353
|
+
for (const d of config.linkDomains ?? []) {
|
|
5354
|
+
const host = hostnameOf(/^https?:\/\//i.test(d) ? d : `https://${d}`);
|
|
5355
|
+
if (host) hosts.push(host);
|
|
5356
|
+
}
|
|
5357
|
+
return hosts;
|
|
5358
|
+
}
|
|
5359
|
+
function findOffDomainLinkHosts(html, fromEmail, extraOwnHosts = []) {
|
|
5300
5360
|
const fromDomain = fromEmail.split("@")[1]?.toLowerCase();
|
|
5301
5361
|
if (!fromDomain) return { hosts: [], majority: false };
|
|
5362
|
+
const own = [fromDomain, ...extraOwnHosts.map((h) => h.toLowerCase())];
|
|
5302
5363
|
const hosts = /* @__PURE__ */ new Set();
|
|
5303
5364
|
let total = 0;
|
|
5304
5365
|
let off = 0;
|
|
@@ -5307,7 +5368,7 @@ function findOffDomainLinkHosts(html, fromEmail) {
|
|
|
5307
5368
|
const host = hostnameOf(href);
|
|
5308
5369
|
if (!host) continue;
|
|
5309
5370
|
total++;
|
|
5310
|
-
if (sameSite(host,
|
|
5371
|
+
if (own.some((o) => sameSite(host, o))) continue;
|
|
5311
5372
|
off++;
|
|
5312
5373
|
hosts.add(host);
|
|
5313
5374
|
}
|
|
@@ -7335,7 +7396,12 @@ function createAdminApiRouter(mailer, opts = {}) {
|
|
|
7335
7396
|
if (!plainText && typeof tpl.body?.plainText === "string") plainText = tpl.body.plainText;
|
|
7336
7397
|
const lint = lintTemplate(
|
|
7337
7398
|
{ subject, preheader, mjml, editorJson, html, plainText, kind, fromEmail },
|
|
7338
|
-
{
|
|
7399
|
+
{
|
|
7400
|
+
senderDomains: mailer.config.senderDomains,
|
|
7401
|
+
varsJsonSchema: varsSchema,
|
|
7402
|
+
publicUrl: mailer.config.publicUrl,
|
|
7403
|
+
linkDomains: mailer.config.linkDomains
|
|
7404
|
+
}
|
|
7339
7405
|
);
|
|
7340
7406
|
res.json({ ...lint, compileFailed: false });
|
|
7341
7407
|
})
|
|
@@ -7386,7 +7452,12 @@ function createAdminApiRouter(mailer, opts = {}) {
|
|
|
7386
7452
|
kind: tpl.kind,
|
|
7387
7453
|
fromEmail: tpl.fromEmail
|
|
7388
7454
|
},
|
|
7389
|
-
{
|
|
7455
|
+
{
|
|
7456
|
+
senderDomains: mailer.config.senderDomains,
|
|
7457
|
+
varsJsonSchema: varsSchema,
|
|
7458
|
+
publicUrl: mailer.config.publicUrl,
|
|
7459
|
+
linkDomains: mailer.config.linkDomains
|
|
7460
|
+
}
|
|
7390
7461
|
);
|
|
7391
7462
|
if (lint.errors.length > 0) {
|
|
7392
7463
|
return res.status(422).json({
|
|
@@ -8253,7 +8324,11 @@ function unitToMs2(value, unit) {
|
|
|
8253
8324
|
}
|
|
8254
8325
|
|
|
8255
8326
|
// src/server/api/agent.ts
|
|
8256
|
-
var VERSION = "0.16.
|
|
8327
|
+
var VERSION = "0.16.7" ;
|
|
8328
|
+
var agentTagsInputSchema = z.object({
|
|
8329
|
+
add: z.array(z.string().min(1).max(128)).max(25).default([]),
|
|
8330
|
+
remove: z.array(z.string().min(1).max(128)).max(25).default([])
|
|
8331
|
+
});
|
|
8257
8332
|
var publishTemplateInputSchema = z.object({
|
|
8258
8333
|
name: z.string().min(1).max(200),
|
|
8259
8334
|
description: z.string().max(2e3).default(""),
|
|
@@ -8498,7 +8573,12 @@ function createAgentRouter(mailer, opts) {
|
|
|
8498
8573
|
kind: input.kind,
|
|
8499
8574
|
fromEmail: input.fromEmail
|
|
8500
8575
|
},
|
|
8501
|
-
{
|
|
8576
|
+
{
|
|
8577
|
+
senderDomains: mailer.config.senderDomains,
|
|
8578
|
+
varsJsonSchema: varsSchema,
|
|
8579
|
+
publicUrl: mailer.config.publicUrl,
|
|
8580
|
+
linkDomains: mailer.config.linkDomains
|
|
8581
|
+
}
|
|
8502
8582
|
);
|
|
8503
8583
|
if (lint.errors.length > 0) {
|
|
8504
8584
|
return res.status(422).json({
|
|
@@ -8829,15 +8909,15 @@ function createAgentRouter(mailer, opts) {
|
|
|
8829
8909
|
const contact = await loadContact(res, String(req.params.externalId));
|
|
8830
8910
|
if (!contact) return;
|
|
8831
8911
|
if (!guardTestContact(res, contact)) return;
|
|
8832
|
-
await mailer.
|
|
8912
|
+
const { removedSuppressions } = await mailer.resubscribe({ externalId: contact.externalId, source: "agent" });
|
|
8833
8913
|
const sub = await c.subscriptions.findOne({ externalId: contact.externalId });
|
|
8834
8914
|
await mailer.audit({
|
|
8835
8915
|
actor: actorOf(req),
|
|
8836
8916
|
action: "agent.contact.subscribe",
|
|
8837
8917
|
resource: { collection: "mailer_subscriptions", id: sub?._id },
|
|
8838
|
-
diffSummary: contact.email
|
|
8918
|
+
diffSummary: `${contact.email} (removed ${removedSuppressions} opt-out suppression${removedSuppressions === 1 ? "" : "s"})`
|
|
8839
8919
|
});
|
|
8840
|
-
res.json({ subscription: sub });
|
|
8920
|
+
res.json({ subscription: sub, removedSuppressions });
|
|
8841
8921
|
})
|
|
8842
8922
|
);
|
|
8843
8923
|
router.post(
|
|
@@ -8857,6 +8937,41 @@ function createAgentRouter(mailer, opts) {
|
|
|
8857
8937
|
res.json({ subscription: sub });
|
|
8858
8938
|
})
|
|
8859
8939
|
);
|
|
8940
|
+
router.post(
|
|
8941
|
+
"/contacts/:externalId/tags",
|
|
8942
|
+
wrap2(async (req, res) => {
|
|
8943
|
+
const contact = await loadContact(res, String(req.params.externalId));
|
|
8944
|
+
if (!contact) return;
|
|
8945
|
+
if (!guardTestContact(res, contact)) return;
|
|
8946
|
+
const parsed = agentTagsInputSchema.safeParse(req.body ?? {});
|
|
8947
|
+
if (!parsed.success) {
|
|
8948
|
+
return res.status(400).json({ error: "validation_failed", issues: parsed.error.issues });
|
|
8949
|
+
}
|
|
8950
|
+
const { add, remove } = parsed.data;
|
|
8951
|
+
if (add.length === 0 && remove.length === 0) {
|
|
8952
|
+
return res.status(400).json({ error: "no_tags", message: "Pass {add: [...]} and/or {remove: [...]}." });
|
|
8953
|
+
}
|
|
8954
|
+
const overlap = add.filter((t) => remove.includes(t));
|
|
8955
|
+
if (overlap.length > 0) {
|
|
8956
|
+
return res.status(400).json({ error: "tag_conflict", tags: overlap });
|
|
8957
|
+
}
|
|
8958
|
+
for (const tag of add) await mailer.tag(contact.externalId, tag);
|
|
8959
|
+
for (const tag of remove) await mailer.untag(contact.externalId, tag);
|
|
8960
|
+
const after = await mailer.adapter.getById(contact.externalId);
|
|
8961
|
+
await mailer.audit({
|
|
8962
|
+
actor: actorOf(req),
|
|
8963
|
+
action: "agent.contact.tags",
|
|
8964
|
+
resource: { collection: "contacts", id: contact.externalId },
|
|
8965
|
+
diffSummary: `${contact.email}: ${add.length ? `+${add.join(", +")}` : ""}${add.length && remove.length ? " " : ""}${remove.length ? `-${remove.join(", -")}` : ""}`
|
|
8966
|
+
});
|
|
8967
|
+
res.json({
|
|
8968
|
+
contact: { externalId: contact.externalId, email: contact.email },
|
|
8969
|
+
added: add,
|
|
8970
|
+
removed: remove,
|
|
8971
|
+
tags: after?.tags ?? []
|
|
8972
|
+
});
|
|
8973
|
+
})
|
|
8974
|
+
);
|
|
8860
8975
|
router.post(
|
|
8861
8976
|
"/contacts/:externalId/reset",
|
|
8862
8977
|
wrap2(async (req, res) => {
|
|
@@ -9127,7 +9242,12 @@ async function verifyTemplate(mailer, tpl, contact, opts = {}) {
|
|
|
9127
9242
|
kind: tpl.kind,
|
|
9128
9243
|
fromEmail: tpl.fromEmail
|
|
9129
9244
|
},
|
|
9130
|
-
{
|
|
9245
|
+
{
|
|
9246
|
+
senderDomains: mailer.config.senderDomains,
|
|
9247
|
+
varsJsonSchema: opts.varsSchema ?? null,
|
|
9248
|
+
publicUrl: mailer.config.publicUrl,
|
|
9249
|
+
linkDomains: mailer.config.linkDomains
|
|
9250
|
+
}
|
|
9131
9251
|
);
|
|
9132
9252
|
push("lint", lint.errors.length ? "fail" : lint.warnings.length ? "warn" : "pass", {
|
|
9133
9253
|
errors: lint.errors.map((i) => ({ rule: i.rule, message: i.message })),
|
|
@@ -9373,8 +9493,9 @@ var ENDPOINTS = [
|
|
|
9373
9493
|
{ method: "GET", path: "/contacts/:externalId", summary: "Contact with subscription, suppressions, recent events, sends and runs." },
|
|
9374
9494
|
{ method: "GET", path: "/contacts/by-email/:email", summary: "Same, looked up by email." },
|
|
9375
9495
|
{ method: "GET", path: "/contacts/:externalId/unsubscribe-url", summary: "A signed one-click unsubscribe URL for a test contact, to exercise POST /m/unsub/:token.", testContactsOnly: true },
|
|
9376
|
-
{ method: "POST", path: "/contacts/:externalId/subscribe", summary: "Subscribe a test contact.", testContactsOnly: true },
|
|
9496
|
+
{ method: "POST", path: "/contacts/:externalId/subscribe", summary: "Subscribe a test contact, clearing any opt-out suppression it has (never bounce/complaint rows).", testContactsOnly: true },
|
|
9377
9497
|
{ method: "POST", path: "/contacts/:externalId/unsubscribe", summary: "Unsubscribe a test contact (marketing scope).", testContactsOnly: true },
|
|
9498
|
+
{ method: "POST", path: "/contacts/:externalId/tags", summary: "Add or remove tags on a test contact ({add: [...], remove: [...]}), so a gated flow lets it through.", testContactsOnly: true },
|
|
9378
9499
|
{ method: "POST", path: "/contacts/:externalId/reset", summary: "Delete a test contact's runs, sends, events ({events: [names]} to narrow) and suppressions, then resubscribe. Each part can be turned off with false.", testContactsOnly: true },
|
|
9379
9500
|
{ method: "POST", path: "/tick", summary: "Run the runner tick now (trigger scan, sweeps, outbox, webhook backlog)." },
|
|
9380
9501
|
{ method: "GET", path: "/webhooks/status", summary: "Provider webhook ingest: last event received, counts by type (24h), unprocessed backlog." },
|
|
@@ -10091,7 +10212,7 @@ var DEDUPE_POLICIES = [
|
|
|
10091
10212
|
];
|
|
10092
10213
|
|
|
10093
10214
|
// src/server/index.ts
|
|
10094
|
-
var VERSION2 = "0.16.
|
|
10215
|
+
var VERSION2 = "0.16.7" ;
|
|
10095
10216
|
|
|
10096
10217
|
export { DEDUPE_POLICIES, DEFAULT_BOT_UA_RE, FLOW_STEP_KINDS, FlowOperationError, MIN_AGENT_TOKEN_LENGTH, Mailer, MongoContactAdapter, NullProvider, PREDICATE_KINDS, RESERVED_VAR_KEYS, SEGMENT_FILTER_KINDS, SendGridProvider, TRACKING_SIG_LENGTH, VERSION2 as VERSION, applyTracking, applyWebhookEvent, armFlow, compileMailyTemplate, compileTemplate, computeDeliveryTime, createAdminApiRouter, createAdminRouter, createAgentRouter, createPublicRouter, defaultFlowStep, defaultPredicate, defaultSegmentFilter, defineVars, derivePlaintext, disarmFlow, dispatchSend, drainPendingUnsubscribes, ensureIndexes, gateFlow, getCollections, isCanaryGate, predicateKind, processNewlyFiredEventTriggers, processOneRunStep, referencedPaths, renderForContact, renderTemplate, runTick, sendgridInboundParser, sha256Hex, signTrackingToken, signUnsubscribeToken, simulateFlow, stampWatermarkIfNull, sweepStrandedFlowRuns, ungateFlow, validateSenderDomain, varsJsonSchema, verifyTemplate, verifyTrackingToken, verifyUnsubscribeToken };
|
|
10097
10218
|
//# sourceMappingURL=index.js.map
|