mailery 0.16.6 → 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 +95 -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 +95 -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/testing.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Db } from 'mongodb';
|
|
2
|
-
import { C as ContactAdapter, a as Contact, A as AdapterFilter, M as MailProvider, S as SendArgs, b as SendResult, N as NormalizedEvent, F as FlowStep, a3 as FlowTrigger, n as FlowGoal, V as TemplateKind, T as TemplateDoc, f as FlowDoc, D as DeliveryWindow, R as RunnerContext, d as Mailer, a4 as QueueDriverConfig } from './null-
|
|
3
|
-
export { t as NullProvider } from './null-
|
|
2
|
+
import { C as ContactAdapter, a as Contact, A as AdapterFilter, M as MailProvider, S as SendArgs, b as SendResult, N as NormalizedEvent, F as FlowStep, a3 as FlowTrigger, n as FlowGoal, V as TemplateKind, T as TemplateDoc, f as FlowDoc, D as DeliveryWindow, R as RunnerContext, d as Mailer, a4 as QueueDriverConfig } from './null-oKPS74Gr.cjs';
|
|
3
|
+
export { t as NullProvider } from './null-oKPS74Gr.cjs';
|
|
4
4
|
import 'zod';
|
|
5
5
|
import 'handlebars';
|
|
6
6
|
import 'ioredis';
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Db } from 'mongodb';
|
|
2
|
-
import { C as ContactAdapter, a as Contact, A as AdapterFilter, M as MailProvider, S as SendArgs, b as SendResult, N as NormalizedEvent, F as FlowStep, a3 as FlowTrigger, n as FlowGoal, V as TemplateKind, T as TemplateDoc, f as FlowDoc, D as DeliveryWindow, R as RunnerContext, d as Mailer, a4 as QueueDriverConfig } from './null-
|
|
3
|
-
export { t as NullProvider } from './null-
|
|
2
|
+
import { C as ContactAdapter, a as Contact, A as AdapterFilter, M as MailProvider, S as SendArgs, b as SendResult, N as NormalizedEvent, F as FlowStep, a3 as FlowTrigger, n as FlowGoal, V as TemplateKind, T as TemplateDoc, f as FlowDoc, D as DeliveryWindow, R as RunnerContext, d as Mailer, a4 as QueueDriverConfig } from './null-oKPS74Gr.js';
|
|
3
|
+
export { t as NullProvider } from './null-oKPS74Gr.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
import 'handlebars';
|
|
6
6
|
import 'ioredis';
|
package/dist/testing.js
CHANGED
|
@@ -14276,6 +14276,15 @@ var unsubscribeInputSchema = z.object({
|
|
|
14276
14276
|
source: z.string().max(256).default("manual"),
|
|
14277
14277
|
notes: z.string().max(1024).optional()
|
|
14278
14278
|
});
|
|
14279
|
+
var resubscribeInputSchema = z.object({
|
|
14280
|
+
externalId: externalIdSchema,
|
|
14281
|
+
/** `marketing` clears marketing + all-scope opt-outs; `all` clears every scope. */
|
|
14282
|
+
scope: z.enum(["marketing", "all"]).default("marketing"),
|
|
14283
|
+
source: z.string().min(1).max(256),
|
|
14284
|
+
consentTimestamp: z.date().optional(),
|
|
14285
|
+
consentIp: z.string().optional(),
|
|
14286
|
+
consentUserAgent: z.string().optional()
|
|
14287
|
+
});
|
|
14279
14288
|
var suppressInputSchema = z.object({
|
|
14280
14289
|
email: emailSchema,
|
|
14281
14290
|
scope: unsubscribeScopeSchema,
|
|
@@ -14773,6 +14782,15 @@ function signDoiToken(payload, secret) {
|
|
|
14773
14782
|
}
|
|
14774
14783
|
|
|
14775
14784
|
// src/server/unsubscribe.ts
|
|
14785
|
+
async function clearUnsubscribeSuppressions(collections, email, scope) {
|
|
14786
|
+
const scopes = scope === "all" ? ["all", "marketing", "transactional"] : ["marketing", "all"];
|
|
14787
|
+
const result = await collections.suppressions.deleteMany({
|
|
14788
|
+
email,
|
|
14789
|
+
reason: "unsubscribed",
|
|
14790
|
+
scope: { $in: scopes }
|
|
14791
|
+
});
|
|
14792
|
+
return result.deletedCount ?? 0;
|
|
14793
|
+
}
|
|
14776
14794
|
async function applyUnsubscribe(collections, input, now = /* @__PURE__ */ new Date()) {
|
|
14777
14795
|
const normalized = input.email;
|
|
14778
14796
|
await collections.suppressions.updateOne(
|
|
@@ -18146,6 +18164,38 @@ var Mailer = class _Mailer {
|
|
|
18146
18164
|
}
|
|
18147
18165
|
}
|
|
18148
18166
|
}
|
|
18167
|
+
/**
|
|
18168
|
+
* An explicit opt-in from a contact who unsubscribed before.
|
|
18169
|
+
*
|
|
18170
|
+
* `upsertSubscription` alone is not enough: an unsubscribe also writes a
|
|
18171
|
+
* `mailer_suppressions` row, and the suppression check runs at enqueue
|
|
18172
|
+
* time regardless of subscription status — so a contact re-subscribed
|
|
18173
|
+
* through `upsertSubscription` reads as subscribed while every send comes
|
|
18174
|
+
* back `suppressed`. This clears the opt-out rows (only those: a bounce or
|
|
18175
|
+
* complaint is not the contact's to reverse), then upserts the subscription
|
|
18176
|
+
* through the normal path, double opt-in included.
|
|
18177
|
+
*
|
|
18178
|
+
* Deliberately a separate method rather than a side effect of
|
|
18179
|
+
* `upsertSubscription`, so a backfill or a model hook that re-upserts every
|
|
18180
|
+
* account cannot silently resurrect addresses that opted out.
|
|
18181
|
+
*/
|
|
18182
|
+
async resubscribe(input) {
|
|
18183
|
+
const parsed = resubscribeInputSchema.parse(input);
|
|
18184
|
+
const contact = await this.adapter.getById(parsed.externalId);
|
|
18185
|
+
if (!contact) throw new Error(`adapter has no contact for externalId ${parsed.externalId}`);
|
|
18186
|
+
const removedSuppressions = await clearUnsubscribeSuppressions(this.collections, contact.email, parsed.scope);
|
|
18187
|
+
const { scope: _scope, ...subscription } = parsed;
|
|
18188
|
+
await this.upsertSubscription(subscription);
|
|
18189
|
+
if (removedSuppressions > 0) {
|
|
18190
|
+
await this.audit({
|
|
18191
|
+
actor: `host:${parsed.source}`,
|
|
18192
|
+
action: "contact.resubscribe",
|
|
18193
|
+
resource: { collection: "mailer_suppressions", id: parsed.externalId },
|
|
18194
|
+
diffSummary: `${contact.email}: removed ${removedSuppressions} unsubscribed suppression${removedSuppressions === 1 ? "" : "s"} (${parsed.scope})`
|
|
18195
|
+
});
|
|
18196
|
+
}
|
|
18197
|
+
return { removedSuppressions };
|
|
18198
|
+
}
|
|
18149
18199
|
/**
|
|
18150
18200
|
* The writes live in `server/unsubscribe.ts` so the pending-unsubscribe
|
|
18151
18201
|
* drain (INVARIANT 8) replays a journaled opt-out through exactly this path
|