·
2 commits
to main
since this release
Immutable
release. Only release title and notes can be modified.
API compatible with node:sqlite from Node.js v26.7.0, plus three APIs landed upstream but not yet in a Node.js release line. SQLite is unchanged at 3.53.4.
Added
StatementSync.prototype.close(): Finalizes a prepared statement deterministically instead of waiting for garbage collection or database close. ThrowsERR_INVALID_STATEif the statement is already finalized, if it is currently executing, or if called from inside an authorizer callback —sqlite3_finalize()modifies the connection, which SQLite forbids there, soclose()joins the same guard the other statement methods use. Ported from Node.js PR #64232.StatementSync.prototype[Symbol.dispose](): Enablesusing stmt = db.prepare(...). Unlikeclose(), it is idempotent and never throws; the two casesclose()rejects for safety become no-ops, leaving the statement to be finalized later by GC or database close. Also from Node.js PR #64232.ArrayBufferandSharedArrayBufferparameter binding: Both now bind as BLOBs, matching Node.js PR #62061. Previously onlyArrayBufferViews (Buffer, TypedArray, DataView) were accepted.
These three landed on nodejs/node@main but are not in the v26.x-staging line this package syncs from, so they ship here ahead of their Node.js release. They are covered by this package's own tests; the corresponding upstream tests will arrive with a future sync.
Fixed
- Use-after-free when a session outlives its database:
Sessionholds a rawDatabaseSync *, and N-API finalization order between the two wrappers is unspecified. If the database was finalized first, every surviving session was left pointing at freed memory and the next session method dereferenced it. Both sides now clear the link, and an orphaned session reportsdatabase is not open. Confirmed with Valgrind before and after. Ports Node.js PR #63797 and #64783 in the shape our N-API port allows — upstream keeps the database alive with a strong reference, which we cannot do: aNapi::Referencemember on a GC-finalizedObjectWrapcorrupts V8 JIT pages on Alpine/musl (see commit 4da0638). ArrayBufferbound as SQLNULL: AnArrayBufferorSharedArrayBufferpassed as the sole argument torun()/get()/all()was treated as a named-parameter object rather than a value, leaving the real parameter unbound. The insert silently storedNULLinstead of the blob.
Changed
- Smaller published tarball: a
filesallowlist inpackage.jsonreplaces.npmignore, dropping the package from 78 files to 46. Everythingbinding.gypcompiles still ships, sonode-gyp-build's source fallback is unaffected on platforms without a prebuild. Gone are the TypeScript sources (the published source maps already embedsourcesContent), the reference copies of Node.js's ownnode_sqlite.cc/.h,Makefile,SECURITY.md, andosv-scanner.toml. - Releases are staged for approval instead of published directly (release process):
Build & Releasenow signs and pushes the version commit and tag, then dispatches a tag-boundStage npm Releaseworkflow that rebuilds all eight prebuilds from the tag, packs one tarball, installs and loads it on every supported platform, and stages it on npm for a maintainer to approve with 2FA. Only the staging job holds npm publishing authority: it checks out no source, installs no dependencies, and runs no third-party action. See RELEASE.md. - Upstream sync: Node.js
v26.x-staging@68dc114→v26.x-staging@079339a. Beyond the session lifetime fix above, this range addsIsOpen()guards toenableLoadExtension()andsetAuthorizer()(Node.js PR #64812) and marks the statement iterator done at exhaustion — all three already matched our port, which had them first. Upstream'sBaseObjectPtrguards inExec()/applyChangeset()(Node.js PR #64535) do not apply: an N-APIObjectWrapreceiver is rooted by the handle scope for the whole synchronous call, verified under Valgrind. - Close-inside-callback error message: now
database cannot be closed while in a callback, matching the wording upstream adopted in Node.js PR #64743. Previouslydatabase cannot be closed inside a user-defined function callback. The errorcode(ERR_INVALID_STATE) is unchanged; only the message text differs, so any test matching the old string needs updating. - Node.js compatibility tests sync from the same branch as the sources:
sync:testsdefaulted tomainwhilesync:nodetracksvNN.x-staging, so the suite ran the next major's tests against current-line sources and reported failures for APIs that did not exist in the baseline. Both now resolve the same staging branch.test-sqlite-udf-close.jshad also been downloaded but never adapted, so its four cases — the ones that pin the close-inside-callback message below — were absent fromnpm run test:node; the adapted file is now generated, andsync-node-tests.tsonly runs its sync when invoked directly, so its exports can be reused without triggering one. memory:checkruns again (developer tooling): the sanitizer harness had three independent faults, each masking the next. It exportedLD_PRELOADfor the whole script, sobinding.gyp'snode -phelper ran under LeakSanitizer, exited non-zero on an unrelated leak, and failedconfigure; it drove the build throughnpx node-gyp, which races on creating the.depsdirectories; and it preloaded only an ASan runtime, so the UBSan*_aborthandlers were missing at load. The preload is now applied to the test command alone, the build goes throughnpm run build:native:rebuild, and both runtimes are preloaded. It also probes candidate ASan runtimes and skips any that cannot complete a leak check — clang's compiler-rt runtime wedges in LSan'sStopTheWorldon clang 21 + Linux 7.x, where GCC's libasan works.- Benchmark comparison refreshed (developer tooling): pinned
better-sqlite313.0.3 and regenerated the published throughput table and charts.
Commits
Full Changelog: v2.2.0...v2.3.0