mailery 0.12.0 → 0.13.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/cli.cjs +109 -37
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +109 -37
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +71 -8
- 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 +71 -8
- package/dist/index.js.map +1 -1
- package/dist/{null-CfzvqNEp.d.cts → null-DDg3ojHe.d.cts} +16 -1
- package/dist/{null-CfzvqNEp.d.ts → null-DDg3ojHe.d.ts} +16 -1
- package/dist/testing.cjs +70 -7
- 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 +70 -7
- 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, a2 as FlowTrigger, m as FlowGoal, U as TemplateKind, T as TemplateDoc, l as FlowDoc, D as DeliveryWindow, R as RunnerContext, d as Mailer, a3 as QueueDriverConfig } from './null-
|
|
3
|
-
export { s 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, a2 as FlowTrigger, m as FlowGoal, U as TemplateKind, T as TemplateDoc, l as FlowDoc, D as DeliveryWindow, R as RunnerContext, d as Mailer, a3 as QueueDriverConfig } from './null-DDg3ojHe.cjs';
|
|
3
|
+
export { s as NullProvider } from './null-DDg3ojHe.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, a2 as FlowTrigger, m as FlowGoal, U as TemplateKind, T as TemplateDoc, l as FlowDoc, D as DeliveryWindow, R as RunnerContext, d as Mailer, a3 as QueueDriverConfig } from './null-
|
|
3
|
-
export { s 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, a2 as FlowTrigger, m as FlowGoal, U as TemplateKind, T as TemplateDoc, l as FlowDoc, D as DeliveryWindow, R as RunnerContext, d as Mailer, a3 as QueueDriverConfig } from './null-DDg3ojHe.js';
|
|
3
|
+
export { s as NullProvider } from './null-DDg3ojHe.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
import 'handlebars';
|
|
6
6
|
import 'ioredis';
|
package/dist/testing.js
CHANGED
|
@@ -14795,6 +14795,9 @@ var QUEUE_NAMES2 = {
|
|
|
14795
14795
|
send: "mailer-send",
|
|
14796
14796
|
webhook: "mailer-webhook"
|
|
14797
14797
|
};
|
|
14798
|
+
var DEFAULT_COLLECTION = "_mailerJobs";
|
|
14799
|
+
var DEFAULT_FAILED_RETENTION_DAYS = 7;
|
|
14800
|
+
var FAILED_SWEEP_INTERVAL_MS = 60 * 60 * 1e3;
|
|
14798
14801
|
var AgendaDriver = class _AgendaDriver {
|
|
14799
14802
|
queues;
|
|
14800
14803
|
agenda;
|
|
@@ -14812,7 +14815,7 @@ var AgendaDriver = class _AgendaDriver {
|
|
|
14812
14815
|
"mailery: queue driver 'agenda' requires the 'agenda' and '@agendajs/mongo-backend' peer dependencies. Run `npm install agenda @agendajs/mongo-backend bottleneck`."
|
|
14813
14816
|
);
|
|
14814
14817
|
}
|
|
14815
|
-
const collectionName = opts.collectionName ??
|
|
14818
|
+
const collectionName = opts.collectionName ?? collectionFor(opts.prefix);
|
|
14816
14819
|
const backend = new backendMod.MongoBackend({
|
|
14817
14820
|
mongo: opts.db,
|
|
14818
14821
|
collection: collectionName
|
|
@@ -14822,17 +14825,29 @@ var AgendaDriver = class _AgendaDriver {
|
|
|
14822
14825
|
processEvery: `${opts.processEverySeconds ?? 5} seconds`,
|
|
14823
14826
|
defaultLockLifetime: (opts.lockLifetimeSeconds ?? 10 * 60) * 1e3,
|
|
14824
14827
|
maxConcurrency: 50,
|
|
14825
|
-
defaultConcurrency: 5
|
|
14828
|
+
defaultConcurrency: 5,
|
|
14829
|
+
// Drop succeeded one-shot jobs instead of retaining them forever.
|
|
14830
|
+
// Agenda guards this on `!nextRunAt`, so repeating jobs survive.
|
|
14831
|
+
removeOnComplete: true
|
|
14826
14832
|
});
|
|
14827
|
-
|
|
14833
|
+
const retentionDays = opts.failedJobRetentionDays ?? DEFAULT_FAILED_RETENTION_DAYS;
|
|
14834
|
+
try {
|
|
14835
|
+
await opts.db.collection(collectionName).createIndex({ failedAt: 1 }, { sparse: true });
|
|
14836
|
+
} catch (err) {
|
|
14837
|
+
console.error("mailery: could not index the Agenda jobs collection on failedAt", err);
|
|
14838
|
+
}
|
|
14839
|
+
return new _AgendaDriver(agenda, agendaMod, opts.db, collectionName, retentionDays);
|
|
14828
14840
|
}
|
|
14829
14841
|
db;
|
|
14830
14842
|
collName;
|
|
14831
|
-
|
|
14843
|
+
failedRetentionDays;
|
|
14844
|
+
sweepTimer = null;
|
|
14845
|
+
constructor(agenda, agendaMod, db, collectionName, failedRetentionDays) {
|
|
14832
14846
|
this.agenda = agenda;
|
|
14833
14847
|
this.agendaMod = agendaMod;
|
|
14834
14848
|
this.db = db;
|
|
14835
14849
|
this.collName = collectionName;
|
|
14850
|
+
this.failedRetentionDays = failedRetentionDays;
|
|
14836
14851
|
this.queues = {
|
|
14837
14852
|
tick: this.makeQueueAPI(QUEUE_NAMES2.tick),
|
|
14838
14853
|
advance: this.makeQueueAPI(QUEUE_NAMES2.advance),
|
|
@@ -14913,9 +14928,45 @@ var AgendaDriver = class _AgendaDriver {
|
|
|
14913
14928
|
if (!this.started) {
|
|
14914
14929
|
await this.agenda.start();
|
|
14915
14930
|
this.started = true;
|
|
14931
|
+
this.startFailedJobSweep();
|
|
14932
|
+
}
|
|
14933
|
+
}
|
|
14934
|
+
/**
|
|
14935
|
+
* Agenda's auto-remove only fires on success, so failed documents would
|
|
14936
|
+
* otherwise be retained forever. Runs once on worker start, then hourly.
|
|
14937
|
+
*/
|
|
14938
|
+
startFailedJobSweep() {
|
|
14939
|
+
if (this.failedRetentionDays <= 0 || this.sweepTimer) return;
|
|
14940
|
+
void this.sweepFailedJobs();
|
|
14941
|
+
this.sweepTimer = setInterval(() => void this.sweepFailedJobs(), FAILED_SWEEP_INTERVAL_MS);
|
|
14942
|
+
this.sweepTimer.unref?.();
|
|
14943
|
+
}
|
|
14944
|
+
/** Delete failed, fully-retired job documents older than the retention window. */
|
|
14945
|
+
async sweepFailedJobs() {
|
|
14946
|
+
const cutoff = new Date(Date.now() - this.failedRetentionDays * 24 * 3600 * 1e3);
|
|
14947
|
+
try {
|
|
14948
|
+
const res = await this.jobsCollection().deleteMany({
|
|
14949
|
+
failedAt: { $lt: cutoff },
|
|
14950
|
+
// Never touch the repeating tick.
|
|
14951
|
+
repeatInterval: { $in: [null, void 0] },
|
|
14952
|
+
// Leave anything still scheduled for a retry, and anything a worker
|
|
14953
|
+
// currently holds a lock on.
|
|
14954
|
+
$and: [
|
|
14955
|
+
{ $or: [{ nextRunAt: null }, { nextRunAt: { $exists: false } }, { nextRunAt: { $lt: cutoff } }] },
|
|
14956
|
+
{ $or: [{ lockedAt: null }, { lockedAt: { $exists: false } }, { lockedAt: { $lt: cutoff } }] }
|
|
14957
|
+
]
|
|
14958
|
+
});
|
|
14959
|
+
return res.deletedCount ?? 0;
|
|
14960
|
+
} catch (err) {
|
|
14961
|
+
console.error("mailery: failed-job sweep failed", err);
|
|
14962
|
+
return 0;
|
|
14916
14963
|
}
|
|
14917
14964
|
}
|
|
14918
14965
|
async stopWorkers() {
|
|
14966
|
+
if (this.sweepTimer) {
|
|
14967
|
+
clearInterval(this.sweepTimer);
|
|
14968
|
+
this.sweepTimer = null;
|
|
14969
|
+
}
|
|
14919
14970
|
if (!this.started) return;
|
|
14920
14971
|
await this.agenda.stop();
|
|
14921
14972
|
this.started = false;
|
|
@@ -14929,6 +14980,15 @@ var AgendaDriver = class _AgendaDriver {
|
|
|
14929
14980
|
await this.stopWorkers();
|
|
14930
14981
|
}
|
|
14931
14982
|
};
|
|
14983
|
+
function collectionFor(prefix) {
|
|
14984
|
+
if (!prefix) return DEFAULT_COLLECTION;
|
|
14985
|
+
if (!/^[A-Za-z0-9_-]+$/.test(prefix)) {
|
|
14986
|
+
throw new Error(
|
|
14987
|
+
`mailery: queue prefix "${prefix}" must contain only letters, digits, '_' or '-' \u2014 it becomes part of a MongoDB collection name.`
|
|
14988
|
+
);
|
|
14989
|
+
}
|
|
14990
|
+
return `${DEFAULT_COLLECTION}_${prefix}`;
|
|
14991
|
+
}
|
|
14932
14992
|
|
|
14933
14993
|
// src/server/queues/noop.ts
|
|
14934
14994
|
function noopQueueAPI() {
|
|
@@ -14965,7 +15025,9 @@ async function createQueueDriver(config, fallbackDb) {
|
|
|
14965
15025
|
db: config.db ?? fallbackDb,
|
|
14966
15026
|
processEverySeconds: config.processEverySeconds,
|
|
14967
15027
|
lockLifetimeSeconds: config.lockLifetimeSeconds,
|
|
14968
|
-
collectionName: config.collectionName
|
|
15028
|
+
collectionName: config.collectionName,
|
|
15029
|
+
prefix: config.prefix,
|
|
15030
|
+
failedJobRetentionDays: config.failedJobRetentionDays
|
|
14969
15031
|
});
|
|
14970
15032
|
case "noop":
|
|
14971
15033
|
return new NoopDriver();
|
|
@@ -17405,7 +17467,8 @@ var Mailer = class _Mailer {
|
|
|
17405
17467
|
* MAILER_MONGODB_URI — Mongo connection string (required)
|
|
17406
17468
|
* MAILER_MONGODB_DB — database name (optional; defaults to the URI default)
|
|
17407
17469
|
* MAILER_REDIS_URL — Redis connection URL (required)
|
|
17408
|
-
* MAILER_QUEUE_PREFIX —
|
|
17470
|
+
* MAILER_QUEUE_PREFIX — namespaces this instance (optional). Bull: Redis key
|
|
17471
|
+
* prefix. Agenda: jobs collection suffix.
|
|
17409
17472
|
* MAILER_PUBLIC_URL — base for tracking/unsub URLs (required)
|
|
17410
17473
|
* MAILER_UNSUBSCRIBE_SECRET — HMAC key (required)
|
|
17411
17474
|
* MAILER_SENDER_ADDRESS — postal address for CAN-SPAM (optional)
|
|
@@ -17454,7 +17517,7 @@ var Mailer = class _Mailer {
|
|
|
17454
17517
|
}
|
|
17455
17518
|
const defaultProvider = env.MAILER_DEFAULT_PROVIDER ?? Object.keys(providers)[0];
|
|
17456
17519
|
const driverEnv = env.MAILER_QUEUE_DRIVER ?? "bull";
|
|
17457
|
-
const queue = driverEnv === "agenda" ? { driver: "agenda" } : driverEnv === "noop" ? { driver: "noop" } : {
|
|
17520
|
+
const queue = driverEnv === "agenda" ? { driver: "agenda", prefix: env.MAILER_QUEUE_PREFIX } : driverEnv === "noop" ? { driver: "noop" } : {
|
|
17458
17521
|
driver: "bull",
|
|
17459
17522
|
redis: { url: required("MAILER_REDIS_URL") },
|
|
17460
17523
|
prefix: env.MAILER_QUEUE_PREFIX
|