diff --git a/app.py b/app.py index 1314d58..838a0de 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,23 @@ # app.py — slim orchestrator +import mimetypes import os + +def register_static_mime_types() -> None: + """Force stable JS module MIME types across platforms. + + Some native Windows setups inherit stale/incorrect registry mappings for + ``.js``/``.mjs``, which can make Starlette serve ES modules with a non-JS + ``Content-Type`` and cause the UI to load but fail on click. Re-register the + standard MIME types at startup so static assets are served consistently. + """ + + mimetypes.add_type("text/javascript", ".js") + mimetypes.add_type("application/javascript", ".mjs") + + +register_static_mime_types() + # Windows: force HuggingFace/fastembed to COPY model files instead of symlinking. # On a network-share/UNC data dir Windows can't follow HF's symlinks ([WinError # 1463]), so the ONNX embedding model fails to load. huggingface_hub reads this diff --git a/tests/test_app_static_mime.py b/tests/test_app_static_mime.py new file mode 100644 index 0000000..a7ff476 --- /dev/null +++ b/tests/test_app_static_mime.py @@ -0,0 +1,37 @@ +import ast +import mimetypes +from pathlib import Path + + +def _load_register_static_mime_types(): + app_path = Path(__file__).resolve().parents[1] / "app.py" + tree = ast.parse(app_path.read_text(encoding="utf-8"), filename=str(app_path)) + fn = next(node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name == "register_static_mime_types") + module = ast.Module(body=[fn], type_ignores=[]) + ns = {"mimetypes": mimetypes} + exec(compile(module, str(app_path), "exec"), ns) + return ns["register_static_mime_types"] + + +def test_register_static_mime_types_restores_js_module_types(): + register_static_mime_types = _load_register_static_mime_types() + original_js = mimetypes.types_map.get(".js") + original_mjs = mimetypes.types_map.get(".mjs") + try: + mimetypes.types_map[".js"] = "text/plain" + mimetypes.types_map.pop(".mjs", None) + + register_static_mime_types() + + assert mimetypes.types_map[".js"] == "text/javascript" + assert mimetypes.types_map[".mjs"] == "application/javascript" + finally: + if original_js is None: + mimetypes.types_map.pop(".js", None) + else: + mimetypes.types_map[".js"] = original_js + + if original_mjs is None: + mimetypes.types_map.pop(".mjs", None) + else: + mimetypes.types_map[".mjs"] = original_mjs