emailengine-app 2.63.1 → 2.63.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/server.js CHANGED
@@ -616,6 +616,8 @@ let workerAssigned = new WeakMap(); // Map of worker -> Set of accounts
616
616
  let onlineWorkers = new WeakSet(); // Set of workers that are online
617
617
  let reassignmentTimer = null; // Timer for failsafe reassignment
618
618
  let reassignmentPending = false; // Flag to track if reassignment is pending
619
+ let assignRetryCount = 0; // Counter for safety-net retries in assignAccounts
620
+ const MAX_ASSIGN_RETRIES = 3; // Max consecutive safety-net retries before giving up
619
621
 
620
622
  // Worker management
621
623
  let imapInitialWorkersLoaded = false; // Have all initial IMAP workers started?
@@ -1578,6 +1580,11 @@ async function assignAccounts() {
1578
1580
  let totalAccounts = assigned.size + unassigned.size;
1579
1581
  let targetPerWorker = Math.ceil(totalAccounts / availableIMAPWorkers.size);
1580
1582
 
1583
+ // Collect accounts that fail assignment so we do not re-insert into
1584
+ // the Set we are iterating (re-insertion during for..of would cause
1585
+ // the same account to be visited again in the same pass).
1586
+ let failedAccounts = [];
1587
+
1581
1588
  // Assign each unassigned account
1582
1589
  for (let account of unassigned) {
1583
1590
  if (!availableIMAPWorkers.size) {
@@ -1636,7 +1643,14 @@ async function assignAccounts() {
1636
1643
  // Roll back -- account was never actually assigned
1637
1644
  workerAssigned.get(worker).delete(account);
1638
1645
  assigned.delete(account);
1639
- unassigned.add(account);
1646
+ // Revert load map so subsequent iterations see accurate counts
1647
+ if (!isReassignment) {
1648
+ workerLoadMap.set(worker, workerLoadMap.get(worker) - 1);
1649
+ sortedWorkers = Array.from(workerLoadMap.entries())
1650
+ .sort((a, b) => a[1] - b[1])
1651
+ .map(entry => entry[0]);
1652
+ }
1653
+ failedAccounts.push(account);
1640
1654
  logger.error({ msg: 'Failed to assign account to worker', account, threadId: worker.threadId, err });
1641
1655
  continue;
1642
1656
  }
@@ -1647,6 +1661,11 @@ async function assignAccounts() {
1647
1661
  }
1648
1662
  }
1649
1663
 
1664
+ // Re-add failed accounts after iteration completes
1665
+ for (let account of failedAccounts) {
1666
+ unassigned.add(account);
1667
+ }
1668
+
1650
1669
  // Log final distribution for monitoring
1651
1670
  let distribution = [];
1652
1671
  for (let worker of availableIMAPWorkers) {
@@ -1661,15 +1680,30 @@ async function assignAccounts() {
1661
1680
  } finally {
1662
1681
  assigning = false;
1663
1682
 
1664
- // Safety net: if accounts remain unassigned, schedule a retry
1683
+ // Safety net: if accounts remain unassigned, schedule a retry with backoff
1665
1684
  if (unassigned && unassigned.size > 0 && availableIMAPWorkers.size > 0 && !isClosing) {
1666
- logger.warn({
1667
- msg: 'Accounts remain unassigned after assignment pass, scheduling retry',
1668
- remaining: unassigned.size
1669
- });
1670
- setTimeout(() => {
1671
- assignAccounts().catch(err => logger.error({ msg: 'Retry assignment failed', err }));
1672
- }, 5000);
1685
+ assignRetryCount++;
1686
+ if (assignRetryCount <= MAX_ASSIGN_RETRIES) {
1687
+ let delay = 5000 * assignRetryCount;
1688
+ logger.warn({
1689
+ msg: 'Accounts remain unassigned after assignment pass, scheduling retry',
1690
+ remaining: unassigned.size,
1691
+ retry: assignRetryCount,
1692
+ maxRetries: MAX_ASSIGN_RETRIES,
1693
+ delayMs: delay
1694
+ });
1695
+ setTimeout(() => {
1696
+ assignAccounts().catch(err => logger.error({ msg: 'Retry assignment failed', err }));
1697
+ }, delay);
1698
+ } else {
1699
+ logger.error({
1700
+ msg: 'Max assignment retries reached, accounts remain unassigned',
1701
+ remaining: unassigned.size
1702
+ });
1703
+ assignRetryCount = 0;
1704
+ }
1705
+ } else {
1706
+ assignRetryCount = 0;
1673
1707
  }
1674
1708
  }
1675
1709
  }
@@ -1,6 +1,6 @@
1
1
  <!doctype html><html><head><meta charset="utf-8"><title>EmailEngine Licenses</title><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"></head><body>
2
2
  <div class="container-fluid">
3
- <h1>EmailEngine v2.63.0</h1><p>EmailEngine includes code from the following software packages:</p>
3
+ <h1>EmailEngine v2.63.2</h1><p>EmailEngine includes code from the following software packages:</p>
4
4
  <table class="table table-sm">
5
5
  <tr><thead class="thead-dark"><th>Package</th><th>Version</th><th>License</th><th>Publisher</th><th>Publisher's Email</th><th>Package URL</th></tr>
6
6
  <tbody>
@@ -216,7 +216,7 @@
216
216
  </tr>
217
217
  <tr>
218
218
  <td><a href="https://npmjs.com/package/@csstools/css-syntax-patches-for-csstree">@csstools/css-syntax-patches-for-csstree</a></td>
219
- <td>1.0.28</td>
219
+ <td>1.0.29</td>
220
220
  <td>MIT-0</td>
221
221
  <td></td>
222
222
  <td></td>
@@ -785,6 +785,16 @@
785
785
  </td
786
786
  </tr>
787
787
  <tr>
788
+ <td><a href="https://npmjs.com/package/@ioredis/commands">@ioredis/commands</a></td>
789
+ <td>1.5.1</td>
790
+ <td>MIT</td>
791
+ <td>Zihua Li</td>
792
+ <td>i@zihua.li</td>
793
+ <td>
794
+ <a href="https://github.com/ioredis/commands">github.com/ioredis/commands</a>
795
+ </td
796
+ </tr>
797
+ <tr>
788
798
  <td><a href="https://npmjs.com/package/@jsdevtools/ono">@jsdevtools/ono</a></td>
789
799
  <td>7.1.3</td>
790
800
  <td>MIT</td>
@@ -836,7 +846,7 @@
836
846
  </tr>
837
847
  <tr>
838
848
  <td><a href="https://npmjs.com/package/@opentelemetry/core">@opentelemetry/core</a></td>
839
- <td>2.5.1</td>
849
+ <td>2.6.0</td>
840
850
  <td>Apache-2.0</td>
841
851
  <td>OpenTelemetry Authors</td>
842
852
  <td></td>
@@ -926,7 +936,7 @@
926
936
  </tr>
927
937
  <tr>
928
938
  <td><a href="https://npmjs.com/package/@postalsys/email-ai-tools">@postalsys/email-ai-tools</a></td>
929
- <td>1.11.3</td>
939
+ <td>1.11.4</td>
930
940
  <td>MIT</td>
931
941
  <td>Postal Systems O&#xDC;</td>
932
942
  <td></td>
@@ -936,7 +946,7 @@
936
946
  </tr>
937
947
  <tr>
938
948
  <td><a href="https://npmjs.com/package/@postalsys/email-text-tools">@postalsys/email-text-tools</a></td>
939
- <td>2.4.1</td>
949
+ <td>2.4.2</td>
940
950
  <td>MIT</td>
941
951
  <td>Postal Systems O&#xDC;</td>
942
952
  <td></td>
@@ -1576,7 +1586,7 @@
1576
1586
  </tr>
1577
1587
  <tr>
1578
1588
  <td><a href="https://npmjs.com/package/brace-expansion">brace-expansion</a></td>
1579
- <td>5.0.3</td>
1589
+ <td>5.0.4</td>
1580
1590
  <td>MIT</td>
1581
1591
  <td></td>
1582
1592
  <td></td>
@@ -1606,7 +1616,7 @@
1606
1616
  </tr>
1607
1617
  <tr>
1608
1618
  <td><a href="https://npmjs.com/package/bullmq">bullmq</a></td>
1609
- <td>5.70.1</td>
1619
+ <td>5.70.2</td>
1610
1620
  <td>MIT</td>
1611
1621
  <td>Taskforce.sh Inc.</td>
1612
1622
  <td></td>
@@ -1976,7 +1986,7 @@
1976
1986
  </tr>
1977
1987
  <tr>
1978
1988
  <td><a href="https://npmjs.com/package/css-tree">css-tree</a></td>
1979
- <td>3.1.0</td>
1989
+ <td>3.2.1</td>
1980
1990
  <td>MIT</td>
1981
1991
  <td>Roman Dvornov</td>
1982
1992
  <td>rdvornov@gmail.com</td>
@@ -2216,7 +2226,7 @@
2216
2226
  </tr>
2217
2227
  <tr>
2218
2228
  <td><a href="https://npmjs.com/package/dompurify">dompurify</a></td>
2219
- <td>3.3.1</td>
2229
+ <td>3.3.2</td>
2220
2230
  <td>(MPL-2.0 OR Apache-2.0)</td>
2221
2231
  <td>Dr.-Ing. Mario Heiderich, Cure53</td>
2222
2232
  <td>mario@cure53.de</td>
@@ -2835,7 +2845,7 @@
2835
2845
  </tr>
2836
2846
  <tr>
2837
2847
  <td><a href="https://npmjs.com/package/flatted">flatted</a></td>
2838
- <td>3.3.3</td>
2848
+ <td>3.3.4</td>
2839
2849
  <td>ISC</td>
2840
2850
  <td>Andrea Giammarchi</td>
2841
2851
  <td></td>
@@ -3465,7 +3475,7 @@
3465
3475
  </tr>
3466
3476
  <tr>
3467
3477
  <td><a href="https://npmjs.com/package/imapflow">imapflow</a></td>
3468
- <td>1.2.10</td>
3478
+ <td>1.2.12</td>
3469
3479
  <td>MIT</td>
3470
3480
  <td>Postal Systems O&#xDC;</td>
3471
3481
  <td></td>
@@ -3545,6 +3555,16 @@
3545
3555
  </tr>
3546
3556
  <tr>
3547
3557
  <td><a href="https://npmjs.com/package/ioredis">ioredis</a></td>
3558
+ <td>5.10.0</td>
3559
+ <td>MIT</td>
3560
+ <td>Zihua Li</td>
3561
+ <td>i@zihua.li</td>
3562
+ <td>
3563
+ <a href="https://github.com/luin/ioredis">github.com/luin/ioredis</a>
3564
+ </td
3565
+ </tr>
3566
+ <tr>
3567
+ <td><a href="https://npmjs.com/package/ioredis">ioredis</a></td>
3548
3568
  <td>5.3.2</td>
3549
3569
  <td>MIT</td>
3550
3570
  <td>Zihua Li</td>
@@ -3905,7 +3925,7 @@
3905
3925
  </tr>
3906
3926
  <tr>
3907
3927
  <td><a href="https://npmjs.com/package/juice">juice</a></td>
3908
- <td>11.1.0</td>
3928
+ <td>11.1.1</td>
3909
3929
  <td>MIT</td>
3910
3930
  <td></td>
3911
3931
  <td></td>
@@ -4185,7 +4205,7 @@
4185
4205
  </tr>
4186
4206
  <tr>
4187
4207
  <td><a href="https://npmjs.com/package/mdn-data">mdn-data</a></td>
4188
- <td>2.12.2</td>
4208
+ <td>2.27.1</td>
4189
4209
  <td>CC0-1.0</td>
4190
4210
  <td>Mozilla Developer Network</td>
4191
4211
  <td></td>
@@ -4485,7 +4505,7 @@
4485
4505
  </tr>
4486
4506
  <tr>
4487
4507
  <td><a href="https://npmjs.com/package/node-html-parser">node-html-parser</a></td>
4488
- <td>7.0.2</td>
4508
+ <td>7.1.0</td>
4489
4509
  <td>MIT</td>
4490
4510
  <td>Xiaoyi Shi</td>
4491
4511
  <td>ashi009@gmail.com</td>
@@ -5135,7 +5155,7 @@
5135
5155
  </tr>
5136
5156
  <tr>
5137
5157
  <td><a href="https://npmjs.com/package/pump">pump</a></td>
5138
- <td>3.0.3</td>
5158
+ <td>3.0.4</td>
5139
5159
  <td>MIT</td>
5140
5160
  <td>Mathias Buus Madsen</td>
5141
5161
  <td>mathiasbuus@gmail.com</td>
@@ -5415,7 +5435,7 @@
5415
5435
  </tr>
5416
5436
  <tr>
5417
5437
  <td><a href="https://npmjs.com/package/sax">sax</a></td>
5418
- <td>1.4.4</td>
5438
+ <td>1.5.0</td>
5419
5439
  <td>BlueOak-1.0.0</td>
5420
5440
  <td>Isaac Z. Schlueter</td>
5421
5441
  <td>i@izs.me</td>
@@ -5915,7 +5935,7 @@
5915
5935
  </tr>
5916
5936
  <tr>
5917
5937
  <td><a href="https://npmjs.com/package/swagger-ui-dist">swagger-ui-dist</a></td>
5918
- <td>5.31.2</td>
5938
+ <td>5.32.0</td>
5919
5939
  <td>Apache-2.0</td>
5920
5940
  <td></td>
5921
5941
  <td></td>
@@ -5995,7 +6015,7 @@
5995
6015
  </tr>
5996
6016
  <tr>
5997
6017
  <td><a href="https://npmjs.com/package/tldts-core">tldts-core</a></td>
5998
- <td>7.0.23</td>
6018
+ <td>7.0.24</td>
5999
6019
  <td>MIT</td>
6000
6020
  <td>R&#xE9;mi Berson</td>
6001
6021
  <td></td>
@@ -6005,7 +6025,7 @@
6005
6025
  </tr>
6006
6026
  <tr>
6007
6027
  <td><a href="https://npmjs.com/package/tldts">tldts</a></td>
6008
- <td>7.0.23</td>
6028
+ <td>7.0.24</td>
6009
6029
  <td>MIT</td>
6010
6030
  <td>R&#xE9;mi Berson</td>
6011
6031
  <td></td>
@@ -6175,16 +6195,6 @@
6175
6195
  </tr>
6176
6196
  <tr>
6177
6197
  <td><a href="https://npmjs.com/package/undici">undici</a></td>
6178
- <td>7.19.2</td>
6179
- <td>MIT</td>
6180
- <td></td>
6181
- <td></td>
6182
- <td>
6183
- <a href="https://github.com/nodejs/undici">github.com/nodejs/undici</a>
6184
- </td
6185
- </tr>
6186
- <tr>
6187
- <td><a href="https://npmjs.com/package/undici">undici</a></td>
6188
6198
  <td>7.22.0</td>
6189
6199
  <td>MIT</td>
6190
6200
  <td></td>
@@ -1,7 +1,7 @@
1
1
  msgid ""
2
2
  msgstr ""
3
3
  "Content-Type: text/plain; charset=ascii\n"
4
- "POT-Creation-Date: 2026-02-26 12:37+0000\n"
4
+ "POT-Creation-Date: 2026-03-05 15:45+0000\n"
5
5
 
6
6
  #: views/config/license.hbs:45
7
7
  msgid "%d day"
@@ -9,10 +9,6 @@ msgid_plural "%d days"
9
9
  msgstr[0] ""
10
10
  msgstr[1] ""
11
11
 
12
- #: views/redirect.hbs:1
13
- msgid "Click <a href=\"%s\">here</a> to continue&mldr;"
14
- msgstr ""
15
-
16
12
  #: views/unsubscribe.hbs:3
17
13
  #: views/unsubscribe.hbs:62
18
14
  #: views/unsubscribe.hbs:85
@@ -62,6 +58,10 @@ msgstr ""
62
58
  msgid "Enter your email address"
63
59
  msgstr ""
64
60
 
61
+ #: views/redirect.hbs:1
62
+ msgid "Click <a href=\"%s\">here</a> to continue&mldr;"
63
+ msgstr ""
64
+
65
65
  #: views/accounts/register/imap.hbs:11
66
66
  msgid "Your name"
67
67
  msgstr ""
@@ -253,7 +253,7 @@ msgstr ""
253
253
  #: lib/routes-ui.js:4874
254
254
  #: lib/routes-ui.js:5121
255
255
  #: lib/routes-ui.js:5157
256
- #: workers/api.js:2417
256
+ #: workers/api.js:2453
257
257
  #: lib/ui-routes/account-routes.js:549
258
258
  #: lib/ui-routes/account-routes.js:585
259
259
  #: lib/ui-routes/account-routes.js:702
@@ -338,12 +338,12 @@ msgstr ""
338
338
  msgid "Subscription Management"
339
339
  msgstr ""
340
340
 
341
- #: workers/api.js:6911
342
- #: workers/api.js:7027
341
+ #: workers/api.js:6947
342
+ #: workers/api.js:7063
343
343
  msgid "Requested page not found"
344
344
  msgstr ""
345
345
 
346
- #: workers/api.js:6912
346
+ #: workers/api.js:6948
347
347
  msgid "Something went wrong"
348
348
  msgstr ""
349
349
 
package/workers/api.js CHANGED
@@ -1492,18 +1492,55 @@ Include your token in requests using one of these methods:
1492
1492
  }
1493
1493
  });
1494
1494
 
1495
+ const staticRoot = pathlib.resolve(__dirname, '..', 'static');
1496
+ const staticRootPrefix = staticRoot + pathlib.sep;
1497
+
1495
1498
  server.route({
1496
1499
  method: 'GET',
1497
1500
  path: '/static/{file*}',
1498
1501
  handler: {
1499
1502
  directory: {
1500
- path: pathlib.join(__dirname, '..', 'static'),
1503
+ path: staticRoot,
1501
1504
  index: false
1502
1505
  }
1503
1506
  },
1504
1507
  options: {
1505
1508
  auth: false,
1506
- tags: ['static']
1509
+ tags: ['static'],
1510
+ // Prevent EISDIR crash in pkg snapshot environments.
1511
+ // Inert calls fs.open() on directory paths which triggers an uncaught exception
1512
+ // in pkg's patched fs, so we intercept directory requests before they reach Inert.
1513
+ pre: process.pkg
1514
+ ? [
1515
+ {
1516
+ method: async request => {
1517
+ const filePath = request.params.file;
1518
+ if (!filePath) {
1519
+ throw Boom.notFound();
1520
+ }
1521
+
1522
+ const resolved = pathlib.resolve(staticRoot, filePath);
1523
+ if (!resolved.startsWith(staticRootPrefix)) {
1524
+ throw Boom.notFound();
1525
+ }
1526
+
1527
+ try {
1528
+ const stat = await fs.promises.stat(resolved);
1529
+ if (stat.isDirectory()) {
1530
+ throw Boom.notFound();
1531
+ }
1532
+ } catch (err) {
1533
+ if (err.isBoom) {
1534
+ throw err;
1535
+ }
1536
+ // stat failed (ENOENT etc.) - let Inert handle it
1537
+ }
1538
+
1539
+ return null;
1540
+ }
1541
+ }
1542
+ ]
1543
+ : undefined
1507
1544
  }
1508
1545
  });
1509
1546