@graphscope-neug/linux-x64 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CMakeLists.txt ADDED
@@ -0,0 +1,111 @@
1
+ # NeuG Node.js Bindings - CMake Build Configuration
2
+ #
3
+ # This file is built as a subdirectory of the main NeuG CMake project.
4
+ # It requires neug_main and neug_proto targets to exist.
5
+ #
6
+ # Build from project root:
7
+ # mkdir -p build && cd build
8
+ # cmake .. -DBUILD_NODEJS=ON -DCMAKE_BUILD_TYPE=Release
9
+ # make -j$(nproc) neug_node_bind
10
+
11
+ # Find Node.js headers by querying the node executable for its install prefix
12
+ find_program(NODE_EXECUTABLE NAMES node nodejs)
13
+ if (NOT NODE_EXECUTABLE)
14
+ message(FATAL_ERROR "Cannot find Node.js executable. Please install Node.js or set PATH.")
15
+ endif()
16
+
17
+ execute_process(
18
+ COMMAND ${NODE_EXECUTABLE} -p "require('path').resolve(process.execPath, '..', '..', 'include', 'node')"
19
+ OUTPUT_VARIABLE NODE_INCLUDE_HINT
20
+ OUTPUT_STRIP_TRAILING_WHITESPACE
21
+ ERROR_QUIET
22
+ )
23
+
24
+ find_path(NODE_INCLUDE_DIR node_api.h
25
+ HINTS ${NODE_INCLUDE_HINT} /usr/include/node /usr/local/include/node
26
+ PATH_SUFFIXES node
27
+ )
28
+ if (NOT NODE_INCLUDE_DIR)
29
+ message(FATAL_ERROR "Cannot find Node.js headers (node_api.h). Hint path: ${NODE_INCLUDE_HINT}")
30
+ endif()
31
+
32
+ # Find node-addon-api include path (if installed via npm)
33
+ execute_process(
34
+ COMMAND ${NODE_EXECUTABLE} -p "require('node-addon-api').include"
35
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
36
+ OUTPUT_VARIABLE NODE_ADDON_API_DIR
37
+ OUTPUT_STRIP_TRAILING_WHITESPACE
38
+ ERROR_QUIET
39
+ )
40
+ string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
41
+
42
+ file(GLOB_RECURSE SOURCE_CPP "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc")
43
+
44
+ # Match Python binding: disable LTO to avoid GCC ICE with large static libs
45
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE)
46
+
47
+ add_library(neug_node_bind MODULE ${SOURCE_CPP})
48
+ set_target_properties(neug_node_bind PROPERTIES
49
+ PREFIX ""
50
+ SUFFIX ".node"
51
+ )
52
+
53
+ target_include_directories(neug_node_bind PRIVATE
54
+ ${NODE_INCLUDE_DIR}
55
+ ${NODE_ADDON_API_DIR}
56
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
57
+ ${CMAKE_CURRENT_BINARY_DIR}/../../src/compiler
58
+ )
59
+
60
+ add_dependencies(neug_node_bind neug_proto)
61
+
62
+ set(NEUG_LIBRARIES neug)
63
+ if (BUILD_HTTP_SERVER)
64
+ set(NEUG_LIBRARIES ${NEUG_LIBRARIES})
65
+ endif()
66
+
67
+ find_package(OpenSSL REQUIRED)
68
+ target_link_libraries(neug_node_bind PRIVATE
69
+ ${NEUG_LIBRARIES}
70
+ ${GLOG_LIBRARIES}
71
+ ${Protobuf_LIBRARIES}
72
+ OpenSSL::SSL
73
+ OpenSSL::Crypto
74
+ )
75
+
76
+ target_compile_definitions(neug_node_bind PRIVATE
77
+ NAPI_VERSION=8
78
+ NODE_ADDON_API_DISABLE_DEPRECATED
79
+ BUILDING_NODE_EXTENSION
80
+ )
81
+
82
+ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
83
+ target_compile_options(neug_node_bind PRIVATE -Wno-vla-extension)
84
+ endif()
85
+
86
+ # Hide protobuf/abseil/arrow symbols to avoid conflicts
87
+ if (COMMAND neug_apply_symbol_visibility)
88
+ neug_apply_symbol_visibility(neug_node_bind)
89
+ endif()
90
+
91
+ # macOS: allow undefined symbols (Node.js resolves them at runtime)
92
+ if (APPLE)
93
+ set_target_properties(neug_node_bind PROPERTIES
94
+ LINK_FLAGS "-undefined dynamic_lookup"
95
+ )
96
+ endif()
97
+
98
+ # Set RPATH so neug_node_bind.node can find libneug.so / libneug.dylib
99
+ # in the same directory (prebuilds/<platform>/). This is essential for
100
+ # npm-distributed packages where the shared lib is bundled alongside the .node file.
101
+ if (APPLE)
102
+ set_target_properties(neug_node_bind PROPERTIES
103
+ INSTALL_RPATH "@loader_path"
104
+ BUILD_WITH_INSTALL_RPATH TRUE
105
+ )
106
+ else()
107
+ set_target_properties(neug_node_bind PROPERTIES
108
+ INSTALL_RPATH "$ORIGIN"
109
+ BUILD_WITH_INSTALL_RPATH TRUE
110
+ )
111
+ endif()
package/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # The Node.js binding API for NeuG
2
+
3
+ ## Prerequisites
4
+
5
+ Same system dependencies as NeuG (CMake >= 3.16, C++20 compiler), plus:
6
+
7
+ - Node.js >= 18.0.0
8
+
9
+ ### Installing Node.js
10
+
11
+ NeuG Node.js bindings require **Node.js >= 18.0.0** (N-API v8). Install via nvm:
12
+
13
+ ```bash
14
+ # Install nvm
15
+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
16
+
17
+ # Reload shell in Linux
18
+ source ~/.bashrc
19
+ # Or MacOS
20
+ source ~/.zshrc
21
+
22
+ # Install Node.js LTS (v22)
23
+ nvm install --lts && nvm use --lts
24
+ # Or install a specific version (>= 18)
25
+ nvm install 18 && nvm use 18
26
+
27
+ # Verify
28
+ node -v # should be >= 18.0.0
29
+ npm -v
30
+ ```
31
+
32
+ ## Building
33
+
34
+ ### Build
35
+
36
+ ```bash
37
+ cd tools/nodejs_bind
38
+ make build
39
+ ```
40
+
41
+ This will:
42
+ 1. Install Node.js dependencies (`npm install`)
43
+ 2. Build the native addon via the main NeuG CMake project (`-DBUILD_NODEJS=ON`)
44
+ 3. Copy the resulting `neug_node_bind.node` to `build/Release/`
45
+
46
+
47
+ ### Pack
48
+
49
+ ```bash
50
+ make pack
51
+ ```
52
+
53
+ Create a self-contained, distributable npm package tarball (`.tgz`). This will:
54
+
55
+ 1. Build the native addon (same as `make build`)
56
+ 2. Copy prebuilt binaries into `prebuilds/<platform>/`:
57
+ - `neug_node_bind.node` — the native addon
58
+ - `libneug.so` — core shared library
59
+ - `libmimalloc.so.2` — memory allocator
60
+ 3. Run `npm pack` to produce `neug-<version>.tgz`
61
+
62
+ The resulting tarball can be installed without a C++ build environment:
63
+
64
+ ```bash
65
+ npm install ./neug-0.1.2.tgz
66
+ ```
67
+
68
+
69
+ ### Clean
70
+
71
+ ```bash
72
+ make clean
73
+ ```
74
+
75
+
76
+ ## API Example
77
+
78
+ A complete runnable example is provided in [`example.js`](example.js):
79
+
80
+ ```bash
81
+ node example.js
82
+ ```
83
+
84
+
85
+ ```js
86
+ const { Database } = require('./lib');
87
+
88
+ // Open an in-memory database
89
+ const db = new Database({ databasePath: '', mode: 'w' });
90
+ const conn = db.connect();
91
+
92
+ // Create schema
93
+ conn.execute('CREATE NODE TABLE person(id INT64, name STRING, age INT32, PRIMARY KEY(id));');
94
+ conn.execute('CREATE REL TABLE knows(FROM person TO person, since INT64);');
95
+
96
+ // Insert vertices
97
+ conn.execute("CREATE (p:person {id: 1, name: 'Alice', age: 30});");
98
+ conn.execute("CREATE (p:person {id: 2, name: 'Bob', age: 25});");
99
+
100
+ // Insert edge
101
+ conn.execute(
102
+ "MATCH (a:person), (b:person) WHERE a.name = 'Alice' AND b.name = 'Bob' " +
103
+ "CREATE (a)-[:knows {since: 2020}]->(b);"
104
+ );
105
+
106
+ // Query
107
+ const result = conn.execute(
108
+ 'MATCH (a:person)-[r:knows]->(b:person) RETURN a.name, r.since, b.name;'
109
+ );
110
+ for (const row of result) {
111
+ console.log(row);
112
+ }
113
+
114
+ conn.close();
115
+ db.close();
116
+ ```
@@ -0,0 +1,90 @@
1
+ /** Copyright 2020 Alibaba Group Holding Limited.
2
+ *
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const { QueryResult } = require('./query-result');
19
+
20
+ /**
21
+ * AsyncConnection provides an asynchronous interface to interact with the NeuG database.
22
+ *
23
+ * The underlying implementation uses Node.js worker_threads (or the synchronous Connection
24
+ * wrapped in a Promise) to execute queries asynchronously.
25
+ *
26
+ * @example
27
+ * const db = new Database({ databasePath: '/tmp/test.db', mode: 'w' });
28
+ * const conn = db.asyncConnect();
29
+ * const result = await conn.execute('MATCH (n) RETURN n');
30
+ * for (const row of result) {
31
+ * console.log(row);
32
+ * }
33
+ * conn.close();
34
+ */
35
+ class AsyncConnection {
36
+ /**
37
+ * @param {object} nativeConnection - The native NodeConnection object.
38
+ */
39
+ constructor(nativeConnection) {
40
+ this._conn = nativeConnection;
41
+ this._isOpen = true;
42
+ }
43
+
44
+ /**
45
+ * Check if the connection is open.
46
+ * @returns {boolean} True if the connection is open.
47
+ */
48
+ get isOpen() {
49
+ return this._isOpen;
50
+ }
51
+
52
+ /**
53
+ * Close the async connection.
54
+ */
55
+ close() {
56
+ if (this._isOpen) {
57
+ this._conn.close();
58
+ this._isOpen = false;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Execute a cypher query asynchronously.
64
+ *
65
+ * @param {string} query - The cypher query to execute.
66
+ * @param {string} [accessMode=''] - The access mode of the query.
67
+ * @param {Object} [parameters=null] - Query parameters as key-value pairs.
68
+ * @returns {Promise<QueryResult>} A promise that resolves to the query result.
69
+ */
70
+ async execute(query, accessMode = '', parameters = null) {
71
+ if (!this._isOpen) {
72
+ throw new Error('Connection is closed.');
73
+ }
74
+ // Wrap synchronous native call in a Promise for async interface
75
+ return new Promise((resolve, reject) => {
76
+ try {
77
+ const nativeResult = this._conn.execute(
78
+ query,
79
+ accessMode,
80
+ parameters || {}
81
+ );
82
+ resolve(new QueryResult(nativeResult));
83
+ } catch (e) {
84
+ reject(e);
85
+ }
86
+ });
87
+ }
88
+ }
89
+
90
+ module.exports = { AsyncConnection };
package/lib/binding.js ADDED
@@ -0,0 +1,30 @@
1
+ /** Copyright 2020 Alibaba Group Holding Limited.
2
+ *
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ // Native artifacts live in build/<Release|Debug>/<platform>/.
19
+ // Platform tags use the format: <os>-<arch> (e.g. linux-x64, darwin-arm64).
20
+ // Prefer Debug if present (dev intent); otherwise Release.
21
+
22
+ const path = require('path');
23
+ const fs = require('fs');
24
+
25
+ const platform = `${process.platform}-${process.arch}`;
26
+
27
+ const buildDir = path.join(__dirname, '..', 'build');
28
+ const cfg = fs.existsSync(path.join(buildDir, 'Debug', platform)) ? 'Debug' : 'Release';
29
+
30
+ module.exports = require(path.join(buildDir, cfg, platform, 'neug_node_bind.node'));
@@ -0,0 +1,127 @@
1
+ /** Copyright 2020 Alibaba Group Holding Limited.
2
+ *
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const { QueryResult } = require('./query-result');
19
+ const { validAccessModes, isAccessModeValid } = require('./utils');
20
+ const { OK, ERR_CONNECTION_CLOSED, codeName } = require('./error-codes');
21
+
22
+ /**
23
+ * Connection represents a logical connection to a NeuG database.
24
+ * Use this class to interact with the database, executing queries and managing transactions.
25
+ *
26
+ * @example
27
+ * const db = new Database({ databasePath: '/tmp/test.db', mode: 'w' });
28
+ * const conn = db.connect();
29
+ * const result = conn.execute('MATCH (n) RETURN n');
30
+ * for (const row of result) {
31
+ * console.log(row);
32
+ * }
33
+ * conn.close();
34
+ */
35
+ class Connection {
36
+ /**
37
+ * @param {object} nativeConnection - The native NodeConnection object from the C++ binding.
38
+ */
39
+ constructor(nativeConnection) {
40
+ this._conn = nativeConnection;
41
+ this._isOpen = true;
42
+ }
43
+
44
+ /**
45
+ * Check if the connection is open.
46
+ * @returns {boolean} True if the connection is open.
47
+ */
48
+ get isOpen() {
49
+ return this._isOpen;
50
+ }
51
+
52
+ /**
53
+ * Close the connection to the database.
54
+ */
55
+ close() {
56
+ if (this._isOpen) {
57
+ this._conn.close();
58
+ this._isOpen = false;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Execute a cypher query on the database.
64
+ *
65
+ * @param {string} query - The cypher query to execute.
66
+ * @param {string} [accessMode=''] - The access mode of the query.
67
+ * Supported modes: 'read'/'r', 'insert'/'i', 'update'/'u', 'schema'/'s'.
68
+ * @param {Object} [parameters=null] - Query parameters as key-value pairs.
69
+ * @returns {QueryResult} The result of the query execution.
70
+ * @throws {Error} If the connection is closed or the query fails.
71
+ *
72
+ * @example
73
+ * // Simple query
74
+ * const result = conn.execute('MATCH (n) RETURN n.id');
75
+ *
76
+ * // Query with parameters
77
+ * const result = conn.execute(
78
+ * 'MATCH (n:person) WHERE n.id = $id RETURN n.name',
79
+ * 'read',
80
+ * { id: 12345 }
81
+ * );
82
+ */
83
+ execute(query, accessMode = '', parameters = null) {
84
+ if (!this._isOpen) {
85
+ throw new Error(
86
+ `Connection is closed. Please open the connection before executing queries. ` +
87
+ `Error code: ${ERR_CONNECTION_CLOSED}`
88
+ );
89
+ }
90
+
91
+ if (accessMode !== '' && !isAccessModeValid(accessMode.toLowerCase())) {
92
+ throw new Error(
93
+ `Invalid access_mode: ${accessMode}. Supported access modes are ` +
94
+ `[${validAccessModes.join(', ')}].`
95
+ );
96
+ }
97
+
98
+ const nativeResult = this._conn.execute(
99
+ query,
100
+ accessMode,
101
+ parameters || {}
102
+ );
103
+
104
+ const statusCode = nativeResult.statusCode();
105
+ const statusMessage = nativeResult.statusMessage();
106
+
107
+ if (statusCode === OK) {
108
+ return new QueryResult(nativeResult);
109
+ }
110
+
111
+ throw new Error(
112
+ `Failed to execute query: ${query}. ` +
113
+ `Error code: ${statusCode}, Error Message: ` +
114
+ `${codeName(statusCode)}: ${statusMessage}`
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Get the schema of the NeuG database.
120
+ * @returns {string} The schema as a string.
121
+ */
122
+ getSchema() {
123
+ return this._conn.getSchema();
124
+ }
125
+ }
126
+
127
+ module.exports = { Connection };