Commit Graph
4715 Commits
Author SHA1 Message Date
Chris Tate ce2e6624ff Check http.request options as Node does (#2606) 2026-10-30 07:16:38 -05:00
Chris Tate f1e4311881 Check the request path and method before a socket is opened
http.request runs the ClientRequest constructor's path and method checks
before it calls a createConnection option, takes a pooled socket or dials
a socketPath, as Node does. Before, the createConnection callback had
already run, and a socketPath server had seen a connection, when the
check threw ERR_UNESCAPED_CHARACTERS or ERR_INVALID_HTTP_TOKEN.
2026-10-30 06:16:58 -05:00
Chris Tate 10c1f11c8e Remove the close race in evolving-let-socket-optional-reads (#2617) 2026-10-30 06:04:27 -05:00
Chris Tate c4cd63108c pi: island references and crossings (typebox re-export, records into and out of islands) (#2593) 2026-10-30 05:54:09 -05:00
Chris Tate 1716899871 pi decisions: per-arm union spreads, checked-dynamic union merges, EventEmitter with any event name, optional reads of always-defined globals (#2596) 2026-10-30 05:53:18 -05:00
Chris Tate 953293e9e4 Close the socket corpus program's connection without a reset
evolving-let-socket-optional-reads.cjs ended the server's side of the
connection as soon as it was accepted and closed the server while the
client was still ending its own side. On macOS the client could then read
ECONNRESET, under Node and the compiled program alike, and each reports the
unhandled 'error' event in its own form.

The server now ends its side once the client's end arrives, and the server
closes when the client socket has closed. The program's output is unchanged.
2026-10-30 05:21:56 -05:00
Chris Tate 787098c087 Type a forward-captured island handle binding checked-dynamic
A closure declared before the island array binding it reads, such as
`const show = () => describe(local); const local = list();`, made the
lowering predeclare the binding's slot with its declared native array type.
The island lowering then retyped the binding checked-dynamic at its first
use, so the closure's capture and the binding disagreed (SC9001). The
predeclared slot of an island handle binding is now checked-dynamic from
the start, as the binding's own declaration lowers.

Test: 6897-node-island-declared-arrays gains arrows and a function
declaration that capture an island array declared after them.
2026-10-30 05:04:49 -05:00
Chris Tate 7ae0a0b322 Merge main 2026-10-30 05:03:19 -05:00
Chris Tate c052e4fc78 Keep records cast from unknown[] elements to dictionary types as live views (#2613) 2026-10-30 04:40:20 -05:00
Chris Tate 32d4ad0072 Compare generic declarations by identity across pinned signatures (#2565) 2026-10-30 04:34:26 -05:00
Chris Tate 895dd44bd7 Keep evaluation order and listener signatures with any-name emitters and union spreads
A distributed union spread evaluates its source before the dispatch, so
the properties written before the spread run after it. When the source
may have effects (a call that changes a value an earlier property reads),
those properties must now be constants; otherwise the literal takes the
checked-dynamic merge, which keeps Node's order.

A listener registered under an unrestricted event name was not recorded
in the program's event scan. A prefixed or suffixed emit with no matching
pattern listener was then lowered to `false` without dispatching, so the
any-name listener never ran. The scan now records such registrations, and
computed-name emits dispatch whenever one exists.

The same registrations can place a listener of any signature in the
bucket of a literal event. listeners(), rawListeners() and
events.getEventListeners() of a literal event returned that bucket typed
by the event's tuple, so calling an element passed the listener arguments
of other types. These calls are now refused once the program registers a
listener under an unrestricted name, as they already are for pattern
registrations.
2026-10-30 04:09:40 -05:00
Chris Tate 834bccdcd0 Keep records cast from unknown[] elements to dictionary types as live views 2026-10-30 04:01:39 -05:00
Chris Tate c889ba300b Read inherited keys in assert.throws with a shared expected object (#2610) 2026-10-30 03:40:57 -05:00
Chris Tate 2bc82f97dc Merge main 2026-10-30 03:40:32 -05:00
Chris Tate 87c0f7f3e2 Alias held process.env values to the live environment; keep a returned arrow's result in value slots (#2554) 2026-10-30 02:56:44 -05:00
Chris Tate 5a70967a0e Read inherited keys in assert.throws with a shared expected object
assert.throws and assert.rejects with an expected object held in a
variable compare each key with the thrown error's own or inherited value,
as Node's `key in actual` check does. An error's name is on its
prototype, so { name, message, code } failed against every error.
2026-10-30 02:55:44 -05:00
Chris Tate cf18a84a2d Call methods of class instances inline at untyped call sites (#2605) 2026-10-30 02:32:48 -05:00
Chris Tate 933d2c5e17 Merge main 2026-10-30 02:02:22 -05:00
Chris Tate 70e967862e Pass strings to an untyped 'data' listener on a child stream after setEncoding (#2602) 2026-10-30 01:54:58 -05:00
Chris Tate 0ad917929e Check http.request options as Node does 2026-10-30 01:51:42 -05:00
Chris Tate 14de5ce992 Call methods of class instances inline at untyped call sites
Untyped code called a method of a compiled class instance through the
site's dispatch helper: the callback that resolves own function-valued
properties, then the helper, which normalizes the capsule, tests each
class key and unwraps the instance before calling the method.

A call of a method dispatch helper from a dynamic-tier function now
tests the receiver's capsule key against the helper's plain class
branches (a direct call of one method with the helper's arguments) and
calls that branch's method with the capsule's instance. A branch
qualifies when every subclass the normalization can rebox its key to
calls the same method, and when the callback has no branch for the key
or those subclasses, so it answers undefined for the capsule. Every
other receiver calls the callback before the arguments and then the
helper, as before. Module initializers call this way only inside loops.
2026-10-30 01:40:20 -05:00
Chris Tate 422a02e225 Compile three Node test shapes: a top-level return before declarations, nullable strictEqual operands, and a branch-assigned let (#2590) 2026-10-30 01:04:18 -05:00
Chris Tate 13c5fc6748 pi wave 5: node process APIs, stdlib singletons, misc lowering (#2548) 2026-10-30 01:00:51 -05:00
Chris Tate 032b95d682 Pass strings to an untyped 'data' listener on a child stream after setEncoding
A JavaScript child stream (`child.stdout`) reached through the child's
checked-dynamic box registers its listeners through the stream's own
`on`. The declarations type an unannotated 'data' listener's chunk as a
Buffer, so the listener body checked the chunk as one and threw
`TypeError: expected Uint8Array at $, got string` once `setEncoding` made
the stream pass strings, as in Node's test-child-process-default-options.

When the file calls `setEncoding` on the same access path, an inline
'data' listener registered with on, once, addListener, prependListener
or prependOnceListener now binds its unannotated chunk as a
checked-dynamic value, which holds the string or the Buffer Node passes.
Streams without a `setEncoding` call keep the Buffer-typed chunk.
2026-10-30 00:48:07 -05:00
Chris Tate 015cc47ff2 Capture island locals in closures, load islands in import order, and narrow SC1010
Three fixes.

A function-local island handle read inside a closure, such as an island
array spread in an arrow (`() => [...local]`), resolved to the enclosing
function's binding without registering a capture, so the closure referred to
a local it does not have (SC9001). The handle is now read through
resolveLocal after its binding is retyped checked-dynamic, which captures
it like any other local.

A static island import or re-export loaded the island module in the
importer's %init prelude, after every native dependency the file imports.
A native module imported after the island package therefore evaluated
first, which reverses Node's dependency order. The load now runs in its
statement's place among the dependencies' %init calls. An island statement
the import order leaves out keeps the prelude's place.

Tests: 6897-node-island-declared-arrays gains closures that read, measure
and spread a function-local island array. node-island-import-order checks
evaluation order when an island re-export precedes a native import, and
when an island import sits between two native imports.

The SC1010 refusal of an import or re-export that resolves to type
declarations now applies only when the declaration has no runtime file
beside it (x.js for x.d.ts, x.mjs for x.d.mts, x.cjs for x.d.cts). A
relative import of `./node_modules/dependency/index.js` names a module
Node loads, and keeps its previous SC2013. The noUnusedLocals test reaches
its declaration file through a type-only re-export, since a runtime import
of a declaration-only file is now refused, as Node fails to load it.
2026-10-30 00:46:57 -05:00
Chris Tate 8e07b89461 Keep typed records unboxed in object-mode streams (#2601) 2026-10-30 00:41:54 -05:00
Chris Tate 8a5d2e14a2 Match Node's socket encodings, rejected writes and event loop liveness (#2599) 2026-10-30 00:09:13 -05:00
Chris Tate 3ddebf4c31 Read a captured handle let as the checked-dynamic value unless the checker types the read as the handle
Inside a nested function, a read of a captured handle `let` narrowed to
the handle whenever it was a member receiver. A function declared before
the first write reads the binding as `any`, so the narrowed handle reached
member reads typed `any`, and an optional read (`c?.remoteAddress`)
compiled to an unconditional SC1090 throw. js-dyn-server, whose listener
reads `server.listening` inside the callback passed to createServer, hit
the same path.

The read now narrows only when the checker types it as the handle. An
optional member read keeps the tagged value, so undefined short-circuits
and the handle arm narrows; other reads take the checked-dynamic value.
The lowerer's closureDynLets collection is a checked journal table.

Corpus evolving-let-socket-optional-reads.cjs reads and calls members
through `?.` on a captured socket before and after its first write.
2026-10-30 00:07:34 -05:00
Chris Tate c36c5d99ce Support non-Error 'error' payloads and checked-dynamic listeners in listeners() and meta events (#2586) 2026-10-30 00:04:33 -05:00
Chris Tate e36a442468 Throw for primitive Reflect.has targets, record isRaw, and match StringDecoder chunks
Reflect.has over a target held as any, unknown or a union answered a
presence result for a primitive. It now checks the target at run time
after both arguments are evaluated and throws Node's TypeError
("Reflect.has called on non-object"). The stdlib-fence snapshot no
longer lists Reflect.has, which lowers.

process.stdin.isRaw read a flag that only the dynamic setRawMode path
set, so a direct process.stdin.setRawMode(true) on a TTY left it false.
The flag now lives with scr_process_stdin_set_raw_mode, which every
path calls, readline's raw mode included.

StringDecoder.end(chunk) with a dynamic chunk that is undefined threw
ERR_INVALID_ARG_TYPE. It now only flushes, as Node's end skips the
write for undefined. A dynamic ArrayBuffer or SharedArrayBuffer chunk
decoded its contents; write and end now throw Node's
ERR_INVALID_ARG_TYPE for it, as it is not an ArrayBufferView.
2026-10-29 23:59:44 -05:00
Chris Tate bcda45ab7f Merge main 2026-10-29 23:58:03 -05:00
Chris Tate 6d496cd0cf Emit each loop once in the body a parameter version leaves (#2587) 2026-10-29 23:47:08 -05:00
Chris Tate 2353a04540 Keep typed records unboxed in object-mode streams 2026-10-29 23:46:36 -05:00
Chris Tate e6cea05db9 Ship ICU data without the categories no Intl form reads (#2598) 2026-10-29 23:29:51 -05:00
Chris Tate f67678b422 Pipe decoded socket strings and give lane jobs their own tmpdirs
After socket.setEncoding(enc), socket.pipe(destination) wrote the raw
bytes it read to the destination. It now writes the decoded strings, as
Node's pipe writes each string chunk: the destination receives their
UTF-8 bytes, and a partial character the decoder holds at EOF is written
before the destination ends.

tests/node-parallel/run.mjs runs each job's files with its own
TEST_THREAD_ID, as Node's test runner does, so test/common/tmpdir gives
concurrent files different directories instead of one shared .tmp.0.
2026-10-29 23:16:13 -05:00
Chris Tate 755b7d997b Read untyped listener arrays for tuple-less events, and route typed meta listeners beside checked-dynamic ones
- listeners() and rawListeners() in TypeScript of an event that no literal
  emit or listener gives a tuple (one registered only under computed names)
  read the untyped array, so each returned listener takes every argument a
  call passes. The array was checked into zero-argument functions, which
  called the listener without its arguments.
- A typed 'newListener' or 'removeListener' listener in a program with a
  checked-dynamic listener of the other meta event registers through the
  adapter. The meta lowering's check looked only at the listener's own
  event and refused it with SC2020.
2026-10-29 23:11:27 -05:00
Chris Tate 2d3361ddc1 pi wave 6: records and unions (#2597) 2026-10-29 22:55:28 -05:00
Chris Tate b47a1bd2cf Merge main 2026-10-29 22:54:58 -05:00
Chris Tate 2cc51b1427 Reject import() of unresolvable specifiers with Node's ERR_MODULE_NOT_FOUND (#2563) 2026-10-29 22:35:34 -05:00
Chris Tate 52111b86c6 Merge main 2026-10-29 22:27:36 -05:00
Chris Tate aa912769f7 Records and literals: multi-arm spreads, nullish-empty destructuring, nested literal layouts, aliased Pick views, checked method-field adapters (#2545) 2026-10-29 22:27:07 -05:00
Chris Tate 671e7d8bc5 Read and write record fields through literal-union keys of one field type (#2580) 2026-10-29 22:18:36 -05:00
Chris Tate d80b34d92d Match Node's socket encodings, rejected writes and event loop liveness 2026-10-29 22:18:16 -05:00
Chris Tate 902e8b4396 Ship ICU data without the categories no Intl form reads
The runtime pack shipped ICU's full icudt78l.dat (33.1 MB), and every
Intl executable gets a copy beside it. scr_intl.c answers its Intl forms
with ICU's break iterators, number and date formatters, calendars and
locale IDs, which never read the charset converters or the collation,
language, region, time zone name, unit, spell-out and transliterator
trees: 21.5 MB of the file.

- The pack build writes icudt78l.dat without those items
  (ICU_DATA_DROPPED in icu-inputs.mjs). filterIcuData rewrites the data
  file's table of contents to the remaining items, and the result is
  checked against a pinned sha256 and size (11,653,984 B).
- tests/harness/icu-data-filter.test.ts runs every @requires-icu corpus
  program under 47 locales and 3 time zones with the pack's data, with
  the full data and in Node, and fails when the outputs differ, so an
  Intl form that reads a dropped category fails there.
- intl-word-segmenter-dictionaries.ts segments Chinese, Japanese, Thai,
  Lao, Khmer and Burmese text, which ICU breaks with the dictionaries in
  brkitr/.
2026-10-29 22:17:47 -05:00
Chris Tate 33018e9d8b Compare a held generic function with scalars without the identity check
A generic declaration held at several signatures and read through
unknown was refused with SC1090 when compared with a string, number or
boolean. A function is unequal to such a value whichever instance it is,
so a comparison with an operand the checker types as a string, number,
boolean, bigint or symbol is not recorded for the identity check.
2026-10-29 21:47:27 -05:00
Chris Tate dd47d1b884 Merge main 2026-10-29 21:34:09 -05:00
Chris Tate ee621414c6 Resolve relative import() specifiers as URLs for the not-found rejection
probeNodeFileImportRefusal resolved a relative or absolute specifier as a
filesystem path, so a percent escape, a query or a fragment became part of
the path: "./a%20b.mjs" was looked up as "a%20b.mjs", and "./x.mjs?v=1" of
an existing file rejected. It now resolves the specifier as a URL against
the importer's file URL, as Node's ESM resolution does: the message names
the decoded path without the query or fragment, and the error's url is the
resolved URL with them. A path ending in "/", an encoded "/" or "\" and a
malformed escape, which Node refuses with other errors, keep the current
lowering.

An absolute specifier that CommonJS resolution finds (for example a file
named without its extension) gets Node's "Did you mean to import" hint.
Node resolves the hint of a relative specifier against the process's
working directory, so relative specifiers get none.

npm-file-import.test.ts compares the probe with Node's import() rejections
for 29 specifiers, and the dynamic-import-not-found corpus program covers
an escaped specifier with a query and fragment.
2026-10-29 21:26:24 -05:00
Chris Tate 98f5f22a7b Forward a configured libnode pack into test:sandbox lanes (#2589) 2026-10-29 21:25:58 -05:00
Chris Tate 6a05a2588f Merge main 2026-10-29 21:25:26 -05:00
Malte UblandClaude Opus 5.5 b767c38e9e Build union spread merges as the checked-dynamic object
`{ ...base, ...override }` over union-typed sources has one field set per
combination of source arms, so the merged value can carry fields from
several arms at once. When the per-arm distribution (slice 1a) cannot pick
one arm, the merge was refused with SC2003 and the site did not compile
(provider-composer's all-optional compat unions, mcp's aggregate config).

Build the object Node builds instead: the checked-dynamic literal, whose
keys are written in source order through the existing dynamic-object path
(field chunks, spreads by copy-data-properties, computed keys by
propertyKey), then converted into the declared union by the existing
checked conversion. Reflection, JSON and inspection see the keys Node
keeps; a value the union cannot hold throws the catchable TypeError at the
conversion rather than being reinterpreted as one arm. The fallback stays
narrow: union-typed spread source, auto tier, no capture buffer, and a
union the checked extraction can validate — everything that compiles
today lowers exactly as before, and `--tier=static-only` keeps the refusal
that tests/diagnostics/union-spread-merge.ts pins.

Corpus union-spread-merge.ts pins key order, last-writer-wins, nested
identity, the accepting typed consumer and the snapshot semantics
differentially; dyncheck's "union spread merge" covers the refusing
single-arm reinterpretation scriptc-only, since Node never checks a cast.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 19:23:44 -07:00