emailengine-app 2.61.1 → 2.61.3
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/CHANGELOG.md +17 -0
- package/data/google-crawlers.json +1 -1
- package/lib/account/account-state.js +248 -0
- package/lib/account.js +45 -193
- package/lib/api-routes/account-routes.js +1023 -0
- package/lib/api-routes/message-routes.js +1377 -0
- package/lib/consts.js +12 -2
- package/lib/email-client/base-client.js +282 -771
- package/lib/email-client/gmail/gmail-api.js +243 -0
- package/lib/email-client/gmail-client.js +145 -53
- package/lib/email-client/imap/mailbox.js +24 -698
- package/lib/email-client/imap/sync-operations.js +812 -0
- package/lib/email-client/imap-client.js +1 -1
- package/lib/email-client/message-builder.js +566 -0
- package/lib/email-client/notification-handler.js +314 -0
- package/lib/email-client/outlook/graph-api.js +326 -0
- package/lib/email-client/outlook-client.js +159 -113
- package/lib/email-client/smtp-pool-manager.js +196 -0
- package/lib/imapproxy/imap-server.js +3 -12
- package/lib/oauth/gmail.js +4 -4
- package/lib/oauth/mail-ru.js +30 -5
- package/lib/oauth/outlook.js +57 -3
- package/lib/oauth/pubsub/google.js +30 -11
- package/lib/oauth/scope-checker.js +202 -0
- package/lib/oauth2-apps.js +8 -4
- package/lib/redis-operations.js +484 -0
- package/lib/routes-ui.js +283 -2582
- package/lib/tools.js +4 -196
- package/lib/ui-routes/account-routes.js +1931 -0
- package/lib/ui-routes/admin-config-routes.js +1233 -0
- package/lib/ui-routes/admin-entities-routes.js +2367 -0
- package/lib/ui-routes/oauth-routes.js +992 -0
- package/lib/utils/network.js +237 -0
- package/package.json +10 -10
- package/sbom.json +1 -1
- package/static/js/app.js +5 -5
- package/static/licenses.html +79 -19
- package/translations/de.mo +0 -0
- package/translations/de.po +97 -86
- package/translations/en.mo +0 -0
- package/translations/en.po +80 -75
- package/translations/et.mo +0 -0
- package/translations/et.po +96 -86
- package/translations/fr.mo +0 -0
- package/translations/fr.po +97 -86
- package/translations/ja.mo +0 -0
- package/translations/ja.po +96 -86
- package/translations/messages.pot +105 -91
- package/translations/nl.mo +0 -0
- package/translations/nl.po +98 -86
- package/translations/pl.mo +0 -0
- package/translations/pl.po +96 -86
- package/views/account/security.hbs +4 -4
- package/views/accounts/account.hbs +13 -13
- package/views/accounts/register/imap-server.hbs +12 -12
- package/views/config/document-store/pre-processing/index.hbs +4 -2
- package/views/config/oauth/app.hbs +6 -7
- package/views/config/oauth/index.hbs +2 -2
- package/views/config/service.hbs +3 -4
- package/views/dashboard.hbs +5 -7
- package/views/error.hbs +22 -7
- package/views/gateways/gateway.hbs +2 -2
- package/views/partials/add_account_modal.hbs +7 -10
- package/views/partials/document_store_header.hbs +1 -1
- package/views/partials/editor_scope_info.hbs +0 -1
- package/views/partials/oauth_config_header.hbs +1 -1
- package/views/partials/side_menu.hbs +3 -3
- package/views/partials/webhook_form.hbs +2 -2
- package/views/templates/index.hbs +1 -1
- package/views/templates/template.hbs +8 -8
- package/views/tokens/index.hbs +6 -6
- package/views/tokens/new.hbs +1 -1
- package/views/webhooks/index.hbs +4 -4
- package/views/webhooks/webhook.hbs +7 -7
- package/workers/api.js +148 -2436
- package/workers/smtp.js +2 -1
- package/lib/imapproxy/imap-core/test/client.js +0 -46
- package/lib/imapproxy/imap-core/test/fixtures/append.eml +0 -1196
- package/lib/imapproxy/imap-core/test/fixtures/chunks.js +0 -44
- package/lib/imapproxy/imap-core/test/fixtures/fix1.eml +0 -6
- package/lib/imapproxy/imap-core/test/fixtures/fix2.eml +0 -599
- package/lib/imapproxy/imap-core/test/fixtures/fix3.eml +0 -32
- package/lib/imapproxy/imap-core/test/fixtures/fix4.eml +0 -6
- package/lib/imapproxy/imap-core/test/fixtures/mimetorture.eml +0 -599
- package/lib/imapproxy/imap-core/test/fixtures/mimetorture.js +0 -2740
- package/lib/imapproxy/imap-core/test/fixtures/mimetorture.json +0 -1411
- package/lib/imapproxy/imap-core/test/fixtures/mimetree.js +0 -85
- package/lib/imapproxy/imap-core/test/fixtures/nodemailer.eml +0 -582
- package/lib/imapproxy/imap-core/test/fixtures/ryan_finnie_mime_torture.eml +0 -599
- package/lib/imapproxy/imap-core/test/fixtures/simple.eml +0 -42
- package/lib/imapproxy/imap-core/test/fixtures/simple.json +0 -164
- package/lib/imapproxy/imap-core/test/imap-compile-stream-test.js +0 -671
- package/lib/imapproxy/imap-core/test/imap-compiler-test.js +0 -272
- package/lib/imapproxy/imap-core/test/imap-indexer-test.js +0 -236
- package/lib/imapproxy/imap-core/test/imap-parser-test.js +0 -922
- package/lib/imapproxy/imap-core/test/memory-notifier.js +0 -129
- package/lib/imapproxy/imap-core/test/prepare.sh +0 -74
- package/lib/imapproxy/imap-core/test/protocol-test.js +0 -1756
- package/lib/imapproxy/imap-core/test/search-test.js +0 -1356
- package/lib/imapproxy/imap-core/test/test-client.js +0 -152
- package/lib/imapproxy/imap-core/test/test-server.js +0 -623
- package/lib/imapproxy/imap-core/test/tools-test.js +0 -22
- package/test/api-test.js +0 -899
- package/test/autoreply-test.js +0 -327
- package/test/bounce-test.js +0 -151
- package/test/complaint-test.js +0 -256
- package/test/fixtures/autoreply/LICENSE +0 -27
- package/test/fixtures/autoreply/rfc3834-01.eml +0 -23
- package/test/fixtures/autoreply/rfc3834-02.eml +0 -24
- package/test/fixtures/autoreply/rfc3834-03.eml +0 -26
- package/test/fixtures/autoreply/rfc3834-04.eml +0 -48
- package/test/fixtures/autoreply/rfc3834-05.eml +0 -19
- package/test/fixtures/autoreply/rfc3834-06.eml +0 -59
- package/test/fixtures/bounces/163.eml +0 -2521
- package/test/fixtures/bounces/fastmail.eml +0 -242
- package/test/fixtures/bounces/gmail.eml +0 -252
- package/test/fixtures/bounces/hotmail.eml +0 -655
- package/test/fixtures/bounces/mailru.eml +0 -121
- package/test/fixtures/bounces/outlook.eml +0 -1107
- package/test/fixtures/bounces/postfix.eml +0 -101
- package/test/fixtures/bounces/rambler.eml +0 -116
- package/test/fixtures/bounces/workmail.eml +0 -142
- package/test/fixtures/bounces/yahoo.eml +0 -139
- package/test/fixtures/bounces/zoho.eml +0 -83
- package/test/fixtures/bounces/zonemta.eml +0 -100
- package/test/fixtures/complaints/LICENSE +0 -27
- package/test/fixtures/complaints/amazonses.eml +0 -72
- package/test/fixtures/complaints/dmarc.eml +0 -59
- package/test/fixtures/complaints/hotmail.eml +0 -49
- package/test/fixtures/complaints/optout.eml +0 -40
- package/test/fixtures/complaints/standard-arf.eml +0 -68
- package/test/fixtures/complaints/yahoo.eml +0 -68
- package/test/oauth2-apps-test.js +0 -301
- package/test/sendonly-test.js +0 -160
- package/test/test-config.js +0 -34
- package/test/webhooks-server.js +0 -39
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const net = require('net');
|
|
5
|
+
const ipaddr = require('ipaddr.js');
|
|
6
|
+
const { reverse: dnsReverse } = require('dns').promises;
|
|
7
|
+
const { resolvePublicInterfaces } = require('pubface');
|
|
8
|
+
const googleCrawlerRanges = require('../../data/google-crawlers.json');
|
|
9
|
+
const settings = require('../settings');
|
|
10
|
+
const logger = require('../logger');
|
|
11
|
+
const { REDIS_PREFIX } = require('../consts');
|
|
12
|
+
|
|
13
|
+
// Build Google crawler IP range map for efficient lookup
|
|
14
|
+
const googleCrawlerMap = new Map();
|
|
15
|
+
for (let prefixEntry of googleCrawlerRanges.prefixes) {
|
|
16
|
+
for (let prefixKey of ['ipv6Prefix', 'ipv4Prefix']) {
|
|
17
|
+
if (prefixEntry[prefixKey]) {
|
|
18
|
+
let parsed = ipaddr.parseCIDR(prefixEntry[prefixKey]);
|
|
19
|
+
if (!googleCrawlerMap.has(prefixKey)) {
|
|
20
|
+
googleCrawlerMap.set(prefixKey, []);
|
|
21
|
+
}
|
|
22
|
+
googleCrawlerMap.get(prefixKey).push(parsed);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Matches an IP address against a list of addresses or CIDR ranges
|
|
29
|
+
* @param {string} ip - IP address to check
|
|
30
|
+
* @param {string[]} addresses - Array of IP addresses or CIDR ranges
|
|
31
|
+
* @returns {boolean} True if IP matches any address in the list
|
|
32
|
+
*/
|
|
33
|
+
function matchIp(ip, addresses) {
|
|
34
|
+
let parsed = ipaddr.parse(ip);
|
|
35
|
+
for (let addr of addresses) {
|
|
36
|
+
try {
|
|
37
|
+
let match;
|
|
38
|
+
if (/\/\d+$/.test(addr)) {
|
|
39
|
+
match = parsed.match(ipaddr.parseCIDR(addr));
|
|
40
|
+
} else {
|
|
41
|
+
match = parsed.toNormalizedString() === ipaddr.parse(addr).toNormalizedString();
|
|
42
|
+
}
|
|
43
|
+
if (match) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
logger.error({ msg: 'Failed to parse IP address', ip, addr, err });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Detects if a request is from an automated scanner (Google, Barracuda, etc.)
|
|
56
|
+
* @param {string} ip - IP address to check
|
|
57
|
+
* @returns {Promise<boolean>} True if request appears to be automated
|
|
58
|
+
*/
|
|
59
|
+
async function detectAutomatedRequest(ip) {
|
|
60
|
+
let prefixKey;
|
|
61
|
+
if (net.isIPv4(ip)) {
|
|
62
|
+
prefixKey = 'ipv4Prefix';
|
|
63
|
+
} else if (net.isIPv6(ip)) {
|
|
64
|
+
prefixKey = 'ipv6Prefix';
|
|
65
|
+
} else {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const addr = ipaddr.parse(ip);
|
|
70
|
+
|
|
71
|
+
// Check if it is a Google security scanner
|
|
72
|
+
for (let prefixEntry of googleCrawlerMap.get(prefixKey)) {
|
|
73
|
+
if (addr.match(prefixEntry)) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check known scanners via reverse DNS
|
|
79
|
+
let hostnames;
|
|
80
|
+
try {
|
|
81
|
+
hostnames = await dnsReverse(ip);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
logger.trace({
|
|
84
|
+
msg: 'Failed to reverse resolve IP',
|
|
85
|
+
ip,
|
|
86
|
+
err
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!hostnames || !hostnames.length) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const hostname = []
|
|
95
|
+
.concat(hostnames || [])
|
|
96
|
+
.shift()
|
|
97
|
+
.toString()
|
|
98
|
+
.trim()
|
|
99
|
+
.toLowerCase();
|
|
100
|
+
|
|
101
|
+
// Barracuda, spfbl
|
|
102
|
+
if (/\bbarracuda\.com$|\bspfbl\.net$/gi.test(hostname)) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Updates Redis with information about available public network interfaces
|
|
111
|
+
* @param {Object} redis - Redis client instance
|
|
112
|
+
* @returns {Promise<void>}
|
|
113
|
+
*/
|
|
114
|
+
async function updatePublicInterfaces(redis) {
|
|
115
|
+
let interfaces = await resolvePublicInterfaces();
|
|
116
|
+
|
|
117
|
+
for (let iface of interfaces) {
|
|
118
|
+
if (!iface.localAddress) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (iface.defaultInterface) {
|
|
123
|
+
await redis.hset(`${REDIS_PREFIX}interfaces`, `default:${iface.family}`, iface.localAddress);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let existingEntry = await redis.hget(`${REDIS_PREFIX}interfaces`, iface.localAddress);
|
|
127
|
+
if (existingEntry) {
|
|
128
|
+
try {
|
|
129
|
+
existingEntry = JSON.parse(existingEntry);
|
|
130
|
+
|
|
131
|
+
iface.name = iface.name || existingEntry.name;
|
|
132
|
+
|
|
133
|
+
if (!iface.localAddress || !iface.ip || !iface.name) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
} catch (err) {
|
|
137
|
+
// ignore parsing errors
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
delete iface.defaultInterface;
|
|
142
|
+
await redis.hset(`${REDIS_PREFIX}interfaces`, iface.localAddress, JSON.stringify(iface));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Gets the local address to use for outbound connections
|
|
148
|
+
* @param {Object} redis - Redis client instance
|
|
149
|
+
* @param {string} protocol - Protocol name (e.g., 'smtp', 'imap')
|
|
150
|
+
* @param {string} account - Account identifier
|
|
151
|
+
* @param {string} [hint] - Optional IP address hint
|
|
152
|
+
* @returns {Promise<Object>} Address information object
|
|
153
|
+
*/
|
|
154
|
+
async function getLocalAddress(redis, protocol, account, hint) {
|
|
155
|
+
// Dynamic require to access tools.getServiceHostname() and tools.selectRendezvousAddress()
|
|
156
|
+
// These functions are defined in tools.js, which also imports from this module
|
|
157
|
+
const tools = require('../tools');
|
|
158
|
+
|
|
159
|
+
let existingAddresses = Object.values(os.networkInterfaces())
|
|
160
|
+
.flatMap(entry => entry)
|
|
161
|
+
.map(entry => entry.address);
|
|
162
|
+
|
|
163
|
+
if (hint) {
|
|
164
|
+
let parsedHint = ipaddr.parse(hint);
|
|
165
|
+
let normalizedHint = parsedHint.toNormalizedString();
|
|
166
|
+
let iface = await redis.hget(`${REDIS_PREFIX}interfaces`, normalizedHint);
|
|
167
|
+
try {
|
|
168
|
+
iface = iface ? JSON.parse(iface) : null;
|
|
169
|
+
} catch (err) {
|
|
170
|
+
// ignore parsing errors
|
|
171
|
+
}
|
|
172
|
+
if (iface && existingAddresses.includes(iface.localAddress)) {
|
|
173
|
+
iface.addressSelector = 'hint';
|
|
174
|
+
return iface;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let addressStrategy = await settings.get(`${protocol}Strategy`);
|
|
179
|
+
let localAddresses = []
|
|
180
|
+
.concat((await settings.get(`localAddresses`)) || [])
|
|
181
|
+
.filter(address => existingAddresses.includes(address))
|
|
182
|
+
.filter(address => net.isIPv4(address));
|
|
183
|
+
let localAddress;
|
|
184
|
+
|
|
185
|
+
let hostname = await tools.getServiceHostname(await settings.get('smtpEhloName'));
|
|
186
|
+
|
|
187
|
+
let addressSelector;
|
|
188
|
+
|
|
189
|
+
if (!localAddresses.length) {
|
|
190
|
+
addressSelector = 'default';
|
|
191
|
+
return { address: false, name: hostname, addressSelector };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (localAddresses.length === 1) {
|
|
195
|
+
addressSelector = 'single';
|
|
196
|
+
localAddress = localAddresses[0];
|
|
197
|
+
} else {
|
|
198
|
+
switch (addressStrategy) {
|
|
199
|
+
case 'random':
|
|
200
|
+
addressSelector = 'random';
|
|
201
|
+
localAddress = localAddresses[Math.floor(Math.random() * localAddresses.length)];
|
|
202
|
+
break;
|
|
203
|
+
case 'dedicated':
|
|
204
|
+
addressSelector = 'dedicated';
|
|
205
|
+
localAddress = tools.selectRendezvousAddress(account, localAddresses);
|
|
206
|
+
break;
|
|
207
|
+
default:
|
|
208
|
+
addressSelector = 'unknown';
|
|
209
|
+
return { address: false, name: hostname, addressSelector };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!localAddress) {
|
|
214
|
+
addressSelector = 'unset';
|
|
215
|
+
return { address: false, name: hostname, addressSelector };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
let addressData = JSON.parse(await redis.hget(`${REDIS_PREFIX}interfaces`, localAddress));
|
|
220
|
+
addressData.name = addressData.name || hostname;
|
|
221
|
+
addressData.addressSelector = addressSelector;
|
|
222
|
+
return addressData;
|
|
223
|
+
} catch (err) {
|
|
224
|
+
logger.error({ msg: 'Failed to load address data', localAddress, err });
|
|
225
|
+
addressSelector = 'error';
|
|
226
|
+
return { address: false, name: hostname, addressSelector };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
module.exports = {
|
|
231
|
+
resolvePublicInterfaces,
|
|
232
|
+
updatePublicInterfaces,
|
|
233
|
+
getLocalAddress,
|
|
234
|
+
matchIp,
|
|
235
|
+
detectAutomatedRequest,
|
|
236
|
+
googleCrawlerMap
|
|
237
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emailengine-app",
|
|
3
|
-
"version": "2.61.
|
|
3
|
+
"version": "2.61.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"productTitle": "EmailEngine",
|
|
6
6
|
"description": "Email Sync Engine",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"build-dist": "pkg --compress Brotli package.json && npm install && node winconf.js",
|
|
18
18
|
"build-dist-fast": "pkg --debug package.json && npm install && node winconf.js",
|
|
19
19
|
"licenses": "license-checker --excludePackages 'emailengine-app' --json | node list-generate.js > static/licenses.html",
|
|
20
|
-
"gettext": "find ./views -name \"*.hbs\" -print0 | xargs -0 xgettext-template -L Handlebars -o translations/messages.pot --force-po && jsxgettext lib/routes-ui.js workers/api.js lib/tools.js lib/autodetect-imap-settings.js -j -o translations/messages.pot",
|
|
20
|
+
"gettext": "find ./views -name \"*.hbs\" -print0 | xargs -0 xgettext-template -L Handlebars -o translations/messages.pot --force-po && jsxgettext lib/routes-ui.js workers/api.js lib/tools.js lib/autodetect-imap-settings.js lib/ui-routes/account-routes.js lib/ui-routes/admin-config-routes.js lib/ui-routes/admin-entities-routes.js -j -o translations/messages.pot",
|
|
21
21
|
"prepare-docker": "echo \"EE_DOCKER_LEGACY=$EE_DOCKER_LEGACY\" >> system.env && cat system.env",
|
|
22
22
|
"update": "rm -rf node_modules package-lock.json && ncu -u && npm install && ./copy-static-files.sh && npm run licenses && npm run gettext",
|
|
23
23
|
"test-gmail-api": "node lib/email-client/gmail-client.js --dbs.redis=redis://127.0.0.1/11",
|
|
@@ -61,14 +61,14 @@
|
|
|
61
61
|
"@postalsys/ee-client": "1.3.0",
|
|
62
62
|
"@postalsys/email-ai-tools": "1.11.1",
|
|
63
63
|
"@postalsys/email-text-tools": "2.4.0",
|
|
64
|
-
"@postalsys/gettext": "4.1.
|
|
64
|
+
"@postalsys/gettext": "4.1.1",
|
|
65
65
|
"@postalsys/joi-messages": "1.0.5",
|
|
66
66
|
"@postalsys/templates": "2.0.0",
|
|
67
67
|
"@zone-eu/mailsplit": "5.4.8",
|
|
68
68
|
"@zone-eu/wild-config": "1.7.3",
|
|
69
69
|
"ace-builds": "1.43.5",
|
|
70
70
|
"base32.js": "0.1.0",
|
|
71
|
-
"bullmq": "5.66.
|
|
71
|
+
"bullmq": "5.66.5",
|
|
72
72
|
"compare-versions": "6.1.1",
|
|
73
73
|
"dotenv": "17.2.3",
|
|
74
74
|
"encoding-japanese": "2.2.0",
|
|
@@ -81,10 +81,10 @@
|
|
|
81
81
|
"he": "1.2.0",
|
|
82
82
|
"html-to-text": "9.0.5",
|
|
83
83
|
"ical.js": "1.5.0",
|
|
84
|
-
"iconv-lite": "0.7.
|
|
85
|
-
"imapflow": "1.2.
|
|
84
|
+
"iconv-lite": "0.7.2",
|
|
85
|
+
"imapflow": "1.2.6",
|
|
86
86
|
"ioredfour": "1.3.0-ioredis-07",
|
|
87
|
-
"ioredis": "5.
|
|
87
|
+
"ioredis": "5.9.1",
|
|
88
88
|
"ipaddr.js": "2.3.0",
|
|
89
89
|
"joi": "17.13.3",
|
|
90
90
|
"jquery": "3.7.1",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"murmurhash": "2.0.1",
|
|
100
100
|
"nanoid": "3.3.8",
|
|
101
101
|
"nodemailer": "7.0.12",
|
|
102
|
-
"pino": "10.1.
|
|
102
|
+
"pino": "10.1.1",
|
|
103
103
|
"popper.js": "1.16.1",
|
|
104
104
|
"prom-client": "15.1.3",
|
|
105
105
|
"psl": "1.15.0",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"speakeasy": "2.0.0",
|
|
112
112
|
"startbootstrap-sb-admin-2": "3.3.7",
|
|
113
113
|
"timezones-list": "3.1.0",
|
|
114
|
-
"undici": "7.
|
|
114
|
+
"undici": "7.18.2",
|
|
115
115
|
"xml2js": "0.6.2"
|
|
116
116
|
},
|
|
117
117
|
"devDependencies": {
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"prettier": "3.7.4",
|
|
129
129
|
"resedit": "3.0.1",
|
|
130
130
|
"spdx-satisfies": "6.0.0",
|
|
131
|
-
"supertest": "7.
|
|
131
|
+
"supertest": "7.2.2",
|
|
132
132
|
"xgettext-template": "5.0.0"
|
|
133
133
|
},
|
|
134
134
|
"engines": {
|