Skip to content

corporatepiyush/dynascript

 
 

Repository files navigation

DynaJS

A small, fast, batteries-included JavaScript runtime.
A QuickJS fork with a native, SIMD-accelerated standard library — no npm, no node_modules, no supply chain.

language: C11 test262: 58/83744 platforms SIMD license: MIT

Quick start · Standard library · Numbers · Why · The Guide · API Reference


DynaJS starts in microseconds, idles in a couple of megabytes, and ships a curated set of native modules — crypto, HTTP, files, compression, vector math, machine learning, data structures — compiled into the binary and reachable with one import. It began as a fork of Fabrice Bellard's QuickJS (release 2026-06-04) and grew the standard library a real runtime needs, without adopting a package ecosystem.

Important

A deliberate stance. DynaJS does not and will not implement Node.js compatibility — no require, no node: modules, no npm. Instead, the functionality of the most-used packages is brought in as first-party native modules under the dyna: namespace: audited, curated, SIMD-accelerated, dependency-free, and re-arranged for DynaJS (not one-to-one clones).

Quick start

# Clone, build with the full native standard library, and install (macOS / Linux / FreeBSD).
# Re-run any time to upgrade or repair — it overrides the previous install.
./install.sh                          # → /usr/local/bin/dynajs (or ~/.local/bin without sudo)
./install.sh --prefix "$HOME/.local"
./install.sh --uninstall

Requires git, make, and a C compiler (clang preferred). Pass --with-deps to let the installer fetch them via your package manager.

dynajs hello.js          # run a file
dynajs -e 'print(1+1)'   # eval
dynajs -i                # REPL

Hello, DynaJS

Train a model on some data and serve predictions over HTTP — two native modules working together, no dependencies, no build step:

import { LinearRegression } from "dyna:ml";
import { App } from "dyna:http";

// 1. Tabular training data for y = 2x + 1.
const X = [[1], [2], [3], [4]];
const y = [3, 5, 7, 9];

// 2. Fit a model natively (vectorised) — recovers y = 2x + 1.
const model = new LinearRegression();
model.fit(X, y);

// 3. Serve predictions as a strict JSON-RPC 2.0 service. Your handler is plain
//    JavaScript and runs on the single-thread reactor (kqueue / epoll / io_uring).
const app = new App({ port: 8080 });
app.rpc("/predict", {
  predict: ([x]) => Math.round(model.predict([[x]])[0]),
});
app.start();        // folds the reactor into the event loop; serves until killed
curl -sd '{"jsonrpc":"2.0","id":1,"method":"predict","params":[10]}' :8080/predict
# → {"jsonrpc":"2.0","result":21,"id":1}

No package.json, no install step, no build — the capabilities are in the binary.

The standard library (dyna:*)

Build with CONFIG_NATIVE_MODULES=y (the installer does this) to get a curated native standard library — one import each, no dependencies.

Text & bytes
Module What it gives you
dyna:strings Go-style string utilities (split/fields/trim/pad/title/replace/equalFold), SIMD substring search (index/indexOfAll/contains/count) and a compiled Matcher
dyna:bytes Byte-buffer ops (compare/search/copy/fill) + read/write every int & float width in LE and BE + the UTF‑8 boundary
dyna:encoding Every binary-to-text codec: hex, base64 / base64url, base32, Ascii85, LEB128 var-ints
dyna:text SIMD text kernels: UTF‑8 validate/count, Latin‑1↔UTF‑8, UTF‑8↔UTF‑16
dyna:csv File-oriented CSV CRUD (create / read / edit rows & columns), RFC 4180, mmap + atomic writes
Math, crypto & identity
Module What it gives you
dyna:simd Multi-ISA vector math over typed arrays (f32/f64/i32): dot/norm/distance/GEMM/activations/scans
dyna:ml 14 model families: linear/logistic regression, kNN, decision trees, random forests, gradient boosting, kernel SVM, naive Bayes, k-means, DBScan, Gaussian mixtures, PCA, scalers, metrics
dyna:mathx Special functions (gamma/erf/hypot) + exact integer math (gcd/lcm/factorial/isPrime, BigInt)
dyna:bits Go math/bits: leading/trailing zeros, popcount, rotate, 64‑bit carry arithmetic
dyna:crypto SHA‑1/224/256/384/512, MD5, HMAC, CRC‑32/32C, and a streaming Hasher
dyna:random A fast, seedable PRNG (reproducible streams)
dyna:uuid RFC 9562 UUIDs: v4, v7, v3/v5, parse/validate
I/O, system & networking
Module What it gives you
dyna:http An HTTP client and App — typed routes (JSON-RPC, static sendfile, upload, WebSocket) on a single-thread kqueue/epoll/io_uring reactor
dyna:file Filesystem: buffered reader/writer (per-OS fast paths) + metadata, dirs, glob, links, temp
dyna:uring High-queue-depth bulk file reads via Linux io_uring
dyna:path POSIX path manipulation (join/resolve/normalize/dirname/relative)
dyna:sys Process & environment (env, args, cwd, platform, pid, hostName, homeDir)
dyna:netip Typed IPv4/IPv6 addresses, CIDR prefixes and ranges
dyna:time Nanosecond durations, a monotonic clock, RFC 3339 formatting
dyna:semver SemVer 2.0.0 parsing/comparison and the full npm range grammar
dyna:compress gzip / gunzip (a real DEFLATE implementation)
Data structures
Module What it gives you
dyna:structures What JS has no builtin for: Heap, BitSet, UnionFind, Deque, List, Fenwick, SegTree, RingBuffer, BloomFilter, Trie, LRU, SortedSet/SortedMap
dyna:graph A native Graph with BFS/DFS/Dijkstra/Bellman-Ford/Floyd-Warshall/topo-sort/components/MST/A* as methods

Every example in the docs is real and runs. See the API Reference for complete signatures.

Batteries on the prototypes, too

The dyna:* modules are opt-in imports. The other half of the batteries needs no import at all: DynaJS bakes the non-ECMAScript methods of SugarJS 2.0 and RamdaJS 0.32 straight onto the built-in prototypes — native C, non-enumerable, and careful to never shadow a standard method.

[1, 1, 2, 3, 3].dropRepeats();                     // [1, 2, 3]   — only adjacent dupes
[10, 9, 1, 2].sortBy();                            // [1, 2, 9, 10]  — numeric, not lexicographic
[1, 3, 5, 7, 9].sortedIndexOf(7);                  // 3           — O(log n) binary search
["a","b","c","d"].mapFromIndex(2, true, s => s);   // ["c","d","a","b"]  — start at 2, wrap
[[1,2],[3,4]].sequence(Array);                     // [[1,3],[1,4],[2,3],[2,4]]  — cartesian product
Object.mergeDeepRight({a:{x:1}}, {a:{y:2}});       // {a:{x:1, y:2}}
(x => x+1).pipe(x => x*2, x => -x)(3);             // -8          — point-free composition
new Date(2024,1,29).endOfMonth().getDate();        // 29          — Sugar dates, immutable

Hundreds of methods across Array/String/Number/Object/Function/Date, plus a Lens type and Ramda transducers — all in the binary, all documented in the API Reference → Built-in prototype extensions.

Numbers

Measured, not estimated. Method and full tables are in bench/; each figure links to the report that produced it.

HTTP, vs Node 26 — same JS-handler-per-request workload, Apple M1 Pro, macOS (bench/REPORT.md):

Connections req/s p99 peak RSS
1 DynaJS 45,996 85µs 1.8 MB
1 Node 26 29,894 152µs 64.3 MB
256 DynaJS 137,306 3.56ms 2.0 MB
256 Node 26 61,242 93.5ms 73.9 MB
1024 DynaJS 136,787 8.54ms 2.2 MB
1024 Node 26 59,377 767ms 96.7 MB

1.5×–2.3× the throughput at every concurrency, 36×–45× lower RSS, and a tail that stays bounded where Node's explodes (p99 8.5ms vs 767ms at 1024 connections). RSS stays flat because the reactor uses one shared receive buffer rather than a per-connection allocation.

SIMD substring search — the same kernel the HTTP server uses for header scanning (tests/test_strings_simd.js, 200 MiB scan):

throughput
dyna:strings index() 14,642 MiB/s
String.prototype.indexOf 475 MiB/s

Machine learning — vectorising the models, before → after (bench/ML_REPORT.md, bench/VECTORIZATION_AUDIT.md):

operation before after
GaussianNB.predict 20000×128 26.18 ms 2.08 ms 12.6×
GaussianMixture.fit 4000×128 200.8 ms 39.2 ms 5.1×
LinearRegression.fit 4000×128 18.65 ms 5.15 ms 3.6×
KMeans.fit 5000×128 k=8 11.60 ms 4.45 ms 2.6×

Note

Two findings from that work are worth more than the speedups, and both are written up in bench/: the shared SIMD dispatch-table kernels lost to portable multi-accumulator C (an indirect call can't be inlined or fused into its caller), and two hot loops weren't vectorised at all because they called log() inside the loop body — which no amount of source tuning can fix. Verified by reading the generated assembly, not by assuming.

Why DynaJS

  • Instant startup, low memory — an interpreter with no JIT warmup and a tiny baseline. Ideal for CLI tools, edge/serverless cold starts, embedding, and short-lived workers.
  • 📉 Predictable, flat memory — reference-counting GC frees promptly; classes that own a descriptor, socket or large buffer dispose deterministically via close() / [Symbol.dispose](), so RSS stays flat under load.
  • 🧮 Native SIMD from JavaScript — a verified, cross-ISA (NEON/SSE4.2/AVX2/AVX‑512/SVE) kernel set, no native-addon build step. Dual-use: the same kernels accelerate the engine internally.
  • 📦 A dependency-free standard library — what ships in the binary is what runs. No npm audit, no lockfile drift, no transitive dependency trees.
  • 🔬 Evidence over adjectives — every performance claim in this repo has a benchmark behind it, and the reports record the changes that were tried and reverted as well as the ones that landed.
Honest boundaries — where DynaJS is the wrong tool
  • Long numeric hot loops. A tracing JIT (V8/JSC) out-throughputs an interpreter once a loop runs for minutes. DynaJS's answer is native SIMD kernels for the heavy lifting, not out-JIT'ing V8.
  • You need npm. There is no compatibility layer and there will not be one. If your value is in the ecosystem, use Node or Bun.
  • AVX-512. Certain kernels are conservatively gated pending verification on real AVX-512 silicon; the runtime falls back to the verified AVX2 path. DynaJS ships the proven path.
  • io_uring is opt-in (CONFIG_IO_URING) and measured slower than epoll for small JS-handler-bound requests — its win is bulk transfer. See bench/IO_TUNING_REPORT.md.

Documentation

The complete book lives in docs/dynajs-guide/:

Chapter What's in it
1 Introduction & Philosophy What DynaJS is, the QuickJS lineage, positioning against Node/Bun/Go
2 Installation & First Steps Building, the CLI, the REPL, your first programs, the dyna: namespace
3 The Language & the Runtime The ES2023–2026 baseline, resource disposal, std/os, workers, BigInt
4 The Standard Library Every dyna:* module with worked examples
API Reference Every module, every signature, every throwing condition

Build from source

make CONFIG_NATIVE_MODULES=y -j"$(getconf _NPROCESSORS_ONLN)"   # engine + native stdlib
make test                                                       # language test suites
make test-native                                                # the dyna:* module suites
./dynajs -e 'print("ok")'

make alone builds the core engine plus the classic std/os modules. Sanitizer builds: make CONFIG_ASAN=y, make CONFIG_UBSAN=y. ./dev.sh gate runs the full proof — a zero-warning build, ASan, UBSan, the test suites, and the test262 baseline. See Chapter 2.

Status

The QuickJS-lineage engine and the core language are mature — the project holds a fixed ECMAScript test262 conformance baseline (58 failures out of 83,744, and every change re-runs it). The standard library is solid and shipping: each module is verified against standard vectors or a reference implementation, sanitizer-clean, and adversarially tested for reentrancy. It is also growing — the "top npm functionality as first-party modules" plan is early. The docs flag landed vs. planned explicitly, and never claim an unverified platform.

License

MIT. DynaJS is derived from QuickJS, © 2017‑2021 Fabrice Bellard and Charlie Gordon; see the license headers. DynaJS additions are under the same terms.

About

Improved version of QuickJS engine with lots of extensions

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages