comlink
Rust bindings for Comlink, plus an RPC layer on top of them: define a service as a trait, annotate it, and the macro generates the client and the service. Calls cross as comlink messages, so the peer on the other end of the endpoint can equally well be JavaScript. JsValue-bearing arguments are passed through untouched, byte buffers cross as Uint8Array (Option<Vec<u8>> as Uint8Array or undefined), and everything else is serialized with serde-wasm-bindgen. A method returning Result maps onto the call's promise: Ok resolves with the encoded value, Err rejects with the encoded error, so a JavaScript peer just returns or throws. Every rejection decodes into the error type, a thrown Error first reduced to its message, so keep that type a serde one such as String. Put the trait definition in a shared crate so both ends agree on it.
The macro generates CalculatorClient, CalculatorService, and a Calculator trait you implement on the serving side:
;
Expose it on the worker's global scope, then announce that the listener is attached:
let scope: DedicatedWorkerGlobalScope = global.unchecked_into;
expose;
announce;
Wrap the worker on the other side once that announcement arrives:
let worker = new.unwrap;
wait.await;
let client = wrap;
assert_eq!;
Features
- Requests, notifications and streams: a method with a return type is a request, a method returning nothing is fire-and-forget, and a method returning
impl Stream<Item = T>forwards items over a callback proxy until the receiver is dropped. - Automatic argument routing:
&strcrosses as a JavaScript string,&[u8]andVec<u8>as aUint8Array, anythingAsRef<JsValue>untouched, everything else through serde. - Transfer semantics via
#[transfer(param)]on a method, naming the owned JavaScript arguments to hand over rather than clone. - Conditional methods:
#[cfg(feature = "...")]on a trait method is propagated onto the generated client and service. - A readiness handshake: comlink has none of its own, so a request posted before the remote's listener attaches is simply lost.
comlink::ready::{announce, wait}closes that window with a sentinel message both comlink listeners ignore.
The crates
comlink-sys: the rawwasm_bindgenbindings and nothing else:expose,wrap,transfer,proxy,windowEndpoint, and comlink's exported symbols. Also re-exported ascomlink::sys.comlink: the macro re-exported, plus the runtime support the generated code calls into.comlink-macro: the#[comlink::service]attribute macro.
The npm comlink package has to be resolvable by whatever bundler or module loader serves the generated JavaScript, since the bindings import it by that bare specifier.
Need help with your latest project? Get in touch via contact@allwright.io. I'm available for new assignments.