mailery 0.13.0 → 0.15.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/testing.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { MongoClient, ObjectId } from 'mongodb';
3
- import crypto2 from 'crypto';
3
+ import crypto2, { randomBytes } from 'crypto';
4
4
  import sgMail from '@sendgrid/mail';
5
5
  import IORedis from 'ioredis';
6
6
  import Handlebars from 'handlebars';
@@ -9,6 +9,9 @@ import mjml2html from 'mjml';
9
9
  import dns from 'dns/promises';
10
10
  import net from 'net';
11
11
  import psl from 'psl';
12
+ import 'fast-xml-parser';
13
+ import fs from 'fs';
14
+ import path from 'path';
12
15
 
13
16
  var __create = Object.create;
14
17
  var __defProp = Object.defineProperty;
@@ -22,11 +25,20 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
22
25
  if (typeof require !== "undefined") return require.apply(this, arguments);
23
26
  throw Error('Dynamic require of "' + x + '" is not supported');
24
27
  });
25
- var __esm = (fn, res) => function __init() {
26
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
28
+ var __esm = (fn, res, err) => function __init() {
29
+ if (err) throw err[0];
30
+ try {
31
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
32
+ } catch (e) {
33
+ throw err = [e], e;
34
+ }
27
35
  };
28
36
  var __commonJS = (cb, mod) => function __require2() {
29
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
37
+ try {
38
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
39
+ } catch (e) {
40
+ throw mod = 0, e;
41
+ }
30
42
  };
31
43
  var __export = (target, all) => {
32
44
  for (var name in all)
@@ -259,6 +271,31 @@ var init_mongo = __esm({
259
271
  }
260
272
  });
261
273
 
274
+ // src/server/providers/webhook-tolerance.ts
275
+ function resolveWebhookToleranceSeconds(option) {
276
+ if (option === false || option === 0) return 0;
277
+ if (option === void 0 || option === null) return DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
278
+ if (typeof option !== "number" || !Number.isFinite(option) || option < 0) {
279
+ return DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
280
+ }
281
+ return option;
282
+ }
283
+ function isWebhookTimestampFresh(rawTimestamp, toleranceSeconds, now = /* @__PURE__ */ new Date()) {
284
+ if (toleranceSeconds === 0) return true;
285
+ if (!rawTimestamp) return false;
286
+ if (!/^\d{1,15}$/.test(rawTimestamp)) return false;
287
+ const timestampMs = Number(rawTimestamp) * 1e3;
288
+ if (!Number.isFinite(timestampMs)) return false;
289
+ const deltaMs = Math.abs(now.getTime() - timestampMs);
290
+ return deltaMs <= toleranceSeconds * 1e3;
291
+ }
292
+ var DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
293
+ var init_webhook_tolerance = __esm({
294
+ "src/server/providers/webhook-tolerance.ts"() {
295
+ DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
296
+ }
297
+ });
298
+
262
299
  // src/server/providers/sendgrid.ts
263
300
  var sendgrid_exports = {};
264
301
  __export(sendgrid_exports, {
@@ -305,6 +342,7 @@ function mapEventType(sgEvent) {
305
342
  var SG_SIG_HEADER, SG_TS_HEADER, SendGridProvider;
306
343
  var init_sendgrid = __esm({
307
344
  "src/server/providers/sendgrid.ts"() {
345
+ init_webhook_tolerance();
308
346
  SG_SIG_HEADER = "x-twilio-email-event-webhook-signature";
309
347
  SG_TS_HEADER = "x-twilio-email-event-webhook-timestamp";
310
348
  SendGridProvider = class {
@@ -312,10 +350,13 @@ var init_sendgrid = __esm({
312
350
  this.opts = opts;
313
351
  sgMail.setApiKey(opts.apiKey);
314
352
  this.sendRatePerSecond = opts.sendRatePerSecond ?? 10;
353
+ this.webhookToleranceSeconds = resolveWebhookToleranceSeconds(opts.webhookToleranceSeconds);
315
354
  }
316
355
  opts;
317
356
  name = "sendgrid";
318
357
  sendRatePerSecond;
358
+ /** Resolved replay window in seconds; `0` means the check is disabled. */
359
+ webhookToleranceSeconds;
319
360
  async send(args) {
320
361
  const msg = {
321
362
  to: args.to,
@@ -355,10 +396,11 @@ var init_sendgrid = __esm({
355
396
  try {
356
397
  const verifier = crypto2.createVerify("sha256");
357
398
  verifier.update(payload);
358
- return verifier.verify(this.opts.webhookVerificationKey, sig, "base64");
399
+ if (!verifier.verify(this.opts.webhookVerificationKey, sig, "base64")) return false;
359
400
  } catch {
360
401
  return false;
361
402
  }
403
+ return isWebhookTimestampFresh(ts, this.webhookToleranceSeconds);
362
404
  }
363
405
  parseWebhookEvents(payload) {
364
406
  if (!Array.isArray(payload)) return [];
@@ -797,13 +839,13 @@ function __disposeResources(env) {
797
839
  }
798
840
  return next();
799
841
  }
800
- function __rewriteRelativeImportExtension(path, preserveJsx) {
801
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
802
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
842
+ function __rewriteRelativeImportExtension(path2, preserveJsx) {
843
+ if (typeof path2 === "string" && /^\.\.?\//.test(path2)) {
844
+ return path2.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
803
845
  return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
804
846
  });
805
847
  }
806
- return path;
848
+ return path2;
807
849
  }
808
850
  var extendStatics, __assign, __createBinding, __setModuleDefault, ownKeys, _SuppressedError, tslib_es6_default;
809
851
  var init_tslib_es6 = __esm({
@@ -1633,7 +1675,7 @@ var require_lib = __commonJS({
1633
1675
  "node_modules/new-find-package-json/lib/index.js"(exports) {
1634
1676
  Object.defineProperty(exports, "__esModule", { value: true });
1635
1677
  exports.findSync = exports.find = exports.findAsync = void 0;
1636
- var path = __require("path");
1678
+ var path2 = __require("path");
1637
1679
  var fs_1 = __require("fs");
1638
1680
  var debug = require_src();
1639
1681
  var log = debug("new-find-package-json:main");
@@ -1641,14 +1683,14 @@ var require_lib = __commonJS({
1641
1683
  log(`findSync: called with "${input}" and "${base}" and "${fileName}"`);
1642
1684
  base = !!base ? base : "";
1643
1685
  const useFileName = !!fileName ? fileName : "package.json";
1644
- const useBase = !!base || path.isAbsolute(base) ? base : process.cwd();
1645
- let testPath = path.resolve(useBase, input);
1686
+ const useBase = !!base || path2.isAbsolute(base) ? base : process.cwd();
1687
+ let testPath = path2.resolve(useBase, input);
1646
1688
  let wasRoot = false;
1647
1689
  while (testPath) {
1648
- if (testPath === path.parse(testPath).root) {
1690
+ if (testPath === path2.parse(testPath).root) {
1649
1691
  wasRoot = true;
1650
1692
  }
1651
- const testFile = path.resolve(testPath, useFileName);
1693
+ const testFile = path2.resolve(testPath, useFileName);
1652
1694
  log(`findSync: testing path "${testFile}"`);
1653
1695
  const result = await statPathAsync(testFile);
1654
1696
  if (!!result && result.isFile()) {
@@ -1659,7 +1701,7 @@ var require_lib = __commonJS({
1659
1701
  if (wasRoot) {
1660
1702
  break;
1661
1703
  }
1662
- testPath = path.resolve(testPath, "..");
1704
+ testPath = path2.resolve(testPath, "..");
1663
1705
  }
1664
1706
  }
1665
1707
  exports.findAsync = findAsync;
@@ -1668,14 +1710,14 @@ var require_lib = __commonJS({
1668
1710
  log(`findSync: called with "${input}" and "${base}" and "${fileName}"`);
1669
1711
  base = !!base ? base : "";
1670
1712
  const useFileName = !!fileName ? fileName : "package.json";
1671
- const useBase = !!base || path.isAbsolute(base) ? base : process.cwd();
1672
- let testPath = path.resolve(useBase, input);
1713
+ const useBase = !!base || path2.isAbsolute(base) ? base : process.cwd();
1714
+ let testPath = path2.resolve(useBase, input);
1673
1715
  let wasRoot = false;
1674
1716
  while (testPath) {
1675
- if (testPath === path.parse(testPath).root) {
1717
+ if (testPath === path2.parse(testPath).root) {
1676
1718
  wasRoot = true;
1677
1719
  }
1678
- const testFile = path.resolve(testPath, useFileName);
1720
+ const testFile = path2.resolve(testPath, useFileName);
1679
1721
  log(`findSync: testing path "${testFile}"`);
1680
1722
  const result = statPathSync(testFile);
1681
1723
  if (!!result && result.isFile()) {
@@ -1686,14 +1728,14 @@ var require_lib = __commonJS({
1686
1728
  if (wasRoot) {
1687
1729
  break;
1688
1730
  }
1689
- testPath = path.resolve(testPath, "..");
1731
+ testPath = path2.resolve(testPath, "..");
1690
1732
  }
1691
1733
  }
1692
1734
  exports.findSync = findSync;
1693
- function statPathSync(path2) {
1735
+ function statPathSync(path3) {
1694
1736
  var _a;
1695
1737
  try {
1696
- return (0, fs_1.statSync)(path2);
1738
+ return (0, fs_1.statSync)(path3);
1697
1739
  } catch (err) {
1698
1740
  if (err != void 0 && err != null && ((_a = err) === null || _a === void 0 ? void 0 : _a.code) === "ENOENT") {
1699
1741
  return void 0;
@@ -1701,8 +1743,8 @@ var require_lib = __commonJS({
1701
1743
  throw err;
1702
1744
  }
1703
1745
  }
1704
- async function statPathAsync(path2) {
1705
- return fs_1.promises.stat(path2).catch((err) => {
1746
+ async function statPathAsync(path3) {
1747
+ return fs_1.promises.stat(path3).catch((err) => {
1706
1748
  if (err.code === "ENOENT") {
1707
1749
  return void 0;
1708
1750
  }
@@ -1821,18 +1863,18 @@ https://typegoose.github.io/mongodb-memory-server/docs/guides/migration/migrate7
1821
1863
  exports.AuthNotObjectError = AuthNotObjectError;
1822
1864
  var InsufficientPermissionsError = class extends Error {
1823
1865
  path;
1824
- constructor(path) {
1825
- super(`File "${path}" does not have the required Permissions, required Permissions: "--x"`);
1826
- this.path = path;
1866
+ constructor(path2) {
1867
+ super(`File "${path2}" does not have the required Permissions, required Permissions: "--x"`);
1868
+ this.path = path2;
1827
1869
  }
1828
1870
  };
1829
1871
  exports.InsufficientPermissionsError = InsufficientPermissionsError;
1830
1872
  var BinaryNotFoundError = class extends Error {
1831
1873
  path;
1832
1874
  extra;
1833
- constructor(path, extra = "") {
1834
- super(`No Binary at path "${path}" was found! (ENOENT)${extra}`);
1835
- this.path = path;
1875
+ constructor(path2, extra = "") {
1876
+ super(`No Binary at path "${path2}" was found! (ENOENT)${extra}`);
1877
+ this.path = path2;
1836
1878
  this.extra = extra;
1837
1879
  }
1838
1880
  };
@@ -3907,7 +3949,7 @@ var require_utils = __commonJS({
3907
3949
  var fs_1 = __require("fs");
3908
3950
  var errors_1 = require_errors();
3909
3951
  var os_1 = __require("os");
3910
- var path = tslib_1.__importStar(__require("path"));
3952
+ var path2 = tslib_1.__importStar(__require("path"));
3911
3953
  var crypto_1 = __require("crypto");
3912
3954
  var semver = tslib_1.__importStar(require_semver2());
3913
3955
  var log = (0, debug_1.default)("MongoMS:utils");
@@ -3991,16 +4033,16 @@ var require_utils = __commonJS({
3991
4033
  ...opts
3992
4034
  };
3993
4035
  }
3994
- async function statPath(path2) {
3995
- return fs_1.promises.stat(path2).catch((err) => {
4036
+ async function statPath(path3) {
4037
+ return fs_1.promises.stat(path3).catch((err) => {
3996
4038
  if (["ENOENT", "EACCES"].includes(err.code)) {
3997
4039
  return void 0;
3998
4040
  }
3999
4041
  throw err;
4000
4042
  });
4001
4043
  }
4002
- async function pathExists(path2) {
4003
- return !isNullOrUndefined(await statPath(path2));
4044
+ async function pathExists(path3) {
4045
+ return !isNullOrUndefined(await statPath(path3));
4004
4046
  }
4005
4047
  var ManagerBase = class {
4006
4048
  };
@@ -4008,27 +4050,27 @@ var require_utils = __commonJS({
4008
4050
  var ManagerAdvanced = class extends ManagerBase {
4009
4051
  };
4010
4052
  exports.ManagerAdvanced = ManagerAdvanced;
4011
- async function checkBinaryPermissions(path2) {
4053
+ async function checkBinaryPermissions(path3) {
4012
4054
  try {
4013
- await fs_1.promises.access(path2, fs_1.constants.X_OK);
4055
+ await fs_1.promises.access(path3, fs_1.constants.X_OK);
4014
4056
  } catch (err) {
4015
4057
  if (errorWithCode(err)) {
4016
4058
  if (err.code === "EACCES") {
4017
- throw new errors_1.InsufficientPermissionsError(path2);
4059
+ throw new errors_1.InsufficientPermissionsError(path3);
4018
4060
  }
4019
4061
  if (err.code === "ENOENT") {
4020
- throw new errors_1.BinaryNotFoundError(path2);
4062
+ throw new errors_1.BinaryNotFoundError(path3);
4021
4063
  }
4022
4064
  }
4023
4065
  throw err;
4024
4066
  }
4025
4067
  }
4026
- async function mkdir(path2) {
4027
- await fs_1.promises.mkdir(path2, { recursive: true });
4068
+ async function mkdir(path3) {
4069
+ await fs_1.promises.mkdir(path3, { recursive: true });
4028
4070
  }
4029
4071
  async function createTmpDir(prefix, atPath) {
4030
4072
  const tmpPath = atPath ?? (0, os_1.tmpdir)();
4031
- return fs_1.promises.mkdtemp(path.join(tmpPath, prefix));
4073
+ return fs_1.promises.mkdtemp(path2.join(tmpPath, prefix));
4032
4074
  }
4033
4075
  async function removeDir(dirPath) {
4034
4076
  const stat = await statPath(dirPath);
@@ -4050,7 +4092,7 @@ var require_utils = __commonJS({
4050
4092
  return md5(await fs_1.promises.readFile(file));
4051
4093
  }
4052
4094
  function lockfilePath(downloadDir, version) {
4053
- return path.resolve(downloadDir, `${version}.lock`);
4095
+ return path2.resolve(downloadDir, `${version}.lock`);
4054
4096
  }
4055
4097
  function getStorageEngine(storageEngine, coercedVersion) {
4056
4098
  if (storageEngine === "ephemeralForTest" && semver.gte(coercedVersion, "7.0.0")) {
@@ -4084,7 +4126,7 @@ var require_resolveConfig = __commonJS({
4084
4126
  var camelcase_1 = tslib_1.__importDefault(require_camelcase());
4085
4127
  var new_find_package_json_1 = require_lib();
4086
4128
  var debug_1 = tslib_1.__importDefault(require_src());
4087
- var path = tslib_1.__importStar(__require("path"));
4129
+ var path2 = tslib_1.__importStar(__require("path"));
4088
4130
  var fs_1 = __require("fs");
4089
4131
  var utils_1 = require_utils();
4090
4132
  Symbol.dispose ??= /* @__PURE__ */ Symbol("Symbol.dispose");
@@ -4141,7 +4183,7 @@ var require_resolveConfig = __commonJS({
4141
4183
  const config = readout?.config?.mongodbMemoryServer;
4142
4184
  if (!(0, utils_1.isNullOrUndefined)(config) && Object.keys(config ?? {}).length > 0) {
4143
4185
  log(`findPackageJson: Found package with non-empty config field at "${filename}"`);
4144
- const filepath = path.dirname(filename);
4186
+ const filepath = path2.dirname(filename);
4145
4187
  packagejson = {
4146
4188
  filePath: filepath,
4147
4189
  config: processConfigOption(config, filepath)
@@ -4161,10 +4203,10 @@ var require_resolveConfig = __commonJS({
4161
4203
  const ccDownloadDir = (0, camelcase_1.default)(ResolveConfigVariables.DOWNLOAD_DIR);
4162
4204
  const ccSystemBinary = (0, camelcase_1.default)(ResolveConfigVariables.SYSTEM_BINARY);
4163
4205
  if (ccDownloadDir in returnobj) {
4164
- returnobj[ccDownloadDir] = path.resolve(filepath, returnobj[ccDownloadDir]);
4206
+ returnobj[ccDownloadDir] = path2.resolve(filepath, returnobj[ccDownloadDir]);
4165
4207
  }
4166
4208
  if (ccSystemBinary in returnobj) {
4167
- returnobj[ccSystemBinary] = path.resolve(filepath, returnobj[ccSystemBinary]);
4209
+ returnobj[ccSystemBinary] = path2.resolve(filepath, returnobj[ccSystemBinary]);
4168
4210
  }
4169
4211
  return returnobj;
4170
4212
  }
@@ -4307,7 +4349,7 @@ var require_debug2 = __commonJS({
4307
4349
  var require_follow_redirects = __commonJS({
4308
4350
  "node_modules/follow-redirects/index.js"(exports, module) {
4309
4351
  var url = __require("url");
4310
- var URL = url.URL;
4352
+ var URL2 = url.URL;
4311
4353
  var http = __require("http");
4312
4354
  var https = __require("https");
4313
4355
  var Writable = __require("stream").Writable;
@@ -4323,7 +4365,7 @@ var require_follow_redirects = __commonJS({
4323
4365
  })();
4324
4366
  var useNativeURL = false;
4325
4367
  try {
4326
- assert(new URL(""));
4368
+ assert(new URL2(""));
4327
4369
  } catch (error) {
4328
4370
  useNativeURL = error.code === "ERR_INVALID_URL";
4329
4371
  }
@@ -4712,7 +4754,7 @@ var require_follow_redirects = __commonJS({
4712
4754
  function parseUrl(input) {
4713
4755
  var parsed;
4714
4756
  if (useNativeURL) {
4715
- parsed = new URL(input);
4757
+ parsed = new URL2(input);
4716
4758
  } else {
4717
4759
  parsed = validateUrl(url.parse(input));
4718
4760
  if (!isString(parsed.protocol)) {
@@ -4722,7 +4764,7 @@ var require_follow_redirects = __commonJS({
4722
4764
  return parsed;
4723
4765
  }
4724
4766
  function resolveUrl(relative, base) {
4725
- return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative));
4767
+ return useNativeURL ? new URL2(relative, base) : parseUrl(url.resolve(base, relative));
4726
4768
  }
4727
4769
  function validateUrl(input) {
4728
4770
  if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -4804,7 +4846,7 @@ var require_follow_redirects = __commonJS({
4804
4846
  return typeof value === "object" && "length" in value;
4805
4847
  }
4806
4848
  function isURL(value) {
4807
- return URL && value instanceof URL;
4849
+ return URL2 && value instanceof URL2;
4808
4850
  }
4809
4851
  function escapeRegex(regex) {
4810
4852
  return regex.replace(/[\]\\/()*+?.$]/g, "\\$&");
@@ -7197,7 +7239,7 @@ var require_pend = __commonJS({
7197
7239
  // node_modules/yauzl/fd-slicer.js
7198
7240
  var require_fd_slicer = __commonJS({
7199
7241
  "node_modules/yauzl/fd-slicer.js"(exports) {
7200
- var fs = __require("fs");
7242
+ var fs2 = __require("fs");
7201
7243
  var util = __require("util");
7202
7244
  var stream = __require("stream");
7203
7245
  var Readable = stream.Readable;
@@ -7222,7 +7264,7 @@ var require_fd_slicer = __commonJS({
7222
7264
  FdSlicer.prototype.read = function(buffer, offset, length, position, callback) {
7223
7265
  var self = this;
7224
7266
  self.pend.go(function(cb) {
7225
- fs.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer2) {
7267
+ fs2.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer2) {
7226
7268
  cb();
7227
7269
  callback(err, bytesRead, buffer2);
7228
7270
  });
@@ -7231,7 +7273,7 @@ var require_fd_slicer = __commonJS({
7231
7273
  FdSlicer.prototype.write = function(buffer, offset, length, position, callback) {
7232
7274
  var self = this;
7233
7275
  self.pend.go(function(cb) {
7234
- fs.write(self.fd, buffer, offset, length, position, function(err, written, buffer2) {
7276
+ fs2.write(self.fd, buffer, offset, length, position, function(err, written, buffer2) {
7235
7277
  cb();
7236
7278
  callback(err, written, buffer2);
7237
7279
  });
@@ -7252,7 +7294,7 @@ var require_fd_slicer = __commonJS({
7252
7294
  if (self.refCount > 0) return;
7253
7295
  if (self.refCount < 0) throw new Error("invalid unref");
7254
7296
  if (self.autoClose) {
7255
- fs.close(self.fd, onCloseDone);
7297
+ fs2.close(self.fd, onCloseDone);
7256
7298
  }
7257
7299
  function onCloseDone(err) {
7258
7300
  if (err) {
@@ -7289,7 +7331,7 @@ var require_fd_slicer = __commonJS({
7289
7331
  self.context.pend.go(function(cb) {
7290
7332
  if (self.destroyed) return cb();
7291
7333
  var buffer = Buffer.allocUnsafe(toRead);
7292
- fs.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) {
7334
+ fs2.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) {
7293
7335
  if (err) {
7294
7336
  self.destroy(err);
7295
7337
  } else if (bytesRead === 0) {
@@ -7336,7 +7378,7 @@ var require_fd_slicer = __commonJS({
7336
7378
  }
7337
7379
  self.context.pend.go(function(cb) {
7338
7380
  if (self.destroyed) return cb();
7339
- fs.write(self.context.fd, buffer, 0, buffer.length, self.pos, function(err2, bytes) {
7381
+ fs2.write(self.context.fd, buffer, 0, buffer.length, self.pos, function(err2, bytes) {
7340
7382
  if (err2) {
7341
7383
  self.destroy();
7342
7384
  cb();
@@ -7774,7 +7816,7 @@ var require_buffer_crc32 = __commonJS({
7774
7816
  // node_modules/yauzl/index.js
7775
7817
  var require_yauzl = __commonJS({
7776
7818
  "node_modules/yauzl/index.js"(exports) {
7777
- var fs = __require("fs");
7819
+ var fs2 = __require("fs");
7778
7820
  var zlib = __require("zlib");
7779
7821
  var fd_slicer = require_fd_slicer();
7780
7822
  var crc32 = require_buffer_crc32();
@@ -7795,7 +7837,7 @@ var require_yauzl = __commonJS({
7795
7837
  exports.Entry = Entry;
7796
7838
  exports.LocalFileHeader = LocalFileHeader;
7797
7839
  exports.RandomAccessReader = RandomAccessReader;
7798
- function open(path, options2, callback) {
7840
+ function open(path2, options2, callback) {
7799
7841
  if (typeof options2 === "function") {
7800
7842
  callback = options2;
7801
7843
  options2 = null;
@@ -7807,10 +7849,10 @@ var require_yauzl = __commonJS({
7807
7849
  if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
7808
7850
  if (options2.strictFileNames == null) options2.strictFileNames = false;
7809
7851
  if (callback == null) callback = defaultCallback;
7810
- fs.open(path, "r", function(err, fd) {
7852
+ fs2.open(path2, "r", function(err, fd) {
7811
7853
  if (err) return callback(err);
7812
7854
  fromFd(fd, options2, function(err2, zipfile) {
7813
- if (err2) fs.close(fd, defaultCallback);
7855
+ if (err2) fs2.close(fd, defaultCallback);
7814
7856
  callback(err2, zipfile);
7815
7857
  });
7816
7858
  });
@@ -7827,7 +7869,7 @@ var require_yauzl = __commonJS({
7827
7869
  if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
7828
7870
  if (options2.strictFileNames == null) options2.strictFileNames = false;
7829
7871
  if (callback == null) callback = defaultCallback;
7830
- fs.fstat(fd, function(err, stats) {
7872
+ fs2.fstat(fd, function(err, stats) {
7831
7873
  if (err) return callback(err);
7832
7874
  var reader = fd_slicer.createFromFd(fd, { autoClose: true });
7833
7875
  fromRandomAccessReader(reader, stats.size, options2, callback);
@@ -8610,15 +8652,15 @@ var require_getos = __commonJS({
8610
8652
  id_like: input.match(OSRegex.id_like)?.[1].toLocaleLowerCase().split(" ")
8611
8653
  };
8612
8654
  }
8613
- async function tryReleaseFile(path, parser) {
8655
+ async function tryReleaseFile(path2, parser) {
8614
8656
  try {
8615
- const output = await fs_1.promises.readFile(path);
8657
+ const output = await fs_1.promises.readFile(path2);
8616
8658
  return parser(output.toString());
8617
8659
  } catch (err) {
8618
8660
  if ((0, utils_1.errorWithCode)(err) && !["ENOENT", "EACCES"].includes(err.code)) {
8619
8661
  throw err;
8620
8662
  }
8621
- log(`tryReleaseFile: "${path}" does not exist`);
8663
+ log(`tryReleaseFile: "${path2}" does not exist`);
8622
8664
  return void 0;
8623
8665
  }
8624
8666
  }
@@ -9591,11 +9633,11 @@ var require_dist2 = __commonJS({
9591
9633
  // node_modules/commondir/index.js
9592
9634
  var require_commondir = __commonJS({
9593
9635
  "node_modules/commondir/index.js"(exports, module) {
9594
- var path = __require("path");
9636
+ var path2 = __require("path");
9595
9637
  module.exports = function(basedir, relfiles) {
9596
9638
  if (relfiles) {
9597
9639
  var files = relfiles.map(function(r) {
9598
- return path.resolve(basedir, r);
9640
+ return path2.resolve(basedir, r);
9599
9641
  });
9600
9642
  } else {
9601
9643
  var files = basedir;
@@ -9718,12 +9760,12 @@ var require_p_locate = __commonJS({
9718
9760
  // node_modules/locate-path/index.js
9719
9761
  var require_locate_path = __commonJS({
9720
9762
  "node_modules/locate-path/index.js"(exports, module) {
9721
- var path = __require("path");
9722
- var fs = __require("fs");
9763
+ var path2 = __require("path");
9764
+ var fs2 = __require("fs");
9723
9765
  var { promisify } = __require("util");
9724
9766
  var pLocate = require_p_locate();
9725
- var fsStat = promisify(fs.stat);
9726
- var fsLStat = promisify(fs.lstat);
9767
+ var fsStat = promisify(fs2.stat);
9768
+ var fsLStat = promisify(fs2.lstat);
9727
9769
  var typeMappings = {
9728
9770
  directory: "isDirectory",
9729
9771
  file: "isFile"
@@ -9746,7 +9788,7 @@ var require_locate_path = __commonJS({
9746
9788
  const statFn = options2.allowSymlinks ? fsStat : fsLStat;
9747
9789
  return pLocate(paths, async (path_) => {
9748
9790
  try {
9749
- const stat = await statFn(path.resolve(options2.cwd, path_));
9791
+ const stat = await statFn(path2.resolve(options2.cwd, path_));
9750
9792
  return matchType(options2.type, stat);
9751
9793
  } catch (_) {
9752
9794
  return false;
@@ -9761,10 +9803,10 @@ var require_locate_path = __commonJS({
9761
9803
  ...options2
9762
9804
  };
9763
9805
  checkType(options2);
9764
- const statFn = options2.allowSymlinks ? fs.statSync : fs.lstatSync;
9806
+ const statFn = options2.allowSymlinks ? fs2.statSync : fs2.lstatSync;
9765
9807
  for (const path_ of paths) {
9766
9808
  try {
9767
- const stat = statFn(path.resolve(options2.cwd, path_));
9809
+ const stat = statFn(path2.resolve(options2.cwd, path_));
9768
9810
  if (matchType(options2.type, stat)) {
9769
9811
  return path_;
9770
9812
  }
@@ -9778,20 +9820,20 @@ var require_locate_path = __commonJS({
9778
9820
  // node_modules/path-exists/index.js
9779
9821
  var require_path_exists = __commonJS({
9780
9822
  "node_modules/path-exists/index.js"(exports, module) {
9781
- var fs = __require("fs");
9823
+ var fs2 = __require("fs");
9782
9824
  var { promisify } = __require("util");
9783
- var pAccess = promisify(fs.access);
9784
- module.exports = async (path) => {
9825
+ var pAccess = promisify(fs2.access);
9826
+ module.exports = async (path2) => {
9785
9827
  try {
9786
- await pAccess(path);
9828
+ await pAccess(path2);
9787
9829
  return true;
9788
9830
  } catch (_) {
9789
9831
  return false;
9790
9832
  }
9791
9833
  };
9792
- module.exports.sync = (path) => {
9834
+ module.exports.sync = (path2) => {
9793
9835
  try {
9794
- fs.accessSync(path);
9836
+ fs2.accessSync(path2);
9795
9837
  return true;
9796
9838
  } catch (_) {
9797
9839
  return false;
@@ -9803,13 +9845,13 @@ var require_path_exists = __commonJS({
9803
9845
  // node_modules/find-up/index.js
9804
9846
  var require_find_up = __commonJS({
9805
9847
  "node_modules/find-up/index.js"(exports, module) {
9806
- var path = __require("path");
9848
+ var path2 = __require("path");
9807
9849
  var locatePath = require_locate_path();
9808
9850
  var pathExists = require_path_exists();
9809
9851
  var stop = /* @__PURE__ */ Symbol("findUp.stop");
9810
9852
  module.exports = async (name, options2 = {}) => {
9811
- let directory = path.resolve(options2.cwd || "");
9812
- const { root } = path.parse(directory);
9853
+ let directory = path2.resolve(options2.cwd || "");
9854
+ const { root } = path2.parse(directory);
9813
9855
  const paths = [].concat(name);
9814
9856
  const runMatcher = async (locateOptions) => {
9815
9857
  if (typeof name !== "function") {
@@ -9827,17 +9869,17 @@ var require_find_up = __commonJS({
9827
9869
  return;
9828
9870
  }
9829
9871
  if (foundPath) {
9830
- return path.resolve(directory, foundPath);
9872
+ return path2.resolve(directory, foundPath);
9831
9873
  }
9832
9874
  if (directory === root) {
9833
9875
  return;
9834
9876
  }
9835
- directory = path.dirname(directory);
9877
+ directory = path2.dirname(directory);
9836
9878
  }
9837
9879
  };
9838
9880
  module.exports.sync = (name, options2 = {}) => {
9839
- let directory = path.resolve(options2.cwd || "");
9840
- const { root } = path.parse(directory);
9881
+ let directory = path2.resolve(options2.cwd || "");
9882
+ const { root } = path2.parse(directory);
9841
9883
  const paths = [].concat(name);
9842
9884
  const runMatcher = (locateOptions) => {
9843
9885
  if (typeof name !== "function") {
@@ -9855,12 +9897,12 @@ var require_find_up = __commonJS({
9855
9897
  return;
9856
9898
  }
9857
9899
  if (foundPath) {
9858
- return path.resolve(directory, foundPath);
9900
+ return path2.resolve(directory, foundPath);
9859
9901
  }
9860
9902
  if (directory === root) {
9861
9903
  return;
9862
9904
  }
9863
- directory = path.dirname(directory);
9905
+ directory = path2.dirname(directory);
9864
9906
  }
9865
9907
  };
9866
9908
  module.exports.exists = pathExists;
@@ -9872,17 +9914,17 @@ var require_find_up = __commonJS({
9872
9914
  // node_modules/pkg-dir/index.js
9873
9915
  var require_pkg_dir = __commonJS({
9874
9916
  "node_modules/pkg-dir/index.js"(exports, module) {
9875
- var path = __require("path");
9917
+ var path2 = __require("path");
9876
9918
  var findUp = require_find_up();
9877
9919
  var pkgDir = async (cwd) => {
9878
9920
  const filePath = await findUp("package.json", { cwd });
9879
- return filePath && path.dirname(filePath);
9921
+ return filePath && path2.dirname(filePath);
9880
9922
  };
9881
9923
  module.exports = pkgDir;
9882
9924
  module.exports.default = pkgDir;
9883
9925
  module.exports.sync = (cwd) => {
9884
9926
  const filePath = findUp.sync("package.json", { cwd });
9885
- return filePath && path.dirname(filePath);
9927
+ return filePath && path2.dirname(filePath);
9886
9928
  };
9887
9929
  }
9888
9930
  });
@@ -11046,14 +11088,14 @@ var require_semver3 = __commonJS({
11046
11088
  // node_modules/make-dir/index.js
11047
11089
  var require_make_dir = __commonJS({
11048
11090
  "node_modules/make-dir/index.js"(exports, module) {
11049
- var fs = __require("fs");
11050
- var path = __require("path");
11091
+ var fs2 = __require("fs");
11092
+ var path2 = __require("path");
11051
11093
  var { promisify } = __require("util");
11052
11094
  var semver = require_semver3();
11053
11095
  var useNativeRecursiveOption = semver.satisfies(process.version, ">=10.12.0");
11054
11096
  var checkPath = (pth) => {
11055
11097
  if (process.platform === "win32") {
11056
- const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, ""));
11098
+ const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path2.parse(pth).root, ""));
11057
11099
  if (pathHasInvalidWinCharacters) {
11058
11100
  const error = new Error(`Path contains invalid characters: ${pth}`);
11059
11101
  error.code = "EINVAL";
@@ -11064,7 +11106,7 @@ var require_make_dir = __commonJS({
11064
11106
  var processOptions = (options2) => {
11065
11107
  const defaults = {
11066
11108
  mode: 511,
11067
- fs
11109
+ fs: fs2
11068
11110
  };
11069
11111
  return {
11070
11112
  ...defaults,
@@ -11084,8 +11126,8 @@ var require_make_dir = __commonJS({
11084
11126
  options2 = processOptions(options2);
11085
11127
  const mkdir = promisify(options2.fs.mkdir);
11086
11128
  const stat = promisify(options2.fs.stat);
11087
- if (useNativeRecursiveOption && options2.fs.mkdir === fs.mkdir) {
11088
- const pth = path.resolve(input);
11129
+ if (useNativeRecursiveOption && options2.fs.mkdir === fs2.mkdir) {
11130
+ const pth = path2.resolve(input);
11089
11131
  await mkdir(pth, {
11090
11132
  mode: options2.mode,
11091
11133
  recursive: true
@@ -11101,13 +11143,13 @@ var require_make_dir = __commonJS({
11101
11143
  throw error;
11102
11144
  }
11103
11145
  if (error.code === "ENOENT") {
11104
- if (path.dirname(pth) === pth) {
11146
+ if (path2.dirname(pth) === pth) {
11105
11147
  throw permissionError(pth);
11106
11148
  }
11107
11149
  if (error.message.includes("null bytes")) {
11108
11150
  throw error;
11109
11151
  }
11110
- await make(path.dirname(pth));
11152
+ await make(path2.dirname(pth));
11111
11153
  return make(pth);
11112
11154
  }
11113
11155
  try {
@@ -11121,15 +11163,15 @@ var require_make_dir = __commonJS({
11121
11163
  return pth;
11122
11164
  }
11123
11165
  };
11124
- return make(path.resolve(input));
11166
+ return make(path2.resolve(input));
11125
11167
  };
11126
11168
  module.exports = makeDir;
11127
11169
  module.exports.sync = (input, options2) => {
11128
11170
  checkPath(input);
11129
11171
  options2 = processOptions(options2);
11130
- if (useNativeRecursiveOption && options2.fs.mkdirSync === fs.mkdirSync) {
11131
- const pth = path.resolve(input);
11132
- fs.mkdirSync(pth, {
11172
+ if (useNativeRecursiveOption && options2.fs.mkdirSync === fs2.mkdirSync) {
11173
+ const pth = path2.resolve(input);
11174
+ fs2.mkdirSync(pth, {
11133
11175
  mode: options2.mode,
11134
11176
  recursive: true
11135
11177
  });
@@ -11143,13 +11185,13 @@ var require_make_dir = __commonJS({
11143
11185
  throw error;
11144
11186
  }
11145
11187
  if (error.code === "ENOENT") {
11146
- if (path.dirname(pth) === pth) {
11188
+ if (path2.dirname(pth) === pth) {
11147
11189
  throw permissionError(pth);
11148
11190
  }
11149
11191
  if (error.message.includes("null bytes")) {
11150
11192
  throw error;
11151
11193
  }
11152
- make(path.dirname(pth));
11194
+ make(path2.dirname(pth));
11153
11195
  return make(pth);
11154
11196
  }
11155
11197
  try {
@@ -11162,7 +11204,7 @@ var require_make_dir = __commonJS({
11162
11204
  }
11163
11205
  return pth;
11164
11206
  };
11165
- return make(path.resolve(input));
11207
+ return make(path2.resolve(input));
11166
11208
  };
11167
11209
  }
11168
11210
  });
@@ -11170,15 +11212,15 @@ var require_make_dir = __commonJS({
11170
11212
  // node_modules/find-cache-dir/index.js
11171
11213
  var require_find_cache_dir = __commonJS({
11172
11214
  "node_modules/find-cache-dir/index.js"(exports, module) {
11173
- var path = __require("path");
11174
- var fs = __require("fs");
11215
+ var path2 = __require("path");
11216
+ var fs2 = __require("fs");
11175
11217
  var commonDir = require_commondir();
11176
11218
  var pkgDir = require_pkg_dir();
11177
11219
  var makeDir = require_make_dir();
11178
11220
  var { env, cwd } = process;
11179
- var isWritable = (path2) => {
11221
+ var isWritable = (path3) => {
11180
11222
  try {
11181
- fs.accessSync(path2, fs.constants.W_OK);
11223
+ fs2.accessSync(path3, fs2.constants.W_OK);
11182
11224
  return true;
11183
11225
  } catch (_) {
11184
11226
  return false;
@@ -11189,20 +11231,20 @@ var require_find_cache_dir = __commonJS({
11189
11231
  makeDir.sync(directory);
11190
11232
  }
11191
11233
  if (options2.thunk) {
11192
- return (...arguments_) => path.join(directory, ...arguments_);
11234
+ return (...arguments_) => path2.join(directory, ...arguments_);
11193
11235
  }
11194
11236
  return directory;
11195
11237
  }
11196
11238
  function getNodeModuleDirectory(directory) {
11197
- const nodeModules = path.join(directory, "node_modules");
11198
- if (!isWritable(nodeModules) && (fs.existsSync(nodeModules) || !isWritable(path.join(directory)))) {
11239
+ const nodeModules = path2.join(directory, "node_modules");
11240
+ if (!isWritable(nodeModules) && (fs2.existsSync(nodeModules) || !isWritable(path2.join(directory)))) {
11199
11241
  return;
11200
11242
  }
11201
11243
  return nodeModules;
11202
11244
  }
11203
11245
  module.exports = (options2 = {}) => {
11204
11246
  if (env.CACHE_DIR && !["true", "false", "1", "0"].includes(env.CACHE_DIR)) {
11205
- return useDirectory(path.join(env.CACHE_DIR, options2.name), options2);
11247
+ return useDirectory(path2.join(env.CACHE_DIR, options2.name), options2);
11206
11248
  }
11207
11249
  let { cwd: directory = cwd() } = options2;
11208
11250
  if (options2.files) {
@@ -11216,7 +11258,7 @@ var require_find_cache_dir = __commonJS({
11216
11258
  if (!nodeModules) {
11217
11259
  return void 0;
11218
11260
  }
11219
- return useDirectory(path.join(directory, "node_modules", ".cache", options2.name), options2);
11261
+ return useDirectory(path2.join(directory, "node_modules", ".cache", options2.name), options2);
11220
11262
  };
11221
11263
  }
11222
11264
  });
@@ -11230,7 +11272,7 @@ var require_DryMongoBinary = __commonJS({
11230
11272
  var debug_1 = tslib_1.__importDefault(require_src());
11231
11273
  var resolveConfig_1 = require_resolveConfig();
11232
11274
  var utils_1 = require_utils();
11233
- var path = tslib_1.__importStar(__require("path"));
11275
+ var path2 = tslib_1.__importStar(__require("path"));
11234
11276
  var os_1 = __require("os");
11235
11277
  var find_cache_dir_1 = tslib_1.__importDefault(require_find_cache_dir());
11236
11278
  var getos_1 = require_getos();
@@ -11251,7 +11293,7 @@ var require_DryMongoBinary = __commonJS({
11251
11293
  const useOpts = await this.generateOptions(opts);
11252
11294
  if (useOpts.systemBinary) {
11253
11295
  log(`locateBinary: env "SYSTEM_BINARY" was provided with value: "${useOpts.systemBinary}"`);
11254
- const systemReturn = await this.getSystemPath(path.resolve(useOpts.systemBinary));
11296
+ const systemReturn = await this.getSystemPath(path2.resolve(useOpts.systemBinary));
11255
11297
  if ((0, utils_1.isNullOrUndefined)(systemReturn)) {
11256
11298
  throw new errors_1.BinaryNotFoundError(useOpts.systemBinary, " (systemBinary)");
11257
11299
  }
@@ -11268,7 +11310,7 @@ var require_DryMongoBinary = __commonJS({
11268
11310
  log("locateBinary: could not find a existing binary");
11269
11311
  return void 0;
11270
11312
  }
11271
- if (returnValue[0] && await (0, utils_1.pathExists)((0, utils_1.lockfilePath)(path.dirname(returnValue[1]), useOpts.version))) {
11313
+ if (returnValue[0] && await (0, utils_1.pathExists)((0, utils_1.lockfilePath)(path2.dirname(returnValue[1]), useOpts.version))) {
11272
11314
  log("locateBinary: binary found, but also a download-lock, trying to resolve lock");
11273
11315
  return void 0;
11274
11316
  }
@@ -11299,7 +11341,7 @@ var require_DryMongoBinary = __commonJS({
11299
11341
  arch: ensuredOpts.arch || (0, os_1.arch)(),
11300
11342
  systemBinary: (0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.SYSTEM_BINARY) || ensuredOpts.systemBinary || ""
11301
11343
  };
11302
- final.downloadDir = path.dirname((await this.generateDownloadPath(final))[1]);
11344
+ final.downloadDir = path2.dirname((await this.generateDownloadPath(final))[1]);
11303
11345
  if ((0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.ARCHIVE_NAME) || (0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.DOWNLOAD_URL)) {
11304
11346
  const input = (0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.DOWNLOAD_URL) || (0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.ARCHIVE_NAME);
11305
11347
  log(`generateOptions: ARCHIVE_NAME or DOWNLOAD_URL defined, generating options based on that (input: "${input}")`);
@@ -11346,7 +11388,7 @@ var require_DryMongoBinary = __commonJS({
11346
11388
  let binaryName;
11347
11389
  if ((0, resolveConfig_1.envToBool)((0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.USE_ARCHIVE_NAME_FOR_BINARY_NAME))) {
11348
11390
  const archiveName = await new MongoBinaryDownloadUrl_1.MongoBinaryDownloadUrl(opts).getArchiveName();
11349
- binaryName = path.parse(archiveName).name;
11391
+ binaryName = path2.parse(archiveName).name;
11350
11392
  } else {
11351
11393
  const addExe = opts.platform === "win32" ? ".exe" : "";
11352
11394
  const dist = (0, getos_1.isLinuxOS)(opts.os) ? opts.os.dist : opts.os.os;
@@ -11359,7 +11401,7 @@ var require_DryMongoBinary = __commonJS({
11359
11401
  */
11360
11402
  static combineBinaryName(basePath, binaryName) {
11361
11403
  log("combineBinaryName");
11362
- return path.resolve(basePath, binaryName);
11404
+ return path2.resolve(basePath, binaryName);
11363
11405
  }
11364
11406
  /**
11365
11407
  * Probe if the provided "systemBinary" is an existing path
@@ -11394,11 +11436,11 @@ ${err instanceof Error ? err.message : err}`);
11394
11436
  };
11395
11437
  const binaryName = await this.getBinaryName(opts);
11396
11438
  let nodeModulesDLDir = process.env["INIT_CWD"] || process.cwd();
11397
- while (nodeModulesDLDir.includes(`node_modules${path.sep}mongodb-memory-server`)) {
11398
- nodeModulesDLDir = path.resolve(nodeModulesDLDir, "..", "..");
11439
+ while (nodeModulesDLDir.includes(`node_modules${path2.sep}mongodb-memory-server`)) {
11440
+ nodeModulesDLDir = path2.resolve(nodeModulesDLDir, "..", "..");
11399
11441
  }
11400
11442
  const configPackagePath = (0, resolveConfig_1.packageJsonPath)();
11401
- if (configPackagePath && await (0, utils_1.pathExists)(path.resolve(configPackagePath, "node_modules"))) {
11443
+ if (configPackagePath && await (0, utils_1.pathExists)(path2.resolve(configPackagePath, "node_modules"))) {
11402
11444
  nodeModulesDLDir = configPackagePath;
11403
11445
  }
11404
11446
  const tmpModulesCache = (0, find_cache_dir_1.default)({
@@ -11406,16 +11448,16 @@ ${err instanceof Error ? err.message : err}`);
11406
11448
  cwd: nodeModulesDLDir
11407
11449
  });
11408
11450
  if (!(0, utils_1.isNullOrUndefined)(tmpModulesCache)) {
11409
- final.modulesCache = this.combineBinaryName(path.resolve(tmpModulesCache), binaryName);
11451
+ final.modulesCache = this.combineBinaryName(path2.resolve(tmpModulesCache), binaryName);
11410
11452
  }
11411
- const homeCache = path.resolve(this.homedir(), ".cache/mongodb-binaries");
11453
+ const homeCache = path2.resolve(this.homedir(), ".cache/mongodb-binaries");
11412
11454
  final.homeCache = this.combineBinaryName(homeCache, binaryName);
11413
11455
  const resolveConfigValue = opts.downloadDir || (0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.DOWNLOAD_DIR);
11414
11456
  if (!(0, utils_1.isNullOrUndefined)(resolveConfigValue) && resolveConfigValue.length > 0) {
11415
11457
  log(`generatePaths: resolveConfigValue is not empty`);
11416
11458
  final.resolveConfig = this.combineBinaryName(resolveConfigValue, binaryName);
11417
11459
  }
11418
- final.relative = this.combineBinaryName(path.resolve(process.cwd(), "mongodb-binaries"), binaryName);
11460
+ final.relative = this.combineBinaryName(path2.resolve(process.cwd(), "mongodb-binaries"), binaryName);
11419
11461
  return final;
11420
11462
  }
11421
11463
  /**
@@ -12378,7 +12420,7 @@ var require_lockfile = __commonJS({
12378
12420
  var events_1 = __require("events");
12379
12421
  var utils = tslib_1.__importStar(require_utils());
12380
12422
  var debug_1 = tslib_1.__importDefault(require_src());
12381
- var path = tslib_1.__importStar(__require("path"));
12423
+ var path2 = tslib_1.__importStar(__require("path"));
12382
12424
  var fs_1 = __require("fs");
12383
12425
  var async_mutex_1 = require_lib2();
12384
12426
  var errors_1 = require_errors();
@@ -12418,7 +12460,7 @@ var require_lockfile = __commonJS({
12418
12460
  static async lock(file) {
12419
12461
  await utils.ensureAsync();
12420
12462
  log(`lock: Locking file "${file}"`);
12421
- const useFile = path.resolve(file.trim());
12463
+ const useFile = path2.resolve(file.trim());
12422
12464
  utils.assertion(useFile.length > 0, new Error("Provided Path for lock file is length of 0"));
12423
12465
  const status = await this.checkLock(useFile);
12424
12466
  switch (status) {
@@ -12519,7 +12561,7 @@ var require_lockfile = __commonJS({
12519
12561
  log(`createLock: Map already has file "${file}"`);
12520
12562
  throw new RepeatError(true);
12521
12563
  }
12522
- await utils.mkdir(path.dirname(file));
12564
+ await utils.mkdir(path2.dirname(file));
12523
12565
  await fs_1.promises.writeFile(file, `${process.pid.toString()} ${uuid}`);
12524
12566
  this.files.add(file);
12525
12567
  this.events.emit(LockFileEvents.lock, file);
@@ -12705,7 +12747,7 @@ var require_MongoInstance = __commonJS({
12705
12747
  exports.MongoInstance = exports.MongoInstanceEvents = void 0;
12706
12748
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
12707
12749
  var child_process_1 = __require("child_process");
12708
- var path = tslib_1.__importStar(__require("path"));
12750
+ var path2 = tslib_1.__importStar(__require("path"));
12709
12751
  var MongoBinary_1 = require_MongoBinary();
12710
12752
  var debug_1 = tslib_1.__importDefault(require_src());
12711
12753
  var utils_1 = require_utils();
@@ -12942,7 +12984,7 @@ var require_MongoInstance = __commonJS({
12942
12984
  */
12943
12985
  _launchMongod(mongoBin) {
12944
12986
  this.debug("_launchMongod: Launching Mongod Process");
12945
- const childProcess = (0, child_process_1.spawn)(path.resolve(mongoBin), this.prepareCommandArgs(), {
12987
+ const childProcess = (0, child_process_1.spawn)(path2.resolve(mongoBin), this.prepareCommandArgs(), {
12946
12988
  ...this.spawnOpts,
12947
12989
  stdio: "pipe"
12948
12990
  // ensure that stdio is always an pipe, regardless of user input
@@ -12952,7 +12994,7 @@ var require_MongoInstance = __commonJS({
12952
12994
  childProcess.on("close", this.closeHandler.bind(this));
12953
12995
  childProcess.on("error", this.errorHandler.bind(this));
12954
12996
  if ((0, utils_1.isNullOrUndefined)(childProcess.pid)) {
12955
- throw new errors_1.StartBinaryFailedError(path.resolve(mongoBin));
12997
+ throw new errors_1.StartBinaryFailedError(path2.resolve(mongoBin));
12956
12998
  }
12957
12999
  childProcess.unref();
12958
13000
  this.emit(MongoInstanceEvents.instanceLaunched);
@@ -12966,7 +13008,7 @@ var require_MongoInstance = __commonJS({
12966
13008
  */
12967
13009
  _launchKiller(parentPid, childPid) {
12968
13010
  this.debug(`_launchKiller: Launching Killer Process (parent: ${parentPid}, child: ${childPid})`);
12969
- const killer = (0, child_process_1.fork)(path.resolve(__dirname, "../../scripts/mongo_killer.js"), [parentPid.toString(), childPid.toString()], {
13011
+ const killer = (0, child_process_1.fork)(path2.resolve(__dirname, "../../scripts/mongo_killer.js"), [parentPid.toString(), childPid.toString()], {
12970
13012
  execArgv: [],
12971
13013
  detached: true,
12972
13014
  stdio: "ignore"
@@ -14137,8 +14179,20 @@ var NullProvider = class {
14137
14179
  status: "accepted"
14138
14180
  };
14139
14181
  }
14182
+ /**
14183
+ * Fails closed. This provider has no signing key, so it can never establish
14184
+ * that an inbound payload is authentic — and a method whose entire job is to
14185
+ * reject unauthenticated input must not answer "yes" by default. Returning
14186
+ * `true` here would mean any unsigned body posted to the webhook route is
14187
+ * accepted as genuine whenever the null provider is registered, which is
14188
+ * exactly the dev/staging configuration where that is easiest to miss.
14189
+ *
14190
+ * Nothing is lost: `parseWebhookEvents()` returns `[]`, so there was never
14191
+ * any inbound behaviour to preserve. To exercise the inbound half locally,
14192
+ * use a provider that actually signs (see `test/README.md`).
14193
+ */
14140
14194
  async verifyWebhook() {
14141
- return true;
14195
+ return false;
14142
14196
  }
14143
14197
  parseWebhookEvents() {
14144
14198
  return [];
@@ -14268,6 +14322,10 @@ var flowStepSchema = z.lazy(
14268
14322
  }),
14269
14323
  z.object({
14270
14324
  type: z.literal("webhook"),
14325
+ // `url` is validated for shape only and is deliberately never templated —
14326
+ // see the comment on handleWebhookStep in src/server/runner/step.ts, and
14327
+ // INVARIANT 16. Rendering contact/event data into this field would turn an
14328
+ // admin-authored flow into a server-side request forgery primitive.
14271
14329
  url: z.string().url(),
14272
14330
  method: z.enum(["POST", "PUT"]).optional(),
14273
14331
  payload: z.record(z.string(), z.unknown()).optional(),
@@ -14361,6 +14419,7 @@ var DEFAULTS = {
14361
14419
  requireDoubleOptIn: false,
14362
14420
  unsubscribeTokenLifetimeDays: 90,
14363
14421
  transactionalRespectUnsubscribe: false,
14422
+ unsubscribeWriteTimeoutMs: 5e3,
14364
14423
  doiTemplateSlug: "doi-confirmation",
14365
14424
  doiTokenLifetimeDays: 7,
14366
14425
  broadcastConfirmationThreshold: 1e3,
@@ -14377,7 +14436,9 @@ var DEFAULTS = {
14377
14436
  trackOpens: true,
14378
14437
  trackClicks: true,
14379
14438
  storeTrackingIp: false,
14380
- storeRenderedBody: false
14439
+ storeRenderedBody: false,
14440
+ requireSignedTrackingUrls: false,
14441
+ trackingUrlLifetimeDays: 0
14381
14442
  };
14382
14443
  var CIRCUIT_BREAKER_DEFAULTS = {
14383
14444
  hardBounceRatePctTrip: 2,
@@ -14394,6 +14455,7 @@ function resolveConfig(c) {
14394
14455
  requireDoubleOptIn: c.requireDoubleOptIn ?? DEFAULTS.requireDoubleOptIn,
14395
14456
  unsubscribeTokenLifetimeDays: c.unsubscribeTokenLifetimeDays ?? DEFAULTS.unsubscribeTokenLifetimeDays,
14396
14457
  transactionalRespectUnsubscribe: c.transactionalRespectUnsubscribe ?? DEFAULTS.transactionalRespectUnsubscribe,
14458
+ unsubscribeWriteTimeoutMs: c.unsubscribeWriteTimeoutMs ?? DEFAULTS.unsubscribeWriteTimeoutMs,
14397
14459
  doiTemplateSlug: c.doiTemplateSlug ?? DEFAULTS.doiTemplateSlug,
14398
14460
  doiTokenLifetimeDays: c.doiTokenLifetimeDays ?? DEFAULTS.doiTokenLifetimeDays,
14399
14461
  broadcastConfirmationThreshold: c.broadcastConfirmationThreshold ?? DEFAULTS.broadcastConfirmationThreshold,
@@ -14411,6 +14473,8 @@ function resolveConfig(c) {
14411
14473
  trackClicks: c.trackClicks ?? DEFAULTS.trackClicks,
14412
14474
  storeTrackingIp: c.storeTrackingIp ?? DEFAULTS.storeTrackingIp,
14413
14475
  storeRenderedBody: c.storeRenderedBody ?? DEFAULTS.storeRenderedBody,
14476
+ requireSignedTrackingUrls: c.requireSignedTrackingUrls ?? DEFAULTS.requireSignedTrackingUrls,
14477
+ trackingUrlLifetimeDays: c.trackingUrlLifetimeDays ?? DEFAULTS.trackingUrlLifetimeDays,
14414
14478
  circuitBreaker: { ...CIRCUIT_BREAKER_DEFAULTS, ...c.circuitBreaker ?? {} }
14415
14479
  };
14416
14480
  }
@@ -14613,6 +14677,22 @@ function isoDay(d) {
14613
14677
  function randomId() {
14614
14678
  return crypto2.randomBytes(8).toString("hex");
14615
14679
  }
14680
+
14681
+ // src/server/provider-lookup.ts
14682
+ var PROVIDER_METHODS = ["send", "verifyWebhook", "parseWebhookEvents"];
14683
+ function isMailProvider(candidate) {
14684
+ if (!candidate || typeof candidate !== "object") return false;
14685
+ const p = candidate;
14686
+ return PROVIDER_METHODS.every((m) => typeof p[m] === "function");
14687
+ }
14688
+ function resolveProvider(providers, name) {
14689
+ if (typeof name !== "string" || !Object.hasOwn(providers, name)) return null;
14690
+ const candidate = providers[name];
14691
+ return isMailProvider(candidate) ? candidate : null;
14692
+ }
14693
+ function registeredProviderNames(providers) {
14694
+ return Object.keys(providers).filter((name) => resolveProvider(providers, name) !== null).sort();
14695
+ }
14616
14696
  function signUnsubscribeToken(payload, secret) {
14617
14697
  const body = JSON.stringify({
14618
14698
  e: payload.email.toLowerCase(),
@@ -14623,6 +14703,19 @@ function signUnsubscribeToken(payload, secret) {
14623
14703
  const hmac = crypto2.createHmac("sha256", secret).update(bodyB64).digest();
14624
14704
  return `${bodyB64}.${b64url(hmac)}`;
14625
14705
  }
14706
+ var TRACKING_SCOPES = ["open", "click"];
14707
+ var TRACKING_SIG_LENGTH = 12;
14708
+ function trackingMessage(scope, params) {
14709
+ const tail = params.linkId === void 0 ? "" : `:${params.linkId}`;
14710
+ return `mailery.track.v1:${scope}:${params.sendId}${tail}`;
14711
+ }
14712
+ function signTrackingToken(scope, params, secret) {
14713
+ if (!TRACKING_SCOPES.includes(scope)) {
14714
+ throw new Error(`signTrackingToken: unknown scope ${String(scope)}`);
14715
+ }
14716
+ const mac = crypto2.createHmac("sha256", secret).update(trackingMessage(scope, params)).digest();
14717
+ return b64url(mac).slice(0, TRACKING_SIG_LENGTH);
14718
+ }
14626
14719
  function b64url(buf) {
14627
14720
  return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
14628
14721
  }
@@ -14639,6 +14732,39 @@ function signDoiToken(payload, secret) {
14639
14732
  const hmac = crypto2.createHmac("sha256", secret).update(bodyB64).digest();
14640
14733
  return `${bodyB64}.${b64url(hmac)}`;
14641
14734
  }
14735
+
14736
+ // src/server/unsubscribe.ts
14737
+ async function applyUnsubscribe(collections, input, now = /* @__PURE__ */ new Date()) {
14738
+ const normalized = input.email;
14739
+ await collections.suppressions.updateOne(
14740
+ { email: normalized, scope: input.scope },
14741
+ {
14742
+ $setOnInsert: {
14743
+ email: normalized,
14744
+ emailHash: sha256Hex(normalized),
14745
+ scope: input.scope,
14746
+ // mailer_suppressions canonical reason — see plans/02-data-model.md.
14747
+ reason: "unsubscribed",
14748
+ source: input.source,
14749
+ notes: input.notes ?? null,
14750
+ addedAt: now,
14751
+ expiresAt: null
14752
+ }
14753
+ },
14754
+ { upsert: true }
14755
+ );
14756
+ await collections.subscriptions.updateOne(
14757
+ { emailAtSubscribe: normalized },
14758
+ {
14759
+ $set: {
14760
+ status: "unsubscribed",
14761
+ unsubscribedAt: now,
14762
+ unsubscribeReason: input.reason,
14763
+ updatedAt: now
14764
+ }
14765
+ }
14766
+ );
14767
+ }
14642
14768
  var QUEUE_NAMES = {
14643
14769
  tick: "mailer-tick",
14644
14770
  advance: "mailer-advance",
@@ -15433,7 +15559,7 @@ async function suppressOnce(ctx, email, reason, scope) {
15433
15559
  }
15434
15560
 
15435
15561
  // src/server/runner/predicate.ts
15436
- var BOT_UA_RE = /Mimecast|SafeLinks|proofpoint|HeadlessChrome|Googlebot|bingbot/i;
15562
+ var DEFAULT_BOT_UA_RE = /Mimecast|SafeLinks|proofpoint|HeadlessChrome|Googlebot|bingbot/i;
15437
15563
  async function evaluatePredicate(predicate, ctx) {
15438
15564
  const p = predicate;
15439
15565
  if ("hasTag" in p) return ctx.contact.tags.includes(p.hasTag);
@@ -15501,17 +15627,35 @@ async function openOrClickCount(ctx, kind, opts, excludeBots) {
15501
15627
  return await ctx.collections.sends.countDocuments(filter);
15502
15628
  }
15503
15629
  const docs = await ctx.collections.sends.find(filter).limit(1e3).toArray();
15630
+ const botRe = ctx.botFilter?.userAgentPattern ?? DEFAULT_BOT_UA_RE;
15631
+ const minOpenDelayMs = ctx.botFilter?.minOpenDelayMs ?? 0;
15504
15632
  let n = 0;
15505
15633
  for (const s of docs) {
15506
- if (kind === "opened") {
15507
- n++;
15508
- continue;
15509
- }
15510
- const hasHumanClick = s.clickedLinks?.some((c) => !c.userAgent || !BOT_UA_RE.test(c.userAgent));
15511
- if (hasHumanClick) n++;
15634
+ const doc = s;
15635
+ const human = kind === "opened" ? hasHumanOpen(doc, botRe, minOpenDelayMs) : (doc.clickedLinks ?? []).some((c) => !isBotUserAgent(c.userAgent, botRe));
15636
+ if (human) n++;
15512
15637
  }
15513
15638
  return n;
15514
15639
  }
15640
+ function isBotUserAgent(ua, botRe) {
15641
+ if (typeof ua !== "string" || ua.trim() === "") return false;
15642
+ return botRe.test(ua);
15643
+ }
15644
+ function hasHumanOpen(send, botRe, minOpenDelayMs) {
15645
+ const opens = send.opens && send.opens.length > 0 ? send.opens : send.openedAt ? [{ openedAt: send.openedAt, userAgent: null }] : [];
15646
+ return opens.some((o) => {
15647
+ if (isBotUserAgent(o.userAgent, botRe)) return false;
15648
+ if (isPrefetchOpen(o.openedAt, send.queuedAt, minOpenDelayMs)) return false;
15649
+ return true;
15650
+ });
15651
+ }
15652
+ function isPrefetchOpen(openedAt, queuedAt, minOpenDelayMs) {
15653
+ if (!minOpenDelayMs || minOpenDelayMs <= 0) return false;
15654
+ if (!(openedAt instanceof Date) || !(queuedAt instanceof Date)) return false;
15655
+ const delta = openedAt.getTime() - queuedAt.getTime();
15656
+ if (!Number.isFinite(delta) || delta < 0) return false;
15657
+ return delta < minOpenDelayMs;
15658
+ }
15515
15659
  function effectiveLowerBound(ctx, opts) {
15516
15660
  const now = ctx.now ?? /* @__PURE__ */ new Date();
15517
15661
  if (opts.sinceFlowStart) return ctx.run.enteredAt;
@@ -15633,6 +15777,7 @@ function derivePlaintext(html) {
15633
15777
  ]
15634
15778
  }).trim();
15635
15779
  }
15780
+ var PLAIN_TEXT = { noEscape: true };
15636
15781
  async function renderTemplate(template, ctx, opts = {}) {
15637
15782
  const hb = makeHandlebars(opts.helpers);
15638
15783
  const subject = hb.compile(template.subject)(ctx);
@@ -15644,7 +15789,7 @@ async function renderTemplate(template, ctx, opts = {}) {
15644
15789
  const compiled = await compileTemplate(template.body.mjml);
15645
15790
  html = hb.compile(compiled.html)(ctx);
15646
15791
  }
15647
- const plainText = template.body.plainText ? hb.compile(template.body.plainText)(ctx) : derivePlaintext(html);
15792
+ const plainText = template.body.plainText ? hb.compile(template.body.plainText, PLAIN_TEXT)(ctx) : derivePlaintext(html);
15648
15793
  return {
15649
15794
  subject,
15650
15795
  preheader,
@@ -15670,12 +15815,14 @@ function applyTracking(html, opts) {
15670
15815
  seen.set(url, linkId);
15671
15816
  links.push({ linkId, url });
15672
15817
  }
15673
- const newHref = `${opts.publicUrl}/m/click/${opts.sendId}/${linkId}`;
15818
+ const sig = opts.signingSecret ? `/${signTrackingToken("click", { sendId: opts.sendId, linkId }, opts.signingSecret)}` : "";
15819
+ const newHref = `${opts.publicUrl}/m/click/${opts.sendId}/${linkId}${sig}`;
15674
15820
  return `<a${pre}href="${newHref}"${post}>`;
15675
15821
  });
15676
15822
  }
15677
15823
  if (opts.trackOpens) {
15678
- const pixel = `<img src="${opts.publicUrl}/m/open/${opts.sendId}.png" width="1" height="1" alt="" style="display:block;border:0" />`;
15824
+ const sig = opts.signingSecret ? `.${signTrackingToken("open", { sendId: opts.sendId }, opts.signingSecret)}` : "";
15825
+ const pixel = `<img src="${opts.publicUrl}/m/open/${opts.sendId}${sig}.png" width="1" height="1" alt="" style="display:block;border:0" />`;
15679
15826
  if (/<\/body>/i.test(out)) {
15680
15827
  out = out.replace(/<\/body>/i, `${pixel}</body>`);
15681
15828
  } else {
@@ -15686,14 +15833,22 @@ function applyTracking(html, opts) {
15686
15833
  }
15687
15834
  function shouldSkipClickRewrite(url, preserve, fullTag) {
15688
15835
  if (!url) return true;
15689
- if (url.startsWith("mailto:")) return true;
15690
- if (url.startsWith("tel:")) return true;
15691
15836
  if (url.startsWith("#")) return true;
15692
15837
  if (url.startsWith("{{") || url.startsWith("{")) return true;
15693
15838
  if (preserve.has(url.trim())) return true;
15694
15839
  if (/data-mailer-notrack=["']true["']/.test(fullTag)) return true;
15840
+ if (!isHttpUrl(url)) return true;
15695
15841
  return false;
15696
15842
  }
15843
+ function isHttpUrl(url) {
15844
+ let parsed;
15845
+ try {
15846
+ parsed = new URL(url.trim());
15847
+ } catch {
15848
+ return false;
15849
+ }
15850
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
15851
+ }
15697
15852
  var NAMED_ENTITIES = {
15698
15853
  amp: "&",
15699
15854
  lt: "<",
@@ -15914,7 +16069,8 @@ async function dispatchSend(sendId, ctx) {
15914
16069
  publicUrl: ctx.config.publicUrl,
15915
16070
  trackOpens: template.trackOpens ?? ctx.config.trackOpens,
15916
16071
  trackClicks: template.trackClicks ?? ctx.config.trackClicks,
15917
- preserveUrls: [renderCtx.unsubscribeUrl]
16072
+ preserveUrls: [renderCtx.unsubscribeUrl],
16073
+ signingSecret: ctx.config.unsubscribeSecret
15918
16074
  });
15919
16075
  await ctx.collections.sends.updateOne(
15920
16076
  { _id: send._id },
@@ -15932,9 +16088,14 @@ async function dispatchSend(sendId, ctx) {
15932
16088
  }
15933
16089
  }
15934
16090
  );
15935
- const provider = ctx.providers[send.provider] ?? ctx.providers[ctx.config.defaultProvider];
16091
+ const provider = resolveProvider(ctx.providers, send.provider);
15936
16092
  if (!provider) {
15937
- await markFailed(send._id, `provider_unknown: ${send.provider}`, ctx);
16093
+ const known = registeredProviderNames(ctx.providers);
16094
+ await markFailed(
16095
+ send._id,
16096
+ `provider_unknown: no provider is registered as "${send.provider}". Registered providers: ${known.length ? known.join(", ") : "(none)"}.`,
16097
+ ctx
16098
+ );
15938
16099
  return;
15939
16100
  }
15940
16101
  const headers = send.kind === "marketing" ? {
@@ -16133,7 +16294,7 @@ async function handleWait(run, step2, ctx) {
16133
16294
  }
16134
16295
  async function handleCondition(run, step2, contact, ctx) {
16135
16296
  if (!contact) return;
16136
- const result = await evaluatePredicate(step2.test, { contact, run, collections: ctx.collections });
16297
+ const result = await evaluatePredicate(step2.test, { contact, run, collections: ctx.collections, botFilter: ctx.config.botFilter });
16137
16298
  if (result) {
16138
16299
  await advanceStep(run, ctx, { action: "condition_evaluated", details: { result: true } });
16139
16300
  return;
@@ -16146,7 +16307,7 @@ async function handleCondition(run, step2, contact, ctx) {
16146
16307
  }
16147
16308
  async function handleBranch(run, step2, contact, ctx) {
16148
16309
  if (!contact) return;
16149
- const result = await evaluatePredicate(step2.test, { contact, run, collections: ctx.collections });
16310
+ const result = await evaluatePredicate(step2.test, { contact, run, collections: ctx.collections, botFilter: ctx.config.botFilter });
16150
16311
  const newPath = [...run.currentBranchPath, run.currentStepIndex, result ? "true" : "false", 0];
16151
16312
  const updated = await ctx.collections.flowRuns.findOneAndUpdate(
16152
16313
  { _id: run._id, currentStepIndex: run.currentStepIndex },
@@ -16917,14 +17078,14 @@ function createPostmasterClient(cfg, fetcher = globalThis.fetch) {
16917
17078
  };
16918
17079
  return cached.accessToken;
16919
17080
  }
16920
- async function authedGet(path) {
17081
+ async function authedGet(path2) {
16921
17082
  const token = await getAccessToken();
16922
- const res = await fetchWithTimeout(fetcher, `${POSTMASTER_BASE}${path}`, {
17083
+ const res = await fetchWithTimeout(fetcher, `${POSTMASTER_BASE}${path2}`, {
16923
17084
  method: "GET",
16924
17085
  headers: { authorization: `Bearer ${token}` }
16925
17086
  });
16926
17087
  if (!res.ok) {
16927
- throw new Error(`Postmaster GET ${path} failed: ${res.status} ${await safeText(res)}`);
17088
+ throw new Error(`Postmaster GET ${path2} failed: ${res.status} ${await safeText(res)}`);
16928
17089
  }
16929
17090
  return await res.json();
16930
17091
  }
@@ -17302,6 +17463,250 @@ async function pruneDmarcFailures(ctx, opts = {}) {
17302
17463
  const r = await ctx.collections.dmarcFailures.deleteMany({ receivedAt: { $lt: cutoff } });
17303
17464
  return r.deletedCount ?? 0;
17304
17465
  }
17466
+ var DRAINING_INFIX = ".draining.";
17467
+ var ADOPT_AFTER_MS = 5 * 60 * 1e3;
17468
+ var JOURNAL_WARN_BYTES = 8 * 1024 * 1024;
17469
+ var O_NOFOLLOW = fs.constants.O_NOFOLLOW ?? 0;
17470
+ var APPEND_FLAGS = fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_APPEND | O_NOFOLLOW;
17471
+ function assertPrivateRegularFile(fd, journalPath) {
17472
+ const st = fs.fstatSync(fd);
17473
+ if (!st.isFile()) {
17474
+ throw new Error(`pending-unsubscribe journal is not a regular file: ${journalPath}`);
17475
+ }
17476
+ if (st.nlink > 1) {
17477
+ throw new Error(`pending-unsubscribe journal has ${st.nlink} hard links: ${journalPath}`);
17478
+ }
17479
+ if ((st.mode & 63) !== 0) fs.fchmodSync(fd, 384);
17480
+ }
17481
+ function serializeEntry(entry) {
17482
+ return JSON.stringify(entry) + "\n";
17483
+ }
17484
+ function claimToken() {
17485
+ return `${Date.now().toString(36)}-${randomBytes(6).toString("hex")}`;
17486
+ }
17487
+ function claimJournal(journalPath) {
17488
+ const claimed = `${journalPath}${DRAINING_INFIX}${claimToken()}`;
17489
+ try {
17490
+ fs.renameSync(journalPath, claimed);
17491
+ } catch (err) {
17492
+ if (isMissing(err)) return null;
17493
+ throw err;
17494
+ }
17495
+ markClaimActive(claimed);
17496
+ return claimed;
17497
+ }
17498
+ function markClaimActive(claimPath) {
17499
+ const now = /* @__PURE__ */ new Date();
17500
+ try {
17501
+ fs.utimesSync(claimPath, now, now);
17502
+ } catch (err) {
17503
+ if (!isMissing(err)) throw err;
17504
+ }
17505
+ }
17506
+ function adoptAbandonedClaims(journalPath, opts = {}) {
17507
+ const olderThanMs = opts.olderThanMs ?? ADOPT_AFTER_MS;
17508
+ const now = opts.now ?? Date.now();
17509
+ const dir = path.dirname(journalPath);
17510
+ const prefix = path.basename(journalPath) + DRAINING_INFIX;
17511
+ let names;
17512
+ try {
17513
+ names = fs.readdirSync(dir);
17514
+ } catch (err) {
17515
+ if (isMissing(err)) return [];
17516
+ throw err;
17517
+ }
17518
+ const adopted = [];
17519
+ for (const name of names) {
17520
+ if (!name.startsWith(prefix)) continue;
17521
+ const full = path.join(dir, name);
17522
+ let st;
17523
+ try {
17524
+ st = fs.statSync(full);
17525
+ } catch (err) {
17526
+ if (isMissing(err)) continue;
17527
+ throw err;
17528
+ }
17529
+ if (!st.isFile()) continue;
17530
+ if (now - st.mtimeMs < olderThanMs) continue;
17531
+ const mine = `${journalPath}${DRAINING_INFIX}${claimToken()}`;
17532
+ try {
17533
+ fs.renameSync(full, mine);
17534
+ markClaimActive(mine);
17535
+ adopted.push(mine);
17536
+ } catch (err) {
17537
+ if (!isMissing(err)) throw err;
17538
+ }
17539
+ }
17540
+ return adopted;
17541
+ }
17542
+ function readClaim(claimPath) {
17543
+ let raw;
17544
+ try {
17545
+ raw = fs.readFileSync(claimPath, "utf8");
17546
+ } catch (err) {
17547
+ if (isMissing(err)) return { entries: [], malformed: [], bytes: 0 };
17548
+ throw err;
17549
+ }
17550
+ const entries = [];
17551
+ const malformed = [];
17552
+ const lines = raw.split("\n");
17553
+ for (let i = 0; i < lines.length; i++) {
17554
+ const line = lines[i];
17555
+ if (line.trim() === "") continue;
17556
+ const parsed = parseEntry(line);
17557
+ if (parsed) entries.push(parsed);
17558
+ else malformed.push({ lineNumber: i + 1, preview: line.slice(0, 120) });
17559
+ }
17560
+ return { entries, malformed, bytes: Buffer.byteLength(raw) };
17561
+ }
17562
+ function parseEntry(line) {
17563
+ let obj;
17564
+ try {
17565
+ obj = JSON.parse(line);
17566
+ } catch {
17567
+ return null;
17568
+ }
17569
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) return null;
17570
+ const o = obj;
17571
+ const check = unsubscribeInputSchema.safeParse({
17572
+ email: o.email,
17573
+ scope: o.scope,
17574
+ reason: "user_request",
17575
+ source: "one-click"
17576
+ });
17577
+ if (!check.success) return null;
17578
+ const at = typeof o.at === "number" && Number.isFinite(o.at) ? o.at : Date.now();
17579
+ const attempts = typeof o.attempts === "number" && Number.isFinite(o.attempts) && o.attempts > 0 ? Math.floor(o.attempts) : void 0;
17580
+ return {
17581
+ email: check.data.email,
17582
+ scope: check.data.scope,
17583
+ at,
17584
+ ...attempts === void 0 ? {} : { attempts }
17585
+ };
17586
+ }
17587
+ function entryToInput(entry) {
17588
+ return unsubscribeInputSchema.parse({
17589
+ email: entry.email,
17590
+ scope: entry.scope,
17591
+ reason: "user_request",
17592
+ // Distinguishable in `mailer_suppressions.source` from a live one-click,
17593
+ // but only on rows the drain actually created (the upsert is
17594
+ // `$setOnInsert`, so a row the live path already wrote keeps its own).
17595
+ source: "one-click-drain"
17596
+ });
17597
+ }
17598
+ function releaseClaim(journalPath, claimPath, remaining) {
17599
+ if (remaining.length > 0) {
17600
+ fs.mkdirSync(path.dirname(journalPath), { recursive: true, mode: 448 });
17601
+ const fd = fs.openSync(journalPath, APPEND_FLAGS, 384);
17602
+ try {
17603
+ assertPrivateRegularFile(fd, journalPath);
17604
+ fs.writeSync(
17605
+ fd,
17606
+ remaining.map((e) => serializeEntry({ ...e, attempts: (e.attempts ?? 0) + 1 })).join("")
17607
+ );
17608
+ } finally {
17609
+ fs.closeSync(fd);
17610
+ }
17611
+ }
17612
+ try {
17613
+ fs.unlinkSync(claimPath);
17614
+ } catch (err) {
17615
+ if (!isMissing(err)) throw err;
17616
+ }
17617
+ }
17618
+ function journalExists(journalPath) {
17619
+ try {
17620
+ return fs.statSync(journalPath).isFile();
17621
+ } catch (err) {
17622
+ if (isMissing(err)) return false;
17623
+ throw err;
17624
+ }
17625
+ }
17626
+ function isMissing(err) {
17627
+ const code = err?.code;
17628
+ return code === "ENOENT" || code === "ENOTDIR";
17629
+ }
17630
+
17631
+ // src/server/runner/pending-unsubs.ts
17632
+ var MAX_ENTRIES_PER_PASS = 1e3;
17633
+ var NOISY_AFTER_ATTEMPTS = 10;
17634
+ var consoleLog = {
17635
+ warn: (fields, msg) => console.warn(msg ?? "mailery", fields),
17636
+ error: (fields, msg) => console.error(msg ?? "mailery", fields)
17637
+ };
17638
+ async function drainPendingUnsubscribes(ctx, opts = {}) {
17639
+ const journalPath = opts.path ?? ctx.config.pendingUnsubsPath;
17640
+ const result = {
17641
+ enabled: Boolean(journalPath),
17642
+ applied: 0,
17643
+ deferred: 0,
17644
+ malformed: 0,
17645
+ batches: 0
17646
+ };
17647
+ if (!journalPath) return result;
17648
+ const log = opts.log ?? consoleLog;
17649
+ const budget = opts.maxEntries ?? MAX_ENTRIES_PER_PASS;
17650
+ const claims = [];
17651
+ try {
17652
+ claims.push(...adoptAbandonedClaims(journalPath));
17653
+ } catch (err) {
17654
+ log.error?.({ err, path: journalPath }, "mailery: pending-unsub claim adoption failed");
17655
+ }
17656
+ if (journalExists(journalPath)) {
17657
+ const claimed = claimJournal(journalPath);
17658
+ if (claimed) claims.push(claimed);
17659
+ }
17660
+ if (claims.length === 0) return result;
17661
+ let remainingBudget = budget;
17662
+ for (const claimPath of claims) {
17663
+ result.batches++;
17664
+ const batch = readClaim(claimPath);
17665
+ if (batch.bytes > JOURNAL_WARN_BYTES) {
17666
+ log.warn?.(
17667
+ { path: claimPath, bytes: batch.bytes, entries: batch.entries.length },
17668
+ "mailery: pending-unsubscribe journal is large \u2014 Mongo has been unreachable for a while"
17669
+ );
17670
+ }
17671
+ for (const bad of batch.malformed) {
17672
+ result.malformed++;
17673
+ log.warn?.(
17674
+ { path: claimPath, lineNumber: bad.lineNumber, preview: bad.preview },
17675
+ "mailery: pending-unsubscribe journal line unusable \u2014 skipped"
17676
+ );
17677
+ }
17678
+ const deferred = [];
17679
+ let stop = false;
17680
+ for (let i = 0; i < batch.entries.length; i++) {
17681
+ const entry = batch.entries[i];
17682
+ if (stop || remainingBudget <= 0) {
17683
+ deferred.push(entry);
17684
+ continue;
17685
+ }
17686
+ try {
17687
+ await applyUnsubscribe(ctx.collections, entryToInput(entry));
17688
+ result.applied++;
17689
+ remainingBudget--;
17690
+ } catch (err) {
17691
+ stop = true;
17692
+ deferred.push(entry);
17693
+ const attempts = (entry.attempts ?? 0) + 1;
17694
+ const fields = { err, path: claimPath, attempts, pending: batch.entries.length - i };
17695
+ if (attempts >= NOISY_AFTER_ATTEMPTS) {
17696
+ log.error?.(
17697
+ fields,
17698
+ "mailery: pending unsubscribe still not applied after repeated attempts \u2014 investigate"
17699
+ );
17700
+ } else {
17701
+ log.warn?.(fields, "mailery: pending-unsubscribe replay failed \u2014 deferred to next tick");
17702
+ }
17703
+ }
17704
+ }
17705
+ result.deferred += deferred.length;
17706
+ releaseClaim(journalPath, claimPath, deferred);
17707
+ }
17708
+ return result;
17709
+ }
17305
17710
 
17306
17711
  // src/server/runner/tick.ts
17307
17712
  var STRANDED_SEND_THRESHOLD_MS = 5 * 60 * 1e3;
@@ -17343,6 +17748,9 @@ async function runTick(ctx) {
17343
17748
  await drainOutbox(ctx).catch((err) => {
17344
17749
  console.error("mailery: outbox drain failed", err);
17345
17750
  });
17751
+ await drainPendingUnsubscribes(ctx).catch((err) => {
17752
+ console.error("mailery: pending-unsubscribe drain failed", err);
17753
+ });
17346
17754
  await processScheduledBroadcasts2(ctx).catch((err) => {
17347
17755
  console.error("mailery: broadcast dispatch failed", err);
17348
17756
  });
@@ -17475,6 +17883,10 @@ var Mailer = class _Mailer {
17475
17883
  * MAILER_FROM_NAME / MAILER_FROM_EMAIL
17476
17884
  * MAILER_DEFAULT_PROVIDER — defaults to 'sendgrid' if SENDGRID_API_KEY is set
17477
17885
  * MAILER_SENDGRID_API_KEY / MAILER_SENDGRID_WEBHOOK_KEY
17886
+ * MAILER_SENDGRID_WEBHOOK_TOLERANCE — signed-webhook replay window, seconds
17887
+ * (default 300; '0' disables the check).
17888
+ * Unparseable values fall back to the
17889
+ * default rather than disabling it.
17478
17890
  * MAILER_HOST_USERS_COLLECTION (default 'users')
17479
17891
  * MAILER_HOST_USERS_EMAIL_FIELD (default 'email')
17480
17892
  * MAILER_HOST_USERS_ID_FIELD (default '_id')
@@ -17509,6 +17921,10 @@ var Mailer = class _Mailer {
17509
17921
  providers.sendgrid = new SendGridProvider2({
17510
17922
  apiKey: env.MAILER_SENDGRID_API_KEY,
17511
17923
  webhookVerificationKey: env.MAILER_SENDGRID_WEBHOOK_KEY,
17924
+ // Left undefined when unset/blank so the provider default (300s) applies.
17925
+ // resolveWebhookToleranceSeconds() rejects NaN back to the default, so a
17926
+ // typo here can never silently switch the replay check off.
17927
+ webhookToleranceSeconds: env.MAILER_SENDGRID_WEBHOOK_TOLERANCE ? Number(env.MAILER_SENDGRID_WEBHOOK_TOLERANCE) : void 0,
17512
17928
  sandbox: env.NODE_ENV !== "production"
17513
17929
  });
17514
17930
  }
@@ -17536,8 +17952,15 @@ var Mailer = class _Mailer {
17536
17952
  }
17537
17953
  static async init(input) {
17538
17954
  const config = resolveConfig(input);
17539
- if (!config.providers[config.defaultProvider]) {
17540
- throw new Error(`defaultProvider "${config.defaultProvider}" not in providers map`);
17955
+ if (!resolveProvider(config.providers, config.defaultProvider)) {
17956
+ throw new Error(
17957
+ `defaultProvider "${config.defaultProvider}" is not a registered provider. Registered: ${registeredProviderNames(config.providers).join(", ") || "(none)"}`
17958
+ );
17959
+ }
17960
+ if (config.defaultTransactionalProvider != null && !resolveProvider(config.providers, config.defaultTransactionalProvider)) {
17961
+ throw new Error(
17962
+ `defaultTransactionalProvider "${config.defaultTransactionalProvider}" is not a registered provider. Registered: ${registeredProviderNames(config.providers).join(", ") || "(none)"}`
17963
+ );
17541
17964
  }
17542
17965
  if (config.varsAdapter) {
17543
17966
  const { assertNoReservedVarKeys: assertNoReservedVarKeys2 } = await Promise.resolve().then(() => (init_vars(), vars_exports));
@@ -17679,38 +18102,14 @@ var Mailer = class _Mailer {
17679
18102
  }
17680
18103
  }
17681
18104
  }
18105
+ /**
18106
+ * The writes live in `server/unsubscribe.ts` so the pending-unsubscribe
18107
+ * drain (INVARIANT 8) replays a journaled opt-out through exactly this path
18108
+ * without holding a `Mailer` instance.
18109
+ */
17682
18110
  async unsubscribe(email, opts) {
17683
18111
  const parsed = unsubscribeInputSchema.parse({ email, ...opts });
17684
- const normalized = parsed.email;
17685
- const now = /* @__PURE__ */ new Date();
17686
- await this.collections.suppressions.updateOne(
17687
- { email: normalized, scope: parsed.scope },
17688
- {
17689
- $setOnInsert: {
17690
- email: normalized,
17691
- emailHash: sha256Hex(normalized),
17692
- scope: parsed.scope,
17693
- // mailer_suppressions canonical reason — see plans/02-data-model.md.
17694
- reason: "unsubscribed",
17695
- source: parsed.source,
17696
- notes: parsed.notes ?? null,
17697
- addedAt: now,
17698
- expiresAt: null
17699
- }
17700
- },
17701
- { upsert: true }
17702
- );
17703
- await this.collections.subscriptions.updateOne(
17704
- { emailAtSubscribe: normalized },
17705
- {
17706
- $set: {
17707
- status: "unsubscribed",
17708
- unsubscribedAt: now,
17709
- unsubscribeReason: parsed.reason,
17710
- updatedAt: now
17711
- }
17712
- }
17713
- );
18112
+ await applyUnsubscribe(this.collections, parsed);
17714
18113
  }
17715
18114
  async suppress(email, opts) {
17716
18115
  const parsed = suppressInputSchema.parse({ email, ...opts });
@@ -18315,7 +18714,7 @@ async function createTestMailer(opts = {}) {
18315
18714
  const db = client.db("mailery-test");
18316
18715
  const memoryAdapter = opts.adapter ? null : new MemoryContactAdapter(opts.seedContacts ?? []);
18317
18716
  const adapter = opts.adapter ?? memoryAdapter;
18318
- const provider = new RecordingProvider(await resolveProvider(opts.provider ?? "null"));
18717
+ const provider = new RecordingProvider(await resolveProvider2(opts.provider ?? "null"));
18319
18718
  const mailer = await Mailer.init({
18320
18719
  db,
18321
18720
  adapter,
@@ -18377,7 +18776,7 @@ async function createTestMailer(opts = {}) {
18377
18776
  };
18378
18777
  return harness;
18379
18778
  }
18380
- async function resolveProvider(spec) {
18779
+ async function resolveProvider2(spec) {
18381
18780
  if (typeof spec !== "string") return spec;
18382
18781
  if (spec === "null") return new NullProvider();
18383
18782
  const apiKey = process.env.SENDGRID_API_KEY;