@bobfrankston/rmfmail 1.1.212 → 1.1.214
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.bundle.js +16 -1
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +23 -2
- package/client/app.js.map +1 -1
- package/client/app.ts +17 -2
- package/client/lib/mailxapi.js +54 -12
- package/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-58140 → node_modules.npmglobalize-stash-57760}/.package-lock.json +0 -0
package/client/app.ts
CHANGED
|
@@ -4750,9 +4750,24 @@ optAutomarkDelay?.addEventListener("change", () => {
|
|
|
4750
4750
|
declare const mailxapi: { isApp: boolean; platform: string; ensureServer: () => Promise<boolean>; getVersion: () => Promise<any> } | undefined;
|
|
4751
4751
|
const isApp = typeof mailxapi !== "undefined" && mailxapi?.isApp;
|
|
4752
4752
|
|
|
4753
|
-
// Wait for server ready signal, then fetch version
|
|
4754
|
-
|
|
4753
|
+
// Wait for server ready signal, then fetch version. getVersion is idempotent
|
|
4754
|
+
// and trivial; msger occasionally drops the startup IPC (see api-client note),
|
|
4755
|
+
// so retry with backoff rather than leaving "(??)" + a "service error" banner
|
|
4756
|
+
// up forever for a cosmetic version read (Bob 2026-06-02). A short per-action
|
|
4757
|
+
// timeout (mailxapi.js) makes each failed attempt give up in 5s.
|
|
4758
|
+
async function getVersionWithRetry(attempts = 5): Promise<any> {
|
|
4759
|
+
for (let i = 0; i < attempts; i++) {
|
|
4760
|
+
try { return await getVersion(); }
|
|
4761
|
+
catch (e) {
|
|
4762
|
+
if (i === attempts - 1) { console.warn("getVersion failed after retries:", e); return {}; }
|
|
4763
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
return {};
|
|
4767
|
+
}
|
|
4768
|
+
const versionPromise = getVersionWithRetry();
|
|
4755
4769
|
versionPromise.then((d: any) => {
|
|
4770
|
+
if (!d || !d.version) return; // all retries dropped — leave the prior text, no scary banner
|
|
4756
4771
|
const els = document.querySelectorAll<HTMLElement>(".app-version");
|
|
4757
4772
|
const storage = d.storage || {};
|
|
4758
4773
|
const storageLabel = storage.provider && storage.provider !== "local"
|
package/client/lib/mailxapi.js
CHANGED
|
@@ -53,27 +53,67 @@
|
|
|
53
53
|
// turned a slow-but-fine search into "mailxapi timeout:
|
|
54
54
|
// searchMessages". Local search stays fast and well under any limit.
|
|
55
55
|
searchMessages: 120000,
|
|
56
|
+
// getVersion reads package.json — instant. With the startup-race
|
|
57
|
+
// re-send above (it's in RESENDABLE) a dropped post is re-tried every
|
|
58
|
+
// 2s, so a 5s ceiling normally allows ~2 re-sends before giving up —
|
|
59
|
+
// enough to ride out the brief window before the Rust→daemon pipe is
|
|
60
|
+
// wired. Short ceiling so a genuinely wedged daemon surfaces fast
|
|
61
|
+
// rather than hanging the version display for 30s.
|
|
62
|
+
getVersion: 5000,
|
|
56
63
|
};
|
|
64
|
+
// Actions that are PURE READS and therefore safe to auto-re-send if the
|
|
65
|
+
// first post is lost. Why a post gets lost: at startup the WebView can
|
|
66
|
+
// call `window.ipc.postMessage` after `window.ipc` is injected but BEFORE
|
|
67
|
+
// the Rust host has wired its message handler to the daemon's stdin — the
|
|
68
|
+
// post is accepted by WebView2 and silently dropped downstream, so no
|
|
69
|
+
// response ever comes and the callback times out. That's the real cause of
|
|
70
|
+
// "getVersion got dropped" (Bob 2026-06-03) — a startup race in our own
|
|
71
|
+
// bridge, not a mysterious msger behavior. Re-sending a read is harmless
|
|
72
|
+
// (the daemon ignores a duplicate response — _msgapiServiceResolve no-ops
|
|
73
|
+
// when the callback is already gone). WRITES are NOT listed: re-sending a
|
|
74
|
+
// send/saveDraft/delete/move/flag could double-apply.
|
|
75
|
+
var RESENDABLE = {
|
|
76
|
+
getVersion: 1, getAccounts: 1, getFolders: 1, getUnifiedInbox: 1, getMessages: 1,
|
|
77
|
+
getSettings: 1, getAutocompleteSettings: 1, getDiagnostics: 1, getOutboxStatus: 1,
|
|
78
|
+
getSyncPending: 1, getCalendars: 1, getCalendarEvents: 1, getTasks: 1,
|
|
79
|
+
getPrimaryAccount: 1, getThreadMessages: 1, getPriorityLists: 1
|
|
80
|
+
};
|
|
81
|
+
var RESEND_INTERVAL_MS = 2000;
|
|
57
82
|
function callNode(action, params) {
|
|
58
83
|
var id = String(++_callbackId);
|
|
84
|
+
var msgStr = JSON.stringify(Object.assign({ _action: action, _cbid: id }, params || {}));
|
|
59
85
|
return new Promise(function(resolve, reject) {
|
|
60
|
-
|
|
86
|
+
function send() {
|
|
87
|
+
if (window.ipc && window.ipc.postMessage) {
|
|
88
|
+
// Trace BEFORE the call so a slow daemon-receive shows up as
|
|
89
|
+
// ipc-send + N s before the daemon logs `[ipc] ←`. Skip the
|
|
90
|
+
// trace event itself to avoid recursion.
|
|
91
|
+
if (action !== "logClientEvent") _traceIpcSend(action, id);
|
|
92
|
+
window.ipc.postMessage(msgStr);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
var entry = { resolve: resolve, reject: reject, timer: null, resend: null };
|
|
98
|
+
_callbacks[id] = entry;
|
|
99
|
+
entry.timer = setTimeout(function() {
|
|
61
100
|
delete _callbacks[id];
|
|
101
|
+
if (entry.resend) clearInterval(entry.resend);
|
|
62
102
|
reject(new Error("mailxapi timeout: " + action));
|
|
63
103
|
}, ACTION_TIMEOUT_MS[action] || 30000);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
// logs `[ipc] ←`. Skip tracing on the trace event itself so
|
|
70
|
-
// we don't recurse.
|
|
71
|
-
if (action !== "logClientEvent") _traceIpcSend(action, id);
|
|
72
|
-
window.ipc.postMessage(JSON.stringify(msg));
|
|
73
|
-
} else {
|
|
74
|
-
clearTimeout(timer);
|
|
104
|
+
var sent = send();
|
|
105
|
+
if (!sent && !RESENDABLE[action]) {
|
|
106
|
+
// Channel object not injected yet AND not safe to wait/re-send
|
|
107
|
+
// (a write) → fail now rather than risk it.
|
|
108
|
+
clearTimeout(entry.timer);
|
|
75
109
|
delete _callbacks[id];
|
|
76
110
|
reject(new Error("No IPC channel available"));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Re-send pure reads until a response lands or the ceiling fires —
|
|
114
|
+
// heals a startup-race drop (and a not-yet-injected window.ipc).
|
|
115
|
+
if (RESENDABLE[action]) {
|
|
116
|
+
entry.resend = setInterval(send, RESEND_INTERVAL_MS);
|
|
77
117
|
}
|
|
78
118
|
});
|
|
79
119
|
}
|
|
@@ -84,6 +124,7 @@
|
|
|
84
124
|
if (!cb) return;
|
|
85
125
|
delete _callbacks[id];
|
|
86
126
|
clearTimeout(cb.timer);
|
|
127
|
+
if (cb.resend) clearInterval(cb.resend);
|
|
87
128
|
cb.resolve(value);
|
|
88
129
|
};
|
|
89
130
|
|
|
@@ -93,6 +134,7 @@
|
|
|
93
134
|
if (!cb) return;
|
|
94
135
|
delete _callbacks[id];
|
|
95
136
|
clearTimeout(cb.timer);
|
|
137
|
+
if (cb.resend) clearInterval(cb.resend);
|
|
96
138
|
cb.reject(new Error(error));
|
|
97
139
|
};
|
|
98
140
|
|
package/package.json
CHANGED