Commit Graph
782 Commits
Author SHA1 Message Date
Chris Tate 8d17fe4e57 Support crypto.getCipherInfo, AES-CCM and AES key wrap (#1873) 2026-10-23 05:36:21 -05:00
Chris Tate 19e0fe8bcd Merge main and read cipher options through getters and prototypes 2026-10-23 05:33:14 -05:00
Chris Tate 63fd3cecb7 Build @hono/node-server 2.x into scriptc (#1886) 2026-10-23 05:25:14 -05:00
Chris Tate 952e01d2ed Stream request bodies in the Node server adapter 2026-10-23 05:03:12 -05:00
Chris Tate 8bb9ee8c57 Check generateKeyPair arguments and RSA parameters as Node does 2026-10-23 03:38:18 -05:00
Chris Tate fc31dd5060 Enumerate each record in the key order its object literal wrote 2026-10-22 22:56:12 -05:00
Chris Tate f030e5ad44 Check with tsc-ts 0.0.8 on every platform
tsc-ts 0.0.8's API server serves on the named pipe the Node host
connects over on Windows (tsc-ts --api --pipe), and the native compiler
already starts it over its standard streams there, so tsc-ts is the
default checker on every platform. SCRIPTC_CHECKER=tsgo still selects
tsgo.

The compiler depends on tsc-ts 0.0.8, which the release-age exclusions
admit with its platform packages and @scriptc/threads 0.4.2.

The Windows CLI smoke test runs with both checkers, and
verify-native-cli.mjs --run checks that a distribution's tsc-ts runs on
its platform (tsc-ts.exe on Windows).
2026-10-22 22:36:11 -05:00
Malte UblandClaude Opus 5.5 9bf76af974 Build @hono/node-server 2.x into scriptc
@hono/node-server replaces the global Request and Response with
lightweight objects and reparents prototypes, tricks that only pay off
under V8 and that native compilation cannot follow. scriptc now compiles
the package from a built-in implementation (shims/hono-node-server/v2.ts)
over node:http and the native Request/Response/Headers: serve,
createAdaptorServer, getRequestListener and RequestError.

Responses are written the way the package writes them, byte for byte on
the wire: the general path (header record in Headers order, Set-Cookie
gathered, default content type, content length for bodies that finish
within the first reads, streamed bodies, error responses) and the
package's cached-response path for a Response that is still exactly its
constructor arguments. The runtime tracks that lightweight state on each
constructed Response (header form, raw plain-record headers, body length;
cleared by body and metadata reads, turned into Headers form by a headers
read) and exposes it to the shim through scriptcResponseLight.

The declaration entry of a shimmed package may now be a .d.mts/.d.cts
file. The npm case hono-node-server drives buffered, streamed, cookie,
header-form, HEAD, error-handler and URL cases against the real package
under Node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 19:32:49 -07:00
Chris Tate 1660531c7b Support FinalizationRegistry with bookkeeping and no cleanup callbacks (#1854) 2026-10-22 20:03:31 -05:00
Chris Tate 636ffa21be Merge main 2026-10-22 19:59:51 -05:00
Chris Tate 3024e57eeb Revert "Enumerate each record in the key order its object literal wrote (#1802)" 2026-10-22 19:02:41 -05:00
Chris Tate 53c43dd140 Merge main 2026-10-22 18:44:10 -05:00
Chris Tate f132cf5f39 Compile Iterable<T> values (#1875) 2026-10-22 18:30:45 -05:00
Chris Tate 81891d55f7 Fix boxed record field hooks and moving key writes, and merge main 2026-10-22 18:19:14 -05:00
Malte UblandClaude Opus 5.5 089ec6739b Compile Iterable<T> values
Iterable<T> maps to a union of the sources that keep their own
representation (T[], Set<T>, Map<K, V> for [K, V] entries, string) and a
generator over T that holds every iterator. An array stays the same
multi-pass object; a generator stays single-pass. for-of gives the array
arm its own array loop, so iterating an Iterable that holds an array runs
at array speed. Spread, Array.from, the Set/Map constructors and
Object.fromEntries consume it; optional Iterables narrow into it;
generator functions may declare an Iterable return. Class iterables and
typed arrays are refused at the conversion with a named diagnostic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:00:41 -07:00
Chris Tate 5372c69459 Pass { signal } to stream operator callbacks and Duplex.from bodies 2026-10-22 17:50:29 -05:00
Chris Tate 98712c364a Merge main 2026-10-22 17:49:58 -05:00
Chris Tate 846552b0e2 Support crypto.getCipherInfo, AES-CCM and the AES key wrap ciphers
getCipherInfo(nameOrNid[, options]) runs in a new runtime unit,
scr_cipher_info.c, over a table that scripts/gen-cipher-info.mjs generates
from Node 24.22's getCipherInfo. The table covers every cipher OpenSSL
names, including the ones without a native implementation. Lookups match
names case-insensitively and accept OpenSSL's nids. The unit applies
cipher.js's argument checks and GetCipherInfo's keyLength and ivLength
rules, and returns the info object in Node's key order. CipherInfo maps
to a checked-dynamic value, so TypeScript and JavaScript code receive
Node's object. Reading the nid Node leaves out for the SIV and SM4 GCM,
CCM and XTS ciphers, where the declared type gives number, throws a
TypeError.

createCipheriv and createDecipheriv support aes-128/192/256-ccm (and the
id-aesN-ccm names) over OpenSSL's ccm128 steps. They follow Node's tag and
IV lengths, the plaintextLength requirement, the message-length caps and
the one-update rule, and report a failed authentication from final().

They also support the RFC 3394 and RFC 5649 key wrap ciphers (aes-N-wrap,
aesN-wrap, id-aesN-wrap, the -pad forms and the -inv ciphers) over
wrap128.c's routines, with Node's lengths, integrity failures and
one-update rule.

The new names follow the existing ones in the cipher table, so the lookup
cost of the existing ciphers is unchanged.
2026-10-22 17:18:54 -05:00
Chris Tate 57c10fc767 Build Sets, Maps and weak collections from checked-dynamic iterables (#1856) 2026-10-22 16:53:29 -05:00
Chris Tate d7717ff1e9 Type empty array literals by their slots, and report package diagnostics accurately (#1799) 2026-10-22 16:40:40 -05:00
Chris Tate ee865bcb0d Support the legacy createCipheriv modes and crypto.checkPrime/generatePrime (#1832) 2026-10-22 16:32:12 -05:00
Chris Tate 5c29845909 Support Readable.fromWeb over web ReadableStreams 2026-10-22 15:23:28 -05:00
Chris Tate 18c2900937 Read web ReadableStreams as async iterable stream sources 2026-10-22 14:20:01 -05:00
Chris Tate d83c676705 Store compact values of string-key maps in 4-byte slots on Linux 2026-10-22 14:08:00 -05:00
Chris Tate d8d75dfa18 Support Duplex.from over streams, pairs, promises and async functions 2026-10-22 13:32:25 -05:00
Chris Tate 8f08cbbec5 Build Sets, Maps and weak collections from checked-dynamic iterables
`new Set(x)`, `new Map(x)`, `new WeakSet(x)`, `new WeakMap(x)` and
`Object.fromEntries(x)` now accept any checked-dynamic iterable, as in
Node: a native iterator (`map.keys()`, `set.values()`), a compiled
generator, an object with a Symbol.iterator method, or a Set. Before, a
Set seed that was not an array or a string threw "is not iterable", and a
Map, weak collection or Object.fromEntries seed that was a generator or a
collection other than a Map refused with SC2020.

The runtime drains native iterators, packs arrays, strings and
collections, and steps every other iterable through its iterator
(scr_dyn_seed_values). A program that builds these from an untyped seed
registers the compiled generators' next and return methods with the
runtime, as destructuring rest already did, so a generator seed can be
stepped.

The seed's values are all taken before the first one is added, so when an
entry of a Map seed is not an object the TypeError comes after the
iterable is exhausted; Node closes the iterator at that entry. The
limitations page says so.
2026-10-22 13:09:01 -05:00
Chris Tate 83d426f9be Support FinalizationRegistry with bookkeeping and no cleanup callbacks
wasm-bindgen output constructs a FinalizationRegistry per exported class
when its module loads, so every wasm-bindgen package stopped at load with
SC2020. `new FinalizationRegistry(cleanup)` now creates a native registry:
- register(target, held, token) and unregister(token) check their
  arguments and answer as Node does, with Node's TypeError messages;
- the unregister tokens are keys of an internal weak map, so the registry
  keeps no token alive;
- the cleanup callback never runs, which ECMAScript allows, so the
  registry keeps neither the callback nor the held values.
The global answers typeof "function", instanceof, Object.prototype.toString
and util.inspect as Node does. Symbol targets and tokens are refused with
SC2020.

`"encodeInto" in encoder` on a TextEncoder, which wasm-bindgen's loaders
test at load, answered false because the encoder is a brand record without
fields for its methods. `in` over such a builtin brand record now answers
from the members the standard library declares, plus Object.prototype's.

The limitations page says that memory a package frees only from a cleanup
callback, such as the WebAssembly memory behind wasm-bindgen wrappers the
program drops without free(), is not reclaimed until the program exits.

Fixes #1678.
2026-10-22 12:14:23 -05:00
Chris Tate a6a1eb2b95 Support Duplex.from, compose stages, readable.compose and pipeline function destinations 2026-10-22 10:57:53 -05:00
Chris Tate 4d7de03054 Store arrays of ProgramLifetime instances in 4-byte slots on Linux 2026-10-22 10:46:16 -05:00
Chris Tate 9aa3b15174 Support construct, writev, decodeStrings and defaultEncoding on streams 2026-10-22 09:44:27 -05:00
Chris Tate 20f4d1ff12 Support the legacy createCipheriv modes and crypto.checkPrime/generatePrime 2026-10-22 09:08:35 -05:00
Chris Tate e6115f87e8 Merge main 2026-10-22 07:22:32 -05:00
Chris Tate 714dbfe2bb Merge main 2026-10-22 06:59:47 -05:00
Chris Tate 75bcf9dfa7 Merge main 2026-10-22 06:55:45 -05:00
Chris Tate 41ef12e463 Construct builtins through stored constructors and fix function-local class names and dispatch 2026-10-22 06:44:59 -05:00
Chris Tate 3edec984d5 Compile JSX with the classic runtime (#1786) 2026-10-22 06:29:40 -05:00
Chris Tate fb544eb98e Validate DiffieHellman peer keys like Node: oversized keys and RFC 5114 subgroups
Keys, primes and generators drop their leading zero bytes before import,
as BN_bin2bn does, and a value still past mbedTLS's 80000-byte limit
throws Node's error or a coded SC2020 error instead of aborting the
process: computeSecret rejects a key longer than the prime as too large,
a long generator is a bad generator, and setPublicKey/setPrivateKey
refuse with SC2020.

Under the RFC 5114 groups, computeSecret rejects a peer key with
key^q mod p != 1 with ERR_CRYPTO_INVALID_KEYTYPE, as OpenSSL's public
key check does, closing small-subgroup confinement of the private key.
The safe-prime groups keep the range check only, as in Node.

Exponents past 8192 bits are split into digits bit by bit, so
generateKeys with a long private key takes linear time.
2026-10-22 06:07:58 -05:00
Chris Tate df6de14c94 Support new tls.TLSSocket and tls.connect({ socket }) (#1791) 2026-10-22 06:01:47 -05:00
Chris Tate daac2a4a15 Support objectMode streams with any value as a chunk (#1785) 2026-10-22 05:55:45 -05:00
Chris Tate cda76716a0 Enumerate each record in the key order its object literal wrote
Record shapes are interned by field set, and their key order was one table
per shape. When object literals of one shape wrote the keys in different
orders (`{ id, name }` and `{ name, id }`), every value enumerated in the
first type's order, and anonymous literal types got no warning.

- The key-order witness pass gives such a shape its orders
  (IrRecordShape.orderVariants, the declared table first) and a hidden
  number field, %order, that each value sets to the index of its order:
  a literal of explicit keys stores its own order, a copy from a shape
  with one order stores that order, and spreads, rest bindings and
  copies of values with orders read their source's index. Allocation
  zeroes the field, so checked conversions and runtime producers keep the
  table as before, and clones copy it.
- A write of an optional key a value lacks moves the key to the end of
  that value's order, as in Node: the orders include each order such
  writes reach, and the write site updates the index first, evaluating a
  receiver other than a binding once.
- Object.keys, Object.values, Object.entries, for-in, util.inspect,
  JSON.stringify, the record-to-dynamic conversion (structuredClone,
  postMessage, untyped views) and live record views branch on the index.
  The dynamic conversion and checked conversions leave the field out.
- A shape keeps its table, and SC6002 still reports its literals, when a
  construction's order cannot be traced: a spread of a value of unknown
  order, a clone that adds an optional key, a copy whose source is not a
  binding, or more than 16 orders.

Corpus 7122 to 7128 check each surface against Node.
2026-10-22 05:51:17 -05:00
Chris Tate 01a467060b Support WebCrypto wrapKey and unwrapKey
crypto.subtle.wrapKey exports a secret key (raw, raw-secret or jwk) and
encrypts it, and unwrapKey decrypts and imports one, as Node's
webcrypto.js does: the wrap algorithm normalizes through the wrapKey
registry (AES-KW) and then the encrypt registry (AES-GCM, AES-CBC and
AES-CTR with their dictionaries), the wrapping key's algorithm and
usages are checked, and a JWK payload is its JSON text, padded with
spaces to a multiple of 8 bytes for AES-KW. A wrapped JWK that is not
UTF-8 JSON with a kty member rejects with Node's DataError.

AES-KW is RFC 3394 key wrap over the vendored mbedTLS block cipher, in
scr_webcrypto_aes.c beside the other AES modes: wrapping takes a
multiple of 8 bytes and at least 16, unwrapping a multiple of 8 and at
least 24, and the integrity check value is compared in constant time.
The cipher's failures, and unwrapKey's import errors, reject on a later
loop turn as Node's job promises do.

This completes jose's symmetric key management: 7132 runs the real
jose 6.2.12 PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW,
A128KW and A256KW paths (EncryptJWT and jwtDecrypt) against Node.
getPublicKey replaces wrapKey as the refused method in the fenced
fixture and the JavaScript refusal test.
2026-10-22 05:48:30 -05:00
Chris Tate aeb429c066 Type empty array literals by their slots, and report package diagnostics accurately
- An empty `[]` whose own type is `never[]` (a `return []` in an async
  function, whose contextual type is `T[] | PromiseLike<T[]>`, or an
  object literal's `files: []` field) builds as the unit-only element
  array, like the empty tuple, so typed array slots receive it through
  the checked empty-array lift. It was `number[]`, so `string[]` slots
  refused it with SC1090 or SC2002.
- The refusal "instanceof against this native constructor" names its
  site, and coverage lists the sites whose right-hand side is `Object` or
  `Function`, which always reach it. An arrow function or method on the
  right of an untyped `instanceof` throws Node's "Function has non-object
  prototype" TypeError for an object.
- Coverage lists the diagnostics of a function whose signature did not
  compile. A use of a parameter whose type did not compile reports SC2004.
- Coverage lists deferred fences in a module's top-level code under "runs
  at startup", and the diagnostics envelope marks them `startup: true`.
2026-10-22 05:45:38 -05:00
Chris Tate 1360830149 Support new tls.TLSSocket and tls.connect({ socket }) 2026-10-22 04:52:44 -05:00
Chris Tate c47a0e90ae Compile JSX with the classic runtime
Fixes #889
Co-authored-by: papack <33105506+papack@users.noreply.github.com>
2026-10-22 04:27:35 -05:00
Chris Tate 67247dd26b Support objectMode streams with any value as a chunk
Readable, Writable, Duplex, Transform and PassThrough take the
objectMode option, and a Duplex, Transform or PassThrough the
readableObjectMode and writableObjectMode options, as true or false
literals in an options object literal or in an options value. An
object-mode side holds any value that converts to a checked-dynamic
value: push, unshift, write, end, pipe, pipeline, for await, 'data'
listeners, Readable.from and the transform callback carry values in
TypeScript and JavaScript, and constructor-function streams and options
held in variables do the same. The object-mode high-water mark counts
values and defaults to 16. A readable buffer that mixes strings, Buffers
and other values holds them all, and push(undefined) and other values on
a byte-mode stream report ERR_INVALID_ARG_TYPE through 'error', as in
Node.

In typed code a write or transform callback, or a _write or _transform
method, may declare its chunk with any such type, which converts when
the callback runs. Writes pass the encoding argument to _write and
_transform as Node does: undefined when absent, null when a callback
took its place, otherwise the value unchanged. A parameter declared
string | null | undefined receives that value and one declared string
receives "" for undefined and null. write(chunk, encoding) on a
byte-mode stream encodes a string chunk with any known encoding and
throws ERR_UNKNOWN_ENCODING for an unknown one. Readable.from takes
typed Maps and Sets of any such value, and arrays that mix strings and
Buffers.

for await over a readable-only stream that will emit 'close' now ends
after 'close', as Node's eos does. Buffered writes that complete inside
clearBuffer run their callbacks in order, and 'finish' waits for a
deferred afterWrite that carries a callback, as Node's pendingcb does,
unless a _final callback schedules it.

The 'data' listener adapters take three slots and write and transform
callbacks read their chunk and encoding through runtime accessors, so
RUNTIME_ABI_VERSION is now 26.
2026-10-22 04:21:55 -05:00
Chris Tate 6e753cd5c8 Merge main 2026-10-22 04:08:52 -05:00
Chris Tate b5fb4115e1 Merge main 2026-10-22 03:33:29 -05:00
Chris Tate 7d20212dd4 Merge main 2026-10-22 03:24:23 -05:00
Chris Tate fe02190d1d Compile JavaScript-path calls that Node runs (#1756) 2026-10-22 03:15:48 -05:00
Chris Tate 921f054891 Extend a generic factory's local class returned through a constructor type (#1761) 2026-10-22 03:15:25 -05:00