Releases: photostructure/node-sqlite
Release list
v2.3.0
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
Release v2.2.0
No API changes. Upstream refresh and dependency updates.
Changed
- SQLite 3.53.4: Updated from 3.53.3. A bug-fix release addressing defects found in 3.53.0–3.53.3, largely by automated analysis — bounds hardening in the JSON/JSONB parsers plus fixes in the session and RBU modules (release notes). No API changes, but the amalgamation is compiled into the shipped binary, so any SQLite bump gets a minor release: consumers choose when to take it.
- Upstream sync: Node.js
v26.x-staging@955e669→v26.x-staging@68dc114, now API compatible withnode:sqlitefrom Node.js v26.5.0. The onlynode_sqlite.ccchange in this range reads the column count after the firststep()inStatementSync.all()(Node.js PR #64219); our port already resolved column metadata lazily on the first row, so no change was needed. - TypeScript held at 6.x:
.ncurc.cjsnow pinstypescriptto the 6.x line. TypeScript 7 is not yet supported bytypedoc(0.28.20 peers<= 6.0.x) ortypescript-eslint(8.63.0 peers< 6.1.0).
Commits
Full Changelog: v2.1.0...v2.2.0
Release v2.1.0
No API changes. Build hardening, supply-chain verification, and one undefined-behavior fix.
Changed
- Compiler and linker hardening: POSIX builds now follow the OpenSSF hardening baseline — stack protector,
_FORTIFY_SOURCE=2, format-string hardening, full RELRO, non-executable stack, and arch-gated control-flow integrity (Intel CET on x64, PAC/BTI on arm64). Windows ARM64 gains/Qspectreand/guard:signret, the backward-edge protection it previously lacked. - Vendored SQLite integrity: the amalgamation sync now verifies the download against a SHA3-256 pinned in-tree and refuses to vendor a mismatch, instead of compiling whatever it fetched.
Fixed
- Empty changeset undefined behavior:
session.changeset()/.patchset()on a session with no recorded changes calledmemcpy(NULL, NULL, 0), which is undefined behavior even at zero length. Results are unchanged (still a zero-lengthUint8Array); the UB is gone. Surfaced by the new UndefinedBehaviorSanitizer pass in CI.
Commits
Full Changelog: v2.0.0...v2.1.0
Release v2.0.0
API compatible with node:sqlite from Node.js v26.4.0.
Added
DatabaseSync.prototype.serialize([dbName])andDatabaseSync.prototype.deserialize(buffer, [options]): Serialize a database to aUint8Arrayand load one back, matching thenode:sqliteAPIs added in Node.js PR #59967. Wrapssqlite3_serialize/sqlite3_deserializeand finalizes any open prepared statements before replacing database content.
Changed
- BREAKING: Dropped support for Node.js 20 (end-of-life April 2026);
@photostructure/sqlitenow requires Node.js 22 or newer (package.jsonenginesis>=22). This is why this release is 2.0.0 rather than a 1.x minor. - SQLite 3.53.3: Updated from 3.53.0. Three patch releases (3.53.1–3.53.3), bug fixes only, no API impact (release notes).
- Upstream sync: Node.js
v25.x-staging@ffa9b8f→v26.x-staging@c96c838. Beyondserialize()/deserialize(), upstream added a column-name caching path and asimdutffast path for ASCII column text inStatementSync— both V8/internal-only optimizations with no N-API equivalent, so not ported. Subsequentnode:sqlitebug fixes — closing the connection after a failedopen(), changesetxFilter/callback-lifetime hardening, and reading the column count after the firststep()inall()— are already covered by our port's structure and needed no change. - Statement finalization on
db.close(): LiveStatementSyncinstances are now eagerly detached when their database closes, so further method calls throwERR_INVALID_STATEwith"statement has been finalized"(matchingnode:sqlite) instead of"Database connection is closed". Statement error messages were also normalized to lowercase"statement has been finalized"throughout. - Build hardening (
SQLITE_ENABLE_API_ARMOR): The bundled SQLite is now compiled with API armor, so misuse of the C API — for example by a loaded extension such as sqlite-vec — returnsSQLITE_MISUSEinstead of risking undefined behavior or a process abort the caller cannot catch. Negligible runtime cost; the public JavaScript API is unaffected. - Callback reentrancy hardening: operations SQLite forbids from inside its own callbacks (notably
close/deserialize, plusprepare/exec/step/serialize/setAuthorizerfrom an authorizer) now throwERR_INVALID_STATEinstead of corrupting connection state. Intentional divergence fromnode:sqlite(nodejs/node#63207). - Config setters frozen mid-step:
setReadBigInts,setReturnArrays, and thesetAllow*parameter setters throwERR_INVALID_STATEif called while the statement is executing.
Fixed
- Backup teardown stability: In-flight
backup()operations are now safe when a Node environment is shutting down. Backup jobs avoid resolving/rejecting promises or routing expected SQLite failures through node-addon-api's async worker error path after teardown begins. - Authorizer error identity: the exact value thrown by an authorizer callback (subclass,
code, message, thrown primitives) now propagates unchanged through prepare/exec/step/serialize/deserialize/changeset/extension load, instead of being replaced by a generic error. - TEXT with embedded NUL bytes: returned in full via byte-length conversion instead of being truncated at the first NUL.
Performance
- Faster multi-row reads: per-statement column-key caching, byte-length string conversion, per-column exception checks removed from the row builder, and a native iterator fast path for flat/raw modes.
-fno-plton Linux removes PLT indirection from Node-API calls in the hot path.
Internal
- Docs: bulk-read performance tradeoff documented honestly; Node 22 requirement propagated across docs and examples.
- Benchmark suite reworked for fair, reproducible driver comparison (deterministic workloads, median confidence intervals, per-scenario ratios, SVG charts) plus correlation-gated memory-leak detection.
- Dependencies: node-addon-api 8.9.0, TypeScript 6, ESLint 10, prettier 3.8.5,
@types/node26. - CI: pinned-action updates (CodeQL, TruffleHog, OSV-Scanner, actions/checkout).
Commits
Full Changelog: v1.2.1...v2.0.0
Release v1.2.1
Full Changelog: v1.2.0...v1.2.1
Release v1.2.0
API compatible with node:sqlite from Node.js v25.9.0.
Changed
- SQLite 3.53.0: Updated from 3.52.0. Adds
json_array_insert()/jsonb_array_insert()SQL functions,ALTER TABLEsupport for adding/removingNOT NULLandCHECKconstraints,REINDEX EXPRESSIONSto rebuild expression indexes,VACUUM INTOreserve=NURI parameter, and new C APIs (sqlite3_str_truncate,sqlite3_str_free,sqlite3_carray_bind_v2,SQLITE_PREPARE_FROM_DDL,SQLITE_DBCONFIG_FP_DIGITS). Floating-point text conversion default changed from 15 to 17 significant digits. Full release notes. - Upstream sync: Node.js
v25.x-staging@ca2d6ea→ffa9b8f(includes content through Node.js v25.9.0). Upstream made a cosmetic lambda-capture fix inApplyChangeset's filter callback; our port already used equivalent by-value captures.
Fixed
- Docs: corrected stale "DataView parameter binding is not currently supported" note;
BLOBbinding acceptsTypedArrayorDataViewinput and returnsUint8Array.
Internal
- Test sync: skip
test-sqlite-serialize.js— Node.jsDatabaseSync.prototype.serialize()/deserialize()APIs are not yet ported.
Full Changelog: v1.1.0...v1.2.0
Release v1.1.0
Full Changelog: v1.0.0...v1.1.0
Release v1.0.0
Promotion to v1.0.0 following API stabilization and 0.5.0 release.
API compatible with node:sqlite from Node.js v25.8.0.
Added
db.limitsproperty: Get and set SQLite limits (length, sqlLength, column, exprDepth, compoundSelect, vdbeOp, functionArg, attach, likePatternLength, variableNumber, triggerDepth) at runtime. SupportsInfinityto reset to compile-time maximum. Also acceptslimitsoption inDatabaseSyncconstructor.- Statement iterator invalidation: Calling
stmt.run(),stmt.get(),stmt.all(), orstmt.iterate()now invalidates any active iterator on the same statement, throwingERR_INVALID_STATE
Changed
- SQLite 3.52.0: Updated from 3.51.2
Full Changelog: v0.5.0...v1.0.0
Release v0.5.0
Added
- Statement modes via
enhance():stmt.pluck(),stmt.raw(),stmt.expand()for better-sqlite3 compatibility.pluck()returns only the first column value from queries.raw()returns rows as arrays instead of objects.expand()returns rows namespaced by table, correctly handling duplicate column names across JOINs- All three modes are mutually exclusive, matching better-sqlite3's toggle semantics
stmt.database: Back-reference from prepared statements to their parent database instanceEnhancedStatementMethodstype: TypeScript interface forpluck(),raw(),expand(), anddatabase
Commits
Full Changelog: v0.4.0...v0.5.0
Release v0.4.0
API compatible with node:sqlite from Node.js v25.6.1.
Added
enhance()function: Adds better-sqlite3-style.pragma()and.transaction()methods to any compatible database instanceisEnhanced()type guard: Check if a database has enhanced methods- Transaction helper: Automatic BEGIN/COMMIT/ROLLBACK with savepoint support for nested transactions
- Pragma convenience method: Simple API for reading and setting SQLite pragmas with
simpleoption - Node.js test sync script:
npm run sync:testsdownloads and adapts upstream Node.js SQLite tests - Percentile extension:
SQLITE_ENABLE_PERCENTILEnow enabled, addingpercentile(),median(),percentile_cont(),percentile_disc()SQL functions (Node.js v25+) - Prepare options:
db.prepare(sql, options)now accepts per-statement options (readBigInts,returnArrays,allowBareNamedParameters,allowUnknownNamedParameters) to override database-level defaults. This is a Node.js v25+ feature;node:sqliteon v24 and earlier silently ignores these options. - StatementColumnMetadata type:
stmt.columns()now returns richer metadata includingcolumn,database,table, andtypeproperties alongsidename - SQLite 3.51.2: Updated from 3.51.1
Changed
- BREAKING: Removed API extensions to achieve exact parity with
node:sqlite:- Removed
stmt.finalize()method (use database close for cleanup) - Removed
stmt.finalizedproperty - Removed
stmt[Symbol.dispose](still available onDatabaseSyncandSession) - Removed
db.backup()instance method (use standalonebackup(db, path)function instead)
- Removed
- BREAKING:
Session.changeset()andSession.patchset()now returnUint8Arrayinstead ofBufferto matchnode:sqliteAPI - BREAKING: Defensive mode now defaults to
trueinstead offalseto match Node.js v25+ behavior. Use{ defensive: false }to restore old behavior.
Fixed
- Alpine Linux / musl stability: Fixed native crashes by removing N-API reference cleanup from destructors that corrupted V8 JIT state
- Session lifecycle management: Fixed use-after-free, double-free, and mutex deadlock when databases are garbage collected before their sessions
- Worker thread stability: Added cleanup hooks and exception handling for worker thread termination
- Callback error preservation:
applyChangeset()now preserves the original error message when JavaScript callbacks throw createTagStore()now throws errors withcode: 'ERR_INVALID_STATE'property when database is closed, matching Node.js error format
Full Changelog: v0.3.0...v0.4.0