@bobfrankston/rmfmail 1.2.69 → 1.2.70

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.
@@ -18,11 +18,52 @@
18
18
  */
19
19
  import * as esbuild from "esbuild";
20
20
  import path from "node:path";
21
+ import fs from "node:fs";
22
+ import crypto from "node:crypto";
21
23
  import { fileURLToPath } from "node:url";
22
24
 
23
25
  const root = path.resolve(fileURLToPath(import.meta.url), "..", "..");
24
26
  const watch = process.argv.includes("--watch");
25
27
 
28
+ /** Cache-bust the bundle references in the HTML entry points with a content
29
+ * hash. The msger custom protocol serves bundles with no cache headers, so
30
+ * WebView2 caches `app.bundle.js` and keeps serving the OLD bundle across
31
+ * daemon relaunches — every client-side fix silently failed to reach the user
32
+ * until the cache happened to evict (Bob 2026-06-26: flag/Ctrl+A fixes "still
33
+ * happening" while the daemon was already on the new version). Stamping a
34
+ * per-build `?v=<hash>` makes the URL change whenever the bundle changes, so
35
+ * the WebView is forced to fetch the new one. */
36
+ function shortHash(file) {
37
+ try { return crypto.createHash("sha1").update(fs.readFileSync(file)).digest("hex").slice(0, 10); }
38
+ catch { return String(Date.now()); }
39
+ }
40
+ function stampHtml(htmlFile, refs) {
41
+ let html;
42
+ try { html = fs.readFileSync(htmlFile, "utf8"); } catch { return; }
43
+ let changed = false;
44
+ for (const { name, file } of refs) {
45
+ const v = shortHash(file);
46
+ // Match the bundle name + optional existing ?v=… up to the closing
47
+ // quote, so re-runs replace the prior hash instead of stacking.
48
+ const re = new RegExp(`(${name.replace(/[.\\/]/g, m => "\\" + m)})(\\?v=[a-z0-9]+)?(")`, "g");
49
+ const next = html.replace(re, `$1?v=${v}$3`);
50
+ if (next !== html) { html = next; changed = true; }
51
+ }
52
+ if (changed) {
53
+ fs.writeFileSync(htmlFile, html);
54
+ console.log(` stamped ${path.relative(root, htmlFile)}`);
55
+ }
56
+ }
57
+ function stampAll() {
58
+ stampHtml(path.join(root, "client", "index.html"), [
59
+ { name: "./app.bundle.js", file: path.join(root, "client", "app.bundle.js") },
60
+ { name: "./android-bootstrap.bundle.js", file: path.join(root, "client", "android-bootstrap.bundle.js") },
61
+ ]);
62
+ stampHtml(path.join(root, "client", "compose", "compose.html"), [
63
+ { name: "compose.bundle.js", file: path.join(root, "client", "compose", "compose.bundle.js") },
64
+ ]);
65
+ }
66
+
26
67
  // Each entry has its own external list because the Android bootstrap bundle
27
68
  // needs to INCLUDE @bobfrankston/mailx-store-web (entire purpose of the
28
69
  // bundle), while app.bundle.js externalizes it so the desktop build doesn't
@@ -132,6 +173,7 @@ async function buildAll() {
132
173
  }
133
174
  const failed = results.filter(r => !r.ok).length;
134
175
  if (failed) process.exit(1);
176
+ stampAll();
135
177
  console.log(`build-bundles: done in ${Date.now() - t0} ms`);
136
178
  }
137
179
 
@@ -557,7 +557,7 @@
557
557
  Replaces a multi-file ES module cascade through msger's custom
558
558
  protocol IPC that was visibly slow on compose-open. Source remains
559
559
  compose.ts; the .js is for fallback / debugging only. -->
560
- <script type="module" src="compose.bundle.js"></script>
560
+ <script type="module" src="compose.bundle.js?v=4c3aed9911"></script>
561
561
  </head>
562
562
  <body>
563
563
  <div class="compose-header">
package/client/index.html CHANGED
@@ -677,7 +677,7 @@
677
677
  // dynamically imported module" bootstrap failure). The bundle inlines
678
678
  // every transitive dep, so no further import-map resolution is needed
679
679
  // after this fetch.
680
- const m = await import("./android-bootstrap.bundle.js");
680
+ const m = await import("./android-bootstrap.bundle.js?v=7e151728fb");
681
681
  window.__btick && window.__btick("android-bootstrap module loaded");
682
682
  await m.initAndroid();
683
683
  window.__btick && window.__btick("initAndroid done");
@@ -692,7 +692,7 @@
692
692
  // bundle is regenerated by `npm run build` via
693
693
  // bin/build-bundles.mjs; dynamic `await import()` of optional
694
694
  // adapters (rmf-tiny, android-bootstrap) stays external.
695
- await import("./app.bundle.js");
695
+ await import("./app.bundle.js?v=14fc25b74f");
696
696
  window.__btick && window.__btick("app.bundle.js loaded");
697
697
  } catch (e) {
698
698
  const msg = "BOOTSTRAP FAILED: " + (e.message || e) + "\n" + (e.stack || "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/rmfmail",
3
- "version": "1.2.69",
3
+ "version": "1.2.70",
4
4
  "description": "Local-first email client with IMAP sync and standalone native app",
5
5
  "type": "module",
6
6
  "main": "bin/mailx.js",