summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-11 13:57:44 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-11 13:57:44 +0200
commit7adbf5163f0193d80bb5578dc875eba811f864bb (patch)
tree95b6fc62835536a41f1fc47e646e904b27f9a042 /packages/meshbay-hub/src
parent4b74ff3fc54f35fbc41d9c0f7d2b4ec89475154a (diff)
downloadmeshbay-7adbf5163f0193d80bb5578dc875eba811f864bb.tar.gz
fix(hub): the asset fingerprint covers every file under static/
Everything under static/ is served at /a/<hash>/ with a year's `immutable`, but the hash was computed from a hand-kept list of 43 top-level modules. The ten catalogues and vendor/ were not on it, nor was anything the guarding test could see: it globbed *.js at the top level only. A change confined to the catalogues therefore kept the hash, and a phone went on showing a heading that had been rewritten and deployed - pull-to-refresh fetched the no-store shell, which was current, and never refetched en.js at a URL that had not moved. The fingerprint now hashes every file under static/, path and content, so a change, a rename or a new file moves the version with nothing to register. _ASSETS is gone, and CLAUDE.md, MESHBAY_DESIGN.md 9.4 step 6, assets/brand/README.md and docs/playlists.md no longer ask for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D9MCBBWSm9GhBESmqzJxNy
Diffstat (limited to 'packages/meshbay-hub/src')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/webapp.py52
1 files changed, 14 insertions, 38 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/webapp.py b/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
index 05485e0..6ac5cdb 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
@@ -21,40 +21,9 @@ STATIC_DIR = Path(__file__).parent.parent / "static"
router = APIRouter(tags=["webapp"])
-# Assets the shell pulls in, in load order. Everything else is imported by
-# app.js from a relative path, which inherits the `/a/<hash>/` prefix the shell
-# loaded app.js under — so the whole module graph moves together.
-# Every module the page loads. A file missing from here is a file whose change
-# does not move the URL, so a browser holding the old one never asks for it —
-# which is the failure this list exists to prevent, and it is silent.
-_ASSETS = ("style.css", "keyderive.js", "crypto.js", "transport.js", "app.js",
- "i18n.js", "downloads.js", "transfers.js", "zipstream.js",
- "platform.js", "meshbay-m.png",
- # Split out of app.js by the group-page refactor — each imported by
- # app.js or group-page.js, so a change to any of them is a change
- # to what the browser must fetch.
- "icon.js", "file-utils.js", "hub-client.js", "apps.js",
- "source-merge.js", "sticky.js",
- "chat-app.js", "files-app.js", "video-player.js", "video-app.js",
- "music-app.js", "music-player.js", "photos-app.js",
- "group-settings.js", "group-page.js",
- # The per-app settings architecture (docs/refactor-groups.md §3):
- # the shared widgets, the folder picker, and one settings pane per
- # app. Reached through the `apps.js` registry rather than imported
- # by name anywhere, which is exactly why they have to be listed —
- # nothing else would notice one of them changing.
- "settings-ui.js", "folder-tree.js",
- "chat-app-settings.js", "video-app-settings.js",
- "music-app-settings.js", "photos-app-settings.js",
- # The reference app (docs/refactor-groups.md §4.1). Hidden behind
- # `?dev=1` client-side, but it is still served and still cached, so
- # it participates in the hash like anything else here.
- "helloworld-app.js", "helloworld-app-settings.js",
- # Pages extracted from app.js — statically imported or lazy-loaded,
- # but all must participate in the content hash.
- "auth-page.js", "explore-page.js", "create-group-page.js",
- "admin-page.js", "node-page.js", "group-name.js",
- "search-page.js", "settings-page.js", "profile-page.js")
+# The shell loads a few assets by name; everything else is imported by app.js
+# from a relative path, which inherits the `/a/<hash>/` prefix the shell loaded
+# app.js under — so the whole module graph moves together.
def _asset_version() -> str:
@@ -67,12 +36,19 @@ def _asset_version() -> str:
for as long as that lasts, which is indistinguishable from the fix not
working. Changing the URL is the only thing that reaches such a browser,
and a content hash changes it exactly when the content changes.
+
+ **Every file under the static directory**, because that is what `/a/<hash>/`
+ serves — with a year's `immutable`, so a file outside the hash is a file a
+ browser never fetches again. The fingerprint used to cover a hand-kept list
+ of top-level modules, and the translations and `vendor/` were not on it: a
+ change confined to the catalogues kept the hash, and a phone went on showing
+ a heading that had been rewritten and deployed. The path is hashed with the
+ content, so a rename or a new file moves the version too.
"""
h = hashlib.sha256()
- for name in _ASSETS:
- path = STATIC_DIR / name
- if path.exists():
- h.update(path.read_bytes())
+ for path in sorted(p for p in STATIC_DIR.rglob("*") if p.is_file()):
+ h.update(path.relative_to(STATIC_DIR).as_posix().encode() + b"\0")
+ h.update(path.read_bytes())
return h.hexdigest()[:12]