Browser Support #9

Open
opened 2026-07-23 14:44:59 +00:00 by erik · 0 comments
Owner

Running logbus in the browser would enable demos, playground UIs, and real use cases like client-side log processing. This is more of a "pie in the sky" idea, not to be taken too seriously.

Feasibility Analysis

What compiles to WASM easily:

  • All transform plugins (pure on_input functions over JSON values) — trivial
  • parse/* and format/* plugins — pure computation, no I/O
  • js plugin — rquickjs doesn't target WASM, but could swap to a WASM-friendly JS engine or just skip it in browser builds
  • Pipeline engine — tokio doesn't run in WASM, but wasm-bindgen-futures + manual scheduling can replace it

What needs platform abstraction:

  • Sources: stdin/file/Kafka → replace with browser equivalents (paste/upload, Fetch/WebSocket)
  • Sinks: stdout/file/Kafka/OpenSearch → replace with DOM output, download blob, fetch POST
  • Timers: tokio::timegloo_timers or setTimeout
  • Channels: tokio channels → futures::channel (works in WASM)

Proposed Architecture

  1. Feature-gate platform-specific code:

    #[cfg(not(target_arch = "wasm32"))]
    mod source_file;   // native only
    
    #[cfg(target_arch = "wasm32")]
    mod source_paste;   // browser only
    
  2. Abstract the runtime:

    trait Runtime: Send + Sync {
        fn spawn(&self, fut: impl Future<Output = ()> + Send + 'static);
        fn sleep(&self, dur: Duration) -> impl Future<Output = ()>;
        fn interval(&self, dur: Duration) -> impl Stream<Item = ()>;
    }
    

    Native impl wraps tokio. WASM impl wraps wasm-bindgen-futures + gloo_timers.

  3. Browser-specific plugins:

    • source/paste — textarea input, processes on paste or button click
    • source/fetch — fetches a URL (via browser Fetch API) and streams response
    • source/upload — File input element, reads via FileReader API
    • sink/dom — renders events into a DOM element (table, JSON tree)
    • sink/download — accumulates output, triggers file download
    • sink/websocket — sends events to a WebSocket endpoint
  4. Build setup:

    [lib]
    crate-type = ["cdylib", "rlib"]
    
    [target.'cfg(target_arch = "wasm32")'.dependencies]
    wasm-bindgen = "0.2"
    wasm-bindgen-futures = "0.4"
    web-sys = { version = "0.3", features = ["console", "Window", ...] }
    gloo-timers = "0.3"
    
  5. JS API surface (via wasm-bindgen):

    #[wasm_bindgen]
    pub struct Pipeline { /* ... */ }
    
    #[wasm_bindgen]
    impl Pipeline {
        pub fn from_yaml(config: &str) -> Result<Pipeline, JsValue>;
        pub fn push_event(&mut self, json: &str);
        pub fn on_output(&mut self, callback: js_sys::Function);
        pub async fn run(&mut self);
    }
    
Running logbus in the browser would enable demos, playground UIs, and real use cases like client-side log processing. This is more of a "pie in the sky" idea, not to be taken too seriously. ### Feasibility Analysis **What compiles to WASM easily:** - All transform plugins (pure `on_input` functions over JSON values) — trivial - `parse/*` and `format/*` plugins — pure computation, no I/O - `js` plugin — rquickjs doesn't target WASM, but could swap to a WASM-friendly JS engine or just skip it in browser builds - Pipeline engine — tokio doesn't run in WASM, but `wasm-bindgen-futures` + manual scheduling can replace it **What needs platform abstraction:** - Sources: stdin/file/Kafka → replace with browser equivalents (paste/upload, Fetch/WebSocket) - Sinks: stdout/file/Kafka/OpenSearch → replace with DOM output, download blob, fetch POST - Timers: `tokio::time` → `gloo_timers` or `setTimeout` - Channels: tokio channels → `futures::channel` (works in WASM) ### Proposed Architecture 1. **Feature-gate platform-specific code:** ```rust #[cfg(not(target_arch = "wasm32"))] mod source_file; // native only #[cfg(target_arch = "wasm32")] mod source_paste; // browser only ``` 2. **Abstract the runtime:** ```rust trait Runtime: Send + Sync { fn spawn(&self, fut: impl Future<Output = ()> + Send + 'static); fn sleep(&self, dur: Duration) -> impl Future<Output = ()>; fn interval(&self, dur: Duration) -> impl Stream<Item = ()>; } ``` Native impl wraps tokio. WASM impl wraps `wasm-bindgen-futures` + `gloo_timers`. 3. **Browser-specific plugins:** - `source/paste` — textarea input, processes on paste or button click - `source/fetch` — fetches a URL (via browser Fetch API) and streams response - `source/upload` — File input element, reads via FileReader API - `sink/dom` — renders events into a DOM element (table, JSON tree) - `sink/download` — accumulates output, triggers file download - `sink/websocket` — sends events to a WebSocket endpoint 4. **Build setup:** ```toml [lib] crate-type = ["cdylib", "rlib"] [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" web-sys = { version = "0.3", features = ["console", "Window", ...] } gloo-timers = "0.3" ``` 5. **JS API surface (via wasm-bindgen):** ```rust #[wasm_bindgen] pub struct Pipeline { /* ... */ } #[wasm_bindgen] impl Pipeline { pub fn from_yaml(config: &str) -> Result<Pipeline, JsValue>; pub fn push_event(&mut self, json: &str); pub fn on_output(&mut self, callback: js_sys::Function); pub async fn run(&mut self); } ```
erik self-assigned this 2026-07-23 14:44:59 +00:00
Sign in to join this conversation.
No description provided.