@graphscope-neug/darwin-arm64 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.
@@ -0,0 +1,134 @@
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
+ /**
19
+ * QueryResult represents the result of a cypher query.
20
+ * It provides an iterator-like interface to access the results.
21
+ *
22
+ * @example
23
+ * const result = conn.execute('MATCH (n) RETURN n');
24
+ * for (const row of result) {
25
+ * console.log(row);
26
+ * }
27
+ */
28
+ class QueryResult {
29
+ /**
30
+ * @param {object} nativeResult - The native NodeQueryResult object from the C++ binding.
31
+ */
32
+ constructor(nativeResult) {
33
+ this._result = nativeResult;
34
+ }
35
+
36
+ /**
37
+ * Check if there are more results available.
38
+ * @returns {boolean} True if there are more results.
39
+ */
40
+ hasNext() {
41
+ return this._result.hasNext();
42
+ }
43
+
44
+ /**
45
+ * Get the next row of results.
46
+ * @returns {Array} The next row as an array of values.
47
+ */
48
+ getNext() {
49
+ return this._result.getNext();
50
+ }
51
+
52
+ /**
53
+ * Get the result at the specified index.
54
+ * @param {number} index - The index of the result to retrieve.
55
+ * @returns {Array} The row at the specified index.
56
+ * @throws {RangeError} If the index is out of range.
57
+ */
58
+ getAt(index) {
59
+ return this._result.getAt(index);
60
+ }
61
+
62
+ /**
63
+ * Get the total number of results.
64
+ * @returns {number} The number of results.
65
+ */
66
+ length() {
67
+ return this._result.length();
68
+ }
69
+
70
+ /**
71
+ * Get the projected column names.
72
+ * @returns {string[]} Column names in projection order.
73
+ */
74
+ columnNames() {
75
+ return this._result.columnNames();
76
+ }
77
+
78
+ /**
79
+ * Get the status code of the query result.
80
+ * @returns {number} 0 for success, non-zero for error.
81
+ */
82
+ statusCode() {
83
+ return this._result.statusCode();
84
+ }
85
+
86
+ /**
87
+ * Get the status message of the query result.
88
+ * @returns {string} The status message.
89
+ */
90
+ statusMessage() {
91
+ return this._result.statusMessage();
92
+ }
93
+
94
+ /**
95
+ * Get the result in Bolt response format.
96
+ * @returns {string} The result in Bolt response format.
97
+ */
98
+ getBoltResponse() {
99
+ return this._result.getBoltResponse();
100
+ }
101
+
102
+ /**
103
+ * Close the query result and release resources.
104
+ */
105
+ close() {
106
+ this._result.close();
107
+ }
108
+
109
+ /**
110
+ * Make QueryResult iterable with for...of loops.
111
+ * @returns {Iterator} An iterator over the result rows.
112
+ */
113
+ [Symbol.iterator]() {
114
+ const self = this;
115
+ return {
116
+ next() {
117
+ if (self.hasNext()) {
118
+ return { value: self.getNext(), done: false };
119
+ }
120
+ return { value: undefined, done: true };
121
+ },
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Get the string representation.
127
+ * @returns {string} String representation.
128
+ */
129
+ toString() {
130
+ return `QueryResult(size ${this.length()})`;
131
+ }
132
+ }
133
+
134
+ module.exports = { QueryResult };
package/lib/session.js ADDED
@@ -0,0 +1,275 @@
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 http = require('http');
19
+ const https = require('https');
20
+ const { URL } = require('url');
21
+ const { QueryResult } = require('./query-result');
22
+ const { isAccessModeValid, validAccessModes } = require('./utils');
23
+ const { ERR_NETWORK, ERR_SESSION_CLOSED } = require('./error-codes');
24
+
25
+ // Load the native binding (unified loader handles prebuilds/ and build/)
26
+ const nativeBinding = require('./binding');
27
+
28
+ /**
29
+ * Parse a timeout string to milliseconds.
30
+ * @param {string|number} timeout - Timeout value (e.g., '10s', '500ms', or number of seconds).
31
+ * @returns {number} Timeout in milliseconds.
32
+ */
33
+ function parseTimeout(timeout) {
34
+ if (typeof timeout === 'number') {
35
+ return timeout * 1000;
36
+ }
37
+ if (typeof timeout === 'string') {
38
+ if (timeout.endsWith('ms')) {
39
+ return parseInt(timeout.slice(0, -2), 10);
40
+ } else if (timeout.endsWith('s')) {
41
+ return parseInt(timeout.slice(0, -1), 10) * 1000;
42
+ }
43
+ }
44
+ return 10000; // default 10 seconds
45
+ }
46
+
47
+ /**
48
+ * Make an HTTP request using Node.js built-in http/https modules.
49
+ * @param {string} url - The URL to request.
50
+ * @param {Object} options - Request options.
51
+ * @param {string} [options.method='GET'] - HTTP method.
52
+ * @param {string} [options.body] - Request body.
53
+ * @param {number} [options.timeout=10000] - Timeout in milliseconds.
54
+ * @returns {Promise<{statusCode: number, body: Buffer}>}
55
+ */
56
+ function httpRequest(url, options = {}) {
57
+ return new Promise((resolve, reject) => {
58
+ const parsedUrl = new URL(url);
59
+ const isHttps = parsedUrl.protocol === 'https:';
60
+ const lib = isHttps ? https : http;
61
+
62
+ const reqOptions = {
63
+ hostname: parsedUrl.hostname,
64
+ port: parsedUrl.port || (isHttps ? 443 : 80),
65
+ path: parsedUrl.pathname + parsedUrl.search,
66
+ method: options.method || 'GET',
67
+ timeout: options.timeout || 10000,
68
+ headers: options.headers || {},
69
+ };
70
+
71
+ const req = lib.request(reqOptions, (res) => {
72
+ const chunks = [];
73
+ res.on('data', (chunk) => chunks.push(chunk));
74
+ res.on('end', () => {
75
+ resolve({
76
+ statusCode: res.statusCode,
77
+ body: Buffer.concat(chunks),
78
+ });
79
+ });
80
+ });
81
+
82
+ req.on('error', reject);
83
+ req.on('timeout', () => {
84
+ req.destroy();
85
+ reject(new Error('Request timed out'));
86
+ });
87
+
88
+ if (options.body) {
89
+ req.write(options.body);
90
+ }
91
+ req.end();
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Session connects to a NeuG server and provides the same query interface as Connection.
97
+ *
98
+ * @example
99
+ * // Start the server
100
+ * const db = new Database({ databasePath: '/tmp/test.db', mode: 'w' });
101
+ * db.serve({ port: 10000, host: 'localhost' });
102
+ *
103
+ * // In another process, connect via Session
104
+ * const session = new Session({ endpoint: 'http://localhost:10000' });
105
+ * const result = session.execute('MATCH (n) RETURN count(n)');
106
+ * for (const row of result) {
107
+ * console.log(row);
108
+ * }
109
+ * session.close();
110
+ */
111
+ class Session {
112
+ /**
113
+ * @param {Object} [options] - Session options.
114
+ * @param {string} [options.endpoint='http://localhost:10000'] - The server endpoint URL.
115
+ * @param {string|number} [options.timeout='10s'] - Request timeout.
116
+ */
117
+ constructor(options = {}) {
118
+ const {
119
+ endpoint = 'http://localhost:10000',
120
+ timeout = '10s',
121
+ } = options;
122
+
123
+ this._endpoint = endpoint.replace(/\/+$/, '');
124
+ this._queryEndpoint = `${this._endpoint}/cypher`;
125
+ this._statusEndpoint = `${this._endpoint}/service_status`;
126
+ this._schemaEndpoint = `${this._endpoint}/schema`;
127
+ this._timeout = parseTimeout(timeout);
128
+ this._closed = false;
129
+
130
+ // Quick connectivity check
131
+ // (We do this synchronously-ish via the constructor, but it's fire-and-forget)
132
+ // For a proper async init, use Session.open() instead.
133
+ }
134
+
135
+ /**
136
+ * Open a session with connectivity check.
137
+ *
138
+ * @param {Object} [options] - Session options (same as constructor).
139
+ * @returns {Promise<Session>} A connected Session instance.
140
+ */
141
+ static async open(options = {}) {
142
+ const session = new Session(options);
143
+ // Wait for the server to be ready, same purpose as Python's
144
+ // implicit readiness via slower HTTP client startup.
145
+ const maxRetries = 5;
146
+ const retryDelayMs = 200;
147
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
148
+ try {
149
+ await session._checkConnection();
150
+ return session;
151
+ } catch (_) {
152
+ if (attempt < maxRetries - 1) {
153
+ await new Promise((r) => setTimeout(r, retryDelayMs));
154
+ }
155
+ }
156
+ }
157
+ // Return anyway — let actual queries surface the real error
158
+ return session;
159
+ }
160
+
161
+ /**
162
+ * Check connectivity to the server.
163
+ * @private
164
+ */
165
+ async _checkConnection() {
166
+ await httpRequest(this._statusEndpoint, {
167
+ method: 'GET',
168
+ timeout: this._timeout,
169
+ });
170
+ }
171
+
172
+ /**
173
+ * Close the session.
174
+ */
175
+ close() {
176
+ if (this._closed) {
177
+ console.warn('[neug] Session is already closed.');
178
+ return;
179
+ }
180
+ this._closed = true;
181
+ }
182
+
183
+ /**
184
+ * Execute a query on the NeuG server.
185
+ *
186
+ * @param {string} query - The cypher query string.
187
+ * @param {string} [accessMode=''] - The access mode.
188
+ * @param {Object} [parameters=null] - Query parameters.
189
+ * @returns {Promise<QueryResult>} The query result.
190
+ * @throws {Error} If the session is closed or the request fails.
191
+ */
192
+ async execute(query, accessMode = '', parameters = null) {
193
+ if (this._closed) {
194
+ throw new Error(
195
+ `Session is closed. Cannot execute query. Error code: ${ERR_SESSION_CLOSED}`
196
+ );
197
+ }
198
+
199
+ if (accessMode !== '' && !isAccessModeValid(accessMode.toLowerCase())) {
200
+ throw new Error(
201
+ `Invalid access_mode: ${accessMode}. Supported access modes are ` +
202
+ `[${validAccessModes.join(', ')}].`
203
+ );
204
+ }
205
+
206
+ // Build payload
207
+ const payload = JSON.stringify({
208
+ query,
209
+ access_mode: accessMode || 'update',
210
+ parameters: parameters || {},
211
+ });
212
+
213
+ let response;
214
+ try {
215
+ response = await httpRequest(this._queryEndpoint, {
216
+ method: 'POST',
217
+ body: payload,
218
+ timeout: this._timeout,
219
+ headers: {
220
+ 'Content-Type': 'application/json',
221
+ 'Content-Length': Buffer.byteLength(payload),
222
+ },
223
+ });
224
+ } catch (e) {
225
+ throw new Error(
226
+ `Could not execute query: ${query}. Error code: ${ERR_NETWORK}. ` +
227
+ `Details: ${e.message}`
228
+ );
229
+ }
230
+
231
+ if (response.statusCode !== 200) {
232
+ throw new Error(
233
+ `Failed to execute query: ${query}. Http code: ${response.statusCode}, ` +
234
+ `Response: ${response.body.toString()}`
235
+ );
236
+ }
237
+
238
+ // The response body is a protobuf-serialized QueryResponse.
239
+ // Use the native binding to deserialize it.
240
+ if (!nativeBinding || !nativeBinding.NodeQueryResult) {
241
+ throw new Error(
242
+ 'Native binding is required for Session but was not loaded. ' +
243
+ 'Please ensure the neug native addon is built and available.'
244
+ );
245
+ }
246
+ const nativeResult = nativeBinding.NodeQueryResult.fromString(response.body);
247
+ return new QueryResult(nativeResult);
248
+ }
249
+
250
+ /**
251
+ * Get the service status of the NeuG server.
252
+ * @returns {Promise<Object>} The server status as a JSON object.
253
+ */
254
+ async serviceStatus() {
255
+ const response = await httpRequest(this._statusEndpoint, {
256
+ method: 'GET',
257
+ timeout: this._timeout,
258
+ });
259
+ return JSON.parse(response.body.toString());
260
+ }
261
+
262
+ /**
263
+ * Get the schema of the NeuG database from the server.
264
+ * @returns {Promise<Object>} The schema as a JSON object.
265
+ */
266
+ async getSchema() {
267
+ const response = await httpRequest(this._schemaEndpoint, {
268
+ method: 'GET',
269
+ timeout: this._timeout,
270
+ });
271
+ return JSON.parse(response.body.toString());
272
+ }
273
+ }
274
+
275
+ module.exports = { Session };
package/lib/utils.js ADDED
@@ -0,0 +1,46 @@
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
+ /**
19
+ * Convert a mode string to a human-readable string.
20
+ * @param {string} mode - The mode string.
21
+ * @returns {string} The readable mode string.
22
+ */
23
+ function readable(mode) {
24
+ if (['r', 'read', 'read-only', 'read_only'].includes(mode)) {
25
+ return 'read-only';
26
+ } else if (['w', 'rw', 'write', 'readwrite', 'read-write', 'read_write'].includes(mode)) {
27
+ return 'read-write';
28
+ }
29
+ throw new Error(
30
+ `Invalid mode: ${mode}. Must be one of: r, read, w, rw, write, readwrite.`
31
+ );
32
+ }
33
+
34
+ /** Valid access modes for query execution. */
35
+ const validAccessModes = ['read', 'r', 'insert', 'i', 'update', 'u', 'schema', 's'];
36
+
37
+ /**
38
+ * Check if the given access mode string is valid.
39
+ * @param {string} mode - The access mode to check.
40
+ * @returns {boolean} True if valid.
41
+ */
42
+ function isAccessModeValid(mode) {
43
+ return validAccessModes.includes(mode);
44
+ }
45
+
46
+ module.exports = { readable, validAccessModes, isAccessModeValid };
package/lib/version.js ADDED
@@ -0,0 +1,53 @@
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 fs = require('fs');
19
+ const path = require('path');
20
+
21
+ /**
22
+ * Read the version from NEUG_VERSION file or package.json.
23
+ * @returns {string} The version string.
24
+ */
25
+ function loadVersion() {
26
+ // Try reading from NEUG_VERSION file at repo root
27
+ const repoRoot = path.resolve(__dirname, '..', '..', '..');
28
+ const versionFile = path.join(repoRoot, 'NEUG_VERSION');
29
+ try {
30
+ if (fs.existsSync(versionFile)) {
31
+ return fs.readFileSync(versionFile, 'utf-8').trim();
32
+ }
33
+ } catch {
34
+ // ignore
35
+ }
36
+
37
+ // Try reading from package.json
38
+ try {
39
+ const pkgPath = path.join(__dirname, '..', 'package.json');
40
+ if (fs.existsSync(pkgPath)) {
41
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
42
+ if (pkg.version) return pkg.version;
43
+ }
44
+ } catch {
45
+ // ignore
46
+ }
47
+
48
+ return '0.0.0';
49
+ }
50
+
51
+ const version = loadVersion();
52
+
53
+ module.exports = { version };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@graphscope-neug/darwin-arm64",
3
+ "version": "0.1.2",
4
+ "description": "NeuG Graph Database Node.js bindings - macOS ARM64",
5
+ "main": "lib/index.js",
6
+ "scripts": {
7
+ "test": "node --test tests/"
8
+ },
9
+ "keywords": [
10
+ "graph",
11
+ "database",
12
+ "embedded",
13
+ "cypher",
14
+ "neug",
15
+ "native",
16
+ "addon",
17
+ "napi"
18
+ ],
19
+ "author": "GraphScope Team",
20
+ "license": "Apache-2.0",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/alibaba/neug"
24
+ },
25
+ "engines": {
26
+ "node": ">=18.0.0"
27
+ },
28
+ "dependencies": {
29
+ "node-addon-api": "^8.0.0"
30
+ },
31
+ "files": [
32
+ "lib/",
33
+ "build/Release/",
34
+ "src/",
35
+ "CMakeLists.txt",
36
+ "README.md"
37
+ ],
38
+ "publishConfig": { "access": "public" },
39
+ "os": ["darwin"],
40
+ "cpu": ["arm64"]
41
+ }
@@ -0,0 +1,65 @@
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
+ #include <napi.h>
17
+
18
+ #include <iostream>
19
+ #include <string>
20
+
21
+ #include <glog/logging.h>
22
+
23
+ #include "neug/utils/exception/exception.h"
24
+ #include "node_connection.h"
25
+ #include "node_database.h"
26
+ #include "node_query_request.h"
27
+ #include "node_query_result.h"
28
+
29
+ namespace neug {
30
+
31
+ void SetupLogging() {
32
+ google::InitGoogleLogging("neug");
33
+ const char* debug = std::getenv("DEBUG");
34
+
35
+ if (debug) {
36
+ std::string mode = debug;
37
+ if (mode == "1" || mode == "true" || mode == "ON") {
38
+ FLAGS_minloglevel = 0; // 0 for verbose
39
+ FLAGS_logtostderr = true; // Log to stderr
40
+ } else {
41
+ std::cerr << "Invalid DEBUG value: " << mode
42
+ << ". Expected '1', 'true', or 'ON'." << std::endl;
43
+ FLAGS_minloglevel = 2; // 2 for error
44
+ FLAGS_logtostderr = false; // Log to file instead of stderr
45
+ }
46
+ } else {
47
+ FLAGS_minloglevel = 2; // 2 for error
48
+ FLAGS_logtostderr = false; // Log to file instead of stderr
49
+ }
50
+ }
51
+
52
+ } // namespace neug
53
+
54
+ static Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
55
+ neug::NodeDatabase::Init(env, exports);
56
+ neug::NodeConnection::Init(env, exports);
57
+ neug::NodeQueryResult::Init(env, exports);
58
+ neug::NodeQueryRequest::Init(env, exports);
59
+
60
+ neug::SetupLogging();
61
+
62
+ return exports;
63
+ }
64
+
65
+ NODE_API_MODULE(neug_node_bind, InitAll)