Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

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