A vulture in sunglasses is never in a hurry β it has already arrived.
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 |
| 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))
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
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.
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 horizontalclaude-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 horizontalclaude-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 horizontalclaude-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β lotelargura-intrinseca-de-controlos: a LARGURA natural de todos os controlos bate (Γ© o que o lote resolve); a ALTURA desel/fsel(<select>sem opΓ§Γ΅es) nΓ£o β<select>nΓ£o passa porlayout_input(is_text_input_tag,layout/pintura.rs:255, sΓ³ cobreinput/textarea), por isso a sua altura de CONTEΓDO real fica em 0 em vez do natural (19) e nΓ£o recebeforced_outer_hno stretch cruzado do flex (36 esperado emfsel). Routearselectporlayout_inputΓ© uma mudanΓ§a embloco.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).
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.
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). |
Context. The engine was rewritten from scratch on a sound value model (
PolyValueNaN-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.
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_levelis left at Cranelift's default, which isnoneβ no GVN, no LICM, no redundant-load elimination.target/mod.rsdocuments this and env-gates the setting behindRTS_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 ironbench/monte_carlo_pi.tsemits 19Widenand 25Guardfor 12 real float operations β every intermediate re-boxed toTaggedand proven back for the next operation. - And it compounds. A value that arrives
Taggedfails the precondition ofemit/call.rs'smachine_operation, the one place this engine turns a library call into an instruction.Math.floor(x)is one instruction whenxis a loop local and a full JavaScript call whenxreached 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.
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
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,­hyphenation), flexbox and grid (areas,repeat(),minmax(), intrinsic tracks), tables,position: relative/absolute/fixed(stickyparses 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
transformwithtransform-origin(the matrix travels in the display list),overflowclipping,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+transitionwith 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 siteState 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.
β
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.iteratorprotocol)
β
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)
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, onceir,evalandemit-typeshad 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 ownREADME.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
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.
# 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
./hellorts 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 directoriesWant to see exactly what the engine emitted?
rts ir file.ts 2>&1 | head -50Prints 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.
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 aconst), 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.
- π οΈ
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
- β No
xtaskβ the build is purecargo - β 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.