Para Lib
Eight @para/* npm packages: signals, parallel, arena,
simd, csv, arrow, rtp, mcp. Pure JS / Wasm, no
native deps. Runs on Node, Bun, Deno, browsers, Cloudflare Workers — anywhere V8 or JSC runs.
The optional .pts syntax desugars to imports from these packages.
ParaBun bundles them and adds GPU / hardware modules.
Install
Each module is its own npm package. Install only the ones your code uses:
Full per-bundler alias setup (Vite / esbuild / webpack) and the rest of the runtime + build instructions live in the install guide.
Libraries
Signals — a shopping cart that recomputes itself
Three signals (items, tax rate, shipping country) feed a chain of derived values; the only side-effect is one
effect() that re-runs whenever any transitive dep changes. No subscriptions, no
onChange handlers, no manual recompute.
import { signal, derived, effect } from "@para/signals";
type Item = { name: string; price: number };
const items = signal<Item[]>([{ name: "book", price: 12 }, { name: "pen", price: 3 }]);
const country = signal("US");
const taxRate = derived(() => ({ US: 0.0825, JP: 0.10, NO: 0.25 })[country.get()] ?? 0);
const subtotal = derived(() => items.get().reduce((s, i) => s + i.price, 0));
const tax = derived(() => subtotal.get() * taxRate.get());
const total = derived(() => subtotal.get() + tax.get());
effect(() => console.log(`${items.get().length} items · $${total.get().toFixed(2)}`));
items.update(arr => [...arr, { name: "mug", price: 8 }]); // 3 items · $24.88
country.set("NO"); // 3 items · $28.75
items.update(arr => arr.slice(1)); // 2 items · $13.75
import { signal, derived } from "@para/signals";
type Item = { name: string; price: number };
signal items: Item[] = [{ name: "book", price: 12 }, { name: "pen", price: 3 }];
signal country = "US";
derived taxRate = ({ US: 0.0825, JP: 0.10, NO: 0.25 })[country] ?? 0;
derived subtotal = items.reduce((s, i) => s + i.price, 0);
derived tax = subtotal * taxRate;
derived total = subtotal + tax;
`${items.length} items · $${total.toFixed(2)}` -> console.log;
items.update(arr => [...arr, { name: "mug", price: 8 }]); // 3 items · $24.88
country.set("NO"); // 3 items · $28.75
items.update(arr => arr.slice(1)); // 2 items · $13.75
Parallel — find duplicate photos in a directory
Hashing 10k images on one thread sits the event loop. pmap ships the pure hash function to a
persistent Worker pool (8 cores → 8 hashes in flight) and returns results in input order. No worker file, no
postMessage wiring; the constraint is that the function must be pure — no closures, no outer
references — because it's serialized via fn.toString().
import { pmap } from "@para/parallel";
import { readdir, readFile } from "node:fs/promises";
// pmap functions can't close over outer scope (they're shipped to the
// worker via fn.toString()). Pre-load bytes here, then send (path,
// bytes) pairs into the worker for hashing.
function hashBytes({ path, bytes }: { path: string; bytes: Uint8Array }) {
let h = 0xcbf29ce484222325n;
for (const b of bytes) h = ((h ^ BigInt(b)) * 0x100000001b3n) & 0xffffffffffffffffn;
return { path, hash: h.toString(16) };
}
const files = await readdir("./photos");
const inputs = await Promise.all(files.map(async name => ({
path: `./photos/${name}`,
bytes: new Uint8Array(await readFile(`./photos/${name}`)),
})));
const hashed = await pmap(hashBytes, inputs, { concurrency: 8 });
const groups = new Map<string, string[]>();
for (const { path, hash } of hashed) groups.set(hash, [...(groups.get(hash) ?? []), path]);
const dupes = [...groups.values()].filter(g => g.length > 1);
console.log(`${dupes.length} duplicate sets across ${files.length} files`);
import { pmap } from "@para/parallel";
import { readdir, readFile } from "node:fs/promises";
// `pure fun` makes the no-closure / no-outer-reference constraint a
// compile-time check. The Para parser rejects `this`, outer references,
// fetch, console, Date.now, Math.random, … inside the body.
pure fun hashBytes({ path, bytes }: { path: string; bytes: Uint8Array }) {
let h = 0xcbf29ce484222325n;
for (const b of bytes) h = ((h ^ BigInt(b)) * 0x100000001b3n) & 0xffffffffffffffffn;
return { path, hash: h.toString(16) };
}
const files = await readdir("./photos");
const inputs = await Promise.all(files.map(async name => ({
path: `./photos/${name}`,
bytes: new Uint8Array(await readFile(`./photos/${name}`)),
})));
const hashed = await pmap(hashBytes, inputs, { concurrency: 8 });
const groups = new Map<string, string[]>();
for (const { path, hash } of hashed) groups.set(hash, [...(groups.get(hash) ?? []), path]);
const dupes = [...groups.values()].filter(g => g.length > 1);
console.log(`${dupes.length} duplicate sets across ${files.length} files`);
CSV + Arrow — turn a 5 GB orders.csv into a per-country Parquet
@para/csv yields rows as it parses; the loop folds them into ~250 per-country totals;
@para/arrow writes those out as Parquet. The CSV never sits in memory.
import { parseStream } from "@para/csv";
import { fromRows, toParquet } from "@para/arrow";
// Tally revenue per country without loading the file into memory.
const totals = new Map<string, number>();
const stream = Bun.file("orders.csv").stream();
for await (const row of parseStream(stream, { header: true, dynamicTyping: true })) {
totals.set(row.country, (totals.get(row.country) ?? 0) + row.revenue);
}
// 5 GB of CSV → ~250 rows of summary → one Parquet file.
const summary = fromRows(
[...totals].map(([country, revenue]) => ({ country, revenue })).sort((a, b) => b.revenue - a.revenue),
);
await Bun.write("revenue-by-country.parquet", await toParquet(summary, { compression: "snappy" }));
MCP — connect to a filesystem server, list tools, call them
Spawns the reference filesystem MCP server over stdio, sandboxed to one directory. The same client connects to
any MCP server — Slack, GitHub, Postgres — without per-server adapter code. await using tears the
subprocess down on scope exit.
import { connectStdio } from "@para/mcp";
await using fs = await connectStdio("npx", [
"-y", "@modelcontextprotocol/server-filesystem", "/tmp/sandbox",
]);
await fs.callTool("write_file", { path: "/tmp/sandbox/note.txt", content: "from para-mcp" });
const dir = await fs.callTool("list_directory", { path: "/tmp/sandbox" });
console.log(dir.content[0].text); // note.txt
// Hand the same fs.listTools() output straight to a model and you have
// a tool-using agent over real disk — no glue per server, no schema
// translation, just whatever the MCP server advertises.
The rest
Three more libraries with their own docs:
-
@para/arena— typed-arrayPool+ scope helper -
@para/simd— WASM v128 vector primitives overFloat32Array -
@para/rtp— RFC 3550 packet framing + jitter buffer
Para Lang and Para Runtime
Para Lang — optional .pts syntax that desugars to imports from these packages.
Para Runtime (ParaBun) — Bun fork. Bundles these packages, parses .pts
natively, adds native modules for GPU, camera, audio, GPIO/I²C/SPI, and on-device LLM.