1 unstable release
0.0.1 | Mar 15, 2024 |
---|
#231 in #remote
1.5MB
17K
SLoC
"LiveView" frameworks like Phoenix LiveView, LiveViewJS, and Dioxus LiveView are amazing. But why limit ourselves to LiveView? Why not LiveEverything?
WSDOM
WSDOM is a no-roundtrip Rust → JavaScript Remote Method Invocation or Distributed Objects system. It lets Rust code hold JavaScript objects and call methods/functions over the network.
WSDOM can be used to add network-dependent functionalities to webpages without writing JS code or making API endpoints. It can also be integrated into "LiveView"-style Rust web frameworks to expose access to the full Web API.
Quick Example
Here is an example using WSDOM to put <div>Hello World!</div>
on a webpage.
// this Rust code runs on your web server
fn hello(browser: wsdom::Browser) {
let document = wsdom::dom::document(&browser) // get hold of a Document object
let body = document.get_body(); // get the <body /> of that document object
let elem = document.create_element(&"div", &wsdom::undefined()); // create a <div />
elem.set_inner_text(&"Hello World!"); // set the text
body.append_child(&elem); // add the <div /> to the <body />
}
// this JavaScript code runs on the browser
WSDOMConnectWebSocket("ws://my-website.domain:4000/");
Our full "Hello World!" code is available here.
Key Features (and Anti-Features)
- WSDOM generates strongly-typed Rust stubs for JS classes/functions/methods based on
.d.ts
TypeScript definitions. - Calling JS code with WSDOM is roundtrip-free. This Rust code
does not block on the network at all; it will finish in microseconds.let mut val: JsNumber = browser.new_value(&1.0); for _ in 0..100 { val = wsdom::js::Math::cos(&browser, &val); // Math.cos on the JS side }
- Roundtrip-free calling is possible because WSDOM keeps values on the JS side, sending them back to Rust only when explicitly requested.
To get the value computed by the loop above, one would do
thelet val_retrieved: f64 = val.retrieve_float().await; println!("the value of (cos^[100])(1.0) computed in JavaScript is {val_retrieved}");
.await
will take one network roundtrip.
- Roundtrip-free calling is possible because WSDOM keeps values on the JS side, sending them back to Rust only when explicitly requested.
To get the value computed by the loop above, one would do
- Due to the roundtrip-free design, WSDOM fundamentally cannot handle JS exceptions.
- If one of the
Math.cos
calls in our loop above throws, the Rust loop will still complete all 100 iterations without panic or any sort of warning (see How It Works for why). As you might expect, this means code using WSDOM are very painful to debug.
- If one of the
- WSDOM is one-way. Rust code can call JS code but not the other way around.
- To make event handling possible, we have Futures-based interactivity;
we connect JS callbacks to streams that can be awaited on the Rust side.
async fn example(browser: Browser, button: &HTMLElement) { let (stream, callback) = wsdom::callback::new_callback::<MouseEvent>(&browser); button.add_event_listener(&"click", &callback, &wsdom::undefined()); let _click_event: MouseEvent = stream.next().await; // wait for the Stream to yield println!("button was clicked on the browser!"); }
- To make event handling possible, we have Futures-based interactivity;
we connect JS callbacks to streams that can be awaited on the Rust side.
- WSDOM is transport-agnostic, framework-agnostic, and executor-agnostic. That said, we provide an integration library for easily getting started with WSDOM on Axum web framework (which uses the Tokio executor) with WebSocket.
Examples
Hosted examples are available. When viewing them, I recommend opening your browser's network devtool to see the WebSocket traffic.
Comparisons
web-sys
WSDOM serves a similar role as web-sys (and a bit of js-sys too), but instead of running your Rust in WebAssembly in the same browser, we let you run your Rust code away across a WebSocket connection.
WSDOM's translation of JS API to Rust is different from web-sys.
We translate from TypeScript declarations, rather than directly from WebIDLs.
The network gap also means our optional types take the form of JsNullable<_>
(compared to the Option<_>
of web-sys).
jsdom
WSDOM and jsdom are similar in that we both expose the web browser's API outside a web browser. jsdom does so by implementing the API themselves. WSDOM does so by forwarding calls to a real web browser running across a WebSocket connection.
Details
The How It Works document describes how WSDOM works in more details.
The crate is on crates.io and the documentation is on docs.rs.
Disclaimer
Use WSDOM at your own risk. It is alpha-quality at best.
The Rust code produced by our .d.ts
loader might change between WSDOM versions.
Dependencies
~1.6–2.5MB
~52K SLoC