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/README.md +1 -0
- package/dist/admin/spa/index-B88z7X-U.css +1 -0
- package/dist/admin/spa/index-m4wK04gU.js +9 -0
- package/dist/admin/spa/index-m4wK04gU.js.map +1 -0
- package/dist/admin/spa/index.html +2 -2
- package/dist/admin/spa/template-editor-CKQLM0Me.js +156 -0
- package/dist/admin/spa/{template-editor-pGcCFnJ7.js.map → template-editor-CKQLM0Me.js.map} +1 -1
- package/dist/cli.cjs +5 -10
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +2315 -153
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +669 -20
- package/dist/index.d.ts +669 -20
- package/dist/index.js +2310 -168
- package/dist/index.js.map +1 -1
- package/dist/{null-DDg3ojHe.d.cts → null-CDlseQxO.d.cts} +136 -6
- package/dist/{null-DDg3ojHe.d.ts → null-CDlseQxO.d.ts} +136 -6
- package/dist/testing.cjs +590 -190
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +589 -191
- package/dist/testing.js.map +1 -1
- package/package.json +15 -10
- package/dist/admin/spa/index-B6riKdpB.css +0 -1
- package/dist/admin/spa/index-l7m1PyMb.js +0 -41
- package/dist/admin/spa/index-l7m1PyMb.js.map +0 -1
- package/dist/admin/spa/template-editor-pGcCFnJ7.js +0 -502
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
814
|
-
if (typeof
|
|
815
|
-
return
|
|
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
|
|
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
|
|
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 ||
|
|
1658
|
-
let testPath =
|
|
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 ===
|
|
1705
|
+
if (testPath === path2.parse(testPath).root) {
|
|
1662
1706
|
wasRoot = true;
|
|
1663
1707
|
}
|
|
1664
|
-
const testFile =
|
|
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 =
|
|
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 ||
|
|
1685
|
-
let testPath =
|
|
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 ===
|
|
1732
|
+
if (testPath === path2.parse(testPath).root) {
|
|
1689
1733
|
wasRoot = true;
|
|
1690
1734
|
}
|
|
1691
|
-
const testFile =
|
|
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 =
|
|
1746
|
+
testPath = path2.resolve(testPath, "..");
|
|
1703
1747
|
}
|
|
1704
1748
|
}
|
|
1705
1749
|
exports.findSync = findSync;
|
|
1706
|
-
function statPathSync(
|
|
1750
|
+
function statPathSync(path3) {
|
|
1707
1751
|
var _a;
|
|
1708
1752
|
try {
|
|
1709
|
-
return (0, fs_1.statSync)(
|
|
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(
|
|
1718
|
-
return fs_1.promises.stat(
|
|
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(
|
|
1838
|
-
super(`File "${
|
|
1839
|
-
this.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(
|
|
1847
|
-
super(`No Binary at path "${
|
|
1848
|
-
this.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
|
|
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(
|
|
4008
|
-
return fs_1.promises.stat(
|
|
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(
|
|
4016
|
-
return !isNullOrUndefined(await statPath(
|
|
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(
|
|
4068
|
+
async function checkBinaryPermissions(path3) {
|
|
4025
4069
|
try {
|
|
4026
|
-
await fs_1.promises.access(
|
|
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(
|
|
4074
|
+
throw new errors_1.InsufficientPermissionsError(path3);
|
|
4031
4075
|
}
|
|
4032
4076
|
if (err.code === "ENOENT") {
|
|
4033
|
-
throw new errors_1.BinaryNotFoundError(
|
|
4077
|
+
throw new errors_1.BinaryNotFoundError(path3);
|
|
4034
4078
|
}
|
|
4035
4079
|
}
|
|
4036
4080
|
throw err;
|
|
4037
4081
|
}
|
|
4038
4082
|
}
|
|
4039
|
-
async function mkdir(
|
|
4040
|
-
await fs_1.promises.mkdir(
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
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] =
|
|
4221
|
+
returnobj[ccDownloadDir] = path2.resolve(filepath, returnobj[ccDownloadDir]);
|
|
4178
4222
|
}
|
|
4179
4223
|
if (ccSystemBinary in returnobj) {
|
|
4180
|
-
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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(
|
|
8670
|
+
async function tryReleaseFile(path2, parser) {
|
|
8627
8671
|
try {
|
|
8628
|
-
const output = await fs_1.promises.readFile(
|
|
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: "${
|
|
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
|
|
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
|
|
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
|
|
9735
|
-
var
|
|
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(
|
|
9739
|
-
var fsLStat = promisify(
|
|
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(
|
|
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 ?
|
|
9821
|
+
const statFn = options2.allowSymlinks ? fs2.statSync : fs2.lstatSync;
|
|
9778
9822
|
for (const path_ of paths) {
|
|
9779
9823
|
try {
|
|
9780
|
-
const stat = statFn(
|
|
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
|
|
9838
|
+
var fs2 = __require("fs");
|
|
9795
9839
|
var { promisify } = __require("util");
|
|
9796
|
-
var pAccess = promisify(
|
|
9797
|
-
module.exports = async (
|
|
9840
|
+
var pAccess = promisify(fs2.access);
|
|
9841
|
+
module.exports = async (path2) => {
|
|
9798
9842
|
try {
|
|
9799
|
-
await pAccess(
|
|
9843
|
+
await pAccess(path2);
|
|
9800
9844
|
return true;
|
|
9801
9845
|
} catch (_) {
|
|
9802
9846
|
return false;
|
|
9803
9847
|
}
|
|
9804
9848
|
};
|
|
9805
|
-
module.exports.sync = (
|
|
9849
|
+
module.exports.sync = (path2) => {
|
|
9806
9850
|
try {
|
|
9807
|
-
|
|
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
|
|
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 =
|
|
9825
|
-
const { root } =
|
|
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
|
|
9887
|
+
return path2.resolve(directory, foundPath);
|
|
9844
9888
|
}
|
|
9845
9889
|
if (directory === root) {
|
|
9846
9890
|
return;
|
|
9847
9891
|
}
|
|
9848
|
-
directory =
|
|
9892
|
+
directory = path2.dirname(directory);
|
|
9849
9893
|
}
|
|
9850
9894
|
};
|
|
9851
9895
|
module.exports.sync = (name, options2 = {}) => {
|
|
9852
|
-
let directory =
|
|
9853
|
-
const { root } =
|
|
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
|
|
9915
|
+
return path2.resolve(directory, foundPath);
|
|
9872
9916
|
}
|
|
9873
9917
|
if (directory === root) {
|
|
9874
9918
|
return;
|
|
9875
9919
|
}
|
|
9876
|
-
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
|
|
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 &&
|
|
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 &&
|
|
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
|
|
11063
|
-
var
|
|
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(
|
|
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 ===
|
|
11101
|
-
const pth =
|
|
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 (
|
|
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(
|
|
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(
|
|
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 ===
|
|
11144
|
-
const pth =
|
|
11145
|
-
|
|
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 (
|
|
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(
|
|
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(
|
|
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
|
|
11187
|
-
var
|
|
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 = (
|
|
11236
|
+
var isWritable = (path3) => {
|
|
11193
11237
|
try {
|
|
11194
|
-
|
|
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_) =>
|
|
11249
|
+
return (...arguments_) => path2.join(directory, ...arguments_);
|
|
11206
11250
|
}
|
|
11207
11251
|
return directory;
|
|
11208
11252
|
}
|
|
11209
11253
|
function getNodeModuleDirectory(directory) {
|
|
11210
|
-
const nodeModules =
|
|
11211
|
-
if (!isWritable(nodeModules) && (
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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)(
|
|
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 =
|
|
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 =
|
|
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
|
|
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${
|
|
11411
|
-
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)(
|
|
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(
|
|
11466
|
+
final.modulesCache = this.combineBinaryName(path2.resolve(tmpModulesCache), binaryName);
|
|
11423
11467
|
}
|
|
11424
|
-
const homeCache =
|
|
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(
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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)(
|
|
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(
|
|
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)(
|
|
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
|
|
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
|
|
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
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
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
|
|
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
|
|
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
|
|
16106
|
+
const provider = resolveProvider(ctx.providers, send.provider);
|
|
15950
16107
|
if (!provider) {
|
|
15951
|
-
|
|
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(
|
|
17096
|
+
async function authedGet(path2) {
|
|
16935
17097
|
const token = await getAccessToken();
|
|
16936
|
-
const res = await fetchWithTimeout(fetcher, `${POSTMASTER_BASE}${
|
|
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 ${
|
|
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
|
|
17554
|
-
throw new Error(
|
|
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
|
-
|
|
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
|
|
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
|
|
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;
|