"LiveView" frameworks like [Phoenix LiveView](https://github.com/phoenixframework/phoenix_live_view), [LiveViewJS](https://www.liveviewjs.com/), and [Dioxus LiveView](https://dioxuslabs.com/learn/0.5/reference/liveview) are amazing. But why limit ourselves to Live*View*? Why not Live*Everything*?
# WSDOM
[<img alt="crates.io" src="https://img.shields.io/crates/v/wsdom?style=for-the-badge" height="20">](https://crates.io/crates/wsdom)
[<img alt="crates.io" src="https://img.shields.io/docsrs/wsdom?style=for-the-badge" height="20">](https://docs.rs/wsdom)
WSDOM is a 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.
```rust
// 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 />
}
```
```js
// this JavaScript code runs on the browser
WSDOMConnectWebSocket("ws://my-website.domain:4000/");
```
Our full "Hello World!" code is available [here](/examples/hello/src/main.rs).
## 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
```rust
let mut val: JsNumber = browser.new_value(&1.0);
for _ in 0..100 {
val = wsdom::js::Math::cos(&browser, &val); }
```
does not block on the network at all; it will finish in microseconds.
- 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
```rust
let val_retrieved: f64 = val.retrieve_float().await;
println!("the value of (cos^[100])(1.0) computed in JavaScript is {val_retrieved}");
```
the `.await` will take one network roundtrip.
- 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](how-it-works.md) for why).
As you might expect, this means code using WSDOM are **very painful to debug**.
- 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.
```rust
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; println!("button was clicked on the browser!");
}
```
- 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](https://github.com/tokio-rs/axum/) (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.
- Counter example: [code](/examples/many-examples/src/counter.rs) [demo](http://141.145.215.129:4000/counter)
- Audio API example: [code](/examples/many-examples/src/audio.rs) [demo](http://141.145.215.129:4000/audio)
- Canvas API example: [code](/examples/many-examples/src/canvas.rs) [demo](http://141.145.215.129:4000/canvas)
## Comparisons
### [web-sys](https://docs.rs/web-sys/latest/web_sys/)
WSDOM serves a similar role as web-sys (and a bit of [js-sys](https://docs.rs/js-sys/latest/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](https://github.com/jsdom/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](how-it-works.md) document describes how WSDOM works in more details.
The crate is [on crates.io](https://crates.io/crates/wsdom) and the documentation is [on docs.rs](https://docs.rs/wsdom/latest).
## 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.