"""Build the content-addressed browser shell served by the dashboard.""" from __future__ import annotations import gzip import hashlib import re from dataclasses import dataclass from pathlib import Path SCRIPT_TAG = re.compile(r'^$', re.MULTILINE) WORKER_RUNTIME_SOURCE = "static/background-issue-sync.js" @dataclass(frozen=True) class FrontendBuild: dashboard_html: str runtime_bytes: bytes runtime_gzip_bytes: bytes runtime_digest: str runtime_name: str page_sources: tuple[str, ...] service_worker_source: str def _bundle(frontend_dir: Path, sources: tuple[str, ...]) -> bytes: chunks = [] for source in sources: path = frontend_dir / source.removeprefix("static/") chunks.append(f"/* {source} */\n".encode() + path.read_bytes() + b"\n;\n") return b"".join(chunks) def build_frontend(frontend_dir: Path) -> FrontendBuild: """Return one internally consistent HTML, runtime, and offline-worker build.""" source_html = (frontend_dir / "index.html").read_text() sources = tuple(SCRIPT_TAG.findall(source_html)) if not sources: raise ValueError("dashboard entry document has no page scripts") runtime = _bundle(frontend_dir, sources) runtime_gzip = gzip.compress(runtime, compresslevel=6, mtime=0) digest = hashlib.sha256(runtime).hexdigest()[:16] runtime_name = f"runtime-{digest}.js" dashboard_html = SCRIPT_TAG.sub("", source_html) dashboard_html = dashboard_html.replace( "", f'\n' ) worker = (frontend_dir / "service-worker.js").read_text() for source in sources: if source != WORKER_RUNTIME_SOURCE: worker = worker.replace(f" BASE + '{source}',\n", "") worker = worker.replace( " BASE + 'static/dashboard.css',\n", f" BASE + 'static/dashboard.css',\n BASE + '{runtime_name}',\n", ) worker = re.sub( r"const CACHE = 'stackchain-dashboard-shell-v\d+';", f"const CACHE = 'stackchain-dashboard-shell-{digest}';", worker, count=1, ) return FrontendBuild( dashboard_html=dashboard_html, runtime_bytes=runtime, runtime_gzip_bytes=runtime_gzip, runtime_digest=digest, runtime_name=runtime_name, page_sources=sources, service_worker_source=worker, )