Repository navigation
Conversation
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
cramforce
force-pushed
the
ni/13-libnode-packs
branch
from
October 21, 2026 18:07
058a7e2 to
3a02321
Compare
cramforce
force-pushed
the
ni/13-libnode-packs
branch
from
October 21, 2026 18:18
3a02321 to
596220e
Compare
Add the @scriptc/libnode-<platform> packs (linux-x64-gnu, linux-arm64-gnu, darwin-arm64, darwin-x64): the pinned Node.js 24.22.0 built as a stripped shared library with full ICU and its startup snapshot, the scriptc bridge compiled against that release's headers, Node's LICENSE and a manifest with the Node version, NODE_MODULE_VERSION, bridge ABI and file hashes. packages/libnode-common holds the pin, the build (fetch and verify the source tarball, configure --shared, build only the libnode target, strip, stage with headers in a cache keyed by release, flags, platform and toolchain; the bridge rebuilds in seconds against the cached headers), a verifier, and a smoke test: a C program that loads a pack the way the runtime does, starts Node from the snapshot and evaluates expressions synchronously, asynchronously and throwing. The libnode-packs workflow builds the packs on native runners, Linux in the manylinux_2_28 images for a glibc 2.28 floor, caches each libnode build, smoke-tests and uploads every pack. It never runs in the ordinary build or test path. The compiler gains the pack lookup (SCRIPTC_LIBNODE, then the installed package), validation, the nodeIslands.libnode placements and the license notices for the Node island loader to use. The bridge source and its C ABI header are the NI-1 bridge agent's, included here so the packs build; the packs stay private until the in-process island runtime ships. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s in CI Stack the libnode packs on the in-process loader (#1685) and use its pack assembler and manifest format instead of a second one: build-pack.mjs keeps its CLI and fields (schema, nodeVersion, moduleVersion, bridgeAbi, platform, arch, files), adds the libnode and bridge names, strips and (on macOS) re-signs the bridge and links it with -z defs, and exports buildPack(), which the platform packs call with their package, target and build inputs as extra manifest fields. libnodeRecord falls back to the installed @scriptc/libnode-<platform> pack, found from the entry's directory and then next to the compiler, when neither --libnode nor SCRIPTC_LIBNODE names one. The Linux test lanes restore the libnode build that the libnode-packs workflow cached, assemble the linux-x64 pack from it in seconds (build.mjs --from-cache never builds Node) and export SCRIPTC_LIBNODE, so the Node island corpus and harness tests run instead of skipping. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An installed @scriptc/libnode-<platform> pack versions in lockstep with the compiler, whose version every executable cache key already holds; keying on it as well made the full compiler's key differ from the bootstrap's routed key wherever a pack directory was present, so routed cache hits stopped. Only directories with a manifest.json count as installed packs, so an unbuilt workspace package is not one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cramforce
force-pushed
the
ni/13-libnode-packs
branch
from
October 22, 2026 00:54
7d156b2 to
79931be
Compare
cramforce
marked this pull request as ready for review
October 22, 2026 02:20
| delete mod.nodeIsland; | ||
| } else if (outputKind === "exe") { | ||
| const recorded = libnodeRecord(opts.libnode, opts.outPath); | ||
| const recorded = libnodeRecord(opts.libnode, opts.outPath, process.env, dirname(entryPath)); |
Contributor
Comment on lines
+11
to
+17
| describe.skipIf(pack === undefined || pack === "")("libnode pack smoke test", () => { | ||
| test("the pack is complete", () => { | ||
| expect(verifyPack(pack!)).toEqual([]); | ||
| }); | ||
|
|
||
| test("a C program starts Node from the pack and evaluates expressions", () => { | ||
| const { status, stdout } = runSmoke(pack!); |
Contributor
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Build prebuilt libnode packs for Node islands
Milestone NI-13 of the scope-B plan: the
@scriptc/libnode-<platform>packs that programs with Node islands load Node from, in process. Like the LLVM and runtime packs, they are built only by an explicitbuild:nativescript and a dedicated workflow, never bypnpm -r buildor the tests.What a pack is
packages/libnode-<platform>/(linux-x64-gnu, linux-arm64-gnu, darwin-arm64, darwin-x64), afterbuild:native:libnode.so.137/libnode.137.dylib: Node.js 24.22.0 (the repository's pin,.node-versionandinternal/compatibility/node-v24.json) built--sharedfrom the official, SHA-256-verified source tarball, with full ICU and the built-in startup snapshot, stripped;libscr_node_bridge.so/.dylib: the scriptc bridge compiled against that release's headers, linked to libnode through$ORIGIN/@loader_path, so the pack directory can move;LICENSE(Node's, which carries every bundled component's license),SCRIPTC_LICENSE(Apache-2.0, for the bridge),THIRD_PARTY_NOTICES;manifest.json: package and version, platform, Node version,NODE_MODULE_VERSION, bridge ABI, size and SHA-256 of both libraries, the libnode cache key, the bridge hash, configure flags, ICU flavor, toolchain and glibc floor.The packs version in lockstep with the other platform packages and stay
privateuntil the in-process island runtime ships (RELEASING.md has the publishing steps).Build (
packages/libnode-common)libnode.jsonpins the release, tarball hash, module version, configure flags and ICU flavor; a test keeps it equal to the repository's Node pin.build-pack.mjsis NI-1's pack assembler (Load Node.js in-process on first use: libnode bridge and runtime loader #1685), kept CLI- and manifest-compatible and extended: it strips the bridge, re-signs it on macOS, links it with-z defs, records thelibnodeandbridgefile names, and exportsbuildPack()for the platform packs, which addpackage,version,target,licenseand their build inputs to the manifest.scripts/build.mjsfetches and verifies the tarball, configures, builds only thelibnodeninja target, strips, and stages the library with its headers and LICENSE under a cache directory keyed by (Node release, flags, platform, toolchain). A cache hit skips Node entirely; the bridge rebuilds throughbuildPack()in about 2 s.scripts/verify.mjs(prepack) checks the manifest against the package, pin and ABI, and the files against their hashes.smoke/smoke.c+scripts/smoke.mjs: a plain C program that loads a pack the way the runtime does (dlopen the bridge, check the ABI, start Node from the snapshot) and evaluates synchronous, asynchronous, throwing and rejecting expressions, including full-ICU formatting, crypto, zlib, fs and timers. Its stdout is compared with an exact transcript.packages/libnode-common/test/smoke.test.tsruns it whenSCRIPTC_LIBNODEnames a pack.CI (
.github/workflows/libnode-packs.yml)A matrix over the four platforms on native runners (Linux x64 and arm64 inside
quay.io/pypa/manylinux_2_28_*, glibc 2.28 and gcc-toolset-14; macOS 15 arm64 and Intel with Xcode clang), triggered only by changes to the packs, the bridge or the workflow, or by hand. It restores the libnode build fromactions/cache, builds, verifies, smoke-tests both inside the build image (the glibc floor) and on the runner, reports sizes, and uploads each pack plus zstd copies of both libraries.Lookup and CI test lanes
libnodeRecord(NI-1) now falls back to the installed@scriptc/libnode-<platform>pack when neither--libnodenorSCRIPTC_LIBNODEnames one: the nearestnode_modulesabove the entry, then next to the compiler (packages/compiler/src/node-islands/libnode-pack.ts). That module also reads and validates pack manifests, parsesnodeIslands.libnode: "embedded" | "beside" | <absolute path>for the project configuration (NI-2), and writes the<output>.licenses.txtnotices..github/actions/libnode-linuxreplaces Load Node.js in-process on first use: libnode bridge and runtime loader #1685's interimlibnode-dev-packaction: it restores the libnode build that this workflow cached, assembles@scriptc/libnode-linux-x64-gnufrom it (build.mjs --from-cache, about 2 s, never builds Node) and exportsSCRIPTC_LIBNODEfor the Linux test lanes. Until that cache exists for a ref (caches written onmainare readable from every PR; until this lands, only this PR has one), it falls back to Load Node.js in-process on first use: libnode bridge and runtime loader #1685's pinned development pack (fetch-dev-pack.mjs), so the island tests never skip. On this PR the lanes already use the NI-13 pack (cache hit onlibnode-linux-x64-gnu-24.22.0-6b41a418…).Measurements (Linux Vercel Sandbox, 8 vCPU, 16 GB)
ninja libnode, 8 vCPU, cold, no ccache)-T0)The portable pack passes the smoke test on Ubuntu 26.04 (glibc 2.43) and inside manylinux_2_28 (glibc 2.28), and NI-1's island tests pass against it (throwaway merge with #1686:
node-island.test.ts7/7, corpus 6800–6803).CI (first, cold run of libnode-packs on this PR, run 39631732110)
All four smoke tests pass (Linux both in the glibc 2.28 build image and on the runner). The uploaded
libnode-<platform>artifacts are 37–48 MB zipped. Each libnode build is now cached; a rerun with an unchanged pin restores it and only rebuilds the bridge.Notes
Node version: the packs stay at 24.22.0, built from the official, SHA-256-verified tarball (still published), matching
.node-version,internal/compatibility/node-v24.jsonand the Node CI runs; the pin test keeps the three equal. Only the fallback development pack follows dockfries to 24.22.1 (its 24.22.0 asset was withdrawn). Moving to 24.22.1 means bumping all three pins together (andpnpm node-compat), which rebuilds the packs once.After merge: the libnode-packs workflow runs on the push to
main(its path filter coverspackages/libnode-*/**), builds cold (about 2 h on the Linux runners), and saves caches inmain's scope, which every PR can restore; until then the lanes use the development pack.Stacked on Load Node.js in-process on first use: libnode bridge and runtime loader #1685 (NI-1 loader and bridge); the bridge source and
scr_node_bridge.hare unchanged from it. Review and merge after Load Node.js in-process on first use: libnode bridge and runtime loader #1685.The design's measured 26.5-minute build used the sandbox's Ubuntu 26.04 toolchain, whose library needs glibc 2.43: not distributable. Packs build on manylinux_2_28 instead.
Not shipped in the npm packages: the
.zstcopies. Nothing in scriptc can decode zstd yet (neither the runtime nor the compiler under the native CLI), soembeddedplacement waits for NI-4 to pick a decoder.🤖 Generated with Claude Code