Commit Graph
561 Commits
Author SHA1 Message Date
Chris Tate 496cf577c7 Print iterators and iterator helper objects as Node does
console.log and util.inspect of a generator object, an iterator helper,
or an array or string iterator were refused, and printing a Map or Set
iterator threw "util.inspect of a dynamic Array Iterator is not
supported yet".

Compiled generators now carry the tag their object prints with:
"Generator" or "AsyncGenerator" by default, "Iterator Helper" for the
helpers, "Array Iterator" and "String Iterator" for the iterators the
compiler builds as generators. The spawn wrapper sets it through the new
scr_gen_set_tag; an adapter over another generator copies that
generator's tag (scr_gen_copy_tag), and generators that stand in for an
object of another class, such as a Set iterator adapted into an
iterator-typed slot, keep refusing to print. They print as Node's
Object [<tag>] {} at any depth, and Object.prototype.toString answers
[object <tag>].

The runtime's native iterators print as Node does: Object [Array
Iterator] {} and Object [String Iterator] {}, and for Map and Set
iterators the entries the next steps would produce, without advancing,
under [Set Iterator], [Set Entries], [Map Iterator] or [Map Entries],
as [Object [Set Iterator]] past the depth limit and {  } when none are
left. Object.prototype.toString answers [object Set Iterator] and the
other tags.
2026-10-19 01:10:39 -05:00
Chris Tate 96d3a0f5e4 Carry AsyncLocalStorage stores into timer, immediate, tick and microtask callbacks (#1365) 2026-10-19 01:01:02 -05:00
Chris Tate ce03d9a1db Lower string and typed array iterators, Iterator.from and helper return() (#1363) 2026-10-18 22:32:46 -05:00
Chris Tate 1f7f029a05 Carry AsyncLocalStorage stores into timer, immediate, tick and microtask callbacks
AsyncLocalStorage changes, matching Node 24.22:

- setTimeout, setInterval, setImmediate, process.nextTick and
  queueMicrotask callbacks run with the stores that were active when they
  were scheduled. Before, getStore() returned undefined in them.
- An interval keeps that context on every tick, and a refreshed timer
  keeps it too.
- enterWith inside one of these callbacks lasts until the callback
  returns. An enterWith in top-level code ends when the main module's body
  returns, and one inside a callback scheduled with no store active ends
  when that callback returns, so the store does not reach callbacks that
  were scheduled without it.

Programs that enter stores (store.run, exit, enterWith, or a
diagnostics_channel store binding) schedule through new *_als runtime
entry points, which wrap a callback scheduled with a store active in a
closure that installs the stores around the call. The emitter picks them
from the new asyncContext runtime feature; other programs keep the plain
entry points, so their scheduling is unchanged.

The limitations page notes that completion callbacks of callback-style
I/O APIs still run with no store.

5815-als-callback-context compares the stores each kind of callback sees
with Node, plain and with SCRIPTC_SAN=1.
2026-10-18 22:26:09 -05:00
Chris Tate 8121e32909 Derive the docs compatibility metadata at build time (#1360) 2026-10-18 22:20:36 -05:00
Chris Tate dc6a638e63 Lower string and typed array iterators, Iterator.from and helper return()
String iterators (s[Symbol.iterator]()) and typed array values(),
keys() and entries() compile as iterator objects: generators over the
code points and over the indices. The iterator helpers apply to them,
and for...of, spreads and Array.from consume them.

Iterator.from over an array, string, typed array, Set, Map or iterator
object answers the value's own iterator, as Node does for iterators
that already inherit from Iterator.prototype. Objects with their own
next method keep the Iterator fence. As the root of a helper chain,
Iterator.from(x) walks x itself: an array by index, a Set or Map live,
a generator directly.

h.return!() and h.return?.() compile when h is a helper call or a const
initialized with one: helper objects always have return(), so both
spellings are the call. Other iterator objects keep the fence, since the
array, string, Map and Set iterators have no return().

flatMap callbacks may return Sets (their values) and Maps (their
entries), walked live; a string result throws Node's TypeError and
closes the source.
2026-10-18 22:10:17 -05:00
Chris Tate b6e9e42308 Derive the docs compatibility metadata at build time
node-v24-compatibility-meta.json held two values derived from the public
compatibility artifact: a content hash for the data URL and its row count.
Every compatibility change rewrote the hash line, so concurrent changes
conflicted on it. next.config.mjs now computes both from the committed
artifact and passes them to the page through Next's build-time env, with
the same values. The generator no longer writes the file, and its local
check compares the public artifact itself.
2026-10-18 21:56:43 -05:00
Chris Tate 96f59b0f3f Free errors that crossed into checked-dynamic code and attach Node's error properties
The error identity cache (scr_dyn_from_error) keeps one checked-dynamic
node per error so a crossing error compares reference-equal and keeps the
properties set on it. It retained both sides for the whole process, so
every error that crossed a checked-dynamic boundary (a caught error read
through a cast, an error delivered to an untyped callback, a dns failure, a
warning) stayed alive until exit: 100,000 caught AssertionErrors read
through a cast grew the resident set by about 210 MiB.

The cache now sweeps itself. An entry whose error and node are referenced
only by the cache is unreachable (a node holds no reference to its error;
a registered subclass box holds one, and a spawnSync error's `error`
property refers to its own node), so a sweep releases both sides. Sweeps
run when the cache doubles past the entries the last sweep kept, so they
cost amortized constant time per crossing. The cache is per thread, and
objects shared across threads are published (immortal), so a sweep never
races another thread's reference. Live errors and live nodes keep their
entries: identity, rethrowing the same instance, isNativeError and kind
checks are unchanged.

scr_error_set_prop sets an own property on an error's node; dns, assert,
cipher, crypto, util.callbackify and warning errors use it for the
properties they attach. util.inspect of a runtime error renders through
its node: Node's own properties follow the bracket form in key order (a
system error's errno, code, syscall and path, and the extra properties a
throw site attached), and an AssertionError renders at depth 0 with long
string actual/expected values shortened, as its custom inspect does. A
node that contains itself renders as Node's <ref *1>/[Circular *1].

child_process errors carry Node's own properties:
- execSync and execFileSync: status, signal, output, pid, stdout and
  stderr (Buffers, or strings with an encoding option); a spawn failure
  or timeout adds errno, syscall, path, spawnargs and error first.
- execFile's callback and the promisified execFile/exec: code (the exit
  status, or null after a signal), killed, signal and cmd, with stdout and
  stderr when promisified; a spawn failure has errno, code, syscall, path,
  spawnargs and cmd.
The sync forms echo the captured stderr before a timeout throw too, as
Node's inheritStderr does. The cp.execSync libCall takes a flag for Buffer
outputs.

An error's own `stack` stays non-enumerable when assigned through its
node. A worker's record-source table is freed when the worker exits.
2026-10-18 21:53:14 -05:00
Chris Tate a7e3a9d0aa Cancel running subtests when their parent test times out (#1350) 2026-10-18 21:24:04 -05:00
Chris Tate 684f73d186 Add ThreadSlots, per-thread values on shared objects, to @scriptc/experimental (#1271) 2026-10-18 20:39:11 -05:00
Chris Tate 45d4105351 Compile maybe-promise unions, unknown[] rest parameters, and function-valued Maps and Sets (#1347) 2026-10-18 19:55:09 -05:00
Chris Tate 3de2fb56a0 Cancel running subtests when their parent test times out
node:test changes, matching Node 24.22:

- When a test's { timeout } elapses, its subtests that are still running
  are cancelled, the innermost first. Each reports its duration so far and
  fails with "test did not finish before its parent and was cancelled",
  before the parent's result.
- A cancelled subtest's body keeps running unobserved: when it settles it
  reports nothing and leaves the runner's state alone. Before, it reported
  its result after the run's summary.

The timeout-subtests fixture compares the results, counts and failing
section with Node, plain and with SCRIPTC_SAN=1.
2026-10-18 19:28:55 -05:00
Chris Tate 6fd2dedb15 Merge main 2026-10-18 19:04:59 -05:00
Chris Tate 6ab49f33b2 Stringify class instances with JSON.stringify (#1338) 2026-10-18 18:59:22 -05:00
Chris Tate 707ac5cf77 Carry Hash and Hmac values through unknown and any slots (#1257) 2026-10-18 18:38:52 -05:00
Chris Tate 8d94e5d993 Refuse RegExp and Promise class fields in JSON.stringify
A Date field reads through the class's checked-dynamic property view and
writes its toJSON text, so json-class-fields.ts compiled and matched Node
while the diagnostics test expected a refusal. The Date cases move to
6000-json-stringify-class-instances.ts.

RegExp and Promise fields read through the view but the runtime JSON
traversal has no writer for them: a RegExp field threw at run time where
Node writes {}, and a Promise field failed to link. Both are now refused
at compile time naming the field, also inside a field's arrays, records
and unions, and the fixture covers them.
2026-10-18 18:34:09 -05:00
Chris Tate cf11fbaaeb WebAssembly W0–W2: free handle tags, a cross-thread clone hook, the WebAssembly types and refusals, and a decoder that reports every module a program can load (#1339) 2026-10-18 18:12:42 -05:00
Malte UblandClaude Opus 5.5 f4a8511dca Compile maybe-promise unions, unknown[] rest parameters, and function-valued Maps and Sets
A union may now hold one promise beside data members (`T | Promise<T>`,
`Promise<R | void> | R | void`). The union tag is the `instanceof Promise`
test, and every operation keeps Node's microtask timing: `await` awaits the
promise arm or takes the one non-thenable turn, an async `return` (block
bodies, concise arrows, then handlers) adopts the promise arm after the
function's own finally blocks, `Promise.resolve` returns the promise arm as
is, and `typeof (u as any).then` reads the tag. Data arms that declare
`then`, two promise arms, and function arms beside a promise stay refused
with diagnostics. Inferred JavaScript literals that mix promises and values
keep the checked-dynamic array.

Rest parameters typed `unknown[]` or `any[]` in TypeScript use the
checked-dynamic variadic ABI that JavaScript rest parameters already use,
including spread forwarding (`(...args) => handler(...args)`).

Functions are now Map values, Map keys and Set elements, compared by
closure identity.

Two pre-existing bugs in the same area are fixed: an async lambda in a
`Promise<void> | void` slot got a `Promise<union>` ABI and threw when called
through the slot, and `return cb()` of such a callback in an async function
dropped the promise instead of adopting it.

The retired-engine 2251 and 2790 diagnostics fixtures (tagged templates
with `any[]` rest tags) now compile and match Node, so they move to the
corpus as 6248 and 6249.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:58:57 -07:00
Chris Tate b32f4e31f3 Merge main 2026-10-18 17:47:55 -05:00
Chris Tate 2f40bcb8d4 Lower iterator helpers on generators and Map and Set iterators
map, filter, take, drop, flatMap, toArray, forEach, reduce, some, every
and find compiled only as one chain from arr.values() or arr.entries()
that ended in a consuming method. Every other form was refused, and in
JavaScript the refusal was thrown at run time.

IteratorObject now maps to the generator type. A consuming method over
a chain of stages fuses into one loop over the source: a generator
driven like for-of, a Map or Set walked live, or an array by index. A
stage on its own builds a helper object: a generator over its source
that supports next(), for...of, spreads, Array.from and yield*. A
for...of whose head is a chain of stages without flatMap runs the
stages inside the loop.

The source is closed where Node closes it: when take reaches its count,
when some, every or find has its answer, when a callback or the loop
body throws, when the loop exits early, and before the RangeError of an
invalid take or drop budget. It is not closed when it is exhausted or
its own next() throws. return() on a helper object before its first
next() closes the source through a new runtime hook,
scr_gen_set_unstarted_return.

When closing an iterator throws while another error propagates, the
first error reaches the caller, as in Node. This applies to for...of
over a generator too, where the error from the close used to replace
the body's error. The try statement takes a dropFinallyErrors flag,
whose cleanup handler calls the new scr_exc_keep_first.

throw() lowers only on values typed as generators. Iterator helpers and
the built-in iterators have no throw method, and a value typed as an
iterator may hold one.

Callbacks in JavaScript bind their unannotated parameters to the
element type, and flatMap callbacks may return generators. A generator
whose record yields share checked storage with its return channel has
each value converted to the declared element type as it is pulled.
2026-10-18 17:46:31 -05:00
Chris Tate b119b4cf03 Stringify class instances through the runtime JSON traversal
JSON.stringify of a class instance, or of a record, array or union that
holds one, boxes the value and serializes it with the runtime JSON
traversal. The output matches Node: own enumerable properties in
creation order, properties added after construction, toJSON on the
instance or a base class, and no methods, accessors, #private fields or
static fields. A class whose declared field cannot be read through the
checked-dynamic property view, such as a Date field, is refused at
compile time with SC1090 naming the field.

Boxed Map and Set values now stringify as {}, as Node does, instead of
throwing.
2026-10-18 17:32:17 -05:00
Malte UblandClaude Opus 5.5 daf4ff6a2b Decode and validate the WebAssembly modules a program can load, and report them in coverage
`scriptc coverage` now lists every WebAssembly module whose bytes the
build can see, with its size, whether it validates, its function, import
and export counts, and the WebAssembly proposals it uses. Nothing is
translated yet; builds are unchanged.

- packages/compiler/src/wasm/binary.ts decodes and validates a module in
  plain TypeScript (the native compiler compiles it too) at the feature
  level scriptc means to translate: the MVP plus sign-extension,
  non-trapping float-to-int, bulk memory, multi-value, mutable globals,
  reference types and extended constant expressions. Where WebAssembly
  3.0 and V8 relaxed a 2.0 rule (global initializers reading defined
  globals, memory indices), it follows V8, the behavior oracle. Modules
  that use SIMD, threads, exception handling, tail calls, function
  references or GC, memory64 or multiple memories still decode, and are
  reported as needing that proposal instead of as invalid.
- packages/compiler/src/wasm/discover.ts finds modules from four sources:
  paths the compiler resolves from the source text (require.resolve,
  createRequire(...).resolve, import.meta.resolve,
  new URL(..., import.meta.url), __dirname joins), inline base64 and byte-array
  literals, the .wasm files of reached packages whose code mentions
  WebAssembly, and a `scriptc.wasm` list in package.json or the new
  coverage-only `--wasm <file>` flag. Modules are reported once, by
  SHA-256, with every source that found them; module files are input
  probes of the coverage verdict cache. The JSON envelope
  (--print=diagnostics) carries the same records under `wasmModules`.

Evidence: packages/compiler/test/wasm-spec.test.ts runs the decoder over
the 3,017 distinct binary modules of the WebAssembly 2.0 core test suite
(without SIMD) and the extended-const proposal's tests, generated by
gen-wasm-spec-cases.mjs with wabt's wast2json, and requires V8's verdict
for each; 8,000 seeded mutations of those modules must also agree with
V8, as must undici's inline llhttp modules (scalar valid, SIMD reported)
and es-module-lexer's. src/wasm/binary.test.ts checks each feature and
each error class against V8. On the survey's real modules the decoder
agrees with V8 for quickjs-wasi (637 KB, 36 ms) and photon (1.9 MB,
121 ms). tests/coverage-fixtures/wasm pins the four discovery sources.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:19:39 -07:00
Malte UblandClaude Opus 5.5 c7f3fa6564 Type-check programs that mention WebAssembly and refuse its API precisely
Packages whose declarations name WebAssembly types (quickjs-wasi,
photon-node, and therefore pi) failed scriptc's type check with SC0001
"Cannot find namespace 'WebAssembly'", because scriptc's ES-only type
world never loads lib.dom, so `scriptc coverage` could not analyze them.

- scriptc.d.ts declares lib.dom's WebAssembly namespace and BufferSource
  (admitting every ArrayBufferView, so Node's Buffer types fit).
- The type mapper gives every type declared in the namespace, and unions
  over one, the checked-dynamic representation, so declarations that only
  mention them compile.
- Every reached use of the API value refuses at its site with a named
  SC2020 diagnostic: `WebAssembly.<member>` reads and calls,
  `new WebAssembly.<Class>`, `instanceof WebAssembly.<Class>`, the bare
  namespace, `globalThis.WebAssembly`, and the global object viewed
  through a cast to a type with a WebAssembly member (pi codemode's
  spelling). TypeScript sources fail the build; JavaScript sources get a
  located runtime fence.
- `typeof WebAssembly` and `typeof WebAssembly.<member>` need no value and
  fold to Node's answers ("object", "function"), including inside
  `typeof x === "..."` tests, so feature probes in JavaScript glue behave
  as in Node. Before, a JavaScript probe answered "string" (the identity
  token) and silently took the non-wasm branch.
- The surface manifest projects the refusal (stdlib.webassembly), and the
  compatibility ledger's `WebAssembly` global row moves from
  not-implemented to refused; no other row changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:19:38 -07:00
Chris Tate 4c27ab8bd7 Compile classes that extend a generic class factory's result
`class Counter extends base(42) {}` with `function base<T>(initial: T) {
return class { value: T = initial; }; }` failed with SC1090 "extending
computed expressions": the call's result type is the factory's local class
instantiated with T = number, which only maps inside the factory's own
generic instance.

A call of a generic function whose result is one of its local classes (or
an instance of one) now keys the instance on its type bindings and maps the
result inside that instance, where the local class takes the instance's
name and bindings. The class value then works as a base (`extends
base(42)`), as a `const` binding, through `new`, and for method calls and
field reads on its instances. Each instantiation has its own class, so
`base(1) === base(1)` is false and `instanceof` tells them apart, as in
Node. Calls inside another generic instance bind through the declaration;
calls elsewhere also record the instantiated checker type, so later uses of
it name the same class.

Fixes #1241
Co-authored-by: BaoLei <50226652+blurname@users.noreply.github.com>
2026-10-18 17:17:22 -05:00
Chris Tate 356d968528 Merge main 2026-10-18 17:07:58 -05:00
Malte UblandClaude Opus 5.5 147013d7b9 Merge origin/main into opt/npm-small-fixes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:58:58 -07:00
Malte UblandClaude Opus 5.5 40c56b96ce Merge origin/main into opt/npm-small-fixes
Keep both the entry module's DEP0040 link warning and #1303's CommonJS
top-level this object in the module init header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:58:53 -07:00
Chris Tate 240f576805 Add execution tiers to the IR and a report-only tier analysis (#1315) 2026-10-18 14:08:12 -05:00
Malte UblandClaude Opus 5.5 8597a364f6 Merge origin/main into opt/npm-small-fixes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 11:45:47 -07:00
Chris Tate cc28e0f6c9 Merge main 2026-10-18 12:20:01 -05:00
Chris Tate 640abd5294 Merge main 2026-10-18 12:15:43 -05:00
Chris Tate 414df19270 Merge main 2026-10-18 11:24:11 -05:00
Malte UblandClaude Opus 5.5 577b0135a9 Add execution tiers to the IR and a report-only tier analysis
Stage S2 of the two-tier design. IrFunction gains a tier (absent means
static); lowering assigns it from function bodies, placing functions that
use the dyn vocabulary in the dynamic tier, and the validator enforces
invariant I1: a static function performs no dynamic operation, only
boundary operations (dynFrom, dynCheck, kind tests) and moves. Serialized
IR keeps the field out in this stage and loaders derive it again, so every
IR, LLVM and executable artifact is unchanged.

Every committed refusal and runtime fence becomes a recorded demotion
request attributed to the binding, field, function instance, class or
module the dynamic tier would take over, while the compiler still refuses
and fences as before. `scriptc coverage` reports dynamic-tier statements
(statementsDynamic), `--explain-dynamic[=<name>]` prints the demotion
chains, and `--tier=auto|static-only` is wired through build, run and
coverage for stage S6; both modes compile identically today.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:12:27 -07:00
Chris Tate 2de6f617e6 Merge main 2026-10-18 11:08:54 -05:00
Chris Tate d3dba4c07f Run module-level before and after hooks
node:test changes, matching Node 24.22:

- before and after hooks outside any suite run, with the root's context,
  whose name is "<root>". They did not run before.
- A module-level before hook runs at its before() call, as Node runs the
  hooks of its already-started root at once: its synchronous part runs
  before the code after the call, and the first top-level test waits for
  an async hook's promise. Every module-level before hook runs, and an
  async hook's rejection is not reported as an unhandled rejection.
- Module-level after hooks run after the last top-level test or suite.
- A throwing root before hook fails each top-level test with the first
  failure; the test's body and beforeEach hooks do not run, and its
  afterEach hooks do. Each top-level suite fails with the error, and the
  tests and suites in it are cancelled before they start: they report
  without a duration and fail with "test did not finish before its parent
  and was cancelled", listed in the failing section in completion order.
- A throwing root after hook reports the entry file as a failed result,
  after the top-level results, and lists it in the failing section at the
  hook's location. It is not counted, and the exit code is unchanged.
- Hooks record the "file:line:col" of their call for that location.

The root-hooks, root-before-throws and root-after-throws fixtures compare
the reporter output, the order of the hooks against the module code, and
the exit codes with Node, plain and with SCRIPTC_SAN=1.
2026-10-18 10:59:27 -05:00
Chris Tate 307387c8ea Answer undefined for missing Record and index-signature keys (#1236) 2026-10-18 10:58:02 -05:00
Chris Tate 18c50b55db Pass the SuiteContext and hook contexts (#1302) 2026-10-18 10:54:53 -05:00
Chris Tate aee210e184 Read named groups through optional chains and keep their null prototype (#1299) 2026-10-18 10:34:42 -05:00
Chris Tate 9638f0b4ef Close three TypeScript gaps: Error.cause redeclaration, match-result object patterns, union-stored string indexing (#1298) 2026-10-18 10:32:50 -05:00
Chris Tate c73f363906 Pass the SuiteContext and hook contexts
node:test changes, matching Node 24.22:

- A describe or suite body may take its SuiteContext, with name,
  fullName, filePath, attempt, passed and diagnostic. Its passed is false
  until the suite has finished, after its after hooks.
- before and after hooks may take the suite's context, and beforeEach
  and afterEach hooks the test's. A test's t.before, t.after,
  t.beforeEach and t.afterEach hooks may take the test's context.
- SuiteContext maps to the runner node that TestContext maps to, so the
  TestContext members that SuiteContext shares lower the same way.

The suite-context fixture compares the reporter output and the order of
the bodies and hooks with Node, plain and with SCRIPTC_SAN=1.
2026-10-18 10:31:11 -05:00
Chris Tate cd61e0fb46 Add the TestContext filePath, attempt, passed, workerId and tags values (#1297) 2026-10-18 10:21:30 -05:00
Chris Tate 675388805c Read named groups through optional chains and keep their null prototype
`m?.groups?.year` compiles on exec and match results, including a null
match, where the read was refused before. The matchAll row callbacks also
cover findIndex and findLastIndex.

Reshaping an index-signature record into another index-signature type
keeps the source's null prototype, since the two are one object in
JavaScript. A match's groups read through `m?.groups`, or assigned to a
declared type such as `{ [k: string]: string } | undefined`, now prints
"[Object: null prototype]" as in Node, where it printed a plain object.
2026-10-18 10:18:12 -05:00
Chris Tate acc920ab85 Add the TestContext filePath, attempt, passed, workerId and tags values
node:test changes, matching Node 24.22:

- t.filePath is the entry file's path, as Node's entryFile, fixed when the
  program is compiled.
- t.attempt is 0, since a test runs once.
- t.passed is false while the body runs and the test's outcome once the
  body ends, so afterEach and t.after hooks see it, as Node's pass() and
  fail() set it before them.
- t.workerId is Number(process.env.NODE_TEST_WORKER_ID) || undefined.
- t.tags is [], since the tags option has no lowering.
- A test's t.before, t.after, t.beforeEach and t.afterEach hooks are
  released when the test ends. Hooks whose closures capture the test's
  context no longer keep it alive in a cycle, which the SCRIPTC_SAN=1
  audit reported as live closures at exit.

The ctx-members fixture compares the reporter output with Node, plain and
with SCRIPTC_SAN=1.
2026-10-18 09:57:26 -05:00
Chris Tate 3de60e4332 Merge main 2026-10-18 09:55:59 -05:00
Chris Tate 8dc0071e92 Merge main 2026-10-18 09:37:20 -05:00
Chris Tate 0bdcccef80 Lower child_process.exec's callback forms (#1290) 2026-10-18 09:26:38 -05:00
Chris Tate 90c697f770 Merge main 2026-10-18 09:19:32 -05:00
Chris Tate 0d4e6a0cc1 Compile classic CommonJS packages: late requires, JSON requires, nested builtin destructuring, settled require cycles (#1287) 2026-10-18 09:16:05 -05:00
Chris Tate 1a007e9a2b Lower util.promisify over the crypto callback functions and dns lookups
promisify(crypto.randomBytes), pbkdf2, scrypt, hkdf, randomFill, randomInt
and generateKey, and promisify(dns.lookup) and dns.lookupService, over a
named or namespace import, now lower. A call through the binding runs the
callback form with a runtime callback that settles the returned promise
where the callback would run: fulfilled with the callback's value, or
rejected with its error. An argument error the callback form throws rejects
the promise instead, as the throw inside Node's promisify wrapper does.

The crypto promise's value follows the declared result type: a Buffer,
the filled view (the same object), a number, or a checked-dynamic value
where @types/node declares unknown (scrypt, randomFill, randomInt) and for
ArrayBuffer and KeyObject results. The runtime adds a callback closure that
owns the promise and settle adapters for each representation, next to the
existing callback forms in scr_crypto_async.c.

dns.lookup's promise fulfills with { address, family }, or the address
array with options.all, and lookupService's with { hostname, service }, as
Node's custom promisified forms do. The runtime builds the checked-dynamic
result; a declared record type is reached through an async helper that
awaits it and checks the value. The type mapper maps the promisified
lookupService result literal like the promisified exec result.

The fallback declarations gain the matching util.promisify overloads.
2026-10-18 09:09:03 -05:00
Malte UblandClaude Opus 5.5 22cbd7623b Compile TypeScript redeclarations of Error.cause
A TypeScript subclass of Error that declared `cause` was refused with SC1090,
although the same field in a JavaScript class already compiled. The
declaration now lowers like the JavaScript field: it defines the error's own
enumerable `cause` after super() returns, undefined without an initializer,
replacing a cause passed through the options exactly as Node does. `declare`
and `abstract` declarations are erased and define nothing.

Reads of `.cause` on Error-rooted classes are always the checked-dynamic
property, so only declarations typed `unknown` or `object` are accepted.
Other declared types, parameter properties and accessors named `cause` keep
named refusals. This unblocks the source of @openfeature/core, whose
abstract error base declares `cause?: unknown`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 07:07:37 -07:00