crates/knut-wasm-host/src/media.rs
//! Extension → content type. A small, honest table, and no fallback.
//!
//! The host never looks at a file *name* — `index.html`, `facet_wrapped.js` and
//! `facet_wrapped_bg.wasm` are a component library's business, not a byte server's.
//! It looks at the extension, and if the extension is not in this table it REFUSES.
//!
//! Two things a fallback would have bought, and why neither is worth it:
//!
//! * `application/octet-stream` is a lie a browser acts on — a `.wasm` served that
//! way fails `WebAssembly.instantiateStreaming` with a message about MIME types
//! that reads like a bug in the wasm.
//! * anything text-ish is a hole: a bundle directory is a directory, and a
//! directory acquires files. Serving whatever turns up, typed by guess, is how a
//! static route becomes an exfiltration route.
use std::path::Path;
/// Every extension this host will serve, and what it serves it as. A wasm bundle
/// ships the first four; wasm-bindgen can add the last two (a themed loader's CSS,
/// a source map).
pub const MEDIA_TYPES: [(&str, &str); 7] = [
("html", "text/html; charset=utf-8"),
("js", "text/javascript; charset=utf-8"),
("mjs", "text/javascript; charset=utf-8"),
("wasm", "application/wasm"),
("json", "application/json"),
("map", "application/json"),
("css", "text/css; charset=utf-8"),
];
/// The content type for `path`'s extension, or `None` — which the caller must turn
/// into a refusal, never into a guess.
///
/// A leading-dot name (`.env`) has no extension as far as `Path` is concerned, and
/// that is the right answer here too: it is a name, not a type.
#[must_use]
pub fn content_type(path: &Path) -> Option<&'static str> {
let ext = path.extension()?.to_str()?;
MEDIA_TYPES
.iter()
.find(|(e, _)| ext.eq_ignore_ascii_case(e))
.map(|(_, ty)| *ty)
}
/// The servable extensions, comma-separated — for a refusal that says what the host
/// *would* have served.
#[must_use]
pub fn known_extensions() -> String {
let mut seen: Vec<&str> = Vec::with_capacity(MEDIA_TYPES.len());
for (ext, _) in MEDIA_TYPES {
if !seen.contains(&ext) {
seen.push(ext);
}
}
seen.join(", ")
}