UNPKG

@on-the-ground/daemonizer

Version:

A minimal async control flow framework for browser and Node.js daemons.

150 lines (100 loc) β€’ 3.44 kB
# πŸŒ€ Daemonizer A minimal async control flow framework for writing browser and Node.js daemons. Daemonizer helps you manage long-running async event loops, safely respond to cancellation, and yield control to the macro task queueβ€”without starving the event loop. --- ## πŸš€ Installation ```bash yarn add daemonizer # or npm install daemonizer ``` --- ## ✨ Features - βœ… **Abort-aware event loop** - βœ… **Task group with lifecycle tracking** - βœ… **Yielding mechanism to avoid blocking** - βœ… **Bounded queue for backpressure** - βœ… **Works in both Node.js and browser** --- ## πŸ§ͺ Try in Your Browser Run the daemon in your browser with no setup: πŸ‘‰ [examples/example.html](./examples/example.html) Open your browser console and watch `tick:` messages stream in real time! ```html <!-- examples/example.html --> <!DOCTYPE html> <html> <body> <script type="module"> import { Daemon } from "https://esm.sh/@on-the-ground/daemonizer@latest"; ///////////// Daemon Example ///////////// let controller = new AbortController(); const daemon = new Daemon(controller.signal, async (_signal, event) => { await new Promise((r) => setTimeout(r, 1000)); console.log("tick:", event); }); for (let i = 1; i <= 5; i++) { await daemon.pushEvent(i); } setTimeout(() => controller.abort(), 3000); console.log("waiting the daemon down"); await daemon.close(); console.log("the daemon got down"); const daemon = new Daemon(signal, handler); await daemon.push(msg); await daemon.close(); // Results: // waiting the daemon down // tick: 1 // tick: 2 // the daemon got down // tick: 3 <- long running task, use strictInterval to abort it </script> </body> </html> ``` --- ## 🧠 API Overview ### `Daemon` – The Core Abstraction ```ts import { Daemon } from "@on-the-ground/daemonizer"; const daemon = new Daemon(signal, async (msg) => { // Your background task handler console.log("received:", msg); }); // Push a task into the daemon's queue await daemon.push({ type: "log", content: "hello" }); // Gracefully shut down when you're done await daemon.close(); ``` #### βœ… Features - Runs background tasks with structured concurrency - Backpressure-safe via internal bounded queue - Auto-shuts down when `AbortSignal` is aborted - One-liner setup: no boilerplate, no ceremony --- ### 🧰 Low-level Tools (also exported) ### `launchEventLoop(signal, taskGroup, events, handler)` Runs a long-lived, abortable event loop over an `AsyncIterable`. Automatically yields to the macro task queue to prevent starvation. ### `TaskGroup` Tracks the lifecycle of multiple concurrent async tasks. - `add(n = 1)` - `done()` - `wait(): Promise<void>` ### `MacroTaskYielder` Yields only if enough time has passed since the last yield. ### `BoundedQueue<T>` A fixed-capacity async queue. Backpressure-aware and safe for multiple consumers. --- ## 🌐 Compatibility Daemonizer is fully compatible with: - βœ… Node.js (v16+) - βœ… Modern browsers (via bundlers like Vite, Webpack, etc.) No external runtime dependencies. --- ## πŸ“œ License MIT Β© 2025 Joohyung Park [github.com/on-the-ground/daemonizer](https://github.com/on-the-ground/daemonizer) --- > _"The name was subconsciously inspired by countless replays of Judas Priest’s 'Demonizer'."_