comlink-sys 0.0.1

Raw bindings to the Comlink JavaScript library
Documentation
  • Coverage
  • 68.75%
    11 out of 16 items documented0 out of 10 items with examples
  • Size
  • Source code size: 12.6 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 198.4 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 9s Average build duration of successful builds.
  • all releases: 9s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • allsey87/comlink-rust
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • allsey87

CI Crates.io api-docs MIT licensed

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.

#[comlink::service]
pub trait Calculator {
    async fn add(&self, left: u32, right: u32) -> u32;
}

The macro generates CalculatorClient, CalculatorService, and a Calculator trait you implement on the serving side:

#[derive(Clone)]
struct CalculatorImpl;
impl Calculator for CalculatorImpl {
    async fn add(&self, left: u32, right: u32) -> u32 { left + right }
}

Expose it on the worker's global scope, then announce that the listener is attached:

let scope: web_sys::DedicatedWorkerGlobalScope = js_sys::global().unchecked_into();
CalculatorService::expose(CalculatorImpl, &scope);
comlink::ready::announce(&scope);

Wrap the worker on the other side once that announcement arrives:

let worker = web_sys::Worker::new("./worker.js").unwrap();
comlink::ready::wait(&worker).await;
let client = CalculatorClient::wrap(&worker);

assert_eq!(client.add(41, 1).await, 42);

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: &str crosses as a JavaScript string, &[u8] and Vec<u8> as a Uint8Array, anything AsRef<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 raw wasm_bindgen bindings and nothing else: expose, wrap, transfer, proxy, windowEndpoint, and comlink's exported symbols. Also re-exported as comlink::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.