Repository navigation
Releases: vercel-labs/scriptc
Releases · vercel-labs/scriptc
Release list
v0.4.8
0.4.8
Highlights
- Faster programs:
- Counted loops run two iterations at a time where the iterations are independent: a single-file mandelbrot port is 39% faster.
- A class whose instances form a tree, each node owned by one parent, keeps its nodes in the group arena that holds the rest of the tree, at 16 bytes per node instead of 24: a class-based binary-trees program is 20% faster and uses 33% less peak memory.
- A dictionary update fuses its second hash probe into the write that follows (4.5% faster on dictionary walks), and number and identity-word map keys hash with one multiply instead of two (6.9% faster on number-key maps).
- A run of dynamic property reads on one object shares one guard, untyped code calls typed records' dynamic parameters through specialized calls, and dynamic class tables dispatch on an indexed class key.
- Compact class fields store 32-bit references to arrays, maps, sets and instances, so programs that keep many objects use less memory.
- musl executables use an allocator built for the program they link into: across the runtime benchmarks they are 2.9% faster, line-by-line reading is 39% faster, and an HTTP server's peak memory falls from 34.6 MiB to 2.4 MiB. musl executables also use musl's string comparison and a faster
getenv. - x86-64-v3 builds keep baseline copies of the program's functions.
- Faster builds:
- The thin-LTO pre-link runs at the build's optimization level: a cold build of a large program is 2.4% faster, with the LLVM stage 10% faster and the same runtime speed.
- The partition cache also covers builds that name their LLVM helper with
SCRIPTC_LLVM_HELPER: a warm one-function edit of a large program rebuilds 13% faster in that setup. - A build shares one frontend services scope across its loads, memoizes type mappings per generic binding context, shares type-memo entries that read no type-parameter binding, and computes its preflight diagnostics once. Island rounds share their program facts across lowering passes.
- LLVM partitions are sized from the host's threads as well as the module, the emission passes read their node scans from one recorded walk, and IR functions decode without a whole-document number reviver.
- The LLVM helper's identity is checked once per install, the installed compiler package digest is reused, and edit builds keep less per-build bookkeeping. The native compiler's optimization profile is refreshed.
- Node.js parity:
- node:path, node:url and node:os match Node 24. node:buffer matches Node 24's Buffer members, encodings and module surface. node:zlib adds the zstd API.
util.inspectsupportsshowHidden,numericSeparator,maxStringLength,maxArrayLengthand thegettersoption,console.dirsupportsshowHidden, and errors print from their stack inutil.format,util.inspectandconsole.log.- child_process lowers the shell forms, typed
spawnSyncoptions includingcwdandinput, thecwdoption of the exec and execFile callback forms, and child stdio pipes typed as node:stream streams. - node:http applies Node's HTTP/1 parser checks, answers ClientRequest listeners, reports late Transfer-Encoding errors, and lowers the server
'clientError'event and the URL-plus-options request form withprotocoland shorthandheaders. process.stdinhas its readable surface, readline acceptsprocess.stderroutput and runtime question options, andgetEventListenersanswers for EventTarget receivers.- ArrayBuffer transfer, detach and resizable or growable buffers compile. A cgroup CPU quota below one CPU is floored at one.
- Node.js is pinned to 24.22.1.
- JavaScript semantics (Test262):
- More early errors are reported,
RegExpcoerces its constructor arguments, and the async-generatoryield*delegation protocol is implemented. - Class values honor
Symbol.hasInstance. Promise statics accept a foreign constructor. Iterator helpers run on a liveIterator.prototype. - BigInt typed arrays work in the checked-dynamic tier, and builtin global functions and classes have values in JavaScript sources.
- Generator parameters bind at the call, higher-order functions honor
thisArg, pattern defaults take their binding's name, and object literals accept__proto__with any prototype value.
- More early errors are reported,
- Node islands: coverage lowers once with the islands the build proves, packages that are not opted in keep their JavaScript out of the inferred surface, and npm-static packages are classified only from real loads.
- Correctness: a record reached through an alias keeps writes made through either name when its shape widens under the shape overlay.
- TypeScript: more record, union and generic patterns compile, among them overload sets held as values, class overload declarations, unions of a class and its subclasses,
Object.assignover a narrowed record arm, and partial record views that gain keys through dynamic writes.
v0.4.7
0.4.7
Highlights
- Node.js parity:
Intl.Segmentersplits text into graphemes and words, andtoLocaleStringuses the default locale, on Linux x64 (ICU 78.3). Malformed UTF-8 in directory entries and command-line arguments decodes to U+FFFD, as in Node.- node:http emits Node's abort events, matches Node's outgoing header behavior, and checks
http.requestoptions before a socket opens. Sockets aftersetEncodingpipe decoded strings, untyped listen ports are validated, and net errors carry Node's system properties. - node:querystring and node:string_decoder match Node 24 for values known only at run time, and node:path matches Node 24's path tests.
TextDecodermatches Node's errors, options and printing. structuredClonecopies Maps and Sets.SharedArrayBuffer,DataView,BigInt64Array,BigUint64Array,ProxyandIntlcan be read as values.process.memoryUsage()counts ArrayBuffer storage.import()of a specifier that cannot be resolved rejects withERR_MODULE_NOT_FOUND. fs reads return Buffers in programs that can observe the difference. Values held fromprocess.envread the live environment.- EventEmitter and stream listeners are called with the emitter as
this, listeners that readargumentsreceive every argument, and the leak warning is delivered at exit.'error'events accept non-Error payloads. - readline accepts listener values and ends lines at U+2028 and U+2029.
fs.watchnames the changed entry.assert.throwsreports validation-object failures as Node does. - Executables ignore SIGPIPE and SIGXFSZ, as Node does. Timer deadlines hold on macOS, and intervals re-arm as in Node. Timeout delay warnings are emitted, and untyped timer delays convert with ToNumber.
process.emitWarning,process.kill,process.chdirandos.userInfobehave as in Node. Replacingprocess.stdout.writeorprocess.stderr.writereceives the arguments, Buffers and whole writes that Node passes.- JavaScript semantics from Test262: each evaluation of a class expression creates its own class, private static members check their brand, wrapper objects for strings, numbers and booleans work in JavaScript sources, and Set, Map and weak-collection methods can be read as values.
for...inruns over a value that may be null or undefined. A null or undefined destructuring source throws Node's TypeError. Functions held in records and arrays keep their names.
- Node islands:
- Sloppy-mode modules and modules that patch builtins run in Node islands automatically.
- Islands can
requireCommonJS modules and hold builtin classes, and garbage cycles are collected across the program's heap and Node's. - Island modules whose load is deterministic are deferred until first use, and shared modules resolve by real path, so caches reached through symbolic links work.
- Faster programs:
- Untyped code calls methods of compiled class instances inline.
- Integer arithmetic: sums built on unsigned shifts stay in 32 bits, and loops are versioned on the range of locals they write as remainders. Saturating loops that run together test their exit every fourth iteration.
- Number-key map lookups test the direct index once and inline the probe in loops.
- Streams: taking from a Readable's buffered queue is constant time, object-mode stream callbacks do less work per chunk and keep typed records unboxed, and a line goes to a single line listener directly.
- fs streams read and write cached regular files on the loop thread, worker threads that allocate large blocks no longer reread the process's memory map, and constructors store their own fields without releasing a previous value.
--cpu-dispatchkeeps x86-64-v3 code from running slower than the baseline.- Profile-guided builds compile the functions a profile never ran as cold code, and weigh profile matches by the profile's counts. The runtime profile is refreshed.
- x86_64 musl executables use SSE2 memory routines and the runtime's rounding and libm definitions.
- Faster builds:
- The native compiler's optimization profile is refreshed: its code is 63.0 MB instead of 73.9 MB, so builds use less memory and run faster.
- Optimized partitions of builds without a profile run a lighter simplification before they are linked, and builds that use a profile keep the full one. Partition placement stays stable when an edit adds a small helper, so more partitions stay cached.
- A compile reads and stats each file once and reuses its type checker across module loads, and finished checker processes no longer stay as zombies.
- The type checker executable is written once, and builds with an empty cache no longer hash the runtime pack's artifacts again.
- Large modules decide in one pass whether fs reads must return Buffers.
- Memory and size:
- Programs return freed memory sooner: large strings are freed when they are released, writes of large shared views go out in chunks, and the main thread returns free pages when its event loop waits.
- An HTTP response whose socket closes releases its completion callbacks, and the web view of an abandoned fetch body is released while its transfer runs.
- Intl executables ship ICU data without the categories no Intl form reads (21.5 MB smaller), and the runtime pack is 24 MB smaller on Linux x64.
- TypeScript: an
import sourceclause from TypeScript 7.1 is reported as unsupported instead of failing the build, and more record, union and generic patterns compile: spreads of unions with an absent arm, literal-union keys of one field type, partial record assertions, records cast fromunknown[]elements, andentries().filterin generic bodies.
v0.4.6
0.4.6
Highlights
- Node islands: programs can load Node.js in-process on first use and run npm packages that scriptc doesn't compile in Node islands.
- Native values, closures and promises pass into islands by reference, and island handles, classes and collections can be used from compiled code.
- Modules are shared between a program and its islands, and island modules are evaluated only when the program uses them.
- Once Node loads, the runtime's event loop runs on Node's libuv, and programs exit cleanly with Node loaded.
- New options:
--exceptions=rareunwinds through recursive functions instead of checking a status after each call.--staticbuilds static Linux executables (opt-in).--large-pagesmoves executable code onto huge pages;--cpu-dispatchadds x86-64-v3 copies of hot functions.- Type patches replace a package's declaration files or supply missing declarations.
- The shape overlay types JavaScript from facts the compiler proves (opt-in).
- Faster programs:
- Exceptions raise through cached unwind plans on x86-64 Linux, and every native function has an unwind table entry.
- Ordinary objects are stored as a layout plus value slots; unions of classes are the instance pointer itself; class instances that stay in their frame use frame storage.
- Object groups that die together are allocated in arenas, and the cycle collector does cheaper trial deletion and adapts its nursery trigger.
- The speed runtime is optimized with a committed runtime profile, and Math functions are computed as V8 does, with libm linked only when needed.
- Loops: short constant-count loops run together, for and while loops are versioned on the ranges their locals enter with, counted loops unroll by the period of a guard, and small leaf functions called from loops are inlined.
- Typed arrays: header fields are read once at a speculative loop's entry, products under bitwise operators stay in 32 bits, and shared typed-array elements are read and written with plain accesses at a counted loop's counter.
- Strings: UTF-16 indexes of long non-ASCII strings map in constant time, ASCII case converts eight bytes at a time, template literals allocate at their exact length, and strings iterate over their bytes.
- Untyped code: class instance fields are read inline at dynamic property sites with two inline-cache entries, native arrays iterate without type-name scans, and numbers pass to directly called untyped functions as JavaScript values.
- I/O: fs thread-pool completions and pool-thread writes make fewer syscalls; sockets, clients and agent entries register in constant time; fetch() reuses keep-alive connections; web streams, WritableStream and TransformStream run natively.
- Large executables map their text with 2 MiB pages, and freed heap pages are returned to the system after the frontend is disposed.
- Faster builds:
- Small release and speed programs compile in balanced partitions, the entry module's top-level code gets its own partition, and unchanged partitions stay cached across edits.
- Optimized executables that stay one partition generate code concurrently, and emission's workers stay busy.
- The native compiler is laid out by context-sensitive profiles collected in two phases.
- Type checking uses a shared tsc-ts server by default on Linux and macOS, and lib diagnostics are reused across options and module sets that cannot change them.
- Rebuilds skip the helper version probe and the digests of unchanged files, and the CLI links without huge text pages, so cold builds use less memory.
- Node.js parity:
Error.captureStackTrace, Node's navigator, builtin globals read through the global object, and unhandled rejections reported in programs without async work.- node:zlib is built on the zlib Node bundles;
Response.cloneandRequest.clonework on typed values; fetch bodies have the members of a web ReadableStream; redirects whose chunked body has not ended are followed. - Classes that extend Array compile; strings spread into their code points; frozen checked records stay frozen; sloppy-mode
thisis the global object. util.formatruntime format strings,inspect.customand colors, and untypedpromisify.
- TypeScript: scriptc reads TypeScript 7.1's binary AST format and API server beside 7.0's, and tsc-ts is updated to 0.0.10.
v0.4.5
0.4.5
Highlights
- Fixes:
- Release builds read a typed array's length without a header load that could read a freed view.
net.Socketwrite()returns Node's backpressure boolean and fires'drain'.- Super calls in JavaScript method values call the declared base method.
- Calling a non-function through an asserted callee throws Node's TypeError.
- Faster programs:
- Exceptions propagate by unwinding from directly called functions on Linux, instead of status checks after every call.
- Typed-array loops prove their bounds and keep their views disjoint in more cases, including views over shared memory in worker programs; integer loops stay in integers across nested counted loops, pure helper calls, powers of two and byte reads.
- Lone array callbacks run as loops with their bodies inlined, and row sums run two rows per pass.
- Number-key maps look keys up on their integer view and write updates through the entry the read found; small number-key maps use a direct index.
- Strings: base64 decodes eight characters at a time; Buffers fill, swap byte order and decode
utf16lea word at a time; readline splits input in one scan; string scans over non-ASCII strings use their UTF-16 units. - Untyped code: property caches also cache absent properties, object literal members go through the store caches, calls on plain arrays skip the method ladder, and dynamic values dispose without key-cache walks.
- JSON parses into discriminated unions, and dynamic values convert into nested unions, in one pass.
- BigInt division settles quotients from the top limbs, and limb shifts are vectorized.
- Faster builds:
- Function bodies are emitted on up to eight workers, including in dev builds, and repeated work in LLVM module emission is cut.
- Linux executables link with lld when the driver finds it, and unchanged executables are reused without selecting the runtime pack.
- Dev builds reuse their frontend output after edits that keep token positions, and unchanged frontend inputs are checked by file identity before hashing.
- Lowering fetches only the syntax trees it reads, and the large compiler tables are built on first use.
- Release builds of the native compiler use a profile collected on the release commit.
- Type checking can use a tsc-ts server shared by later builds (opt-in).
- Node.js parity:
- Crypto: RSA key generation lengths, RSA-PSS keys, encrypted private keys,
X509Certificatefields and methods, and TLS certificates andtls.Servercontexts. - zlib: Brotli compression and decompression.
- Dates are mutable objects with Node's setters and conversions; URL hosts and paths parse as the URL Standard does, including non-ASCII hosts with UTS #46.
Math,JSONandReflectare real objects in JavaScript; objects can inherit from JavaScript class prototypes; typed arrays held as untyped values support named properties andSymbol.species.- Error messages:
JSON.parsesyntax errors use V8's messages and positions;Object.getPrototypeOfof an Error answersError.prototype. util.inspectof dynamic classes, functions and%oarguments, andSymbol.toStringTagfields and getters, match Node.
- Crypto: RSA key generation lengths, RSA-PSS keys, encrypted private keys,
- npm packages: more npm and CommonJS dependencies compile statically, including namespace-style packages with guarded declarations and
varexports, and untyped Maps, Sets and arrays are typed from their uses.
v0.4.4
0.4.4
Highlights
- Fixes for 0.4.3:
- Unchanged rebuilds hit the frontend cache again. An unchanged rebuild of a 23-module program takes 93 ms instead of 1.26 s, and of tsc-ts (dev) 1.6 s instead of 58.2 s.
- The native compiler builds programs with several typed-array accesses at one masked index in a loop again, including tsc-ts's release build, which failed with SC3004.
x!returned from a function, used as an arrow function's result, or passed as an argument keeps null or undefined when the destination type holds it, as JavaScript does.x!operands of+,+=and other conversions that hold undefined or null convert as Node does.- Effect 4 programs whose generators yield clients built by other generators run again.
- The cache for untyped property access in programs that declare classes is reverted, fixing a crash in Effect 4 observability programs.
- Faster:
- Number compares and typed-array indexes use the exact integer of sums like
i + 1: a heap sift-down over anInt32Arrayruns 17.5% faster. - glibc's per-thread heaps use huge pages: the VS Code type-check runs 3.4% faster.
- Mid-size executables (over 1 MiB of LLVM text) build in at least eight partitions: the LLVM backend phase takes 34 to 74% less time for three.js programs of 1.4 to 7.8 MB.
- JavaScript class instances are boxed at their exact class, and stores into nested string-keyed dictionaries keep class method calls direct.
--target-cpuselects an x86-64 microarchitecture level.
- Number compares and typed-array indexes use the exact integer of sums like
- Node.js parity:
- X25519 keys and
crypto.diffieHellman;existsSyncandfs.promisesacceptfile:URLs;Buffer.fromranges over ArrayBuffers, the deprecatedBuffer()constructor andBufferas a value. - Dates are objects with identity;
Promise.allover different entry types settles on Node's turns; completion callbacks run in their call's AsyncLocalStorage context. - TypedArray methods run on typed arrays held as untyped values, and unions of typed-array classes compile. Borrowed
TypedArray.prototype.setcalls compile and throw Node's TypeError for other targets. - Split CommonJS
requirebindings,__-prefixed exports, string-key destructuring ofanyvalues, and record key order for values JavaScript built.
- X25519 keys and
- npm packages: zod 4 and
@hono/zod-validatorvalues are projected at the declaration boundary.
v0.4.3
0.4.3
Highlights
- Faster checking and smaller builds with tsc-ts 0.0.9: scriptc checks with tsc-ts 0.0.9, and builds reuse the bundled declaration files' diagnostics instead of checking them again. Small IR builds are 10.6 to 16.0% faster and small executable builds 5.5 to 7.6% faster, with identical output. A native build of tsc-ts with this release checks 0.4 to 6.6% faster than with 0.4.2 and peaks 4 to 16% lower.
- Integer arrays and loops:
- Number arrays that only hold int32 values use 4-byte slots in locals, across direct calls and in module globals, and loop-carried locals written from their elements stay int32. A workload that walks arrays of cursors runs 54.5% faster.
- Loops with two counters closing in on each other (
for (let i = lo, j = hi; i < j; i++, j--)) and element copies througha[i]!run without per-element checks, and two-counterwhileloops run on integer counters. - Loops over typed arrays and charAt scans are versioned on their bounds, and ASCII strings are scanned as bytes.
- Less work for untyped and dynamic code:
- String-keyed record reads under
??and!no longer allocate: a dictionary-walking workload makes no union boxes instead of 363,359 and runs 12 to 15% faster. - Checked-dynamic method calls dispatch on method ids, observed class methods call natively while their slot is unchanged, and calls on bag-tracked class instances skip building their views.
- String-keyed record reads under
- Node.js parity:
crypto.getCipherInfo, AES-CCM, AES key wrap, the legacycreateCipherivmodes,checkPrime/generatePrime, and Node's argument checks forgenerateKeyPair.- Streams:
construct,writev,decodeStringsanddefaultEncoding, includingwritevonTransformandPassThrough. FinalizationRegistry,new.target,String.prototype.aton untyped values,Error.stackTraceLimit,spawnSync'senvoption, and class constructors called throughcall/applythrow Node's TypeError.- Records enumerate in the key order their object literal wrote, and objects converting to a union with an empty-record arm keep every field.
- npm packages: zod 3 compiles natively and zod 4's core runs, minified packages such as marked compile, chalk 5 and hot-shots 8 build from built-in shims, and
@hono/node-server2.x is built in. User code types npm package declarations at a checked, identity-preserving boundary. - Builds: the cached build is reused after whitespace and comment edits, function bodies are emitted on forked workers, and the LLVM emitter reads its analyses from one recorded walk of each module.
v0.4.2
0.4.2
Highlights
- Faster builds of large programs:
- scriptc checks with tsc-ts 0.0.7, which answers contextual types and resolved signatures in batches, so lowering asks for them in one request per chunk instead of one per node. A native build of tsc-ts is 7% faster (110.5 → 103.5 s on 8 vCPUs, lowering 12% faster), with an identical executable.
- The native compiler is built with profile-guided optimization, so tsc-ts builds 6.9% faster cold and 10.7% faster after an edit.
- The native compiler's optimization profile is refreshed, so a native build of tsc-ts is a further 10% faster (124.7 → 111.7 s on 16 vCPUs), with lowering 17% faster.
- The class dispatch passes index their receivers by name instead of testing each one, and they reuse their per-function scans while a function's body is unchanged.
- Checker replies resolve objects the compiler already holds from their ids without decoding them.
- Leaner builds:
- The frontend of an optimized executable's first build runs in a child process, so the native code generator starts after its memory is released. The cold build of tsc-ts peaks at 2.21 GB instead of 3.30 GB.
- The compiler collects its heap as lowering and LLVM emission grow it.
- WebCrypto:
- The
cryptoglobal,node:crypto'swebcryptoandcrypto.subtleare one object. generateKey,importKey,exportKey,encrypt,decrypt,deriveBits,deriveKey,wrapKeyandunwrapKeycover HMAC, AES-GCM, AES-CBC, AES-CTR, AES-KW, PBKDF2, HKDF and JSON Web Keys.crypto.subtlesigns and verifies with ECDSA, RSASSA-PKCS1-v1_5, RSA-PSS and Ed25519.- All of it follows Node 24's argument conversion, algorithm normalization and
DOMExceptions. jose's symmetric JWE paths run as in Node.
- The
- JSX:
.tsx,.jsxand JavaScript files with JSX compile under the project'sjsxoptions.react,react-jsxandreact-jsxdevlower to the factory calls TypeScript emits, with the same keys, fallbacks and source positions.preserveandreact-nativeare refused with a named diagnostic.
- Faster worker messages:
- A message is encoded into one block sized to its contents.
- A typed record is posted from its fields.
- A
'message'listener with a typed parameter builds it from the message, without a checked-dynamic copy. - Messages between
MessageChannelports take 90 ms where they took 250 ms in 0.4.1 (Node: 337 ms), with a peak RSS of 45 MB instead of 115 MB.
- Loops over typed arrays vectorize: a loop to a typed array's length keeps one bounds test for every typed array it indexes.
- Index temporaries such as
const j = i * 4are computed as integers. - Uint8ClampedArray stores convert inline.
- Row-major index terms such as
r * cols + jare tested once before the loop. - Typed-array callback methods call a literal callback directly on an integer counter.
- An 896 × 896 matrix-vector product with four accumulators takes 55 ms instead of 396 ms (Node: 275 ms).
Uint8Array.mapis 4.5 times as fast andInt32Array.some7.8 times as fast as in 0.4.1.
- Index temporaries such as
Features
- Asymmetric keys and signatures in
node:crypto:generateKeyPair,generateKeyPairSync,createPrivateKeyandcreatePublicKeyhandle RSA, EC and Ed25519 keys in PEM, DER and JWK.sign,verify,createSignandcreateVerifysign and verify with those keys, with Node's output and errors.- Ed25519 verification matches Node on 2,083 test vectors, including small-order and mixed-order keys.
crypto.createDiffieHellman,crypto.getDiffieHellmanandcrypto.createDiffieHellmanGroup:- The modp1, modp2 and modp5 to modp18 groups, explicit primes and generators, and generated safe primes are supported, with Node 24's key widths,
verifyErrorbits, key range checks and errors. - Secrets of 0, 1 and p − 1 are rejected, and so are peer keys outside the RFC 5114 subgroups.
- Primes longer than 8192 bits are refused.
- The modp1, modp2 and modp5 to modp18 groups, explicit primes and generators, and generated safe primes are supported, with Node 24's key widths,
- TLS:
tls.connectandtls.createServernegotiateALPNProtocols, and both sockets reportalpnProtocol.minVersionandmaxVersionbound the handshake, andgetProtocol()reports the version it picked.tls.createServertakes anSNICallbackthat answers aSecureContextsynchronously or later.new tls.TLSSocket(socket, { isServer: true, ... })andtls.connect({ socket })run the handshake on an open socket, such as an HTTPCONNECTtunnel.- A client whose peer closes before the handshake completes fails with Node's
ECONNRESET.
- Streams:
- Readable, Writable, Duplex, Transform and PassThrough streams take
objectMode,readableObjectModeandwritableObjectMode, and an object-mode side carries any value throughpush,write,pipe,pipeline,for awaitand'data'listeners. _writeand_transformreceive the encoding argument as Node passes it.CipherivandDecipherivare Transforms: they can be piped, used aspipeline()stages, read and iterated.pipeline()accepts function stages anywhere among its streams, and an array, a string or a synchronous generator as its source.
- Readable, Writable, Duplex, Transform and PassThrough streams take
- Packages compiled from their JavaScript:
- js-yaml and smol-toml compile statically and match Node: ES5 error constructors (
Object.create(Error.prototype),Error.call(this),Error.captureStackTrace) and classes extendingDatebehave as in Node. - TypeScript's UMD module wrapper compiles as the module body, and tsc's
__importDefaultand__importStarhelpers around builtin modules compile as plainrequire()calls. - Interfaces, type aliases and enums that only a package's
.d.tsdeclares keep their types.
- js-yaml and smol-toml compile statically and match Node: ES5 error constructors (
- JavaScript sources:
- Error constructors can be read as values, so
err.constructor === TypeErrorworks. workerDatacan be destructured.- Unary
+converts unions of strings, numbers, booleans,nullandundefinedasNumber()does, so+process.argv[2]compiles. - These compile and behave as in Node:
- chained readline listener registration;
Number.parseIntwithout a radix;matchandsearchwith a possibly undefined regular expression;Buffer.fromover an untypedArrayBuffer, with its byte offset;- Atomics on untyped arrays.
- Buffer methods run on Buffers held as untyped values.
- Error constructors can be read as values, so
fs:- The synchronous functions (
readFileSync,writeFileSync,statSync,readdirSync,mkdirSync,rmSync,openSyncand the others) acceptfile:URLs wherever they accept a path, with Node's conversion errors. fs.promises.rmtakesrecursive,force,maxRetriesandretryDelay, and removing a directory withoutrecursivethrows Node'sERR_FS_EISDIR.
- The synchronous functions (
TypedArray.ofcreates a typed array of its arguments, and a dynamic iterable spread intoArray.of,pushorTypedArray.ofis drained through the iterator protocol.- Global
isFiniteconverts its argument with ToNumber, asisNaNdoes. The Math functions andString.fromCharCodeevaluate every argument before converting any. Object.freeze,Object.sealandObject.preventExtensionsof a function act on its own properties, andObject.isFrozen,Object.isSealedandObject.isExtensibleanswer for functions. @noble/hashes' frozen hashers work.- EventEmitters used through both typed and untyped references share one listener list. An untyped
emitreaches typed listeners, an untypedemit('error')follows Node's contract, andlistenerCount,eventNames,listeners,rawListeners,removeAllListeners,setMaxListenersandgetMaxListenerswork on untyped values. - Template literals,
join,Array.prototype.toStringand the defaultsort()call an object's own or inheritedtoString, asString()does, instead of printing[object Object]. - Classes and generics:
- A generic factory declared to return a constructor type can return one of its local classes, including an anonymous class expression, and
class X extends factory(...)extends it. - Static methods of a function-local class compile.
- Constructor types declared in the program (
new () => unknown, types that also name static members, construct overloads) hold class values. - Members typed by a conditional type over a literal class type argument (
Field<string, "required">) resolve. - Object literals under an erased generic member may hold callbacks with optional or defaulted parameters.
- A generic factory declared to return a constructor type can return one of its local classes, including an anonymous class expression, and
- Assignment and update targets wrapped in parentheses or non-null assertions (
(x) = 1,h.sums[k]! += 10) compile. - Untyped values keep what JavaScript code adds:
- Properties that untyped code adds to a typed object literal stay on it.
- Named properties written to an array through an
unknownview stay on the array, andfor...invisits them after the indices. - An
unknownvalue asserted to a structural type with methods keeps its class's methods. - The type inferred from an empty object literal holds any non-nullish value, as TypeScript's
{}does.
- A WebAssembly module the build finds but does not translate rejects with a
CompileErrorthat names the reason, for example a proposal outside the translated feature set. - SC6101 names the functions that likely run often and use untyped values:
- It shows up to five, most likely hot first, with why their values are untyped: an
any, an untypedJSON.parseresult or an untyped JavaScript file. scriptc coverage --fail-on=dynamicfails on it, and--no-dynamic-tier-warningleaves it out.
- It shows up to five, most likely hot first, with why their values are untyped: an
Performance
- Regular expressions skip start positions that their leading character sets rule out, testing 16 positions at once. Searching 50 MB of text for nine case-insensitive alternations takes 0.83 s instead of 3.88 s (Node: 4.38 s).
- Loop counters stay integers in more loops:
- Counters that step beside a counted loop's counter (
for (let i = 0, p = 0; i < n; i++, p += 4)) stay integers: a pixel-str...
- Counters that step beside a counted loop's counter (
v0.4.1
0.4.1
Highlights
- Profile-guided optimization:
scriptc build --profile-generatebuilds an instrumented executable,scriptc merge-profiles(with--sparse) merges the profiles its runs write, and--profile-useoptimizes a release or speed build with the merged profile. tsc-ts built with a profile from its own type check checks other projects 1.15 to 1.2 times as fast, with the same diagnostics and memory. Builds without a profile are unchanged. - Faster cold builds of large programs: large class-based programs build in partitions instead of as one module, so their cold builds are 25 to 32% faster, and an edit to one function recompiles only its partition.
- npm packages: more packages that pass builtins, classes and promises around as values now compile from their JavaScript. Among them are execa 5, hot-shots, @opentelemetry/api, async-sema, buffer-crc32, yazl, tar-stream, smol-toml and truncate-utf8-bytes.
undici5.x and 7.x compile from an implementation that ships with scriptc, andisarray,safe-bufferandprocess-nextick-argscompile as the Node builtins they stand in for. - Dynamic-tier property access goes through per-site inline caches keyed on object shapes. On constructor-and-prototype code, the code that reads several object shapes at one site and an AST interpreter, untyped code runs 1.4 to 3 times as fast as in 0.4.0. Typed programs compile to the same code as before.
- Smaller arrays, class instances and number-keyed maps:
- The array header is 48 bytes instead of 64.
- Small arrays start with three inline slots.
- Instances of program class hierarchies keep their class id in the reference-count word instead of a vtable pointer.
Maps andSets with number keys store 16-byte entries.- Together they lower the peak memory of a large TypeScript type checker by 8 to 10%, with the same output and wall time.
Features
for awaititerates untyped values, including async generators,Symbol.asyncIteratorresults and synchronous iterators, whose values are awaited.yield*delegates inside async generators. A JavaScript generator whose return type is inferred asanycompiles, including one that delegates to itself.crypto.createECDHandECDH.convertKeyare supported for the 11 short Weierstrass curves mbedTLS implements, with Node 24's key formats, encodings and errors. Other curves are refused.undici5.x and 7.x:Client,Pool,Agent,request, the global dispatcher,fetchthrough a dispatcher, the error classes and theundici:diagnostics channels follow each version's behavior. Each importing package compiles against its own installed version. The limitations page lists the parts that differ.- Packages marked as never loaded:
"scriptc": { "packages": { "<name>": "unloaded" } }in the entry'spackage.jsonmakesrequire("<name>")throw Node'sMODULE_NOT_FOUND, so optional dependencies take their absent branch. util.promisifyof an untyped function returns a promise-returning wrapper at run time.- Destructuring with runtime computed keys (
const { [name]: v } = source) reads records and untyped values. A missing key givesundefinedand the element default applies. - Import cycles compile when a class is used only after both modules finish initializing. Reaching the class earlier throws Node's
ReferenceError: Cannot access 'X' before initialization. awaitof an untyped value adopts thenables, including objects that definethenat run time.Promise.prototypeandpromise.constructorcan be read as values. A child process can be given new properties and awaited as a thenable.- Function values convert their results and arguments when they are stored in a slot whose signature has the same structure but a different shape, such as a function returning
Promise<Message>stored in a() => Promise<Message | undefined>slot. - Declarations from SDKs and shared type packages: index signatures of
any,any[]members, classes that a package only declares, andPickviews of classes compile. - Builtins as values:
bind,callandapplywork on builtin functions, andBuffer.byteLength,Buffer.compare,Buffer.alloc,JSON.parse,fs.realpathSyncandString.prototypemethods can be read as values.eventsandEventEmittercan be used as untyped constructor values. - JavaScript sources:
- Bitwise operators convert boolean operands as
ToNumberdoes. arguments.lengthcan be read in sloppy-mode functions with named parameters.- A function value in an untyped slot may have any signature.
- Prototype methods take their JSDoc parameter types.
- A top-level function declaration that its module reassigns works.
- A
varcan be overwritten with a different kind of value. - A default-export object literal can hold class values.
- Bitwise operators convert boolean operands as
- Index-signature records answer
"k" in recordfor literal keys. A fresh array literal spreads into an array of a wider element type. Aconstannotated with a type that resolves toanyholds an untyped object. Set.prototype.addreturns the Set when its result is used, sos.add(a).add(b)compiles.execworks on regular expressions held asany, and a numeric enum from a package can be read by a runtime key (DiagLogLevel[level]).dns.lookupanswers that are decided at run time can be read as untyped values,net.connectaccepts Node'slookupoption, anddns.lookuppasses theADDRCONFIG,V4MAPPEDandALLhints.util.inspect,console.logandutil.formatprint program-declaredErrorsubclasses with Node's constructor-name header ([Plain [Error]: m]).- Typed arrays:
forEach,map,filter,some,every,find,findIndex,findLast,findLastIndex,reduceandreduceRightcompile on every typed-array kind.mapandfilterreturn the receiver's kind (a Buffer for a Buffer), and callbacks receive the element, index and array.BYTES_PER_ELEMENTreads answer the element size, including on untyped values. Reads andintests of members Node 24's typed-array and Buffer prototypes lack answerundefinedandfalse, as in Node. Destructuring assignment into typed-array elements compiles. Math.maxandMath.minwith spreads among other arguments (Math.max(...xs, 5)) compile, with Node's NaN and ±0 rules.- Generic arrow and function-expression bindings inside functions compile when they use nothing from the enclosing function's frame.
const xs = []inside a function takes the element type its later writes give it, as TypeScript evolves it, instead ofnumber[]. Bindings whose reads disagree or that are reassigned become dynamic arrays, as at module scope.
Performance
- Loop counters stay integers in more loops: counters compared as
i + cwith a bound, and for-loop counters that are reset or copied inside the loop. A record-boundary scan takes 104 ms (Node: 260 ms), and a typed-array scan takes 318 ms (Node: 801 ms). - Indexed reads of non-ASCII strings are inline in worker executables, and strings of up to 2^24 UTF-16 units keep their unit table. A lexer over a 2.7 MB non-ASCII source takes 190 ms (Node: 327 ms).
- Worker messages: a thread that finds its mailbox empty spins for up to 20 µs before it blocks. 100,000 round trips between the main thread and a worker take 360 ms, a quarter of Node's time.
- Local-time
Dategetters cache the zone offset, UTC getters reuse the last decomposition, and integers 0 to 99 convert to strings without allocating. Local-time date formatting takes 0.81 times Node's time. - Untyped code:
%and the bitwise operators run on integers for integral operands,o.x += dupdates a number property in place, and arraylengthreads answer from the element count. - Sorts by several keys (comparators that chain field differences with
||, including optional number fields with a default) sort by key in the runtime instead of calling the comparator, about 24% faster on a benchmark of such sorts. - Worker executables start their threads faster: the allocator marks its memory for core dumps only when the system would write a core, which removes about 680 calls that hold the address-space lock as threads start. tsc-ts checks tRPC 6% faster at 16 checkers.
- The native compiler walks IR without a closure per node, about 3 to 10% faster in the frontend and LLVM emission phases of large builds.
- Full cycle collections that the death rule requests run at the event loop's idle moments. Watch-mode rebuilds of large projects are 6 to 11% faster.
Fixes
a?.b.con ananyvalue short-circuits only the guarded step, as in Node: it isundefinedwhenais nullish and throws whena.bis undefined.- A failed checked cast to an optional record names the deepest failing path, for example
expected string at $.order.owner.id, got number. - Spreading a non-iterable value held as
anythrows Node'sx is not iterablemessage. process.versionsentries that the runtime doesn't have read asundefinedin projects that use@types/node.- Functions stored in JavaScript object literals get Node's names, taken from the property key.
monitorEventLoopDelay().percentile()returns 511 on an empty histogram, as Node does.- A CommonJS
varstatement that mixesrequire()declarators with other bindings no longer crashes. - Untyped async methods called through overrides or untyped receivers return their promises, which fixes internal compiler errors in programs that use zod's
parseAsyncthis way. - TypeScript enum exports in compiled packages stay
undefineduntil they are assigned, as in Node. - Element writes accept a possibly undefined index (typed-array writes, compound and update writes, element assignments used as values and destructuring targets) and convert it with ToNumber, ...
libnode dev pack inputs (CI only)
CI-only mirror. This is not a scriptc release.
These files are inputs for the development libnode pack that scriptc's Linux CI test lanes assemble (packages/libnode-common/dev-pack.json, .github/actions/libnode-dev-pack) until the prebuilt @scriptc/libnode-<platform> packs exist. They are mirrored byte for byte so CI does not depend on a third-party release staying available. CI verifies each file's sha256 before use.
| File | Original URL | sha256 |
|---|---|---|
libnode-linux-x64.tar.gz |
https://github.com/dockfries/libnode/releases/download/v24.22.1/libnode-linux-x64.tar.gz | b02809c5d332516cc72890fec0cbbca404bac526bd0f841e00a8a6056ec1ff54 |
node-v24.22.1-headers.tar.gz |
https://nodejs.org/dist/v24.22.1/node-v24.22.1-headers.tar.gz | 322e0a27db9088093ab8347bdc8c7e0aaa21dc9ed548390bf06b394494c09267 |
LICENSE |
https://raw.githubusercontent.com/nodejs/node/v24.22.1/LICENSE | Node.js's license, covering the libnode build and its bundled components |
libnode-linux-x64.tar.gz is the community dockfries/libnode build of Node.js v24.22.1 (libnode.so.137), redistributed under Node.js's license (MIT, with the bundled components' licenses in LICENSE).
v0.4.0
0.4.0
Breaking changes
- tsc-ts is the default TypeScript 7 checker on Linux and macOS. scriptc installs it with the compiler (
tsc-ts0.0.5 and its platform package for the host). Programs get the same diagnostics and generated code as with tsgo, and compiling takes less checker memory.SCRIPTC_CHECKER=tsgoselects tsgo, which stays the default on Windows. A build cache entry made with one checker isn't reused with the other. --npm-static=auto, the default, compiles more packages: packages typed by their DefinitelyTyped twin (@types/<name>), and untyped dependencies that another package's JavaScript imports. A program whose dependencies were refused before can now compile them, and a twin whose declarations disagree with the package's JavaScript fails the build at the use. Use--npm-static <pkg,…>to name the packages to compile.- Program type errors are reported as type errors instead of as refusals of the npm package that contains them.
Highlights
- WebAssembly: builds translate every WebAssembly module whose bytes they can see into native code, and
WebAssembly.compile,instantiate,validate,new WebAssembly.Moduleandnew WebAssembly.Instancerun them. There is no interpreter.WebAssembly.Memory,TableandGlobal,Module.imports,Module.exportsandModule.customSections, and function references from tables are supported, and modules can be sent to workers.scriptc coveragelists the modules a program loads, with--wasm <file.wasm>for modules loaded from paths the sources don't spell out. - Async functions that suspend only at their
awaits run as stackless coroutines instead of on fibers with their own stack, so a suspended call keeps only the values live across its awaits. - Discriminated unions of records are the record pointer itself, with no box around each value.
- The dynamic tier computes with NaN-boxed values: numbers, booleans,
undefinedandnullbetween operators no longer allocate.scriptc coverage --explain-dynamiclists the functions that run in the dynamic tier and why, and--tier=static-onlykeeps a program fully static. - npm packages: esbuild's bundled CommonJS chunks, CommonJS cycles and lazily initialized ES modules compile statically, as do classic CommonJS packages (late and JSON
requires, require cycles), class hierarchies across a package's modules, and the packages unblocked by type-only imports,@typespackages,punycodeandconstants. chalk 5 and the jose root entry compile.
Features
@scriptc/experimentaladdsThreadSlots, one value per key and thread on shared objects, read without a lock.node:testaddsmock.timers,getTestContext,t.assert.snapshotandt.assert.fileSnapshot, theTestContextvaluesfilePath,attempt,passed,workerIdandtags, module-levelbeforeandafterhooks, theSuiteContextand hook contexts, and unnamedtestanddescribecalls. A timed-out test cancels its running subtests.node:worker_threadsaddsBroadcastChannel,setEnvironmentData/getEnvironmentData,receiveMessageOnPort,markAsUntransferable,markAsUncloneable,threadName,resourceLimits.stackSizeMb, thestdout/stderr/stdinoptions and streams, andworker[Symbol.asyncDispose].node:netaddsSocketAddressandBlockList, andnet.connectresolves host names.node:httppools keep-alive sockets inAgents, usesglobalAgentfor default requests, readshttp.requestoptions at run time, and speaks HTTP over Unix domain sockets.fetchtunnels throughHTTP_PROXY/HTTPS_PROXYwithCONNECT.DisposableStack,Float16Array,Error.isError,Promise.tryand the regular expressionvflag are supported.- Iterator helpers run on generators,
MapandSetiterators, string and typed array iterators,Iterator.fromand untyped iterators. Object.groupByandMap.groupBygroup Sets, Maps and generators.v8,stream/web,dns/promises,EvalErrorandgraceful-fsare available, andTemporalis reported as absent, as in Node 24.util.promisifyworks over the crypto callback functions, dns lookups,child_process.exec's callback forms and callback-style functions.events.oncetakes anAbortSignaland works over streams.AsyncLocalStoragestores carry into timer, immediate,process.nextTickand microtask callbacks.- JavaScript classes compile like TypeScript classes when their layout or JSDoc types allow it.
- Classes that extend
EventEmitteror a stream class can be used as values, and so canEventEmitter, the stream classes and the error classes themselves. Constructor functions can inherit from them. - Class instances can be frozen and their fields redefined.
- Method overrides may differ from the method they override in parameter and result types, as long as each value converts.
- Library profiles add the
cstringclass. - An experimental young-object nursery for compiled classes and records is available at compile time with
SCRIPTC_YOUNG_NURSERY=1.
Performance
- Dense arrays have head room, so
shift,splice(0, n)andunshiftruns cost amortized O(1), andpopof a packed array is inline. - Index loops over arrays and typed arrays are versioned on one bounds guard, with stable lengths proven and typed-array memory tagged for alias analysis.
- Retain and release pairs are removed through private-array precision and last-use moves, borrowed string slices go into stack buffers, and records, tuples and spread updates stay in frame storage.
- SHA-384 and SHA-512 use AVX2, AVX-512VL or the arm64 SHA-512 instructions, and MD5's block function is unrolled.
- String character tests, optional-string
switches,String.fromCharCodeand long-stringlengthare faster, andBuffer.writewidens ASCII to UTF-16 sixteen bytes per step. - The cycle collector walks dead cycles once, frees large dead cycle sets in address order and no longer creates self-capture cycles on the release fast path.
- Workers return their memory while the event loop waits and when they finish, and default-region memory is committed as it is used.
- Lifetime regions carry across
await, and freed region chunks are reused. - Large closure environments compile in linear time, and two frontend scans no longer repeat.
- Programs keep stack frames only when they can read
stack. Before, a computed-key read on a dynamic value kept frames in every function; tsc-ts's own check is back to 0.3.6's speed. - The child-process runtime unit links only into programs that spawn.
Fixes
- Node 24 parity:
util.format,util.typesandutil.inspect,URLSearchParams,querystring(null-prototype results),string_decoder,os,eventsandtimers/promisesmatch Node 24. Class instances, iterators, iterator helper objects and holes in arrays print as Node prints them, and%d/%iformat dynamic arguments as Node does. - Silent wrong output is fixed for object identity,
Object.prototypevalues,Date.parse(V8's date grammar), stored builtins, constructor returns,arr && arr[0], ES5 class names, builtin function values, uninitializedletwrites, module-exportthisand1 ** Infinity. - Strings compare in UTF-16 code-unit order.
- Dates format with Node's local time zone names, convert to local time once for their local getters, and follow
TZchanges.Date.prototype.toJSONgivesnullfor invalid dates. - Errors: thrown Maps, Sets, typed arrays, functions and arrays keep their identity. Errors list their own name where they were created, errors held as
anyhave their ownstackaccessor and convert withError.prototype.toString, and errors that cross into dynamic code carry Node's error properties. matchAllrows andexecresults haveindex,inputandgroups, and named groups keep their null prototype through optional chains.- Unhandled rejections are reported after each timer and immediate callback, and async generator completions settle on Node's microtask turns.
- Missing
Recordand index-signature keys read asundefined, and generic functions that read a missing element returnundefined. - Sockets:
'data'listeners get strings aftersetEncoding, socket and stream chunks are Buffers, and an idle pooled http socket that receives data is destroyed. - Diagnostic code frames window long source lines.
- npm crashes, internal errors and false TDZ errors found while compiling popular packages are fixed.