@cqlite/node 0.3.1-rc1

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/README.md ADDED
@@ -0,0 +1,252 @@
1
+ # @cqlite/node
2
+
3
+ Node.js bindings for [CQLite](https://github.com/pmcfadin/cqlite) - a high-performance library for reading Apache Cassandra 5.0 SSTable files locally, without requiring a running Cassandra cluster.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @cqlite/node
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { Database } from '@cqlite/node';
15
+
16
+ // Open a database with schema
17
+ const db = await Database.open('path/to/sstables', { schema: 'schema.cql' });
18
+
19
+ // Execute queries
20
+ const result = await db.execute('SELECT * FROM keyspace.table LIMIT 10');
21
+ for (const row of result.rows) {
22
+ console.log(row.name);
23
+ }
24
+
25
+ await db.close();
26
+ ```
27
+
28
+ ## Features
29
+
30
+ - **Zero cluster dependency** - Read SSTable files directly from disk
31
+ - **Full CQL type support** - All primitive types, collections, UDTs, and frozen types
32
+ - **Native JavaScript types** - BigInt, Date, Buffer, Set, Map via `executeNative()`
33
+ - **Memory-efficient streaming** - Configure buffer sizes for large datasets
34
+ - **Thread-safe** - Safe concurrent access from multiple workers
35
+ - **Cross-platform** - Linux (x86_64, ARM64), macOS (Intel, Apple Silicon), Windows
36
+
37
+ ## Supported Platforms
38
+
39
+ | Platform | Architecture | Status |
40
+ |----------|--------------|--------|
41
+ | Linux | x86_64 | ✅ |
42
+ | Linux | ARM64 | ✅ |
43
+ | macOS | Intel (x86_64) | ✅ |
44
+ | macOS | Apple Silicon | ✅ |
45
+ | Windows | x64 | ✅ |
46
+
47
+ ## Requirements
48
+
49
+ - Node.js 18+
50
+ - Cassandra 5.0 SSTable files
51
+
52
+ ## API Reference
53
+
54
+ ### Opening a Database
55
+
56
+ ```typescript
57
+ import { Database } from '@cqlite/node';
58
+
59
+ // With schema file
60
+ const db = await Database.open('/path/to/sstables', {
61
+ schema: '/path/to/schema.cql',
62
+ });
63
+
64
+ // Always close when done
65
+ await db.close();
66
+ ```
67
+
68
+ The `close()` method is idempotent - safe to call multiple times.
69
+
70
+ ### Executing Queries
71
+
72
+ ```typescript
73
+ // Simple query - returns JSON-serializable values
74
+ const result = await db.execute('SELECT * FROM keyspace.table');
75
+ for (const row of result.rows) {
76
+ console.log(row);
77
+ }
78
+
79
+ // With LIMIT
80
+ const limited = await db.execute('SELECT name, age FROM users LIMIT 100');
81
+
82
+ // Access query metadata
83
+ console.log(`Rows returned: ${result.rowCount}`);
84
+ console.log(`Execution time: ${result.executionTimeMs}ms`);
85
+ console.log(`Columns: ${result.columns.map(c => c.name).join(', ')}`);
86
+ ```
87
+
88
+ ### Native Types with executeNative()
89
+
90
+ Use `executeNative()` to get native JavaScript types instead of JSON-serializable values:
91
+
92
+ ```typescript
93
+ const result = await db.executeNative('SELECT * FROM keyspace.table');
94
+ for (const row of result.rows) {
95
+ // BigInt for CQL bigint/varint
96
+ const balance: bigint = row.balance;
97
+
98
+ // Date for CQL timestamp
99
+ const created: Date = row.created;
100
+
101
+ // Buffer for CQL blob
102
+ const data: Buffer = row.blob_data;
103
+
104
+ // Set for CQL set
105
+ const tags: Set<string> = row.tags;
106
+
107
+ // Map for CQL map
108
+ const metadata: Map<string, string> = row.metadata;
109
+ }
110
+ ```
111
+
112
+ ### JSON Encoding (execute method)
113
+
114
+ The `execute()` method returns JSON-serializable values. For most types this works intuitively,
115
+ but `varint` and `decimal` types use a hex-based encoding to preserve arbitrary precision:
116
+
117
+ ```typescript
118
+ // Using execute() - hex encoding (deprecated)
119
+ const result = await db.execute('SELECT amount FROM transactions');
120
+ console.log(result.rows[0].amount);
121
+ // Varint: "0x7f" (127), "0xff" (-1), "0x0100" (256)
122
+ // Decimal: "decimal:2:0x7b" (1.23), "decimal:2:0xee29" (-45.67)
123
+
124
+ // Using executeNative() - proper types (recommended)
125
+ const native = await db.executeNative('SELECT amount FROM transactions');
126
+ console.log(native.rows[0].amount);
127
+ // Varint: 127n (BigInt)
128
+ // Decimal: "1.23" (human-readable string)
129
+ ```
130
+
131
+ **Hex Format Details:**
132
+ - `varint`: `"0x{hex}"` - Two's complement big-endian hex encoding
133
+ - `decimal`: `"decimal:{scale}:0x{hex}"` - Scale (decimal places) + hex-encoded unscaled value
134
+
135
+ **Recommendation:** Use `executeNative()` for most applications. The `execute()` method is
136
+ primarily for JSON serialization scenarios where you need raw, reversible encoding.
137
+
138
+ ### Column Metadata
139
+
140
+ Each query result includes column information:
141
+
142
+ ```typescript
143
+ const result = await db.execute('SELECT * FROM keyspace.table');
144
+
145
+ for (const col of result.columns) {
146
+ console.log(`${col.name}: ${col.dataType}`);
147
+ console.log(` nullable: ${col.nullable}`);
148
+ console.log(` position: ${col.position}`);
149
+ }
150
+ ```
151
+
152
+ ### Database Statistics
153
+
154
+ ```typescript
155
+ const stats = await db.getStats();
156
+ console.log(`SSTables: ${stats.totalSstables}`);
157
+ console.log(`Total rows: ${stats.totalRows}`);
158
+ console.log(`Memory: ${stats.memoryUsedBytes} bytes`);
159
+ ```
160
+
161
+ ### Error Handling
162
+
163
+ All errors include structured metadata for programmatic handling:
164
+
165
+ ```typescript
166
+ import { Database } from '@cqlite/node';
167
+
168
+ try {
169
+ const db = await Database.open('/path/to/data');
170
+ const result = await db.execute('SELECT * FROM keyspace.table');
171
+ } catch (e) {
172
+ // Error code for programmatic handling
173
+ console.log(`Code: ${e.code}`); // 'IO', 'SCHEMA', 'QUERY', 'PARSE', etc.
174
+
175
+ // Error category
176
+ console.log(`Category: ${e.category}`); // 'System', 'Schema', 'Query', etc.
177
+
178
+ // Whether the operation can be retried
179
+ console.log(`Recoverable: ${e.isRecoverable}`);
180
+
181
+ // Original error message
182
+ console.log(`Message: ${e.message}`);
183
+ }
184
+ ```
185
+
186
+ **Error Codes:**
187
+
188
+ | Code | Category | Description | Recoverable |
189
+ |------|----------|-------------|-------------|
190
+ | `IO` | System | File system errors | Yes |
191
+ | `SCHEMA` | Schema | Schema parsing/validation | No |
192
+ | `QUERY` | Query | Query execution failures | No |
193
+ | `PARSE` | Data | CQL syntax errors | No |
194
+ | `CONFIG` | Configuration | Invalid configuration | No |
195
+ | `STORAGE` | Storage | Storage engine errors | No |
196
+ | `NOT_FOUND` | NotFound | Table/resource not found | No |
197
+ | `INVALID_INPUT` | Logic | Invalid operation (e.g., closed db) | No |
198
+
199
+ ## Type Conversions
200
+
201
+ CQL types are automatically converted to JavaScript types:
202
+
203
+ | CQL Type | JavaScript Type | Notes |
204
+ |----------|-----------------|-------|
205
+ | `text`, `varchar`, `ascii` | `string` | |
206
+ | `int`, `smallint`, `tinyint` | `number` | |
207
+ | `bigint`, `varint`*, `counter` | `bigint` | via `executeNative()` |
208
+ | `float`, `double` | `number` | |
209
+ | `decimal`* | `string` | Preserves precision |
210
+ | `boolean` | `boolean` | |
211
+ | `blob` | `Buffer` | via `executeNative()` |
212
+ | `timestamp` | `Date` | via `executeNative()` |
213
+ | `date` | `Date` | via `executeNative()` |
214
+ | `time` | `bigint` | Nanoseconds since midnight, via `executeNative()` |
215
+ | `duration` | `object` | `{ months, days, nanos }` |
216
+ | `uuid`, `timeuuid` | `string` | Lowercase formatted |
217
+ | `inet` | `string` | IP address string |
218
+ | `list<T>` | `T[]` | |
219
+ | `set<T>` | `Set<T>` | via `executeNative()` |
220
+ | `map<K,V>` | `Map<K,V>` | via `executeNative()` |
221
+ | `tuple<...>` | `[...]` | Array |
222
+ | `frozen<T>` | Inner type | Unwrapped |
223
+ | UDT | `object` | With `_type` and `_keyspace` fields |
224
+
225
+ \* **Note:** With `execute()`, `varint` returns `"0x{hex}"` and `decimal` returns `"decimal:{scale}:0x{hex}"`.
226
+ Use `executeNative()` for human-readable formats.
227
+
228
+ ## Examples
229
+
230
+ See the [examples/](examples/) directory for complete working examples:
231
+
232
+ - [basic-query.ts](examples/basic-query.ts) - Simple SELECT queries
233
+ - [type-handling.ts](examples/type-handling.ts) - Working with CQL types
234
+ - [error-handling.ts](examples/error-handling.ts) - Error handling patterns
235
+ - [streaming.ts](examples/streaming.ts) - Large result handling
236
+ - [performance.ts](examples/performance.ts) - Memory-optimized usage
237
+
238
+ ## Resources
239
+
240
+ - [TypeScript Definitions](lib/index.d.ts) - Complete API type hints
241
+ - [Main Project README](../../README.md) - CQLite project overview
242
+ - [Issue Tracker](https://github.com/pmcfadin/cqlite/issues) - Report bugs or request features
243
+
244
+ ## License
245
+
246
+ MIT OR Apache-2.0
247
+
248
+ ## Links
249
+
250
+ - [GitHub Repository](https://github.com/pmcfadin/cqlite)
251
+ - [npm Package](https://www.npmjs.com/package/@cqlite/node)
252
+ - [Documentation](https://github.com/pmcfadin/cqlite/tree/main/docs)
Binary file
package/index.d.ts ADDED
File without changes
package/index.js ADDED
@@ -0,0 +1,318 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /* prettier-ignore */
4
+
5
+ /* auto-generated by NAPI-RS */
6
+
7
+ const { existsSync, readFileSync } = require('fs')
8
+ const { join } = require('path')
9
+
10
+ const { platform, arch } = process
11
+
12
+ let nativeBinding = null
13
+ let localFileExisted = false
14
+ let loadError = null
15
+
16
+ function isMusl() {
17
+ // For Node 10
18
+ if (!process.report || typeof process.report.getReport !== 'function') {
19
+ try {
20
+ const lddPath = require('child_process').execSync('which ldd').toString().trim()
21
+ return readFileSync(lddPath, 'utf8').includes('musl')
22
+ } catch (e) {
23
+ return true
24
+ }
25
+ } else {
26
+ const { glibcVersionRuntime } = process.report.getReport().header
27
+ return !glibcVersionRuntime
28
+ }
29
+ }
30
+
31
+ switch (platform) {
32
+ case 'android':
33
+ switch (arch) {
34
+ case 'arm64':
35
+ localFileExisted = existsSync(join(__dirname, 'cqlite-node.android-arm64.node'))
36
+ try {
37
+ if (localFileExisted) {
38
+ nativeBinding = require('./cqlite-node.android-arm64.node')
39
+ } else {
40
+ nativeBinding = require('@cqlite/node-android-arm64')
41
+ }
42
+ } catch (e) {
43
+ loadError = e
44
+ }
45
+ break
46
+ case 'arm':
47
+ localFileExisted = existsSync(join(__dirname, 'cqlite-node.android-arm-eabi.node'))
48
+ try {
49
+ if (localFileExisted) {
50
+ nativeBinding = require('./cqlite-node.android-arm-eabi.node')
51
+ } else {
52
+ nativeBinding = require('@cqlite/node-android-arm-eabi')
53
+ }
54
+ } catch (e) {
55
+ loadError = e
56
+ }
57
+ break
58
+ default:
59
+ throw new Error(`Unsupported architecture on Android ${arch}`)
60
+ }
61
+ break
62
+ case 'win32':
63
+ switch (arch) {
64
+ case 'x64':
65
+ localFileExisted = existsSync(
66
+ join(__dirname, 'cqlite-node.win32-x64-msvc.node')
67
+ )
68
+ try {
69
+ if (localFileExisted) {
70
+ nativeBinding = require('./cqlite-node.win32-x64-msvc.node')
71
+ } else {
72
+ nativeBinding = require('@cqlite/node-win32-x64-msvc')
73
+ }
74
+ } catch (e) {
75
+ loadError = e
76
+ }
77
+ break
78
+ case 'ia32':
79
+ localFileExisted = existsSync(
80
+ join(__dirname, 'cqlite-node.win32-ia32-msvc.node')
81
+ )
82
+ try {
83
+ if (localFileExisted) {
84
+ nativeBinding = require('./cqlite-node.win32-ia32-msvc.node')
85
+ } else {
86
+ nativeBinding = require('@cqlite/node-win32-ia32-msvc')
87
+ }
88
+ } catch (e) {
89
+ loadError = e
90
+ }
91
+ break
92
+ case 'arm64':
93
+ localFileExisted = existsSync(
94
+ join(__dirname, 'cqlite-node.win32-arm64-msvc.node')
95
+ )
96
+ try {
97
+ if (localFileExisted) {
98
+ nativeBinding = require('./cqlite-node.win32-arm64-msvc.node')
99
+ } else {
100
+ nativeBinding = require('@cqlite/node-win32-arm64-msvc')
101
+ }
102
+ } catch (e) {
103
+ loadError = e
104
+ }
105
+ break
106
+ default:
107
+ throw new Error(`Unsupported architecture on Windows: ${arch}`)
108
+ }
109
+ break
110
+ case 'darwin':
111
+ localFileExisted = existsSync(join(__dirname, 'cqlite-node.darwin-universal.node'))
112
+ try {
113
+ if (localFileExisted) {
114
+ nativeBinding = require('./cqlite-node.darwin-universal.node')
115
+ } else {
116
+ nativeBinding = require('@cqlite/node-darwin-universal')
117
+ }
118
+ break
119
+ } catch {}
120
+ switch (arch) {
121
+ case 'x64':
122
+ localFileExisted = existsSync(join(__dirname, 'cqlite-node.darwin-x64.node'))
123
+ try {
124
+ if (localFileExisted) {
125
+ nativeBinding = require('./cqlite-node.darwin-x64.node')
126
+ } else {
127
+ nativeBinding = require('@cqlite/node-darwin-x64')
128
+ }
129
+ } catch (e) {
130
+ loadError = e
131
+ }
132
+ break
133
+ case 'arm64':
134
+ localFileExisted = existsSync(
135
+ join(__dirname, 'cqlite-node.darwin-arm64.node')
136
+ )
137
+ try {
138
+ if (localFileExisted) {
139
+ nativeBinding = require('./cqlite-node.darwin-arm64.node')
140
+ } else {
141
+ nativeBinding = require('@cqlite/node-darwin-arm64')
142
+ }
143
+ } catch (e) {
144
+ loadError = e
145
+ }
146
+ break
147
+ default:
148
+ throw new Error(`Unsupported architecture on macOS: ${arch}`)
149
+ }
150
+ break
151
+ case 'freebsd':
152
+ if (arch !== 'x64') {
153
+ throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
154
+ }
155
+ localFileExisted = existsSync(join(__dirname, 'cqlite-node.freebsd-x64.node'))
156
+ try {
157
+ if (localFileExisted) {
158
+ nativeBinding = require('./cqlite-node.freebsd-x64.node')
159
+ } else {
160
+ nativeBinding = require('@cqlite/node-freebsd-x64')
161
+ }
162
+ } catch (e) {
163
+ loadError = e
164
+ }
165
+ break
166
+ case 'linux':
167
+ switch (arch) {
168
+ case 'x64':
169
+ if (isMusl()) {
170
+ localFileExisted = existsSync(
171
+ join(__dirname, 'cqlite-node.linux-x64-musl.node')
172
+ )
173
+ try {
174
+ if (localFileExisted) {
175
+ nativeBinding = require('./cqlite-node.linux-x64-musl.node')
176
+ } else {
177
+ nativeBinding = require('@cqlite/node-linux-x64-musl')
178
+ }
179
+ } catch (e) {
180
+ loadError = e
181
+ }
182
+ } else {
183
+ localFileExisted = existsSync(
184
+ join(__dirname, 'cqlite-node.linux-x64-gnu.node')
185
+ )
186
+ try {
187
+ if (localFileExisted) {
188
+ nativeBinding = require('./cqlite-node.linux-x64-gnu.node')
189
+ } else {
190
+ nativeBinding = require('@cqlite/node-linux-x64-gnu')
191
+ }
192
+ } catch (e) {
193
+ loadError = e
194
+ }
195
+ }
196
+ break
197
+ case 'arm64':
198
+ if (isMusl()) {
199
+ localFileExisted = existsSync(
200
+ join(__dirname, 'cqlite-node.linux-arm64-musl.node')
201
+ )
202
+ try {
203
+ if (localFileExisted) {
204
+ nativeBinding = require('./cqlite-node.linux-arm64-musl.node')
205
+ } else {
206
+ nativeBinding = require('@cqlite/node-linux-arm64-musl')
207
+ }
208
+ } catch (e) {
209
+ loadError = e
210
+ }
211
+ } else {
212
+ localFileExisted = existsSync(
213
+ join(__dirname, 'cqlite-node.linux-arm64-gnu.node')
214
+ )
215
+ try {
216
+ if (localFileExisted) {
217
+ nativeBinding = require('./cqlite-node.linux-arm64-gnu.node')
218
+ } else {
219
+ nativeBinding = require('@cqlite/node-linux-arm64-gnu')
220
+ }
221
+ } catch (e) {
222
+ loadError = e
223
+ }
224
+ }
225
+ break
226
+ case 'arm':
227
+ if (isMusl()) {
228
+ localFileExisted = existsSync(
229
+ join(__dirname, 'cqlite-node.linux-arm-musleabihf.node')
230
+ )
231
+ try {
232
+ if (localFileExisted) {
233
+ nativeBinding = require('./cqlite-node.linux-arm-musleabihf.node')
234
+ } else {
235
+ nativeBinding = require('@cqlite/node-linux-arm-musleabihf')
236
+ }
237
+ } catch (e) {
238
+ loadError = e
239
+ }
240
+ } else {
241
+ localFileExisted = existsSync(
242
+ join(__dirname, 'cqlite-node.linux-arm-gnueabihf.node')
243
+ )
244
+ try {
245
+ if (localFileExisted) {
246
+ nativeBinding = require('./cqlite-node.linux-arm-gnueabihf.node')
247
+ } else {
248
+ nativeBinding = require('@cqlite/node-linux-arm-gnueabihf')
249
+ }
250
+ } catch (e) {
251
+ loadError = e
252
+ }
253
+ }
254
+ break
255
+ case 'riscv64':
256
+ if (isMusl()) {
257
+ localFileExisted = existsSync(
258
+ join(__dirname, 'cqlite-node.linux-riscv64-musl.node')
259
+ )
260
+ try {
261
+ if (localFileExisted) {
262
+ nativeBinding = require('./cqlite-node.linux-riscv64-musl.node')
263
+ } else {
264
+ nativeBinding = require('@cqlite/node-linux-riscv64-musl')
265
+ }
266
+ } catch (e) {
267
+ loadError = e
268
+ }
269
+ } else {
270
+ localFileExisted = existsSync(
271
+ join(__dirname, 'cqlite-node.linux-riscv64-gnu.node')
272
+ )
273
+ try {
274
+ if (localFileExisted) {
275
+ nativeBinding = require('./cqlite-node.linux-riscv64-gnu.node')
276
+ } else {
277
+ nativeBinding = require('@cqlite/node-linux-riscv64-gnu')
278
+ }
279
+ } catch (e) {
280
+ loadError = e
281
+ }
282
+ }
283
+ break
284
+ case 's390x':
285
+ localFileExisted = existsSync(
286
+ join(__dirname, 'cqlite-node.linux-s390x-gnu.node')
287
+ )
288
+ try {
289
+ if (localFileExisted) {
290
+ nativeBinding = require('./cqlite-node.linux-s390x-gnu.node')
291
+ } else {
292
+ nativeBinding = require('@cqlite/node-linux-s390x-gnu')
293
+ }
294
+ } catch (e) {
295
+ loadError = e
296
+ }
297
+ break
298
+ default:
299
+ throw new Error(`Unsupported architecture on Linux: ${arch}`)
300
+ }
301
+ break
302
+ default:
303
+ throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
304
+ }
305
+
306
+ if (!nativeBinding) {
307
+ if (loadError) {
308
+ throw loadError
309
+ }
310
+ throw new Error(`Failed to load native binding`)
311
+ }
312
+
313
+ const { Database, PreparedStatement, StreamingResult, version } = nativeBinding
314
+
315
+ module.exports.Database = Database
316
+ module.exports.PreparedStatement = PreparedStatement
317
+ module.exports.StreamingResult = StreamingResult
318
+ module.exports.version = version