zod 4 defines its error message with Object.defineProperty(inst, "message",
{ get, set }). The typed-ref commit of an Error subclass read the accessor
as a data value and threw "expected string". The commit and the slot sync
now leave the slot alone and mark the property absent; typed reads and
writes of name and message call the accessor when the program reflects on
an untyped or Error-shaped target.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The inspector throws the 'not supported yet' Error for checked-dynamic
values it does not render (a promise, a URL, a RegExp reached through
`unknown`). Its calls were not marked as throwing, so
`console.log("x", p)` printed "x " and the program went on; records and
arrays with such a field printed `{ a: }` and `[ ]`. insp.dyn and
insp.dynS are now may-throw library calls, so the Error propagates from
the call that inspects the value, before anything is written.
- A class reached through `unknown` prints `[class C]`,
`[class C extends B]` or `[class (anonymous)]` instead of
`[Function: C]`. Functions and classes reached as dynamic values print
their enumerable own properties (`[Function: f] { x: 1 }`,
`[class S extends A] { count: 3 }`), cycles through them as
`[Circular *N]`, and `[Function]` past the depth limit. Class objects
carry their base class's name. A compiled class's static fields are
module globals that the class object does not carry, so inspecting a
class that declares static fields through `unknown` throws the
'not supported yet' Error.
- util.format's %o renders a checked-dynamic argument with showHidden: an
array's `[length]` entry and an object's non-enumerable own properties,
string- and symbol-keyed, in brackets. An array with more than 100
output entries keeps the last one out of the grid, as Node's
groupArrayElements does. %o of a function, an Error, a typed array or a
native handle other than Map, Set and Date throws the
'not supported yet' Error.
ScrClassObj has two new fields, so RUNTIME_ABI_VERSION is now 27.
Writes and deletes of `code` through an error's checked-dynamic node (a
`catch (e: any)` binding, a reflective write) changed only the node, so
typed `err.code` reads kept the old value. The slot sync that already
covered name, message, stack and cause now covers code: a string takes
the slot, any other value or a delete clears it. A write, delete or
Object.assign through an Error subclass's typed view now syncs the
slots the subclass inherits from Error after the class commit, which
writes only declared fields.
`'code' in err` read the code slot, so an own `code` set to undefined
answered false. It now asks the runtime: the slot holds a string, or the
error has an own `code` property on its node.
Overrides whose parameters widen and whose results convert into the
inherited ones keep the inherited slot, which converts around the
override's own body; Error values take code writes, `in` tests and
Object.assign; and a few gaps behind generic wrappers close:
- Method overrides: each inherited argument converts into the
override's parameter (equal, wider, or a checked extraction from an
`any` slot) and the override's result converts into the inherited one
(narrower, a convertible union, a widening Promise payload, anything
convertible over an `any` base). The slot %C.m keeps the inherited
ABI and forwards to %C.m$own; TypeScript callers typed with the
subclass call $own directly when nothing overrides below. Async
overrides of sync and async bases, and JavaScript overrides of
builtin or TypeScript bases (zod's ZodError.toString) are included;
a toString slot converts its result with ToString.
- `err.code = value` on Error values in TypeScript: strings set the
runtime code slot (and the error's checked-dynamic node, if any); other
values clear it and keep the dynamic own property.
- `'key' in err` with any receiver: declared members answer true;
otherwise a class test over the declaring subclasses, a runtime lookup
of the error's own properties, or an Object.prototype member.
- `Object.assign(err, ...sources)` onto an Error target copies onto the
error, re-reads name, message, stack, cause and code into the typed
slots, and returns the error.
- `Error & { ... }` intersections whose extra members have checkable
types are Error values; those members read and write the error's own
properties, each read checked at its declared type.
- Catch bindings typed `any` read as checked-dynamic values (member
reads and writes, assertions); a multi-statement type guard with an
`unknown` parameter takes the caught value as an ordinary argument;
Error members of a binding narrowed to an Error-extending interface
read through the same value.
- Lambda parameters typed `any` by their default are checked-dynamic
slots; forwarded-only rest tuples with optional positions forward
their slots; `value as never` from an untyped value into a typed
parameter takes the checked extraction to the parameter's type.
- Lexical `this` stops at generic instance frames, so an instance no
longer sees the receiver of the caller that triggered it. The fence
for `this` captured through a plain function now points at the
`this` keyword.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`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.
fs.cpSync, fs.cp and fs/promises cp compile with every option:
dereference, errorOnExist, filter, force, mode, preserveTimestamps,
recursive and verbatimSymlinks. They follow Node's two implementations.
cpSync follows cp-sync.js over the fs binding: copy errors are plain
Errors with a code, std::error_code failures throw an ErrnoException,
and links inside a copied directory point at their canonical targets.
fs.cp and fs/promises cp follow cp.js, whose copy errors are
SystemErrors with code, info, errno, syscall and path.
Options and paths validate in Node's order with Node's errors. filter
runs for the top-level paths and each entry in Node's order; an async
filter must return a promise that is already settled, and a pending one
throws SC2020. The callback and promise forms complete the copy before
the callback runs or the promise settles. Windows builds throw SC2020.
The copy lives in the new scr_fs_cp.c. Directory walks keep their frames
on the heap, so deep trees also copy on a fiber's stack. File contents
copy with sendfile on Linux, and COPYFILE_FICLONE uses FICLONE
where it exists. ScrError gains the SystemError context and the
ErrnoException key order, with matching fields in the compiler's Error
layout. The stored fs.cp callback uses the same implementation.
Corpus 5567-5571 cover the sync, callback and promise forms, errors,
symlinks and 1500-level trees.
- http.request accepts `signal`: an abort, or a signal already aborted at
construction, destroys the request with an AbortError (code ABORT_ERR,
the signal's reason as cause). The abort listener is removed when the
request closes, and a value that is not an AbortSignal throws
ERR_INVALID_ARG_TYPE.
- req.destroy(err) reports err as the request's 'error'. Error listeners
receive the Error object through scr_event_error_payload, and
AbortError construction is shared through scr_error_abort.
- https.request accepts `servername`, sent as SNI and used for the
certificate name check.
- server.on('checkContinue' | 'checkExpectation') take Expect requests
instead of 'request', and the client emits 'continue' on a 100
response.
- createServer and http.request accept maxHeaderSize, validated with
Node's ERR_OUT_OF_RANGE messages.
Corpus 5564 and the extended 5559, 5561 and 5562 cover each API.
- server.close() closes idle keep-alive connections, and every open
connection closes after keepAliveTimeout + keepAliveTimeoutBuffer, so a
closed server whose clients keep their connections no longer stays up.
The Keep-Alive response header gives keepAliveTimeout in whole seconds
and is omitted when it is 0.
- A client request through an Agent with neither keepAlive nor a finite
maxSockets sends Connection: close.
- "socket hang up" and "aborted" errors carry code ECONNRESET, a client
head over the header limit fails with HPE_HEADER_OVERFLOW, and TLS
verification failures carry their openssl code.
- Outgoing header values keep the type they were set with: getHeader and
getHeaders answer numbers and arrays, appendHeader turns an existing
value into an array, and setHeader(name, string[]) writes one line per
element.
- Repeated incoming header fields join with ", " ("; " for cookie),
Node's single-value fields keep the first value, and set-cookie reads
as an array under @types/node, on server requests and client responses.
Fetch reads response headers line by line.
- Expect: 100-continue is answered with 100 Continue before 'request';
other expectations answer 417.
- HEAD requests and 1xx/204/304 responses send no body or length header,
and HTTP/1.0 requests without a set length get an unframed body and
Connection: close.
- The 16 KiB maxHeaderSize applies to the request target plus header
names and values (431 on the server).
- https.Agent TLS options are refused by name instead of being dropped,
and server.address() lowers for @types/node's
string | AddressInfo | null result.
Corpus 5555-5563 and the node-types http-headers-address fixture cover
each case.
scr_runtime.h now includes 48 module headers (scr_core.h, scr_rc.h,
scr_string.h, scr_dyn.h, scr_http.h, ...) in dependency order and holds no
declarations. Each module header holds whole sections of the former file,
includes the headers whose names it uses, and compiles on its own.
Declarations, macros, struct layouts and inline bodies are unchanged. Five
blocks move to join their module: the class-hierarchy vtable header (into
scr_error.h, after the string core), the node:timers/promises pair (into
scr_async.h), the JSON.parse entry points and the JSON output buffer (into
scr_json.h, after the dyn declarations) and the EventTarget block (after
scr_json.h). No moved block uses or declares a name that a block it moves
past uses or declares. The string methods keep their position after the
class objects in scr_string_methods.h, because their struct ScrDyn
parameters rely on the file-scope struct ScrDyn declaration there.
The runtime ABI scan reads scr_runtime.h with its module headers expanded
in include order. A runtime test compiles every module header on its own.
scripts/runtime-header-hunks.mjs retargets a diff of the single-file
header onto the module headers.