mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-08 18:54:21 +01:00
rename database-core -> database-common
This commit is contained in:
8
bindings/javascript/packages/common/README.md
Normal file
8
bindings/javascript/packages/common/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
## About
|
||||
|
||||
This package is the Turso embedded database common JS library which is shared between final builds for Node and Browser.
|
||||
|
||||
Do not use this package directly - instead you must use `@tursodatabase/database` or `@tursodatabase/database-browser`.
|
||||
|
||||
> **⚠️ Warning:** This software is ALPHA, only use for development, testing, and experimentation. We are working to make it production ready, but do not use it for critical data right now.
|
||||
|
||||
72
bindings/javascript/packages/common/bind.ts
Normal file
72
bindings/javascript/packages/common/bind.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
// Bind parameters to a statement.
|
||||
//
|
||||
// This function is used to bind parameters to a statement. It supports both
|
||||
// named and positional parameters, and nested arrays.
|
||||
//
|
||||
// The `stmt` parameter is a statement object.
|
||||
// The `params` parameter is an array of parameters.
|
||||
//
|
||||
// The function returns void.
|
||||
export function bindParams(stmt, params) {
|
||||
const len = params?.length;
|
||||
if (len === 0) {
|
||||
return;
|
||||
}
|
||||
if (len === 1) {
|
||||
const param = params[0];
|
||||
if (isPlainObject(param)) {
|
||||
bindNamedParams(stmt, param);
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(param)) {
|
||||
bindPositionalParams(stmt, [param]);
|
||||
return;
|
||||
}
|
||||
bindValue(stmt, 1, param);
|
||||
return;
|
||||
}
|
||||
bindPositionalParams(stmt, params);
|
||||
}
|
||||
|
||||
// Check if object is plain (no prototype chain)
|
||||
function isPlainObject(obj) {
|
||||
if (!obj || typeof obj !== 'object') return false;
|
||||
const proto = Object.getPrototypeOf(obj);
|
||||
return proto === Object.prototype || proto === null;
|
||||
}
|
||||
|
||||
// Handle named parameters
|
||||
function bindNamedParams(stmt, paramObj) {
|
||||
const paramCount = stmt.parameterCount();
|
||||
|
||||
for (let i = 1; i <= paramCount; i++) {
|
||||
const paramName = stmt.parameterName(i);
|
||||
if (paramName) {
|
||||
const key = paramName.substring(1); // Remove ':' or '$' prefix
|
||||
const value = paramObj[key];
|
||||
|
||||
if (value !== undefined) {
|
||||
bindValue(stmt, i, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle positional parameters (including nested arrays)
|
||||
function bindPositionalParams(stmt, params) {
|
||||
let bindIndex = 1;
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
const param = params[i];
|
||||
if (Array.isArray(param)) {
|
||||
for (let j = 0; j < param.length; j++) {
|
||||
bindValue(stmt, bindIndex++, param[j]);
|
||||
}
|
||||
} else {
|
||||
bindValue(stmt, bindIndex++, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bindValue(stmt, index, value) {
|
||||
stmt.bindAt(index, value);
|
||||
}
|
||||
413
bindings/javascript/packages/common/compat.ts
Normal file
413
bindings/javascript/packages/common/compat.ts
Normal file
@@ -0,0 +1,413 @@
|
||||
import { bindParams } from "./bind.js";
|
||||
import { SqliteError } from "./sqlite-error.js";
|
||||
import { NativeDatabase, NativeStatement, STEP_IO, STEP_ROW, STEP_DONE } from "./types.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 {
|
||||
db: NativeDatabase;
|
||||
memory: boolean;
|
||||
open: boolean;
|
||||
private _inTransaction: boolean = false;
|
||||
|
||||
/**
|
||||
* 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(db: NativeDatabase, opts: any = {}) {
|
||||
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 = db;
|
||||
this.memory = this.db.memory;
|
||||
|
||||
Object.defineProperties(this, {
|
||||
inTransaction: {
|
||||
get: () => this._inTransaction,
|
||||
},
|
||||
name: {
|
||||
get() {
|
||||
return db.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 (!this.open) {
|
||||
throw new TypeError("The database connection is not open");
|
||||
}
|
||||
|
||||
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);
|
||||
db._inTransaction = true;
|
||||
try {
|
||||
const result = fn(...bindParameters);
|
||||
db.exec("COMMIT");
|
||||
db._inTransaction = false;
|
||||
return result;
|
||||
} catch (err) {
|
||||
db.exec("ROLLBACK");
|
||||
db._inTransaction = false;
|
||||
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 pragma = `PRAGMA ${source}`;
|
||||
|
||||
const stmt = this.prepare(pragma);
|
||||
const results = stmt.all();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
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) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
maxWriteReplicationIndex() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a SQL statement.
|
||||
*
|
||||
* @param {string} sql - The SQL statement string to execute.
|
||||
*/
|
||||
exec(sql) {
|
||||
if (!this.open) {
|
||||
throw new TypeError("The database connection is not open");
|
||||
}
|
||||
|
||||
try {
|
||||
this.db.batchSync(sql);
|
||||
} catch (err) {
|
||||
throw convertError(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupts the database connection.
|
||||
*/
|
||||
interrupt() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default safe integers mode for all statements from this database.
|
||||
*
|
||||
* @param {boolean} [toggle] - Whether to use safe integers by default.
|
||||
*/
|
||||
defaultSafeIntegers(toggle) {
|
||||
this.db.defaultSafeIntegers(toggle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the database connection.
|
||||
*/
|
||||
close() {
|
||||
this.db.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Statement represents a prepared SQL statement that can be executed.
|
||||
*/
|
||||
class Statement {
|
||||
stmt: NativeStatement;
|
||||
db: Database;
|
||||
|
||||
constructor(stmt: NativeStatement, database: 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets safe integers mode for this statement.
|
||||
*
|
||||
* @param {boolean} [toggle] - Whether to use safe integers.
|
||||
*/
|
||||
safeIntegers(toggle) {
|
||||
this.stmt.safeIntegers(toggle);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get column information for the statement.
|
||||
*
|
||||
* @returns {Array} An array of column objects with name, column, table, database, and type properties.
|
||||
*/
|
||||
columns() {
|
||||
return this.stmt.columns();
|
||||
}
|
||||
|
||||
get source() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
get reader() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
get database() {
|
||||
return this.db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns an info object.
|
||||
*/
|
||||
run(...bindParameters) {
|
||||
const totalChangesBefore = this.db.db.totalChanges();
|
||||
|
||||
this.stmt.reset();
|
||||
bindParams(this.stmt, bindParameters);
|
||||
for (; ;) {
|
||||
const stepResult = this.stmt.stepSync();
|
||||
if (stepResult === STEP_IO) {
|
||||
this.db.db.ioLoopSync();
|
||||
continue;
|
||||
}
|
||||
if (stepResult === STEP_DONE) {
|
||||
break;
|
||||
}
|
||||
if (stepResult === STEP_ROW) {
|
||||
// For run(), we don't need the row data, just continue
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const lastInsertRowid = this.db.db.lastInsertRowid();
|
||||
const changes = this.db.db.totalChanges() === totalChangesBefore ? 0 : this.db.db.changes();
|
||||
|
||||
return { changes, lastInsertRowid };
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns the first row.
|
||||
*
|
||||
* @param bindParameters - The bind parameters for executing the statement.
|
||||
*/
|
||||
get(...bindParameters) {
|
||||
this.stmt.reset();
|
||||
bindParams(this.stmt, bindParameters);
|
||||
for (; ;) {
|
||||
const stepResult = this.stmt.stepSync();
|
||||
if (stepResult === STEP_IO) {
|
||||
this.db.db.ioLoopSync();
|
||||
continue;
|
||||
}
|
||||
if (stepResult === STEP_DONE) {
|
||||
return undefined;
|
||||
}
|
||||
if (stepResult === STEP_ROW) {
|
||||
return this.stmt.row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns an iterator to the resulting rows.
|
||||
*
|
||||
* @param bindParameters - The bind parameters for executing the statement.
|
||||
*/
|
||||
*iterate(...bindParameters) {
|
||||
this.stmt.reset();
|
||||
bindParams(this.stmt, bindParameters);
|
||||
|
||||
while (true) {
|
||||
const stepResult = this.stmt.stepSync();
|
||||
if (stepResult === STEP_IO) {
|
||||
this.db.db.ioLoopSync();
|
||||
continue;
|
||||
}
|
||||
if (stepResult === STEP_DONE) {
|
||||
break;
|
||||
}
|
||||
if (stepResult === STEP_ROW) {
|
||||
yield this.stmt.row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns an array of the resulting rows.
|
||||
*
|
||||
* @param bindParameters - The bind parameters for executing the statement.
|
||||
*/
|
||||
all(...bindParameters) {
|
||||
this.stmt.reset();
|
||||
bindParams(this.stmt, bindParameters);
|
||||
const rows: any[] = [];
|
||||
for (; ;) {
|
||||
const stepResult = this.stmt.stepSync();
|
||||
if (stepResult === STEP_IO) {
|
||||
this.db.db.ioLoopSync();
|
||||
continue;
|
||||
}
|
||||
if (stepResult === STEP_DONE) {
|
||||
break;
|
||||
}
|
||||
if (stepResult === STEP_ROW) {
|
||||
rows.push(this.stmt.row());
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupts the statement.
|
||||
*/
|
||||
interrupt() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
bindParams(this.stmt, bindParameters);
|
||||
return this;
|
||||
} catch (err) {
|
||||
throw convertError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { Database, Statement }
|
||||
6
bindings/javascript/packages/common/index.ts
Normal file
6
bindings/javascript/packages/common/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { NativeDatabase, NativeStatement, DatabaseOpts } from "./types.js";
|
||||
import { Database as DatabaseCompat, Statement as StatementCompat } from "./compat.js";
|
||||
import { Database as DatabasePromise, Statement as StatementPromise } from "./promise.js";
|
||||
import { SqliteError } from "./sqlite-error.js";
|
||||
|
||||
export { DatabaseCompat, StatementCompat, DatabasePromise, StatementPromise, NativeDatabase, NativeStatement, SqliteError, DatabaseOpts }
|
||||
25
bindings/javascript/packages/common/package.json
Normal file
25
bindings/javascript/packages/common/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@tursodatabase/database-common",
|
||||
"version": "0.1.5-pre.3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tursodatabase/turso"
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"packageManager": "yarn@4.9.2",
|
||||
"files": [
|
||||
"dist/**",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"scripts": {
|
||||
"tsc-build": "npm exec tsc",
|
||||
"build": "npm run tsc-build",
|
||||
"test": "echo 'no tests'"
|
||||
}
|
||||
}
|
||||
422
bindings/javascript/packages/common/promise.ts
Normal file
422
bindings/javascript/packages/common/promise.ts
Normal file
@@ -0,0 +1,422 @@
|
||||
import { bindParams } from "./bind.js";
|
||||
import { SqliteError } from "./sqlite-error.js";
|
||||
import { NativeDatabase, NativeStatement, STEP_IO, STEP_ROW, STEP_DONE, DatabaseOpts } from "./types.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 {
|
||||
db: NativeDatabase;
|
||||
memory: boolean;
|
||||
open: boolean;
|
||||
private _inTransaction: boolean = false;
|
||||
/**
|
||||
* 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(db: NativeDatabase, opts: DatabaseOpts = {}) {
|
||||
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.initialize(db, opts.name, opts.readonly);
|
||||
}
|
||||
static create() {
|
||||
return Object.create(this.prototype);
|
||||
}
|
||||
initialize(db: NativeDatabase, name, readonly) {
|
||||
this.db = db;
|
||||
this.memory = db.memory;
|
||||
Object.defineProperties(this, {
|
||||
inTransaction: {
|
||||
get: () => this._inTransaction,
|
||||
},
|
||||
name: {
|
||||
get() {
|
||||
return name;
|
||||
},
|
||||
},
|
||||
readonly: {
|
||||
get() {
|
||||
return 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 (!this.open) {
|
||||
throw new TypeError("The database connection is not open");
|
||||
}
|
||||
|
||||
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: (...any) => Promise<any>) {
|
||||
if (typeof fn !== "function")
|
||||
throw new TypeError("Expected first argument to be a function");
|
||||
|
||||
const db = this;
|
||||
const wrapTxn = (mode) => {
|
||||
return async (...bindParameters) => {
|
||||
await db.exec("BEGIN " + mode);
|
||||
db._inTransaction = true;
|
||||
try {
|
||||
const result = await fn(...bindParameters);
|
||||
await db.exec("COMMIT");
|
||||
db._inTransaction = false;
|
||||
return result;
|
||||
} catch (err) {
|
||||
await db.exec("ROLLBACK");
|
||||
db._inTransaction = false;
|
||||
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;
|
||||
}
|
||||
|
||||
async 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 pragma = `PRAGMA ${source}`;
|
||||
|
||||
const stmt = await this.prepare(pragma);
|
||||
const results = await stmt.all();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
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) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
maxWriteReplicationIndex() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a SQL statement.
|
||||
*
|
||||
* @param {string} sql - The SQL statement string to execute.
|
||||
*/
|
||||
async exec(sql) {
|
||||
if (!this.open) {
|
||||
throw new TypeError("The database connection is not open");
|
||||
}
|
||||
|
||||
try {
|
||||
await this.db.batchAsync(sql);
|
||||
} catch (err) {
|
||||
throw convertError(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupts the database connection.
|
||||
*/
|
||||
interrupt() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default safe integers mode for all statements from this database.
|
||||
*
|
||||
* @param {boolean} [toggle] - Whether to use safe integers by default.
|
||||
*/
|
||||
defaultSafeIntegers(toggle) {
|
||||
this.db.defaultSafeIntegers(toggle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the database connection.
|
||||
*/
|
||||
async close() {
|
||||
this.db.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Statement represents a prepared SQL statement that can be executed.
|
||||
*/
|
||||
class Statement {
|
||||
stmt: NativeStatement;
|
||||
db: Database;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets safe integers mode for this statement.
|
||||
*
|
||||
* @param {boolean} [toggle] - Whether to use safe integers.
|
||||
*/
|
||||
safeIntegers(toggle) {
|
||||
this.stmt.safeIntegers(toggle);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get column information for the statement.
|
||||
*
|
||||
* @returns {Array} An array of column objects with name, column, table, database, and type properties.
|
||||
*/
|
||||
columns() {
|
||||
return this.stmt.columns();
|
||||
}
|
||||
|
||||
get source() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
get reader() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
get database() {
|
||||
return this.db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns an info object.
|
||||
*/
|
||||
async run(...bindParameters) {
|
||||
const totalChangesBefore = this.db.db.totalChanges();
|
||||
|
||||
this.stmt.reset();
|
||||
bindParams(this.stmt, bindParameters);
|
||||
|
||||
while (true) {
|
||||
const stepResult = await this.stmt.stepAsync();
|
||||
if (stepResult === STEP_IO) {
|
||||
await this.db.db.ioLoopAsync();
|
||||
continue;
|
||||
}
|
||||
if (stepResult === STEP_DONE) {
|
||||
break;
|
||||
}
|
||||
if (stepResult === STEP_ROW) {
|
||||
// For run(), we don't need the row data, just continue
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const lastInsertRowid = this.db.db.lastInsertRowid();
|
||||
const changes = this.db.db.totalChanges() === totalChangesBefore ? 0 : this.db.db.changes();
|
||||
|
||||
return { changes, lastInsertRowid };
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns the first row.
|
||||
*
|
||||
* @param bindParameters - The bind parameters for executing the statement.
|
||||
*/
|
||||
async get(...bindParameters) {
|
||||
this.stmt.reset();
|
||||
bindParams(this.stmt, bindParameters);
|
||||
|
||||
while (true) {
|
||||
const stepResult = await this.stmt.stepAsync();
|
||||
if (stepResult === STEP_IO) {
|
||||
await this.db.db.ioLoopAsync();
|
||||
continue;
|
||||
}
|
||||
if (stepResult === STEP_DONE) {
|
||||
return undefined;
|
||||
}
|
||||
if (stepResult === STEP_ROW) {
|
||||
return this.stmt.row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns an iterator to the resulting rows.
|
||||
*
|
||||
* @param bindParameters - The bind parameters for executing the statement.
|
||||
*/
|
||||
async *iterate(...bindParameters) {
|
||||
this.stmt.reset();
|
||||
bindParams(this.stmt, bindParameters);
|
||||
|
||||
while (true) {
|
||||
const stepResult = await this.stmt.stepAsync();
|
||||
if (stepResult === STEP_IO) {
|
||||
await this.db.db.ioLoopAsync();
|
||||
continue;
|
||||
}
|
||||
if (stepResult === STEP_DONE) {
|
||||
break;
|
||||
}
|
||||
if (stepResult === STEP_ROW) {
|
||||
yield this.stmt.row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement and returns an array of the resulting rows.
|
||||
*
|
||||
* @param bindParameters - The bind parameters for executing the statement.
|
||||
*/
|
||||
async all(...bindParameters) {
|
||||
this.stmt.reset();
|
||||
bindParams(this.stmt, bindParameters);
|
||||
const rows: any[] = [];
|
||||
|
||||
while (true) {
|
||||
const stepResult = await this.stmt.stepAsync();
|
||||
if (stepResult === STEP_IO) {
|
||||
await this.db.db.ioLoopAsync();
|
||||
continue;
|
||||
}
|
||||
if (stepResult === STEP_DONE) {
|
||||
break;
|
||||
}
|
||||
if (stepResult === STEP_ROW) {
|
||||
rows.push(this.stmt.row());
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupts the statement.
|
||||
*/
|
||||
interrupt() {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
bindParams(this.stmt, bindParameters);
|
||||
return this;
|
||||
} catch (err) {
|
||||
throw convertError(err);
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this.stmt.finalize();
|
||||
}
|
||||
}
|
||||
export { Database, Statement }
|
||||
13
bindings/javascript/packages/common/sqlite-error.ts
Normal file
13
bindings/javascript/packages/common/sqlite-error.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export class SqliteError extends Error {
|
||||
name: string;
|
||||
code: string;
|
||||
rawCode: string;
|
||||
constructor(message, code, rawCode) {
|
||||
super(message);
|
||||
this.name = 'SqliteError';
|
||||
this.code = code;
|
||||
this.rawCode = rawCode;
|
||||
|
||||
(Error as any).captureStackTrace(this, SqliteError);
|
||||
}
|
||||
}
|
||||
16
bindings/javascript/packages/common/tsconfig.json
Normal file
16
bindings/javascript/packages/common/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"outDir": "dist/",
|
||||
"lib": [
|
||||
"es2020"
|
||||
],
|
||||
},
|
||||
"include": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
46
bindings/javascript/packages/common/types.ts
Normal file
46
bindings/javascript/packages/common/types.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
export interface DatabaseOpts {
|
||||
readonly?: boolean,
|
||||
fileMustExist?: boolean,
|
||||
timeout?: number
|
||||
name?: string
|
||||
tracing?: 'info' | 'debug' | 'trace'
|
||||
}
|
||||
|
||||
export interface NativeDatabase {
|
||||
memory: boolean,
|
||||
path: string,
|
||||
new(path: string): NativeDatabase;
|
||||
batchSync(sql: string);
|
||||
batchAsync(sql: string): Promise<void>;
|
||||
|
||||
ioLoopSync();
|
||||
ioLoopAsync(): Promise<void>;
|
||||
|
||||
prepare(sql: string): NativeStatement;
|
||||
|
||||
pluck(pluckMode: boolean);
|
||||
defaultSafeIntegers(toggle: boolean);
|
||||
totalChanges(): number;
|
||||
changes(): number;
|
||||
lastInsertRowid(): number;
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
// Step result constants
|
||||
export const STEP_ROW = 1;
|
||||
export const STEP_DONE = 2;
|
||||
export const STEP_IO = 3;
|
||||
|
||||
export interface NativeStatement {
|
||||
stepAsync(): Promise<number>;
|
||||
stepSync(): number;
|
||||
|
||||
pluck(pluckMode: boolean);
|
||||
safeIntegers(toggle: boolean);
|
||||
raw(toggle: boolean);
|
||||
columns(): string[];
|
||||
row(): any;
|
||||
reset();
|
||||
finalize();
|
||||
}
|
||||
Reference in New Issue
Block a user