A member read in an export getter (a.b or a[b]) can invoke another
accessor that runs code on every read, so the recorder now accepts only a
bare identifier as a plain binding getter. Everything else keeps its
runtime read and is not snapshotted.
The mutation check that makes an earlier island module eager copied only
data properties of a module value, so an object exported through a
binding getter could stay stale when a later load changed it. The shape
now reads binding getters the same way the snapshot does, and compares
them by reading again, so those modules no longer defer.
Corpus fixtures pin both: a getter that reads a member counts every read,
and a later load that mutates a getter-exported object keeps both modules
eager.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main resolves computed createRequire specifiers at run time, so a computed
require inside a function no longer makes an island; one in module code
still does.
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>
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>
Scope-B milestone NI-1, API side: the explicit call API that replaces the
build container's hand-written node subprocess (api patch 09).
- The new package @scriptc/node exports callSync(specifier, name, ...args)
and call(...). The module loads with require() from the calling module;
arguments and results cross as JSON copies; a thrown Error crosses as its
nearest intrinsic class with its message and string code, other thrown
values as JSON copies; callSync refuses a function that returns a
promise. Its Node implementation runs the calls in-process with the same
copies, so a program prints the same under Node and compiled.
- The compiler lowers calls to the named or namespace imports
(node-api-import.ts, lower-node-island.ts) to the nodeIsland.callSync
and nodeIsland.call libCalls: the calling module's real path, the
specifier, the export name and the boxed argument array, with the result
checked against the declared type like `JSON.parse(s) as T`. Arguments
and results whose types are not JSON data are refused at build time, and
the functions and the namespace refuse as values.
- Corpus programs 6800-6803 (sync and async results, errors, process.exit
from Node with the program's exit listeners, Node's pending timers
keeping the program alive) run under Node and as binaries that load
libnode; `// @requires-libnode` skips them where SCRIPTC_LIBNODE names no
pack (until the pack CI matrix publishes packs). The node-island harness
covers loading Node only on the first call, the beside packaging, a
missing pack, a pack of another Node version or bridge ABI, and the
executable's symbols.
- Release tooling publishes @scriptc/node with the other packages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>