Skip to content

Latest commit

Β 

History

6,065 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

RTS β€” TypeScript that flies

rts_

TypeScript compiled to a native binary. No runtime. No heavy GC. No excuses.

A vulture in sunglasses is never in a hurry β€” it has already arrived.

Cranelift Rust License Single Binary

[Bun/Node parity](the spec removed 2026-08-03 (see git history))

Node test suite

CSS vs Chrome

🌐 Cross-runtime parity

JS spec compatibility validated against Bun and Node over 1516 standalone TS fixtures.

[β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±] 80.4%   1218/1515 fixtures passing
Metric Value
Parity 80.4% (1218/1515)
βœ… RTS = Bun = Node 1218
❌ RTS diverges 216
πŸ’₯ RTS runtime error 81
πŸ› οΈ Left to fix 297
⚠️ Bun β‰  Node (skip) 0
🚫 Rejected (RTS-only) 0
πŸ“¦ Total fixtures 1516

Updated: 2026-09-07 β€” [how to add a fixture](the spec removed 2026-08-03 (see git history))

🟒 Node test suite

As bibliotecas node: medidas contra a suΓ­te de testes do prΓ³prio Node (test/parallel), um processo por ficheiro, sem traduΓ§Γ£o nenhuma.

[β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±] 52.4%   1659/3168 ficheiros passando
Metric Value
Compatibilidade 52.4% (1659/3168)
βœ… Sai com 0 1659
❌ Asserção falhou 668
πŸ’₯ ExceΓ§Γ£o nΓ£o apanhada 829
⏱️ Não terminou 12
βž– Fora da conta 374

Os 374 de fora leem os mΓ³dulos internos do Node (internal/…, _http_common) ou pedem --expose-gc: o prΓ³prio Node sΓ³ os corre com uma flag, e nenhum runtime de terceiros os pode passar. Contados Γ  parte porque as duas alternativas mentem β€” somΓ‘-los Γ s falhas afirma que hΓ‘ centenas de bibliotecas por fazer, apagΓ‘-los esconde que 10% do corpus nunca foi uma pergunta sobre isto.

Por grupo (os dez maiores). O grupo Γ© o prefixo do ficheiro na suΓ­te β€” a forma como o Node os agrupa, nΓ£o uma classificaΓ§Γ£o nossa: test-worker-* Γ© worker_threads e test-child-* Γ© child_process.

Grupo % ok/total
test-http-* 74.6% 261/350
test-http2-* 100.0% 222/222
test-fs-* 41.0% 89/217
test-tls-* 91.0% 162/178
test-stream-* 37.8% 62/164
test-net-* 64.7% 88/136
test-worker-* 14.6% 18/123
test-child-* 21.4% 21/98
test-crypto-* 98.9% 93/94
test-process-* 33.3% 28/84

As causas mais frequentes β€” uma mensagem repetida Γ© um nome em falta, nΓ£o N problemas

Ficheiros Mensagem
224 AssertionError [strictEqual] #N: Expected values to be strictly equal:
139 TypeError: Cannot read properties of undefined (reading '…')
121 AssertionError [throws] #N: Missing expected exception.
59 rts: uncaught '…' event: an object
48 AssertionError [ok] #N: The expression evaluated to a falsy value: fal

Updated: 2026-08-24 β€” como isto Γ© medido

🎨 CSS and DOM parity

Layout and computed style measured against Chrome/Blink (Edge headless, 1280Γ—800, 1 px tolerance) over the fixtures in tests/css/. Two numbers, on purpose: a fixture passes only when every measurement in it matches; measurements count each x/y/w/h and each computed property one by one. Read it as "what we implemented is right", not as a share of CSS: the corpus measures what has a fixture, and each new fixture is written to fail first (tests/css/README.md).

[β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°] 98.6%   3292/3340 measurements matching Blink
[β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±] 95.2%   139/146 fixtures passing
[β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±] 69.1%   601/870 WPT reftests (css-flexbox) rendering test == reference

The WPT line is self-consistency, the way browsers run reftests: test and reference are both rendered by this engine and compared pixel by pixel, no browser involved (scripts/wpt_reftests.md). It measures coherence, not Blink parity.

Every checkpoint, with its own percentage

The total alone says nothing about where the work is β€” it does not tell a branch this engine does well from one it does not attempt.

subfolders of css-flexbox
  [β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±]  12.8%   5/39      balance
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±]  45.8%   11/24     intrinsic-size
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±β–±]  52.6%   10/19     abspos
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°] 100.0%   4/4       flex-lines
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±]  75.0%   3/4       order
  [β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±]  33.3%   1/3       alignment

the 777 tests at the root, by subject
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±]  87.5%   140/160   outros
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±]  59.7%   46/77     item
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±]  91.4%   53/58     align
  [β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±]  37.7%   20/53     aspect-ratio
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±]  82.6%   38/46     gap
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±]  48.7%   19/39     percentage
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±]  59.5%   22/37     baseline
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±]  94.6%   35/37     shrink
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±]  61.8%   21/34     min-
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±]  84.4%   27/32     overflow
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±]  80.0%   24/30     basis
  [β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±]  41.4%   12/29     table
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±]  78.6%   22/28     wrap
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±]  58.3%   14/24     writing-mode
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±]  95.2%   20/21     justify
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±]  80.0%   12/15     order
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±]  81.8%   9/11      column
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±]  88.9%   8/9       anonymous
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±]  66.7%   6/9       grow
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±]  87.5%   7/8       max-
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±]  66.7%   4/6       row
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±]  60.0%   3/5       scrollbar
  [β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°β–°] 100.0%   4/4       abspos
  [β–°β–°β–°β–°β–°β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±]  25.0%   1/4       position
  [β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±β–±]   0.0%   0/1       visibility

Subfolders are the WPT's own hierarchy. The subject grouping is ours, read off the test names β€” it is a way to find the work, not a structure the WPT declares.

Fixtures that fail on purpose (each names a measured gap; tests/css/esperado-a-falhar.txt):

  • claude-ua-form-disabled.html β€” folha de UA (lote I): largura de texto a negrito e de controlos no medidor aproximado, fonte dos controlos, sem border-spacing horizontal
  • claude-ua-headings.html β€” folha de UA (lote I): largura de texto a negrito e de controlos no medidor aproximado, fonte dos controlos, sem border-spacing horizontal
  • claude-ua-th.html β€” folha de UA (lote I): largura de texto a negrito e de controlos no medidor aproximado, fonte dos controlos, sem border-spacing horizontal
  • claude-cursor-pointer-events.html β€” cursor: url(x.png) β€” o Blink resolve a URL contra a base do documento; nenhuma propriedade deste motor resolve URLs (lote S-decor, dito no teste)
  • claude-controlos-tamanho-natural.html β€” lote largura-intrinseca-de-controlos: a LARGURA natural de todos os controlos bate (Γ© o que o lote resolve); a ALTURA de sel/fsel (<select> sem opΓ§Γ΅es) nΓ£o β€” <select> nΓ£o passa por layout_input (is_text_input_tag, layout/pintura.rs:255, sΓ³ cobre input/textarea), por isso a sua altura de CONTEÚDO real fica em 0 em vez do natural (19) e nΓ£o recebe forced_outer_h no stretch cruzado do flex (36 esperado em fsel). Routear select por layout_input Γ© uma mudanΓ§a em bloco.rs, que este lote nΓ£o toca (tecto de linhas). Fica para o lote que der ao <select> a sua prΓ³pria caixa.

DOM engine state (crates/rts-dom/PLAN.md Β§0): 68/77 lots done, 5 partial, pending: Q, U, V–Y, borda-conflito-hidden. The paint ruler (pixels against Blink, scripts/css_pintura.md) needs a browser and runs locally; its last number is recorded there.

Updated 2026-09-07 by CI (dom-rulers).

πŸ“¦ RTS vs Electron: packaging the same app

Three builds of the same app (scripts/rts_vs_electron/app/index.html, ~145 KB, no network calls) β€” an Electron 44.2.0 bundle, a native RTS .exe (AOT, rts compile), and the RTS rts.exe engine running the .ts source (JIT, rts run) β€” measured by scripts/rts_vs_electron/medir.mjs: 3 Electron runs, 3 AOT runs, 3 JIT runs, startup timed to a visible window, memory and CPU sampled over the whole process tree, median reported. Measured 2026-09-05 on win32 10.0.26100, AMD EPYC 9V74 80-Core Processor (4 logical cores), 16 GB RAM.

Electron RTS .exe AOT RTS rts.exe + app
Exe size 234.8 MB 33.2 MB 110.1 MB
Folder size 367.6 MB 34.3 MB 110.2 MB
Files in folder 73 5 3
Page JavaScript runs yes ? yes
Processes 4 2 2
Startup (median, min–max) 540 ms (228–1422) 2370 ms (2357–2793) 2302 ms (2025–2633)
RSS (median, min–max) 261.3 MB (260.8–263.2) 105.5 MB (104.7–107.0) 109.8 MB (106.9–111.5)
Private bytes (median) 90.4 MB 203.5 MB 209.7 MB
CPU at rest (median) 0% 264.82% 318.82%

Honesty: the side comparable to Electron today is the last column, not the middle one. Electron ships a JS engine (V8) inside its runtime, so any page script runs regardless of how the app was built. RTS's AOT .exe does not β€” it only runs the JavaScript that was compiled INTO it ahead of time, so it fits an app with no page <script> (or one whose HTML is generated from already-compiled TS, not read as text). rts.exe + the app is the actual Electron-equivalent pair (engine binary with a compiler + the page it opens, same relationship as Chromium + app.asar), and it is the only RTS column above where the same React page the Electron column renders also renders here.

What this does NOT measure: no GPU workload, no network I/O, one tiny static-plus-React page β€” the size and idle-memory difference between the three is the whole comparison, not a claim about any of them under load.

Updated 2026-09-05 by scripts/rts_vs_electron/medir.mjs.


πŸ¦… What it is

RTS is a compiler + runtime that takes your .ts and spits out a native .exe. It is not a transpiler, not a bundler, not a wrapper around V8 β€” it is Cranelift generating machine code directly from the SWC AST, with a minimal Rust runtime and a typed, boxing-free ABI.

Two paths, same codegen:

Mode Command What it does
πŸš€ JIT rts run app.ts Compiles to executable memory and runs. Zero disk.
πŸ“¦ AOT rts compile -p app.ts out Object file β†’ linker β†’ standalone binary (~3 KB).

⚑ Performance β€” honest numbers, new engine

Context. The engine was rewritten from scratch on a sound value model (PolyValue NaN-box + shapes + inline caches) and the campaign so far has been correctness-first (parity badge above). The deleted old engine's peak (Monte Carlo AOT 16.9 ms, 5.4Γ— faster than Bun; HTTP 29k req/s) is the documented performance target to re-clear, not the current state. Numbers below are end-to-end process time (startup included β€” AOT runtime init is ~70 ms of every figure) measured now on the new engine.

πŸ“Š Measured benchmarks (auto-updated by CI)

End-to-end process time (includes startup/JIT compile), median of 20 runs after 3 warmups, GitHub Actions windows-latest β€” commit c9cc8a9.

Bench Bun Node Deno RTS JIT RTS AOT AOT vs Bun AOT vs Node

Updated: 2026-09-07 β€” run locally with powershell -File bench/benchmark.ps1

Why native wins (and where the work is). RTS compiles TS to machine code via Cranelift β€” no JIT warmup, no interpreter tier, native 64-bit integer arithmetic JS engines can't touch without BigInt.

Read the Monte Carlo row as the honest statement of where the work is. RTS AOT (1.08 s) is barely ahead of RTS JIT (1.12 s), and that gap is the whole finding: AOT removes the compile step, and the compile step was never the cost. Both paths emit the same IR, so both pay the same thing.

This paragraph used to say the PolyValue NaN-box "only pays where code is actually polymorphic and the Cranelift egraph folds redundant box/unbox away". Measured 2026-08-28, that is not what happens:

  • The egraph is off. opt_level is left at Cranelift's default, which is none β€” no GVN, no LICM, no redundant-load elimination. target/mod.rs documents this and env-gates the setting behind RTS_CL_OPT; turning it on moves Monte Carlo by less than the run-to-run noise, because the mid-end cannot see across an opaque call and this engine's IR is mostly opaque calls.
  • So the box/unbox is not folded. rts ir on bench/monte_carlo_pi.ts emits 19 Widen and 25 Guard for 12 real float operations β€” every intermediate re-boxed to Tagged and proven back for the next operation.
  • And it compounds. A value that arrives Tagged fails the precondition of emit/call.rs's machine_operation, the one place this engine turns a library call into an instruction. Math.floor(x) is one instruction when x is a loop local and a full JavaScript call when x reached it through a guard β€” which is most real code, and it fails silently because falling back is a correct answer.

The pass that folds a redundant widen/guard pair across a block boundary is the single missing piece, and it is missing rather than disabled: docs/codegen/the-missing-pass.md prices it and ir/fold.rs declines it by name. That, not startup and not the value model, is the tuning phase.


🧰 The runtime stack β€” the whole std::*, in pure Rust

40+ namespaces today β€” being reshaped into per-module rts:* imports (camelCase, JS globals for everything the language already covers): see docs/engine/architecture.md. No dependency on OpenSSL, schannel, libuv, or any external runtime.

Family Namespaces
I/O & FS io fs path process env os
Compute math num bigfloat fmt hash crypto
Memory gc buffer mem alloc ptr ffi
Concurrency thread atomic sync parallel
Network net tls http_server (embedded actix-web)
Data collections string regex json date
Async events (EventEmitter), native Promise + Function
Meta runtime test trace hint

🌐 Painless HTTPS: rustls + webpki-roots (Mozilla CAs embedded in the binary) 🧡 Threading: 4 coexisting mechanisms β€” spawn/join, spawn_async, spawn_detached (8-worker pool, 5M spawn/s), scope auto-join πŸ”’ Shard-aware HandleTable: 32 mutually lock-free shards


πŸ•ΈοΈ Native HTML/CSS render engine

The UrubuCode β€” the mascot, drawn with nothing but CSS boxes and animated by the RTS render engine

The mascot above is an HTML page β€” no images: just CSS boxes + @keyframes, rendered and animated by the engine.

RTS has its own HTML+CSS render engine, in pure Rust (crates/rts-dom), following the canonical browser pipeline: DOM β†’ CSS cascade β†’ layout (x,y,w,h) β†’ display list β†’ paint. The backend (egui/wgpu) only paints β€” the DOM owns everything, headless and testable without a window.

What already renders faithfully β€” measured, not claimed: every fixture in tests/css/ is written to fail first, then measured in Chrome/Blink (getBoundingClientRect and getComputedStyle, element by element, 1 px), and the engine is held to that number by CI. The share is in the CSS and DOM parity block above, written by the dom-rulers job on every push:

  • Cascade: specificity, !important, inheritance, @layer, @media (ranges, prefers-*), @supports, @property, var() with fallback, revert, the user-agent sheet as real CSS (style/ua.css)
  • Selectors: the full combinator set, :nth-*, :not/:is/:where, :has(), :target, :placeholder-shown, ::before/::after/::marker, with scoped invalidation on mutation
  • Layout: block flow with margin collapsing, floats and clear (real BFCs), rich inline (baseline, vertical-align, white-space, inline boxes painted per line fragment, &shy; hyphenation), flexbox and grid (areas, repeat(), minmax(), intrinsic tracks), tables, position: relative/absolute/fixed (sticky parses but still flows), scroll containers with their own scrollbars
  • Units and values: rem/em/ex/ch/%/vw/vh, calc(), max-content, negative margins, box-sizing
  • Paint: 2D transform with transform-origin (the matrix travels in the display list), overflow clipping, border-radius, box-shadow, gradients, opacity, filter, clip-path, borders joined on the diagonal (the CSS triangle) β€” checked pixel by pixel against Blink by a second ruler (scripts/css_pintura.md)
  • Motion: @keyframes + transition with full easing β€” colors, sizes and margins interpolating (the vulture above flaps its wings with this)
  • External resources: local <link rel="stylesheet"> + @import

Known gaps, each one a fixture that fails on purpose or a lot in the plan: real font metrics and @font-face (text width is a calibrated approximation), images in the new engine (the loader is designed, not built), hyphens: auto, 3D transforms, @container/@scope.

Test any local page with one line:

rts run examples/view.ts examples/urubu.html                            # the mascot
rts run examples/view.ts examples/bootstrap-5.3.8-examples/cover/index.html  # real Bootstrap
rts run examples/view.ts path/to/your/index.html                    # your site

State and plan: crates/rts-dom/PLAN.md (Β§0 is the lot-by-lot state with the measured numbers) and the structural audit of 2026-09-04 in docs/ui/html-engine/analises/2026-09-04-auditoria-estrutural/. Issue #1793 is the June backlog that the plan replaced.


🎯 What the language understands today

βœ… Control flow β€” if/else, while, do-while, for, for-of/for-in (real iterator protocol with IteratorClose on break), switch (native jump table via br_table when all cases are integer literals), labeled break/continue

βœ… Functions β€” declaration, expression, arrow, closures with mutable capture (cells), tail call optimization (return f(x) becomes return_call), first-class function pointers, call/apply/bind/toString, new Function (runtime compile), spread call f(...args)

βœ… Classes β€” constructor, methods, this, extends, super(...), super.method(...), static methods/fields + static {}, getters/setters, real C.prototype + constructor.name, shape-keyed virtual dispatch, private fields #x, Rust-style operator overload (a + b becomes a.add(b) at compile time)

βœ… Generators & async β€” function*/yield/yield* (lazy state machine), async generators + for await, async/await with a real microtask queue (.then chains in spec order), Promise combinators (all/allSettled/race/any), thenable adoption, withResolvers

βœ… Objects β€” hidden-class shapes + inline caches, getters/setters in literals, computed keys, spread, Object.* statics, property descriptors, freeze/seal, prototype chain, Proxy (get/set/delete/apply/ownKeys traps)

  • Reflect, Symbol (+ well-known, Symbol.iterator protocol)

βœ… Data β€” TypedArrays/ArrayBuffer/DataView/SharedArrayBuffer + Atomics, Map/Set/WeakMap/WeakSet (TS stdlib), WeakRef/ FinalizationRegistry (strong interim, #217), JSON (+JSON5), Date, RegExp (exec/matchAll/named groups/d flag), big decimal (bigfloat, i128 fixed-point ~30 digits), full destructuring (incl. assignment targets), template literals + tagged templates + String.raw

βœ… Errors β€” try/catch/finally, real throw/instanceof over the Error family, error slot + pending-error unwind at call edges

βœ… Web/Node surface β€” fetch/Headers/FormData/Blob/streams, URL, TextEncoder/Decoder, timers + microtasks, EventTarget/AbortController, console, node:fs/os/path/process/crypto/util shims, N-API addons (.node), import.meta

❌ Not there yet β€” decorators, full generics/type-checker, real weak semantics (#217), Intl (partial), full BigInt semantics, real async event loop for every path (#207), var hoisting edge cases (#301)


πŸ—οΈ Architecture

The cutover happened, twice. The old engine went first; then rts-codegen-new β€” the crate this section used to describe β€” was itself deleted on 2026-08-10, once ir, eval and emit-types had been rebuilt on the engine below. Its doctrine went with it and is the model for nothing. Canonical design: docs/engine/architecture.md; binding rules live in each crate's own README.md.

Cargo workspace in crates/. src/ is the facade of the rts bin (re-exports the crates); real paths live under crates/<crate>/src/.

Two crates and a boundary. rts-codegen is the language and knows no machine; rts-cranelift is the machine and knows no language. Either rule alone is a preference; both at once means a decision has exactly one place it can be made.

crates/
β”œβ”€ rts-cranelift/    the machine β€” IR, representations, GC contract, frames,
β”‚                    calls, unwinding. The ONLY crate that touches Cranelift.
β”œβ”€ rts-codegen/      the language β€” JS/TS tree, SWC bridge, emit, type pass
β”œβ”€ rts-core/         the runtime β€” values, heap, objects, coercion, entry points
β”œβ”€ rts-host/         where the three meet, and where a program runs
β”œβ”€ rts-macro/        #[rtse::entry] / #[rtse::class] β€” declare one, derive four
β”œβ”€ rts-std/          the `rts:` surface, and the globals
β”œβ”€ rts-node/         the `node:` surface (fs, os, path, process, crypto, util…)
β”œβ”€ rts-runtime/      the AOT staticlib a compiled program links against
β”œβ”€ rts-napi/         N-API (.node addons) β€” 146 symbols, a real addon runs
β”œβ”€ rts-dom/          headless HTML+CSS engine (DOM β†’ cascade β†’ layout β†’ paint)
β”œβ”€ rts-egui/         window/paint backend (egui/wgpu)
β”œβ”€ rts-render/       rts-input/  rts-dom-bridge/  rts-physics/  rts-ui/
β”œβ”€ rts-linker/       native link (system linker + object fallback)
└─ rts-cli/          run Β· compile Β· test Β· ir Β· eval Β· emit-types Β· repl

Pipeline

TS β†’ SWC β†’ rts-codegen syntax tree β†’ emit β†’ rts_cranelift::ir β†’ verify
   β†’ lower/ β†’ Cranelift β†’ JIT (executable memory) | AOT (object + link)

One path, no MIR tier. rts ir prints the middle stage β€” this engine's own representation, not Cranelift's .clif.

What optimizes it: almost nothing, and that is the honest statement. This section used to claim the Cranelift egraph (use_egraphs=true) was the optimizer. opt_level is left at Cranelift's default of none, which gates the egraph mid-end out entirely β€” target/mod.rs documents that and puts the knob behind RTS_CL_OPT, where turning it on measures as noise because the mid-end cannot see across an opaque call. So the box/unbox pairs the front end inserts are not folded away by anything; see the performance section above and docs/codegen/the-missing-pass.md.

The front end does what Cranelift cannot β€” JS semantics: ToNumber/ToString/ToBoolean coercions, the polymorphic +, widen/guard insertion, shape and inline-cache site emission, narrow-int wrap, exception edges. AOT and JIT share one lowering and differ only in the destination.

One source, generated views. A runtime symbol is declared by an attribute and never written by hand: #[rtse::class] derives the wrappers, the install lists, the registration and the TypeScript declaration rts emit-types prints β€” four views from one impl block. There is no symbol table to bake, because a native here is a function pointer beside a cell, not a name a linker resolves. See docs/engine/authoring-natives.md.

The one permanent exception is rts-napi's 146 napi_* declarations: a foreign C ABI whose names are the interface, since a compiled .node addon links against those exact strings.

Where the three crates agree, they are made to. An entry point crosses as ABI scalars, and rts-host is the only crate that may name the compiler's statement of the entry-point set and the runtime's derivation of it at once β€” so that is where the two are asserted equal, by name, rather than assumed.


πŸš€ Get started in 30 seconds

# Install
git clone https://github.com/UrubuCode/rts && cd rts
cargo build --release

# Run
./target/release/rts run examples/console.ts

# Compile to a binary (~3 KB, no runtime DLL)
./target/release/rts compile -p examples/console.ts hello
./hello

CLI

rts run file.ts                  # in-memory JIT
rts compile -p file.ts out       # AOT with use-based slicing
rts test [path]                  # the *.test.ts corpus
rts ir file.ts                   # this engine's own IR, no execution
rts emit-types [out.d.ts]        # TypeScript declarations, from #[rtse::class]
rts init my-app                  # project scaffolding
rts i [pkg@version …]            # install from package.json or args
rts clean                        # remove generated object/cache directories

πŸ”¬ Codegen debugging

Want to see exactly what the engine emitted?

rts ir file.ts 2>&1 | head -50

Prints this engine's own IR β€” not Cranelift's .clif, which only exists inside lower/ after every decision has already been taken. A callee legend sits at the top, so a Call { callee: FuncId(7) } can be read back to a name.

It is the tool for the questions that matter here: how many Widen/Guard pairs surround each real operation, which operations became runtime calls instead of instructions, and where a throw check follows something that cannot throw. docs/guides/reading-ir.md is how to read it; docs/codegen/ is what past readings settled.


🎯 JS/TS compatibility

Honest number: the new engine's real cross-runtime parity is the block 🌐 Cross-runtime parity at the top (generated by CI against Bun+Node). The old engine hit 100% (372/372) at tag v0.0-202606072107 β€” a local maximum of a hardcoded approach on an unsound value model; the redesign exists to break through that wall, not to repeat the number. Do NOT quote "1015/1015"/"100%" as current state.

What the new engine already covers (under construction, parity climbing):

  • Core syntax: classes (extends/super/static/getters/setters), destructuring, spread in literals, optional chaining, nullish coalescing, arrow/function expressions, template literals
  • Async: Promise + async/await (synchronous path without await; real event loop still open, #207)
  • JS globals as prelude .ts (data-driven): Object + statics, Boolean/Number/String prototypes, Error family, console.*, Map/Set, JSON, Date β€” none named in the front
  • Operators: JS-spec division (/ ALWAYS f64 β€” 44100/48000 === 0.91875, even when assigned to a const), comparisons, ternary, bitwise, shifts
  • try/catch/finally phase 1 (thread-local error slot; finally runs and re-propagates the error correctly)
  • Diagnostics: an unresolved identifier becomes a compile error, never a segfault β€” and never a wrong value (the redesign's soundness floor)

Heavy items still open (some in redesign phase): real async event loop (#207), closures with mutable capture (#195), TCO, Proxy (#218), typed arrays/DataView/ArrayBuffer, Symbol/Reflect/BigInt (#216/#219). Master JS/TS parity tracker: #226.


πŸ“š Documentation

  • πŸ› οΈ CLAUDE.md β€” internal architecture + codebase rules (includes Β§ anti-hardcode)
  • πŸ“– [the specs removed 2026-08-03 (see git history)](the specs removed 2026-08-03 (see git history)) β€” technical feature specs
  • πŸ—ΊοΈ docs/engine/architecture.md β€” canonical plan of the engine redesign
  • πŸ› Issues: master JS/TS parity tracker at #226

πŸ›‘οΈ Guardrails

  • βœ‹ No xtask β€” the build is pure cargo
  • βœ‹ No runtime-support download at build time
  • βœ‹ No Rust/Cargo dependency in the AOT binary's final environment
  • βœ‹ Single distributed binary, runs on any Windows/Linux/macOS without installing anything

Made with πŸ¦… by UrubuCode

If Bun is a rocket, RTS is a bird of prey.

About

No description, website, or topics provided.

Resources

Stars

27 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages