@bobfrankston/mailx 1.0.15 → 1.0.16
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/client/app.js
CHANGED
|
@@ -505,7 +505,8 @@ fetch("/api/version").then(r => r.json()).then(d => {
|
|
|
505
505
|
startupStatus.textContent = "Server offline — start with: node packages/mailx-server/index.js";
|
|
506
506
|
}
|
|
507
507
|
});
|
|
508
|
-
// ── Sync pending indicator ──
|
|
508
|
+
// ── Sync pending indicator + server health check ──
|
|
509
|
+
let serverDown = false;
|
|
509
510
|
setInterval(async () => {
|
|
510
511
|
try {
|
|
511
512
|
const res = await fetch("/api/sync/pending");
|
|
@@ -517,8 +518,25 @@ setInterval(async () => {
|
|
|
517
518
|
el.textContent = data.pending > 0 ? `↻ ${data.pending} pending` : "";
|
|
518
519
|
el.style.color = data.pending > 0 ? "oklch(0.75 0.15 60)" : "";
|
|
519
520
|
}
|
|
521
|
+
// Server is back — reload if it was down
|
|
522
|
+
if (serverDown) {
|
|
523
|
+
serverDown = false;
|
|
524
|
+
const statusEl = document.getElementById("status-sync");
|
|
525
|
+
if (statusEl)
|
|
526
|
+
statusEl.textContent = "Server reconnected";
|
|
527
|
+
location.reload();
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
catch {
|
|
531
|
+
if (!serverDown) {
|
|
532
|
+
serverDown = true;
|
|
533
|
+
const statusEl = document.getElementById("status-sync");
|
|
534
|
+
if (statusEl) {
|
|
535
|
+
statusEl.textContent = "SERVER OFFLINE";
|
|
536
|
+
statusEl.style.color = "oklch(0.65 0.2 25)";
|
|
537
|
+
}
|
|
538
|
+
}
|
|
520
539
|
}
|
|
521
|
-
catch { /* offline */ }
|
|
522
540
|
}, 5000);
|
|
523
541
|
console.log("mailx client initialized, location:", location.href);
|
|
524
542
|
// Diagnostic: test API connectivity (helps debug WebView2 blank screen)
|
|
@@ -6,13 +6,16 @@ import { getMessage, updateFlags } from "../lib/api-client.js";
|
|
|
6
6
|
let currentMessage = null;
|
|
7
7
|
let currentAccountId = "";
|
|
8
8
|
let showMessageGeneration = 0; // Cancel stale fetches
|
|
9
|
+
let retryCount = 0;
|
|
9
10
|
export function getCurrentMessage() {
|
|
10
11
|
if (!currentMessage)
|
|
11
12
|
return null;
|
|
12
13
|
return { accountId: currentAccountId, message: currentMessage };
|
|
13
14
|
}
|
|
14
|
-
export async function showMessage(accountId, uid, folderId, specialUse) {
|
|
15
|
+
export async function showMessage(accountId, uid, folderId, specialUse, isRetry = false) {
|
|
15
16
|
const gen = ++showMessageGeneration;
|
|
17
|
+
if (!isRetry)
|
|
18
|
+
retryCount = 0;
|
|
16
19
|
const headerEl = document.getElementById("mv-header");
|
|
17
20
|
const bodyEl = document.getElementById("mv-body");
|
|
18
21
|
const attEl = document.getElementById("mv-attachments");
|
|
@@ -221,9 +224,17 @@ export async function showMessage(accountId, uid, folderId, specialUse) {
|
|
|
221
224
|
}
|
|
222
225
|
}
|
|
223
226
|
catch (e) {
|
|
224
|
-
const
|
|
225
|
-
bodyEl.innerHTML = `<div class="mv-empty">Failed to load message: ${msg}<br><button onclick="location.reload()">Retry</button></div>`;
|
|
227
|
+
const err = e.message || "Unknown error";
|
|
226
228
|
console.error("showMessage error:", e);
|
|
229
|
+
if (retryCount < 3) {
|
|
230
|
+
retryCount++;
|
|
231
|
+
bodyEl.innerHTML = `<div class="mv-empty">Loading failed: ${err} — retrying (${retryCount}/3)...</div>`;
|
|
232
|
+
setTimeout(() => { if (gen === showMessageGeneration)
|
|
233
|
+
showMessage(accountId, uid, folderId, specialUse, true); }, 3000);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
bodyEl.innerHTML = `<div class="mv-empty">Failed to load message: ${err}</div>`;
|
|
237
|
+
}
|
|
227
238
|
}
|
|
228
239
|
}
|
|
229
240
|
function formatAddr(addr) {
|
package/client/lib/api-client.js
CHANGED
|
@@ -19,10 +19,19 @@ function newMessageListSignal() {
|
|
|
19
19
|
return messageListAbort.signal;
|
|
20
20
|
}
|
|
21
21
|
async function api(path, options) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
let res;
|
|
23
|
+
try {
|
|
24
|
+
res = await fetch(`/api${path}`, {
|
|
25
|
+
headers: { "Content-Type": "application/json" },
|
|
26
|
+
...options
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
// Network error — server is down
|
|
31
|
+
if (e.name === "AbortError")
|
|
32
|
+
throw e;
|
|
33
|
+
throw new Error("Server offline — restart with launch.ps1");
|
|
34
|
+
}
|
|
26
35
|
if (!res.ok) {
|
|
27
36
|
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
28
37
|
throw new Error(err.error || res.statusText);
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
background: var(--color-bg-surface);
|
|
93
93
|
color: var(--color-text);
|
|
94
94
|
font-size: var(--font-size-sm);
|
|
95
|
-
width:
|
|
95
|
+
width: 500px;
|
|
96
96
|
|
|
97
97
|
&::placeholder { color: var(--color-text-muted); }
|
|
98
98
|
&:focus { outline: 1px solid var(--color-accent); border-color: var(--color-accent); }
|
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* mailx postinstall —
|
|
3
|
+
* mailx postinstall — sets binary permissions on Linux/Mac.
|
|
4
|
+
* Tries shared rust-builder; falls back to inline logic if not available.
|
|
4
5
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
try {
|
|
7
|
+
const { runPostinstall } = await import("@bobfrankston/rust-builder/postinstall");
|
|
8
|
+
const path = await import("path");
|
|
9
|
+
const { fileURLToPath } = await import("url");
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
runPostinstall({
|
|
13
|
+
binaryName: "mailx-app",
|
|
14
|
+
binDir: path.join(__dirname, "..", "bin"),
|
|
15
|
+
binaries: {
|
|
16
|
+
win32: "mailx-app.exe",
|
|
17
|
+
darwin: "mailx-app",
|
|
18
|
+
darwinArm64: "mailx-app-arm64",
|
|
19
|
+
linux: "mailx-app-linux",
|
|
20
|
+
linuxArm64: "mailx-app-linux-aarch64",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
} catch {
|
|
24
|
+
// rust-builder not available (e.g., local dev with file: deps)
|
|
25
|
+
// On Windows, nothing to do. On Linux/Mac, try chmod directly.
|
|
26
|
+
if (process.platform !== "win32") {
|
|
27
|
+
const fs = await import("fs");
|
|
28
|
+
const path = await import("path");
|
|
29
|
+
const { fileURLToPath } = await import("url");
|
|
30
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
31
|
+
const arch = process.arch;
|
|
32
|
+
const name = process.platform === "darwin"
|
|
33
|
+
? (arch === "arm64" ? "mailx-app-arm64" : "mailx-app")
|
|
34
|
+
: (arch === "arm64" ? "mailx-app-linux-aarch64" : "mailx-app-linux");
|
|
35
|
+
const bin = path.join(__dirname, "..", "bin", name);
|
|
36
|
+
try { fs.chmodSync(bin, 0o755); } catch { /* binary may not exist */ }
|
|
37
|
+
}
|
|
38
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -44,6 +44,9 @@
|
|
|
44
44
|
],
|
|
45
45
|
"author": "Bob Frankston",
|
|
46
46
|
"license": "MIT",
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
47
50
|
"repository": {
|
|
48
51
|
"type": "git",
|
|
49
52
|
"url": "https://github.com/BobFrankston/mailx.git"
|
|
@@ -233,6 +233,14 @@ process.on("SIGTERM", shutdown);
|
|
|
233
233
|
process.on("unhandledRejection", (err) => {
|
|
234
234
|
console.error("Unhandled rejection:", err?.message || err);
|
|
235
235
|
});
|
|
236
|
+
process.on("uncaughtException", (err) => {
|
|
237
|
+
console.error("FATAL uncaught exception:", err.message);
|
|
238
|
+
console.error(err.stack);
|
|
239
|
+
// Don't exit — let node --watch handle restart
|
|
240
|
+
});
|
|
241
|
+
process.on("exit", (code) => {
|
|
242
|
+
console.log(`Process exiting with code ${code}`);
|
|
243
|
+
});
|
|
236
244
|
// ── Entry Point ──
|
|
237
245
|
await start();
|
|
238
246
|
export { app, start };
|