@yaebal/workers

a tiny worker_threads pool — keep the bot on the main loop and offload only the CPU-heavy bits to threads.

install

terminal
pnpm add @yaebal/workers

when to reach for it

Most handlers are I/O-bound and are already served by @yaebal/runner's concurrency. Threads help only for genuinely CPU-heavy work: image processing, crypto, heavy parsing. Offload that — not the whole bot.

usage

Register tasks in a worker file, then call them from a handler:

tasks.ts
// tasks.ts — runs in a worker thread
import { register } from "@yaebal/workers";

register({
  resize: (buf) => sharp(buf).resize(100).toBuffer(),
  hash:   (s)   => crypto.createHash("sha256").update(s).digest("hex"),
});
bot.ts
// bot — stays on the main event loop
import { createPool } from "@yaebal/workers";

const pool = createPool(new URL("./tasks.js", import.meta.url), { size: 4 });

bot.on("message:photo", async (ctx) => {
  const thumb = await pool.run("resize", await ctx.download()); // → thread → back
  await ctx.sendPhoto(media.buffer(thumb));
});

how it works

The main thread posts name + arg to the next worker (round-robin); the worker runs the registered function and posts the result back, resolving the pool.run promise by id. Buffers can be passed as transferables (no copy) via the third argument. If a worker crashes, in-flight calls reject and the thread is respawned.

api

exportsignaturewhat
createPool(file, { size })spawn a pool of workers
pool.run(name, arg?, transfer?)run a task; returns a promise
pool.destroy()terminate all workers
register(handlers)called inside the worker file
The worker file must be a built .js (or run under a TS loader). Workers don't share closures with the main thread — they only receive the data you pass to run.