Construction from checked-dynamic code (`new F(options)` with F an
untyped value holding a ctor-less subclass of a stream class or
EventEmitter) dispatched to the class's constructor and completed the
arguments against the inherited signature, which takes none here, so the
options were dropped. The checked construction dispatch now reports
those arguments at run time with the message typed construction reports
at compile time, located at the class: a fixed argument count refuses at
the call, and a spread call refuses when its argument list is not empty.
- Promise.race admits a Promise<never> entry (the deadline idiom) under
any result type: it can only reject, so its fulfillment adapter traps.
A Promise<never> executor's one-parameter resolve validates against the
void inner.
- Array(n).fill(v) and new Array(n).fill(v), which the checker types as
any[], take v's type.
- Object.entries/values over an index-signature record cast to a narrower
value type check each value into the element type instead of refusing.
- dns.LookupAllOptions and the lookup-function option union that holds it
(LookupOneOptions | LookupAllOptions | number | undefined) ride
checked-dynamic storage, and dns.lookup passes a cast of a
checked-dynamic options value through unchanged.
- npm packages: a CommonJS module that names its export object through a
variable (`var ip = exports; ip.isPrivate = ...`, the ip package) is
respelled through `exports`, and tsc output that adds a default-export
object (`exports.default = { metrics: metrics_api_1.metrics }`, the
@opentelemetry/api index) keeps its named exports linked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TypeScript's ES module emit for an exported enum is `export var Kind;`
followed by the IIFE. The rewrite replaced the declaration with a plain
`var Kind = { ... };`, so importers of the module lost the export. The
replacement now keeps the modifiers before `var`.
`s.add(a).add(b)`, `s.add(x) === s`, `return s.add(x)` and other reads of
add()'s result were refused with "chained Set method calls": the lowered
add was a void intrinsic. A used result now reads the receiver once into a
slot, adds, and answers the slot, as Map's set() already does. Discarded
results (statements, arrow expression bodies, `void`, for headers, comma
left operands) keep the plain intrinsic, so existing programs compile
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`deadDeclarators.add(d).add(memoDecl)` stopped the self-hosting build
with "chained Set method calls ... has no scriptc lowering yet".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `s.match(re) ?? []` / `|| []` made typescript-go's API panic whenever the
union's members were serialized (the empty literal is a tuple clone
without tuple data), poisoning the statement with SC0004. The members
of the union's widened type are readable and describe the same values,
so constituent reads answer with them; the empty tuple beside a match
array is one of its values. `checker-panic-tuple.ts` compiles and moves
to the corpus.
- Spreading a fixed tuple into an array literal copies its positions
into the literal's element type.
- fs.Stats values are an array element kind, so `Promise.all` over
`stat()` promises and `Stats[].reduce` compile.
- `.then(String)`, `.then(Number)` and `.then(Boolean)` over a number or
boolean settled value convert it through an interned closure.
- Inspecting (util.format, %s, util.inspect) an Error-typed value whose
runtime class declares fields prints the stackless base with Node's
constructor-name header (`[Bailable [Error]: m]`) and the own
properties, `[Bailable [Error]: m] { bail: true }`; it was refused. A
subclass whose own fields cannot render (functions, or a field Node's
error inspector treats specially) throws a named refusal on its own
instances only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inspecting (util.format, %s, util.inspect, console) a value of an Error
class whose hierarchy contains program-declared subclasses was refused:
errorRenderable counted the subclass layout's repeated builtin prefix
fields (name, message) as declared fields. Those are the runtime slots
the error base already renders, so field-less subclasses now render.
The runtime builds the stackless base from the instance's `name`
(`[Error: m]`); Node's improveStack also names the constructor. Each
program class (most derived first) is an instanceof arm applying that
rule: `[Plain [Error]: m]`, or `[FetchFailedError: m]` when the
constructor name contains the error's name. Runtime error classes keep
their own header.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Node answers a Unix-socket server's address() with its path. Programs
that call listen() with a string argument now get a Unix-aware
address(): typed `string | AddressInfo | null` (the @types/node
declaration), it answers the path; typed as the AddressInfo record alone,
which cannot hold the path, it throws for such a server, as does the
composed address().port read. Every other program keeps the record-only
lowering, so their output is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`deadDeclarators.add(d).add(memoDecl)` stopped the self-hosting build
with "chained Set method calls ... has no scriptc lowering yet".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@types/node types the transform/flush completion callback's data as any
and a child stream 'data' chunk as any, and ChildProcess accepts
(code: number, signal: string) exit and close listeners through its
catch-all overload. None of these compiled: the any-typed completion
callback crashed the LLVM emitter, and the child listeners were refused.
The completion callback's data parameter is typed Buffer | string |
undefined while the option callback lowers. Child stream 'data' listeners
with an unknown or any chunk box it as a checked-dynamic value, taking
strings when a setEncoding statement on the same stream precedes the
listener and Buffers when the function never calls setEncoding on it;
other placements are refused. Exit and close listener parameters declared
number or string bind as checked-dynamic values, so the body sees the
null Node passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in the review merge and "Keep an Error's code slot in step with
dynamic writes and own properties". With main's split, `'code' in err`
(now the runtime's error.hasCode) lands in expressions/relational.ts.
Corpus 6417-error-code-dynamic-writes renumbers to 6415 (main has its own
6417), and the corpus-cost entries of 6407/6408 follow their renumbering
to 6412/6413.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Node's promisify copies the original's own property descriptors and
prototype onto the wrapper and consults util.promisify.custom. The
runtime wrapper now copies name and length and refuses, with SC2020 where
util.promisify runs, an original with any other own property, instead of
returning a wrapper that lacks them. The node24-util-adapters npm case
now gets past its promisify scenarios and stops at an unrelated deferred
Object.create refusal; its known state records that.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`throw Object.assign(failure, { bail: true })`, where the record's type
lacks the source's member, used to fall to the Object.assign fence: a
fixed record cannot grow a field. As the operand of a throw, the call now
copies the target into a checked-dynamic object and adds the source's
members. The copy is unobservable, because control leaves with the throw
and nothing on that path reads the target again. Paths that already
lowered run first, so no compiled program changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A workspace package loaded on demand had its npm dependency selected, but
the selection never scanned that dependency's own JavaScript: it is
outside the startup module order, so a compiled CommonJS dist requiring a
sibling package was refused with "the module is not supported yet". The
scan of the import() graph now follows opted-in package JavaScript through
its literal require() and import edges.
The lowering appended those modules by their import declarations only, so
a CommonJS file's requires never joined the compiled graph and their
bindings stayed fenced. Preflight's resolved module edges, require edges
included, now reach the walks that append import(), fork() and Worker
modules, with lazy requires ordered after their module and inline require
cycles answered as at startup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A compiled package spells each enum as a bare `var` filled by an
immediately invoked function (`(function (Kind) { Kind["A"] = "A"; })(Kind
|| (exports.Kind = Kind = {}))`). Inference types the variable from the
empty object the argument creates, so every member read landed on `{}` and
failed at run time. When every member value is a literal, the declaration
now becomes the object literal the function builds, keys in Node's
own-property order, and the call becomes the export assignment it
performs. The TypeScript 5 spelling, the earlier `Kind = exports.Kind ||
(exports.Kind = {})` spelling and unexported enums are recognized; merged,
computed or otherwise written enums keep their text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A program that spelled "stack" anywhere treated every computed-key read on a checked-dynamic value as a possible read of an error's stack, so it kept a source frame in every function. The key of such a read now counts only when it can be "stack": a string literal answers by its value; a number, bigint or boolean key, or one converted from such a value, cannot be "stack"; an immutable local answers with its one initializer, and a parameter of a function the program only calls by name answers with the arguments its call sites pass. Any other key counts when the program spells "stack", as before.
An 'unknown' value flowing or cast into a NodeJS.ReadableStream slot and
'for await' over one now select the general representation, where the
checked cast to Readable applies. pipeline() stages that arrive untyped
unwrap boxed native streams at run time. @types/node's zlib interface
declarations map to the runtime Transform.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- '"k" in record' with a literal key naming no declared member of an
index-signature record (a match's named groups) takes the keyed
presence probe a runtime key takes.
- Spreading a fresh array literal (directly, through a conditional, or
through a const used only there) into push/unshift or an insertion
converts its elements into the destination's element layout; nothing
else can reach them, so the copy is unobservable.
- A const annotated with a type the checker reads as 'any' and
initialized by an empty object literal holds the checked-dynamic
object, so keyed writes fill it (the empty struct could hold nothing).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
http.request, https.request and get now accept options the compiler
cannot see as an object literal: a RequestOptions variable, a literal
that spreads one, or a literal that sets socketPath. The runtime reads
the record in the ClientRequest constructor's order with Node's errors;
documented options it does not implement throw the runtime fence when
they carry a value. https adds rejectUnauthorized, ca and servername.
socketPath dials a Unix domain socket in the net unit; the agent keys
its sockets by the path as Node's getName does, and the Host header
defaults to localhost. server.listen(path[, cb]) binds a Unix socket and
removes the socket file when the server closes; a numeric string is
still a port. Errors name the path ("connect ENOENT /path", "listen
EADDRINUSE: address already in use /path"). Windows reports ENOTSUP.
A TypeScript object literal with no contextual type that spreads a
checked-dynamic value maps to the checked dynamic, so
`const options = { ...location, path, timeout }` compiles.
A socket's 'lookup' event reaches its listeners with (err, address,
family, host) for each address a host name resolves to, or once with
the lookup error, before 'connect'. Only programs that listen for
'lookup' install the lookup records; elsewhere the hook sites are a NULL
check.
server.close((err) => ...) runs the callback with undefined at 'close',
or with ERR_SERVER_NOT_RUNNING when the server was not listening; a
server that never listened now emits 'close'. IncomingMessage 'data'
listeners may take string chunks after setEncoding, and res.end()
accepts a string-or-undefined chunk.
fetch's BodyInit and HeadersInit unions, as RequestInit-derived option
types spell them under @types/node, map to the checked dynamic, and a
URLSearchParams body is sent form-encoded, as by Node.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- writeFile and outputFile with byte data and a mode open the file with
that mode, and writeJsonSync and outputJsonSync pass the mode.
- copySync and moveSync compare source and destination inodes and walk
the destination's parents, as copy and move do.
- The resolver parity sweep skips the project-import comparison for
shimmed packages.
- The JSON reader writes the byte order mark as an escape.
Replaces the single-parent "Merge main" squash (6ab9a5423) with a two-parent merge of the same tree.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fallback NodeJS stream interfaces declare Node's EventEmitter listener
signature for events beyond data/end, so checked JavaScript keeps its
contextual typing for process stream listeners. The child stdin fence
fixture becomes a differential test of the forms that now compile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #1433's review follow-up (now merged into #1413). In `key in
err`, own `stack` and Object.prototype keys answer true, keys built-in
errors keep outside their class fields stay fenced, and every other key
keeps this branch's answer: a declaring subclass or a runtime-set own
property.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings main in through the already-resolved stack merge (f4c6441ce: main
merged into #1413 and #1433). Main split lower-exprs.ts into
expressions/*: the review follow-up's Error 'in' keys (own 'stack',
Object.prototype keys, BUILTIN_ERROR_SLOT_KEYS gating the declared-member
fold) move to expressions/relational.ts, and the perfLoop feature keeps
main's rule table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An object literal that spreads an index-signature record but has
members outside the scalar join (an array of labels), an unknown-valued
spread (Record<string, unknown>), or a member a later spread may
overwrite with another type now maps to the checked-dynamic value: it is
built like a JavaScript object literal, so spreads copy their own keys
in order and later keyed writes land on the same object.
'c && { k: v }' spreads contribute k exactly when c is truthy even where
the checker's union of the two does not map (an AbortSignal member), and
a conditional spread onto an 'unknown' field leaves the checked-dynamic
undefined in its empty arm.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every read of an AST list field built an AstNodeList whose pos and end
were decoded from the wire first, though most readers only use `nodes`.
The list now keeps the wire file and the list's index, and pos and end
decode when they are read. The allocation per read stays the same size,
and a decoded file still holds no list objects.
An object literal with get/set accessors converted to `any` or `unknown`
listed its reserved closure slots (`%get:x`) as data keys, so a dynamic
read of the accessor answered undefined without calling the getter, and
Object.keys and JSON.stringify listed the slot instead of the property.
The checked-dynamic copy now defines each accessor as one enumerable,
configurable property whose getter and setter are the slot closures, in
declaration order: the record shape records its accessor key order
(`accessorOrder`). Live views of such records build that copy, and
checking a copy back into the record reads the closures from the
accessor's getter and setter. Reads, writes, Object.keys,
Object.entries, JSON.stringify and property descriptors answer as in
Node.
- A builtin alias whose replacement starts with a global the file
declares (`process`, `Array`) keeps the package's own module: the
replacement would read the local binding.
- A polyfill package the project marks unloaded has no builtin alias, so
its require throws MODULE_NOT_FOUND.
- The undici shim rejects a Host header value with characters outside
the header value set (CR, LF and other controls) with `invalid <key>
header`, as undici 5.19.1 and later and 7.x do, before the client
writes it into the request head.
- Format lowerer.ts and type-mapper.ts.
child.stdout.pipe(), stream events and writes beyond write(string) on a
NodeJS stream interface slot, and pipes into a child's stdin now lower
the program with the general representation instead of fencing. The
fallback ChildStdin is a NodeJS.WritableStream, and the coverage
remainder relowers its unreached code the same way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A call that resolves to an overload signature of a generic function or
method with an implementation (`load<T>(k, map: Mapper<T>): Promise<T>;
load(k): Promise<Reply>; load<T>(k, map?) {...}`) monomorphizes the
implementation under the bindings the overload's resolved signature
proves; a type parameter the overload never mentions is unknown, and
parameters the overload omits must be optional in the implementation.
It was refused.
- `view.m(...)` where `view` is a `Pick`/`Omit` view of a program class
and `m` is a generic (or generically overloaded) method extracts the
instance from the view's live receiver and calls the method statically,
instead of a runtime refusal reading the method through the untyped
view.
- Binding inference pairs the arms of `T | Promise<T>` with an
instantiated `R | Promise<R>`, and `unknown | Promise<unknown>` maps to
the checked-dynamic value the checker reduces it to.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In minified output the class closes right where the initializer body
does (`…Counter=class{…}}})`), so the empty insertion that opens the next
run-once piece shared its position with the body's closing edit and was
dropped: the initializer then called a piece that did not exist. The
opener now rides on the class's own closing brace.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A bundle that embeds fs-extra (build-utils does) compiled the package's
own JavaScript, whose computed export table and graceful-fs internals the
static model cannot follow. A __commonJS chunk whose key names a shimmed
package's main file at a supported version now requires the shim
instead, through a forwarder declaration beside the bundle: the checker
follows it to the shim (a package's JavaScript cannot load TypeScript
sources directly), and module resolution answers the shim for its path.
The package's other chunks are no longer reached. Calls through such a
require of a TypeScript module lower when the export never reads `this`
(require() answers the module's namespace).
package-shim-guard.test.ts compiles every registered shim module under
both type worlds and fails on any lowering diagnostic or fence inside the
shims, reached or not.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In minified output the class closes right where the initializer body
does (`…Counter=class{…}}})`), so the empty insertion that opens the next
run-once piece shared its position with the body's closing edit and was
dropped: the initializer then called a piece that did not exist. The
opener now rides on the class's own closing brace.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pass respells `exports.x` and `module.exports.x` through the
exported function's name. A parameter, local, nested function or catch
binding of that name between a respelled read and the top level would
capture it, so the pass now applies only when the file binds the name
once, at its top-level declaration.
`{ exports }` after the rebinding respelled to `{ SemVer }`, which
renames the property; the shorthand now becomes `{ exports: SemVer }`.
Lazy loaders that share a var statement each removed their declarators
on their own, and declarators of one statement removed separately leave
overlapping edits, of which applyEdits keeps the first. A later loader
in the statement (one using __toESM, say) stayed behind and the interop
pass refused the package. Every removed declarator of the pass (the
interop requires, the loaders and the unused export tables) now goes
into one set, removed once.
A call through a loader (`load().fn()`) respelled the callee as the
export's live read, which drops the namespace receiver. Loader calls now
take the rule namespace calls already use: the callee must be a
top-level function that never reads `this`; other calls keep the package
refused.