perf(audit): emojibase lazy-split, SW precache, Prism subset, lazy images

- emojibase (~965 KB) is now fully lazy: plugins/emoji.ts loads compact data +
  shortcode maps via a memoized dynamic import (rejections reset the memo so a
  mid-deploy chunk 404 can retry); reaction labels degrade to the raw glyph
  until loaded. Consumers get FRESH array references on load (the module arrays
  populate in place — same-ref state updates would skip re-render and leave
  emoji search empty; reviewer-caught). Verified out of the eager graph.
- Service worker precaches hashed assets (workbox precacheAndRoute, 82 entries
  ~10.8 MB incl. the crypto wasm): repeat visits stop re-downloading the app.
  index.html is NOT precached — navigations stay network-first so deploys are
  picked up immediately; the media-auth fetch handler is untouched.
- ReactPrism: curated 21-language set — chunk 574 KB → 71 KB.
- Timeline inline images get loading="lazy".
- Removed dead dompurify (+types); sanitize-html is the real sanitizer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 00:19:16 -04:00
parent 664dcd4cd8
commit 96f7187031
11 changed files with 268 additions and 408 deletions
+33 -1
View File
@@ -1,7 +1,39 @@
/// <reference lib="WebWorker" />
import { precacheAndRoute, type PrecacheEntry } from 'workbox-precaching';
export type {};
declare const self: ServiceWorkerGlobalScope;
declare const self: ServiceWorkerGlobalScope & {
// Replaced at build time by vite-plugin-pwa (injectManifest) with the list of
// hashed build assets to precache. See vite.config.js VitePWA injectManifest.
__WB_MANIFEST: Array<string | PrecacheEntry>;
};
/**
* PRECACHE (workbox-precaching). `self.__WB_MANIFEST` is replaced at build time
* by vite-plugin-pwa with the list of hashed build assets
* (assets/**\/*.{js,css,wasm}; see vite.config.js injectManifest.globPatterns).
*
* DEPLOY-SAFETY INVARIANTS (do not break):
* 1. index.html / navigations are NEVER precached or precache-routed. The
* manifest globs only `assets/**` (content-hashed), so index.html (served
* from the app root) is absent from it and navigation requests fall through
* to the network — a new deploy is picked up immediately, no stale SPA
* shell. We deliberately do NOT register a navigation route /
* createHandlerBoundToURL fallback.
* 2. precacheAndRoute only matches its own manifest URLs (same-origin hashed
* assets). It never matches the media-auth paths handled by the fetch
* listener below — those are cross-origin homeserver URLs absent from the
* manifest — so the existing media fetch behaviour is fully preserved. It
* is registered before that listener; for a media request the precache
* route finds no match and does not call respondWith, so the media handler
* still runs.
* 3. Assets are content-hashed, so a changed asset ships under a new filename;
* PrecacheController drops entries no longer in the current manifest on
* activate, so the precache self-updates each deploy without unbounded
* growth.
*/
precacheAndRoute(self.__WB_MANIFEST);
type SessionInfo = {
accessToken: string;