Release notes copied from 0.8.0-alpha. Changelog relative to 0.8.0-alpha.
0.8 has been planned for a while, primarily to accommodate small changes that arose during the course of testing and adopting 0.7, most of which are technically semver-breaking but should not meaningfully affect user code.
Noteworthy features:
- Axum 0.8 support. (This alone required a major version bump, as we reexport some Axum types.) (thanks to @sabify for the migration work here)
- Significant improvements to compile times when using
--cfg=erase_components
, which is useful as a dev-mode optimization (thanks to @zakstucke) - Support for the new
islands-router
features that allow a client-side routing experience while using islands (see theislands_router
example) (this one was me) - Improved server function error handling by allowing you to use any type that implements
FromServerFnError
rather than being constrained to useServerFnError
(see #3274). (Note: This will require changes if you're using a custom error type, but should be a better experience.) (thanks to @ryo33) - Support for creating WebSockets via server fns (thanks to @ealmloff)
As you can see this was a real team effort and, as always, I'm grateful for the contributions of everyone named above, and all those who made commits below. (Apologies if I missed any big features! I will add missing things to release notes as release nears.)
WebSocket Example
The WebSocket support is particularly exciting, as it allows you to call server functions using the default Rust Stream
trait from the futures
crate, and have those streams send messages over websockets without you needing to know anything about that process. The API landed in a place that feels like a great extension of the "server function" abstraction in which you can make HTTP requests as if they were ordinary async calls. The websocket stuff doesn't integrate directly with Resources/SSR (which make more sense for one-shot things) but is really easy to use:
use server_fn::{codec::JsonEncoding, BoxedStream, ServerFnError, Websocket};
// The websocket protocol can be used on any server function that accepts and returns a [`BoxedStream`]
// with items that can be encoded by the input and output encoding generics.
//
// In this case, the input and output encodings are [`Json`] and [`Json`], respectively which requires
// the items to implement [`Serialize`] and [`Deserialize`].
#[server(protocol = Websocket<JsonEncoding, JsonEncoding>)]
async fn echo_websocket(
input: BoxedStream<String, ServerFnError>,
) -> Result<BoxedStream<String, ServerFnError>, ServerFnError> {
use futures::channel::mpsc;
use futures::{SinkExt, StreamExt};
let mut input = input; // FIXME :-) server fn fields should pass mut through to destructure
// create a channel of outgoing websocket messages
// we'll return rx, so sending a message to tx will send a message to the client via the websocket
let (mut tx, rx) = mpsc::channel(1);
// spawn a task to listen to the input stream of messages coming in over the websocket
tokio::spawn(async move {
while let Some(msg) = input.next().await {
// do some work on each message, and then send our responses
tx.send(msg.map(|msg| msg.to_ascii_uppercase())).await;
}
});
Ok(rx.into())
}
#[component]
pub fn App() -> impl IntoView {
use futures::channel::mpsc;
use futures::StreamExt;
let (mut tx, rx) = mpsc::channel(1);
let latest = RwSignal::new(None);
// we'll only listen for websocket messages on the client
if cfg!(feature = "hydrate") {
spawn_local(async move {
match echo_websocket(rx.into()).await {
Ok(mut messages) => {
while let Some(msg) = messages.next().await {
latest.set(Some(msg));
}
}
Err(e) => leptos::logging::warn!("{e}"),
}
});
}
view! {
<input type="text" on:input:target=move |ev| {
tx.try_send(Ok(ev.target().value()));
}/>
<p>{latest}</p>
}
}
What's Changed
- Allow LocalResource sync methods to be used outside Suspense by @zakstucke in #3708
- fix: ensure that store subfield mutations notify from the root down (closes #3704) by @gbj in #3714
- fix(reactive_stores_macro): Make tuple struct field locator in
impl Patch
besyn::Index
instead ofusize
by @DanikVitek in #3700 - feat(reactive_stores): Replace
AsRef
bound ofStoreFieldIterator
blanket impl withLen
bound by @DanikVitek in #3701 - refactor: make
shell
parameter infile_and_error_handler*
generic by @tversteeg in #3711 - Various issues related to setting signals and context in cleanups by @gbj in #3687
- view!{} macro optimisation: don't wrap string types in closures when passing to ToChildren by @zakstucke in #3716
- test: regression from #3502 by @metatoaster in #3720
- Remove SendWrapper from the external interface of LocalResource by @zakstucke in #3715
Full Changelog: 0.8.0-alpha...0.8.0-beta