0.4.13
Highlights
- Faster programs:
@scriptc/experimentaladdsparallelInto,parallelReduceandparallelFor: the chunks of a loop run on a runtime thread pool when a syntactic check proves that no chunk writes a value another chunk reads or writes, the same source runs its chunks in order under Node.js, and a loop whose chunks cannot run in parallel compiles to the sequential loop it compiled to before, with an SC6106 warning and the same output.new URL()answers an input that repeats the last parse from that parse's component strings, so a server that builds aRequestfrom a URL and then parsesrequest.urlparses the URL once per request instead of three times, and every returned URL is still its own mutable value. A compiled HTTP server serves 1.20 to 1.23 times the requests per second of the same server on the previous build, over 11 paired samples per workload with 95% confidence intervals that exclude 1.0 in all three, and the IPv4 host parser and the port normalization write their digits directly instead of callingsnprintf.end()on a chunked response whose head and chunks are still corked sends the head, the chunks and the terminator, with any trailers, in one write, as Node does, where the previous build sent the terminator in a second write; the bytes on the wire do not change. Small responses from a server built onnode:httpandResponseserve 15% to 16% more requests per second, and a larger framework app 3% to 8% on its text, JSON and POST routes, over 10 paired runs each with confidence intervals above zero; a response that already carried aContent-Lengthwent out in one write and is unchanged.- A checked element read that the lowering stabilizes in its hidden local is lent to the call that projects it, which removes the retain and release pair that each iteration took: the two target workloads run 1.57 to 1.62 times faster, with both intervals excluding no difference, and a program that stores the read in a
constfirst reaches the speed of the form without the copy (1.0327, an interval that includes no difference). - A class whose tree children are typed
C | undefined, orC | null | undefined, takes the tree-class layout that theC | nullspelling already used, which is the same pointer shape: a tree built withundefinedchildren runs 5% to 8% faster, and the gap between the two spellings falls from 1.18 times to 1.05. (x / c) | 0for a proven-int32xand a constant nonzero integercdivides with onesdiv i64instead of a floating-point divide and a conversion, 1.43 times faster on the workload that divides in a loop, with a 95% interval excluding no difference;INT32_MIN / -1keeps the conversion, because the 32-bit result wraps there.
- Faster builds:
- A build answered from the executable cache prints every warning the build reported, not only the performance summary, so a program with a type patch, a divergence or a start-up warning keeps the cached fast path, and a replayed warning uses the source text the cache entry kept, so the cached stderr matches the original build.
- Node.js parity:
fs.openSync,fs.mkdirSync,fs.chmodSyncandfs.fchmodSyncread the modes Node'sparseFileModeaccepts, an octal string or an integer in0..2^32-1, and an argument whose static type isanyor a union runs Node's own validation ladder at run time instead of being refused by the build:openSynctakes a string flag name or rawopen(2)bits,mkdirSyncanswers the first directory a recursive walk created and accepts the mode as the options argument itself, and each check throws Node's error with its code and message.fs.open's callback form reads numeric flags the same way, and an omitted,nullorundefinedmode there opens withparseFileMode's0o666default. The options record offs.writeFileSyncandfs.appendFileSynctakesmodeas a number or an octal string too, over string, Buffer and checked data, and a value whose type the call site did not pin down runs the same ladder at run time, so an invalid mode throwsparseFileMode's error before the path is touched. Theirflagoption takes rawopen(2)bits as well as a name, and a value the call site did not pin down runs Node'soptions.flag || defaultandstringToFlagsat run time, so an invalid flag throws itsERR_OUT_OF_RANGEorERR_INVALID_ARG_VALUEbefore the mode is read.- The stat family is supported:
fs.stat,fs.lstatandfs.fstatcallbacks andfs.statSync,fs.lstatSyncandfs.fstatSyncread Node's option ladders when their options are only known at run time,bigint: trueanswers theBigIntStatstable, in which every number member is a bigint and the four*Nsmembers sit beside them, andfs.Statsanswers every field and its sevenis*predicates. The three callback rows, the three sync forms,fs.Statsand its field rows, and thedev,inoandctimeMsrows move tosupported. fs.readSyncandfs.writeSyncrun Node's own argument ladders when the argument kinds are only known at run time: the options record read in destructuring order,length |= 0,validateOffsetLengthRead,validatePositionwith bigint positions, every ArrayBufferView and Buffer encoding for writes, and the descriptor where Node checks it.fs.closeSync,fs.fsyncSync,fs.fdatasyncSync,fs.ftruncateSync,fs.fchmodSync,fs.fchownSync,fs.readvSyncandfs.writevSynctake the same run-time path with Node's check order (the mode and the owner ids before the descriptor, the truncate length before it, an empty write vector answering 0 before it) and Node's error messages, and their rows move tosupportedtogether withfs.futimesSync. AreadSyncposition may be a bigint up to2n ** 63n - 1nless the read length.- node:os is complete:
getPriorityandsetPriorityfollow Node's default pid, one-argument shift, integer and range validation,userInforeads its encoding as Node does, andos.cpus()rows read their fields when the array comes from a binding; all 24osrows move frompartialtosupported. - node:dns answers resolve queries natively with a stub resolver that is linked only into programs that use it: UDP with a TCP retry, every resolve record type, Node's
DNSExceptionshapes and retry ladder, theResolverclasses withcancelandsetLocalAddress, andERR_INVALID_IP_ADDRESS. The 31 resolve and reverse rows acrossdnsanddns/promises, the four server-list rows and theResolverrows move fromnot-implementedtopartial, 42 rows in all. subprocess.channel, itsref()andunref(), answer as Node's do, anullorundefinedscalar or tuple entry inoptions.stdioopens Node's default pipe inspawn,spawnSyncandfork, andchild.stdiois Node's one array per child: the piped handles at their slots (the same objects asstdin,stdoutandstderr),nullelsewhere and the 3- or 4-entry length Node reports.sendlowers a nullish handle, thekeepOpenoptions record and a callback in either slot, and a'message'listener's second parameter is alwaysundefined; handle transfer stays refused by name.- crypto adds
publicEncrypt,privateDecrypt,privateEncryptandpublicDecryptwith Node 24's padding coercions, OAEP handling, error codes and implicit-rejection bytes, plusgetFips,getMacs,secureHeapUsed,setEngine, the deprecatedcrypto.fips,createMacwith itsMachandle, andX509Certificate.issuerCertificate, which answersundefinedfor every certificate a program builds and leaves every member on the prototype Node keeps it on. Node 24's Argon2, ECDH, KMAC and RSA-OAEP and import parameter dictionaries are declared for typed sources, and the new rows include 30 WebCrypto rows. NodeEventTargetand its eleven documented listener methods are available on aWorkerorMessagePortand follow Node's listener behavior, andTimeout.close()answers with the handle'sSymbol.toPrimitiveandSymbol.disposespellings.events.onceandevents.onregister the listener under a computed, symbol or other non-literal event name and move tosupported.- readline takes a non-string prompt through
getPrompt,getCursorPosandwrite, redraws on a terminal resize for stream andprocess.stdoutoutputs, and pausesprocess.stdinwhen the interface closes; 38 rows andERR_USE_AFTER_CLOSEandERR_INVALID_CURSOR_POSmove tosupported.InterfaceConstructoris recorded as not applicable, since Node does not export it. - The
util.typesbrand checks answer Node's brands: a module namespace and a compiledargumentsobject answer their brand, a class extendingDateanswersisDate, iterator helpers stop answeringisGeneratorObject, and the last two that refused to compile,isArgumentsObjectandisModuleNamespaceObject, answer at a call site the compiler knows the value for (theargumentsobject, a provableimport * asnamespace) and through the runtime's thread-local namespace hook otherwise. 44utilrows move tosupported, and the two predicates getpartialrows. - zlib is complete: the Brotli and zstd one-shots and factories cover every parameter, a
chunkSizeabove 2^53-1 throws Node'sERR_OUT_OF_RANGEon"size", a refused allocation throwsERR_MEMORY_ALLOCATION_FAILED, and the parameter error codes and theinfo: trueengine result are pinned against Node. The nine remaining rows move tosupported, and the chapter is fully supported at 77 of 77 rows. TextEncoder.encodeIntolowers to the typed runtime entry when both operands have their staticstringandUint8Arraytypes and the result is the{ read, written }record; the runtime fills the two counts through one walk, and every other shape keeps the checked-dynamic path with its destination check.- perf_hooks adds resource timing and the entry classes:
performance.markResourceTiming,clearResourceTimingsandsetResourceTimingBufferSizefollow Node'scacheModeassertion, getter values,toJSON()order, 250-entry buffer andsetImmediatesecondary-buffer flush, andgetEntriesreturns resource entries asPerformanceResourceTiming. Entry records take the class theirentryTypenames, a connectedPerformanceObserverbuffers the entries its marks and measures produce,takeRecordsdrains them synchronously, the callback receives aPerformanceObserverEntryListfrom an immediate,observevalidatesentryTypesandtype, and the three classes Node keeps unconstructible throwERR_ILLEGAL_CONSTRUCTOR.performance.nodeTiminganswersPerformanceNodeTiming's fields in Node's key order with the live loop metrics, andperformance.toJSON(),supportedEntryTypesand the module's freeeventLoopUtilizationlower too. 40 rows move offnot-implemented, ten of them tosupported. console.traceprints Node'sTrace[: args]line on stderr with Node's formatting and then the frames the call captured; the line, the formatting, the stream and which frames exist match Node, and the frame text is the program's own, so the row stays partial.- The stream utility predicates
isDestroyed,isErrored,isDisturbed,isReadableandisWritable, andReadable.isDisturbed, answer for thenode:streamclasses in every lifecycle state, for the web stream classes, their readers and writers and aResponsebody, and for any other value by the members Node's duck typing reads. The readable operatorsmap,filter,forEach,toArray,some,find,every,flatMap,drop,takeandreduceread theirconcurrency,highWaterMarkandsignaloptions from a value,find's type-guard callback narrows its result, and a checked-dynamicreduceaccumulator settles at its declared type. 21 rows move tosupported. - Colored
util.inspectrenders errors,ArrayBuffer,SharedArrayBuffer,DataView, weak collections, URL and MIME handles, generator objects andDatesubclass instances instead of refusing them, andformatWithOptions'%oand%Oreach the same renderings; a colored error dims builtin frames, underlinesnode_modulespackages and dims the working-directory prefix. - A compiled program takes
--test-reporter=specand--test-reporter-destination=stdout|stderr|<file>from its leading arguments in any order, sends the report where they say while program output stays on stdout, reports every taken flag inprocess.execArgvin the order given, and refuses an unknown reporter or an unopenable destination with a message on stderr and exit code 1. - A compiled program resets inherited ignored signal dispositions at startup, as Node does before it runs any program code.
Math.max,Math.minandMath.hypotaccept a spread iterable such as aMap's keys instead of throwing at run time,fetch()takes aRequest,RequestInit.cacheandcredentialsfollow Node, and a program with an unhandled rejection exits with Node's status and message.
- TypeScript parity:
- Computed class heritage expressions and generic bases whose type argument is
anylower, including defaulted arguments and JSDoc forms; a function-local class carries its static members on its class value, which reads static fields and accessors throughany,Reflect.getandObject.getOwnPropertyDescriptor; construction through a mixin result binding works; andthisin a class static block is the class constructor, sothis.prototype.xandthis.staticFieldrun as in Node. - Class index signatures take a
numberkey or a typed value and a read of a missing key answersundefined; a generic class taken as a value without type arguments is its erased instantiation andinstanceofthrough it answers for every instantiation; a getter override may narrow its base getter's type and a getter and setter may declare different types, with reads taking the getter's type and writes the setter's; a member of a class that extendsArraycan be called through elements of an array of its instances, and a JavaScript subclass'sthis.length = nwrites the inherited length. - A slot typed as a class's class value (
typeof C, or a constructor type returningC) holds a subclass whose constructor takes other parameters, and construction through the slot runs the held class's constructor with the arguments as written, as in Node. - A constructor declared inside a function can return an object that replaces the receiver as it does at the top level; a field holding a function that reads
argumentsor takes an untyped rest parameter can be called from typed code with the declared parameters and the argument array passed natively; and aletwithout an initializer can be assigned from a nested function declared above it, while reads and writes before the declaration throw Node's catchableReferenceError. - A JavaScript value of a union type converts to a narrower union at run time: an arm the destination holds converts on its tag, and any other arm throws the catchable
TypeErrorinstead of failing the build. Settinglengthon a JavaScript array held as a checked-dynamic value follows Node's rules: a shorter length truncates, a longer one leaves holes, an invalid one throwsRangeError, and a string is coerced first. - A
typeofof an import-cycle binding that can run before the declaration compiles instead of failing with SC1016, and until the declaration runs it throws Node'sReferenceError, as a value and inside atypeof x !== "undefined"guard. A JavaScript parameter documented with a bare{Map}or{Set}takes checked-dynamic storage as a bare array parameter already does, and a typedSetorMapfrom TypeScript is passed as the caller's own collection. - A JSDoc generic written without its type arguments (
{Array},{Map},{Set},{WeakMap},{WeakSet}) documents the type withanyarguments, and@readonlyon a getter, a setter or a method is read as documentation, so both compile instead of failing the build. new WeakRef(target)andderef()compile for the objects and symbols Node accepts: a reference keeps its target alive, soderef()answers it, and any other target throws Node'sTypeError. A JavaScript function reached through dynamic dispatch runs when its caller leaves a documented callback argument out and the callee readsundefined, and atypeofor property-key read of a binding declared later in its module cycle compiles when the read runs after the declaration.- Logical assignment and chained assignment through properties, computed members and callable own properties compile, as do
super[key](...)andObject.prototype.isPrototypeOfover live prototype chains.Function.prototype.toStringon a boxed function prints its source text, andutil.inspectrenders an Error subclass instance the way Node inspects it, including itscauseand fields created at run time, with the runtime additions linked only where the construct is used. - In unchecked JavaScript, an undeclared global read is a run-time
ReferenceErrorsotypeofguards work, a subclass static whose signature differs from its base's compiles, and a truthiness test of avoid-typed operand reads the value the call returned, while a// @ts-checkfile keeps all three diagnostics. - A JavaScript constructor that returns an object and reads or captures
this, or returns a primitive on another path, is refused at the construction site, and constructors that only return objects keep compiling, including one declared inside another function. - Converting a class instance to a primitive compiles in programs that write properties under computed keys: the conversion uses the declared methods until a write replaces one and the replacement after that, as in Node.
name in C.prototypeanswers for every class, and a TypeScript class's prototype works as a value forObject.create,Object.getPrototypeOf,isPrototypeOfand===, as a JavaScript class's already did. - A type-position
import('spec')['name']resolves astypeof import('spec')['name'], the form the type checker's own diagnostic asks for; the strict class-field demand is no longer reported for JavaScript the project did not ask the type checker to check, and reads of fields the constructor never assigns are validated.
- Computed class heritage expressions and generic bases whose type argument is
- CommonJS and islands:
- CommonJS modules whose whole export object or member exports are chosen inside a conditional compile, so
requiresees the table the executed branch named. - A function whose result is a promise of a module namespace converts into an untyped slot through an interned adapter, so
() => import("./chunk.mjs")loaders compile and the chunk loads at run time. - Package resolution selects a package's
scriptcexport condition beforenode,import,requireanddefault, with atypesbranch still leading, in every resolution the compiler performs, including the type-checked program. - A program built with
--libnode=besidehashes the Node island modules next to it against the executable's manifest on every start and refuses a tree that differs before Node loads. The build no longer writes a tree-name file beside the modules and removes one an earlier build left there, and trees installed in the user's cache directory are unchanged.
- CommonJS modules whose whole export object or member exports are chosen inside a conditional compile, so
- Running under Node.js:
@scriptc/experimental/registeris a preload fornode --requireandnode --importthat refuses unsupported builtin modules, native addons andprocess.getBuiltinModulecalls with the compiler's code and message, and warns when the running Node.js version or the code-generation flag differs from what a compiled binary enforces. It reads the refused modules from@scriptc/experimental/refusals.json, which lists every refused API with the builtin module, member path and access that reach it and every fenced builtin module with the SC1010 code and message the preload raises, beside the schema version, the Node pin and the compiler version.@scriptc/experimental/dual-runaddsdualRun(entry, options), which builds a test file through the executable cache, runs it under Node.js and as the binary, and returns both runs,equaland a readable diff; a build the compiler refuses rejects with aDualRunBuildErrorholding its diagnostics. Its reporter compares every filenode --testran, prints one line per file with a diff for a difference, keeps Node's own test failures in the report, reports a file whose Node run varies as flaky and fails the run on a difference.
- Fixes:
- A
LifetimeRegionover(C | null)[]no longer crashes, because the region's retain and release skip the shared null sentinel, and a nullableMapvalue read under a split key links and answersundefinedon a miss as Node does. - A stabilized checked read's hidden local is released by the statement or branch arm that stored it instead of by the enclosing block, which a short-circuit pass reaches without storing anything, and the read's projection is lent only to an in-place receiver, so a field read off it takes its own count instead of pointing into a temporary that dies with the statement.
- An
Immutablevalue passed to a library call that always throws is no longer counted as escaping to it, because the call throws before it can keep or write the value: afor…ofloop over anImmutablearray compiles when some other array in the program holds elements of a foreign type and makes the loop's checked element read lower toarray.foreignThrow, the read still throws, and a write through the array is still refused. - An indexed read of an
Immutablearray may yield anImmutablevalue only when the element type can hold an object: readingt.nums[0]of anImmutable<number[]>into untyped code compiles, while an element of anImmutable<Row[]>or anImmutable<number[][]>keeps SC7005. - A keyed read or write through an untyped value dispatches to the fields of a compiled class only when the value it receives can be a class instance, so a record or array receiver no longer hands a class's
Immutablefield to dynamic code; a site that can receive an instance keeps its per-class branches, and a write through anImmutablevalue is still refused. - A native
Proxykeeps its traps when it flows into aRecord<string, T>or another dictionary-typed slot, andString(proxy), concatenation and template literals produce Node's result ([object Object], or the handler's owntoStringor@@toPrimitive), with a named SC2020 diagnostic when a typed slot still cannot hold one.Promise.resolve(this)inside a method, andPromise.resolveof any value typed as the enclosing class, compile, and a destructuring loop variable in afor-ofover a class with its own[Symbol.iterator]binds array and object patterns from each yielded value. - A method call through a class-backed interface on a user
Errorsubclass that does not overridetoStringcalls the runtime'serror.toStringinstead of failing the build with SC9001; the arm tests the exact class, whose chain overrides nothing below the root. - The abort check of the
fs.statcallback form and offs.writeFileandfs.appendFilereadsabortedandreasonas ordinary JavaScript properties, so a statefuloptions.signalgetter cannot hand a non-handle value to native dispatch. - An extended key usage OID renders every arc exactly, so an arc wider than 64 bits no longer prints as a different, standard purpose in
keyUsageor intoLegacyObject().ext_key_usage. - net holds the listen, backlog and keep-alive option values while user code runs during coercion and reads bound handles only after callbacks register, drops connection-attempt and signal listeners once a socket settles, and dns frees a partly parsed server list on rejection.
- An aborted spawn's
'error'emit calls the listeners registered when the emit began: a handler may unregister itself or a sibling and still gets its call, a handler added during the emit waits for the next one, and a handler that unregisters itself keeps its captured values readable for the rest of its body. - A
parallelIntoorparallelReducecallback that is async, or that the compiler adapts to the documented callback type, runs its chunks one after another on the calling thread with an SC6106 warning, and in a program that reads stack traces every parallel loop runs its chunks the same way, as Node runs them. The runtime holds its own reference to the uncaught exception capture callback while calling it, so a callback that clears itself keeps its captured state. util.inspectholds aMaporSetiterator's collection and copies the property names and values it prints for as long as rendering can run program code, through custom inspect functions and theinspect.stylesaccessors, and a query pair whose value conversion throws releases both converted halves.- DNS queries draw their transaction id from the system CSPRNG without scanning the outstanding-query list, because each query's responses are read from its own connected socket, so allocating an id always finishes however many queries are outstanding.
net.connectandnet.Server.listenvalidate an abort signal before opening anything: a value that only looks like anAbortSignalfails with theTypeErrorthe listener install raises, and the socket, callback and registration are dropped instead of dialing behind a pending exception. - A member of a class that extends
Array, used on an array value, checks that the array is an instance of the class: a method call on any other array throws Node's "is not a function"TypeError, and a field or accessor throws aTypeErrornaming the member. NativeProxyoperations hold their own reference to the Proxy until they return, andnew DOMExceptionholdsoptions.namewhile converting it, so a trap may delete the property that refers to the Proxy. - A native
Proxyreturned from a function declaredanyor with an interface or object type keeps its traps for typed callers, and theTypeErrorfor a Proxy converted to a program class type names interface and object types, says that a class type holds only instances of the class and suggests an interface. Object.setPrototypeOfon a class instance lasts as long as the instance does:getPrototypeOf,isPrototypeOf,in,instanceof, method calls and reads go through the new prototype, while instances whose prototype was never replaced keep native class dispatch.- The abort-signal check that net's connect path uses is linked with the net unit, so a program that links http, http2 and net without event targets builds again.
- The sequence scope of a stabilized checked read opens with the emitter's scope pair instead of a closure: through that closure the compiler's own build had lowered
emitReadReceiverdynamically (4,933 stack slots, 108,940 LLVM lines and 4,815 tag checks, against 409 slots and 14,196 lines), and the bootstrapped second generation overran the shard's 8 MiB stack. The emitted module of every program is unchanged. - A compiled program suspends when its launcher blocked SIGTSTP, restores the terminal mode at exit and on
process.exitthe way Node'suv_tty_reset_modedoes, and clears an inherited signal mask at startup.
- A