Skip to content

Code samples

Runnable Elide samples in TypeScript, JavaScript, Java, Kotlin, Python and WebAssembly: HTTP servers, SQLite, files, streams, workers, coroutines, JSON, Maven dependencies, annotations, tests, interop and OpenTelemetry, each with its command and output.

On this page

Run each sample in an empty directory.

TypeScript without a build

TypeScriptData

Generics, union types and satisfies run directly, with no build step or tsconfig.json.

ts
// 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}`);
utf-8 · ts · Lines: 25
Terminal
bash
elide run shapes.ts
text
circle: 12.57
rect: 15.00
largest: rect
utf-8 · text · Lines: 3

HTTP handler

TypeScriptHTTP
ts
// server.ts
export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    return Response.json({ path: url.pathname });
  },
};
utf-8 · ts · Lines: 7
Terminal
bash
elide serve --no-tui server.ts
curl http://127.0.0.1:8080/hello
text
{"path":"/hello"}
utf-8 · text · Lines: 1

Routes with URLPattern

TypeScriptHTTP

Matches request paths with URLPattern in a handler served by elide serve.

ts
// 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 });
  },
};
utf-8 · ts · Lines: 16
Terminal
bash
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
text
{"id":2,"name":"Grace"}
{"error":"no such user"} 404
utf-8 · text · Lines: 2

Local server and fetch

TypeScriptHTTP

Starts a node:http server on a free port, calls it with fetch, and shuts it down.

ts
// 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();
});
utf-8 · ts · Lines: 24
Terminal
bash
elide run roundtrip.ts
text
200 application/json
{"count":6,"sum":108}
utf-8 · text · Lines: 2

Embedded SQLite

TypeScriptDatabases
ts
// 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);
utf-8 · ts · Lines: 7
Terminal
bash
elide run database.ts
text
Ada
utf-8 · text · Lines: 1

SQLite transactions

TypeScriptDatabases

Moves money between accounts in a SQLite transaction that rolls back when a balance would go negative.

ts
// 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);
}
utf-8 · ts · Lines: 28
Terminal
bash
elide run bank.ts
text
rolled back: bob cannot send 500
alice 70
bob 50
utf-8 · text · Lines: 3

Access rules with CEL

TypeScriptExpressions

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.

ts
// 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(", "));
utf-8 · ts · Lines: 27
Terminal
bash
elide run rules.ts
text
ada 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, user
utf-8 · text · Lines: 5

Files and paths

TypeScriptFiles

Writes a small directory tree with node:fs, then walks it and totals file sizes by extension.

ts
// 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`);
utf-8 · ts · Lines: 18
Terminal
bash
elide run sizes.ts
text
.css: 18 bytes
.html: 30 bytes
utf-8 · text · Lines: 2

CSV report

TypeScriptDataFiles

Parses a CSV file and prints revenue per region, largest first.

text
# 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
utf-8 · text · Lines: 7
ts
// 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)}`);
}
utf-8 · ts · Lines: 19
Terminal
bash
elide run report.ts
text
4 columns, 5 rows
south     70.00
north     60.00
east      28.00
utf-8 · text · Lines: 4

Streams and async iteration

JavaScriptData

Reads a log file line by line with node:readline and counts entries per level.

javascript
// 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));
utf-8 · javascript · Lines: 19
Terminal
bash
elide run levels.mjs
text
{"INFO":3,"WARN":1,"ERROR":1}
utf-8 · text · Lines: 1

Transform stream pipeline

JavaScriptData

Chains a generator source, a Transform and a writable sink with pipeline from node:stream/promises.

javascript
// 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"));
utf-8 · javascript · Lines: 28
Terminal
bash
elide run pipeline.mjs
text
sku,qty,total
A-1,3,13.50
B-7,1,19.99
C-2,10,2.50
utf-8 · text · Lines: 4

Command-line arguments

JavaScriptCLI

Parses flags and positional arguments with parseArgs from node:util.

javascript
// 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);
}
utf-8 · javascript · Lines: 16
Terminal
bash
elide run greet.mjs -- --greeting Hi Ada Grace
elide run greet.mjs -- --shout Alan
text
Hi, Ada!
Hi, Grace!
HELLO, ALAN!
utf-8 · text · Lines: 3

Worker threads

JavaScriptConcurrency

Counts primes below 200,000 in four node:worker_threads workers and adds up their results.

javascript
// 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));
}
utf-8 · javascript · Lines: 27
Terminal
bash
elide run primes.mjs
text
per worker: 5133, 4459, 4256, 4136
primes below 200000 = 17984
utf-8 · text · Lines: 2

TypeScript tests

TypeScriptTesting

Runs node:test tests written in TypeScript with elide test, with no configuration. --reporter=tap prints TAP instead of the summary.

ts
// slugify.ts
export function slugify(title: string): string {
  return title
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-|-$/g, "");
}
utf-8 · ts · Lines: 8
ts
// 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");
  });
});
utf-8 · ts · Lines: 14
Terminal
bash
elide test --reporter=tap
text
TAP 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..2
utf-8 · text · Lines: 6

Java records and streams

JavaData

Runs a single Java file with records, switch expressions and streams, with no build file.

java
// 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);
    });
  }
}
utf-8 · java · Lines: 31
Terminal
bash
elide run Payroll.java
text
Engineering   115,000
Design         95,000
Management    130,000
utf-8 · text · Lines: 3

Kotlin collections

KotlinData

Groups, sums, sorts and partitions a list of orders with the Kotlin standard library.

kotlin
// 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()}")
}
utf-8 · kotlin · Lines: 21
Terminal
bash
elide run Orders.kt
text
grace   1324.00
ada       27.50
alan      24.00
books: 2, other items: [laptop, mouse, pen]
utf-8 · text · Lines: 4

Kotlin tests

KotlinTesting

Runs kotlin.test tests with elide test in a project with an elide.pkl. --reporter=tap prints TAP instead of the summary.

pkl
// elide.pkl
amends "elide:project.pkl"

name = "pricing"
utf-8 · pkl · Lines: 4
kotlin
// src/main/kotlin/Pricing.kt
fun priceWithTax(net: Double, ratePercent: Int): Double = Math.round(net * (100 + ratePercent)) / 100.0
utf-8 · kotlin · Lines: 2
kotlin
// 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))
  }
}
utf-8 · kotlin · Lines: 15
Terminal
bash
elide test --reporter=tap
text
TAP version 13
# start 1: PricingTest > addsTax()
ok 1 - PricingTest > addsTax()
# start 2: PricingTest > roundsToCents()
ok 2 - PricingTest > roundsToCents()
1..2
utf-8 · text · Lines: 6

Kotlin coroutines

KotlinConcurrency

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.

kotlin
// 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")
}
utf-8 · kotlin · Lines: 18
Terminal
bash
elide run Fetch.kt
text
loaded prices
loaded orders
loaded users
sizes in request order: [5, 6, 6]
utf-8 · text · Lines: 4

Kotlin JSON serialization

KotlinData

Decodes JSON into a @Serializable data class, filling defaults, and encodes a copy. The serialization plugin and runtime are bundled.

kotlin
// 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)))
}
utf-8 · kotlin · Lines: 13
Terminal
bash
elide run Config.kt
text
Server(host=localhost, port=8080, tags=[dev])
{
    "host": "localhost",
    "port": 9090,
    "tags": [
        "dev"
    ]
}
utf-8 · text · Lines: 8

Java with a Maven dependency

JavaDependencies

Declares Gson in elide.pkl; elide run resolves it, compiles and runs jvm.main.

pkl
// elide.pkl
amends "elide:project.pkl"

name = "inventory"

jvm {
  main = "app.Inventory"
}

dependencies {
  maven {
    packages { "com.google.code.gson:gson:2.13.1" }
  }
}
utf-8 · pkl · Lines: 14
java
// 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]));
  }
}
utf-8 · java · Lines: 22
Terminal
bash
elide run
text
2 items, total 17.5
{
  "sku": "A-1",
  "quantity": 3,
  "price": 2.5
}
utf-8 · text · Lines: 6

Elide annotations in Kotlin

KotlinAnnotations

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.

pkl
// elide.pkl
amends "elide:project.pkl"

name = "cache"

jvm {
  main = "MainKt"
}
utf-8 · pkl · Lines: 8
kotlin
// 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))
}
utf-8 · kotlin · Lines: 32
Terminal
bash
elide run
text
Ada
null
true
utf-8 · text · Lines: 3

Without @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

PythonJavaScriptInterop

Needs the Python component and --interop.

python
# interop.py
from elide import js

total = js("[1, 2, 3, 4].reduce((a, b) => a + b, 0)")
print(total)
utf-8 · python · Lines: 5
Terminal
bash
elide --interop python interop.py
text
10
utf-8 · text · Lines: 1

JavaScript importing Python

JavaScriptPythonInterop

Needs the Python component and --interop.

python
# greet.py
def hello():
    return "Hello from Python"
utf-8 · python · Lines: 3
javascript
// main.mjs
import greet from "./greet.py";
console.log(greet.hello());
utf-8 · javascript · Lines: 3
Terminal
bash
elide --interop run main.mjs
text
Hello from Python
utf-8 · text · Lines: 1

WebAssembly from JavaScript

JavaScriptWebAssembly

Builds a WebAssembly library from C with zig, then imports its exports as an ES module. Needs zig to build the .wasm file.

c
// 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;
}
utf-8 · c · Lines: 9
javascript
// 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));
utf-8 · javascript · Lines: 6
Terminal
bash
zig cc --target=wasm32-freestanding -O2 -nostdlib -Wl,--no-entry -Wl,--export=adler32 -o checksum.wasm checksum.c
elide run main.mjs
text
11e60398
utf-8 · text · Lines: 1

WASI command

WebAssemblyCLI

Builds a WASI command from C with zig and runs it with arguments. Needs zig to build the .wasm file.

c
// 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;
}
utf-8 · c · Lines: 13
Terminal
bash
zig cc --target=wasm32-wasi -O2 -o wc.wasm wc.c
elide run wc.wasm -- polyglot runtime toolchain
text
1: polyglot
2: runtime
3: toolchain
3 words, 24 characters
utf-8 · text · Lines: 4

Python script

PythonData

Python’s standard library runs with no virtual environment. Needs the Python component (elide setup python).

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)))
utf-8 · python · Lines: 18
Terminal
bash
elide python words.py
text
{"words": 15, "unique": 11, "top": [["runs", 3], ["python", 2], ["javascript", 2]]}
utf-8 · text · Lines: 1

PostgreSQL queries

TypeScriptDatabases

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.

ts
// 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();
utf-8 · ts · Lines: 22
Terminal
bash
DATABASE_URL=postgresql://app:secret@127.0.0.1:5432/app elide run orders.ts
text
{customer: "ada", orders: 2, spent: 44.78}
{customer: "grace", orders: 1, spent: 18}
utf-8 · text · Lines: 2

Server-sent events

TypeScriptHTTP

A ReadableStream body streams events as they are produced; request.signal stops the timer when the client leaves.

ts
// 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" },
    });
  },
});
utf-8 · ts · Lines: 27
Terminal
bash
elide run events.ts
curl -N http://127.0.0.1:3000/
text
event: tick
data: 1

event: tick
data: 2

event: tick
data: 3
utf-8 · text · Lines: 8

Each event arrives half a second after the previous one.

Request limits

JavaScriptHTTPSandbox

maxBodyBytes rejects large request bodies before the handler reads them.

js
// 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);
utf-8 · js · Lines: 17
Terminal
bash
elide run limits.mjs
text
512 200 received 512 bytes
4096 413 Payload Too Large
utf-8 · text · Lines: 2

node:http server and client

JavaScriptHTTP
js
// 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");
});
utf-8 · js · Lines: 26
Terminal
bash
elide run http.mjs
text
200 application/json
{"method":"POST","url":"/items","received":5}
utf-8 · text · Lines: 2

Sandboxed subprocess

JavaScriptSandboxCLI

--allow-run lists the commands a program may start; any other command fails.

js
// 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);
}
utf-8 · js · Lines: 9
Terminal
bash
elide --allow-run=echo run spawn.mjs
text
allowed
spawn of 'ls' is not permitted; grant it with --allow-run=ls
utf-8 · text · Lines: 2

Sign data with Web Crypto

TypeScriptCrypto
ts
// 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);
utf-8 · ts · Lines: 19
Terminal
bash
elide run sign.ts
text
signature bytes: 64
valid: true
tampered valid: false
public key: EC P-256
utf-8 · text · Lines: 4

Gzip with CompressionStream

TypeScriptData
ts
// 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}`);
utf-8 · ts · Lines: 14
Terminal
bash
elide run gzip.ts
text
original: 6000 bytes
gzip: 73 bytes
round trip: true
utf-8 · text · Lines: 3

Dates with Temporal

TypeScriptData

Month arithmetic clamps to the end of the month, and zoned times keep their wall-clock time across a daylight-saving change.

ts
// 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());
utf-8 · ts · Lines: 10
Terminal
bash
elide run dates.ts
text
2026-02-28
2026-03-29T09:00:00+01:00[Europe/London]
05:00:00
P10M25D
utf-8 · text · Lines: 4

Limit workers

JavaScriptConcurrencySandbox

--limit=workers=N caps live workers; one more throws ERR_WORKER_LIMIT.

js
// 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()));
utf-8 · js · Lines: 14
Terminal
bash
elide --limit=workers=2 run workers.mjs
text
worker 1 started
worker 2 started
worker 3: ERR_WORKER_LIMIT
utf-8 · text · Lines: 3

Filter tests with CEL

TypeScriptTestingExpressions

--where selects tests with a CEL expression over name, parents and file.

ts
// 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));
});
utf-8 · ts · Lines: 13
Terminal
bash
elide test --where 'name.endsWith("[slow]")'
text
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)
utf-8 · text · Lines: 10

JUnit XML in CI

TypeScriptTestingCi

--reporter junit writes one XML file per test file for CI test-report steps; a failure exits 1.

ts
// 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));
utf-8 · ts · Lines: 8
Terminal
bash
elide test --reporter junit --reporter-outfile reports
text
 1 pass
 0 skip
 1 fail
Ran 2 tests across one file. (0.11s)
utf-8 · text · Lines: 4

reports/js/TEST-price.test.ts.xml:

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:&#10;&#10;0 !== 1&#10;" type="ERR_ASSERTION">…</failure>
  </testcase>
</testsuite>
utf-8 · xml · Lines: 7

Multi-context server

TypeScriptHTTPConcurrency

--contexts=auto runs one copy of the module per core (Linux). Each copy keeps its own globals, so the counter counts per context.

ts
// counter.ts
const id = crypto.randomUUID().slice(0, 8);
let count = 0;

export default {
  fetch(): Response {
    count += 1;
    return Response.json({ context: id, count });
  },
};
utf-8 · ts · Lines: 10
Terminal
bash
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
text
{"context":"60cf3464","count":1}
{"context":"60e90376","count":1}
{"context":"90086101","count":1}
{"context":"4adfe8e1","count":1}
utf-8 · text · Lines: 4

Each curl opens a new connection, which the server may hand to any context.

Custom span in TypeScript

TypeScriptObservability

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.

ts
// 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());
utf-8 · ts · Lines: 20
Terminal
bash
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_SERVICE_NAME=checkout elide run checkout.ts
text
charged order 42, sampled: true
exported: true
utf-8 · text · Lines: 2

The collector receives checkout with its child charge card, and the console.log line as a log record with their trace id.

Metrics instruments

TypeScriptObservability

A counter, a histogram with explicit buckets, and an observable gauge.

ts
// 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());
utf-8 · ts · Lines: 21
Terminal
bash
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_SERVICE_NAME=shop elide run metrics.ts
text
exported: true
utf-8 · text · Lines: 1

The collector’s debug output, trimmed:

text
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.000000
utf-8 · text · Lines: 12

Python span and logging

PythonObservability

Needs the Python component. logging output is exported as log records with the span’s ids; no opentelemetry package is needed.

python
# 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())
utf-8 · python · Lines: 16
Terminal
bash
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_SERVICE_NAME=orders elide python orders.py
text
INFO orders: imported 3 orders
sampled: True
exported: True
utf-8 · text · Lines: 3

OTLP to a local collector

TypeScriptHTTPObservability

A served handler continues the caller’s trace: the server span takes the traceparent parent, and the handler’s own span is its child.

ts
// 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 });
    });
  },
};
utf-8 · ts · Lines: 15
Terminal
bash
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
text
{"id":"17","stock":7}
utf-8 · text · Lines: 1

The collector’s debug output, trimmed:

text
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: 6a28bf795e58acfb
utf-8 · text · Lines: 17

Baggage across services

TypeScriptHTTPObservability

Elide’s server ignores an incoming baggage header until inbound baggage is on, and then admits only allowed keys.

ts
// 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`);
  },
};
utf-8 · ts · Lines: 11
Terminal
bash
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/
text
tenant=acme debug=none
utf-8 · text · Lines: 1

Without ELIDE_OTEL_BAGGAGE_INBOUND=true, the response is tenant=none debug=none.

Was this page helpful?