proc 0.22.2
proc
makes running child processes really, really easy.
The real power of proc
, however, is that it lets you use (most of) the higher
order functions from JavaScript arrays - map
, filter
, find
, etc. - on
AsyncIterable
data. The code you run this way is lazy and streamed, so you can work with data
that is much larger than you can fit in memory. This lets you do all sorts
of complex IO, and simply. If you have struggled to get JavaScript streams to
run without edge case bugs, and if you find streams awkward to work with, you
are going to love this.
proc
also includes functions that make concurrent processing with
controlled/limited concurrency easy, and easy to understand. If you thought
that JavaScript was not great for parallel programming, this might change your
mind.
I personally use proc
for sysadmin work as a replacement for Bash scripting. I
can just drop deno
into /usr/local/bin/
and now I can write standalone Deno
scripts that can do (almost) anything I would want to do with a Bash script, but
with proper type-checking, error handling, and with safety (tightly
sandboxed). The ability to run and manage child processes in a sane manner is
critical for this kind of work, and this is where proc
shines.
Project Status as of 2025-05-19
This project is actively maintained. I use it almost every day, and I have since the first version. If you find bugs, errors, or omissions in the documentation, please file an issue.
The API is stable. There still could be breaking changes. I don't expect any major redesigns.
The documentation is somewhat broken from the move to JSR. I am working on it.
Usage
import { run } from "jsr:@j50n/proc@0.22.2";
A Simple Example
Run ls -la
as a child process. Decode stdout
as lines of text. Print to
console.
await run("ls", "-la").toStdout();
A Better Example
Don't worry about understanding everything in this example yet. This shows a
little of what is possible using proc
.
Given the text for War and Peace:
- Read the file into an
AsyncIterable
ofUint8Array
. - Uncompress it (the file is GZ'd).
- Convert to lowercase using JavaScript, because the JavaScript conversion is
more correct than the one in
tr
. grep
out all the words on word boundaries.tee
this into two streams (AsyncIterable
ofUint8Array
) of words.- Count the total number of words.
- Use
sort
withuniq
to count the unique words.
const [words1, words2] = read(
fromFileUrl(import.meta.resolve("./warandpeace.txt.gz")),
)
.transform(gunzip)
.lines
.map((line) => line.toLocaleLowerCase())
.run("grep", "-oE", "(\\w|')+") // grep out the words to individual lines
.tee();
const [uniqueWords, totalWords] = await Promise.all([
words1.run("sort").run("uniq").lines.count(),
words2.lines.count(),
]);
console.log(`Total: ${totalWords.toLocaleString()}`);
console.log(`Unique: ${uniqueWords.toLocaleString()}`);
Up to the point where we run Promise.all
, this is asynchronous, streaming,
lazily evaluated code. It is trivially running three child processes (grep
,
sort
, and uniq
), a DecompressionStream
transform, and in-process logic to
normalize to lower-case. This is all happening concurrently, mostly in parallel,
one buffer, one line, or one word at a time.