mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-07 02:04:21 +01:00
binding/javascript: Promise API by default
The JavaScript API is promise-based by default, but with a `sync` variant that tracks `better-sqlite3` API for compatibility.
This commit is contained in:
315
bindings/javascript/sync.js
Normal file
315
bindings/javascript/sync.js
Normal file
@@ -0,0 +1,315 @@
|
||||
"use strict";
|
||||
|
||||
const { Database: NativeDB } = require("./index.js");
|
||||
|
||||
const SqliteError = require("./sqlite-error.js");
|
||||
|
||||
const convertibleErrorTypes = { TypeError };
|
||||
const CONVERTIBLE_ERROR_PREFIX = "[TURSO_CONVERT_TYPE]";
|
||||
|
||||
function convertError(err) {
|
||||
if ((err.code ?? "").startsWith(CONVERTIBLE_ERROR_PREFIX)) {
|
||||
return createErrorByName(
|
||||
err.code.substring(CONVERTIBLE_ERROR_PREFIX.length),
|
||||
err.message,
|
||||
);
|
||||
}
|
||||
|
||||
return new SqliteError(err.message, err.code, err.rawCode);
|
||||
}
|
||||
|
||||
function createErrorByName(name, message) {
|
||||
const ErrorConstructor = convertibleErrorTypes[name];
|
||||
if (!ErrorConstructor) {
|
||||
throw new Error(`unknown error type ${name} from Turso`);
|
||||
}
|
||||
|
||||
return new ErrorConstructor(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Database represents a connection that can prepare and execute SQL statements.
|
||||
*/
|
||||
class Database {
|
||||
/**
|
||||
* Creates a new database connection. If the database file pointed to by `path` does not exists, it will be created.
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} path - Path to the database file.
|
||||
* @param {Object} opts - Options for database behavior.
|
||||
* @param {boolean} [opts.readonly=false] - Open the database in read-only mode.
|
||||
* @param {boolean} [opts.fileMustExist=false] - If true, throws if database file does not exist.
|
||||
* @param {number} [opts.timeout=0] - Timeout duration in milliseconds for database operations. Defaults to 0 (no timeout).
|
||||
*/
|
||||
constructor(path, opts = {}) {
|
||||
opts.readonly = opts.readonly === undefined ? false : opts.readonly;
|
||||
opts.fileMustExist =
|
||||
opts.fileMustExist === undefined ? false : opts.fileMustExist;
|
||||
opts.timeout = opts.timeout === undefined ? 0 : opts.timeout;
|
||||
|
||||
this.db = new NativeDB(path, opts);
|
||||
this.memory = this.db.memory;
|
||||
const db = this.db;
|
||||
|
||||
Object.defineProperties(this, {
|
||||
inTransaction: {
|
||||
get() {
|
||||
return db.inTransaction();
|
||||
},
|
||||
},
|
||||
name: {
|
||||
get() {
|
||||
return path;
|
||||
},
|
||||
},
|
||||
readonly: {
|
||||
get() {
|
||||
return opts.readonly;
|
||||
},
|
||||
},
|
||||
open: {
|
||||
get() {
|
||||
return this.db.open;
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a SQL statement for execution.
|
||||
*
|
||||
* @param {string} sql - The SQL statement string to prepare.
|
||||
*/
|
||||
prepare(sql) {
|
||||
if (!sql) {
|
||||
throw new RangeError("The supplied SQL string contains no statements");
|
||||
}
|
||||
|
||||
try {
|
||||
return new Statement(this.db.prepare(sql), this);
|
||||
} catch (err) {
|
||||
throw convertError(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function that executes the given function in a transaction.
|
||||
*
|
||||
* @param {function} fn - The function to wrap in a transaction.
|
||||
*/
|
||||
transaction(fn) {
|
||||
if (typeof fn !== "function")
|
||||
throw new TypeError("Expected first argument to be a function");
|
||||
|
||||
const db = this;
|
||||
const wrapTxn = (mode) => {
|
||||
return (...bindParameters) => {
|
||||
db.exec("BEGIN " + mode);
|
||||
try {
|
||||
const result = fn(...bindParameters);
|
||||
db.exec("COMMIT");
|
||||
return result;
|
||||
} catch (err) {
|
||||
db.exec("ROLLBACK");
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
};
|
||||
const properties = {
|
||||
default: { value: wrapTxn("") },
|
||||
deferred: { value: wrapTxn("DEFERRED") },
|
||||
immediate: { value: wrapTxn("IMMEDIATE") },
|
||||
exclusive: { value: wrapTxn("EXCLUSIVE") },
|
||||
database: { value: this, enumerable: true },
|
||||
};
|
||||
Object.defineProperties(properties.default.value, properties);
|
||||
Object.defineProperties(properties.deferred.value, properties);
|
||||
Object.defineProperties(properties.immediate.value, properties);
|
||||
Object.defineProperties(properties.exclusive.value, properties);
|
||||
return properties.default.value;
|
||||
}
|
||||
|
||||
pragma(source, options) {
|
||||
if (options == null) options = {};
|
||||
|
||||
if (typeof source !== "string")
|
||||
throw new TypeError("Expected first argument to be a string");
|
||||
|
||||
if (typeof options !== "object")
|
||||
throw new TypeError("Expected second argument to be an options object");
|
||||
|
||||
const simple = options["simple"];
|
||||
const pragma = `PRAGMA ${source}`;
|
||||
|
||||
return simple
|
||||
? this.db.pragma(source, { simple: true })
|
||||
: this.db.pragma(source);
|
||||
}
|
||||
|
||||
backup(filename, options) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
serialize(options) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
function(name, options, fn) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
aggregate(name, options) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
table(name, factory) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
loadExtension(path) {
|
||||
this.db.loadExtension(path);
|
||||
}
|
||||
|
||||
maxWriteReplicationIndex() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a SQL statement.
|
||||
*
|
||||
* @param {string} sql - The SQL statement string to execute.
|
||||
*/
|
||||
exec(sql) {
|
||||
try {
|
||||
this.db.exec(sql);
|
||||
} catch (err) {
|
||||
throw convertError(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupts the database connection.
|
||||
*/
|
||||
interrupt() {
|
||||
this.db.interrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the database connection.
|
||||
*/
|
||||
close() {
|
||||
this.db.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Statement represents a prepared SQL statement that can be executed.
|
||||
*/
|
||||
class Statement {
|
||||
constructor(stmt, database) {
|
||||
this.stmt = stmt;
|
||||
this.db = database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle raw mode.
|
||||
*
|
||||
* @param raw Enable or disable raw mode. If you don't pass the parameter, raw mode is enabled.
|
||||
*/
|
||||
raw(raw) {
|
||||
this.stmt.raw(raw);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle pluck mode.
|
||||
*
|
||||
* @param pluckMode Enable or disable pluck mode. If you don't pass the parameter, pluck mode is enabled.
|
||||
*/
|
||||
pluck(pluckMode) {
|
||||
this.stmt.pluck(pluckMode);
|
||||
return this;
|
||||
}
|
||||
|
||||
get source() {
|
||||
return this.stmt.source;
|
||||
}
|
||||
|
||||
get reader() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
get source() {
|
||||
return this.stmt.source;
|
||||
}
|
||||
|
||||
get database() {
|
||||
return this.db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns an info object.
|
||||
*/
|
||||
run(...bindParameters) {
|
||||
return this.stmt.run(bindParameters.flat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns the first row.
|
||||
*
|
||||
* @param bindParameters - The bind parameters for executing the statement.
|
||||
*/
|
||||
get(...bindParameters) {
|
||||
return this.stmt.get(bindParameters.flat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns an iterator to the resulting rows.
|
||||
*
|
||||
* @param bindParameters - The bind parameters for executing the statement.
|
||||
*/
|
||||
*iterate(...bindParameters) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns an array of the resulting rows.
|
||||
*
|
||||
* @param bindParameters - The bind parameters for executing the statement.
|
||||
*/
|
||||
all(...bindParameters) {
|
||||
return this.stmt.all(bindParameters.flat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupts the statement.
|
||||
*/
|
||||
interrupt() {
|
||||
this.stmt.interrupt();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the columns in the result set returned by this prepared statement.
|
||||
*/
|
||||
columns() {
|
||||
return this.stmt.columns();
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the given parameters to the statement _permanently_
|
||||
*
|
||||
* @param bindParameters - The bind parameters for binding the statement.
|
||||
* @returns this - Statement with binded parameters
|
||||
*/
|
||||
bind(...bindParameters) {
|
||||
try {
|
||||
return new Statement(this.stmt.bind(bindParameters.flat()), this.db);
|
||||
} catch (err) {
|
||||
throw convertError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Database;
|
||||
module.exports.SqliteError = SqliteError;
|
||||
Reference in New Issue
Block a user