Repository navigation
v0.14.0 — Deno and Bun, 91% smaller Linux prebuilds, release scaffolding
Distribution. A napi-mojo addon now runs on Deno and Bun as well as Node,
the Linux prebuilds are 91% smaller, every platform package declares what it
actually contains, and napi-mojo release --scaffold gives an addon author the
same publishing setup this repo uses. Three new CI gates hold the claims: a
cross-runtime job, an artifact-portability parser, and a glibc-floor check.
No Mojo signature changed, so downstream addons compile against 0.14.0
unmodified — but recompiling does change one runtime behaviour, module export
attributes, described first below.
See docs/plan-distribution.md for the
measurements behind all of it.
Behaviour changes
- Module exports are now enumerable, writable and configurable — ordinary
exports.foo = fnsemantics instead ofnapi_default. They were none of the
three, soObject.keys(addon)returned only the classes,{...addon}lost
every function, and assigning over an export silently did nothing. Class
prototype members are unchanged: a JS class method is non-enumerable, and
matching that is correct. If your code worked around the old behaviour with
Object.getOwnPropertyNames, it still works. addAsyncCleanupHook()returns its handle (an External) rather than
true, andremoveAsyncCleanupHook(handle)takes it. The old pair could
not work: add dropped the handle, so remove registered a second hook with
an identical(function, data)pair just to have one to remove, and the
caller's hook stayed registered. The duplicate also aborted Bun and corrupted
Deno's heap. The handle is type-tagged and remembers its removal, so a second
removeAsyncCleanupHook(handle)throwshandle already removedand an
External from anywhere else throws aTypeError— both were reachable from
plain JavaScript and crashed the process, because
napi_remove_async_cleanup_hookfrees the handle it is given.
Added
- Deno and Bun are supported and gated. Every surface this framework
exposes behaves identically on Node, Deno and Bun — async work,
ThreadsafeFunction, type tagging, primitives in anapi_refincluded. The
runtimesCI job keeps that true. Two upstream defects are recorded with
reproducers; neither is napi-mojo's, and both reproduce from a plain C addon.
Do not gate onnapi_get_version: Bun answers 9 and implements the v10
behaviour anyway. napi-mojo release --scaffoldwrites an addon author's prebuild and
publish setup:optionalDependenciesper platform, a loader, platform
manifests, and a release workflow whose consume job has no checkout and no
toolchain. It is safe to run in an existing project:package.jsonand the
platform manifests are patched (dependencies merged, an existingmainand
an absentfilesleft alone, author fields kept), and an existingindex.js
orrelease.ymlis kept unless you pass--force. Platform manifests
declare a licence covering what they carry, with the texts, and inherit a
repository(from the git remote if needed) so provenance publishing
accepts them.napi-mojo init'spixi.tomldeclares every prebuild
platform, and the workflow generates bindings before building and installs
exactly the version it published.- Load failures explain themselves. When a prebuilt binary is installed
but cannot load — a container image without the C++ runtime
(oven/bun:*-distroless,denoland/deno:alpine), glibc older than 2.35, or
musl —napi-mojo/demoand every scaffolded addon loader now throw an error
with acode(ERR_NATIVE_NO_CXX_RUNTIME,ERR_NATIVE_GLIBC_TOO_OLD,
ERR_NATIVE_LIBSTDCXX_TOO_OLD,ERR_NATIVE_MUSL), the fix, and a link to
the newdocs/TROUBLESHOOTING.md; the loader's
message is kept ascause.demo.jsused to report every such failure as
"No prebuilt demo binary available". The doc carries a support matrix, a
Dockerfile build-time check, and a two-line fix for Bun distroless — all
built and loaded on both Linux architectures on every release, with the
recipe extracted from the doc itself. napi-mojo release --syncsets every platform manifest and
optionalDependenciesentry topackage.json's version; the scaffold wires
it tonpm version, and the release workflow refuses to publish when they
disagree.napi-mojo release --bootstrapdoes the one-time first publish npm's
trusted publishing requires, as a0.0.0-bootstrap.0placeholder under the
bootstrapdist-tag. Publishingnpm/<platform>directly — what the
scaffold's first version printed — pushes a package with no binary at the
real version, which can never be republished.scripts/check-portable.mjs, a release gate that reads an artifact's
Mach-O/ELF load commands and refuses one that depends on the machine that
built it — the property a load test on the build machine cannot establish.
It models where the loader actually looks: a library beside the artifact
counts only if a self-relative search path (@loader_path,$ORIGIN) sends
the loader there, a@loader_path/dependency must exist,@executable_path
is node's directory rather than the addon's, and a library the bundler's
manifest names but that is not present fails the check.publish.ymlpreflights its publish targets before spending three
platform builds. The 0.13.0 release failed at the last step on a package npm
had never seen, leaving two platform packages published at the new version
and the root package on the old one. That is now a 20-second stop before
anything is pushed.
Changed
-
The Linux packages are 91% smaller — 26.1 MB → 2.4 MB unpacked, a
9.4 MB → 0.9 MB tarball. GCC'slibstdc++.so.6andlibgcc_s.so.1are no
longer bundled: the Mojo runtime needs at mostGLIBCXX_3.4.30, and its
GLIBC_2.35requirement — which cannot be bundled, glibc being the loader —
already implies a host that provides it, and Node itself links both.If you install on a host with glibc ≥ 2.35 but an unusually old
libstdc++,require()now fails with a loader version error instead of
working. That combination is constructible but not something any mainstream
distribution produces. Two CI gates hold the premise:check-glibc-floor.mjs
on every PR, and a no-toolchain container consume job on every release.Under Bun or Deno, the container image must provide
libstdc++.
Neither runtime links it, so the "Node links it" argument does not apply.
oven/bun:1.3.11-distrolessanddenoland/deno:alpine-2.9.6contain none
and fail atrequire()withlibstdc++.so.6: cannot open shared object file, where 0.13.0 loaded. The Debian- and Ubuntu-based Bun and Deno images,
anddenoland/deno:distroless, work. (oven/bun:*-alpineis musl and never
could load a glibc addon.) -
The Linux packages no longer declare
GPL-3.0-or-later WITH GCC-exception-3.1, because they no longer contain GCC's runtime. All
three platforms are nowMIT AND Apache-2.0 WITH LLVM-exception. Tarballs
published at 0.13.0 and earlier do contain those libraries; the exception is
what permitted it, andlicenses/NOTICE.bundle.txtsays so for anyone
auditing one. -
The 19 handle-only
raw_*wrappers no longer takeAnyOrigin— phase 1
of the deferred signature flip. Every argument was already a V8 handle
aliasedMutUntrackedOrigin, so nothing formed aPointer(to=<local>)and
there was no lifetime forAnyOriginto extend: the widening calls were
ceremony. Semantically a no-op. The remaining 128 wrappers take a raw pointer
and stay deferred; see
docs/plan-origin-migration.md.
Fixed
- The prebuilt platform packages declared
"license": "MIT"while shipping
the Mojo runtime and, on Linux, GCC'slibstdc++andlibgcc_s. Each now
declares what it actually contains and carries the licence texts.