structuredClone in an island of a value holding native records, class
instances or objects (interceptor objects V8's serializer cannot clone)
clones a plain copy of that data, as Node clones the objects themselves.
An island array of primitives or strings (or arrays of those) returned
directly or through a promise crosses as a copy, which the checked cast
then meets, instead of failing the cast as an object handle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tsgo has no getContextualTypes or getResolvedSignatures. Each prefetch
asked for them anyway, and a failed chunk was split and retried down to
single nodes, so a tsgo build paid about two failed requests per
prefetched node. The checker facade now remembers a batch method the
server answers with "unknown API method" and asks those queries node by
node from then on.
A function expression stored in a function type with more parameters
folds `arguments.length` to that type's parameter count, the count every
call through the slot passes. A named expression that calls itself
through its own name passes only its declared parameters there, so no
constant answers both calls. Such a read now reports SC1090 with the
reason, and expressions that do not refer to their own name keep the
fold.
tsc-ts 0.0.7 answers getContextualTypes and getResolvedSignatures, so
NODE_BATCH_QUERIES is on: lowering asks the checker for the contextual
types of identity-flow sites, array literals and conditionals, and the
resolved signatures of calls, in batches instead of node by node.
The release-age exclusions move to tsc-ts 0.0.7 and its platform
packages, and add @scriptc/threads 0.4.1, which tsc-ts 0.0.7 depends on.
Structural positions started with a file's absolute path, so lowered
names (closures, recursive records and unions, and every name keyed by a
type that holds one) changed with the directory the program was compiled
in. Union arms are ordered by their types' keys, so a program compiled in
a new temporary directory could also get its union arms in another order.
union-retype.test.ts compiles each program in a fresh temporary
directory, and in about one run in ten the arms of the visitor's unions
came out in an order where no retag keeps its tags, which failed "lowered
identity retags share the box".
Positions now name a file by its path relative to the entry's directory
(default library files keep their base name), so the same program gets
the same names wherever it sits.
Array reduce typed its accumulator by the call's checked type, widened
with undefined only when the callback could return a missing value or a
receiver without an initial value could have holes. Three runtime values
fell outside it and threw "not representable in the target union":
- an initial value read from a missing element;
- a callback result of another primitive (`missing[0] ?? "fallback"`);
- a callback wrapped in parentheses, which the analysis and the callback
lowering did not see as the arrow it wraps.
The accumulator now also takes undefined from a possibly missing initial
value, and the primitive arms the callback's result can have. Array
callbacks are taken without their parentheses.
`arguments.length` in a fixed-arity function expression folded to its
declared parameter count. When the expression flows into a fixed-arity
function type with more parameters, as array callbacks do (value, index,
array, and the accumulator for reduce), every call passes that type's
count, and the length is that count.
The receiver index read its methods or members table by a computed key and
iterated generic method names through `?.keys() ?? []`. The native frontend
refuses dynamic keyed reads of records and cannot convert the iterator-or-array
union, so the complete frontend no longer lowered. The table is now selected
with a conditional and the generic method names are iterated under a check.
The program the lowering compiles serves each file that has JSX at its
own path, with the JSX rewritten to calls (host.addVirtualFile). The
host reported such a path to the checker server as created whenever it
was not virtual yet. The files exist on disk and the server had already
read them for the program that checks them as written, so tsgo kept
their original text, and lowering met the JSX syntax: SC1090 "syntax
'JsxFragment' is not supported yet" in 6822, 6824 and 6825 under
SCRIPTC_CHECKER=tsgo, while tsc-ts reread the files.
A virtual file that replaces a file on disk is now reported as changed,
and both checkers give the same LLVM IR and diagnostics for the JSX
corpus programs.
ClassDynamicDispatch.process tested every property receiver for every
property dispatch and for every method name a dynamic call names, in every
pass: on the flat tsc-ts bundle, 9.2 million receiver tests per build. It
now indexes each receiver list once by the method, generic method,
accessor and field names along each receiver's base chain, and tests only
the receivers listed under a name, plus the ones whose emitter, stream or
EventTarget root answers names of its own, in list order. Names that the
test decides on every receiver (compiler names, constructor, stack) and
emit-override specializations still test every receiver.
SCRIPTC_CHECK_DISPATCH_CANDIDATES=1 checks each selection against a test
of every receiver.
A binding that may hold a value its declared type omits (a function
parameter fed array elements, which can be holes) and that the checker
narrowed to one record or class arm of a union with more than two arms
was read through a checked extraction of that arm. When the binding held
a hole, a member read or method call threw "undefined is not
representable in the target union ..." for a record arm and "Value is
not an instance of 'Shape'" for a class arm, where Node throws "Cannot
read properties of undefined (reading 'a')".
The read now tests for undefined first and throws Node's TypeError, as
the two-arm path already does.
A generic binding inside a function that a nested function read before
the declaration lowered stores its instance into a TDZ box when the
declaration runs. That value was the instance's closure, which the
backend interns for module functions, so every evaluation of the
declaration stored the same function object. JavaScript makes a new one
each time. The stored value is now a closure over a lifted forwarder that
calls the instance, so each evaluation allocates a new closure; the
forwarder is created once per instance. Async and rest-parameter
bindings read this way report SC1090.
Corpus 7180 checks the identities.
ClassProtocols' identity-flow fixpoint asks the contextual type of each
site whose source carries identity, one round trip at a time, and lowering
asks the resolved signature of each call and the contextual type of each
array literal and conditional it lowers the same way. The checker facade
can now batch them: prefetchContextualTypes for the fixpoint's sites before
it runs, and, for each prefetched wave of reachable roots, the resolved
signatures of its calls and the contextual types of its array literals and
conditionals. A batched query that fails is left out of the memo, so a
direct query still throws where it is made.
The batches use the checker's getContextualTypes and getResolvedSignatures
requests. NODE_BATCH_QUERIES keeps them off until scriptc depends on a
tsc-ts release that answers them; while it is off the prefetches do
nothing.
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.