From ecbdea5cd00f023f3cea0b2e4cb00ee12c513342 Mon Sep 17 00:00:00 2001 From: slhx agent Date: Mon, 13 Jul 2026 09:26:32 +0200 Subject: [PATCH] feat(axum): support axum 0.8 applications Upgrade the integration crate and examples to Axum 0.8 and use the native async extractor traits while preserving hemx routing, request, runtime, and effect behavior. req: axum/002 --- AGENTS.md | 2 +- Cargo.lock | 21 +++++++++------------ REQUIREMENTS.md | 5 ++++- examples/html_examples/Cargo.toml | 2 +- examples/kanban/Cargo.toml | 2 +- examples/saas/Cargo.toml | 2 +- examples/techdemo/Cargo.toml | 2 +- examples/v0/Cargo.toml | 2 +- examples/workout/Cargo.toml | 2 +- hemx-axum/Cargo.toml | 2 +- hemx-axum/src/lib.rs | 4 ---- 11 files changed, 21 insertions(+), 25 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 200abf1..84edd19 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -68,4 +68,4 @@ Keep it stable. Prefer pointers to canonical sources over copied structure, file - Opaque island JavaScript is a leaf adapter for high-frequency local behavior only; client-local handlers keep the server-handler shape while `hemx-wasm` owns concrete opt-in syntax. Use native events/generated helpers at the boundary and do not introduce a component runtime, client state graph, VDOM, selector interop, or second UI model. req: canonical_authoring/017 req: client_local/001 req: client_local/003 req: client_local/004 req: interop/001 req: interop/002 req: interop/003 req: interop/006 req: interop/007 req: interop/008 req: interop/009 req: interop/010 req: interop/011 req: interop/012 - Host capability adapters must stay at the `hemx-host` boundary: typed capabilities use fire/request/stream/schedule shapes; adapters may call host APIs and return host events, but they must not mutate DOM or own app/domain state. req: host/001 req: host/002 - Local/offline app behavior should be commands/events/projections; do not add `hemx-local`, stored DOM patches, stored `EffectBatch` truth, or a core client state graph without a proven reusable contract. Atoms are explicit addressable/bootstrap/sync resources, not the default state container or a reactive framework. Replay, reconciliation, export, and deletion rules stay explicit product decisions, and exemplars should show UI effects as app-state output. The local-first multiplayer kanban remains an advanced north-star integration milestone, not beginner/API surface scope. req: canonical_authoring/018 req: canonical_authoring/019 req: state/001 req: state/002 req: state/003 req: state/004 req: state/005 req: state/006 req: state/007 req: local/001 req: local/002 req: local/003 req: local/004 req: milestone/001 req: milestone/002 req: milestone/003 -- Axum apps should serve and load the shared runtime through hemx-axum helpers such as `runtime_js_path()` and `runtime_js()`, not hard-coded `/hemx.js` URLs or app-owned cache-busting strings; keep hemx-axum as route/runtime/handler adapter around generated partial swaps, not a routing owner. req: axum_integration/001 req: axum_integration/002 req: axum_integration/003 req: axum_integration/005 req: axum_integration/006 +- Axum 0.8 apps should serve and load the shared runtime through hemx-axum helpers such as `runtime_js_path()` and `runtime_js()`, not hard-coded `/hemx.js` URLs or app-owned cache-busting strings; keep hemx-axum as route/runtime/handler adapter around generated partial swaps, not a routing owner. req: axum/002 req: axum_integration/001 req: axum_integration/002 req: axum_integration/003 req: axum_integration/005 req: axum_integration/006 diff --git a/Cargo.lock b/Cargo.lock index 65b79db..bffbc00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,13 +39,13 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "axum" -version = "0.7.9" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ - "async-trait", "axum-core", "bytes", + "form_urlencoded", "futures-util", "http", "http-body", @@ -59,8 +59,7 @@ dependencies = [ "multer", "percent-encoding", "pin-project-lite", - "rustversion", - "serde", + "serde_core", "serde_json", "serde_path_to_error", "serde_urlencoded", @@ -74,19 +73,17 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ - "async-trait", "bytes", - "futures-util", + "futures-core", "http", "http-body", "http-body-util", "mime", "pin-project-lite", - "rustversion", "sync_wrapper", "tower-layer", "tower-service", @@ -994,9 +991,9 @@ dependencies = [ [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index 30e296e..f9e8a38 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -511,6 +511,9 @@ what a valid business email is. [north_star] ### req: wire/005 0 005 No JSON anywhere in hemx-internal artifacts. Public request/response envelopes use `application/x-www-form-urlencoded`; effects and symbols use postcard. `application/json` is acceptable only at integration boundaries. [north_star] +### req: wire/007 +0 007 `EffectBatch::encoded_len` reports the exact canonical wire size, and `to_wire` uses it to pre-size encoding to one output allocation while preserving canonical bytes. [north_star] + --- ## abi @@ -585,7 +588,7 @@ what a valid business email is. [north_star] 0 006 Page helpers add shell/title/history/fallback behavior without changing the render/target/effect model. [north_star] ### req: axum/002 -0 002 Existing Axum routes remain normal Axum routes. hemx does not own routing. hemx-axum only mounts handler dispatch, runtime assets, and optional push endpoints. [north_star] +0 002 Existing Axum 0.8 routes remain normal Axum routes. hemx does not own routing. hemx-axum only mounts handler dispatch, runtime assets, and optional push endpoints. [north_star] ### req: axum/003 0 003 Interactive fragments from `/demo/...` HTMX endpoints are Rust handlers returning generated target commands. Registration reads as generated handle/page/partial helpers, not low-level registry wiring. [north_star] diff --git a/examples/html_examples/Cargo.toml b/examples/html_examples/Cargo.toml index 086fbf4..c55f18f 100644 --- a/examples/html_examples/Cargo.toml +++ b/examples/html_examples/Cargo.toml @@ -8,7 +8,7 @@ publish = false path = "src/lib.rs" [dependencies] -axum = "0.7" +axum = "0.8" hemplate = { path = "../../../hemplate/hemplate" } hemx = { path = "../../hemx" } hemx-axum = { path = "../../hemx-axum" } diff --git a/examples/kanban/Cargo.toml b/examples/kanban/Cargo.toml index 0325dc0..10007af 100644 --- a/examples/kanban/Cargo.toml +++ b/examples/kanban/Cargo.toml @@ -8,7 +8,7 @@ publish = false path = "src/lib.rs" [dependencies] -axum = "0.7" +axum = "0.8" futures-util = "0.3" hemplate = { path = "../../../hemplate/hemplate" } hemx = { path = "../../hemx" } diff --git a/examples/saas/Cargo.toml b/examples/saas/Cargo.toml index a4bb58b..78e42d1 100644 --- a/examples/saas/Cargo.toml +++ b/examples/saas/Cargo.toml @@ -12,7 +12,7 @@ name = "hemx-saas-example" path = "src/main.rs" [dependencies] -axum = "0.7" +axum = "0.8" futures-util = "0.3" hemplate = { path = "../../../hemplate/hemplate" } hemx = { path = "../../hemx" } diff --git a/examples/techdemo/Cargo.toml b/examples/techdemo/Cargo.toml index 5fd3f0f..7e6d4da 100644 --- a/examples/techdemo/Cargo.toml +++ b/examples/techdemo/Cargo.toml @@ -8,7 +8,7 @@ publish = false path = "src/lib.rs" [dependencies] -axum = "0.7" +axum = "0.8" futures-util = "0.3" hemplate = { path = "../../../hemplate/hemplate" } hemx = { path = "../../hemx" } diff --git a/examples/v0/Cargo.toml b/examples/v0/Cargo.toml index 22a5198..08ccb93 100644 --- a/examples/v0/Cargo.toml +++ b/examples/v0/Cargo.toml @@ -8,7 +8,7 @@ publish = false path = "src/lib.rs" [dependencies] -axum = "0.7" +axum = "0.8" futures-util = "0.3" hemplate = { path = "../../../hemplate/hemplate" } hemx = { path = "../../hemx" } diff --git a/examples/workout/Cargo.toml b/examples/workout/Cargo.toml index 8b1643b..5ecd40c 100644 --- a/examples/workout/Cargo.toml +++ b/examples/workout/Cargo.toml @@ -12,7 +12,7 @@ name = "hemx-workout-example" path = "src/main.rs" [dependencies] -axum = "0.7" +axum = "0.8" hemplate = { path = "../../../hemplate/hemplate" } hemx = { path = "../../hemx" } hemx-axum = { path = "../../hemx-axum" } diff --git a/hemx-axum/Cargo.toml b/hemx-axum/Cargo.toml index 648189d..764b9c3 100644 --- a/hemx-axum/Cargo.toml +++ b/hemx-axum/Cargo.toml @@ -7,7 +7,7 @@ edition.workspace = true path = "src/lib.rs" [dependencies] -axum = { version = "0.7", default-features = false, features = ["multipart", "tokio"] } +axum = { version = "0.8", default-features = false, features = ["multipart", "tokio"] } futures-util = { version = "0.3", default-features = false } hemx-core = { path = "../hemx-core" } hemx-js = { path = "../hemx-js" } diff --git a/hemx-axum/src/lib.rs b/hemx-axum/src/lib.rs index ecba4f3..7f08654 100644 --- a/hemx-axum/src/lib.rs +++ b/hemx-axum/src/lib.rs @@ -1,4 +1,3 @@ -use axum::async_trait; use axum::body::{to_bytes, Body}; pub use axum::extract::State; use axum::extract::{FromRequest, FromRequestParts, Multipart}; @@ -91,7 +90,6 @@ impl PageRequest { } } -#[async_trait] impl FromRequestParts for PageRequest where S: Send + Sync, @@ -1237,7 +1235,6 @@ impl IntoResponse for InteractionFormRejection { } } -#[async_trait] impl FromRequest for InteractionRequest where S: Send + Sync, @@ -1251,7 +1248,6 @@ where } } -#[async_trait] impl FromRequest for InteractionForm where S: Send + Sync,