feat(axum): expose hashed runtime asset path

Generate a SHA-256 content hash for the embedded hemx runtime, expose the hashed path from hemx-axum, serve immutable runtime headers, and update examples to load the hemx-owned path instead of fixed /hemx.js URLs.

req: axum_integration/005
This commit is contained in:
slhx agent
2026-06-05 18:25:14 +02:00
parent c56ec39813
commit ce4c2e086d
20 changed files with 204 additions and 36 deletions
+28
View File
@@ -27,6 +27,16 @@ pub const fn runtime_js() -> RuntimeJs {
RuntimeJs
}
// req: axum_integration/005
pub const fn runtime_js_hash() -> &'static str {
hemx_js::RUNTIME_JS_HASH
}
// req: axum_integration/005
pub const fn runtime_js_path() -> &'static str {
hemx_js::RUNTIME_JS_PATH
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PageMode {
Full,
@@ -1382,6 +1392,16 @@ impl IntoResponse for RuntimeJs {
header::CACHE_CONTROL,
HeaderValue::from_static("public, max-age=31536000, immutable"),
);
response.headers_mut().insert(
header::ETAG,
HeaderValue::from_str(&format!("\"{}\"", runtime_js_hash()))
.expect("runtime hash is a valid ETag"),
);
response.headers_mut().insert(
header::CONTENT_LENGTH,
HeaderValue::from_str(hemx_js::RUNTIME_JS.len().to_string().as_str())
.expect("runtime length is a valid header value"),
);
response
}
}
@@ -1390,6 +1410,14 @@ pub fn runtime_js_source() -> &'static str {
hemx_js::RUNTIME_JS
}
pub fn runtime_js_script_src() -> &'static str {
runtime_js_path()
}
pub fn runtime_js_route_path() -> &'static str {
runtime_js_path()
}
// req: push/001, req: push/003, req: push/004
pub fn sse<S, E>(batches: S) -> Sse<impl Stream<Item = Result<Event, E>> + Send>
where
+29 -4
View File
@@ -2,9 +2,10 @@ use axum::extract::State;
use axum::http::{header, HeaderMap};
use axum::response::IntoResponse;
use hemx_axum::{
interactions, runtime_js, DispatchRejection, EffectResponse, Form, HandlerErrorContext,
HandlerFailure, InteractionForm, InteractionFormRejection, InteractionRequest,
IntoHandlerFailure, PageMode, PageRequest, PageResponse, HEMX_CONTENT_TYPE,
interactions, runtime_js, runtime_js_hash, runtime_js_path, runtime_js_route_path,
runtime_js_script_src, runtime_js_source, DispatchRejection, EffectResponse, Form,
HandlerErrorContext, HandlerFailure, InteractionForm, InteractionFormRejection,
InteractionRequest, IntoHandlerFailure, PageMode, PageRequest, PageResponse, HEMX_CONTENT_TYPE,
HEMX_FINGERPRINT_HEADER, HEMX_PARTIAL_HEADER, HEMX_RUNTIME_CONTENT_TYPE, HEMX_TITLE_HEADER,
};
use hemx_core::{push, BuildFingerprint, Handle, IntoEffect, SafeHtml, Slot};
@@ -480,5 +481,29 @@ fn runtime_js_response_serves_embedded_runtime() {
response.headers()[header::CONTENT_TYPE],
HEMX_RUNTIME_CONTENT_TYPE
);
assert!(response.headers().contains_key(header::CACHE_CONTROL));
assert_eq!(
response.headers()[header::CACHE_CONTROL],
"public, max-age=31536000, immutable"
);
assert_eq!(
response.headers()[header::ETAG],
format!("\"{}\"", runtime_js_hash())
);
assert_eq!(
response.headers()[header::CONTENT_LENGTH],
runtime_js_source().len().to_string()
);
}
// req: axum_integration/005
#[test]
fn runtime_js_path_is_content_hashed() {
let path = runtime_js_path();
assert!(path.starts_with("/hemx."));
assert!(path.ends_with(".js"));
assert!(path.contains(runtime_js_hash()));
assert_eq!(runtime_js_script_src(), path);
assert_eq!(runtime_js_route_path(), path);
assert_eq!(runtime_js_hash().len(), 64);
}