A full batch of 64 events polled again without returning to the event
loop. When synchronous socket callbacks kept 64 events ready on every
drain, timers and setImmediate callbacks never ran. The poll phase now
drains at most 48 full batches, the count libuv's uv__io_poll uses, and
the budget carries across the reopen that lets microtasks run, so the
remaining events wait for the next loop iteration.
Structural conversion lifts a function into a union's function arm only
when cleanFuncAdaptable accepts the pair, and it refused every rest
signature. A typed-rest function assigned to `((m: string) => void) |
null`, an optional parameter of that type, or a `| undefined` binding
therefore failed with SC2003. cleanFuncAdaptable now accepts the pairs
the rest packing adapter serves, so these slots use the same adapter as
a plain fixed-parameter slot.
6445 covers the nullable, optional and undefined slots, and 6445 and
6446 get their corpus costs.
A comptime record value whose union has a discriminant takes the arm its
discriminant field names, so a value that fits two arms with overlapping
shapes gets that arm's tag and layout instead of the first arm it fits.
comptimeBakeable reports a record type reachable from its own fields as
not bakeable instead of recursing without end, so such a comptime result
reports SC1090, and the message names unions and that limit.
A read through a computed key now counts as a stack read when the program
contains the string "stack", so `e[key]` captures frames like `e.stack`
does. A key the program assembles from other strings or from data it
reads keeps the static answer.
structuredClone of an error held as `any` copies a stack that was already
formatted as a non-enumerable data property, as it did before the stack
became an accessor.
When a name or message getter throws while the stack is formatted, the
stack accessor propagates the exception instead of reading the missing
string.
The references of a cycle class were found by their spelling, so a class
imported under another name (`import { Sink as Alias }`) was never seen as
reached early, and neither was a let or const imported under another
name. An `extends` clause only counted when its expression was the bare
class name. A static method read as a value (`const f = Sink.m`) built a
closure that no check covered.
The early-binding scan and the class reference scan now also match each
file's local import names that resolve to the binding, and record them:
Node's ReferenceError names the binding the access spells, so the check
names the alias. The heritage check walks the whole `extends` expression.
A closure of a flagged class's static method checks the class like a
static call does.
A function's parameter initializers run in their own scope, which sees
the parameters and a function expression's own name but not the body's
`var`s. Chunk splitting resolved them against the body's names too, so a
helper that a default value reads was not copied into the chunk when the
body declared a `var` of the same name, and the chunk read an undefined
binding. A computed method name now resolves outside the method.
A named function expression whose body declares a `var` of its own name
still exposes the shared function to its parameter initializers, so those
reads must be calls for the function to travel with a chunk.
The on-demand import scan tested each file's text for `import(` before
reading its calls, so `import /* note */ ("pkg")` was never followed:
the workspace package it loads and that package's npm dependencies were
left out of the module graph, and the dependency was refused with
SC2013. A file is now skipped only when it does not contain the keyword,
and its literal import() calls are read from the syntax. The
workspace-dynamic-import case loads its package with such a comment.
The trim helper returned parked workers' memory once no worker had run for
100 ms. A compiler in watch mode starts its workers again for each rebuild
and faulted the returned pages back in, and a rebuild that runs on the main
thread alone had the discards run alongside it.
- The delay starts at 1 s (SCR_SA_DISCARD_DELAY_MS) and doubles each time
workers start within ten delays of a discard, up to
SCR_SA_DISCARD_DELAY_MAX_MS (10 s by default).
- Once the event loop of the thread that is not a worker runs, the helper
discards one arena at a time, and only while that loop has been in one
wait with nothing ready for a step (scr_sa_loop_waiting, called by the
event loop around its sleep). A loop that does not wait gets the
discards SCR_SA_DISCARD_DELAY_MAX_MS after the delay.
- Programs without a running event loop discard after the delay, as before.
Node passes write and end callbacks an error argument (null on success),
but the native callbacks receive none, so an inline callback that names
the parameter read undefined. It now refuses where it is written. The
stream-shims npm case compiles now that stream subclasses are values;
its remaining scenarios are deferred refusals, so it is recorded as one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A class without a constructor that extends a stream class or
EventEmitter inherits the runtime constructor, which takes no arguments
here. Direct construction with arguments already reported that, but
construction through the class's value (`new registry.upper({ ... })`)
completed the arguments against the empty inherited signature and
dropped them. Both paths now share refuseInheritedBuiltinCtorArgs, so
the class-value path reports at the first argument with the same
message.
Validation, translation and instruction skipping now read memarg flags
through one function, readMemargFlags in module.ts. Flags with bit 6 set
are followed by an explicit memory index, which may be 0; flags of 128 or
more are malformed.
The translator refuses a module (WasmTranslateError) when a memarg,
memory.size, memory.grow, memory.init, memory.copy or memory.fill names a
memory other than 0, and when the operand of local.set, local.tee,
global.set, table.set, a load or a store does not have the type of its
destination. Locals are stored with their declared type.
A const annotated with the asserted type (const view: Route & { order: number } = route as Route & { order: number }) took the annotated layout, so the initializer was coerced into it: a width copy that read members the writes had not created yet, and writes through the binding no longer reached the original object. Local and module-level consts now keep the stored type when it hosts the annotation, as unannotated ones do.
A Pick or Omit view of a program class that holds the instance (its view
exposes a method) now carries the class's identity in the flow analysis,
and its values count as instances of that class. Assigning such a view
to another structural type keeps the instance, so reads through the
second type see the instance's later writes instead of a copy's.
- A function adapter whose source returns `any`/`unknown` (a generic
wrapper's result handed back `as F`) converts the result through the
checked-dynamic bridge when the slot's result is checkable: a record
checks its shape, and a Promise keeps its identity and checks its
payload when it settles. Before, these adapters threw "the call has
no exact lowering" when called.
- In a class constructor, `Object.setPrototypeOf(this, new.target.prototype)`
(directly, through a const, or the `if (Object.setPrototypeOf) ... else
this.__proto__ = ...` form), and `Object.setPrototypeOf(this,
C.prototype)` in C's own constructor when no class extends C, are
no-ops: a compiled instance already has that prototype.
- Object.assign between records copies a source field into a wider
target field (`{ bail: true }` into `bail?: boolean`).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
util.promisify(fn) over a function typed any (an npm package method typed
by its JavaScript, a bound method of an untyped value) was refused,
because the promisified signature is only known at run time. A native
wrapper now calls the function with the call's receiver, its arguments
and a callback, and answers a promise: a truthy error rejects, otherwise
the first value fulfills, the first settlement wins, and a synchronous
throw rejects. util.promisify.custom functions are not consulted.
The wrapper lives in its own runtime unit (scr_util_promisify.c), linked
by the new utilPromisify feature, so other programs are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build-utils declares its error module's bindings and initializer in one
statement (`var NowBuildError, mapTypoToSuggestion, init_errors = __esm(…)`).
The split pieces follow the statement, so the initializer only needs to be
its last declarator.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `Promise.all([p, q, r])` over different promise types (the tuple
overload) lowers in TypeScript through one interned async helper that
awaits the runtime combinator over the same promises (widened to
Promise<void>, a type-only view), so settlement order and rejection
handling are Node's, then reads each fulfilled entry into the tuple.
Entries keep their identity; plain scalar entries pass through, and a
checked-dynamic entry joins through the promise it holds.
- The ambient `Boolean` as an `every`/`some`/`find`/`findIndex`/`map`
callback in TypeScript is the interned one-parameter truthiness
closure `filter(Boolean)` already used, and `filter(Boolean)` accepts
checked-dynamic elements (runtime truthiness).
- `E[key]` with a runtime string key answers the named member's constant,
or undefined for a name no member carries; a numeric member's reverse
key and inherited Object.prototype names throw a named runtime refusal.
- Regex-argument `replace`/`replaceAll`/`split`/`search`/`matchAll` on a
`string | undefined` receiver take the string lowering and throw Node's
"Cannot read properties of undefined (reading 'replace')".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The read used to defer a refusal, which kept the corpus program from
compiling statically. It now throws Node's catchable ReferenceError
("__dirname is not defined"), and typeof answers "undefined" as in
Node.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`s.charAt(i) === "c"`, and `s[i] === "c"` where the index is proven in
bounds, built a one-character string to compare with the literal. When
the literal is one UTF-16 code unit, the comparison now reads
`s.charCodeAt(i)` and compares the number. charCodeAt converts the index
the same way charAt does, and an index out of range answers NaN, which
equals no unit, as "" equals no one-unit literal. A switch on
`s.charAt(i)` whose case tests are all one-unit literals switches on the
code unit the same way.
A switch on a `string | undefined` value with string literal cases, such
as an array element or an optional field, lowered to a chain of union
equalities. It now tests for undefined, which matches no case, and
dispatches the string through the string switch: by byte length, then by
the literal's leading bytes.
`String.fromCharCode(a, b, ...)` with up to sixteen arguments passes the
codes to the runtime in a stack array instead of a heap f64[], and up to
64 code units encode into a stack buffer instead of a heap scratch block.
`.length` of a string that is not ASCII counts its UTF-16 units. The
string index keeps four entries, so a program that read the lengths of
more long texts than that in turn (the text of every source file) walked
each whole string on every read. Lengths of non-ASCII strings longer than
64 bytes now go into a 1024-slot table keyed by address, allocated by
the first length it records. A slot is dropped when the string is freed
or reallocated and when an append changes it in place, which is where
the index drops its facts.
Date-only utimes, literal mkdir recursion and promise-backed readFile
callbacks: the forms that compile natively with either type world.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A bundled package's error class defined inside an __esm initializer
(`NowBuildError = class extends Error { … }`) now hoists to a declaration
like the classes over builtin module members, so a program class can
extend it and `new` and `instanceof` work against both. Reading a standard
global constructor the bundle never rebinds runs no code. References in
esbuild's `0 && (module.exports = { … })` annotation for Node's CommonJS
lexer never run and no longer block the move.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fs-extra builds its exports at load time (computed keys over graceful-fs,
universalify wrappers), which the static CommonJS model cannot follow, so
programs reading its members through the default import failed. The
package-shim registry now maps fs-extra 8.x-11.x to a TypeScript
implementation over node:fs: fs's members re-exported, the wrapped methods
following universalify's callback-or-promise rule over fs.promises, and
the extras (copy, move, remove, emptyDir, mkdirs, ensureFile, pathExists,
outputFile, the JSON helpers and their aliases) with fs-extra's messages.
Its declarations stand in for @types/fs-extra, including the default
import, and a namespace import resolves to the shim. What it does not
implement is not declared, so using it is a type error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A class of an import cycle that code in the other module can reach before the class statement was refused (SC1016). It now gets the initialization flag a module's own early-reached classes already had: references from code that can run earlier check it, so Node's ReferenceError appears only when the class is actually reached early. The reference scan resolves other modules' import bindings. An `extends` clause in another module that can run early keeps the refusal, since a compiled subclass is static.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Linux test jobs set ASAN_OPTIONS=detect_leaks=0 for both flavors and
left leak detection to the RC audit, which does not see runtime tables
allocated with calloc. A worker thread's record-source table leaked that
way, and only local runs reported it. The san shards' case-sharded corpus
task now sets detect_leaks=1 and an LSAN_OPTIONS suppressions file,
tests/harness/lsan.supp, which starts empty. Plain shards, the side queue,
the Effect jobs and macOS, whose ASan has no LeakSanitizer, keep leak
detection off.
A loop that counts with number locals (`lines++`, `run = 0`,
`longest = run`) kept each counter a double, so every increment was a
dependent floating-point add, and conditional increments compiled to
branches. The speculative integer copy only covered loops with products
or variable steps.
The range analysis now bounds for-loop cursors that are reset to an
integer literal or set to another bounded binding, besides those it
already bounded by their increments. A cursor's writes take its loop
range, which holds every value it takes in the loop, so `changes + 1`
stays proven at the limit of a typed array's length. The analysis
reports these loop ranges per for loop.
Where an exception leaves the function (no enclosing try or finally,
no coroutine), the emitter keeps such cursors in i64 shadows for the
loop: one copy of the loop, no checks, and each double is written back
when the loop exits. A loop with a labeled break or continue keeps its
doubles.
An object literal with get/set accessors keeps the accessors' closures in
reserved slots of its record. Since function values became convertible to
checked-dynamic values, these records passed the JSON and conversion
checks: JSON.stringify omitted the getter's property, as it omits
function-valued ones, and a copy held as `any` or `unknown` read the
getter's property as undefined and listed the slot's internal name among
its keys. Node invokes the getter in both cases.
Records with accessor slots are now outside the JSON-safe and
dyn-convertible domains, so JSON.stringify of such a value and its
conversion to `unknown` or `any` are refused at compile time. A literal
written directly into an `any` slot keeps its accessors and is unchanged.