Other Modules
Additional npm packages and Node.js APIs supported by Perry. All listed here are wired through Perry’s well-known native bindings registry (#466) and compile to native code with no JavaScript runtime involvement.
sharp (Image Processing)
Native bindings via perry-ext-sharp (v0.5.551). Resizes, format conversion,
and buffer/file output all work.
import sharp from "sharp";
const buf = await sharp("input.jpg")
.resize(1600, 900)
.jpeg({ quality: 80 })
.toBuffer();
await sharp("input.png")
.resize(300, 200)
.toFile("output.png");
cheerio (HTML Parsing)
Native bindings via perry-ext-cheerio (v0.5.550).
import * as cheerio from "cheerio";
const html = "<html><body><h1>Hello</h1><p>World</p></body></html>";
const $ = cheerio.load(html);
console.log($("h1").text()); // "Hello"
nodemailer (Email)
import nodemailer from "nodemailer"
async function nodemailerExample(): Promise<void> {
const transporter = nodemailer.createTransport({
host: "smtp.example.com",
port: 587,
auth: { user: "user", pass: "pass" },
})
await transporter.sendMail({
from: "sender@example.com",
to: "recipient@example.com",
subject: "Hello from Perry",
text: "This email was sent from a compiled TypeScript binary!",
})
}
zlib (Compression)
Native bindings via perry-ext-zlib (v0.5.541).
import zlib from "zlib";
const compressed = zlib.gzipSync("Hello, World!");
const decompressed = zlib.gunzipSync(compressed);
console.log(decompressed.toString()); // "Hello, World!"
cron / node-cron (Job Scheduling)
Native bindings via perry-ext-cron (v0.5.564). Both cron and node-cron
package names route to the same backend.
import { CronJob } from "cron";
const job = new CronJob("*/5 * * * *", () => {
console.log("Runs every 5 minutes");
});
job.start();
ethers (Ethereum)
Native bindings via perry-ext-ethers (v0.5.556) — backed by
ethers-rs-style ABI plumbing through
perry-ffi’s BigInt + Buffer surfaces.
import { ethers } from "ethers";
const wallet = ethers.Wallet.createRandom();
console.log("address:", wallet.address);
console.log("private key:", wallet.privateKey);
events (EventEmitter)
Native bindings via perry-ext-events (v0.5.546). The EventEmitter shape
matches Node.js — on, off, once, emit, removeAllListeners.
import { EventEmitter } from "events";
const ee = new EventEmitter();
ee.on("data", (chunk) => console.log("got:", chunk));
ee.emit("data", "hello");
exponential-backoff (Retry Logic)
Native bindings via perry-ext-exponential-backoff (v0.5.542).
import { backOff } from "exponential-backoff";
const result = await backOff(() => fetchUnstableEndpoint(), {
numOfAttempts: 5,
startingDelay: 200,
timeMultiple: 2,
});
decimal.js / bignumber.js (Arbitrary Precision)
Native bindings via perry-ext-decimal (v0.5.547). Both package names route
to the same backend — Decimal and BigNumber are both exposed.
import Decimal from "decimal.js"
function decimalExample(): void {
const a = new Decimal("0.1")
const b = new Decimal("0.2")
const sum = a.plus(b) // Exactly 0.3 (no floating point errors)
console.log(sum.toFixed(2)) // "0.30"
console.log(sum.toNumber()) // 0.3
console.log(a.times(b).toFixed(2)) // "0.02"
console.log(a.div(b).toFixed(1)) // "0.5"
console.log(a.pow(10).toString()) // 1e-10
console.log(a.sqrt().toFixed(3)) // "0.316"
}
dayjs / date-fns (Date Manipulation)
Native bindings via perry-ext-dayjs (v0.5.548). Both package names route to
the same Rust backend — same parse/format/diff surface.
import dayjs from "dayjs";
const now = dayjs();
const tomorrow = now.add(1, "day");
console.log(tomorrow.format("YYYY-MM-DD"));
moment (Legacy Date)
Native bindings via perry-ext-moment (v0.5.549). moment is in maintenance
mode upstream — prefer dayjs for new code, but Perry supports both for
existing codebases.
import moment from "moment";
const m = moment().add(7, "days");
console.log(m.format());
rate-limiter-flexible
Native bindings via perry-ext-ratelimit (v0.5.552). In-memory limiter is
wired; Redis / cluster backing stores are follow-ups.
import { RateLimiterMemory } from "rate-limiter-flexible";
const limiter = new RateLimiterMemory({ points: 5, duration: 1 });
try {
await limiter.consume("ip-1.2.3.4");
} catch (rateLimitErr) {
console.warn("blocked:", rateLimitErr);
}
worker_threads
Partially recognized at HIR-lowering time (parentPort / Worker shapes)
but full dispatch is incomplete. For data-parallel work today, prefer
parallelMap / parallelFilter / spawn from perry/thread
(see Threading).
import { Worker, parentPort, workerData } from "worker_threads";
if (parentPort) {
// Worker thread
const data = workerData;
parentPort.postMessage({ result: data.value * 2 });
} else {
// Main thread
const worker = new Worker("./worker.ts", {
workerData: { value: 21 },
});
worker.on("message", (msg) => {
console.log(msg.result); // 42
});
}
commander (CLI Parsing)
import { Command } from "commander"
function commanderExample(): void {
const program = new Command()
program.name("my-cli").version("1.0.0").description("My CLI tool")
program
.command("serve")
.option("-p, --port <number>", "Port number")
.option("--verbose", "Verbose output")
.action((options: any) => {
console.log(`Starting server on port ${options.port}`)
})
program.parse(process.argv)
}
lru-cache
The wired constructor takes the npm v7+ options-object shape
(new LRUCache({ max: 100 })) — the older positional form
new LRUCache(100) falls through to a max=100 default.
import { LRUCache } from "lru-cache"
function lruCacheExample(): void {
const cache = new LRUCache({ max: 100 }) // max 100 entries
cache.set("key", "value")
console.log(cache.get("key")) // "value"
console.log(cache.has("key")) // true
cache.delete("key")
cache.clear()
}
child_process
import { spawnBackground, getProcessStatus, killProcess } from "child_process"
function childProcessExample(): void {
// Spawn a background process
const { pid, handleId } = spawnBackground("sleep", ["10"], "/tmp/log.txt")
// Check if it's still running
const status = getProcessStatus(handleId)
console.log(status.alive) // true
console.log(`pid=${pid}`)
// Kill it
killProcess(handleId)
}
External native bindings
Two packages live in their own GitHub repos with their own semver — they’re
imported by bun add like any npm package, but Rust-backed and compiled
natively via perry-ffi:
@perryts/tursodb— Turso (libSQL fork) database client. See PerryTS/tursodb-bindings.@perryts/iroh— Iroh peer-to-peer networking. See PerryTS/iroh-bindings.
Pure-TypeScript drivers compiled via compilePackages:
@perryts/postgres,@perryts/mysql,@perryts/mongodb,@perryts/redis— wire-protocol clients that also run on Node.js and Bun unchanged.
See Native Bindings — Overview for the contract these external packages follow.
Next Steps
- Overview — All stdlib modules
- File System — fs and path APIs
- Native Bindings — Authoring your own