The main motivation was that async I/O in JS feels great, but CPU-bound work and structured concurrency still get awkward quickly. Even simple worker-thread use cases usually mean separate worker files, manual message passing, lifecycle management, and a lot of glue code.
So I built `puru` to make those patterns feel smaller while still staying explicit about the worker model.
Example:
```ts import { spawn } from '@dmop/puru'
const { result } = spawn(() => { function fibonacci(n: number): number { if (n <= 1) return n return fibonacci(n - 1) + fibonacci(n - 2) } return fibonacci(40) })
console.log(await result) ```
It also includes primitives for the coordination side of the problem:
for (let i = 0; i < 4; i++) { spawn(async ({ input, output }) => { for await (const n of input) { await output.send(n * 2) } }, { channels: { input, output } }) } ```
One intentional tradeoff is that functions passed to `spawn()` are serialized and sent to a worker, so they cannot capture outer variables. I preferred keeping that constraint explicit instead of hiding it behind a more magical abstraction.
Interested in feedback from people who deal with worker threads, CPU-heavy jobs, pipelines, or structured concurrency in JavaScript. #technology #js #programming #dev