A re-export from an island package that tsconfig `paths` maps to its
installed directory pushed the package's declaration file into the module
order, so the lowering compiled the declarations' own imports (SC2013 in
the package's .d.mts). Such a re-export now adds no module edge; the
lowering reports the island boundary as for any island re-export.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A monorepo can pin one copy of a package with a tsconfig `paths` entry
that names its installed directory. Its import then resolves as a project
import to the package's declarations, so when the package runs in a Node
island, preflight refused it as types-only (SC1010) and the lowering
skipped it as a project module, leaving its bindings unlowered (SC2013).
Both now treat it as the island import it is: typed by its declarations,
loaded in Node.
Corpus 6839 imports an ESM island package through such a mapping and uses
its interfaces and generic aliases from typed code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a native build failed and the packages it compiled with runtime fences
then failed as islands too, the native build's diagnostics were reported.
That build types those packages from their JavaScript, where declared-only
types (interfaces, `Static<T>`-style aliases) are `any`, so typed user code
reported cascades the island build does not have (a schema library's `TSchema`).
The island build's errors are reported instead.
In a generic function instance over island types (`<T extends TSchema>`),
a `T` value bound to dyn now crosses into the island the way `any` does,
instead of being refused as a native object.
Corpus 6829 types user code with an island package's interfaces, generic
aliases and a generic function; node-island.test.ts pins the reported
diagnostics of a failed build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A host callback marks where it runs on Node's stack, and a call into
Node.js from a fiber starts below that mark. Runtime code the callback
runs may go deeper on Node's stack before it starts or resumes a fiber,
and those frames stay live while the fiber runs: a call into Node.js
from the fiber overwrote them. scr_switch now reports where it leaves
Node's stack, and the mark moves down to it for the rest of the
callback.
6823 runs a timer callback 200 frames deep that starts an async
function calling into Node.js before its first await.
A lambda with no type parameters of its own, contextually typed by a
generic signature (`send: (m) => …` for an interface's `send<T>(m:
Message<T>): string`), takes that signature's type parameters: its
parameter is `Message<T>`. A generic lambda written with its own type
parameters already lowers once, erased, with them bound to `unknown`, but
this one tried to map `Message<T>` with `T` unbound and was refused
(SC2009).
It now lowers the same way, erased, binding the contextual signature's
type parameters to `unknown`. Parameters bound in the enclosing scope
keep their bindings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A union with a checked-dynamic arm (`unknown`, fetch's AbortSignal)
maps to the checked-dynamic value when every other arm is one the
checked-dynamic tree holds faithfully. Function arms were excluded, so
`Progress | AbortSignal | undefined` was refused (SC2009).
The tree boxes a function whose parameters check in and whose result
converts out by reference (canBoxFuncIntoDyn), and `typeof x ===
"function"` narrows it back out to the typed function. Such arms now
join the collapse. The corpus program checks calls, identity (`===`
against the original), and the other arm's members against Node.
pi's SessionManager.listAll takes `onProgressOrSignal?: SessionListProgress
| AbortSignal`. The refusal rejected the whole SessionManager class.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
libunicode composes an LV syllable with U+11A7 as if it were a trailing
consonant (U+11A8..U+11C2), which drops it: "가ᆧ".normalize()
gave "가" where Node gives "가ᆧ". U+11A7 is a class-0 vowel
that composes with nothing, so for the composing forms the code points
between occurrences of it normalize on their own and it is kept as it
is. The guard sits in front of the vendored library (scr_unicode_normalize),
for String.prototype.normalize and the NFC step of IDNA mapping.
8009-normalize-hangul-jungseong-o-yae prints the code points of all four
forms for U+11A7 after LV and LVT syllables, conjoining jamo, other
starters and combining marks.
The range analysis caps a counter that steps by 1 at 2^53, where
`j + 1 === j`, so a loop such as `for (let j = 2 ** 53 - 2;
j < 2 ** 53 + 10; j++)` has a range of three values at its condition and
was taken for a loop of three runs. It never ends, and without its poll
worker.terminate() could not stop it. A loop's runs now count as bounded
only when a step from every value in its counter's range gives a
different value.
A number-key Map or Set read whose key has an integer view
(sc_map_entry_f64_int) fell back to the same hashed probe as any double
key, which tests for NaN and -0 before hashing. The fallback is now
sc_map_entry_f64_hashed_int, which takes the integer and converts it to
the key word directly: an integer is never NaN, and zero converts to +0.
Both hashed probes test the absent-key filter only before scanning a map
that keeps no bucket table. The filter keeps two of its 64 bits per key,
so it rejects few absent keys once a map has a bucket table, and the
probe's first bucket read answers those.
The map layout and the runtime lookup are unchanged. Corpus 8331 covers
integers the compiler tracks as integers on hashed maps and absent keys on
small bucket tables, and matches Node's output.