JavaScript / TypeScript-syntax AffineScript. Write code that looks like JS or TS, get affine resource guarantees and typed-wasm output.
JaffaScript is
AffineScript with its
jaffa face pre-selected. If you write JavaScript or TypeScript, you
already know most of the syntax. The compiler checks that your resources
(files, sockets, tokens, handles) are used exactly as many times as you
declare — and proves it at compile time. No null pointer exceptions. No
use-after-free. No silent data races. No GC overhead.
This repo is a brand surface only. The compiler, type checker, borrow checker, and codegen all live in affinescript. This repo carries:
-
Examples idiomatic to JS / TS developers
-
Documentation aimed at the web-development community
-
A
jaffashim CLI that aliasesaffinescript--facejaffa -
Tutorial and migration guides for moving TypeScript codebases into a strongly-typed, affine-typed, WASM-targeting world
examples/hello.affine:
// face: jaffascript
effect IO {
fn println(s: String) -> ();
}
function main() -{IO}-> () {
const greeting = "Hello, JaffaScript!";
println(greeting);
}function, const, ===, null/undefined, import
{ x } from "module" — all lower to canonical
AffineScript and produce the same typed-wasm output as every other face.
opam install affinescript
git clone https://github.com/hyperpolymath/jaffascript
cd jaffascriptThe affinescript binary does the work. The bin/jaffa shim in
this repo just defaults the --face flag.
# Direct, via affinescript:
affinescript eval --face jaffa examples/hello.affine
affinescript compile --face jaffa examples/hello.affine -o hello.wasm
# Or via the jaffa shim (same thing):
./bin/jaffa eval examples/hello.affine
./bin/jaffa compile examples/hello.affine -o hello.wasm
# Or via the justfile:
just run examples/hello.affine
just preview examples/hello.affine # show the canonical loweringSource files use the canonical .affine extension. The face is
selected by the pragma on the first comment line, or by the --face
jaffa flag.
JaffaScript is one of six established faces over the AffineScript core:
-
AffineScript — the canonical face
-
RattleScript — Python-style
-
JaffaScript — JavaScript / TypeScript-style (this repo)
-
LucidScript — PureScript / Haskell-style
-
CafeScripto — CoffeeScript-style
-
PseudoScript — pseudocode-style
All six share the canonical .affine extension and lower to the same
AST. Errors are reported in face-appropriate vocabulary.
JavaScript and TypeScript are the dominant languages of the web.
TypeScript adds structural typing on top of dynamic JS, which catches a
lot of bugs but doesn’t help with: ownership, resource leaks, effect
tracking, formal proofs, or portable native compilation. JaffaScript is
a JS-shaped on-ramp to a language that adds all of those — without
forcing you to abandon brace syntax, arrow functions, or import
{ x } from "module".
For a TypeScript developer migrating in, the steps are:
-
Rename
.tsfiles to.affineand add at the top. -
Replace
import{…}from"module"(TS) with the same syntax in canonical AffineScript form. -
Replace
null/undefinedwith()(canonical unit) in semantics-relevant positions. -
Keep writing
===— the face transforms===→==automatically. -
Compile to typed-wasm; the output runs in browsers, Node, Deno, Wasmtime, and any WASI runtime.
Alpha. The face transformer is implemented in
affinescript/lib/js_face.ml. Known limitations are tracked in
the
affinescript faces README under “Known transformer gaps”.