Run each sample in an empty directory.
TypeScript without a build
Generics, union types and satisfies run directly, with no build step or tsconfig.json.
// shapes.ts
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rect"; width: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rect":
return shape.width * shape.height;
}
}
function largest<T>(items: T[], size: (item: T) => number): T {
return items.reduce((a, b) => (size(b) > size(a) ? b : a));
}
const shapes = [
{ kind: "circle", radius: 2 },
{ kind: "rect", width: 3, height: 5 },
] satisfies Shape[];
for (const s of shapes) console.log(`${s.kind}: ${area(s).toFixed(2)}`);
console.log(`largest: ${largest(shapes, area).kind}`);elide run shapes.tscircle: 12.57
rect: 15.00
largest: rectHTTP handler
// server.ts
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
return Response.json({ path: url.pathname });
},
};elide serve --no-tui server.ts
curl http://127.0.0.1:8080/hello{"path":"/hello"}Routes with URLPattern
Matches request paths with URLPattern in a handler served by elide serve.
// api.ts
const users = new Map([
["1", { id: 1, name: "Ada" }],
["2", { id: 2, name: "Grace" }],
]);
const userRoute = new URLPattern({ pathname: "/users/:id" });
export default {
async fetch(request: Request): Promise<Response> {
const match = userRoute.exec(request.url);
if (!match) return new Response("Not found\n", { status: 404 });
const user = users.get(match.pathname.groups.id!);
return user ? Response.json(user) : Response.json({ error: "no such user" }, { status: 404 });
},
};elide serve --no-tui api.ts
curl -s http://127.0.0.1:8080/users/2; echo
curl -s -w ' %{http_code}\n' http://127.0.0.1:8080/users/9{"id":2,"name":"Grace"}
{"error":"no such user"} 404Local server and fetch
Starts a node:http server on a free port, calls it with fetch, and shuts it down.
// roundtrip.ts
import { createServer } from "node:http";
import type { AddressInfo } from "node:net";
const server = createServer((req, res) => {
let body = "";
req.on("data", (chunk) => (body += chunk));
req.on("end", () => {
const { items } = JSON.parse(body) as { items: number[] };
res.setHeader("content-type", "application/json");
res.end(JSON.stringify({ count: items.length, sum: items.reduce((a, b) => a + b, 0) }));
});
});
server.listen(0, "127.0.0.1", async () => {
const { port } = server.address() as AddressInfo;
const response = await fetch(`http://127.0.0.1:${port}/sum`, {
method: "POST",
body: JSON.stringify({ items: [4, 8, 15, 16, 23, 42] }),
});
console.log(response.status, response.headers.get("content-type"));
console.log(JSON.stringify(await response.json()));
server.close();
});elide run roundtrip.ts200 application/json
{"count":6,"sum":108}Embedded SQLite
// database.ts
import { Database } from "elide:sqlite";
const db = new Database();
db.exec("CREATE TABLE users (name TEXT)");
db.exec("INSERT INTO users VALUES ('Ada')");
console.log(db.query("SELECT name FROM users").get().name);elide run database.tsAdaSQLite transactions
Moves money between accounts in a SQLite transaction that rolls back when a balance would go negative.
// bank.ts
import { Database } from "elide:sqlite";
const db = new Database();
db.exec("CREATE TABLE accounts (name TEXT PRIMARY KEY, balance INTEGER NOT NULL)");
const open = db.prepare("INSERT INTO accounts VALUES ($name, $balance)");
open.run({ $name: "alice", $balance: 100 });
open.run({ $name: "bob", $balance: 20 });
const adjust = db.prepare("UPDATE accounts SET balance = balance + ? WHERE name = ?");
const balance = db.prepare("SELECT balance FROM accounts WHERE name = ?");
const transfer = db.transaction((from: string, to: string, amount: number) => {
adjust.run(-amount, from);
adjust.run(amount, to);
if (balance.get(from).balance < 0) throw new Error(`${from} cannot send ${amount}`);
});
transfer("alice", "bob", 30);
try {
transfer("bob", "alice", 500);
} catch (e) {
console.log("rolled back:", (e as Error).message);
}
for (const [name, amount] of db.prepare("SELECT name, balance FROM accounts ORDER BY name").values()) {
console.log(name, amount);
}elide run bank.tsrolled back: bob cannot send 500
alice 70
bob 50Access rules with CEL
Compiles permission rules written in CEL once with elide:cel, then checks them against each user and document. CEL expressions have no side effects and always terminate, so rules can come from configuration.
// rules.ts
import { compile } from "elide:cel";
const rules = {
"read": "true",
"edit": "user.id == doc.owner || 'admin' in user.roles",
"export": "user.plan == 'pro' && !doc.tags.exists(t, t.startsWith('pii'))",
};
const programs = Object.entries(rules).map(([name, rule]) => ({ name, program: compile(rule) }));
const docs = [
{ id: 1, owner: "ada", tags: ["report"] },
{ id: 2, owner: "grace", tags: ["pii-email", "report"] },
];
const users = [
{ id: "ada", plan: "pro", roles: [] },
{ id: "grace", plan: "free", roles: ["admin"] },
];
for (const user of users) {
for (const doc of docs) {
const allowed = programs.filter(({ program }) => program.test({ user, doc }));
console.log(`${user.id} on doc ${doc.id}: ${allowed.map(({ name }) => name).join(", ")}`);
}
}
console.log("export reads:", programs[2]?.program.variables.join(", "));elide run rules.tsada on doc 1: read, edit, export
ada on doc 2: read
grace on doc 1: read, edit
grace on doc 2: read, edit
export reads: doc, userFiles and paths
Writes a small directory tree with node:fs, then walks it and totals file sizes by extension.
// sizes.ts
import { mkdirSync, readdirSync, statSync, writeFileSync } from "node:fs";
import { extname, join } from "node:path";
mkdirSync("site/css", { recursive: true });
writeFileSync("site/index.html", "<h1>Home</h1>");
writeFileSync("site/about.html", "<h1>About us</h1>");
writeFileSync("site/css/main.css", "body { margin: 0 }");
const totals = new Map<string, number>();
for (const entry of readdirSync("site", { recursive: true, withFileTypes: true })) {
if (!entry.isFile()) continue;
const ext = extname(entry.name);
const size = statSync(join(entry.parentPath, entry.name)).size;
totals.set(ext, (totals.get(ext) ?? 0) + size);
}
for (const [ext, bytes] of [...totals].sort()) console.log(`${ext}: ${bytes} bytes`);elide run sizes.ts.css: 18 bytes
.html: 30 bytesCSV report
Parses a CSV file and prints revenue per region, largest first.
# sales.csv
region,product,units,price
north,widget,12,2.50
south,widget,4,2.50
north,gadget,3,10.00
east,gizmo,7,4.00
south,gadget,6,10.00// report.ts
import { readFileSync } from "node:fs";
type Row = { region: string; product: string; units: number; price: number };
const [header, ...lines] = readFileSync("sales.csv", "utf8").trim().split("\n");
const keys = header.split(",");
const rows: Row[] = lines.map((line) => {
const v = line.split(",");
return { region: v[0], product: v[1], units: Number(v[2]), price: Number(v[3]) };
});
const revenue = new Map<string, number>();
for (const r of rows) revenue.set(r.region, (revenue.get(r.region) ?? 0) + r.units * r.price);
console.log(`${keys.length} columns, ${rows.length} rows`);
for (const [region, total] of [...revenue].sort((a, b) => b[1] - a[1])) {
console.log(`${region.padEnd(6)} ${total.toFixed(2).padStart(8)}`);
}elide run report.ts4 columns, 5 rows
south 70.00
north 60.00
east 28.00Streams and async iteration
Reads a log file line by line with node:readline and counts entries per level.
// levels.mjs
import { createReadStream, writeFileSync } from "node:fs";
import { createInterface } from "node:readline";
writeFileSync("app.log", [
"INFO server started",
"WARN slow query: 812ms",
"INFO request /users",
"ERROR connection reset",
"INFO request /orders",
].join("\n"));
const counts = {};
const lines = createInterface({ input: createReadStream("app.log") });
for await (const line of lines) {
const level = line.split(" ", 1)[0];
counts[level] = (counts[level] ?? 0) + 1;
}
console.log(JSON.stringify(counts));elide run levels.mjs{"INFO":3,"WARN":1,"ERROR":1}Transform stream pipeline
Chains a generator source, a Transform and a writable sink with pipeline from node:stream/promises.
// pipeline.mjs
import { Readable, Transform, Writable } from "node:stream";
import { pipeline } from "node:stream/promises";
async function* records() {
yield { sku: "A-1", qty: 3, price: 4.5 };
yield { sku: "B-7", qty: 1, price: 19.99 };
yield { sku: "C-2", qty: 10, price: 0.25 };
}
const toCsv = new Transform({
objectMode: true,
transform(r, _enc, done) {
done(null, `${r.sku},${r.qty},${(r.qty * r.price).toFixed(2)}\n`);
},
});
const lines = [];
const sink = new Writable({
objectMode: true,
write(chunk, _enc, done) {
lines.push(chunk.toString().trim());
done();
},
});
await pipeline(Readable.from(records()), toCsv, sink);
console.log(["sku,qty,total", ...lines].join("\n"));elide run pipeline.mjssku,qty,total
A-1,3,13.50
B-7,1,19.99
C-2,10,2.50Command-line arguments
Parses flags and positional arguments with parseArgs from node:util.
// greet.mjs
import { parseArgs } from "node:util";
const { values, positionals } = parseArgs({
args: process.argv.slice(2),
options: {
greeting: { type: "string", short: "g", default: "Hello" },
shout: { type: "boolean", default: false },
},
allowPositionals: true,
});
for (const name of positionals) {
const line = `${values.greeting}, ${name}!`;
console.log(values.shout ? line.toUpperCase() : line);
}elide run greet.mjs -- --greeting Hi Ada Grace
elide run greet.mjs -- --shout AlanHi, Ada!
Hi, Grace!
HELLO, ALAN!Worker threads
Counts primes below 200,000 in four node:worker_threads workers and adds up their results.
// primes.mjs
import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";
function countPrimes(from, to) {
let count = 0;
for (let n = Math.max(from, 2); n < to; n++) {
let prime = true;
for (let d = 2; d * d <= n; d++) if (n % d === 0) { prime = false; break; }
if (prime) count++;
}
return count;
}
if (isMainThread) {
const limit = 200_000, parts = 4, step = limit / parts;
const jobs = Array.from({ length: parts }, (_, i) =>
new Promise((resolve, reject) => {
const w = new Worker(new URL(import.meta.url), { workerData: [i * step, (i + 1) * step] });
w.once("message", resolve);
w.once("error", reject);
}));
const counts = await Promise.all(jobs);
console.log("per worker:", counts.join(", "));
console.log("primes below", limit, "=", counts.reduce((a, b) => a + b));
} else {
parentPort.postMessage(countPrimes(...workerData));
}elide run primes.mjsper worker: 5133, 4459, 4256, 4136
primes below 200000 = 17984TypeScript tests
Runs node:test tests written in TypeScript with elide test, with no configuration. --reporter=tap prints TAP instead of the summary.
// slugify.ts
export function slugify(title: string): string {
return title
.toLowerCase()
.trim()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-|-$/g, "");
}// slugify.test.ts
import { describe, test } from "node:test";
import assert from "node:assert";
import { slugify } from "./slugify.ts";
describe("slugify", () => {
test("lowercases and joins words", () => {
assert.equal(slugify("Hello World"), "hello-world");
});
test("drops punctuation at the ends", () => {
assert.equal(slugify(" Elide: a runtime! "), "elide-a-runtime");
});
});elide test --reporter=tapTAP version 13
# start 1: slugify > lowercases and joins words
ok 1 - slugify > lowercases and joins words
# start 2: slugify > drops punctuation at the ends
ok 2 - slugify > drops punctuation at the ends
1..2Java records and streams
Runs a single Java file with records, switch expressions and streams, with no build file.
// Payroll.java
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
public class Payroll {
enum Role { ENGINEER, DESIGNER, MANAGER }
record Employee(String name, Role role, int salary) {}
public static void main(String[] args) {
var staff = List.of(
new Employee("Ada", Role.ENGINEER, 120_000),
new Employee("Linus", Role.ENGINEER, 110_000),
new Employee("Susan", Role.DESIGNER, 95_000),
new Employee("Grace", Role.MANAGER, 130_000));
Map<Role, Double> average = staff.stream().collect(
Collectors.groupingBy(Employee::role, TreeMap::new, Collectors.averagingInt(Employee::salary)));
average.forEach((role, avg) -> {
String label = switch (role) {
case ENGINEER -> "Engineering";
case DESIGNER -> "Design";
case MANAGER -> "Management";
};
System.out.printf("%-12s %,8.0f%n", label, avg);
});
}
}elide run Payroll.javaEngineering 115,000
Design 95,000
Management 130,000Kotlin collections
Groups, sums, sorts and partitions a list of orders with the Kotlin standard library.
// Orders.kt
data class Order(val customer: String, val item: String, val amount: Double)
fun main() {
val orders = listOf(
Order("ada", "book", 24.0),
Order("grace", "laptop", 1299.0),
Order("ada", "pen", 3.5),
Order("alan", "book", 24.0),
Order("grace", "mouse", 25.0),
)
orders.groupBy { it.customer }
.mapValues { (_, list) -> list.sumOf { it.amount } }
.entries
.sortedByDescending { it.value }
.forEach { (customer, total) -> println("%-6s %8.2f".format(customer, total)) }
val (books, other) = orders.partition { it.item == "book" }
println("books: ${books.size}, other items: ${other.map { it.item }.distinct().sorted()}")
}elide run Orders.ktgrace 1324.00
ada 27.50
alan 24.00
books: 2, other items: [laptop, mouse, pen]Kotlin tests
Runs kotlin.test tests with elide test in a project with an elide.pkl. --reporter=tap prints TAP instead of the summary.
// elide.pkl
amends "elide:project.pkl"
name = "pricing"// src/main/kotlin/Pricing.kt
fun priceWithTax(net: Double, ratePercent: Int): Double = Math.round(net * (100 + ratePercent)) / 100.0// src/test/kotlin/PricingTest.kt
import kotlin.test.Test
import kotlin.test.assertEquals
class PricingTest {
@Test
fun addsTax() {
assertEquals(12.0, priceWithTax(10.0, 20))
}
@Test
fun roundsToCents() {
assertEquals(10.69, priceWithTax(9.99, 7))
}
}elide test --reporter=tapTAP version 13
# start 1: PricingTest > addsTax()
ok 1 - PricingTest > addsTax()
# start 2: PricingTest > roundsToCents()
ok 2 - PricingTest > roundsToCents()
1..2Kotlin coroutines
Starts three loads with async and waits for all of them; the shortest delay finishes first. kotlinx.coroutines is
bundled, so a single file needs no build file.
// Fetch.kt
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
suspend fun load(name: String, ms: Long): Int {
delay(ms)
println("loaded $name")
return name.length
}
fun main() = runBlocking {
val sizes = listOf("users" to 300L, "orders" to 200L, "prices" to 100L)
.map { (name, ms) -> async { load(name, ms) } }
.awaitAll()
println("sizes in request order: $sizes")
}elide run Fetch.ktloaded prices
loaded orders
loaded users
sizes in request order: [5, 6, 6]Kotlin JSON serialization
Decodes JSON into a @Serializable data class, filling defaults, and encodes a copy. The serialization plugin and
runtime are bundled.
// Config.kt
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@Serializable
data class Server(val host: String, val port: Int = 8080, val tags: List<String> = emptyList())
fun main() {
val json = Json { prettyPrint = true }
val server = json.decodeFromString<Server>("""{"host": "localhost", "tags": ["dev"]}""")
println(server)
println(json.encodeToString(server.copy(port = 9090)))
}elide run Config.ktServer(host=localhost, port=8080, tags=[dev])
{
"host": "localhost",
"port": 9090,
"tags": [
"dev"
]
}Java with a Maven dependency
Declares Gson in elide.pkl; elide run resolves it, compiles and runs jvm.main.
// elide.pkl
amends "elide:project.pkl"
name = "inventory"
jvm {
main = "app.Inventory"
}
dependencies {
maven {
packages { "com.google.code.gson:gson:2.13.1" }
}
}// src/main/java/app/Inventory.java
package app;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.List;
public class Inventory {
record Item(String sku, int quantity, double price) {}
public static void main(String[] args) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
Item[] items = gson.fromJson("""
[{"sku": "A-1", "quantity": 3, "price": 2.5},
{"sku": "B-2", "quantity": 1, "price": 10.0}]
""", Item[].class);
double total = List.of(items).stream().mapToDouble(i -> i.quantity() * i.price()).sum();
System.out.println(items.length + " items, total " + total);
System.out.println(gson.toJson(items[0]));
}
}elide run2 items, total 17.5
{
"sku": "A-1",
"quantity": 3,
"price": 2.5
}Elide annotations in Kotlin
Marks an interface with @API and a method with @Delicate, which callers must opt in to. Kotlin projects get
dev.elide.annotations on the classpath; see Elide annotations.
// elide.pkl
amends "elide:project.pkl"
name = "cache"
jvm {
main = "MainKt"
}// src/main/kotlin/Main.kt
import dev.elide.annotations.API
import dev.elide.annotations.Delicate
@API
interface Cache {
fun get(key: String): String?
fun put(key: String, value: String)
}
class MemoryCache : Cache {
private val entries = mutableMapOf<String, String>()
override fun get(key: String) = entries[key]
override fun put(key: String, value: String) {
entries[key] = value
}
@Delicate
fun clear() = entries.clear()
}
@OptIn(Delicate::class)
fun main() {
val cache = MemoryCache()
cache.put("user:1", "Ada")
println(cache.get("user:1"))
cache.clear()
println(cache.get("user:1"))
println(Cache::class.java.isAnnotationPresent(API::class.java))
}elide runAda
null
trueWithout @OptIn(Delicate::class), the build fails with This API is marked delicate; opt in with @OptIn(Delicate::class) if you understand its caveats.
Python calling JavaScript
Needs the Python component and --interop.
# interop.py
from elide import js
total = js("[1, 2, 3, 4].reduce((a, b) => a + b, 0)")
print(total)elide --interop python interop.py10JavaScript importing Python
Needs the Python component and --interop.
# greet.py
def hello():
return "Hello from Python"// main.mjs
import greet from "./greet.py";
console.log(greet.hello());elide --interop run main.mjsHello from PythonWebAssembly from JavaScript
Builds a WebAssembly library from C with zig, then imports its exports as an ES module. Needs zig to build the .wasm file.
// checksum.c
unsigned int adler32(const unsigned char *data, unsigned int len) {
unsigned int a = 1, b = 0;
for (unsigned int i = 0; i < len; i++) {
a = (a + data[i]) % 65521;
b = (b + a) % 65521;
}
return (b << 16) | a;
}// main.mjs
import { adler32, memory } from "./checksum.wasm";
const input = new TextEncoder().encode("Wikipedia");
new Uint8Array(memory.buffer).set(input, 1024);
console.log(adler32(1024, input.length).toString(16));zig cc --target=wasm32-freestanding -O2 -nostdlib -Wl,--no-entry -Wl,--export=adler32 -o checksum.wasm checksum.c
elide run main.mjs11e60398WASI command
Builds a WASI command from C with zig and runs it with arguments. Needs zig to build the .wasm file.
// wc.c
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
int chars = 0;
for (int i = 1; i < argc; i++) {
chars += strlen(argv[i]);
printf("%d: %s\n", i, argv[i]);
}
printf("%d words, %d characters\n", argc - 1, chars);
return 0;
}zig cc --target=wasm32-wasi -O2 -o wc.wasm wc.c
elide run wc.wasm -- polyglot runtime toolchain1: polyglot
2: runtime
3: toolchain
3 words, 24 charactersPython script
Python’s standard library runs with no virtual environment. Needs the Python component (elide setup python).
# words.py
import json
import re
from collections import Counter
from dataclasses import asdict, dataclass
@dataclass
class Report:
words: int
unique: int
top: list[tuple[str, int]]
text = """Elide runs Python, JavaScript and TypeScript.
Python runs without a separate interpreter; JavaScript runs too."""
words = re.findall(r"[a-z]+", text.lower())
report = Report(len(words), len(set(words)), Counter(words).most_common(3))
print(json.dumps(asdict(report)))elide python words.py{"words": 15, "unique": 11, "top": [["runs", 3], ["python", 2], ["javascript", 2]]}PostgreSQL queries
A prepared batch insert, a transaction and an aggregate query with elide:postgres. Start a server first, for example
docker run -d -e POSTGRES_USER=app -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=app -p 127.0.0.1:5432:5432 postgres:17.
// orders.ts
import { Database } from "elide:postgres";
const db = await Database.connect(process.env.DATABASE_URL!);
await db.exec("DROP TABLE IF EXISTS orders");
await db.exec("CREATE TABLE orders (id SERIAL PRIMARY KEY, customer TEXT NOT NULL, total NUMERIC(10, 2))");
const insert = await db.prepare("INSERT INTO orders (customer, total) VALUES ($1, $2)");
await insert.executeBatch([["ada", 42.5], ["grace", 18], ["ada", 7.25]]);
await insert.close();
await db.transaction(async (tx) => {
await tx.exec("UPDATE orders SET total = total * 0.9 WHERE customer = $1", "ada");
});
const result = await db.query(
"SELECT customer, count(*)::int AS orders, sum(total)::float AS spent FROM orders GROUP BY customer ORDER BY customer",
);
for (const row of await result.all()) console.log(row);
await db.close();DATABASE_URL=postgresql://app:secret@127.0.0.1:5432/app elide run orders.ts{customer: "ada", orders: 2, spent: 44.78}
{customer: "grace", orders: 1, spent: 18}Server-sent events
A ReadableStream body streams events as they are produced; request.signal stops the timer when the client leaves.
// events.ts
const encoder = new TextEncoder();
Elide.serve({
hostname: "127.0.0.1",
port: 3000,
fetch(request) {
let timer: ReturnType<typeof setInterval>;
const body = new ReadableStream({
start(controller) {
let n = 0;
timer = setInterval(() => {
n += 1;
controller.enqueue(encoder.encode(`event: tick\ndata: ${n}\n\n`));
if (n === 3) {
clearInterval(timer);
controller.close();
}
}, 500);
request.signal.addEventListener("abort", () => clearInterval(timer));
},
});
return new Response(body, {
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache" },
});
},
});elide run events.ts
curl -N http://127.0.0.1:3000/event: tick
data: 1
event: tick
data: 2
event: tick
data: 3Each event arrives half a second after the previous one.
Request limits
maxBodyBytes rejects large request bodies before the handler reads them.
// limits.mjs
const server = Elide.serve({
hostname: "127.0.0.1",
port: 0,
maxBodyBytes: 1024,
async fetch(request) {
const body = await request.text();
return new Response(`received ${body.length} bytes\n`);
},
});
setTimeout(async () => {
for (const size of [512, 4096]) {
const res = await fetch(`http://127.0.0.1:${server.port}/`, { method: "POST", body: "x".repeat(size) });
console.log(size, res.status, (await res.text()).trim());
}
server.close();
}, 0);elide run limits.mjs512 200 received 512 bytes
4096 413 Payload Too Largenode:http server and client
// http.mjs
import http from "node:http";
const server = http.createServer((req, res) => {
let body = "";
req.on("data", (chunk) => (body += chunk));
req.on("end", () => {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ method: req.method, url: req.url, received: body.length }));
});
});
server.listen(0, "127.0.0.1", () => {
const { port } = server.address();
const req = http.request({ port, host: "127.0.0.1", method: "POST", path: "/items" }, (res) => {
let data = "";
res.setEncoding("utf8");
res.on("data", (chunk) => (data += chunk));
res.on("end", () => {
console.log(res.statusCode, res.headers["content-type"]);
console.log(data);
server.close();
});
});
req.end("hello");
});elide run http.mjs200 application/json
{"method":"POST","url":"/items","received":5}Sandboxed subprocess
--allow-run lists the commands a program may start; any other command fails.
// spawn.mjs
import { execFileSync } from "node:child_process";
console.log(execFileSync("echo", ["allowed"], { encoding: "utf8" }).trim());
try {
execFileSync("ls", ["/"]);
} catch (e) {
console.log(e.message);
}elide --allow-run=echo run spawn.mjsallowed
spawn of 'ls' is not permitted; grant it with --allow-run=lsSign data with Web Crypto
// sign.ts
const { privateKey, publicKey } = await crypto.subtle.generateKey(
{ name: "ECDSA", namedCurve: "P-256" },
true,
["sign", "verify"],
);
const message = new TextEncoder().encode("ship order 1042");
const algorithm = { name: "ECDSA", hash: "SHA-256" };
const signature = await crypto.subtle.sign(algorithm, privateKey, message);
console.log("signature bytes:", signature.byteLength);
console.log("valid:", await crypto.subtle.verify(algorithm, publicKey, signature, message));
const tampered = new TextEncoder().encode("ship order 9999");
console.log("tampered valid:", await crypto.subtle.verify(algorithm, publicKey, signature, tampered));
const jwk = await crypto.subtle.exportKey("jwk", publicKey);
console.log("public key:", jwk.kty, jwk.crv);elide run sign.tssignature bytes: 64
valid: true
tampered valid: false
public key: EC P-256Gzip with CompressionStream
// gzip.ts
const text = "elide ".repeat(1000);
const compressed = await new Response(
new Blob([text]).stream().pipeThrough(new CompressionStream("gzip")),
).arrayBuffer();
const restored = await new Response(
new Blob([compressed]).stream().pipeThrough(new DecompressionStream("gzip")),
).text();
console.log(`original: ${text.length} bytes`);
console.log(`gzip: ${compressed.byteLength} bytes`);
console.log(`round trip: ${restored === text}`);elide run gzip.tsoriginal: 6000 bytes
gzip: 73 bytes
round trip: trueDates with Temporal
Month arithmetic clamps to the end of the month, and zoned times keep their wall-clock time across a daylight-saving change.
// dates.ts
const start = Temporal.PlainDate.from("2026-01-30");
console.log(start.add({ months: 1 }).toString());
const meeting = Temporal.ZonedDateTime.from("2026-03-28T09:00[Europe/London]");
console.log(meeting.add({ days: 1 }).toString());
console.log(meeting.withTimeZone("America/New_York").toPlainTime().toString());
const due = Temporal.PlainDate.from("2026-12-25");
console.log(start.until(due, { largestUnit: "months" }).toString());elide run dates.ts2026-02-28
2026-03-29T09:00:00+01:00[Europe/London]
05:00:00
P10M25DLimit workers
--limit=workers=N caps live workers; one more throws ERR_WORKER_LIMIT.
// workers.mjs
import { Worker } from "node:worker_threads";
const source = "setInterval(() => {}, 1000)"; // stays alive until terminated
const workers = [];
for (let i = 1; i <= 3; i++) {
try {
workers.push(new Worker(source, { eval: true }));
console.log(`worker ${i} started`);
} catch (e) {
console.log(`worker ${i}: ${e.code}`);
}
}
await Promise.all(workers.map((w) => w.terminate()));elide --limit=workers=2 run workers.mjsworker 1 started
worker 2 started
worker 3: ERR_WORKER_LIMITFilter tests with CEL
--where selects tests with a CEL expression over name, parents and file.
// cart.test.ts
import { describe, test } from "node:test";
import assert from "node:assert/strict";
describe("cart", () => {
test("adds items", () => assert.equal([1, 2].length, 2));
test("applies discount [slow]", () => assert.equal(Math.round(10 * 0.9), 9));
});
describe("checkout", () => {
test("charges card", () => assert.ok(true));
test("sends receipt [slow]", () => assert.ok(true));
});elide test --where 'name.endsWith("[slow]")'cart.test.ts
⇥ cart > adds items
✓ cart > applies discount [slow] [1ms]
⇥ checkout > charges card
✓ checkout > sends receipt [slow] [3ms]
2 pass
2 skip
0 fail
Ran 4 tests across one file. (0.14s)JUnit XML in CI
--reporter junit writes one XML file per test file for CI test-report steps; a failure exits 1.
// price.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
const total = (items: number[]) => items.reduce((a, b) => a + b, 0);
test("sums prices", () => assert.equal(total([2, 3]), 5));
test("empty cart", () => assert.equal(total([]), 1));elide test --reporter junit --reporter-outfile reports 1 pass
0 skip
1 fail
Ran 2 tests across one file. (0.11s)reports/js/TEST-price.test.ts.xml:
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="price.test.ts" tests="2" failures="1" errors="0" skipped="0" time="0.026">
<testcase name="sums prices" classname="price.test.ts" time="0.005"/>
<testcase name="empty cart" classname="price.test.ts" time="0.021">
<failure message="Expected values to be strictly equal: 0 !== 1 " type="ERR_ASSERTION">…</failure>
</testcase>
</testsuite>Multi-context server
--contexts=auto runs one copy of the module per core (Linux). Each copy keeps its own globals, so the counter counts
per context.
// counter.ts
const id = crypto.randomUUID().slice(0, 8);
let count = 0;
export default {
fetch(): Response {
count += 1;
return Response.json({ context: id, count });
},
};elide serve --no-tui --contexts=auto --port 8080 counter.ts
for i in 1 2 3 4; do curl -s http://127.0.0.1:8080/; echo; done{"context":"60cf3464","count":1}
{"context":"60e90376","count":1}
{"context":"90086101","count":1}
{"context":"4adfe8e1","count":1}Each curl opens a new connection, which the server may hand to any context.
Custom span in TypeScript
Spans from @opentelemetry/api, exported by Elide with no SDK installed. using ends the inner span with its block.
Start a local collector first: docker run --rm -p 4318:4318 otel/opentelemetry-collector:0.115.1.
// checkout.ts
import { trace } from "@opentelemetry/api";
import { currentSpanContext, flush } from "elide:telemetry";
const tracer = trace.getTracer("checkout");
async function chargeCard(amount: number): Promise<void> {
using span = tracer.startSpan("charge card");
span.setAttribute("payment.amount", amount);
await new Promise((resolve) => setTimeout(resolve, 20));
}
await tracer.startActiveSpan("checkout", async (span) => {
span.setAttribute("order.id", 42);
await chargeCard(19.99);
console.log("charged order 42, sampled:", currentSpanContext()?.traceFlags === 1);
span.end();
});
console.log("exported:", await flush());OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_SERVICE_NAME=checkout elide run checkout.tscharged order 42, sampled: true
exported: trueThe collector receives checkout with its child charge card, and the console.log line as a log record with
their trace id.
Metrics instruments
A counter, a histogram with explicit buckets, and an observable gauge.
// metrics.ts
import { metrics } from "@opentelemetry/api";
import { flush } from "elide:telemetry";
const meter = metrics.getMeter("shop");
const orders = meter.createCounter("shop.orders", { unit: "{order}" });
const latency = meter.createHistogram("shop.charge.duration", {
unit: "s",
advice: { explicitBucketBoundaries: [0.05, 0.1, 0.25, 0.5] },
});
const queue: string[] = ["a", "b"];
meter
.createObservableGauge("shop.queue.depth", { unit: "{job}" })
.addCallback((result) => result.observe(queue.length));
for (const [region, seconds] of [["eu", 0.08], ["us", 0.21], ["eu", 0.04]] as const) {
orders.add(1, { region });
latency.record(seconds, { region });
}
console.log("exported:", await flush());OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_SERVICE_NAME=shop elide run metrics.tsexported: trueThe collector’s debug output, trimmed:
InstrumentationScope shop
-> Name: shop.orders
Data point attributes:
-> region: Str(eu)
Value: 2.000000
-> Name: shop.charge.duration
Data point attributes:
-> region: Str(eu)
Count: 2
Sum: 0.120000
-> Name: shop.queue.depth
Value: 2.000000Python span and logging
Needs the Python component. logging output is exported as log records with the span’s ids; no
opentelemetry package is needed.
# orders.py
import logging
import elide.telemetry
from opentelemetry import trace
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s")
log = logging.getLogger("orders")
tracer = trace.get_tracer("orders")
with tracer.start_as_current_span("import orders") as span:
span.set_attribute("orders.count", 3)
log.info("imported %d orders", 3)
print("sampled:", elide.telemetry.current_span_context()["trace_flags"] == 1)
print("exported:", elide.telemetry.flush())OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_SERVICE_NAME=orders elide python orders.pyINFO orders: imported 3 orders
sampled: True
exported: TrueOTLP to a local collector
A served handler continues the caller’s trace: the server span takes the traceparent parent, and the handler’s own
span is its child.
// server.ts
import { trace } from "@opentelemetry/api";
const tracer = trace.getTracer("inventory");
export default {
async fetch(request: Request): Promise<Response> {
const id = new URL(request.url).pathname.split("/").pop() ?? "";
return tracer.startActiveSpan("load item", async (span) => {
span.setAttribute("item.id", id);
span.end();
return Response.json({ id, stock: 7 });
});
},
};docker run --rm -d -p 4317:4317 -p 4318:4318 otel/opentelemetry-collector:0.115.1
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_SERVICE_NAME=inventory elide serve server.ts
curl -H 'traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' http://localhost:8080/items/17{"id":"17","stock":7}The collector’s debug output, trimmed:
InstrumentationScope inventory
Trace ID : 4bf92f3577b34da6a3ce929d0e0e4736
Parent ID : 6a28bf795e58acfb
Name : load item
-> item.id: Str(17)
InstrumentationScope dev.elide.runtime 1.6.0
Trace ID : 4bf92f3577b34da6a3ce929d0e0e4736
Parent ID : 00f067aa0ba902b7
ID : 6a28bf795e58acfb
Name : GET
Kind : Server
-> url.path: Str(/items/17)
-> http.response.status_code: Int(200)
-> Name: http.server.request.duration
Exemplar #0
-> Trace ID: 4bf92f3577b34da6a3ce929d0e0e4736
-> Span ID: 6a28bf795e58acfbBaggage across services
Elide’s server ignores an incoming baggage header until inbound baggage is on, and then admits only allowed keys.
// server.ts
import { context, propagation } from "@opentelemetry/api";
export default {
async fetch(): Promise<Response> {
const baggage = propagation.getBaggage(context.active());
const tenant = baggage?.getEntry("tenant")?.value ?? "none";
const debug = baggage?.getEntry("debug")?.value ?? "none";
return new Response(`tenant=${tenant} debug=${debug}\n`);
},
};OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \
ELIDE_OTEL_BAGGAGE_INBOUND=true ELIDE_OTEL_BAGGAGE_ALLOW=tenant \
elide serve server.ts
curl -H 'baggage: tenant=acme,debug=true' http://localhost:8080/tenant=acme debug=noneWithout ELIDE_OTEL_BAGGAGE_INBOUND=true, the response is tenant=none debug=none.
No samples match.