feat(axum): add safe html page composition
Add a SafeHtml PageRequest path for shell/partial composition and migrate examples so rendered hemplate fragments stay typed through the transport boundary instead of round-tripping through unchecked strings. req: axum_integration/001 req: html_safety/001 req: html_safety/002
This commit is contained in:
@@ -223,7 +223,7 @@ async fn main() {
|
||||
async fn home(State(state): State<Arc<Shared>>, request: PageRequest) -> impl IntoResponse {
|
||||
let demo = state.demo.lock().unwrap().clone();
|
||||
request
|
||||
.page(page_html(&demo), shell)
|
||||
.page_html(page_html(&demo), shell)
|
||||
.title("slhx Techdemo")
|
||||
.fingerprint(ui::BUILD_FINGERPRINT)
|
||||
}
|
||||
@@ -238,7 +238,7 @@ async fn architecture(request: PageRequest) -> impl IntoResponse {
|
||||
island_snapshot: "2|3|21|architecture route · same opaque island bridge".to_owned(),
|
||||
});
|
||||
request
|
||||
.page(body, shell)
|
||||
.page_html(body, shell)
|
||||
.title("slhx Architecture")
|
||||
.fingerprint(ui::BUILD_FINGERPRINT)
|
||||
}
|
||||
@@ -446,8 +446,7 @@ fn island_snapshot(demo: &DemoState) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
fn page_html(demo: &DemoState) -> String {
|
||||
// Explicit full-page composition boundary for already-rendered hemplate fragments.
|
||||
fn page_html(demo: &DemoState) -> SafeHtml {
|
||||
// req: html_safety/002 req: view/001
|
||||
render_control_center(ControlCenter {
|
||||
hero: render_hero(demo),
|
||||
@@ -458,10 +457,9 @@ fn page_html(demo: &DemoState) -> String {
|
||||
})
|
||||
}
|
||||
|
||||
fn shell(body: String) -> String {
|
||||
// Explicit full-page shell composition boundary for already-rendered hemplate fragments.
|
||||
// req: html_safety/002 req: view/001 req: component/003
|
||||
ui::app_shell::render(&AppShell { body: SafeHtml::trusted(body) }).into_string()
|
||||
fn shell(body: SafeHtml) -> SafeHtml {
|
||||
// req: html_safety/001 req: html_safety/002 req: axum_integration/001 req: component/003
|
||||
ui::app_shell::render(&AppShell { body })
|
||||
}
|
||||
|
||||
fn hero_view(demo: &DemoState) -> HeroMetrics {
|
||||
@@ -580,12 +578,11 @@ fn architecture_activity() -> SafeHtml {
|
||||
ui::render(&ArchitectureActivity)
|
||||
}
|
||||
|
||||
fn render_control_center(page: ControlCenter) -> String {
|
||||
fn render_control_center(page: ControlCenter) -> SafeHtml {
|
||||
// req: html_safety/002 req: view/001 req: component/003
|
||||
ui::render(&page).into_string()
|
||||
ui::render(&page)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -599,7 +596,7 @@ mod tests {
|
||||
#[test]
|
||||
fn shell_is_composed_by_a_hemplate_view() {
|
||||
let html = shell(page_html(&DemoState::default()));
|
||||
let document = Html::parse_document(&html);
|
||||
let document = Html::parse_document(html.as_str());
|
||||
assert_eq!(
|
||||
document
|
||||
.select(&selector("title"))
|
||||
@@ -624,7 +621,7 @@ mod tests {
|
||||
assert!(!html.contains("__INSPECTOR__"));
|
||||
assert!(!html.contains("__ACTIVITY__"));
|
||||
|
||||
let document = Html::parse_fragment(&html);
|
||||
let document = Html::parse_fragment(html.as_str());
|
||||
assert_eq!(document.select(&selector("[data-slhx-root=\"techdemo\"]")).count(), 1);
|
||||
assert!(document.select(&selector("[data-sid]")).count() >= 7);
|
||||
assert!(document.select(&selector("[data-hid]")).count() >= 7);
|
||||
|
||||
Reference in New Issue
Block a user