Skip to content

Commit 1b4d97d

Browse files
committed
feat(spannerlib-node): add support for Node wrapper with TypeScript and dual-publishing (ESM/CJS)
1 parent ed619ad commit 1b4d97d

18 files changed

Lines changed: 913 additions & 0 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
gorm/
22
.idea
33
.DS_Store
4+
node_modules
5+
build/
6+
package-lock.json
7+
googleapis/

spannerlib/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ vendor/bundle
1010
*.swp
1111
ext/
1212
Gemfile.lock
13+
/googleapis/
14+
package-lock.json
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"timeout": 10000,
3+
"reporter": "spec",
4+
"spec": ["build/esm/test/**/*.js"]
5+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"all": true,
3+
"check-coverage": true,
4+
"include": ["src/**/*.js", "index.js"],
5+
"exclude": ["test/**"],
6+
"reporter": ["text", "lcov", "html"],
7+
"branches": 80,
8+
"lines": 80,
9+
"functions": 80,
10+
"statements": 80
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"printWidth": 80,
3+
"tabWidth": 2,
4+
"useTabs": false,
5+
"semi": true,
6+
"singleQuote": true,
7+
"trailingComma": "es5",
8+
"bracketSpacing": true,
9+
"arrowParens": "always"
10+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Node-API Wrapper for Spanner Shared Library
2+
3+
This package provides a high-performance Node-API (N-API) bridge to the Go-based Spanner shared library. It offers superior stability and performance compared to traditional FFI approaches.
4+
5+
## Prerequisites
6+
7+
- Node.js >= 20.0.0
8+
- Go compiler (to build the underlying shared library, if not pre-built)
9+
- C++ toolchain (GCC/Clang or MSVC)
10+
11+
## Installation & Building
12+
13+
To build the native addon, run:
14+
15+
```bash
16+
npm install
17+
```
18+
19+
This will trigger `node-gyp` to compile the C++ bridge and link it with `libspanner.so`.
20+
21+
## Usage
22+
23+
```javascript
24+
const { Pool, Connection } = require('spannerlib-node');
25+
26+
async function run() {
27+
const pool = new Pool('my-user-agent', 'projects/.../instances/.../databases/...');
28+
await pool.create();
29+
30+
const conn = await pool.createConnection();
31+
const rows = await conn.executeSql('SELECT 1');
32+
33+
while (await rows.next()) {
34+
// process rows
35+
}
36+
37+
await rows.close();
38+
await conn.close();
39+
await pool.close();
40+
}
41+
```
42+
43+
## Architecture
44+
45+
The wrapper consists of:
46+
1. **`src/cpp/addon.cc`**: C++ Node-API bridge that handles thread boundaries and type conversions between V8 and C.
47+
2. **`src/ffi/utils.js`**: Helper functions to invoke native methods asynchronously using Promises.
48+
3. **`src/lib/`**: JavaScript classes (`Pool`, `Connection`, `Rows`) that provide a clean object-oriented interface.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
'targets': [
3+
{
4+
'target_name': 'spanner_napi',
5+
'sources': [ 'src/cpp/addon.cc' ],
6+
'include_dirs': [
7+
'<!@(node -p "require(\'node-addon-api\').include")',
8+
'../../shared',
9+
],
10+
'dependencies': [
11+
'<!(node -p "require(\'node-addon-api\').gyp")'
12+
],
13+
'cflags!': [ '-fno-exceptions' ],
14+
'cflags_cc!': [ '-fno-exceptions' ],
15+
'xcode_settings': {
16+
'MACOSX_DEPLOYMENT_TARGET': '10.15',
17+
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
18+
'CLANG_CXX_LIBRARY': 'libc++',
19+
},
20+
'msvs_settings': {
21+
'VCCLCompilerTool': { 'ExceptionHandling': 1 },
22+
},
23+
'conditions': [
24+
['OS=="mac"', {
25+
'libraries': [
26+
'<(module_root_dir)/../../shared/libspanner.so'
27+
],
28+
'xcode_settings': {
29+
'OTHER_LDFLAGS': [
30+
'-Wl,-rpath,@loader_path'
31+
]
32+
},
33+
'copies': [
34+
{
35+
'destination': '<(PRODUCT_DIR)',
36+
'files': [ '<(module_root_dir)/../../shared/libspanner.so' ]
37+
}
38+
]
39+
}],
40+
['OS=="linux"', {
41+
'ldflags': [
42+
'-Wl,-rpath,$$ORIGIN'
43+
],
44+
'libraries': [
45+
'<(module_root_dir)/../../shared/libspanner.so'
46+
],
47+
'copies': [
48+
{
49+
'destination': '<(PRODUCT_DIR)',
50+
'files': [ '<(module_root_dir)/../../shared/libspanner.so' ]
51+
}
52+
]
53+
}]
54+
]
55+
}
56+
]
57+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"name": "spannerlib-node",
3+
"version": "0.1.0",
4+
"main": "./build/cjs/src/index.js",
5+
"module": "./build/esm/src/index.js",
6+
"types": "./build/esm/src/index.d.ts",
7+
"type": "module",
8+
"exports": {
9+
".": {
10+
"import": {
11+
"types": "./build/esm/src/index.d.ts",
12+
"default": "./build/esm/src/index.js"
13+
},
14+
"require": {
15+
"types": "./build/cjs/src/index.d.ts",
16+
"default": "./build/cjs/src/index.js"
17+
}
18+
}
19+
},
20+
"engines": {
21+
"node": ">=20.0.0"
22+
},
23+
"scripts": {
24+
"build": "node-gyp rebuild",
25+
"postbuild": "node -e \"if (process.platform === 'darwin') require('child_process').execSync('install_name_tool -change libspanner.so @loader_path/libspanner.so ./build/Release/spanner_napi.node')\"",
26+
"compile:esm": "tsc -p .",
27+
"compile:cjs": "tsc -p ./tsconfig.cjs.json",
28+
"compile": "npm run compile:esm && npm run compile:cjs",
29+
"test": "mocha build/esm/test/**/*.js"
30+
},
31+
"files": [
32+
"build/esm",
33+
"build/cjs",
34+
"build/Release/*.node"
35+
],
36+
"dependencies": {
37+
"node-addon-api": "^8.0.0"
38+
},
39+
"devDependencies": {
40+
"mocha": "^10.2.0",
41+
"typescript": "^5.4.0",
42+
"@types/node": "^20.11.0",
43+
"@types/mocha": "^10.0.6",
44+
"@google-cloud/spanner": "^7.13.0"
45+
},
46+
"gypfile": false
47+
}

0 commit comments

Comments
 (0)