One codebase. Every platform. Native performance.
Perry is a native TypeScript compiler written in Rust. It takes your TypeScript and compiles it straight to native executables — no Node.js, no Electron, no browser engine. Just fast, small binaries that run anywhere.
Current Version: 0.5.17 | Website | Documentation | Showcase
perry compile src/main.ts -o myapp
./myapp # that's it — a standalone native binaryPerry uses SWC for TypeScript parsing and LLVM for native code generation. The output is a single binary with no runtime dependencies.
People are building real apps with Perry today. Here are some highlights:
| Project | What it is | Platforms |
|---|---|---|
| Bloom Engine | Native TypeScript game engine — Metal, DirectX 12, Vulkan, OpenGL. Write games in TS, ship native. | macOS, Windows, Linux, iOS, tvOS, Android |
| Mango | Native MongoDB GUI. ~7 MB binary, <100 MB RAM, sub-second cold start. | macOS, Windows, Linux, iOS, Android |
| Hone | AI-powered native code editor with built-in terminal, Git, and LSP. | macOS, Windows, Linux, iOS, Android, Web |
| Pry | Fast, native JSON viewer with tree navigation and search. | macOS, iOS, Android |
| dB Meter | Real-time sound level measurement with 60fps updates and per-device calibration. | iOS, macOS, Android |
Mango — Native MongoDB GUI (source)
Hone — AI-powered native code editor (hone.codes)
Have something you've built with Perry? Open a PR to add it here!
Perry beats Node.js, Bun, and Static Hermes on every benchmark. Best of 3 runs, macOS ARM64 (Apple Silicon), Node.js v25, Bun 1.3, Static Hermes (static_h).
| Benchmark | Perry | Node.js | Bun | Hermes | vs Node | What it tests |
|---|---|---|---|---|---|---|
| factorial | 24ms | 592ms | 99ms | 112ms | 24.6x faster | Modular accumulation (integer fast path) |
| method_calls | 2ms | 11ms | 7ms | 103ms | 5.5x faster | Class method dispatch (10M calls) |
| loop_overhead | 13ms | 53ms | 51ms | 96ms | 4x faster | Tight numeric loop (100M iterations) |
| math_intensive | 14ms | 50ms | 51ms | 51ms | 3.6x faster | Harmonic series with sqrt/sin/cos |
| fibonacci(40) | 310ms | 991ms | 514ms | 2553ms | 3.2x faster | Recursive function calls (i64 specialization) |
| array_read | 4ms | 14ms | 18ms | 46ms | 3.5x faster | Sequential read (10M elements) |
| closure | 8ms | 305ms | 50ms | 294ms | 38x faster | Closure creation + invocation (10M calls) |
| array_write | 2ms | 9ms | 6ms | 92ms | 4.5x faster | Sequential write (10M elements) |
| object_create | 4ms | 8ms | 7ms | 2ms | 2x faster | Object allocation (1M objects, scalar replacement) |
| binary_trees | 3ms | 9ms | 7ms | 3ms | 3x faster | Tree allocation + traversal (1M nodes, scalar replacement) |
| string_concat | 0ms | 2ms | 1ms | 1ms | fast | 100K string appends |
| nested_loops | 9ms | 17ms | 19ms | 82ms | 1.9x faster | Nested array access (3000x3000) |
| prime_sieve | 4ms | 8ms | 6ms | 27ms | 2x faster | Sieve of Eratosthenes |
| mandelbrot | 21ms | 25ms | 29ms | 23ms | 1.2x faster | Complex f64 iteration (800x800) |
| matrix_multiply | 22ms | 34ms | 34ms | 232ms | 1.5x faster | 256x256 matrix multiply |
Perry compiles to native machine code via LLVM — no JIT warmup, no interpreter overhead. Key optimizations: scalar replacement of non-escaping objects (escape analysis eliminates heap allocation entirely — object fields become registers), inline bump allocator for objects that do escape, i32 loop counters for bounded array access, reassoc contract fast-math flags for f64 vectorization, integer-modulo fast path (fptosi → srem → sitofp instead of fmod), elimination of redundant js_number_coerce calls on numeric function returns, and i64 specialization for pure numeric recursive functions.
Perry also competes with systems languages. All implementations use f64/double to match TypeScript's number type — no SIMD intrinsics, no unsafe code. See benchmarks/polyglot/ for source and methodology.
| Benchmark | Perry | Rust | C++ | Go | Swift | Java | Node | Python |
|---|---|---|---|---|---|---|---|---|
| fibonacci | 316 | 317 | 308 | 444 | 401 | 278 | 994 | 15777 |
| loop_overhead | 12 | 96 | 96 | 96 | 96 | 97 | 53 | 2951 |
| array_write | 2 | 6 | 1 | 8 | 2 | 6 | 8 | 390 |
| array_read | 4 | 9 | 9 | 10 | 9 | 10 | 13 | 331 |
| math_intensive | 14 | 48 | 50 | 48 | 48 | 51 | 49 | 2212 |
| object_create | 2 | 0 | 0 | 0 | 0 | 4 | 8 | 161 |
| nested_loops | 8 | 8 | 8 | 9 | 8 | 10 | 16 | 473 |
| accumulate | 24 | 96 | 96 | 96 | 96 | 97 | 592 | 4953 |
Perry beats or ties Rust and C++ on 7 of 8 benchmarks. Object_create dropped from 8ms to 2ms thanks to scalar replacement — non-escaping objects are decomposed into register-allocated fields, bringing Perry within striking distance of Rust/C++ (which report 0ms because their structs are stack-allocated by default). Perry's advantage on numeric benchmarks comes from domain-specific optimizations — reassoc fast-math flags, integer-modulo fast path, and i64 specialization for recursive numeric functions — that exploit properties of TypeScript's number type which strict-IEEE compilers can't assume by default. See benchmarks/polyglot/RESULTS.md for full analysis.
Perry switched from Cranelift to LLVM as its sole code generation backend in v0.5.0. The initial cutover had significant performance regressions due to NaN-boxing overhead in the new backend. Subsequent optimization work recovered and surpassed the original numbers:
| Benchmark | Cranelift | LLVM v0.5.0 | LLVM now | Node.js |
|---|---|---|---|---|
| method_calls | 16ms | 1,084ms | 2ms | 11ms |
| math_intensive | 370ms | 131ms | 14ms | 50ms |
| object_create | 5ms | 318ms | 4ms | 8ms |
| binary_trees | — | — | 3ms | 9ms |
| matrix_multiply | 61ms | 184ms | 22ms | 34ms |
| nested_loops | 32ms | 57ms | 9ms | 17ms |
| array_read | 4ms | 26ms | 4ms | 14ms |
| mandelbrot | 71ms | 47ms | 21ms | 25ms |
| string_concat | 7ms | 0–1ms | 0ms | 2ms |
| prime_sieve | 11ms | 11ms | 4ms | 8ms |
| fibonacci(40) | 505ms | 1,156ms | 310ms | 991ms |
| closure | — | — | 8ms | 305ms |
| factorial | — | — | 24ms | 592ms |
The Cranelift column is from the pre-v0.5.0 era (the old README on main). LLVM v0.5.0 was the initial cutover — it regressed badly because the new backend routed most operations through runtime helpers instead of inlining them. The current LLVM column shows the state after scalar replacement of non-escaping objects, inline bump allocators, i32 loop counters, fast-math flags, integer-mod fast paths, loop-invariant length hoisting, and redundant number-coerce elimination. LLVM now beats both Cranelift and Node on every workload.
Cranelift is often praised for fast compilation, and it is — but the difference is smaller than you'd expect. Perry previously used Cranelift and switched to LLVM in v0.5.0. Compile times increased by only ~20-50ms (8-19%), because the bulk of Perry's compile time is SWC parsing, HIR lowering, and linking — not the codegen backend. On a typical file LLVM adds about 25ms over Cranelift while producing code that runs up to 24x faster. A worthwhile trade.
Run benchmarks yourself: cd benchmarks/suite && ./run_benchmarks.sh (requires node, cargo; optional: bun, shermes).
Perry produces small, self-contained binaries with no external dependencies at run time:
| Program | Binary Size |
|---|---|
console.log("Hello, world!") |
~330KB |
hello world + fs / path / process imports |
~380KB |
| full stdlib app (fastify, mysql2, etc.) | ~48MB |
with --enable-js-runtime (V8 embedded) |
+~15MB |
Perry automatically detects which parts of the runtime your program uses and only links what's needed.
brew install perryts/perry/perrywinget install PerryTS.Perrycurl -fsSL https://perryts.github.io/perry-apt/perry.gpg.pub | sudo gpg --dearmor -o /usr/share/keyrings/perry.gpg
echo "deb [signed-by=/usr/share/keyrings/perry.gpg] https://perryts.github.io/perry-apt stable main" | sudo tee /etc/apt/sources.list.d/perry.list
sudo apt update && sudo apt install perrycurl -fsSL https://raw.githubusercontent.com/PerryTS/perry/main/packaging/install.sh | shgit clone https://github.com/PerryTS/perry.git
cd perry
cargo build --release
# Binary at: target/release/perryPerry requires a C linker to link compiled executables:
- macOS: Xcode Command Line Tools (
xcode-select --install) - Linux: GCC or Clang (
sudo apt install build-essential) - Windows: MSVC (Visual Studio Build Tools)
Run perry doctor to verify your environment.
# Initialize a new project
perry init my-project
cd my-project
# Compile and run
perry compile src/main.ts -o myapp
./myapp
# Or compile and run in one step
perry run .
# Check TypeScript compatibility
perry check src/
# Diagnose environment
perry doctorPerry supports standard ES module imports and npm packages. Here's a real-world API server with multi-file project structure:
Project layout:
my-api/
├── package.json
├── src/
│ ├── main.ts
│ ├── config.ts
│ └── routes/
│ └── users.ts
└── node_modules/
src/config.ts
export const config = {
port: 3000,
dbHost: process.env.DB_HOST || 'localhost',
};src/routes/users.ts
export function getUsers(): object[] {
return [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
}
export function getUserById(id: number): object | undefined {
return getUsers().find((u: any) => u.id === id);
}src/main.ts
import fastify from 'fastify';
import { config } from './config';
import { getUsers, getUserById } from './routes/users';
const app = fastify();
app.get('/api/users', async () => {
return getUsers();
});
app.get('/api/users/:id', async (request) => {
const { id } = request.params as { id: string };
return getUserById(parseInt(id));
});
app.listen({ port: config.port }, () => {
console.log(`Server running on port ${config.port}`);
});Compile and run:
perry compile src/main.ts -o my-api && ./my-api
# or: perry run .The output is a standalone binary — no node_modules needed at runtime.
The example-code/ directory contains ready-to-run projects showing Perry in real-world scenarios:
| Example | Stack | What it demonstrates |
|---|---|---|
| express-postgres | Express + PostgreSQL | Multi-file routes, middleware (CORS, Helmet), connection pooling, error handling |
| fastify-redis-mysql | Fastify + Redis + MySQL | Rate limiting, caching layer, database queries, dotenv config |
| hono-mongodb | Hono + MongoDB | Lightweight HTTP framework with document database |
| nestjs-typeorm | NestJS + TypeORM | Decorator-based architecture, dependency injection |
| nextjs-prisma | Next.js-style + Prisma | ORM integration, database migrations |
| koa-redis | Koa + Redis | Middleware composition, session storage |
| http-server | Raw HTTP | Low-level request handling, routing, JSON APIs |
| blockchain-demo | Custom | Blockchain implementation in pure TypeScript |
Each example has its own package.json and can be compiled with:
cd example-code/fastify-redis-mysql
npm install
perry compile src/index.ts -o server && ./serverPerry includes a declarative UI system (perry/ui) that compiles directly to native platform widgets — no WebView, no Electron. The programming model is SwiftUI-like: compose native widgets with stack-based layout, alignment, and distribution — not CSS/HTML.
import {
App, VStack, HStack, Text, Button, Spacer, SplitView, splitViewAddChild,
stackSetAlignment, stackSetDistribution, widgetAddChild, widgetMatchParentWidth,
} from 'perry/ui';
// Sidebar + content layout with a split view
const sidebar = VStack(8, [Text("Projects"), Text("Settings"), Spacer()]);
sidebar.setEdgeInsets(12, 12, 12, 12);
sidebar.setBackgroundColor("#F5F5F5");
const header = HStack(8, [Text("Dashboard"), Spacer(), Button("New", () => {})]);
const actions = HStack(8, [Button("Cancel", () => {}), Button("Save", () => {})]);
stackSetDistribution(actions, 1); // FillEqually — both buttons get equal width
const content = VStack(16, [header, Text("Welcome back!"), Spacer(), actions]);
content.setEdgeInsets(20, 20, 20, 20);
stackSetAlignment(content, 5); // Leading — children align left
const split = SplitView();
splitViewAddChild(split, sidebar);
splitViewAddChild(split, content);
App({ title: 'My App', width: 800, height: 500, body: split });9 platforms from one codebase:
| Platform | Backend | Target Flag |
|---|---|---|
| macOS | AppKit (NSView) | (default on macOS) |
| iOS / iPadOS | UIKit | --target ios / --target ios-simulator |
| tvOS | UIKit | --target tvos / --target tvos-simulator |
| watchOS | WatchKit | --target watchos / --target watchos-simulator |
| Android | Android Views (JNI) | --target android |
| Windows | Win32 | (default on Windows) |
| Linux | GTK4 | (default on Linux) |
| Web | DOM (JS codegen) | --target web |
| WebAssembly | DOM (WASM) | --target wasm |
127+ UI functions — widgets (Button, Text, TextField, Toggle, Slider, Picker, Table, Canvas, Image, ProgressView, SecureField, NavigationStack, ZStack, LazyVStack, Form/Section, CameraView, SplitView), layout control (alignment, distribution, match-parent, content hugging, overlay positioning, edge insets), and system APIs (keychain, notifications, file dialogs, clipboard, dark mode, openURL, audio capture).
The perry/thread module provides real OS threads with compile-time safety — no shared mutable state, no data races:
import { parallelMap, parallelFilter, spawn } from 'perry/thread';
// Data-parallel array processing across all CPU cores
const results = parallelMap([1, 2, 3, 4, 5], n => fibonacci(n));
// Parallel filtering
const evens = parallelFilter(numbers, n => n % 2 === 0);
// Background thread with Promise
const result = await spawn(() => expensiveComputation());Values cross threads via deep-copy. Each thread gets its own arena and GC. The compiler enforces that closures don't capture mutable state.
Compile-time localization with zero runtime overhead:
import { t, Currency, ShortDate } from 'perry/i18n';
console.log(t('hello')); // "Hallo" (German locale)
console.log(t('items', { count: 3 })); // "3 Artikel" (CLDR plural rules)
console.log(Currency(9.99, 'EUR')); // "9,99 €"
console.log(ShortDate(Date.now())); // "24.03.2026"Configure in perry.toml:
[i18n]
default_locale = "en"
locales = ["en", "de", "fr", "ja"]All locale strings are baked into the binary at compile time. Native locale detection on all 6 platforms. CLDR plural rules for 30+ locales.
Build native home screen widgets from TypeScript — iOS, Android, watchOS, and Wear OS:
perry compile src/widget.ts --target ios-widget -o MyWidget
perry compile src/widget.ts --target android-widget -o MyWidget
perry compile src/widget.ts --target watchos-widget -o MyWidget
perry compile src/widget.ts --target wearos-tile -o MyWidget# Desktop (default for host platform)
perry compile src/main.ts -o myapp
# Mobile
perry compile src/main.ts --target ios -o MyApp
perry compile src/main.ts --target ios-simulator -o MyApp
perry compile src/main.ts --target android -o MyApp
# TV / Watch
perry compile src/main.ts --target tvos -o MyApp
perry compile src/main.ts --target watchos -o MyApp
# Web
perry compile src/main.ts --target web -o app.html # JavaScript output
perry compile src/main.ts --target wasm -o app.wasm # WebAssembly output
# Home screen widgets
perry compile src/widget.ts --target ios-widget -o MyWidget
perry compile src/widget.ts --target android-widget -o MyWidget
perry compile src/widget.ts --target wearos-tile -o MyWidgetperry publish macos # or: ios / android / linuxperry publish sends your TypeScript source to perry-hub (the cloud build server), which cross-compiles and signs for each target platform.
| Feature | Status |
|---|---|
| Variables (let, const, var) | ✅ |
| All operators (+, -, *, /, %, **, &, |, ^, <<, >>, ???, ?., ternary) | ✅ |
| Control flow (if/else, for, while, switch, break, continue) | ✅ |
| Try-catch-finally, throw | ✅ |
| Functions, arrow functions, rest params, defaults | ✅ |
| Closures with mutable captures | ✅ |
| Classes (inheritance, private fields #, static, getters/setters, super) | ✅ |
| Generics (monomorphized at compile time) | ✅ |
| Interfaces, type aliases, union types, type guards | ✅ |
| Async/await, Promise | ✅ |
| Generators (function*) | ✅ |
ES modules (import/export, re-exports, import * as) |
✅ |
| Destructuring (array, object, rest, defaults, rename) | ✅ |
| Spread operator in calls and literals | ✅ |
| RegExp (test, match, replace) | ✅ |
| BigInt (256-bit) | ✅ |
| Decorators | ✅ |
| Module | Functions |
|---|---|
console |
log, error, warn, debug |
fs |
readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, readdirSync, statSync, readFileBuffer, rmRecursive |
path |
join, dirname, basename, extname, resolve |
process |
env, exit, cwd, argv, uptime, memoryUsage |
JSON |
parse, stringify |
Math |
floor, ceil, round, abs, sqrt, pow, min, max, random, log, sin, cos, tan, PI |
Date |
Date.now(), new Date(), toISOString(), component getters |
crypto |
randomBytes, randomUUID, sha256, md5 |
os |
platform, arch, hostname, homedir, tmpdir, totalmem, freemem, uptime, type, release |
Buffer |
from, alloc, allocUnsafe, byteLength, isBuffer, concat; instance methods |
child_process |
execSync, spawnSync, spawnBackground, getProcessStatus, killProcess |
Map |
get, set, has, delete, size, clear, forEach, keys, values, entries |
Set |
add, has, delete, size, clear, forEach |
setTimeout/clearTimeout |
✅ |
setInterval/clearInterval |
✅ |
worker_threads |
parentPort, workerData |
These packages are natively implemented in Rust — no Node.js required:
| Category | Packages |
|---|---|
| HTTP | fastify, axios, node-fetch, ws (WebSocket) |
| Database | mysql2, pg, ioredis |
| Security | bcrypt, argon2, jsonwebtoken |
| Utilities | dotenv, uuid, nodemailer, zlib, node-cron |
Perry can compile pure TypeScript/JavaScript npm packages directly to native code instead of routing them through the V8 runtime. Add a perry.compilePackages array to your package.json:
{
"perry": {
"compilePackages": [
"@noble/curves",
"@noble/hashes",
"superstruct"
]
}
}Then compile with --enable-js-runtime as usual. Packages in the list are compiled natively; all others use the V8 runtime.
Good candidates: Pure math/crypto libraries, serialization/encoding, data structures with no I/O. Keep as V8-interpreted: Packages using HTTP/WebSocket, native addons, or unsupported Node.js builtins.
- Scalar Replacement — escape analysis identifies non-escaping objects (
let p = new Point(x, y); sum += p.x + p.y); fields are decomposed into stack allocas that LLVM promotes to registers — zero heap allocation - NaN-Boxing — all values are 64-bit words (f64/u64); no boxing overhead for numbers
- Mark-Sweep GC — conservative stack scan, arena block walking, 8-byte GcHeader per alloc
- Inline Bump Allocator — objects that do escape use a 13-cycle inline arena bump (no function call on hot path)
- Parallel Compilation — rayon-based module codegen, transform passes, and symbol scanning across CPU cores
- FMA / CSE / Loop Unrolling — fused multiply-add, common subexpression elimination, 8x loop unroll
- Fast-Math Flags —
reassoc contracton all f64 ops enables LLVM to break serial accumulator chains into parallel accumulators + NEON vectorization - Integer-Modulo Fast Path —
fptosi → srem → sitofpinstead offmodfor provably-integer locals (64x speedup on factorial) - i64 Specialization — pure numeric recursive functions compile to native
i64registers (no f64 round-trips) - i32 Loop Counters — integer registers for loop variables (no f64 round-trips)
- LICM — loop-invariant code motion for nested loops
- Shape-Cached Objects — 5-6x faster object allocation for escaping objects
- TimSort — O(n log n) hybrid sort for
Array.sort() __platform__Constant — compile-time platform elimination (dead code removal per target)
Compile TypeScript as a native shared library plugin:
perry compile my-plugin.ts --output-type dylib -o my-plugin.dylibimport { PluginRegistry } from 'perry/plugin';
export function activate(api: any) {
api.registerTool('my-tool', (args: any) => { /* ... */ });
api.on('event', (data: any) => { /* ... */ });
}Perry includes Geisterhand, an in-process UI testing framework with HTTP-driven interaction and screenshot capture:
perry compile src/main.ts --enable-geisterhand -o myapp
./myapp
# UI test server runs on http://localhost:7676Supports screenshot capture on all native platforms. See the Geisterhand docs for details.
| Package | Description |
|---|---|
| Bloom Engine | Native TypeScript game engine — 2D/3D rendering, skeletal animation, spatial audio, physics. Metal/DirectX 12/Vulkan/OpenGL. |
| perry-react | React/JSX that compiles to native widgets. Standard React components → native macOS/iOS/Android app. |
| perry-sqlite | SQLite with a Prisma-compatible API (findMany, create, upsert, $transaction, etc.) |
| perry-postgres | PostgreSQL with the same Prisma-compatible API |
| perry-prisma | MySQL with the same Prisma-compatible API |
| perry-apn | Apple Push Notifications (APNs) native library |
| @perry/threads | Web Worker parallelism (parallelMap, parallelFilter, spawn) for browser/Node.js |
| perry-starter | Minimal starter project — get up and running in 30 seconds |
| perry-demo | Live benchmark dashboard comparing Perry vs Node.js vs Bun |
| perry-react-dom | Perry React DOM bridge |
Write React components that compile to native widgets — no DOM, no browser:
import { useState } from 'react';
import { createRoot } from 'react-dom/client';
function Counter() {
const [n, setN] = useState(0);
return (
<div>
<h1>Count: {n}</h1>
<button onClick={() => setN(n + 1)}>+</button>
</div>
);
}
createRoot(null, { title: 'Counter', width: 300, height: 200 }).render(<Counter />);Drop-in replacements for @prisma/client backed by Rust (sqlx):
import { PrismaClient } from 'perry-sqlite';
const prisma = new PrismaClient();
await prisma.$connect();
const users = await prisma.user.findMany({
where: { email: { contains: '@example.com' } },
orderBy: { createdAt: 'desc' },
take: 20,
});
await prisma.$disconnect();| Command | What it does |
|---|---|
perry compile <input.ts> -o <output> |
Compile TypeScript to a native binary |
perry run <path> [platform] |
Compile and run in one step (supports ios, android, etc.) |
perry init <name> |
Scaffold a new project |
perry check <path> |
Validate TypeScript compatibility without compiling |
perry publish <platform> |
Build, sign, and publish via the cloud build server |
perry doctor |
Check your development environment |
perry i18n extract |
Extract translatable strings from source |
-o, --output <name> Output file name
--target <target> ios | ios-simulator | tvos | tvos-simulator |
watchos | watchos-simulator | android |
web | wasm | ios-widget | android-widget |
wearos-tile | watchos-widget
--output-type <type> executable | dylib
--enable-js-runtime Embed V8 for npm package compatibility (+~15MB)
--enable-geisterhand Enable UI testing server
--print-hir Print HIR for debugging
perry/
├── crates/
│ ├── perry/ # CLI (compile, run, check, init, doctor, publish)
│ ├── perry-parser/ # SWC TypeScript parser
│ ├── perry-types/ # Type system
│ ├── perry-hir/ # HIR data structures and AST→HIR lowering
│ ├── perry-transform/ # IR passes (closure conversion, async, inlining)
│ ├── perry-codegen/ # LLVM native codegen
│ ├── perry-codegen-js/ # JavaScript codegen (--target web)
│ ├── perry-codegen-wasm/ # WebAssembly codegen (--target wasm)
│ ├── perry-codegen-swiftui/ # SwiftUI codegen (iOS/watchOS widgets)
│ ├── perry-codegen-glance/ # Android Glance widget codegen
│ ├── perry-codegen-wear-tiles/ # Wear OS Tiles codegen
│ ├── perry-runtime/ # Runtime (NaN-boxing, GC, arena, strings)
│ ├── perry-stdlib/ # Node.js API support (fastify, mysql2, redis, etc.)
│ ├── perry-ui-*/ # Native UI (macOS, iOS, tvOS, watchOS, Android, GTK4, Windows)
│ ├── perry-ui-geisterhand/ # UI testing framework
│ ├── perry-jsruntime/ # Optional V8 interop via QuickJS
│ └── perry-diagnostics/ # Error reporting
├── docs/ # Documentation site (mdBook)
├── example-code/ # 8 example applications
├── benchmarks/ # Benchmark suite (Perry vs Node.js vs Bun)
├── packages/ # npm packages (@perry/threads)
└── test-files/ # Test suite
- Garbage Collection — mark-sweep GC with conservative stack scanning, arena block walking, 8-byte GcHeader per allocation
- Single-Threaded by Default — async I/O on Tokio workers, callbacks on main thread. Use
perry/threadfor explicit multi-threading. - No Runtime Type Checking — types erased at compile time. Use
typeofandinstanceoffor runtime checks. - Small Binaries — ~330KB hello world, ~48MB with full stdlib. Automatically stripped.
cargo build --release # Build everything
cargo build --release -p perry-runtime -p perry-stdlib # Rebuild runtime (after changes)
cargo test --workspace --exclude perry-ui-ios # Run tests
cargo run --release -- compile file.ts -o out && ./out # Compile and run
cargo run --release -- compile file.ts --print-hir # Debug HIR- HIR — add node type to
crates/perry-hir/src/ir.rs - Lowering — handle AST→HIR in
crates/perry-hir/src/lower.rs - Codegen — generate LLVM IR in
crates/perry-codegen/src/codegen.rs - Runtime — add runtime functions in
crates/perry-runtime/if needed - Test — add
test-files/test_feature.ts
MIT


