No description
  • Rust 97.3%
  • JavaScript 1.6%
  • MDX 0.4%
  • CSS 0.4%
  • TypeScript 0.3%
Find a file
Guy Bedford 552bedcb2c
Some checks failed
Lint & Format / Rustfmt (push) Has been cancelled
Lint & Format / Clippy (push) Has been cancelled
Tests / Native Tests (push) Has been cancelled
Tests / Native Tests-1 (push) Has been cancelled
Tests / Native Tests-2 (push) Has been cancelled
Tests / Browser Tests (push) Has been cancelled
Tests / Browser Tests-1 (push) Has been cancelled
Tests / Test gloo-net-2 (push) Has been cancelled
Tests / Browser Tests-2 (push) Has been cancelled
Tests / Node Tests (push) Has been cancelled
Tests / Node Tests-1 (push) Has been cancelled
Tests / Node Tests-2 (push) Has been cancelled
Tests / Build with -Cpanic=unwind (push) Has been cancelled
Tests / Test gloo-history WASI (push) Has been cancelled
Tests / Test gloo-history WASI-1 (push) Has been cancelled
Tests / Test gloo-history WASI-2 (push) Has been cancelled
Tests / Test gloo-worker (push) Has been cancelled
Tests / Test gloo-worker-1 (push) Has been cancelled
Tests / Test gloo-worker-2 (push) Has been cancelled
Tests / Test gloo-net (push) Has been cancelled
Tests / Test gloo-net-1 (push) Has been cancelled
fix: propagate unwind safety to user callbacks across all gloo crates (#562)
wasm-bindgen 0.2.109+ added a `MaybeUnwindSafe` bound on `Closure::wrap`
and `Closure::once` to make panics in JS-invoked closures sound under
`panic = "unwind"`. The bound landed quietly because under the default
`panic = "abort"` it resolves to a no-op blanket impl, but downstream
crates that test under `-Cpanic=unwind` (notably wasm-streams) hit a
hard compile error: the `Box<F> as Box<dyn Fn*>` coercion used to
construct the closure type erases the static `UnwindSafe` bound the
constructors now require. See
https://github.com/MattiasBuelens/wasm-streams/pull/35 for the
originating report.

This change applies the same pattern to every gloo crate that hosts a
`Closure::wrap` / `Closure::once` call site: a `CallbackUnwindSafe`
marker on the public boundary (where one exists), and an internal
switch to the `_assert_unwind_safe` variant under `panic = "unwind"`
to acknowledge the dyn-erasure at the call site that has already
enforced the bound.

gloo-timers
* `Timeout::new` and `Interval::new` add a `CallbackUnwindSafe` bound,
  a marker that resolves to `std::panic::UnwindSafe` under
  `panic = "unwind"` on wasm and to a no-op blanket otherwise.
  Internally both route through `Closure::once_assert_unwind_safe` and
  `Closure::wrap_assert_unwind_safe` under panic=unwind.
* The `futures` feature wraps its internal `oneshot::Sender` and
  `mpsc::UnboundedSender` captures in `std::panic::AssertUnwindSafe`.
  `TimeoutFuture` consumes the wrapper through a by-value helper so
  that RFC 2229 disjoint capture sees the closure capturing the
  wrapper rather than projecting to the inner sender (writing `tx.0`
  or `let AssertUnwindSafe(x) = tx` inside the closure would silently
  defeat the assertion).

gloo-events
* All four `EventListener` constructors (`new`, `once`,
  `new_with_options`, `once_with_options`) surface
  `CallbackUnwindSafe` on the user callback and route through the
  `_assert_unwind_safe` variants under panic=unwind.

gloo-render
* `request_animation_frame` surfaces the same bound. The internal
  trampoline that fans the user `FnOnce` out through
  `Rc<RefCell<Option<CallbackWrapper>>>` is audited unwind-safe: the
  slot is transitioned to `None` via `borrow_mut().take()` before the
  user callback runs, so a panic leaves a legitimately reachable
  post-fire state and `AnimationFrame::drop` correctly skips
  cancellation.

gloo-net
* Internal closures only; no public bound. The four WebSocket
  callbacks and the two EventSource callbacks route through a
  crate-local `wrap_internal!` macro that resolves to
  `Closure::wrap_assert_unwind_safe` under panic=unwind. The captures
  (`Rc<RefCell<Option<Waker>>>` and `mpsc::UnboundedSender<...>`) are
  exclusively owned by the crate; `unbounded_send` is lock-free and
  the waker slot is taken in one shot, so a caught panic leaves no
  observable invariant violated.

gloo-worker
* The `set_on_packed_message` trampoline is `pub(crate)` and called
  only from the spawner and registrar. Both call sites either
  construct a value and forward via `scope.send`, or hold a
  `RefMut<callbacks>` that is correctly released during unwind via
  `RefMut::drop`, so asserting unwind safety here is sound.

Public-API semver impact is none under the default panic strategy:
`CallbackUnwindSafe` blanket-impls every type, so the new bound is
invisible to existing callers. Under `panic = "unwind"` the affected
crates previously did not compile against wasm-bindgen 0.2.109+, so
there are no existing callers to break. The `wasm-bindgen` floor is
left at `"0.2"` in every crate; the `_assert_unwind_safe` call sites
are cfg-gated on `panic = "unwind"`, and any user opting into that
strategy will have already resolved a wasm-bindgen new enough to
expose the helpers.

CI gains a `panic_unwind_build` job that builds every affected crate
with `-Cpanic=unwind -Zbuild-std=std,panic_unwind --all-features` so
the same regression cannot reappear silently. The `--all-features`
flag covers gloo-timers' `futures` feature, gloo-net's transport
features (json, websocket, http, eventsource), and gloo-worker's
`futures` feature in a single invocation.
2026-05-16 11:16:00 +09:00
.cargo fix(worker): enable spawning web workers from inside web workers (#547) 2026-04-02 18:08:16 +09:00
.github fix: propagate unwind safety to user callbacks across all gloo crates (#562) 2026-05-16 11:16:00 +09:00
crates fix: propagate unwind safety to user callbacks across all gloo crates (#562) 2026-05-16 11:16:00 +09:00
examples Migrate to Edition 2024 and increase MSRV to 1.85 (#553) 2026-04-16 00:28:07 +08:00
src fix: docs.rs build failure for gloo 0.12.0 (#543) 2026-03-27 20:24:57 +08:00
website chore(deps): bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /website (#457) 2026-03-24 14:15:17 +09:00
.firebaserc Create a website (#140) 2021-06-29 19:15:23 +05:00
.gitattributes Generate README.mds for each crate from its top-level docs 2019-05-23 14:41:05 -07:00
.gitignore Add an example of processing transferrable types with worker (#371) 2023-10-05 21:54:51 +05:00
Cargo.lock docs(net): add json body example code and correct outdated GET example (#556) 2026-04-23 00:07:19 +09:00
Cargo.toml Migrate to Edition 2024 and increase MSRV to 1.85 (#553) 2026-04-16 00:28:07 +08:00
CHANGELOG.md docs(net): add json body example code and correct outdated GET example (#556) 2026-04-23 00:07:19 +09:00
CONTRIBUTING.md fix: update documented wasm-pack commands used for testing 2025-01-24 16:44:28 +01:00
firebase.json Create a website (#140) 2021-06-29 19:15:23 +05:00
LICENSE-APACHE The very first Gloo commit! 2019-02-21 14:29:14 -08:00
LICENSE-MIT The very first Gloo commit! 2019-02-21 14:29:14 -08:00
new-design-workflow.dot Add comment describing how to re-render workflow graph 2019-04-02 10:04:13 -07:00
new-design-workflow.png Expand and formalize contribution workflow a bit 2019-04-01 13:49:05 -07:00
README.md Update README.md (#233) 2022-07-02 01:29:58 +05:00
release.toml release.toml 2023-08-11 21:04:00 +05:00
update-readmes.sh Generate README.mds for each crate from its top-level docs 2019-05-23 14:41:05 -07:00

Gloo

A toolkit for building fast, reliable Web applications and libraries with Rust and Wasm.

What?

Gloo is a collection of libraries, and those libraries provide ergonomic Rust wrappers for browser APIs. web-sys/js-sys are very difficult/inconvenient to use directly so gloo provides wrappers around the raw bindngs which makes it easier to consume those APIs. This is why it is called a "toolkit", instead of "library" or "framework".

Background

In the Rust and WebAssembly working group's 2019 roadmap, we chose to deliberately cultivate our library ecosystem by building a modular toolkit:

Collaborating on a Modular Toolkit

The idea of building [high-level libraries] in a modular way that will allow others in the community to put the components together in a different way is very exciting to me. This hopefully will make the ecosystem as a whole much stronger.

In particular Id love to see a modular effort towards implementing a virtual DOM library with JSX like syntax. There have been several efforts on this front but all have seemed relatively monolithic and “batteries included”. I hope this will change in 2019.

— Ryan Levick in Rust WebAssembly 2019

Don't create branded silos. Branding might perhaps be useful to achieve fame. But if we truly want Rust's Wasm story to succeed we should think of ways to collaborate instead of carving out territory.

— Yoshua Wuyts in Wasm 2019

In 2018, we created foundational libraries like js-sys and web-sys. In 2019, we should build modular, high-level libraries on top of them, and collect the libraries under an umbrella toolkit crate for a holistic experience. This toolkit and its libraries will make available all the batteries you want when targeting Wasm.

Building a greenfield Web application? Use the whole toolkit to hit the ground running. Carefully crafting a tiny Wasm module and integrating it back into an existing JavaScript project? Grab that one targeted library you need out from the toolkit and use it by itself.

Gloo is this modular toolkit.

Goals

  • Support both whole Web applications and small, targeted libraries: Gloo, and the collection of utility crates that make up its toolkit, should help you be productive if you are writing a green-field web application with Rust and Wasm. And it should also help you be productive if you are writing a small, targeted Wasm library that will be integrated into an existing JavaScript application.

  • Cultivate the Rust and Wasm library ecosystem: We want to use Gloo as a forcing function for creating and sharing the building blocks of Web development. The kinds of libraries that any framework or high-level library would need to build. We want to explicitly disentangle these libraries and make them available for sharing across the whole ecosystem.

  • Modular Toolkit, not Framework: Gloo should be a loose collection of utility crates that can be used individually, or all together. Gloo doesn't assume that it "owns" the whole Webpage, that it controls the Wasm start function, etc. This lack of assumptions enables reaching more use cases (such as surgically replacing a hot code path from JS) than monolithic frameworks can. Wherever possible, Gloo should prefer interfaces over implementations, so that different implementations with different approaches are swap-able.

  • Fast: Let's leverage Rust's zero-cost abstractions, and design with performance in mind, to show everyone how fast the Web can be ;)

  • Reliable: Every crate should be thoroughly tested. Headless browser tests. Quickcheck tests. Using the type system to make whole classes of bugs impossible.

  • Small: Small code size for faster page loads. No accidentally pulling in all of the panicking and formatting infrastructure. Users shouldn't have to make a trade off between using Gloo libraries and having small Wasm binaries.

  • Idiomatic: We want to build Rust-y APIs, that feel natural to use. The Web's APIs were not designed for the Rust language, and you can feel the impedance mismatch every now and then. Let's correct that, bridge the gap, and make libraries that are a joy to use.

Example

This example uses gloo::events for adding event listeners and gloo::timers for creating timeouts. It creates a <button> element and adds a "click" event listener to it. Whenever the button is clicked, it starts a one second timeout, which sets the button's text content to "Hello from one second ago!".

use gloo::{events::EventListener, timers::callback::Timeout};
use wasm_bindgen::prelude::*;

pub struct DelayedHelloButton {
    button: web_sys::Element,
    on_click: events::EventListener,
}

impl DelayedHelloButton {
    pub fn new(document: &web_sys::Document) -> Result<DelayedHelloButton, JsValue> {
        // Create a `<button>` element.
        let button = document.create_element("button")?;

        // Listen to "click" events on the button.
        let button2 = button.clone();
        let on_click = EventListener::new(&button, "click", move |_event| {
            // After a one second timeout, update the button's text content.
            let button3 = button2.clone();
            Timeout::new(1_000, move || {
                button3.set_text_content(Some("Hello from one second ago!"));
            })
            .forget();
        });

        Ok(DelayedHelloButton { button, on_click })
    }
}

Get Involved!

Want to help us build Gloo? Check out CONTRIBUTING.md!