@tursodatabase/database 0.1.4-pre.5

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,82 @@
1
+ # @tursodatabase/database
2
+
3
+ The next evolution of SQLite: A high-performance, SQLite-compatible database library for Node.js
4
+
5
+ ## Features
6
+
7
+ - **SQLite Compatible**: Drop-in replacement for better-sqlite3 with familiar API
8
+ - **High Performance**: Built with Rust for maximum speed and efficiency
9
+ - **In-Process**: No network overhead, runs directly in your Node.js process
10
+ - **TypeScript Support**: Full TypeScript definitions included
11
+ - **Cross-Platform**: Supports Linux, macOS, Windows and browsers (through WebAssembly)
12
+ - **Transaction Support**: Full ACID transactions with rollback support
13
+ - **Prepared Statements**: Optimized query execution with parameter binding
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @tursodatabase/database
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### In-Memory Database
24
+
25
+ ```javascript
26
+ import Database from '@tursodatabase/database';
27
+
28
+ // Create an in-memory database
29
+ const db = new Database(':memory:');
30
+
31
+ // Create a table
32
+ db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)');
33
+
34
+ // Insert data
35
+ const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
36
+ insert.run('Alice', 'alice@example.com');
37
+ insert.run('Bob', 'bob@example.com');
38
+
39
+ // Query data
40
+ const users = db.prepare('SELECT * FROM users').all();
41
+ console.log(users);
42
+ // Output: [
43
+ // { id: 1, name: 'Alice', email: 'alice@example.com' },
44
+ // { id: 2, name: 'Bob', email: 'bob@example.com' }
45
+ // ]
46
+ ```
47
+
48
+ ### File-Based Database
49
+
50
+ ```javascript
51
+ import Database from '@tursodatabase/database';
52
+
53
+ // Create or open a database file
54
+ const db = new Database('my-database.db');
55
+
56
+ // Create a table
57
+ db.exec(`
58
+ CREATE TABLE IF NOT EXISTS posts (
59
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
60
+ title TEXT NOT NULL,
61
+ content TEXT,
62
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
63
+ )
64
+ `);
65
+
66
+ // Insert a post
67
+ const insertPost = db.prepare('INSERT INTO posts (title, content) VALUES (?, ?)');
68
+ const result = insertPost.run('Hello World', 'This is my first blog post!');
69
+
70
+ console.log(`Inserted post with ID: ${result.lastInsertRowid}`);
71
+ ```
72
+
73
+ ## API Reference
74
+ ## License
75
+
76
+ MIT
77
+
78
+ ## Support
79
+
80
+ - [GitHub Issues](https://github.com/tursodatabase/turso/issues)
81
+ - [Documentation](https://docs.turso.tech)
82
+ - [Discord Community](https://discord.gg/turso)
package/browser.js ADDED
@@ -0,0 +1 @@
1
+ export * from '@tursodatabase/database-wasm32-wasi'
package/dist/bind.js ADDED
@@ -0,0 +1,64 @@
1
+ // Bind parameters to a statement.
2
+ //
3
+ // This function is used to bind parameters to a statement. It supports both
4
+ // named and positional parameters, and nested arrays.
5
+ //
6
+ // The `stmt` parameter is a statement object.
7
+ // The `params` parameter is an array of parameters.
8
+ //
9
+ // The function returns void.
10
+ export function bindParams(stmt, params) {
11
+ const len = params?.length;
12
+ if (len === 0) {
13
+ return;
14
+ }
15
+ if (len === 1) {
16
+ const param = params[0];
17
+ if (isPlainObject(param)) {
18
+ bindNamedParams(stmt, param);
19
+ return;
20
+ }
21
+ bindValue(stmt, 1, param);
22
+ return;
23
+ }
24
+ bindPositionalParams(stmt, params);
25
+ }
26
+ // Check if object is plain (no prototype chain)
27
+ function isPlainObject(obj) {
28
+ if (!obj || typeof obj !== 'object')
29
+ return false;
30
+ const proto = Object.getPrototypeOf(obj);
31
+ return proto === Object.prototype || proto === null;
32
+ }
33
+ // Handle named parameters
34
+ function bindNamedParams(stmt, paramObj) {
35
+ const paramCount = stmt.parameterCount();
36
+ for (let i = 1; i <= paramCount; i++) {
37
+ const paramName = stmt.parameterName(i);
38
+ if (paramName) {
39
+ const key = paramName.substring(1); // Remove ':' or '$' prefix
40
+ const value = paramObj[key];
41
+ if (value !== undefined) {
42
+ bindValue(stmt, i, value);
43
+ }
44
+ }
45
+ }
46
+ }
47
+ // Handle positional parameters (including nested arrays)
48
+ function bindPositionalParams(stmt, params) {
49
+ let bindIndex = 1;
50
+ for (let i = 0; i < params.length; i++) {
51
+ const param = params[i];
52
+ if (Array.isArray(param)) {
53
+ for (let j = 0; j < param.length; j++) {
54
+ bindValue(stmt, bindIndex++, param[j]);
55
+ }
56
+ }
57
+ else {
58
+ bindValue(stmt, bindIndex++, param);
59
+ }
60
+ }
61
+ }
62
+ function bindValue(stmt, index, value) {
63
+ stmt.bindAt(index, value);
64
+ }
@@ -0,0 +1,352 @@
1
+ import { Database as NativeDB } from "#entry-point";
2
+ import { bindParams } from "./bind.js";
3
+ import { SqliteError } from "./sqlite-error.js";
4
+ // Step result constants
5
+ const STEP_ROW = 1;
6
+ const STEP_DONE = 2;
7
+ const STEP_IO = 3;
8
+ const convertibleErrorTypes = { TypeError };
9
+ const CONVERTIBLE_ERROR_PREFIX = "[TURSO_CONVERT_TYPE]";
10
+ function convertError(err) {
11
+ if ((err.code ?? "").startsWith(CONVERTIBLE_ERROR_PREFIX)) {
12
+ return createErrorByName(err.code.substring(CONVERTIBLE_ERROR_PREFIX.length), err.message);
13
+ }
14
+ return new SqliteError(err.message, err.code, err.rawCode);
15
+ }
16
+ function createErrorByName(name, message) {
17
+ const ErrorConstructor = convertibleErrorTypes[name];
18
+ if (!ErrorConstructor) {
19
+ throw new Error(`unknown error type ${name} from Turso`);
20
+ }
21
+ return new ErrorConstructor(message);
22
+ }
23
+ /**
24
+ * Database represents a connection that can prepare and execute SQL statements.
25
+ */
26
+ class Database {
27
+ db;
28
+ memory;
29
+ open;
30
+ /**
31
+ * Creates a new database connection. If the database file pointed to by `path` does not exists, it will be created.
32
+ *
33
+ * @constructor
34
+ * @param {string} path - Path to the database file.
35
+ * @param {Object} opts - Options for database behavior.
36
+ * @param {boolean} [opts.readonly=false] - Open the database in read-only mode.
37
+ * @param {boolean} [opts.fileMustExist=false] - If true, throws if database file does not exist.
38
+ * @param {number} [opts.timeout=0] - Timeout duration in milliseconds for database operations. Defaults to 0 (no timeout).
39
+ */
40
+ constructor(path, opts = {}) {
41
+ opts.readonly = opts.readonly === undefined ? false : opts.readonly;
42
+ opts.fileMustExist =
43
+ opts.fileMustExist === undefined ? false : opts.fileMustExist;
44
+ opts.timeout = opts.timeout === undefined ? 0 : opts.timeout;
45
+ const db = new NativeDB(path);
46
+ this.initialize(db, opts.path, opts.readonly);
47
+ }
48
+ static create() {
49
+ return Object.create(this.prototype);
50
+ }
51
+ initialize(db, name, readonly) {
52
+ this.db = db;
53
+ this.memory = db.memory;
54
+ Object.defineProperties(this, {
55
+ inTransaction: {
56
+ get() {
57
+ throw new Error("not implemented");
58
+ },
59
+ },
60
+ name: {
61
+ get() {
62
+ return name;
63
+ },
64
+ },
65
+ readonly: {
66
+ get() {
67
+ return readonly;
68
+ },
69
+ },
70
+ open: {
71
+ get() {
72
+ return this.db.open;
73
+ },
74
+ },
75
+ });
76
+ }
77
+ /**
78
+ * Prepares a SQL statement for execution.
79
+ *
80
+ * @param {string} sql - The SQL statement string to prepare.
81
+ */
82
+ prepare(sql) {
83
+ if (!this.open) {
84
+ throw new TypeError("The database connection is not open");
85
+ }
86
+ if (!sql) {
87
+ throw new RangeError("The supplied SQL string contains no statements");
88
+ }
89
+ try {
90
+ return new Statement(this.db.prepare(sql), this);
91
+ }
92
+ catch (err) {
93
+ throw convertError(err);
94
+ }
95
+ }
96
+ /**
97
+ * Returns a function that executes the given function in a transaction.
98
+ *
99
+ * @param {function} fn - The function to wrap in a transaction.
100
+ */
101
+ transaction(fn) {
102
+ if (typeof fn !== "function")
103
+ throw new TypeError("Expected first argument to be a function");
104
+ const db = this;
105
+ const wrapTxn = (mode) => {
106
+ return (...bindParameters) => {
107
+ db.exec("BEGIN " + mode);
108
+ try {
109
+ const result = fn(...bindParameters);
110
+ db.exec("COMMIT");
111
+ return result;
112
+ }
113
+ catch (err) {
114
+ db.exec("ROLLBACK");
115
+ throw err;
116
+ }
117
+ };
118
+ };
119
+ const properties = {
120
+ default: { value: wrapTxn("") },
121
+ deferred: { value: wrapTxn("DEFERRED") },
122
+ immediate: { value: wrapTxn("IMMEDIATE") },
123
+ exclusive: { value: wrapTxn("EXCLUSIVE") },
124
+ database: { value: this, enumerable: true },
125
+ };
126
+ Object.defineProperties(properties.default.value, properties);
127
+ Object.defineProperties(properties.deferred.value, properties);
128
+ Object.defineProperties(properties.immediate.value, properties);
129
+ Object.defineProperties(properties.exclusive.value, properties);
130
+ return properties.default.value;
131
+ }
132
+ pragma(source, options) {
133
+ if (options == null)
134
+ options = {};
135
+ if (typeof source !== "string")
136
+ throw new TypeError("Expected first argument to be a string");
137
+ if (typeof options !== "object")
138
+ throw new TypeError("Expected second argument to be an options object");
139
+ const pragma = `PRAGMA ${source}`;
140
+ const stmt = this.prepare(pragma);
141
+ const results = stmt.all();
142
+ return results;
143
+ }
144
+ backup(filename, options) {
145
+ throw new Error("not implemented");
146
+ }
147
+ serialize(options) {
148
+ throw new Error("not implemented");
149
+ }
150
+ function(name, options, fn) {
151
+ throw new Error("not implemented");
152
+ }
153
+ aggregate(name, options) {
154
+ throw new Error("not implemented");
155
+ }
156
+ table(name, factory) {
157
+ throw new Error("not implemented");
158
+ }
159
+ loadExtension(path) {
160
+ throw new Error("not implemented");
161
+ }
162
+ maxWriteReplicationIndex() {
163
+ throw new Error("not implemented");
164
+ }
165
+ /**
166
+ * Executes a SQL statement.
167
+ *
168
+ * @param {string} sql - The SQL statement string to execute.
169
+ */
170
+ exec(sql) {
171
+ if (!this.open) {
172
+ throw new TypeError("The database connection is not open");
173
+ }
174
+ try {
175
+ this.db.batch(sql);
176
+ }
177
+ catch (err) {
178
+ throw convertError(err);
179
+ }
180
+ }
181
+ /**
182
+ * Interrupts the database connection.
183
+ */
184
+ interrupt() {
185
+ throw new Error("not implemented");
186
+ }
187
+ /**
188
+ * Closes the database connection.
189
+ */
190
+ close() {
191
+ this.db.close();
192
+ }
193
+ }
194
+ /**
195
+ * Statement represents a prepared SQL statement that can be executed.
196
+ */
197
+ class Statement {
198
+ stmt;
199
+ db;
200
+ constructor(stmt, database) {
201
+ this.stmt = stmt;
202
+ this.db = database;
203
+ }
204
+ /**
205
+ * Toggle raw mode.
206
+ *
207
+ * @param raw Enable or disable raw mode. If you don't pass the parameter, raw mode is enabled.
208
+ */
209
+ raw(raw) {
210
+ this.stmt.raw(raw);
211
+ return this;
212
+ }
213
+ /**
214
+ * Toggle pluck mode.
215
+ *
216
+ * @param pluckMode Enable or disable pluck mode. If you don't pass the parameter, pluck mode is enabled.
217
+ */
218
+ pluck(pluckMode) {
219
+ this.stmt.pluck(pluckMode);
220
+ return this;
221
+ }
222
+ get source() {
223
+ throw new Error("not implemented");
224
+ }
225
+ get reader() {
226
+ throw new Error("not implemented");
227
+ }
228
+ get database() {
229
+ return this.db;
230
+ }
231
+ /**
232
+ * Executes the SQL statement and returns an info object.
233
+ */
234
+ async run(...bindParameters) {
235
+ const totalChangesBefore = this.db.db.totalChanges();
236
+ this.stmt.reset();
237
+ bindParams(this.stmt, bindParameters);
238
+ while (true) {
239
+ const stepResult = this.stmt.step();
240
+ if (stepResult === STEP_IO) {
241
+ await this.db.db.ioLoopAsync();
242
+ continue;
243
+ }
244
+ if (stepResult === STEP_DONE) {
245
+ break;
246
+ }
247
+ if (stepResult === STEP_ROW) {
248
+ // For run(), we don't need the row data, just continue
249
+ continue;
250
+ }
251
+ }
252
+ const lastInsertRowid = this.db.db.lastInsertRowid();
253
+ const changes = this.db.db.totalChanges() === totalChangesBefore ? 0 : this.db.db.changes();
254
+ return { changes, lastInsertRowid };
255
+ }
256
+ /**
257
+ * Executes the SQL statement and returns the first row.
258
+ *
259
+ * @param bindParameters - The bind parameters for executing the statement.
260
+ */
261
+ async get(...bindParameters) {
262
+ this.stmt.reset();
263
+ bindParams(this.stmt, bindParameters);
264
+ while (true) {
265
+ const stepResult = this.stmt.step();
266
+ if (stepResult === STEP_IO) {
267
+ await this.db.db.ioLoopAsync();
268
+ continue;
269
+ }
270
+ if (stepResult === STEP_DONE) {
271
+ return undefined;
272
+ }
273
+ if (stepResult === STEP_ROW) {
274
+ return this.stmt.row();
275
+ }
276
+ }
277
+ }
278
+ /**
279
+ * Executes the SQL statement and returns an iterator to the resulting rows.
280
+ *
281
+ * @param bindParameters - The bind parameters for executing the statement.
282
+ */
283
+ async *iterate(...bindParameters) {
284
+ this.stmt.reset();
285
+ bindParams(this.stmt, bindParameters);
286
+ while (true) {
287
+ const stepResult = this.stmt.step();
288
+ if (stepResult === STEP_IO) {
289
+ await this.db.db.ioLoopAsync();
290
+ continue;
291
+ }
292
+ if (stepResult === STEP_DONE) {
293
+ break;
294
+ }
295
+ if (stepResult === STEP_ROW) {
296
+ yield this.stmt.row();
297
+ }
298
+ }
299
+ }
300
+ /**
301
+ * Executes the SQL statement and returns an array of the resulting rows.
302
+ *
303
+ * @param bindParameters - The bind parameters for executing the statement.
304
+ */
305
+ async all(...bindParameters) {
306
+ this.stmt.reset();
307
+ bindParams(this.stmt, bindParameters);
308
+ const rows = [];
309
+ while (true) {
310
+ const stepResult = this.stmt.step();
311
+ if (stepResult === STEP_IO) {
312
+ await this.db.db.ioLoopAsync();
313
+ continue;
314
+ }
315
+ if (stepResult === STEP_DONE) {
316
+ break;
317
+ }
318
+ if (stepResult === STEP_ROW) {
319
+ rows.push(this.stmt.row());
320
+ }
321
+ }
322
+ return rows;
323
+ }
324
+ /**
325
+ * Interrupts the statement.
326
+ */
327
+ interrupt() {
328
+ throw new Error("not implemented");
329
+ }
330
+ /**
331
+ * Returns the columns in the result set returned by this prepared statement.
332
+ */
333
+ columns() {
334
+ throw new Error("not implemented");
335
+ }
336
+ /**
337
+ * Binds the given parameters to the statement _permanently_
338
+ *
339
+ * @param bindParameters - The bind parameters for binding the statement.
340
+ * @returns this - Statement with binded parameters
341
+ */
342
+ bind(...bindParameters) {
343
+ try {
344
+ bindParams(this.stmt, bindParameters);
345
+ return this;
346
+ }
347
+ catch (err) {
348
+ throw convertError(err);
349
+ }
350
+ }
351
+ }
352
+ export { Database, SqliteError };
@@ -0,0 +1,12 @@
1
+ export class SqliteError extends Error {
2
+ name;
3
+ code;
4
+ rawCode;
5
+ constructor(message, code, rawCode) {
6
+ super(message);
7
+ this.name = 'SqliteError';
8
+ this.code = code;
9
+ this.rawCode = rawCode;
10
+ Error.captureStackTrace(this, SqliteError);
11
+ }
12
+ }
package/dist/sync.js ADDED
@@ -0,0 +1,346 @@
1
+ import { Database as NativeDB } from "#entry-point";
2
+ import { bindParams } from "./bind.js";
3
+ import { SqliteError } from "./sqlite-error.js";
4
+ // Step result constants
5
+ const STEP_ROW = 1;
6
+ const STEP_DONE = 2;
7
+ const STEP_IO = 3;
8
+ const convertibleErrorTypes = { TypeError };
9
+ const CONVERTIBLE_ERROR_PREFIX = "[TURSO_CONVERT_TYPE]";
10
+ function convertError(err) {
11
+ if ((err.code ?? "").startsWith(CONVERTIBLE_ERROR_PREFIX)) {
12
+ return createErrorByName(err.code.substring(CONVERTIBLE_ERROR_PREFIX.length), err.message);
13
+ }
14
+ return new SqliteError(err.message, err.code, err.rawCode);
15
+ }
16
+ function createErrorByName(name, message) {
17
+ const ErrorConstructor = convertibleErrorTypes[name];
18
+ if (!ErrorConstructor) {
19
+ throw new Error(`unknown error type ${name} from Turso`);
20
+ }
21
+ return new ErrorConstructor(message);
22
+ }
23
+ /**
24
+ * Database represents a connection that can prepare and execute SQL statements.
25
+ */
26
+ class Database {
27
+ db;
28
+ memory;
29
+ open;
30
+ /**
31
+ * Creates a new database connection. If the database file pointed to by `path` does not exists, it will be created.
32
+ *
33
+ * @constructor
34
+ * @param {string} path - Path to the database file.
35
+ * @param {Object} opts - Options for database behavior.
36
+ * @param {boolean} [opts.readonly=false] - Open the database in read-only mode.
37
+ * @param {boolean} [opts.fileMustExist=false] - If true, throws if database file does not exist.
38
+ * @param {number} [opts.timeout=0] - Timeout duration in milliseconds for database operations. Defaults to 0 (no timeout).
39
+ */
40
+ constructor(path, opts = {}) {
41
+ opts.readonly = opts.readonly === undefined ? false : opts.readonly;
42
+ opts.fileMustExist =
43
+ opts.fileMustExist === undefined ? false : opts.fileMustExist;
44
+ opts.timeout = opts.timeout === undefined ? 0 : opts.timeout;
45
+ this.db = new NativeDB(path);
46
+ this.memory = this.db.memory;
47
+ const db = this.db;
48
+ Object.defineProperties(this, {
49
+ inTransaction: {
50
+ get() {
51
+ throw new Error("not implemented");
52
+ },
53
+ },
54
+ name: {
55
+ get() {
56
+ return path;
57
+ },
58
+ },
59
+ readonly: {
60
+ get() {
61
+ return opts.readonly;
62
+ },
63
+ },
64
+ open: {
65
+ get() {
66
+ return this.db.open;
67
+ },
68
+ },
69
+ });
70
+ }
71
+ /**
72
+ * Prepares a SQL statement for execution.
73
+ *
74
+ * @param {string} sql - The SQL statement string to prepare.
75
+ */
76
+ prepare(sql) {
77
+ if (!this.open) {
78
+ throw new TypeError("The database connection is not open");
79
+ }
80
+ if (!sql) {
81
+ throw new RangeError("The supplied SQL string contains no statements");
82
+ }
83
+ try {
84
+ return new Statement(this.db.prepare(sql), this);
85
+ }
86
+ catch (err) {
87
+ throw convertError(err);
88
+ }
89
+ }
90
+ /**
91
+ * Returns a function that executes the given function in a transaction.
92
+ *
93
+ * @param {function} fn - The function to wrap in a transaction.
94
+ */
95
+ transaction(fn) {
96
+ if (typeof fn !== "function")
97
+ throw new TypeError("Expected first argument to be a function");
98
+ const db = this;
99
+ const wrapTxn = (mode) => {
100
+ return (...bindParameters) => {
101
+ db.exec("BEGIN " + mode);
102
+ try {
103
+ const result = fn(...bindParameters);
104
+ db.exec("COMMIT");
105
+ return result;
106
+ }
107
+ catch (err) {
108
+ db.exec("ROLLBACK");
109
+ throw err;
110
+ }
111
+ };
112
+ };
113
+ const properties = {
114
+ default: { value: wrapTxn("") },
115
+ deferred: { value: wrapTxn("DEFERRED") },
116
+ immediate: { value: wrapTxn("IMMEDIATE") },
117
+ exclusive: { value: wrapTxn("EXCLUSIVE") },
118
+ database: { value: this, enumerable: true },
119
+ };
120
+ Object.defineProperties(properties.default.value, properties);
121
+ Object.defineProperties(properties.deferred.value, properties);
122
+ Object.defineProperties(properties.immediate.value, properties);
123
+ Object.defineProperties(properties.exclusive.value, properties);
124
+ return properties.default.value;
125
+ }
126
+ pragma(source, options) {
127
+ if (options == null)
128
+ options = {};
129
+ if (typeof source !== "string")
130
+ throw new TypeError("Expected first argument to be a string");
131
+ if (typeof options !== "object")
132
+ throw new TypeError("Expected second argument to be an options object");
133
+ const pragma = `PRAGMA ${source}`;
134
+ const stmt = this.prepare(pragma);
135
+ const results = stmt.all();
136
+ return results;
137
+ }
138
+ backup(filename, options) {
139
+ throw new Error("not implemented");
140
+ }
141
+ serialize(options) {
142
+ throw new Error("not implemented");
143
+ }
144
+ function(name, options, fn) {
145
+ throw new Error("not implemented");
146
+ }
147
+ aggregate(name, options) {
148
+ throw new Error("not implemented");
149
+ }
150
+ table(name, factory) {
151
+ throw new Error("not implemented");
152
+ }
153
+ loadExtension(path) {
154
+ throw new Error("not implemented");
155
+ }
156
+ maxWriteReplicationIndex() {
157
+ throw new Error("not implemented");
158
+ }
159
+ /**
160
+ * Executes a SQL statement.
161
+ *
162
+ * @param {string} sql - The SQL statement string to execute.
163
+ */
164
+ exec(sql) {
165
+ if (!this.open) {
166
+ throw new TypeError("The database connection is not open");
167
+ }
168
+ try {
169
+ this.db.batch(sql);
170
+ }
171
+ catch (err) {
172
+ throw convertError(err);
173
+ }
174
+ }
175
+ /**
176
+ * Interrupts the database connection.
177
+ */
178
+ interrupt() {
179
+ throw new Error("not implemented");
180
+ }
181
+ /**
182
+ * Closes the database connection.
183
+ */
184
+ close() {
185
+ this.db.close();
186
+ }
187
+ }
188
+ /**
189
+ * Statement represents a prepared SQL statement that can be executed.
190
+ */
191
+ class Statement {
192
+ stmt;
193
+ db;
194
+ constructor(stmt, database) {
195
+ this.stmt = stmt;
196
+ this.db = database;
197
+ }
198
+ /**
199
+ * Toggle raw mode.
200
+ *
201
+ * @param raw Enable or disable raw mode. If you don't pass the parameter, raw mode is enabled.
202
+ */
203
+ raw(raw) {
204
+ this.stmt.raw(raw);
205
+ return this;
206
+ }
207
+ /**
208
+ * Toggle pluck mode.
209
+ *
210
+ * @param pluckMode Enable or disable pluck mode. If you don't pass the parameter, pluck mode is enabled.
211
+ */
212
+ pluck(pluckMode) {
213
+ this.stmt.pluck(pluckMode);
214
+ return this;
215
+ }
216
+ get source() {
217
+ throw new Error("not implemented");
218
+ }
219
+ get reader() {
220
+ throw new Error("not implemented");
221
+ }
222
+ get database() {
223
+ return this.db;
224
+ }
225
+ /**
226
+ * Executes the SQL statement and returns an info object.
227
+ */
228
+ run(...bindParameters) {
229
+ const totalChangesBefore = this.db.db.totalChanges();
230
+ this.stmt.reset();
231
+ bindParams(this.stmt, bindParameters);
232
+ for (;;) {
233
+ const stepResult = this.stmt.step();
234
+ if (stepResult === STEP_IO) {
235
+ this.db.db.ioLoopSync();
236
+ continue;
237
+ }
238
+ if (stepResult === STEP_DONE) {
239
+ break;
240
+ }
241
+ if (stepResult === STEP_ROW) {
242
+ // For run(), we don't need the row data, just continue
243
+ continue;
244
+ }
245
+ }
246
+ const lastInsertRowid = this.db.db.lastInsertRowid();
247
+ const changes = this.db.db.totalChanges() === totalChangesBefore ? 0 : this.db.db.changes();
248
+ return { changes, lastInsertRowid };
249
+ }
250
+ /**
251
+ * Executes the SQL statement and returns the first row.
252
+ *
253
+ * @param bindParameters - The bind parameters for executing the statement.
254
+ */
255
+ get(...bindParameters) {
256
+ this.stmt.reset();
257
+ bindParams(this.stmt, bindParameters);
258
+ for (;;) {
259
+ const stepResult = this.stmt.step();
260
+ if (stepResult === STEP_IO) {
261
+ this.db.db.ioLoopSync();
262
+ continue;
263
+ }
264
+ if (stepResult === STEP_DONE) {
265
+ return undefined;
266
+ }
267
+ if (stepResult === STEP_ROW) {
268
+ return this.stmt.row();
269
+ }
270
+ }
271
+ }
272
+ /**
273
+ * Executes the SQL statement and returns an iterator to the resulting rows.
274
+ *
275
+ * @param bindParameters - The bind parameters for executing the statement.
276
+ */
277
+ *iterate(...bindParameters) {
278
+ this.stmt.reset();
279
+ bindParams(this.stmt, bindParameters);
280
+ while (true) {
281
+ const stepResult = this.stmt.step();
282
+ if (stepResult === STEP_IO) {
283
+ this.db.db.ioLoopSync();
284
+ continue;
285
+ }
286
+ if (stepResult === STEP_DONE) {
287
+ break;
288
+ }
289
+ if (stepResult === STEP_ROW) {
290
+ yield this.stmt.row();
291
+ }
292
+ }
293
+ }
294
+ /**
295
+ * Executes the SQL statement and returns an array of the resulting rows.
296
+ *
297
+ * @param bindParameters - The bind parameters for executing the statement.
298
+ */
299
+ all(...bindParameters) {
300
+ this.stmt.reset();
301
+ bindParams(this.stmt, bindParameters);
302
+ const rows = [];
303
+ for (;;) {
304
+ const stepResult = this.stmt.step();
305
+ if (stepResult === STEP_IO) {
306
+ this.db.db.ioLoopSync();
307
+ continue;
308
+ }
309
+ if (stepResult === STEP_DONE) {
310
+ break;
311
+ }
312
+ if (stepResult === STEP_ROW) {
313
+ rows.push(this.stmt.row());
314
+ }
315
+ }
316
+ return rows;
317
+ }
318
+ /**
319
+ * Interrupts the statement.
320
+ */
321
+ interrupt() {
322
+ throw new Error("not implemented");
323
+ }
324
+ /**
325
+ * Returns the columns in the result set returned by this prepared statement.
326
+ */
327
+ columns() {
328
+ throw new Error("not implemented");
329
+ }
330
+ /**
331
+ * Binds the given parameters to the statement _permanently_
332
+ *
333
+ * @param bindParameters - The bind parameters for binding the statement.
334
+ * @returns this - Statement with binded parameters
335
+ */
336
+ bind(...bindParameters) {
337
+ try {
338
+ bindParams(this.stmt, bindParameters);
339
+ return this;
340
+ }
341
+ catch (err) {
342
+ throw convertError(err);
343
+ }
344
+ }
345
+ }
346
+ export { Database, SqliteError };
package/index.js ADDED
@@ -0,0 +1,398 @@
1
+ // prettier-ignore
2
+ /* eslint-disable */
3
+ // @ts-nocheck
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ import { createRequire } from 'node:module'
7
+ const require = createRequire(import.meta.url)
8
+ const __dirname = new URL('.', import.meta.url).pathname
9
+
10
+ const { readFileSync } = require('node:fs')
11
+ let nativeBinding = null
12
+ const loadErrors = []
13
+
14
+ const isMusl = () => {
15
+ let musl = false
16
+ if (process.platform === 'linux') {
17
+ musl = isMuslFromFilesystem()
18
+ if (musl === null) {
19
+ musl = isMuslFromReport()
20
+ }
21
+ if (musl === null) {
22
+ musl = isMuslFromChildProcess()
23
+ }
24
+ }
25
+ return musl
26
+ }
27
+
28
+ const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
29
+
30
+ const isMuslFromFilesystem = () => {
31
+ try {
32
+ return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
33
+ } catch {
34
+ return null
35
+ }
36
+ }
37
+
38
+ const isMuslFromReport = () => {
39
+ let report = null
40
+ if (typeof process.report?.getReport === 'function') {
41
+ process.report.excludeNetwork = true
42
+ report = process.report.getReport()
43
+ }
44
+ if (!report) {
45
+ return null
46
+ }
47
+ if (report.header && report.header.glibcVersionRuntime) {
48
+ return false
49
+ }
50
+ if (Array.isArray(report.sharedObjects)) {
51
+ if (report.sharedObjects.some(isFileMusl)) {
52
+ return true
53
+ }
54
+ }
55
+ return false
56
+ }
57
+
58
+ const isMuslFromChildProcess = () => {
59
+ try {
60
+ return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
61
+ } catch (e) {
62
+ // If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
63
+ return false
64
+ }
65
+ }
66
+
67
+ function requireNative() {
68
+ if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
69
+ try {
70
+ nativeBinding = require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH);
71
+ } catch (err) {
72
+ loadErrors.push(err)
73
+ }
74
+ } else if (process.platform === 'android') {
75
+ if (process.arch === 'arm64') {
76
+ try {
77
+ return require('./turso.android-arm64.node')
78
+ } catch (e) {
79
+ loadErrors.push(e)
80
+ }
81
+ try {
82
+ return require('@tursodatabase/database-android-arm64')
83
+ } catch (e) {
84
+ loadErrors.push(e)
85
+ }
86
+ } else if (process.arch === 'arm') {
87
+ try {
88
+ return require('./turso.android-arm-eabi.node')
89
+ } catch (e) {
90
+ loadErrors.push(e)
91
+ }
92
+ try {
93
+ return require('@tursodatabase/database-android-arm-eabi')
94
+ } catch (e) {
95
+ loadErrors.push(e)
96
+ }
97
+ } else {
98
+ loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
99
+ }
100
+ } else if (process.platform === 'win32') {
101
+ if (process.arch === 'x64') {
102
+ try {
103
+ return require('./turso.win32-x64-msvc.node')
104
+ } catch (e) {
105
+ loadErrors.push(e)
106
+ }
107
+ try {
108
+ return require('@tursodatabase/database-win32-x64-msvc')
109
+ } catch (e) {
110
+ loadErrors.push(e)
111
+ }
112
+ } else if (process.arch === 'ia32') {
113
+ try {
114
+ return require('./turso.win32-ia32-msvc.node')
115
+ } catch (e) {
116
+ loadErrors.push(e)
117
+ }
118
+ try {
119
+ return require('@tursodatabase/database-win32-ia32-msvc')
120
+ } catch (e) {
121
+ loadErrors.push(e)
122
+ }
123
+ } else if (process.arch === 'arm64') {
124
+ try {
125
+ return require('./turso.win32-arm64-msvc.node')
126
+ } catch (e) {
127
+ loadErrors.push(e)
128
+ }
129
+ try {
130
+ return require('@tursodatabase/database-win32-arm64-msvc')
131
+ } catch (e) {
132
+ loadErrors.push(e)
133
+ }
134
+ } else {
135
+ loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
136
+ }
137
+ } else if (process.platform === 'darwin') {
138
+ try {
139
+ return require('./turso.darwin-universal.node')
140
+ } catch (e) {
141
+ loadErrors.push(e)
142
+ }
143
+ try {
144
+ return require('@tursodatabase/database-darwin-universal')
145
+ } catch (e) {
146
+ loadErrors.push(e)
147
+ }
148
+ if (process.arch === 'x64') {
149
+ try {
150
+ return require('./turso.darwin-x64.node')
151
+ } catch (e) {
152
+ loadErrors.push(e)
153
+ }
154
+ try {
155
+ return require('@tursodatabase/database-darwin-x64')
156
+ } catch (e) {
157
+ loadErrors.push(e)
158
+ }
159
+ } else if (process.arch === 'arm64') {
160
+ try {
161
+ return require('./turso.darwin-arm64.node')
162
+ } catch (e) {
163
+ loadErrors.push(e)
164
+ }
165
+ try {
166
+ return require('@tursodatabase/database-darwin-arm64')
167
+ } catch (e) {
168
+ loadErrors.push(e)
169
+ }
170
+ } else {
171
+ loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
172
+ }
173
+ } else if (process.platform === 'freebsd') {
174
+ if (process.arch === 'x64') {
175
+ try {
176
+ return require('./turso.freebsd-x64.node')
177
+ } catch (e) {
178
+ loadErrors.push(e)
179
+ }
180
+ try {
181
+ return require('@tursodatabase/database-freebsd-x64')
182
+ } catch (e) {
183
+ loadErrors.push(e)
184
+ }
185
+ } else if (process.arch === 'arm64') {
186
+ try {
187
+ return require('./turso.freebsd-arm64.node')
188
+ } catch (e) {
189
+ loadErrors.push(e)
190
+ }
191
+ try {
192
+ return require('@tursodatabase/database-freebsd-arm64')
193
+ } catch (e) {
194
+ loadErrors.push(e)
195
+ }
196
+ } else {
197
+ loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
198
+ }
199
+ } else if (process.platform === 'linux') {
200
+ if (process.arch === 'x64') {
201
+ if (isMusl()) {
202
+ try {
203
+ return require('./turso.linux-x64-musl.node')
204
+ } catch (e) {
205
+ loadErrors.push(e)
206
+ }
207
+ try {
208
+ return require('@tursodatabase/database-linux-x64-musl')
209
+ } catch (e) {
210
+ loadErrors.push(e)
211
+ }
212
+ } else {
213
+ try {
214
+ return require('./turso.linux-x64-gnu.node')
215
+ } catch (e) {
216
+ loadErrors.push(e)
217
+ }
218
+ try {
219
+ return require('@tursodatabase/database-linux-x64-gnu')
220
+ } catch (e) {
221
+ loadErrors.push(e)
222
+ }
223
+ }
224
+ } else if (process.arch === 'arm64') {
225
+ if (isMusl()) {
226
+ try {
227
+ return require('./turso.linux-arm64-musl.node')
228
+ } catch (e) {
229
+ loadErrors.push(e)
230
+ }
231
+ try {
232
+ return require('@tursodatabase/database-linux-arm64-musl')
233
+ } catch (e) {
234
+ loadErrors.push(e)
235
+ }
236
+ } else {
237
+ try {
238
+ return require('./turso.linux-arm64-gnu.node')
239
+ } catch (e) {
240
+ loadErrors.push(e)
241
+ }
242
+ try {
243
+ return require('@tursodatabase/database-linux-arm64-gnu')
244
+ } catch (e) {
245
+ loadErrors.push(e)
246
+ }
247
+ }
248
+ } else if (process.arch === 'arm') {
249
+ if (isMusl()) {
250
+ try {
251
+ return require('./turso.linux-arm-musleabihf.node')
252
+ } catch (e) {
253
+ loadErrors.push(e)
254
+ }
255
+ try {
256
+ return require('@tursodatabase/database-linux-arm-musleabihf')
257
+ } catch (e) {
258
+ loadErrors.push(e)
259
+ }
260
+ } else {
261
+ try {
262
+ return require('./turso.linux-arm-gnueabihf.node')
263
+ } catch (e) {
264
+ loadErrors.push(e)
265
+ }
266
+ try {
267
+ return require('@tursodatabase/database-linux-arm-gnueabihf')
268
+ } catch (e) {
269
+ loadErrors.push(e)
270
+ }
271
+ }
272
+ } else if (process.arch === 'riscv64') {
273
+ if (isMusl()) {
274
+ try {
275
+ return require('./turso.linux-riscv64-musl.node')
276
+ } catch (e) {
277
+ loadErrors.push(e)
278
+ }
279
+ try {
280
+ return require('@tursodatabase/database-linux-riscv64-musl')
281
+ } catch (e) {
282
+ loadErrors.push(e)
283
+ }
284
+ } else {
285
+ try {
286
+ return require('./turso.linux-riscv64-gnu.node')
287
+ } catch (e) {
288
+ loadErrors.push(e)
289
+ }
290
+ try {
291
+ return require('@tursodatabase/database-linux-riscv64-gnu')
292
+ } catch (e) {
293
+ loadErrors.push(e)
294
+ }
295
+ }
296
+ } else if (process.arch === 'ppc64') {
297
+ try {
298
+ return require('./turso.linux-ppc64-gnu.node')
299
+ } catch (e) {
300
+ loadErrors.push(e)
301
+ }
302
+ try {
303
+ return require('@tursodatabase/database-linux-ppc64-gnu')
304
+ } catch (e) {
305
+ loadErrors.push(e)
306
+ }
307
+ } else if (process.arch === 's390x') {
308
+ try {
309
+ return require('./turso.linux-s390x-gnu.node')
310
+ } catch (e) {
311
+ loadErrors.push(e)
312
+ }
313
+ try {
314
+ return require('@tursodatabase/database-linux-s390x-gnu')
315
+ } catch (e) {
316
+ loadErrors.push(e)
317
+ }
318
+ } else {
319
+ loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
320
+ }
321
+ } else if (process.platform === 'openharmony') {
322
+ if (process.arch === 'arm64') {
323
+ try {
324
+ return require('./turso.linux-arm64-ohos.node')
325
+ } catch (e) {
326
+ loadErrors.push(e)
327
+ }
328
+ try {
329
+ return require('@tursodatabase/database-linux-arm64-ohos')
330
+ } catch (e) {
331
+ loadErrors.push(e)
332
+ }
333
+ } else if (process.arch === 'x64') {
334
+ try {
335
+ return require('./turso.linux-x64-ohos.node')
336
+ } catch (e) {
337
+ loadErrors.push(e)
338
+ }
339
+ try {
340
+ return require('@tursodatabase/database-linux-x64-ohos')
341
+ } catch (e) {
342
+ loadErrors.push(e)
343
+ }
344
+ } else if (process.arch === 'arm') {
345
+ try {
346
+ return require('./turso.linux-arm-ohos.node')
347
+ } catch (e) {
348
+ loadErrors.push(e)
349
+ }
350
+ try {
351
+ return require('@tursodatabase/database-linux-arm-ohos')
352
+ } catch (e) {
353
+ loadErrors.push(e)
354
+ }
355
+ } else {
356
+ loadErrors.push(new Error(`Unsupported architecture on OpenHarmony: ${process.arch}`))
357
+ }
358
+ } else {
359
+ loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
360
+ }
361
+ }
362
+
363
+ nativeBinding = requireNative()
364
+
365
+ if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
366
+ try {
367
+ nativeBinding = require('./turso.wasi.cjs')
368
+ } catch (err) {
369
+ if (process.env.NAPI_RS_FORCE_WASI) {
370
+ loadErrors.push(err)
371
+ }
372
+ }
373
+ if (!nativeBinding) {
374
+ try {
375
+ nativeBinding = require('@tursodatabase/database-wasm32-wasi')
376
+ } catch (err) {
377
+ if (process.env.NAPI_RS_FORCE_WASI) {
378
+ loadErrors.push(err)
379
+ }
380
+ }
381
+ }
382
+ }
383
+
384
+ if (!nativeBinding) {
385
+ if (loadErrors.length > 0) {
386
+ throw new Error(
387
+ `Cannot find native binding. ` +
388
+ `npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` +
389
+ 'Please try `npm i` again after removing both package-lock.json and node_modules directory.',
390
+ { cause: loadErrors }
391
+ )
392
+ }
393
+ throw new Error(`Failed to load native binding`)
394
+ }
395
+
396
+ const { Database, Statement } = nativeBinding
397
+ export { Database }
398
+ export { Statement }
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@tursodatabase/database",
3
+ "version": "0.1.4-pre.5",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/tursodatabase/turso"
7
+ },
8
+ "description": "The Turso database library",
9
+ "module": "./dist/promise.js",
10
+ "main": "./dist/promise.js",
11
+ "type": "module",
12
+ "exports": {
13
+ ".": "./dist/promise.js",
14
+ "./sync": "./dist/sync.js"
15
+ },
16
+ "files": [
17
+ "browser.js",
18
+ "index.js",
19
+ "dist/**"
20
+ ],
21
+ "types": "index.d.ts",
22
+ "napi": {
23
+ "binaryName": "turso",
24
+ "targets": [
25
+ "x86_64-unknown-linux-gnu",
26
+ "x86_64-pc-windows-msvc",
27
+ "universal-apple-darwin",
28
+ "wasm32-wasip1-threads"
29
+ ]
30
+ },
31
+ "license": "MIT",
32
+ "devDependencies": {
33
+ "@napi-rs/cli": "^3.0.4",
34
+ "@napi-rs/wasm-runtime": "^1.0.1",
35
+ "ava": "^6.0.1",
36
+ "better-sqlite3": "^11.9.1",
37
+ "typescript": "^5.9.2"
38
+ },
39
+ "ava": {
40
+ "timeout": "3m"
41
+ },
42
+ "engines": {
43
+ "node": ">= 10"
44
+ },
45
+ "scripts": {
46
+ "artifacts": "napi artifacts",
47
+ "build": "npm exec tsc && napi build --platform --release --esm",
48
+ "build:debug": "npm exec tsc && napi build --platform",
49
+ "prepublishOnly": "npm exec tsc && napi prepublish -t npm",
50
+ "test": "true",
51
+ "universal": "napi universalize",
52
+ "version": "napi version"
53
+ },
54
+ "packageManager": "yarn@4.9.2",
55
+ "imports": {
56
+ "#entry-point": {
57
+ "types": "./index.d.ts",
58
+ "browser": "./browser.js",
59
+ "node": "./index.js"
60
+ }
61
+ },
62
+ "optionalDependencies": {
63
+ "@tursodatabase/database-linux-x64-gnu": "0.1.4-pre.5",
64
+ "@tursodatabase/database-win32-x64-msvc": "0.1.4-pre.5",
65
+ "@tursodatabase/database-darwin-universal": "0.1.4-pre.5",
66
+ "@tursodatabase/database-wasm32-wasi": "0.1.4-pre.5"
67
+ }
68
+ }