@bobfrankston/rmfmail 1.2.169 → 1.2.171
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/android-bootstrap.bundle.js +111 -40
- package/client/android-bootstrap.bundle.js.map +2 -2
- package/package.json +3 -3
- package/packages/mailx-store-web/package.json +1 -1
- package/packages/mailx-store-web/web-service.d.ts.map +1 -1
- package/packages/mailx-store-web/web-service.js +33 -28
- package/packages/mailx-store-web/web-service.js.map +1 -1
- package/packages/mailx-store-web/web-service.ts +23 -23
- package/packages/mailx-store-web/web-settings.d.ts +12 -1
- package/packages/mailx-store-web/web-settings.d.ts.map +1 -1
- package/packages/mailx-store-web/web-settings.js +110 -15
- package/packages/mailx-store-web/web-settings.js.map +1 -1
- package/packages/mailx-store-web/web-settings.ts +107 -19
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-69656 → node_modules.npmglobalize-stash-3252}/.package-lock.json +0 -0
|
@@ -2588,7 +2588,8 @@ __export(web_settings_exports, {
|
|
|
2588
2588
|
savePreferences: () => savePreferences,
|
|
2589
2589
|
saveSettings: () => saveSettings,
|
|
2590
2590
|
setGDriveFolderId: () => setGDriveFolderId,
|
|
2591
|
-
setGDriveTokenProvider: () => setGDriveTokenProvider
|
|
2591
|
+
setGDriveTokenProvider: () => setGDriveTokenProvider,
|
|
2592
|
+
updateAllowlist: () => updateAllowlist
|
|
2592
2593
|
});
|
|
2593
2594
|
function openSettingsDb() {
|
|
2594
2595
|
return new Promise((resolve, reject) => {
|
|
@@ -2665,17 +2666,28 @@ async function cloudStat(filename) {
|
|
|
2665
2666
|
return null;
|
|
2666
2667
|
}
|
|
2667
2668
|
}
|
|
2669
|
+
async function gDriveFindFile(filename, token) {
|
|
2670
|
+
const q = encodeURIComponent(`name='${filename}' and '${gDriveFolderId}' in parents and trashed=false`);
|
|
2671
|
+
const res = await globalThis.fetch(`https://www.googleapis.com/drive/v3/files?q=${q}&fields=files(id,modifiedTime,ownedByMe,capabilities(canEdit))`, { headers: { "Authorization": `Bearer ${token}` } });
|
|
2672
|
+
if (!res.ok)
|
|
2673
|
+
return null;
|
|
2674
|
+
const files = (await res.json()).files || [];
|
|
2675
|
+
if (files.length === 0)
|
|
2676
|
+
return null;
|
|
2677
|
+
if (files.length > 1) {
|
|
2678
|
+
console.warn(`[settings] ${filename}: ${files.length} same-named files in the shared folder \u2014 using own/newest. Clean the duplicates (owners differ?).`);
|
|
2679
|
+
files.sort((a, b) => (b.ownedByMe === true ? 1 : 0) - (a.ownedByMe === true ? 1 : 0) || String(b.modifiedTime).localeCompare(String(a.modifiedTime)));
|
|
2680
|
+
}
|
|
2681
|
+
const f = files[0];
|
|
2682
|
+
return { id: f.id, modifiedTime: f.modifiedTime || "", ownedByMe: f.ownedByMe === true, capabilitiesCanEdit: f.capabilities?.canEdit !== false };
|
|
2683
|
+
}
|
|
2668
2684
|
async function gDriveRead(filename) {
|
|
2669
2685
|
if (!tokenProvider || !gDriveFolderId)
|
|
2670
2686
|
return null;
|
|
2671
2687
|
try {
|
|
2672
2688
|
const token = await tokenProvider();
|
|
2673
|
-
const
|
|
2674
|
-
const
|
|
2675
|
-
if (!listRes.ok)
|
|
2676
|
-
return null;
|
|
2677
|
-
const listData = await listRes.json();
|
|
2678
|
-
const fileId = listData.files?.[0]?.id;
|
|
2689
|
+
const found = await gDriveFindFile(filename, token);
|
|
2690
|
+
const fileId = found?.id;
|
|
2679
2691
|
if (!fileId)
|
|
2680
2692
|
return null;
|
|
2681
2693
|
const res = await globalThis.fetch(`https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`, { headers: { "Authorization": `Bearer ${token}` } });
|
|
@@ -2692,13 +2704,13 @@ async function gDriveWrite(filename, content) {
|
|
|
2692
2704
|
return false;
|
|
2693
2705
|
try {
|
|
2694
2706
|
const token = await tokenProvider();
|
|
2695
|
-
const
|
|
2696
|
-
const
|
|
2697
|
-
if (!listRes.ok)
|
|
2698
|
-
return false;
|
|
2699
|
-
const listData = await listRes.json();
|
|
2700
|
-
const fileId = listData.files?.[0]?.id;
|
|
2707
|
+
const found = await gDriveFindFile(filename, token);
|
|
2708
|
+
const fileId = found?.id;
|
|
2701
2709
|
if (fileId) {
|
|
2710
|
+
if (!found.capabilitiesCanEdit) {
|
|
2711
|
+
console.error(`[settings] ${filename}: existing shared file is not editable by this account \u2014 NOT creating a duplicate. Change kept locally.`);
|
|
2712
|
+
return false;
|
|
2713
|
+
}
|
|
2702
2714
|
const res = await globalThis.fetch(`https://www.googleapis.com/upload/drive/v3/files/${fileId}?uploadType=media`, {
|
|
2703
2715
|
method: "PATCH",
|
|
2704
2716
|
headers: {
|
|
@@ -2707,6 +2719,8 @@ async function gDriveWrite(filename, content) {
|
|
|
2707
2719
|
},
|
|
2708
2720
|
body: content
|
|
2709
2721
|
});
|
|
2722
|
+
if (!res.ok)
|
|
2723
|
+
console.error(`[settings] ${filename}: cloud update failed (${res.status})`);
|
|
2710
2724
|
return res.ok;
|
|
2711
2725
|
} else {
|
|
2712
2726
|
const metadata = JSON.stringify({
|
|
@@ -2816,6 +2830,22 @@ function parseJsonc(text) {
|
|
|
2816
2830
|
stripped = stripped.replace(/,(\s*[}\]])/g, "$1");
|
|
2817
2831
|
return JSON.parse(stripped);
|
|
2818
2832
|
}
|
|
2833
|
+
function refreshSharedFile(filename, cached) {
|
|
2834
|
+
const now = Date.now();
|
|
2835
|
+
if (now - (__lastSharedRefresh.get(filename) || 0) < SHARED_REFRESH_MS)
|
|
2836
|
+
return;
|
|
2837
|
+
__lastSharedRefresh.set(filename, now);
|
|
2838
|
+
void (async () => {
|
|
2839
|
+
try {
|
|
2840
|
+
const fresh = await gDriveRead(filename);
|
|
2841
|
+
if (fresh && fresh !== cached) {
|
|
2842
|
+
await idbWrite(filename, fresh);
|
|
2843
|
+
console.log(`[settings] ${filename} refreshed from GDrive (shared copy changed)`);
|
|
2844
|
+
}
|
|
2845
|
+
} catch {
|
|
2846
|
+
}
|
|
2847
|
+
})();
|
|
2848
|
+
}
|
|
2819
2849
|
async function loadAccounts() {
|
|
2820
2850
|
const cached = await idbRead("accounts.jsonc");
|
|
2821
2851
|
if (cached) {
|
|
@@ -2864,6 +2894,7 @@ async function saveAccounts(accounts) {
|
|
|
2864
2894
|
async function loadPreferences() {
|
|
2865
2895
|
const cached = await idbRead("preferences.jsonc");
|
|
2866
2896
|
if (cached) {
|
|
2897
|
+
refreshSharedFile("preferences.jsonc", cached);
|
|
2867
2898
|
try {
|
|
2868
2899
|
const data = parseJsonc(cached);
|
|
2869
2900
|
return {
|
|
@@ -2915,6 +2946,7 @@ async function saveSettings(settings) {
|
|
|
2915
2946
|
async function loadAllowlist() {
|
|
2916
2947
|
const cached = await idbRead("allowlist.jsonc");
|
|
2917
2948
|
if (cached) {
|
|
2949
|
+
refreshSharedFile("allowlist.jsonc", cached);
|
|
2918
2950
|
try {
|
|
2919
2951
|
return parseJsonc(cached);
|
|
2920
2952
|
} catch {
|
|
@@ -2930,9 +2962,43 @@ async function loadAllowlist() {
|
|
|
2930
2962
|
}
|
|
2931
2963
|
return { ...DEFAULT_ALLOWLIST };
|
|
2932
2964
|
}
|
|
2965
|
+
function allowlistEntryCount(l) {
|
|
2966
|
+
return ALLOWLIST_KEYS.reduce((n, k) => n + (Array.isArray(l?.[k]) ? l[k].length : 0), 0);
|
|
2967
|
+
}
|
|
2968
|
+
async function updateAllowlist(mutate) {
|
|
2969
|
+
let base = null;
|
|
2970
|
+
try {
|
|
2971
|
+
const fresh = await gDriveRead("allowlist.jsonc");
|
|
2972
|
+
if (fresh) {
|
|
2973
|
+
await idbWrite("allowlist.jsonc", fresh);
|
|
2974
|
+
try {
|
|
2975
|
+
base = parseJsonc(fresh);
|
|
2976
|
+
} catch {
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
} catch {
|
|
2980
|
+
}
|
|
2981
|
+
if (!base)
|
|
2982
|
+
base = await loadAllowlist();
|
|
2983
|
+
const out = mutate(base) || base;
|
|
2984
|
+
await saveAllowlist(out);
|
|
2985
|
+
return out;
|
|
2986
|
+
}
|
|
2933
2987
|
async function saveAllowlist(list) {
|
|
2934
2988
|
const content = JSON.stringify(list, null, 2);
|
|
2935
2989
|
await idbWrite("allowlist.jsonc", content);
|
|
2990
|
+
try {
|
|
2991
|
+
const cloudRaw = await gDriveRead("allowlist.jsonc");
|
|
2992
|
+
if (cloudRaw) {
|
|
2993
|
+
const cloudCount = allowlistEntryCount(parseJsonc(cloudRaw));
|
|
2994
|
+
const newCount = allowlistEntryCount(list);
|
|
2995
|
+
if (cloudCount > 10 && newCount < cloudCount / 2) {
|
|
2996
|
+
console.error(`[settings] REFUSING allowlist cloud write: ${cloudCount} \u2192 ${newCount} entries (stale/default base \u2014 see 2026-07-23 wipe). Local cache updated only.`);
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
} catch {
|
|
3001
|
+
}
|
|
2936
3002
|
await gDriveWrite("allowlist.jsonc", content);
|
|
2937
3003
|
}
|
|
2938
3004
|
async function loadAutocomplete() {
|
|
@@ -3002,7 +3068,7 @@ async function loadDeviceState() {
|
|
|
3002
3068
|
}
|
|
3003
3069
|
return {};
|
|
3004
3070
|
}
|
|
3005
|
-
var IDB_NAME2, IDB_VERSION, STORE_NAME2, gDriveFolderId, gDriveFolderName, gDriveFolderPath, gDriveFolderOwner, tokenProvider, PROVIDERS, DEFAULT_PREFERENCES, DEFAULT_ALLOWLIST, DEVICE_ID_KEY;
|
|
3071
|
+
var IDB_NAME2, IDB_VERSION, STORE_NAME2, gDriveFolderId, gDriveFolderName, gDriveFolderPath, gDriveFolderOwner, tokenProvider, PROVIDERS, DEFAULT_PREFERENCES, DEFAULT_ALLOWLIST, SHARED_REFRESH_MS, __lastSharedRefresh, ALLOWLIST_KEYS, DEVICE_ID_KEY;
|
|
3006
3072
|
var init_web_settings = __esm({
|
|
3007
3073
|
"packages/mailx-store-web/web-settings.js"() {
|
|
3008
3074
|
"use strict";
|
|
@@ -3077,6 +3143,9 @@ var init_web_settings = __esm({
|
|
|
3077
3143
|
flaggedSenders: [],
|
|
3078
3144
|
flaggedDomains: []
|
|
3079
3145
|
};
|
|
3146
|
+
SHARED_REFRESH_MS = 5 * 6e4;
|
|
3147
|
+
__lastSharedRefresh = /* @__PURE__ */ new Map();
|
|
3148
|
+
ALLOWLIST_KEYS = ["senders", "domains", "recipients", "flaggedSenders", "flaggedDomains"];
|
|
3080
3149
|
DEVICE_ID_KEY = "mailx-device-id";
|
|
3081
3150
|
}
|
|
3082
3151
|
});
|
|
@@ -5919,23 +5988,24 @@ var WebMailxService = class _WebMailxService {
|
|
|
5919
5988
|
* flaggedSenders / flaggedDomains in allowlist.jsonc, which syncs
|
|
5920
5989
|
* back to the cloud copy. */
|
|
5921
5990
|
async flagSenderOrDomain(type, value) {
|
|
5922
|
-
const list = await loadAllowlist();
|
|
5923
|
-
const key = type === "sender" ? "flaggedSenders" : "flaggedDomains";
|
|
5924
|
-
const arr = Array.isArray(list[key]) ? list[key] : [];
|
|
5925
5991
|
const v = (value || "").trim().toLowerCase();
|
|
5926
5992
|
if (!v)
|
|
5927
5993
|
return { flagged: false };
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
arr.
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5994
|
+
let flagged = false;
|
|
5995
|
+
await updateAllowlist((list) => {
|
|
5996
|
+
const key = type === "sender" ? "flaggedSenders" : "flaggedDomains";
|
|
5997
|
+
const arr = Array.isArray(list[key]) ? list[key] : [];
|
|
5998
|
+
const idx = arr.findIndex((x) => (x || "").toLowerCase() === v);
|
|
5999
|
+
if (idx >= 0) {
|
|
6000
|
+
arr.splice(idx, 1);
|
|
6001
|
+
flagged = false;
|
|
6002
|
+
} else {
|
|
6003
|
+
arr.push(v);
|
|
6004
|
+
flagged = true;
|
|
6005
|
+
}
|
|
6006
|
+
list[key] = arr;
|
|
6007
|
+
return list;
|
|
6008
|
+
});
|
|
5939
6009
|
return { flagged };
|
|
5940
6010
|
}
|
|
5941
6011
|
async updateFlags(accountId, uid, flags) {
|
|
@@ -5944,18 +6014,19 @@ var WebMailxService = class _WebMailxService {
|
|
|
5944
6014
|
}
|
|
5945
6015
|
// ── Remote content allow-list ──
|
|
5946
6016
|
async allowRemoteContent(type, value) {
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
6017
|
+
await updateAllowlist((list) => {
|
|
6018
|
+
if (type === "sender" && !list.senders.includes(value))
|
|
6019
|
+
list.senders.push(value);
|
|
6020
|
+
else if (type === "domain" && !list.domains.includes(value))
|
|
6021
|
+
list.domains.push(value);
|
|
6022
|
+
else if (type === "recipient") {
|
|
6023
|
+
if (!list.recipients)
|
|
6024
|
+
list.recipients = [];
|
|
6025
|
+
if (!list.recipients.includes(value))
|
|
6026
|
+
list.recipients.push(value);
|
|
6027
|
+
}
|
|
6028
|
+
return list;
|
|
6029
|
+
});
|
|
5959
6030
|
}
|
|
5960
6031
|
// ── Search ──
|
|
5961
6032
|
async search(q, page = 1, pageSize = 50, scope = "all", accountId, folderId) {
|