mailery 0.14.0 → 0.16.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.cjs CHANGED
@@ -11,6 +11,9 @@ var mjml2html = require('mjml');
11
11
  var dns = require('dns/promises');
12
12
  var net = require('net');
13
13
  var psl = require('psl');
14
+ require('fast-xml-parser');
15
+ var fs = require('fs');
16
+ var path = require('path');
14
17
 
15
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
19
 
@@ -22,6 +25,8 @@ var mjml2html__default = /*#__PURE__*/_interopDefault(mjml2html);
22
25
  var dns__default = /*#__PURE__*/_interopDefault(dns);
23
26
  var net__default = /*#__PURE__*/_interopDefault(net);
24
27
  var psl__default = /*#__PURE__*/_interopDefault(psl);
28
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
29
+ var path__default = /*#__PURE__*/_interopDefault(path);
25
30
 
26
31
  var __create = Object.create;
27
32
  var __defProp = Object.defineProperty;
@@ -35,11 +40,20 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
35
40
  if (typeof require !== "undefined") return require.apply(this, arguments);
36
41
  throw Error('Dynamic require of "' + x + '" is not supported');
37
42
  });
38
- var __esm = (fn, res) => function __init() {
39
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
43
+ var __esm = (fn, res, err) => function __init() {
44
+ if (err) throw err[0];
45
+ try {
46
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
47
+ } catch (e) {
48
+ throw err = [e], e;
49
+ }
40
50
  };
41
51
  var __commonJS = (cb, mod) => function __require2() {
42
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
52
+ try {
53
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
54
+ } catch (e) {
55
+ throw mod = 0, e;
56
+ }
43
57
  };
44
58
  var __export = (target, all) => {
45
59
  for (var name in all)
@@ -272,6 +286,31 @@ var init_mongo = __esm({
272
286
  }
273
287
  });
274
288
 
289
+ // src/server/providers/webhook-tolerance.ts
290
+ function resolveWebhookToleranceSeconds(option) {
291
+ if (option === false || option === 0) return 0;
292
+ if (option === void 0 || option === null) return DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
293
+ if (typeof option !== "number" || !Number.isFinite(option) || option < 0) {
294
+ return DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
295
+ }
296
+ return option;
297
+ }
298
+ function isWebhookTimestampFresh(rawTimestamp, toleranceSeconds, now = /* @__PURE__ */ new Date()) {
299
+ if (toleranceSeconds === 0) return true;
300
+ if (!rawTimestamp) return false;
301
+ if (!/^\d{1,15}$/.test(rawTimestamp)) return false;
302
+ const timestampMs = Number(rawTimestamp) * 1e3;
303
+ if (!Number.isFinite(timestampMs)) return false;
304
+ const deltaMs = Math.abs(now.getTime() - timestampMs);
305
+ return deltaMs <= toleranceSeconds * 1e3;
306
+ }
307
+ var DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
308
+ var init_webhook_tolerance = __esm({
309
+ "src/server/providers/webhook-tolerance.ts"() {
310
+ DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
311
+ }
312
+ });
313
+
275
314
  // src/server/providers/sendgrid.ts
276
315
  var sendgrid_exports = {};
277
316
  __export(sendgrid_exports, {
@@ -318,6 +357,7 @@ function mapEventType(sgEvent) {
318
357
  var SG_SIG_HEADER, SG_TS_HEADER, SendGridProvider;
319
358
  var init_sendgrid = __esm({
320
359
  "src/server/providers/sendgrid.ts"() {
360
+ init_webhook_tolerance();
321
361
  SG_SIG_HEADER = "x-twilio-email-event-webhook-signature";
322
362
  SG_TS_HEADER = "x-twilio-email-event-webhook-timestamp";
323
363
  SendGridProvider = class {
@@ -325,10 +365,13 @@ var init_sendgrid = __esm({
325
365
  this.opts = opts;
326
366
  sgMail__default.default.setApiKey(opts.apiKey);
327
367
  this.sendRatePerSecond = opts.sendRatePerSecond ?? 10;
368
+ this.webhookToleranceSeconds = resolveWebhookToleranceSeconds(opts.webhookToleranceSeconds);
328
369
  }
329
370
  opts;
330
371
  name = "sendgrid";
331
372
  sendRatePerSecond;
373
+ /** Resolved replay window in seconds; `0` means the check is disabled. */
374
+ webhookToleranceSeconds;
332
375
  async send(args) {
333
376
  const msg = {
334
377
  to: args.to,
@@ -368,10 +411,11 @@ var init_sendgrid = __esm({
368
411
  try {
369
412
  const verifier = crypto2__default.default.createVerify("sha256");
370
413
  verifier.update(payload);
371
- return verifier.verify(this.opts.webhookVerificationKey, sig, "base64");
414
+ if (!verifier.verify(this.opts.webhookVerificationKey, sig, "base64")) return false;
372
415
  } catch {
373
416
  return false;
374
417
  }
418
+ return isWebhookTimestampFresh(ts, this.webhookToleranceSeconds);
375
419
  }
376
420
  parseWebhookEvents(payload) {
377
421
  if (!Array.isArray(payload)) return [];
@@ -810,13 +854,13 @@ function __disposeResources(env) {
810
854
  }
811
855
  return next();
812
856
  }
813
- function __rewriteRelativeImportExtension(path, preserveJsx) {
814
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
815
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
857
+ function __rewriteRelativeImportExtension(path2, preserveJsx) {
858
+ if (typeof path2 === "string" && /^\.\.?\//.test(path2)) {
859
+ return path2.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
816
860
  return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
817
861
  });
818
862
  }
819
- return path;
863
+ return path2;
820
864
  }
821
865
  var extendStatics, __assign, __createBinding, __setModuleDefault, ownKeys, _SuppressedError, tslib_es6_default;
822
866
  var init_tslib_es6 = __esm({
@@ -1646,7 +1690,7 @@ var require_lib = __commonJS({
1646
1690
  "node_modules/new-find-package-json/lib/index.js"(exports) {
1647
1691
  Object.defineProperty(exports, "__esModule", { value: true });
1648
1692
  exports.findSync = exports.find = exports.findAsync = void 0;
1649
- var path = __require("path");
1693
+ var path2 = __require("path");
1650
1694
  var fs_1 = __require("fs");
1651
1695
  var debug = require_src();
1652
1696
  var log = debug("new-find-package-json:main");
@@ -1654,14 +1698,14 @@ var require_lib = __commonJS({
1654
1698
  log(`findSync: called with "${input}" and "${base}" and "${fileName}"`);
1655
1699
  base = !!base ? base : "";
1656
1700
  const useFileName = !!fileName ? fileName : "package.json";
1657
- const useBase = !!base || path.isAbsolute(base) ? base : process.cwd();
1658
- let testPath = path.resolve(useBase, input);
1701
+ const useBase = !!base || path2.isAbsolute(base) ? base : process.cwd();
1702
+ let testPath = path2.resolve(useBase, input);
1659
1703
  let wasRoot = false;
1660
1704
  while (testPath) {
1661
- if (testPath === path.parse(testPath).root) {
1705
+ if (testPath === path2.parse(testPath).root) {
1662
1706
  wasRoot = true;
1663
1707
  }
1664
- const testFile = path.resolve(testPath, useFileName);
1708
+ const testFile = path2.resolve(testPath, useFileName);
1665
1709
  log(`findSync: testing path "${testFile}"`);
1666
1710
  const result = await statPathAsync(testFile);
1667
1711
  if (!!result && result.isFile()) {
@@ -1672,7 +1716,7 @@ var require_lib = __commonJS({
1672
1716
  if (wasRoot) {
1673
1717
  break;
1674
1718
  }
1675
- testPath = path.resolve(testPath, "..");
1719
+ testPath = path2.resolve(testPath, "..");
1676
1720
  }
1677
1721
  }
1678
1722
  exports.findAsync = findAsync;
@@ -1681,14 +1725,14 @@ var require_lib = __commonJS({
1681
1725
  log(`findSync: called with "${input}" and "${base}" and "${fileName}"`);
1682
1726
  base = !!base ? base : "";
1683
1727
  const useFileName = !!fileName ? fileName : "package.json";
1684
- const useBase = !!base || path.isAbsolute(base) ? base : process.cwd();
1685
- let testPath = path.resolve(useBase, input);
1728
+ const useBase = !!base || path2.isAbsolute(base) ? base : process.cwd();
1729
+ let testPath = path2.resolve(useBase, input);
1686
1730
  let wasRoot = false;
1687
1731
  while (testPath) {
1688
- if (testPath === path.parse(testPath).root) {
1732
+ if (testPath === path2.parse(testPath).root) {
1689
1733
  wasRoot = true;
1690
1734
  }
1691
- const testFile = path.resolve(testPath, useFileName);
1735
+ const testFile = path2.resolve(testPath, useFileName);
1692
1736
  log(`findSync: testing path "${testFile}"`);
1693
1737
  const result = statPathSync(testFile);
1694
1738
  if (!!result && result.isFile()) {
@@ -1699,14 +1743,14 @@ var require_lib = __commonJS({
1699
1743
  if (wasRoot) {
1700
1744
  break;
1701
1745
  }
1702
- testPath = path.resolve(testPath, "..");
1746
+ testPath = path2.resolve(testPath, "..");
1703
1747
  }
1704
1748
  }
1705
1749
  exports.findSync = findSync;
1706
- function statPathSync(path2) {
1750
+ function statPathSync(path3) {
1707
1751
  var _a;
1708
1752
  try {
1709
- return (0, fs_1.statSync)(path2);
1753
+ return (0, fs_1.statSync)(path3);
1710
1754
  } catch (err) {
1711
1755
  if (err != void 0 && err != null && ((_a = err) === null || _a === void 0 ? void 0 : _a.code) === "ENOENT") {
1712
1756
  return void 0;
@@ -1714,8 +1758,8 @@ var require_lib = __commonJS({
1714
1758
  throw err;
1715
1759
  }
1716
1760
  }
1717
- async function statPathAsync(path2) {
1718
- return fs_1.promises.stat(path2).catch((err) => {
1761
+ async function statPathAsync(path3) {
1762
+ return fs_1.promises.stat(path3).catch((err) => {
1719
1763
  if (err.code === "ENOENT") {
1720
1764
  return void 0;
1721
1765
  }
@@ -1834,18 +1878,18 @@ https://typegoose.github.io/mongodb-memory-server/docs/guides/migration/migrate7
1834
1878
  exports.AuthNotObjectError = AuthNotObjectError;
1835
1879
  var InsufficientPermissionsError = class extends Error {
1836
1880
  path;
1837
- constructor(path) {
1838
- super(`File "${path}" does not have the required Permissions, required Permissions: "--x"`);
1839
- this.path = path;
1881
+ constructor(path2) {
1882
+ super(`File "${path2}" does not have the required Permissions, required Permissions: "--x"`);
1883
+ this.path = path2;
1840
1884
  }
1841
1885
  };
1842
1886
  exports.InsufficientPermissionsError = InsufficientPermissionsError;
1843
1887
  var BinaryNotFoundError = class extends Error {
1844
1888
  path;
1845
1889
  extra;
1846
- constructor(path, extra = "") {
1847
- super(`No Binary at path "${path}" was found! (ENOENT)${extra}`);
1848
- this.path = path;
1890
+ constructor(path2, extra = "") {
1891
+ super(`No Binary at path "${path2}" was found! (ENOENT)${extra}`);
1892
+ this.path = path2;
1849
1893
  this.extra = extra;
1850
1894
  }
1851
1895
  };
@@ -3920,7 +3964,7 @@ var require_utils = __commonJS({
3920
3964
  var fs_1 = __require("fs");
3921
3965
  var errors_1 = require_errors();
3922
3966
  var os_1 = __require("os");
3923
- var path = tslib_1.__importStar(__require("path"));
3967
+ var path2 = tslib_1.__importStar(__require("path"));
3924
3968
  var crypto_1 = __require("crypto");
3925
3969
  var semver = tslib_1.__importStar(require_semver2());
3926
3970
  var log = (0, debug_1.default)("MongoMS:utils");
@@ -4004,16 +4048,16 @@ var require_utils = __commonJS({
4004
4048
  ...opts
4005
4049
  };
4006
4050
  }
4007
- async function statPath(path2) {
4008
- return fs_1.promises.stat(path2).catch((err) => {
4051
+ async function statPath(path3) {
4052
+ return fs_1.promises.stat(path3).catch((err) => {
4009
4053
  if (["ENOENT", "EACCES"].includes(err.code)) {
4010
4054
  return void 0;
4011
4055
  }
4012
4056
  throw err;
4013
4057
  });
4014
4058
  }
4015
- async function pathExists(path2) {
4016
- return !isNullOrUndefined(await statPath(path2));
4059
+ async function pathExists(path3) {
4060
+ return !isNullOrUndefined(await statPath(path3));
4017
4061
  }
4018
4062
  var ManagerBase = class {
4019
4063
  };
@@ -4021,27 +4065,27 @@ var require_utils = __commonJS({
4021
4065
  var ManagerAdvanced = class extends ManagerBase {
4022
4066
  };
4023
4067
  exports.ManagerAdvanced = ManagerAdvanced;
4024
- async function checkBinaryPermissions(path2) {
4068
+ async function checkBinaryPermissions(path3) {
4025
4069
  try {
4026
- await fs_1.promises.access(path2, fs_1.constants.X_OK);
4070
+ await fs_1.promises.access(path3, fs_1.constants.X_OK);
4027
4071
  } catch (err) {
4028
4072
  if (errorWithCode(err)) {
4029
4073
  if (err.code === "EACCES") {
4030
- throw new errors_1.InsufficientPermissionsError(path2);
4074
+ throw new errors_1.InsufficientPermissionsError(path3);
4031
4075
  }
4032
4076
  if (err.code === "ENOENT") {
4033
- throw new errors_1.BinaryNotFoundError(path2);
4077
+ throw new errors_1.BinaryNotFoundError(path3);
4034
4078
  }
4035
4079
  }
4036
4080
  throw err;
4037
4081
  }
4038
4082
  }
4039
- async function mkdir(path2) {
4040
- await fs_1.promises.mkdir(path2, { recursive: true });
4083
+ async function mkdir(path3) {
4084
+ await fs_1.promises.mkdir(path3, { recursive: true });
4041
4085
  }
4042
4086
  async function createTmpDir(prefix, atPath) {
4043
4087
  const tmpPath = atPath ?? (0, os_1.tmpdir)();
4044
- return fs_1.promises.mkdtemp(path.join(tmpPath, prefix));
4088
+ return fs_1.promises.mkdtemp(path2.join(tmpPath, prefix));
4045
4089
  }
4046
4090
  async function removeDir(dirPath) {
4047
4091
  const stat = await statPath(dirPath);
@@ -4063,7 +4107,7 @@ var require_utils = __commonJS({
4063
4107
  return md5(await fs_1.promises.readFile(file));
4064
4108
  }
4065
4109
  function lockfilePath(downloadDir, version) {
4066
- return path.resolve(downloadDir, `${version}.lock`);
4110
+ return path2.resolve(downloadDir, `${version}.lock`);
4067
4111
  }
4068
4112
  function getStorageEngine(storageEngine, coercedVersion) {
4069
4113
  if (storageEngine === "ephemeralForTest" && semver.gte(coercedVersion, "7.0.0")) {
@@ -4097,7 +4141,7 @@ var require_resolveConfig = __commonJS({
4097
4141
  var camelcase_1 = tslib_1.__importDefault(require_camelcase());
4098
4142
  var new_find_package_json_1 = require_lib();
4099
4143
  var debug_1 = tslib_1.__importDefault(require_src());
4100
- var path = tslib_1.__importStar(__require("path"));
4144
+ var path2 = tslib_1.__importStar(__require("path"));
4101
4145
  var fs_1 = __require("fs");
4102
4146
  var utils_1 = require_utils();
4103
4147
  Symbol.dispose ??= /* @__PURE__ */ Symbol("Symbol.dispose");
@@ -4154,7 +4198,7 @@ var require_resolveConfig = __commonJS({
4154
4198
  const config = readout?.config?.mongodbMemoryServer;
4155
4199
  if (!(0, utils_1.isNullOrUndefined)(config) && Object.keys(config ?? {}).length > 0) {
4156
4200
  log(`findPackageJson: Found package with non-empty config field at "${filename}"`);
4157
- const filepath = path.dirname(filename);
4201
+ const filepath = path2.dirname(filename);
4158
4202
  packagejson = {
4159
4203
  filePath: filepath,
4160
4204
  config: processConfigOption(config, filepath)
@@ -4174,10 +4218,10 @@ var require_resolveConfig = __commonJS({
4174
4218
  const ccDownloadDir = (0, camelcase_1.default)(ResolveConfigVariables.DOWNLOAD_DIR);
4175
4219
  const ccSystemBinary = (0, camelcase_1.default)(ResolveConfigVariables.SYSTEM_BINARY);
4176
4220
  if (ccDownloadDir in returnobj) {
4177
- returnobj[ccDownloadDir] = path.resolve(filepath, returnobj[ccDownloadDir]);
4221
+ returnobj[ccDownloadDir] = path2.resolve(filepath, returnobj[ccDownloadDir]);
4178
4222
  }
4179
4223
  if (ccSystemBinary in returnobj) {
4180
- returnobj[ccSystemBinary] = path.resolve(filepath, returnobj[ccSystemBinary]);
4224
+ returnobj[ccSystemBinary] = path2.resolve(filepath, returnobj[ccSystemBinary]);
4181
4225
  }
4182
4226
  return returnobj;
4183
4227
  }
@@ -4320,7 +4364,7 @@ var require_debug2 = __commonJS({
4320
4364
  var require_follow_redirects = __commonJS({
4321
4365
  "node_modules/follow-redirects/index.js"(exports, module) {
4322
4366
  var url = __require("url");
4323
- var URL = url.URL;
4367
+ var URL2 = url.URL;
4324
4368
  var http = __require("http");
4325
4369
  var https = __require("https");
4326
4370
  var Writable = __require("stream").Writable;
@@ -4336,7 +4380,7 @@ var require_follow_redirects = __commonJS({
4336
4380
  })();
4337
4381
  var useNativeURL = false;
4338
4382
  try {
4339
- assert(new URL(""));
4383
+ assert(new URL2(""));
4340
4384
  } catch (error) {
4341
4385
  useNativeURL = error.code === "ERR_INVALID_URL";
4342
4386
  }
@@ -4725,7 +4769,7 @@ var require_follow_redirects = __commonJS({
4725
4769
  function parseUrl(input) {
4726
4770
  var parsed;
4727
4771
  if (useNativeURL) {
4728
- parsed = new URL(input);
4772
+ parsed = new URL2(input);
4729
4773
  } else {
4730
4774
  parsed = validateUrl(url.parse(input));
4731
4775
  if (!isString(parsed.protocol)) {
@@ -4735,7 +4779,7 @@ var require_follow_redirects = __commonJS({
4735
4779
  return parsed;
4736
4780
  }
4737
4781
  function resolveUrl(relative, base) {
4738
- return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative));
4782
+ return useNativeURL ? new URL2(relative, base) : parseUrl(url.resolve(base, relative));
4739
4783
  }
4740
4784
  function validateUrl(input) {
4741
4785
  if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -4817,7 +4861,7 @@ var require_follow_redirects = __commonJS({
4817
4861
  return typeof value === "object" && "length" in value;
4818
4862
  }
4819
4863
  function isURL(value) {
4820
- return URL && value instanceof URL;
4864
+ return URL2 && value instanceof URL2;
4821
4865
  }
4822
4866
  function escapeRegex(regex) {
4823
4867
  return regex.replace(/[\]\\/()*+?.$]/g, "\\$&");
@@ -7210,7 +7254,7 @@ var require_pend = __commonJS({
7210
7254
  // node_modules/yauzl/fd-slicer.js
7211
7255
  var require_fd_slicer = __commonJS({
7212
7256
  "node_modules/yauzl/fd-slicer.js"(exports) {
7213
- var fs = __require("fs");
7257
+ var fs2 = __require("fs");
7214
7258
  var util = __require("util");
7215
7259
  var stream = __require("stream");
7216
7260
  var Readable = stream.Readable;
@@ -7235,7 +7279,7 @@ var require_fd_slicer = __commonJS({
7235
7279
  FdSlicer.prototype.read = function(buffer, offset, length, position, callback) {
7236
7280
  var self = this;
7237
7281
  self.pend.go(function(cb) {
7238
- fs.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer2) {
7282
+ fs2.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer2) {
7239
7283
  cb();
7240
7284
  callback(err, bytesRead, buffer2);
7241
7285
  });
@@ -7244,7 +7288,7 @@ var require_fd_slicer = __commonJS({
7244
7288
  FdSlicer.prototype.write = function(buffer, offset, length, position, callback) {
7245
7289
  var self = this;
7246
7290
  self.pend.go(function(cb) {
7247
- fs.write(self.fd, buffer, offset, length, position, function(err, written, buffer2) {
7291
+ fs2.write(self.fd, buffer, offset, length, position, function(err, written, buffer2) {
7248
7292
  cb();
7249
7293
  callback(err, written, buffer2);
7250
7294
  });
@@ -7265,7 +7309,7 @@ var require_fd_slicer = __commonJS({
7265
7309
  if (self.refCount > 0) return;
7266
7310
  if (self.refCount < 0) throw new Error("invalid unref");
7267
7311
  if (self.autoClose) {
7268
- fs.close(self.fd, onCloseDone);
7312
+ fs2.close(self.fd, onCloseDone);
7269
7313
  }
7270
7314
  function onCloseDone(err) {
7271
7315
  if (err) {
@@ -7302,7 +7346,7 @@ var require_fd_slicer = __commonJS({
7302
7346
  self.context.pend.go(function(cb) {
7303
7347
  if (self.destroyed) return cb();
7304
7348
  var buffer = Buffer.allocUnsafe(toRead);
7305
- fs.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) {
7349
+ fs2.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) {
7306
7350
  if (err) {
7307
7351
  self.destroy(err);
7308
7352
  } else if (bytesRead === 0) {
@@ -7349,7 +7393,7 @@ var require_fd_slicer = __commonJS({
7349
7393
  }
7350
7394
  self.context.pend.go(function(cb) {
7351
7395
  if (self.destroyed) return cb();
7352
- fs.write(self.context.fd, buffer, 0, buffer.length, self.pos, function(err2, bytes) {
7396
+ fs2.write(self.context.fd, buffer, 0, buffer.length, self.pos, function(err2, bytes) {
7353
7397
  if (err2) {
7354
7398
  self.destroy();
7355
7399
  cb();
@@ -7787,7 +7831,7 @@ var require_buffer_crc32 = __commonJS({
7787
7831
  // node_modules/yauzl/index.js
7788
7832
  var require_yauzl = __commonJS({
7789
7833
  "node_modules/yauzl/index.js"(exports) {
7790
- var fs = __require("fs");
7834
+ var fs2 = __require("fs");
7791
7835
  var zlib = __require("zlib");
7792
7836
  var fd_slicer = require_fd_slicer();
7793
7837
  var crc32 = require_buffer_crc32();
@@ -7808,7 +7852,7 @@ var require_yauzl = __commonJS({
7808
7852
  exports.Entry = Entry;
7809
7853
  exports.LocalFileHeader = LocalFileHeader;
7810
7854
  exports.RandomAccessReader = RandomAccessReader;
7811
- function open(path, options2, callback) {
7855
+ function open(path2, options2, callback) {
7812
7856
  if (typeof options2 === "function") {
7813
7857
  callback = options2;
7814
7858
  options2 = null;
@@ -7820,10 +7864,10 @@ var require_yauzl = __commonJS({
7820
7864
  if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
7821
7865
  if (options2.strictFileNames == null) options2.strictFileNames = false;
7822
7866
  if (callback == null) callback = defaultCallback;
7823
- fs.open(path, "r", function(err, fd) {
7867
+ fs2.open(path2, "r", function(err, fd) {
7824
7868
  if (err) return callback(err);
7825
7869
  fromFd(fd, options2, function(err2, zipfile) {
7826
- if (err2) fs.close(fd, defaultCallback);
7870
+ if (err2) fs2.close(fd, defaultCallback);
7827
7871
  callback(err2, zipfile);
7828
7872
  });
7829
7873
  });
@@ -7840,7 +7884,7 @@ var require_yauzl = __commonJS({
7840
7884
  if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
7841
7885
  if (options2.strictFileNames == null) options2.strictFileNames = false;
7842
7886
  if (callback == null) callback = defaultCallback;
7843
- fs.fstat(fd, function(err, stats) {
7887
+ fs2.fstat(fd, function(err, stats) {
7844
7888
  if (err) return callback(err);
7845
7889
  var reader = fd_slicer.createFromFd(fd, { autoClose: true });
7846
7890
  fromRandomAccessReader(reader, stats.size, options2, callback);
@@ -8623,15 +8667,15 @@ var require_getos = __commonJS({
8623
8667
  id_like: input.match(OSRegex.id_like)?.[1].toLocaleLowerCase().split(" ")
8624
8668
  };
8625
8669
  }
8626
- async function tryReleaseFile(path, parser) {
8670
+ async function tryReleaseFile(path2, parser) {
8627
8671
  try {
8628
- const output = await fs_1.promises.readFile(path);
8672
+ const output = await fs_1.promises.readFile(path2);
8629
8673
  return parser(output.toString());
8630
8674
  } catch (err) {
8631
8675
  if ((0, utils_1.errorWithCode)(err) && !["ENOENT", "EACCES"].includes(err.code)) {
8632
8676
  throw err;
8633
8677
  }
8634
- log(`tryReleaseFile: "${path}" does not exist`);
8678
+ log(`tryReleaseFile: "${path2}" does not exist`);
8635
8679
  return void 0;
8636
8680
  }
8637
8681
  }
@@ -9604,11 +9648,11 @@ var require_dist2 = __commonJS({
9604
9648
  // node_modules/commondir/index.js
9605
9649
  var require_commondir = __commonJS({
9606
9650
  "node_modules/commondir/index.js"(exports, module) {
9607
- var path = __require("path");
9651
+ var path2 = __require("path");
9608
9652
  module.exports = function(basedir, relfiles) {
9609
9653
  if (relfiles) {
9610
9654
  var files = relfiles.map(function(r) {
9611
- return path.resolve(basedir, r);
9655
+ return path2.resolve(basedir, r);
9612
9656
  });
9613
9657
  } else {
9614
9658
  var files = basedir;
@@ -9731,12 +9775,12 @@ var require_p_locate = __commonJS({
9731
9775
  // node_modules/locate-path/index.js
9732
9776
  var require_locate_path = __commonJS({
9733
9777
  "node_modules/locate-path/index.js"(exports, module) {
9734
- var path = __require("path");
9735
- var fs = __require("fs");
9778
+ var path2 = __require("path");
9779
+ var fs2 = __require("fs");
9736
9780
  var { promisify } = __require("util");
9737
9781
  var pLocate = require_p_locate();
9738
- var fsStat = promisify(fs.stat);
9739
- var fsLStat = promisify(fs.lstat);
9782
+ var fsStat = promisify(fs2.stat);
9783
+ var fsLStat = promisify(fs2.lstat);
9740
9784
  var typeMappings = {
9741
9785
  directory: "isDirectory",
9742
9786
  file: "isFile"
@@ -9759,7 +9803,7 @@ var require_locate_path = __commonJS({
9759
9803
  const statFn = options2.allowSymlinks ? fsStat : fsLStat;
9760
9804
  return pLocate(paths, async (path_) => {
9761
9805
  try {
9762
- const stat = await statFn(path.resolve(options2.cwd, path_));
9806
+ const stat = await statFn(path2.resolve(options2.cwd, path_));
9763
9807
  return matchType(options2.type, stat);
9764
9808
  } catch (_) {
9765
9809
  return false;
@@ -9774,10 +9818,10 @@ var require_locate_path = __commonJS({
9774
9818
  ...options2
9775
9819
  };
9776
9820
  checkType(options2);
9777
- const statFn = options2.allowSymlinks ? fs.statSync : fs.lstatSync;
9821
+ const statFn = options2.allowSymlinks ? fs2.statSync : fs2.lstatSync;
9778
9822
  for (const path_ of paths) {
9779
9823
  try {
9780
- const stat = statFn(path.resolve(options2.cwd, path_));
9824
+ const stat = statFn(path2.resolve(options2.cwd, path_));
9781
9825
  if (matchType(options2.type, stat)) {
9782
9826
  return path_;
9783
9827
  }
@@ -9791,20 +9835,20 @@ var require_locate_path = __commonJS({
9791
9835
  // node_modules/path-exists/index.js
9792
9836
  var require_path_exists = __commonJS({
9793
9837
  "node_modules/path-exists/index.js"(exports, module) {
9794
- var fs = __require("fs");
9838
+ var fs2 = __require("fs");
9795
9839
  var { promisify } = __require("util");
9796
- var pAccess = promisify(fs.access);
9797
- module.exports = async (path) => {
9840
+ var pAccess = promisify(fs2.access);
9841
+ module.exports = async (path2) => {
9798
9842
  try {
9799
- await pAccess(path);
9843
+ await pAccess(path2);
9800
9844
  return true;
9801
9845
  } catch (_) {
9802
9846
  return false;
9803
9847
  }
9804
9848
  };
9805
- module.exports.sync = (path) => {
9849
+ module.exports.sync = (path2) => {
9806
9850
  try {
9807
- fs.accessSync(path);
9851
+ fs2.accessSync(path2);
9808
9852
  return true;
9809
9853
  } catch (_) {
9810
9854
  return false;
@@ -9816,13 +9860,13 @@ var require_path_exists = __commonJS({
9816
9860
  // node_modules/find-up/index.js
9817
9861
  var require_find_up = __commonJS({
9818
9862
  "node_modules/find-up/index.js"(exports, module) {
9819
- var path = __require("path");
9863
+ var path2 = __require("path");
9820
9864
  var locatePath = require_locate_path();
9821
9865
  var pathExists = require_path_exists();
9822
9866
  var stop = /* @__PURE__ */ Symbol("findUp.stop");
9823
9867
  module.exports = async (name, options2 = {}) => {
9824
- let directory = path.resolve(options2.cwd || "");
9825
- const { root } = path.parse(directory);
9868
+ let directory = path2.resolve(options2.cwd || "");
9869
+ const { root } = path2.parse(directory);
9826
9870
  const paths = [].concat(name);
9827
9871
  const runMatcher = async (locateOptions) => {
9828
9872
  if (typeof name !== "function") {
@@ -9840,17 +9884,17 @@ var require_find_up = __commonJS({
9840
9884
  return;
9841
9885
  }
9842
9886
  if (foundPath) {
9843
- return path.resolve(directory, foundPath);
9887
+ return path2.resolve(directory, foundPath);
9844
9888
  }
9845
9889
  if (directory === root) {
9846
9890
  return;
9847
9891
  }
9848
- directory = path.dirname(directory);
9892
+ directory = path2.dirname(directory);
9849
9893
  }
9850
9894
  };
9851
9895
  module.exports.sync = (name, options2 = {}) => {
9852
- let directory = path.resolve(options2.cwd || "");
9853
- const { root } = path.parse(directory);
9896
+ let directory = path2.resolve(options2.cwd || "");
9897
+ const { root } = path2.parse(directory);
9854
9898
  const paths = [].concat(name);
9855
9899
  const runMatcher = (locateOptions) => {
9856
9900
  if (typeof name !== "function") {
@@ -9868,12 +9912,12 @@ var require_find_up = __commonJS({
9868
9912
  return;
9869
9913
  }
9870
9914
  if (foundPath) {
9871
- return path.resolve(directory, foundPath);
9915
+ return path2.resolve(directory, foundPath);
9872
9916
  }
9873
9917
  if (directory === root) {
9874
9918
  return;
9875
9919
  }
9876
- directory = path.dirname(directory);
9920
+ directory = path2.dirname(directory);
9877
9921
  }
9878
9922
  };
9879
9923
  module.exports.exists = pathExists;
@@ -9885,17 +9929,17 @@ var require_find_up = __commonJS({
9885
9929
  // node_modules/pkg-dir/index.js
9886
9930
  var require_pkg_dir = __commonJS({
9887
9931
  "node_modules/pkg-dir/index.js"(exports, module) {
9888
- var path = __require("path");
9932
+ var path2 = __require("path");
9889
9933
  var findUp = require_find_up();
9890
9934
  var pkgDir = async (cwd) => {
9891
9935
  const filePath = await findUp("package.json", { cwd });
9892
- return filePath && path.dirname(filePath);
9936
+ return filePath && path2.dirname(filePath);
9893
9937
  };
9894
9938
  module.exports = pkgDir;
9895
9939
  module.exports.default = pkgDir;
9896
9940
  module.exports.sync = (cwd) => {
9897
9941
  const filePath = findUp.sync("package.json", { cwd });
9898
- return filePath && path.dirname(filePath);
9942
+ return filePath && path2.dirname(filePath);
9899
9943
  };
9900
9944
  }
9901
9945
  });
@@ -11059,14 +11103,14 @@ var require_semver3 = __commonJS({
11059
11103
  // node_modules/make-dir/index.js
11060
11104
  var require_make_dir = __commonJS({
11061
11105
  "node_modules/make-dir/index.js"(exports, module) {
11062
- var fs = __require("fs");
11063
- var path = __require("path");
11106
+ var fs2 = __require("fs");
11107
+ var path2 = __require("path");
11064
11108
  var { promisify } = __require("util");
11065
11109
  var semver = require_semver3();
11066
11110
  var useNativeRecursiveOption = semver.satisfies(process.version, ">=10.12.0");
11067
11111
  var checkPath = (pth) => {
11068
11112
  if (process.platform === "win32") {
11069
- const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, ""));
11113
+ const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path2.parse(pth).root, ""));
11070
11114
  if (pathHasInvalidWinCharacters) {
11071
11115
  const error = new Error(`Path contains invalid characters: ${pth}`);
11072
11116
  error.code = "EINVAL";
@@ -11077,7 +11121,7 @@ var require_make_dir = __commonJS({
11077
11121
  var processOptions = (options2) => {
11078
11122
  const defaults = {
11079
11123
  mode: 511,
11080
- fs
11124
+ fs: fs2
11081
11125
  };
11082
11126
  return {
11083
11127
  ...defaults,
@@ -11097,8 +11141,8 @@ var require_make_dir = __commonJS({
11097
11141
  options2 = processOptions(options2);
11098
11142
  const mkdir = promisify(options2.fs.mkdir);
11099
11143
  const stat = promisify(options2.fs.stat);
11100
- if (useNativeRecursiveOption && options2.fs.mkdir === fs.mkdir) {
11101
- const pth = path.resolve(input);
11144
+ if (useNativeRecursiveOption && options2.fs.mkdir === fs2.mkdir) {
11145
+ const pth = path2.resolve(input);
11102
11146
  await mkdir(pth, {
11103
11147
  mode: options2.mode,
11104
11148
  recursive: true
@@ -11114,13 +11158,13 @@ var require_make_dir = __commonJS({
11114
11158
  throw error;
11115
11159
  }
11116
11160
  if (error.code === "ENOENT") {
11117
- if (path.dirname(pth) === pth) {
11161
+ if (path2.dirname(pth) === pth) {
11118
11162
  throw permissionError(pth);
11119
11163
  }
11120
11164
  if (error.message.includes("null bytes")) {
11121
11165
  throw error;
11122
11166
  }
11123
- await make(path.dirname(pth));
11167
+ await make(path2.dirname(pth));
11124
11168
  return make(pth);
11125
11169
  }
11126
11170
  try {
@@ -11134,15 +11178,15 @@ var require_make_dir = __commonJS({
11134
11178
  return pth;
11135
11179
  }
11136
11180
  };
11137
- return make(path.resolve(input));
11181
+ return make(path2.resolve(input));
11138
11182
  };
11139
11183
  module.exports = makeDir;
11140
11184
  module.exports.sync = (input, options2) => {
11141
11185
  checkPath(input);
11142
11186
  options2 = processOptions(options2);
11143
- if (useNativeRecursiveOption && options2.fs.mkdirSync === fs.mkdirSync) {
11144
- const pth = path.resolve(input);
11145
- fs.mkdirSync(pth, {
11187
+ if (useNativeRecursiveOption && options2.fs.mkdirSync === fs2.mkdirSync) {
11188
+ const pth = path2.resolve(input);
11189
+ fs2.mkdirSync(pth, {
11146
11190
  mode: options2.mode,
11147
11191
  recursive: true
11148
11192
  });
@@ -11156,13 +11200,13 @@ var require_make_dir = __commonJS({
11156
11200
  throw error;
11157
11201
  }
11158
11202
  if (error.code === "ENOENT") {
11159
- if (path.dirname(pth) === pth) {
11203
+ if (path2.dirname(pth) === pth) {
11160
11204
  throw permissionError(pth);
11161
11205
  }
11162
11206
  if (error.message.includes("null bytes")) {
11163
11207
  throw error;
11164
11208
  }
11165
- make(path.dirname(pth));
11209
+ make(path2.dirname(pth));
11166
11210
  return make(pth);
11167
11211
  }
11168
11212
  try {
@@ -11175,7 +11219,7 @@ var require_make_dir = __commonJS({
11175
11219
  }
11176
11220
  return pth;
11177
11221
  };
11178
- return make(path.resolve(input));
11222
+ return make(path2.resolve(input));
11179
11223
  };
11180
11224
  }
11181
11225
  });
@@ -11183,15 +11227,15 @@ var require_make_dir = __commonJS({
11183
11227
  // node_modules/find-cache-dir/index.js
11184
11228
  var require_find_cache_dir = __commonJS({
11185
11229
  "node_modules/find-cache-dir/index.js"(exports, module) {
11186
- var path = __require("path");
11187
- var fs = __require("fs");
11230
+ var path2 = __require("path");
11231
+ var fs2 = __require("fs");
11188
11232
  var commonDir = require_commondir();
11189
11233
  var pkgDir = require_pkg_dir();
11190
11234
  var makeDir = require_make_dir();
11191
11235
  var { env, cwd } = process;
11192
- var isWritable = (path2) => {
11236
+ var isWritable = (path3) => {
11193
11237
  try {
11194
- fs.accessSync(path2, fs.constants.W_OK);
11238
+ fs2.accessSync(path3, fs2.constants.W_OK);
11195
11239
  return true;
11196
11240
  } catch (_) {
11197
11241
  return false;
@@ -11202,20 +11246,20 @@ var require_find_cache_dir = __commonJS({
11202
11246
  makeDir.sync(directory);
11203
11247
  }
11204
11248
  if (options2.thunk) {
11205
- return (...arguments_) => path.join(directory, ...arguments_);
11249
+ return (...arguments_) => path2.join(directory, ...arguments_);
11206
11250
  }
11207
11251
  return directory;
11208
11252
  }
11209
11253
  function getNodeModuleDirectory(directory) {
11210
- const nodeModules = path.join(directory, "node_modules");
11211
- if (!isWritable(nodeModules) && (fs.existsSync(nodeModules) || !isWritable(path.join(directory)))) {
11254
+ const nodeModules = path2.join(directory, "node_modules");
11255
+ if (!isWritable(nodeModules) && (fs2.existsSync(nodeModules) || !isWritable(path2.join(directory)))) {
11212
11256
  return;
11213
11257
  }
11214
11258
  return nodeModules;
11215
11259
  }
11216
11260
  module.exports = (options2 = {}) => {
11217
11261
  if (env.CACHE_DIR && !["true", "false", "1", "0"].includes(env.CACHE_DIR)) {
11218
- return useDirectory(path.join(env.CACHE_DIR, options2.name), options2);
11262
+ return useDirectory(path2.join(env.CACHE_DIR, options2.name), options2);
11219
11263
  }
11220
11264
  let { cwd: directory = cwd() } = options2;
11221
11265
  if (options2.files) {
@@ -11229,7 +11273,7 @@ var require_find_cache_dir = __commonJS({
11229
11273
  if (!nodeModules) {
11230
11274
  return void 0;
11231
11275
  }
11232
- return useDirectory(path.join(directory, "node_modules", ".cache", options2.name), options2);
11276
+ return useDirectory(path2.join(directory, "node_modules", ".cache", options2.name), options2);
11233
11277
  };
11234
11278
  }
11235
11279
  });
@@ -11243,7 +11287,7 @@ var require_DryMongoBinary = __commonJS({
11243
11287
  var debug_1 = tslib_1.__importDefault(require_src());
11244
11288
  var resolveConfig_1 = require_resolveConfig();
11245
11289
  var utils_1 = require_utils();
11246
- var path = tslib_1.__importStar(__require("path"));
11290
+ var path2 = tslib_1.__importStar(__require("path"));
11247
11291
  var os_1 = __require("os");
11248
11292
  var find_cache_dir_1 = tslib_1.__importDefault(require_find_cache_dir());
11249
11293
  var getos_1 = require_getos();
@@ -11264,7 +11308,7 @@ var require_DryMongoBinary = __commonJS({
11264
11308
  const useOpts = await this.generateOptions(opts);
11265
11309
  if (useOpts.systemBinary) {
11266
11310
  log(`locateBinary: env "SYSTEM_BINARY" was provided with value: "${useOpts.systemBinary}"`);
11267
- const systemReturn = await this.getSystemPath(path.resolve(useOpts.systemBinary));
11311
+ const systemReturn = await this.getSystemPath(path2.resolve(useOpts.systemBinary));
11268
11312
  if ((0, utils_1.isNullOrUndefined)(systemReturn)) {
11269
11313
  throw new errors_1.BinaryNotFoundError(useOpts.systemBinary, " (systemBinary)");
11270
11314
  }
@@ -11281,7 +11325,7 @@ var require_DryMongoBinary = __commonJS({
11281
11325
  log("locateBinary: could not find a existing binary");
11282
11326
  return void 0;
11283
11327
  }
11284
- if (returnValue[0] && await (0, utils_1.pathExists)((0, utils_1.lockfilePath)(path.dirname(returnValue[1]), useOpts.version))) {
11328
+ if (returnValue[0] && await (0, utils_1.pathExists)((0, utils_1.lockfilePath)(path2.dirname(returnValue[1]), useOpts.version))) {
11285
11329
  log("locateBinary: binary found, but also a download-lock, trying to resolve lock");
11286
11330
  return void 0;
11287
11331
  }
@@ -11312,7 +11356,7 @@ var require_DryMongoBinary = __commonJS({
11312
11356
  arch: ensuredOpts.arch || (0, os_1.arch)(),
11313
11357
  systemBinary: (0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.SYSTEM_BINARY) || ensuredOpts.systemBinary || ""
11314
11358
  };
11315
- final.downloadDir = path.dirname((await this.generateDownloadPath(final))[1]);
11359
+ final.downloadDir = path2.dirname((await this.generateDownloadPath(final))[1]);
11316
11360
  if ((0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.ARCHIVE_NAME) || (0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.DOWNLOAD_URL)) {
11317
11361
  const input = (0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.DOWNLOAD_URL) || (0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.ARCHIVE_NAME);
11318
11362
  log(`generateOptions: ARCHIVE_NAME or DOWNLOAD_URL defined, generating options based on that (input: "${input}")`);
@@ -11359,7 +11403,7 @@ var require_DryMongoBinary = __commonJS({
11359
11403
  let binaryName;
11360
11404
  if ((0, resolveConfig_1.envToBool)((0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.USE_ARCHIVE_NAME_FOR_BINARY_NAME))) {
11361
11405
  const archiveName = await new MongoBinaryDownloadUrl_1.MongoBinaryDownloadUrl(opts).getArchiveName();
11362
- binaryName = path.parse(archiveName).name;
11406
+ binaryName = path2.parse(archiveName).name;
11363
11407
  } else {
11364
11408
  const addExe = opts.platform === "win32" ? ".exe" : "";
11365
11409
  const dist = (0, getos_1.isLinuxOS)(opts.os) ? opts.os.dist : opts.os.os;
@@ -11372,7 +11416,7 @@ var require_DryMongoBinary = __commonJS({
11372
11416
  */
11373
11417
  static combineBinaryName(basePath, binaryName) {
11374
11418
  log("combineBinaryName");
11375
- return path.resolve(basePath, binaryName);
11419
+ return path2.resolve(basePath, binaryName);
11376
11420
  }
11377
11421
  /**
11378
11422
  * Probe if the provided "systemBinary" is an existing path
@@ -11407,11 +11451,11 @@ ${err instanceof Error ? err.message : err}`);
11407
11451
  };
11408
11452
  const binaryName = await this.getBinaryName(opts);
11409
11453
  let nodeModulesDLDir = process.env["INIT_CWD"] || process.cwd();
11410
- while (nodeModulesDLDir.includes(`node_modules${path.sep}mongodb-memory-server`)) {
11411
- nodeModulesDLDir = path.resolve(nodeModulesDLDir, "..", "..");
11454
+ while (nodeModulesDLDir.includes(`node_modules${path2.sep}mongodb-memory-server`)) {
11455
+ nodeModulesDLDir = path2.resolve(nodeModulesDLDir, "..", "..");
11412
11456
  }
11413
11457
  const configPackagePath = (0, resolveConfig_1.packageJsonPath)();
11414
- if (configPackagePath && await (0, utils_1.pathExists)(path.resolve(configPackagePath, "node_modules"))) {
11458
+ if (configPackagePath && await (0, utils_1.pathExists)(path2.resolve(configPackagePath, "node_modules"))) {
11415
11459
  nodeModulesDLDir = configPackagePath;
11416
11460
  }
11417
11461
  const tmpModulesCache = (0, find_cache_dir_1.default)({
@@ -11419,16 +11463,16 @@ ${err instanceof Error ? err.message : err}`);
11419
11463
  cwd: nodeModulesDLDir
11420
11464
  });
11421
11465
  if (!(0, utils_1.isNullOrUndefined)(tmpModulesCache)) {
11422
- final.modulesCache = this.combineBinaryName(path.resolve(tmpModulesCache), binaryName);
11466
+ final.modulesCache = this.combineBinaryName(path2.resolve(tmpModulesCache), binaryName);
11423
11467
  }
11424
- const homeCache = path.resolve(this.homedir(), ".cache/mongodb-binaries");
11468
+ const homeCache = path2.resolve(this.homedir(), ".cache/mongodb-binaries");
11425
11469
  final.homeCache = this.combineBinaryName(homeCache, binaryName);
11426
11470
  const resolveConfigValue = opts.downloadDir || (0, resolveConfig_1.resolveConfig)(resolveConfig_1.ResolveConfigVariables.DOWNLOAD_DIR);
11427
11471
  if (!(0, utils_1.isNullOrUndefined)(resolveConfigValue) && resolveConfigValue.length > 0) {
11428
11472
  log(`generatePaths: resolveConfigValue is not empty`);
11429
11473
  final.resolveConfig = this.combineBinaryName(resolveConfigValue, binaryName);
11430
11474
  }
11431
- final.relative = this.combineBinaryName(path.resolve(process.cwd(), "mongodb-binaries"), binaryName);
11475
+ final.relative = this.combineBinaryName(path2.resolve(process.cwd(), "mongodb-binaries"), binaryName);
11432
11476
  return final;
11433
11477
  }
11434
11478
  /**
@@ -12391,7 +12435,7 @@ var require_lockfile = __commonJS({
12391
12435
  var events_1 = __require("events");
12392
12436
  var utils = tslib_1.__importStar(require_utils());
12393
12437
  var debug_1 = tslib_1.__importDefault(require_src());
12394
- var path = tslib_1.__importStar(__require("path"));
12438
+ var path2 = tslib_1.__importStar(__require("path"));
12395
12439
  var fs_1 = __require("fs");
12396
12440
  var async_mutex_1 = require_lib2();
12397
12441
  var errors_1 = require_errors();
@@ -12431,7 +12475,7 @@ var require_lockfile = __commonJS({
12431
12475
  static async lock(file) {
12432
12476
  await utils.ensureAsync();
12433
12477
  log(`lock: Locking file "${file}"`);
12434
- const useFile = path.resolve(file.trim());
12478
+ const useFile = path2.resolve(file.trim());
12435
12479
  utils.assertion(useFile.length > 0, new Error("Provided Path for lock file is length of 0"));
12436
12480
  const status = await this.checkLock(useFile);
12437
12481
  switch (status) {
@@ -12532,7 +12576,7 @@ var require_lockfile = __commonJS({
12532
12576
  log(`createLock: Map already has file "${file}"`);
12533
12577
  throw new RepeatError(true);
12534
12578
  }
12535
- await utils.mkdir(path.dirname(file));
12579
+ await utils.mkdir(path2.dirname(file));
12536
12580
  await fs_1.promises.writeFile(file, `${process.pid.toString()} ${uuid}`);
12537
12581
  this.files.add(file);
12538
12582
  this.events.emit(LockFileEvents.lock, file);
@@ -12718,7 +12762,7 @@ var require_MongoInstance = __commonJS({
12718
12762
  exports.MongoInstance = exports.MongoInstanceEvents = void 0;
12719
12763
  var tslib_1 = (init_tslib_es6(), __toCommonJS(tslib_es6_exports));
12720
12764
  var child_process_1 = __require("child_process");
12721
- var path = tslib_1.__importStar(__require("path"));
12765
+ var path2 = tslib_1.__importStar(__require("path"));
12722
12766
  var MongoBinary_1 = require_MongoBinary();
12723
12767
  var debug_1 = tslib_1.__importDefault(require_src());
12724
12768
  var utils_1 = require_utils();
@@ -12955,7 +12999,7 @@ var require_MongoInstance = __commonJS({
12955
12999
  */
12956
13000
  _launchMongod(mongoBin) {
12957
13001
  this.debug("_launchMongod: Launching Mongod Process");
12958
- const childProcess = (0, child_process_1.spawn)(path.resolve(mongoBin), this.prepareCommandArgs(), {
13002
+ const childProcess = (0, child_process_1.spawn)(path2.resolve(mongoBin), this.prepareCommandArgs(), {
12959
13003
  ...this.spawnOpts,
12960
13004
  stdio: "pipe"
12961
13005
  // ensure that stdio is always an pipe, regardless of user input
@@ -12965,7 +13009,7 @@ var require_MongoInstance = __commonJS({
12965
13009
  childProcess.on("close", this.closeHandler.bind(this));
12966
13010
  childProcess.on("error", this.errorHandler.bind(this));
12967
13011
  if ((0, utils_1.isNullOrUndefined)(childProcess.pid)) {
12968
- throw new errors_1.StartBinaryFailedError(path.resolve(mongoBin));
13012
+ throw new errors_1.StartBinaryFailedError(path2.resolve(mongoBin));
12969
13013
  }
12970
13014
  childProcess.unref();
12971
13015
  this.emit(MongoInstanceEvents.instanceLaunched);
@@ -12979,7 +13023,7 @@ var require_MongoInstance = __commonJS({
12979
13023
  */
12980
13024
  _launchKiller(parentPid, childPid) {
12981
13025
  this.debug(`_launchKiller: Launching Killer Process (parent: ${parentPid}, child: ${childPid})`);
12982
- const killer = (0, child_process_1.fork)(path.resolve(__dirname, "../../scripts/mongo_killer.js"), [parentPid.toString(), childPid.toString()], {
13026
+ const killer = (0, child_process_1.fork)(path2.resolve(__dirname, "../../scripts/mongo_killer.js"), [parentPid.toString(), childPid.toString()], {
12983
13027
  execArgv: [],
12984
13028
  detached: true,
12985
13029
  stdio: "ignore"
@@ -14150,8 +14194,20 @@ var NullProvider = class {
14150
14194
  status: "accepted"
14151
14195
  };
14152
14196
  }
14197
+ /**
14198
+ * Fails closed. This provider has no signing key, so it can never establish
14199
+ * that an inbound payload is authentic — and a method whose entire job is to
14200
+ * reject unauthenticated input must not answer "yes" by default. Returning
14201
+ * `true` here would mean any unsigned body posted to the webhook route is
14202
+ * accepted as genuine whenever the null provider is registered, which is
14203
+ * exactly the dev/staging configuration where that is easiest to miss.
14204
+ *
14205
+ * Nothing is lost: `parseWebhookEvents()` returns `[]`, so there was never
14206
+ * any inbound behaviour to preserve. To exercise the inbound half locally,
14207
+ * use a provider that actually signs (see `test/README.md`).
14208
+ */
14153
14209
  async verifyWebhook() {
14154
- return true;
14210
+ return false;
14155
14211
  }
14156
14212
  parseWebhookEvents() {
14157
14213
  return [];
@@ -14281,6 +14337,10 @@ var flowStepSchema = zod.z.lazy(
14281
14337
  }),
14282
14338
  zod.z.object({
14283
14339
  type: zod.z.literal("webhook"),
14340
+ // `url` is validated for shape only and is deliberately never templated —
14341
+ // see the comment on handleWebhookStep in src/server/runner/step.ts, and
14342
+ // INVARIANT 16. Rendering contact/event data into this field would turn an
14343
+ // admin-authored flow into a server-side request forgery primitive.
14284
14344
  url: zod.z.string().url(),
14285
14345
  method: zod.z.enum(["POST", "PUT"]).optional(),
14286
14346
  payload: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
@@ -14374,6 +14434,7 @@ var DEFAULTS = {
14374
14434
  requireDoubleOptIn: false,
14375
14435
  unsubscribeTokenLifetimeDays: 90,
14376
14436
  transactionalRespectUnsubscribe: false,
14437
+ unsubscribeWriteTimeoutMs: 5e3,
14377
14438
  doiTemplateSlug: "doi-confirmation",
14378
14439
  doiTokenLifetimeDays: 7,
14379
14440
  broadcastConfirmationThreshold: 1e3,
@@ -14390,7 +14451,9 @@ var DEFAULTS = {
14390
14451
  trackOpens: true,
14391
14452
  trackClicks: true,
14392
14453
  storeTrackingIp: false,
14393
- storeRenderedBody: false
14454
+ storeRenderedBody: false,
14455
+ requireSignedTrackingUrls: false,
14456
+ trackingUrlLifetimeDays: 0
14394
14457
  };
14395
14458
  var CIRCUIT_BREAKER_DEFAULTS = {
14396
14459
  hardBounceRatePctTrip: 2,
@@ -14407,6 +14470,7 @@ function resolveConfig(c) {
14407
14470
  requireDoubleOptIn: c.requireDoubleOptIn ?? DEFAULTS.requireDoubleOptIn,
14408
14471
  unsubscribeTokenLifetimeDays: c.unsubscribeTokenLifetimeDays ?? DEFAULTS.unsubscribeTokenLifetimeDays,
14409
14472
  transactionalRespectUnsubscribe: c.transactionalRespectUnsubscribe ?? DEFAULTS.transactionalRespectUnsubscribe,
14473
+ unsubscribeWriteTimeoutMs: c.unsubscribeWriteTimeoutMs ?? DEFAULTS.unsubscribeWriteTimeoutMs,
14410
14474
  doiTemplateSlug: c.doiTemplateSlug ?? DEFAULTS.doiTemplateSlug,
14411
14475
  doiTokenLifetimeDays: c.doiTokenLifetimeDays ?? DEFAULTS.doiTokenLifetimeDays,
14412
14476
  broadcastConfirmationThreshold: c.broadcastConfirmationThreshold ?? DEFAULTS.broadcastConfirmationThreshold,
@@ -14424,6 +14488,8 @@ function resolveConfig(c) {
14424
14488
  trackClicks: c.trackClicks ?? DEFAULTS.trackClicks,
14425
14489
  storeTrackingIp: c.storeTrackingIp ?? DEFAULTS.storeTrackingIp,
14426
14490
  storeRenderedBody: c.storeRenderedBody ?? DEFAULTS.storeRenderedBody,
14491
+ requireSignedTrackingUrls: c.requireSignedTrackingUrls ?? DEFAULTS.requireSignedTrackingUrls,
14492
+ trackingUrlLifetimeDays: c.trackingUrlLifetimeDays ?? DEFAULTS.trackingUrlLifetimeDays,
14427
14493
  circuitBreaker: { ...CIRCUIT_BREAKER_DEFAULTS, ...c.circuitBreaker ?? {} }
14428
14494
  };
14429
14495
  }
@@ -14626,6 +14692,22 @@ function isoDay(d) {
14626
14692
  function randomId() {
14627
14693
  return crypto2__default.default.randomBytes(8).toString("hex");
14628
14694
  }
14695
+
14696
+ // src/server/provider-lookup.ts
14697
+ var PROVIDER_METHODS = ["send", "verifyWebhook", "parseWebhookEvents"];
14698
+ function isMailProvider(candidate) {
14699
+ if (!candidate || typeof candidate !== "object") return false;
14700
+ const p = candidate;
14701
+ return PROVIDER_METHODS.every((m) => typeof p[m] === "function");
14702
+ }
14703
+ function resolveProvider(providers, name) {
14704
+ if (typeof name !== "string" || !Object.hasOwn(providers, name)) return null;
14705
+ const candidate = providers[name];
14706
+ return isMailProvider(candidate) ? candidate : null;
14707
+ }
14708
+ function registeredProviderNames(providers) {
14709
+ return Object.keys(providers).filter((name) => resolveProvider(providers, name) !== null).sort();
14710
+ }
14629
14711
  function signUnsubscribeToken(payload, secret) {
14630
14712
  const body = JSON.stringify({
14631
14713
  e: payload.email.toLowerCase(),
@@ -14636,6 +14718,19 @@ function signUnsubscribeToken(payload, secret) {
14636
14718
  const hmac = crypto2__default.default.createHmac("sha256", secret).update(bodyB64).digest();
14637
14719
  return `${bodyB64}.${b64url(hmac)}`;
14638
14720
  }
14721
+ var TRACKING_SCOPES = ["open", "click"];
14722
+ var TRACKING_SIG_LENGTH = 12;
14723
+ function trackingMessage(scope, params) {
14724
+ const tail = params.linkId === void 0 ? "" : `:${params.linkId}`;
14725
+ return `mailery.track.v1:${scope}:${params.sendId}${tail}`;
14726
+ }
14727
+ function signTrackingToken(scope, params, secret) {
14728
+ if (!TRACKING_SCOPES.includes(scope)) {
14729
+ throw new Error(`signTrackingToken: unknown scope ${String(scope)}`);
14730
+ }
14731
+ const mac = crypto2__default.default.createHmac("sha256", secret).update(trackingMessage(scope, params)).digest();
14732
+ return b64url(mac).slice(0, TRACKING_SIG_LENGTH);
14733
+ }
14639
14734
  function b64url(buf) {
14640
14735
  return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
14641
14736
  }
@@ -14652,6 +14747,39 @@ function signDoiToken(payload, secret) {
14652
14747
  const hmac = crypto2__default.default.createHmac("sha256", secret).update(bodyB64).digest();
14653
14748
  return `${bodyB64}.${b64url(hmac)}`;
14654
14749
  }
14750
+
14751
+ // src/server/unsubscribe.ts
14752
+ async function applyUnsubscribe(collections, input, now = /* @__PURE__ */ new Date()) {
14753
+ const normalized = input.email;
14754
+ await collections.suppressions.updateOne(
14755
+ { email: normalized, scope: input.scope },
14756
+ {
14757
+ $setOnInsert: {
14758
+ email: normalized,
14759
+ emailHash: sha256Hex(normalized),
14760
+ scope: input.scope,
14761
+ // mailer_suppressions canonical reason — see plans/02-data-model.md.
14762
+ reason: "unsubscribed",
14763
+ source: input.source,
14764
+ notes: input.notes ?? null,
14765
+ addedAt: now,
14766
+ expiresAt: null
14767
+ }
14768
+ },
14769
+ { upsert: true }
14770
+ );
14771
+ await collections.subscriptions.updateOne(
14772
+ { emailAtSubscribe: normalized },
14773
+ {
14774
+ $set: {
14775
+ status: "unsubscribed",
14776
+ unsubscribedAt: now,
14777
+ unsubscribeReason: input.reason,
14778
+ updatedAt: now
14779
+ }
14780
+ }
14781
+ );
14782
+ }
14655
14783
  var QUEUE_NAMES = {
14656
14784
  tick: "mailer-tick",
14657
14785
  advance: "mailer-advance",
@@ -15446,7 +15574,7 @@ async function suppressOnce(ctx, email, reason, scope) {
15446
15574
  }
15447
15575
 
15448
15576
  // src/server/runner/predicate.ts
15449
- var BOT_UA_RE = /Mimecast|SafeLinks|proofpoint|HeadlessChrome|Googlebot|bingbot/i;
15577
+ var DEFAULT_BOT_UA_RE = /Mimecast|SafeLinks|proofpoint|HeadlessChrome|Googlebot|bingbot/i;
15450
15578
  async function evaluatePredicate(predicate, ctx) {
15451
15579
  const p = predicate;
15452
15580
  if ("hasTag" in p) return ctx.contact.tags.includes(p.hasTag);
@@ -15514,17 +15642,35 @@ async function openOrClickCount(ctx, kind, opts, excludeBots) {
15514
15642
  return await ctx.collections.sends.countDocuments(filter);
15515
15643
  }
15516
15644
  const docs = await ctx.collections.sends.find(filter).limit(1e3).toArray();
15645
+ const botRe = ctx.botFilter?.userAgentPattern ?? DEFAULT_BOT_UA_RE;
15646
+ const minOpenDelayMs = ctx.botFilter?.minOpenDelayMs ?? 0;
15517
15647
  let n = 0;
15518
15648
  for (const s of docs) {
15519
- if (kind === "opened") {
15520
- n++;
15521
- continue;
15522
- }
15523
- const hasHumanClick = s.clickedLinks?.some((c) => !c.userAgent || !BOT_UA_RE.test(c.userAgent));
15524
- if (hasHumanClick) n++;
15649
+ const doc = s;
15650
+ const human = kind === "opened" ? hasHumanOpen(doc, botRe, minOpenDelayMs) : (doc.clickedLinks ?? []).some((c) => !isBotUserAgent(c.userAgent, botRe));
15651
+ if (human) n++;
15525
15652
  }
15526
15653
  return n;
15527
15654
  }
15655
+ function isBotUserAgent(ua, botRe) {
15656
+ if (typeof ua !== "string" || ua.trim() === "") return false;
15657
+ return botRe.test(ua);
15658
+ }
15659
+ function hasHumanOpen(send, botRe, minOpenDelayMs) {
15660
+ const opens = send.opens && send.opens.length > 0 ? send.opens : send.openedAt ? [{ openedAt: send.openedAt, userAgent: null }] : [];
15661
+ return opens.some((o) => {
15662
+ if (isBotUserAgent(o.userAgent, botRe)) return false;
15663
+ if (isPrefetchOpen(o.openedAt, send.queuedAt, minOpenDelayMs)) return false;
15664
+ return true;
15665
+ });
15666
+ }
15667
+ function isPrefetchOpen(openedAt, queuedAt, minOpenDelayMs) {
15668
+ if (!minOpenDelayMs || minOpenDelayMs <= 0) return false;
15669
+ if (!(openedAt instanceof Date) || !(queuedAt instanceof Date)) return false;
15670
+ const delta = openedAt.getTime() - queuedAt.getTime();
15671
+ if (!Number.isFinite(delta) || delta < 0) return false;
15672
+ return delta < minOpenDelayMs;
15673
+ }
15528
15674
  function effectiveLowerBound(ctx, opts) {
15529
15675
  const now = ctx.now ?? /* @__PURE__ */ new Date();
15530
15676
  if (opts.sinceFlowStart) return ctx.run.enteredAt;
@@ -15684,12 +15830,14 @@ function applyTracking(html, opts) {
15684
15830
  seen.set(url, linkId);
15685
15831
  links.push({ linkId, url });
15686
15832
  }
15687
- const newHref = `${opts.publicUrl}/m/click/${opts.sendId}/${linkId}`;
15833
+ const sig = opts.signingSecret ? `/${signTrackingToken("click", { sendId: opts.sendId, linkId }, opts.signingSecret)}` : "";
15834
+ const newHref = `${opts.publicUrl}/m/click/${opts.sendId}/${linkId}${sig}`;
15688
15835
  return `<a${pre}href="${newHref}"${post}>`;
15689
15836
  });
15690
15837
  }
15691
15838
  if (opts.trackOpens) {
15692
- const pixel = `<img src="${opts.publicUrl}/m/open/${opts.sendId}.png" width="1" height="1" alt="" style="display:block;border:0" />`;
15839
+ const sig = opts.signingSecret ? `.${signTrackingToken("open", { sendId: opts.sendId }, opts.signingSecret)}` : "";
15840
+ const pixel = `<img src="${opts.publicUrl}/m/open/${opts.sendId}${sig}.png" width="1" height="1" alt="" style="display:block;border:0" />`;
15693
15841
  if (/<\/body>/i.test(out)) {
15694
15842
  out = out.replace(/<\/body>/i, `${pixel}</body>`);
15695
15843
  } else {
@@ -15700,14 +15848,22 @@ function applyTracking(html, opts) {
15700
15848
  }
15701
15849
  function shouldSkipClickRewrite(url, preserve, fullTag) {
15702
15850
  if (!url) return true;
15703
- if (url.startsWith("mailto:")) return true;
15704
- if (url.startsWith("tel:")) return true;
15705
15851
  if (url.startsWith("#")) return true;
15706
15852
  if (url.startsWith("{{") || url.startsWith("{")) return true;
15707
15853
  if (preserve.has(url.trim())) return true;
15708
15854
  if (/data-mailer-notrack=["']true["']/.test(fullTag)) return true;
15855
+ if (!isHttpUrl(url)) return true;
15709
15856
  return false;
15710
15857
  }
15858
+ function isHttpUrl(url) {
15859
+ let parsed;
15860
+ try {
15861
+ parsed = new URL(url.trim());
15862
+ } catch {
15863
+ return false;
15864
+ }
15865
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
15866
+ }
15711
15867
  var NAMED_ENTITIES = {
15712
15868
  amp: "&",
15713
15869
  lt: "<",
@@ -15928,7 +16084,8 @@ async function dispatchSend(sendId, ctx) {
15928
16084
  publicUrl: ctx.config.publicUrl,
15929
16085
  trackOpens: template.trackOpens ?? ctx.config.trackOpens,
15930
16086
  trackClicks: template.trackClicks ?? ctx.config.trackClicks,
15931
- preserveUrls: [renderCtx.unsubscribeUrl]
16087
+ preserveUrls: [renderCtx.unsubscribeUrl],
16088
+ signingSecret: ctx.config.unsubscribeSecret
15932
16089
  });
15933
16090
  await ctx.collections.sends.updateOne(
15934
16091
  { _id: send._id },
@@ -15946,9 +16103,14 @@ async function dispatchSend(sendId, ctx) {
15946
16103
  }
15947
16104
  }
15948
16105
  );
15949
- const provider = ctx.providers[send.provider] ?? ctx.providers[ctx.config.defaultProvider];
16106
+ const provider = resolveProvider(ctx.providers, send.provider);
15950
16107
  if (!provider) {
15951
- await markFailed(send._id, `provider_unknown: ${send.provider}`, ctx);
16108
+ const known = registeredProviderNames(ctx.providers);
16109
+ await markFailed(
16110
+ send._id,
16111
+ `provider_unknown: no provider is registered as "${send.provider}". Registered providers: ${known.length ? known.join(", ") : "(none)"}.`,
16112
+ ctx
16113
+ );
15952
16114
  return;
15953
16115
  }
15954
16116
  const headers = send.kind === "marketing" ? {
@@ -16147,7 +16309,7 @@ async function handleWait(run, step2, ctx) {
16147
16309
  }
16148
16310
  async function handleCondition(run, step2, contact, ctx) {
16149
16311
  if (!contact) return;
16150
- const result = await evaluatePredicate(step2.test, { contact, run, collections: ctx.collections });
16312
+ const result = await evaluatePredicate(step2.test, { contact, run, collections: ctx.collections, botFilter: ctx.config.botFilter });
16151
16313
  if (result) {
16152
16314
  await advanceStep(run, ctx, { action: "condition_evaluated", details: { result: true } });
16153
16315
  return;
@@ -16160,7 +16322,7 @@ async function handleCondition(run, step2, contact, ctx) {
16160
16322
  }
16161
16323
  async function handleBranch(run, step2, contact, ctx) {
16162
16324
  if (!contact) return;
16163
- const result = await evaluatePredicate(step2.test, { contact, run, collections: ctx.collections });
16325
+ const result = await evaluatePredicate(step2.test, { contact, run, collections: ctx.collections, botFilter: ctx.config.botFilter });
16164
16326
  const newPath = [...run.currentBranchPath, run.currentStepIndex, result ? "true" : "false", 0];
16165
16327
  const updated = await ctx.collections.flowRuns.findOneAndUpdate(
16166
16328
  { _id: run._id, currentStepIndex: run.currentStepIndex },
@@ -16931,14 +17093,14 @@ function createPostmasterClient(cfg, fetcher = globalThis.fetch) {
16931
17093
  };
16932
17094
  return cached.accessToken;
16933
17095
  }
16934
- async function authedGet(path) {
17096
+ async function authedGet(path2) {
16935
17097
  const token = await getAccessToken();
16936
- const res = await fetchWithTimeout(fetcher, `${POSTMASTER_BASE}${path}`, {
17098
+ const res = await fetchWithTimeout(fetcher, `${POSTMASTER_BASE}${path2}`, {
16937
17099
  method: "GET",
16938
17100
  headers: { authorization: `Bearer ${token}` }
16939
17101
  });
16940
17102
  if (!res.ok) {
16941
- throw new Error(`Postmaster GET ${path} failed: ${res.status} ${await safeText(res)}`);
17103
+ throw new Error(`Postmaster GET ${path2} failed: ${res.status} ${await safeText(res)}`);
16942
17104
  }
16943
17105
  return await res.json();
16944
17106
  }
@@ -17316,6 +17478,250 @@ async function pruneDmarcFailures(ctx, opts = {}) {
17316
17478
  const r = await ctx.collections.dmarcFailures.deleteMany({ receivedAt: { $lt: cutoff } });
17317
17479
  return r.deletedCount ?? 0;
17318
17480
  }
17481
+ var DRAINING_INFIX = ".draining.";
17482
+ var ADOPT_AFTER_MS = 5 * 60 * 1e3;
17483
+ var JOURNAL_WARN_BYTES = 8 * 1024 * 1024;
17484
+ var O_NOFOLLOW = fs__default.default.constants.O_NOFOLLOW ?? 0;
17485
+ var APPEND_FLAGS = fs__default.default.constants.O_WRONLY | fs__default.default.constants.O_CREAT | fs__default.default.constants.O_APPEND | O_NOFOLLOW;
17486
+ function assertPrivateRegularFile(fd, journalPath) {
17487
+ const st = fs__default.default.fstatSync(fd);
17488
+ if (!st.isFile()) {
17489
+ throw new Error(`pending-unsubscribe journal is not a regular file: ${journalPath}`);
17490
+ }
17491
+ if (st.nlink > 1) {
17492
+ throw new Error(`pending-unsubscribe journal has ${st.nlink} hard links: ${journalPath}`);
17493
+ }
17494
+ if ((st.mode & 63) !== 0) fs__default.default.fchmodSync(fd, 384);
17495
+ }
17496
+ function serializeEntry(entry) {
17497
+ return JSON.stringify(entry) + "\n";
17498
+ }
17499
+ function claimToken() {
17500
+ return `${Date.now().toString(36)}-${crypto2.randomBytes(6).toString("hex")}`;
17501
+ }
17502
+ function claimJournal(journalPath) {
17503
+ const claimed = `${journalPath}${DRAINING_INFIX}${claimToken()}`;
17504
+ try {
17505
+ fs__default.default.renameSync(journalPath, claimed);
17506
+ } catch (err) {
17507
+ if (isMissing(err)) return null;
17508
+ throw err;
17509
+ }
17510
+ markClaimActive(claimed);
17511
+ return claimed;
17512
+ }
17513
+ function markClaimActive(claimPath) {
17514
+ const now = /* @__PURE__ */ new Date();
17515
+ try {
17516
+ fs__default.default.utimesSync(claimPath, now, now);
17517
+ } catch (err) {
17518
+ if (!isMissing(err)) throw err;
17519
+ }
17520
+ }
17521
+ function adoptAbandonedClaims(journalPath, opts = {}) {
17522
+ const olderThanMs = opts.olderThanMs ?? ADOPT_AFTER_MS;
17523
+ const now = opts.now ?? Date.now();
17524
+ const dir = path__default.default.dirname(journalPath);
17525
+ const prefix = path__default.default.basename(journalPath) + DRAINING_INFIX;
17526
+ let names;
17527
+ try {
17528
+ names = fs__default.default.readdirSync(dir);
17529
+ } catch (err) {
17530
+ if (isMissing(err)) return [];
17531
+ throw err;
17532
+ }
17533
+ const adopted = [];
17534
+ for (const name of names) {
17535
+ if (!name.startsWith(prefix)) continue;
17536
+ const full = path__default.default.join(dir, name);
17537
+ let st;
17538
+ try {
17539
+ st = fs__default.default.statSync(full);
17540
+ } catch (err) {
17541
+ if (isMissing(err)) continue;
17542
+ throw err;
17543
+ }
17544
+ if (!st.isFile()) continue;
17545
+ if (now - st.mtimeMs < olderThanMs) continue;
17546
+ const mine = `${journalPath}${DRAINING_INFIX}${claimToken()}`;
17547
+ try {
17548
+ fs__default.default.renameSync(full, mine);
17549
+ markClaimActive(mine);
17550
+ adopted.push(mine);
17551
+ } catch (err) {
17552
+ if (!isMissing(err)) throw err;
17553
+ }
17554
+ }
17555
+ return adopted;
17556
+ }
17557
+ function readClaim(claimPath) {
17558
+ let raw;
17559
+ try {
17560
+ raw = fs__default.default.readFileSync(claimPath, "utf8");
17561
+ } catch (err) {
17562
+ if (isMissing(err)) return { entries: [], malformed: [], bytes: 0 };
17563
+ throw err;
17564
+ }
17565
+ const entries = [];
17566
+ const malformed = [];
17567
+ const lines = raw.split("\n");
17568
+ for (let i = 0; i < lines.length; i++) {
17569
+ const line = lines[i];
17570
+ if (line.trim() === "") continue;
17571
+ const parsed = parseEntry(line);
17572
+ if (parsed) entries.push(parsed);
17573
+ else malformed.push({ lineNumber: i + 1, preview: line.slice(0, 120) });
17574
+ }
17575
+ return { entries, malformed, bytes: Buffer.byteLength(raw) };
17576
+ }
17577
+ function parseEntry(line) {
17578
+ let obj;
17579
+ try {
17580
+ obj = JSON.parse(line);
17581
+ } catch {
17582
+ return null;
17583
+ }
17584
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) return null;
17585
+ const o = obj;
17586
+ const check = unsubscribeInputSchema.safeParse({
17587
+ email: o.email,
17588
+ scope: o.scope,
17589
+ reason: "user_request",
17590
+ source: "one-click"
17591
+ });
17592
+ if (!check.success) return null;
17593
+ const at = typeof o.at === "number" && Number.isFinite(o.at) ? o.at : Date.now();
17594
+ const attempts = typeof o.attempts === "number" && Number.isFinite(o.attempts) && o.attempts > 0 ? Math.floor(o.attempts) : void 0;
17595
+ return {
17596
+ email: check.data.email,
17597
+ scope: check.data.scope,
17598
+ at,
17599
+ ...attempts === void 0 ? {} : { attempts }
17600
+ };
17601
+ }
17602
+ function entryToInput(entry) {
17603
+ return unsubscribeInputSchema.parse({
17604
+ email: entry.email,
17605
+ scope: entry.scope,
17606
+ reason: "user_request",
17607
+ // Distinguishable in `mailer_suppressions.source` from a live one-click,
17608
+ // but only on rows the drain actually created (the upsert is
17609
+ // `$setOnInsert`, so a row the live path already wrote keeps its own).
17610
+ source: "one-click-drain"
17611
+ });
17612
+ }
17613
+ function releaseClaim(journalPath, claimPath, remaining) {
17614
+ if (remaining.length > 0) {
17615
+ fs__default.default.mkdirSync(path__default.default.dirname(journalPath), { recursive: true, mode: 448 });
17616
+ const fd = fs__default.default.openSync(journalPath, APPEND_FLAGS, 384);
17617
+ try {
17618
+ assertPrivateRegularFile(fd, journalPath);
17619
+ fs__default.default.writeSync(
17620
+ fd,
17621
+ remaining.map((e) => serializeEntry({ ...e, attempts: (e.attempts ?? 0) + 1 })).join("")
17622
+ );
17623
+ } finally {
17624
+ fs__default.default.closeSync(fd);
17625
+ }
17626
+ }
17627
+ try {
17628
+ fs__default.default.unlinkSync(claimPath);
17629
+ } catch (err) {
17630
+ if (!isMissing(err)) throw err;
17631
+ }
17632
+ }
17633
+ function journalExists(journalPath) {
17634
+ try {
17635
+ return fs__default.default.statSync(journalPath).isFile();
17636
+ } catch (err) {
17637
+ if (isMissing(err)) return false;
17638
+ throw err;
17639
+ }
17640
+ }
17641
+ function isMissing(err) {
17642
+ const code = err?.code;
17643
+ return code === "ENOENT" || code === "ENOTDIR";
17644
+ }
17645
+
17646
+ // src/server/runner/pending-unsubs.ts
17647
+ var MAX_ENTRIES_PER_PASS = 1e3;
17648
+ var NOISY_AFTER_ATTEMPTS = 10;
17649
+ var consoleLog = {
17650
+ warn: (fields, msg) => console.warn(msg ?? "mailery", fields),
17651
+ error: (fields, msg) => console.error(msg ?? "mailery", fields)
17652
+ };
17653
+ async function drainPendingUnsubscribes(ctx, opts = {}) {
17654
+ const journalPath = opts.path ?? ctx.config.pendingUnsubsPath;
17655
+ const result = {
17656
+ enabled: Boolean(journalPath),
17657
+ applied: 0,
17658
+ deferred: 0,
17659
+ malformed: 0,
17660
+ batches: 0
17661
+ };
17662
+ if (!journalPath) return result;
17663
+ const log = opts.log ?? consoleLog;
17664
+ const budget = opts.maxEntries ?? MAX_ENTRIES_PER_PASS;
17665
+ const claims = [];
17666
+ try {
17667
+ claims.push(...adoptAbandonedClaims(journalPath));
17668
+ } catch (err) {
17669
+ log.error?.({ err, path: journalPath }, "mailery: pending-unsub claim adoption failed");
17670
+ }
17671
+ if (journalExists(journalPath)) {
17672
+ const claimed = claimJournal(journalPath);
17673
+ if (claimed) claims.push(claimed);
17674
+ }
17675
+ if (claims.length === 0) return result;
17676
+ let remainingBudget = budget;
17677
+ for (const claimPath of claims) {
17678
+ result.batches++;
17679
+ const batch = readClaim(claimPath);
17680
+ if (batch.bytes > JOURNAL_WARN_BYTES) {
17681
+ log.warn?.(
17682
+ { path: claimPath, bytes: batch.bytes, entries: batch.entries.length },
17683
+ "mailery: pending-unsubscribe journal is large \u2014 Mongo has been unreachable for a while"
17684
+ );
17685
+ }
17686
+ for (const bad of batch.malformed) {
17687
+ result.malformed++;
17688
+ log.warn?.(
17689
+ { path: claimPath, lineNumber: bad.lineNumber, preview: bad.preview },
17690
+ "mailery: pending-unsubscribe journal line unusable \u2014 skipped"
17691
+ );
17692
+ }
17693
+ const deferred = [];
17694
+ let stop = false;
17695
+ for (let i = 0; i < batch.entries.length; i++) {
17696
+ const entry = batch.entries[i];
17697
+ if (stop || remainingBudget <= 0) {
17698
+ deferred.push(entry);
17699
+ continue;
17700
+ }
17701
+ try {
17702
+ await applyUnsubscribe(ctx.collections, entryToInput(entry));
17703
+ result.applied++;
17704
+ remainingBudget--;
17705
+ } catch (err) {
17706
+ stop = true;
17707
+ deferred.push(entry);
17708
+ const attempts = (entry.attempts ?? 0) + 1;
17709
+ const fields = { err, path: claimPath, attempts, pending: batch.entries.length - i };
17710
+ if (attempts >= NOISY_AFTER_ATTEMPTS) {
17711
+ log.error?.(
17712
+ fields,
17713
+ "mailery: pending unsubscribe still not applied after repeated attempts \u2014 investigate"
17714
+ );
17715
+ } else {
17716
+ log.warn?.(fields, "mailery: pending-unsubscribe replay failed \u2014 deferred to next tick");
17717
+ }
17718
+ }
17719
+ }
17720
+ result.deferred += deferred.length;
17721
+ releaseClaim(journalPath, claimPath, deferred);
17722
+ }
17723
+ return result;
17724
+ }
17319
17725
 
17320
17726
  // src/server/runner/tick.ts
17321
17727
  var STRANDED_SEND_THRESHOLD_MS = 5 * 60 * 1e3;
@@ -17357,6 +17763,9 @@ async function runTick(ctx) {
17357
17763
  await drainOutbox(ctx).catch((err) => {
17358
17764
  console.error("mailery: outbox drain failed", err);
17359
17765
  });
17766
+ await drainPendingUnsubscribes(ctx).catch((err) => {
17767
+ console.error("mailery: pending-unsubscribe drain failed", err);
17768
+ });
17360
17769
  await processScheduledBroadcasts2(ctx).catch((err) => {
17361
17770
  console.error("mailery: broadcast dispatch failed", err);
17362
17771
  });
@@ -17489,6 +17898,10 @@ var Mailer = class _Mailer {
17489
17898
  * MAILER_FROM_NAME / MAILER_FROM_EMAIL
17490
17899
  * MAILER_DEFAULT_PROVIDER — defaults to 'sendgrid' if SENDGRID_API_KEY is set
17491
17900
  * MAILER_SENDGRID_API_KEY / MAILER_SENDGRID_WEBHOOK_KEY
17901
+ * MAILER_SENDGRID_WEBHOOK_TOLERANCE — signed-webhook replay window, seconds
17902
+ * (default 300; '0' disables the check).
17903
+ * Unparseable values fall back to the
17904
+ * default rather than disabling it.
17492
17905
  * MAILER_HOST_USERS_COLLECTION (default 'users')
17493
17906
  * MAILER_HOST_USERS_EMAIL_FIELD (default 'email')
17494
17907
  * MAILER_HOST_USERS_ID_FIELD (default '_id')
@@ -17523,6 +17936,10 @@ var Mailer = class _Mailer {
17523
17936
  providers.sendgrid = new SendGridProvider2({
17524
17937
  apiKey: env.MAILER_SENDGRID_API_KEY,
17525
17938
  webhookVerificationKey: env.MAILER_SENDGRID_WEBHOOK_KEY,
17939
+ // Left undefined when unset/blank so the provider default (300s) applies.
17940
+ // resolveWebhookToleranceSeconds() rejects NaN back to the default, so a
17941
+ // typo here can never silently switch the replay check off.
17942
+ webhookToleranceSeconds: env.MAILER_SENDGRID_WEBHOOK_TOLERANCE ? Number(env.MAILER_SENDGRID_WEBHOOK_TOLERANCE) : void 0,
17526
17943
  sandbox: env.NODE_ENV !== "production"
17527
17944
  });
17528
17945
  }
@@ -17550,8 +17967,15 @@ var Mailer = class _Mailer {
17550
17967
  }
17551
17968
  static async init(input) {
17552
17969
  const config = resolveConfig(input);
17553
- if (!config.providers[config.defaultProvider]) {
17554
- throw new Error(`defaultProvider "${config.defaultProvider}" not in providers map`);
17970
+ if (!resolveProvider(config.providers, config.defaultProvider)) {
17971
+ throw new Error(
17972
+ `defaultProvider "${config.defaultProvider}" is not a registered provider. Registered: ${registeredProviderNames(config.providers).join(", ") || "(none)"}`
17973
+ );
17974
+ }
17975
+ if (config.defaultTransactionalProvider != null && !resolveProvider(config.providers, config.defaultTransactionalProvider)) {
17976
+ throw new Error(
17977
+ `defaultTransactionalProvider "${config.defaultTransactionalProvider}" is not a registered provider. Registered: ${registeredProviderNames(config.providers).join(", ") || "(none)"}`
17978
+ );
17555
17979
  }
17556
17980
  if (config.varsAdapter) {
17557
17981
  const { assertNoReservedVarKeys: assertNoReservedVarKeys2 } = await Promise.resolve().then(() => (init_vars(), vars_exports));
@@ -17693,38 +18117,14 @@ var Mailer = class _Mailer {
17693
18117
  }
17694
18118
  }
17695
18119
  }
18120
+ /**
18121
+ * The writes live in `server/unsubscribe.ts` so the pending-unsubscribe
18122
+ * drain (INVARIANT 8) replays a journaled opt-out through exactly this path
18123
+ * without holding a `Mailer` instance.
18124
+ */
17696
18125
  async unsubscribe(email, opts) {
17697
18126
  const parsed = unsubscribeInputSchema.parse({ email, ...opts });
17698
- const normalized = parsed.email;
17699
- const now = /* @__PURE__ */ new Date();
17700
- await this.collections.suppressions.updateOne(
17701
- { email: normalized, scope: parsed.scope },
17702
- {
17703
- $setOnInsert: {
17704
- email: normalized,
17705
- emailHash: sha256Hex(normalized),
17706
- scope: parsed.scope,
17707
- // mailer_suppressions canonical reason — see plans/02-data-model.md.
17708
- reason: "unsubscribed",
17709
- source: parsed.source,
17710
- notes: parsed.notes ?? null,
17711
- addedAt: now,
17712
- expiresAt: null
17713
- }
17714
- },
17715
- { upsert: true }
17716
- );
17717
- await this.collections.subscriptions.updateOne(
17718
- { emailAtSubscribe: normalized },
17719
- {
17720
- $set: {
17721
- status: "unsubscribed",
17722
- unsubscribedAt: now,
17723
- unsubscribeReason: parsed.reason,
17724
- updatedAt: now
17725
- }
17726
- }
17727
- );
18127
+ await applyUnsubscribe(this.collections, parsed);
17728
18128
  }
17729
18129
  async suppress(email, opts) {
17730
18130
  const parsed = suppressInputSchema.parse({ email, ...opts });
@@ -18329,7 +18729,7 @@ async function createTestMailer(opts = {}) {
18329
18729
  const db = client.db("mailery-test");
18330
18730
  const memoryAdapter = opts.adapter ? null : new MemoryContactAdapter(opts.seedContacts ?? []);
18331
18731
  const adapter = opts.adapter ?? memoryAdapter;
18332
- const provider = new RecordingProvider(await resolveProvider(opts.provider ?? "null"));
18732
+ const provider = new RecordingProvider(await resolveProvider2(opts.provider ?? "null"));
18333
18733
  const mailer = await Mailer.init({
18334
18734
  db,
18335
18735
  adapter,
@@ -18391,7 +18791,7 @@ async function createTestMailer(opts = {}) {
18391
18791
  };
18392
18792
  return harness;
18393
18793
  }
18394
- async function resolveProvider(spec) {
18794
+ async function resolveProvider2(spec) {
18395
18795
  if (typeof spec !== "string") return spec;
18396
18796
  if (spec === "null") return new NullProvider();
18397
18797
  const apiKey = process.env.SENDGRID_API_KEY;