Skip to content

Commit 21a4d49

Browse files
committed
Rewrite driver from NAPI-RS to N-API C addon
Replace the Rust/NAPI-RS binding with a pure C addon (src/stoolap.c) that loads libstoolap via dlopen. Platform packages now ship the shared library instead of a prebuilt .node file, and node-gyp compiles the thin C wrapper on install. Key changes: - N-API C addon with stable ABI (NAPI_VERSION 8), works across Node >= 18 - Hybrid query pipeline: small results parsed in C, large results as zero-copy ArrayBuffer parsed in JS (lib/parse.js) - Truly async prepared statement operations via libuv thread pool (stmtExecAsync, stmtQueryBufAsync) instead of setImmediate shim - Database tracks child handles (statements, transactions) and invalidates them on close to prevent use-after-free - Pin stoolap engine version (v0.3.4) in CI/publish workflows - Expand test suite to 129 tests covering vectors, persistence, concurrency, batch ops, and sync/async parity
1 parent 33ce786 commit 21a4d49

39 files changed

Lines changed: 7038 additions & 7355 deletions

.github/workflows/ci.yml

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ concurrency:
1111
group: ${{ github.workflow }}-${{ github.ref }}
1212
cancel-in-progress: true
1313

14+
env:
15+
STOOLAP_ENGINE_REF: v0.3.4
16+
1417
jobs:
1518
build:
1619
strategy:
@@ -19,51 +22,56 @@ jobs:
1922
settings:
2023
- host: macos-latest
2124
target: aarch64-apple-darwin
22-
build: npx napi build --platform --release --no-dts-header --target aarch64-apple-darwin
25+
lib: libstoolap.dylib
2326
- host: macos-latest
2427
target: x86_64-apple-darwin
25-
build: npx napi build --platform --release --no-dts-header --target x86_64-apple-darwin
28+
lib: libstoolap.dylib
2629
- host: ubuntu-latest
2730
target: x86_64-unknown-linux-gnu
28-
build: npx napi build --platform --release --no-dts-header --target x86_64-unknown-linux-gnu
31+
lib: libstoolap.so
2932
- host: ubuntu-latest
3033
target: aarch64-unknown-linux-gnu
31-
build: |
32-
sudo apt-get update
33-
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
34-
export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc
35-
export CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++
36-
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
37-
npx napi build --platform --release --no-dts-header --target aarch64-unknown-linux-gnu
34+
lib: libstoolap.so
3835
- host: windows-latest
3936
target: x86_64-pc-windows-msvc
40-
build: npx napi build --platform --release --no-dts-header --target x86_64-pc-windows-msvc
37+
lib: stoolap.dll
4138

4239
name: Build - ${{ matrix.settings.target }}
4340
runs-on: ${{ matrix.settings.host }}
4441
steps:
4542
- uses: actions/checkout@v4
4643

47-
- uses: actions/setup-node@v4
48-
with:
49-
node-version: 22
44+
- name: Clone stoolap engine
45+
run: git clone --depth 1 --branch ${{ env.STOOLAP_ENGINE_REF }} https://github.com/stoolap/stoolap.git stoolap-engine
5046

5147
- name: Install Rust
5248
uses: dtolnay/rust-toolchain@stable
5349
with:
5450
targets: ${{ matrix.settings.target }}
5551

56-
- name: Install dependencies
57-
run: npm install
52+
- name: Rust cache
53+
uses: Swatinem/rust-cache@v2
54+
with:
55+
workspaces: stoolap-engine
56+
key: ${{ matrix.settings.target }}
57+
58+
- name: Install cross-compilation tools
59+
if: matrix.settings.target == 'aarch64-unknown-linux-gnu'
60+
run: |
61+
sudo apt-get update
62+
sudo apt-get install -y gcc-aarch64-linux-gnu
5863
59-
- name: Build native addon
60-
run: ${{ matrix.settings.build }}
64+
- name: Build stoolap shared library
65+
working-directory: stoolap-engine
66+
run: cargo build --release --features ffi --target ${{ matrix.settings.target }}
67+
env:
68+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
6169

62-
- name: Upload artifact
70+
- name: Upload shared library
6371
uses: actions/upload-artifact@v4
6472
with:
65-
name: bindings-${{ matrix.settings.target }}
66-
path: '*.node'
73+
name: lib-${{ matrix.settings.target }}
74+
path: stoolap-engine/target/${{ matrix.settings.target }}/release/${{ matrix.settings.lib }}
6775
if-no-files-found: error
6876

6977
test:
@@ -73,7 +81,7 @@ jobs:
7381
fail-fast: false
7482
matrix:
7583
os: [ubuntu-latest, macos-latest, windows-latest]
76-
node: [22]
84+
node: [18, 22]
7785
runs-on: ${{ matrix.os }}
7886
steps:
7987
- uses: actions/checkout@v4
@@ -82,26 +90,26 @@ jobs:
8290
with:
8391
node-version: ${{ matrix.node }}
8492

85-
- name: Install dependencies
86-
run: npm install
87-
8893
- name: Determine artifact name
8994
id: artifact
9095
shell: bash
9196
run: |
9297
if [[ "${{ matrix.os }}" == "macos-latest" ]]; then
93-
echo "name=bindings-aarch64-apple-darwin" >> $GITHUB_OUTPUT
98+
echo "name=lib-aarch64-apple-darwin" >> $GITHUB_OUTPUT
9499
elif [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then
95-
echo "name=bindings-x86_64-unknown-linux-gnu" >> $GITHUB_OUTPUT
100+
echo "name=lib-x86_64-unknown-linux-gnu" >> $GITHUB_OUTPUT
96101
elif [[ "${{ matrix.os }}" == "windows-latest" ]]; then
97-
echo "name=bindings-x86_64-pc-windows-msvc" >> $GITHUB_OUTPUT
102+
echo "name=lib-x86_64-pc-windows-msvc" >> $GITHUB_OUTPUT
98103
fi
99104
100-
- name: Download artifact
105+
- name: Download shared library
101106
uses: actions/download-artifact@v4
102107
with:
103108
name: ${{ steps.artifact.outputs.name }}
104109
path: .
105110

111+
- name: Install dependencies and build C addon
112+
run: npm install --omit=optional
113+
106114
- name: Run tests
107115
run: npm test

.github/workflows/publish.yml

Lines changed: 63 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ permissions:
88
contents: write
99
id-token: write
1010

11+
env:
12+
STOOLAP_ENGINE_REF: v0.3.4
13+
1114
jobs:
1215
build:
1316
strategy:
@@ -16,51 +19,56 @@ jobs:
1619
settings:
1720
- host: macos-latest
1821
target: aarch64-apple-darwin
19-
build: npx napi build --platform --release --no-dts-header --target aarch64-apple-darwin
22+
lib: libstoolap.dylib
2023
- host: macos-latest
2124
target: x86_64-apple-darwin
22-
build: npx napi build --platform --release --no-dts-header --target x86_64-apple-darwin
25+
lib: libstoolap.dylib
2326
- host: ubuntu-latest
2427
target: x86_64-unknown-linux-gnu
25-
build: npx napi build --platform --release --no-dts-header --target x86_64-unknown-linux-gnu
28+
lib: libstoolap.so
2629
- host: ubuntu-latest
2730
target: aarch64-unknown-linux-gnu
28-
build: |
29-
sudo apt-get update
30-
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
31-
export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc
32-
export CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++
33-
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
34-
npx napi build --platform --release --no-dts-header --target aarch64-unknown-linux-gnu
31+
lib: libstoolap.so
3532
- host: windows-latest
3633
target: x86_64-pc-windows-msvc
37-
build: npx napi build --platform --release --no-dts-header --target x86_64-pc-windows-msvc
34+
lib: stoolap.dll
3835

3936
name: Build - ${{ matrix.settings.target }}
4037
runs-on: ${{ matrix.settings.host }}
4138
steps:
4239
- uses: actions/checkout@v4
4340

44-
- uses: actions/setup-node@v4
45-
with:
46-
node-version: 22
41+
- name: Clone stoolap engine
42+
run: git clone --depth 1 --branch ${{ env.STOOLAP_ENGINE_REF }} https://github.com/stoolap/stoolap.git stoolap-engine
4743

4844
- name: Install Rust
4945
uses: dtolnay/rust-toolchain@stable
5046
with:
5147
targets: ${{ matrix.settings.target }}
5248

53-
- name: Install dependencies
54-
run: npm install
49+
- name: Rust cache
50+
uses: Swatinem/rust-cache@v2
51+
with:
52+
workspaces: stoolap-engine
53+
key: ${{ matrix.settings.target }}
54+
55+
- name: Install cross-compilation tools
56+
if: matrix.settings.target == 'aarch64-unknown-linux-gnu'
57+
run: |
58+
sudo apt-get update
59+
sudo apt-get install -y gcc-aarch64-linux-gnu
5560
56-
- name: Build native addon
57-
run: ${{ matrix.settings.build }}
61+
- name: Build stoolap shared library
62+
working-directory: stoolap-engine
63+
run: cargo build --release --features ffi --target ${{ matrix.settings.target }}
64+
env:
65+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
5866

59-
- name: Upload artifact
67+
- name: Upload shared library
6068
uses: actions/upload-artifact@v4
6169
with:
62-
name: bindings-${{ matrix.settings.target }}
63-
path: '*.node'
70+
name: lib-${{ matrix.settings.target }}
71+
path: stoolap-engine/target/${{ matrix.settings.target }}/release/${{ matrix.settings.lib }}
6472
if-no-files-found: error
6573

6674
publish:
@@ -75,45 +83,55 @@ jobs:
7583
node-version: 22
7684
registry-url: https://registry.npmjs.org
7785

78-
- name: Install dependencies
79-
run: npm install
80-
8186
- name: Download all artifacts
8287
uses: actions/download-artifact@v4
8388
with:
8489
path: artifacts
8590

86-
- name: Move artifacts to project root
91+
- name: Copy shared libraries into platform packages
92+
shell: bash
8793
run: |
88-
for dir in artifacts/bindings-*/; do
89-
cp "$dir"*.node .
94+
declare -A TARGETS=(
95+
["aarch64-apple-darwin"]="darwin-arm64/libstoolap.dylib"
96+
["x86_64-apple-darwin"]="darwin-x64/libstoolap.dylib"
97+
["x86_64-unknown-linux-gnu"]="linux-x64-gnu/libstoolap.so"
98+
["aarch64-unknown-linux-gnu"]="linux-arm64-gnu/libstoolap.so"
99+
["x86_64-pc-windows-msvc"]="win32-x64-msvc/stoolap.dll"
100+
)
101+
102+
for target in "${!TARGETS[@]}"; do
103+
dest="npm/${TARGETS[$target]}"
104+
src=$(ls artifacts/lib-${target}/*)
105+
echo "Copying $src -> $dest"
106+
cp "$src" "$dest"
90107
done
91-
ls -la *.node
92-
93-
- name: Copy artifacts into platform packages
94-
run: npx napi artifacts --output-dir . --npm-dir npm
95-
96-
- name: Update platform package versions
97-
run: npx napi version
98108
99-
- name: Sync optionalDependencies versions
109+
- name: Sync platform package versions
100110
run: |
101111
node -e "
102112
const fs = require('fs');
113+
const path = require('path');
103114
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
104115
const ver = pkg.version;
105-
let changed = false;
116+
117+
// Sync optionalDependencies
106118
for (const dep of Object.keys(pkg.optionalDependencies || {})) {
107-
if (pkg.optionalDependencies[dep] !== ver) {
108-
pkg.optionalDependencies[dep] = ver;
109-
changed = true;
110-
}
119+
pkg.optionalDependencies[dep] = ver;
111120
}
112-
if (changed) {
113-
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
114-
console.log('Updated optionalDependencies to version ' + ver);
115-
} else {
116-
console.log('optionalDependencies already at version ' + ver);
121+
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
122+
123+
// Sync platform package versions
124+
const dirs = fs.readdirSync('npm', { withFileTypes: true })
125+
.filter(d => d.isDirectory())
126+
.map(d => d.name);
127+
for (const dir of dirs) {
128+
const p = path.join('npm', dir, 'package.json');
129+
if (fs.existsSync(p)) {
130+
const ppkg = JSON.parse(fs.readFileSync(p, 'utf8'));
131+
ppkg.version = ver;
132+
fs.writeFileSync(p, JSON.stringify(ppkg, null, 2) + '\n');
133+
console.log('Updated ' + p + ' to v' + ver);
134+
}
117135
}
118136
"
119137
@@ -144,4 +162,3 @@ jobs:
144162
uses: softprops/action-gh-release@v2
145163
with:
146164
generate_release_notes: true
147-
files: '*.node'

.gitignore

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
# Rust
2-
target/
3-
41
# Node.js
52
node_modules/
63

4+
# Build output
5+
build/
6+
77
# Native binaries
88
*.node
9-
10-
# npm platform packages - only ignore built binaries
119
npm/*/*.node
1210

13-
# Profiling scripts
14-
profile.mjs
11+
# Local shared library
12+
libstoolap.dylib
13+
libstoolap.so
14+
stoolap.dll
1515

1616
# OS
1717
.DS_Store

0 commit comments

Comments
 (0)