2266 Commits
Author SHA1 Message Date
Chris Tate fab1a29b33 Merge pull request #1192 from vercel-labs/ctate/v0-3-6-814afa
v0.3.6
v0.3.6
2026-10-17 13:50:36 -05:00
Chris Tate 3032248c11 v0.3.6 2026-10-17 13:47:41 -05:00
Chris Tate 8620fa533a Keep record views of class instances only where a copy is observable (#1188) 2026-10-17 12:30:36 -05:00
Chris Tate 79dd586b82 Treat switch, assert and identity-keyed constructors as identity observations
A class instance that flows into a structural type keeps a live view when
the program can tell a copy from it. switch statements match their cases
with ===, node:assert's named comparison exports and util's
isDeepStrictEqual compare their arguments, and new Set, Map, WeakSet,
WeakMap and WeakRef key their arguments by identity; these now count as
observations of the values' types.
2026-10-17 12:08:58 -05:00
Chris Tate 9c924818b8 Reserve heap growth for worker threads that allocate large blocks (#1191) 2026-10-17 11:47:44 -05:00
Chris Tate 5d02923bba Add readline's terminal mode, cursor helpers and emitKeypressEvents (#1178) 2026-10-17 11:45:52 -05:00
Chris Tate 82ee707ce5 Support request signals, destroy(err), servername, Expect events and maxHeaderSize in http (#1076) 2026-10-17 11:41:29 -05:00
Chris Tate 348b52c3d9 Keep while-loop counters bounded by a string's length in int32 (#1190) 2026-10-17 11:30:19 -05:00
Chris Tate 81142b99c5 Reserve heap growth for worker threads that allocate large blocks 2026-10-17 11:24:45 -05:00
Chris Tate f36767199e Check every TLS client certificate against the request's name
A TLS client set the name mbedTLS verifies only when it also sent that
name as SNI, so the certificate name check depended on SNI:

- https.request to an IP-literal host with servername: "" checked no name,
  so a certificate for another name was accepted.
- tls.connect with servername: "" sent SNI "localhost" and checked the
  certificate against localhost instead of the host.
- A verified request to an IP-literal host always failed the handshake,
  because mbedTLS refuses to verify without a name.

Each client now keeps the name it checks apart from the SNI name. The
checked name is a non-empty servername, otherwise the host; SNI carries
the same name except for an IP literal or servername: "", which send
none, as in Node. When no SNI goes out, mbedTLS is told to skip its own
name check and the verify callback checks the leaf instead: an IP literal
must be one of the certificate's IP SANs (Node's checkServerIdentity), and
any other name follows mbedTLS's DNS SAN and CN rules. A missing name
fails the check. A mismatch fails with ERR_TLS_CERT_ALTNAME_INVALID, or,
for tls.connect with rejectUnauthorized: false, reads as authorized false
with that authorizationError.

Corpus 5664 covers IP-literal hosts with and without servername: "",
tls.connect with servername: "", and that no SNI is sent.
2026-10-17 11:17:59 -05:00
Chris Tate 2840a92124 Keep while-loop counters bounded by a string's length in int32
A while loop that advances a counter through a string, such as

    while (pos < text.length) {
      const ch = text.charCodeAt(pos);
      pos++;
      if (ch === 13 && pos < text.length && text.charCodeAt(pos) === 10) pos++;
      ...
    }

kept the counter as a double, so every read converted it and every
charCodeAt took the general index path. The guarded while-counter facts
accepted only a literal or a binding the loop never writes as the
bound. They now also accept the length of a string binding the loop
never writes, which is at most 2^31 - 1 units.

The counter's maximum is also tracked along each path of the body
instead of summing every increment: an increment inside an `if` whose
condition compares the counter below such a bound starts from that
bound, and exits end their path. The second increment above therefore
keeps the counter within int32, and the counter gets int32 storage and
integer charCodeAt reads.
2026-10-17 11:09:48 -05:00
Chris Tate 23056949da Keep readline's terminal cursor on character boundaries, and merge main 2026-10-17 11:06:19 -05:00
Chris Tate 287119c449 Keep int32 class fields next to arithmetic on dynamic operands (#1187) 2026-10-17 10:42:20 -05:00
Chris Tate c5d5170e1a Keep record views of class instances only where a copy is observable
A structural type that receives class instances became a checked view
for all of its values, even when nothing could tell a copy from the
original. In tsc-ts this turned ResolvedProjectReference, the redirect
parameter of every module resolution, into a checked-dynamic value.

markIdentityFlows still finds every flow, then keeps a type marked only
when the program can observe the difference:

- values of the type are written through, compared by identity (also
  inside generic functions), used as Set or Map keys or array search
  needles, or passed to whole-object observers or any-value parameters;
- a field the type exposes is written on the source class after
  construction, or is an accessor;
- its values flow into another kept view or class-backed protocol.

Other types stay native records.
2026-10-17 10:34:06 -05:00
Chris Tate 72cf76921d Keep int32 class fields next to arithmetic on dynamic operands
The int32 field analysis keeps every field of a class whose instances
reach checked-dynamic code as a double when the program contains a
dynamic helper call that may store a property through a live view.
The dynamic operator helpers counted as such calls, so a single `+`,
`*`, `|`, `++` or unary `+` with a dynamic operand kept the fields of
every exposed class as doubles.

dyn.add, dyn.arithmetic, dyn.bitwise, dyn.toNumeric and
dyn.toNumberCoerce convert their operands with the ToPrimitive and
coercion helpers of dyn.compare, dyn.numberConstructor and
dyn.toStringCoerce, which are already on the read-only list, and return
a new number, string or bigint. dyn.increment steps the number or bigint
dyn.toNumeric returns. All six are now on the read-only list.
2026-10-17 10:22:00 -05:00
Chris Tate a904a552d7 Add the concurrency test option (#1180) 2026-10-17 09:37:02 -05:00
Chris Tate b429d4ca6f Merge pull request #1108 from vercel-labs/ctate/add-messagechannel-receivemessageonport-5d9f36
Add MessageChannel, receiveMessageOnPort and MessagePort transfer
2026-10-17 09:35:15 -05:00
Chris Tate 4b7ff49da8 Support createHash and createHmac values as streams (#1120) 2026-10-17 08:47:55 -05:00
Chris Tate defca89c93 Revert "Do not merge: CI check of the exec source changes on current main" (#1186) 2026-10-17 08:46:29 -05:00
Chris Tate 591994217c Run every subtest at once with concurrency: true
Node sets a test's concurrency to Infinity for `true` whenever the test
has a parent, and to os.availableParallelism() - 1 only for the root
test. Every test and suite a program declares has a parent, so `true`
now runs all of its subtests at once instead of capping them at the CPU
count minus one.

The concurrency fixture adds a suite of 64 concurrent tests that checks
all of them run at once.
2026-10-17 08:39:10 -05:00
Chris Tate 3fadf25b02 Merge main 2026-10-17 08:38:10 -05:00
Chris Tate 89e920ac99 Give a copy of a Hash whose stream closed a stream of its own
copy() of a Hash whose stream was destroyed before a digest, by
destroy() or an error, carried over the closed-stream flag, so the
copy's first stream use threw the SC2020 error. The copy now starts
without a stream, as in Node, and gets one on first stream use.
Corpus 5761 covers a copy written and ended after the original's
'close' event.
2026-10-17 08:24:36 -05:00
Chris Tate 4a53870702 Revert "Do not merge: CI check of the exec source changes on current main" (#1184) 2026-10-17 08:20:45 -05:00
Chris Tate 66125d783b Do not merge: CI check of the exec source changes on current main (#1184) 2026-10-17 05:08:21 -05:00
Chris Tate 4060f9135b Do not merge: CI check of the exec source changes on current main 2026-10-17 04:30:49 -05:00
Chris Tate 31ab50381f Give Object.groupBy results a null prototype (#1182) 2026-10-17 03:57:52 -05:00
Chris Tate 78ec9c1752 Fix or refuse Node 24 builtin forms that ran with wrong output (#1181) 2026-10-17 03:53:23 -05:00
Chris Tate 844aa87ae0 Give Object.groupBy results a null prototype
Object.groupBy now builds its result record with a null prototype,
stored as a flag on the record's overflow map. Inspection prints
"[Object: null prototype]", Object.getPrototypeOf answers null, `in`
finds no inherited names, and String(), template literals, toString(),
hasOwnProperty() and propertyIsEnumerable() throw Node's TypeErrors.
Spread copies and structuredClone keep Object.prototype, as in Node.
Index-signature records converted from an Object.create(null) value
keep the null prototype too.
2026-10-17 03:35:02 -05:00
Chris Tate d5034758fb Merge main 2026-10-17 03:33:06 -05:00
Chris Tate 53023c615d Merge main 2026-10-17 03:30:55 -05:00
Chris Tate b37c9491fe Fix or refuse Node 24 builtin forms that ran with wrong output
- JavaScript calls of Promise.withResolvers' resolve/reject through the
  record no longer fail with an internal compiler error.
- new RegExp with the d or v flag is refused (SC1120) for literal flags
  and throws a named error for computed flags, instead of SyntaxError.
- JavaScript .groups reads on matches whose regex is not traceable are
  refused like TypeScript's, instead of reading undefined.
- Iterator helpers on the string iterator are refused instead of
  failing at run time.
2026-10-17 03:29:53 -05:00
Chris Tate 05cb9bd609 Add the concurrency test option
node:test changes, matching Node 24.22:

- { concurrency } on describe, suite, test, it and t.test sets how many
  subtests run at once: a number, true for os.availableParallelism() - 1,
  or false for 1. Subtests inherit the parent's value.
- Above 1, each subtest runs on its own fiber. Subtests start in
  declaration order as slots free up, and t.test and test() calls inside a
  concurrent test start the subtest at once; t.test's promise settles when
  it finishes. The parent finishes after all of them.
- Results, diagnostics and failures are reported in declaration order.
- A concurrent subtest waits after its beforeEach hooks, and a concurrent
  suite before its children, for as many turns as Node's runner does, so
  sibling bodies interleave as they do there.
- A concurrency that is not an integer from 1 to 4294967295 throws Node's
  ERR_OUT_OF_RANGE RangeError when the test or suite is created.
- Nested test() calls and hooks inside a concurrent subtest attach to the
  subtest that runs on the calling fiber.

The concurrency fixture compares the reporter output, the order of the
bodies' side effects, and the range errors with Node, plain and with
SCRIPTC_SAN=1.
2026-10-17 03:28:15 -05:00
Chris Tate 0c4056fb0e Add the plan and timeout test options (#1165) 2026-10-17 03:22:28 -05:00
Chris Tate 26aeeb4c6f Lower util.promisify over the zlib callback one-shots (#1179) 2026-10-17 03:10:14 -05:00
Chris Tate 2896e94b20 Merge main 2026-10-17 02:59:21 -05:00
Chris Tate bf63243e64 Add fs.cpSync, fs.cp and fs/promises cp (#1166) 2026-10-17 02:55:40 -05:00
Chris Tate 7dcb1e6c4e Lower util.promisify over the zlib callback one-shots
promisify(zlib.deflate) and the other callback one-shots (inflate,
deflateRaw, inflateRaw, gzip, gunzip, unzip) over a named or namespace
import of node:zlib now lower. A call through the binding,
gzipAsync(data, options?), runs the callback form's work on the worker
pool and returns a promise that settles where the callback would run:
fulfilled with the Buffer, or rejected with the callback form's Error
(errno, code and message). An invalid option or input rejects the promise
instead of throwing, as the throw inside Node's promisify wrapper does.

The runtime adds scr_zlib_codec_promise and its checked-dynamic twin,
which share the callback forms' options ladder and worker-pool operation
and settle a pending promise in place of the callback. The fallback
declarations gain the matching util.promisify overload.
2026-10-17 02:46:20 -05:00
Chris Tate bc0636de7b Merge main 2026-10-17 02:35:31 -05:00
Chris Tate 0777ad7223 Merge main 2026-10-17 02:33:12 -05:00
Chris Tate 5c39821726 Merge main 2026-10-17 02:30:43 -05:00
Chris Tate 6fa1f8ce01 Lower util.promisify(stream.finished) and util.promisify(stream.pipeline) (#1177) 2026-10-17 02:19:12 -05:00
Chris Tate f61e5dc938 Add readline's terminal mode, cursor helpers and emitKeypressEvents 2026-10-17 02:10:27 -05:00
Chris Tate eca55f55ed Lower Array.fromAsync over arrays (#1176) 2026-10-17 02:09:54 -05:00
Chris Tate 3e83b4a5c7 Lower util.promisify(stream.finished) and util.promisify(stream.pipeline)
Node's util.promisify returns the custom promisified forms of
stream.finished and stream.pipeline, which are stream/promises' functions
of the same name. A const binding of promisify over the node:stream named
imports or namespace members now registers that stream/promises member
as the projection, so calls through the binding lower as the
stream/promises call and accept the stream arguments and options it
accepts.

The fallback declarations gain the matching util.promisify overloads.
pipeline's overload comes first: its (...streams: unknown[]) => unknown
type is assignable to every other target's parameter type.
2026-10-17 01:51:57 -05:00
Chris Tate f7ad1f96a4 Lower Array.fromAsync over arrays
The ESNext array lib is now part of the type world, and Array.fromAsync(source,
mapFn?) compiles when the source is an array of values or of promises. Each
call lowers to an async helper with the spec's loop: elements are awaited
through the async-from-sync iterator, with the same microtask turns as a
`for await` loop over an array, and a mapping function's result is awaited
before it is stored. A throwing or rejecting mapping function and a rejected
element close the iterator before the returned promise rejects, on the turn
Node rejects it. Async generator sources, a thisArg, and arrays that mix
values and promises are refused.
2026-10-17 01:47:40 -05:00
Chris Tate 4700ce5c21 Dispose Disposable-typed values and expose SuppressedError's error and suppressed (#1169) 2026-10-17 01:43:10 -05:00
Chris Tate c34dd7898b Keep disposal snapshots out of programs that never read them
The SuppressedError hook was installed by a load-time constructor in
scr_json.c, which kept the checked-dynamic conversion of caught values in
every program that linked that unit. scr_exc_suppress now records the
disposal and body snapshots in a side table in scr_exception.c, the error's
dyn view reads them when scr_json.c builds it, and the SuppressedError's
teardown releases them.
2026-10-16 23:09:58 -05:00
Chris Tate 6d4843ea99 Dispose Disposable-typed values and expose SuppressedError's error and suppressed
`using` and `await using` now accept object literals and other values typed
Disposable, AsyncDisposable or unknown. The declaration reads the value's
[Symbol.dispose] method ([Symbol.asyncDispose] first for `await using`) and
throws Node's TypeErrors for values that are not objects and for missing or
non-callable methods; null and undefined dispose nothing. Scope exit calls the
method with the value as `this`, and `await using` awaits the result of an
async method but not of the sync fallback. A class instance reached through
such a value throws an SC2020 error, because its dynamic view has no methods.

SuppressedError is its own error class: a failing disposal over a pending
error throws one whose `error` is the disposal error and whose `suppressed` is
the pending error, both non-enumerable as in Node. `new SuppressedError(error,
suppressed, message?)` and `instanceof SuppressedError` compile, and
`extends SuppressedError` is refused.
2026-10-16 22:36:18 -05:00
Chris Tate 1b34b4069a Revert "Lower child_process.exec's callback forms" (#1168) 2026-10-16 22:30:09 -05:00
Chris Tate d7a5568726 Revert "Lower child_process.exec's callback forms" (#1161) 2026-10-16 22:05:36 -05:00