@storecraft/database-turso
Version:
`Storecraft` database driver for `Turso` (cloud sqlite)
251 lines (216 loc) • 5.23 kB
JavaScript
/**
* @import { Driver, Dialect, DatabaseConnection, QueryResult } from 'kysely';
* @import { Config } from './types.public.js';
* @import { Row, InArgs } from '@libsql/client';
*/
import * as libsql from "@libsql/client";
import * as kysely from "kysely";
/**
*
* @implements {Dialect}
*/
export class LibsqlDialect {
/** @type {Config} */
#config;
/**
*
* @param {Config} config
*/
constructor(config) {
this.#config = config;
}
get config() {
return this.#config;
}
createAdapter() {
return new kysely.SqliteAdapter();
}
createQueryCompiler() {
return new kysely.SqliteQueryCompiler();
}
createDriver() {
if (this.#config?.url===undefined) {
throw new Error(
"Please specify either `client` or `url` in the LibsqlDialect config"
);
}
return new LibsqlDriver(
// @ts-ignore
libsql.createClient(this.#config),
this.#config
);
}
/** @type {Dialect["createIntrospector"]} */
createIntrospector(db) {
return new kysely.SqliteIntrospector(db);
}
}
/**
*
* @implements {Driver}
*/
export class LibsqlDriver {
/** @type {libsql.Client} */
client;
/**
* @param {libsql.Client} client
* @param {Config} config
*/
constructor(client, config) {
this.client = client;
this.config = config;
}
async init() {}
async acquireConnection() {
return new LibsqlConnection(this.client, this.config);
}
/**
*
* @param {LibsqlConnection} connection
* @param {kysely.TransactionSettings} settings
*/
async beginTransaction(connection, settings) {
await connection.beginTransaction();
}
/**
*
* @param {LibsqlConnection} connection
*/
async commitTransaction(connection) {
await connection.commitTransaction();
}
/**
*
* @param {LibsqlConnection} connection
*/
async rollbackTransaction(connection) {
await connection.rollbackTransaction();
}
/**
*
* @param {LibsqlConnection} _conn
*/
async releaseConnection(_conn) {}
async destroy() {
this.client.close();
}
}
/**
* @implements {DatabaseConnection}
*/
export class LibsqlConnection {
/** @type {libsql.Client} */
client;
/** @type {Config} */
config;
isBatch = false;
/** @type {kysely.CompiledQuery[]} */
batch = []
/** @type {libsql.Transaction} */
#transaction;
/**
*
* @param {libsql.Client} client
* @param {Config} config
*/
constructor(client, config) {
this.client = client;
this.config = config;
}
/**
* @param {kysely.CompiledQuery[]} compiledQueries
*
* @returns {Promise<QueryResult<Row>>}
*/
async #internal_executeQuery(compiledQueries) {
// console.log(compiledQueries)
const target = this.#transaction ?? this.client;
const stmts = compiledQueries.map(
cq => (
{
sql: cq.sql,
args: (/** @type {InArgs} */ (cq.parameters)),
}
)
);
const results = await target.batch(
stmts
);
// console.log('q', JSON.stringify({sql, params}, null, 2))
// console.log('stmts', JSON.stringify(stmts, null, 2))
// console.log('result', JSON.stringify(results, null, 2))
const last_result = results?.at(-1);
return {
insertId: last_result?.lastInsertRowid,
rows: last_result?.rows ?? [],
numAffectedRows: BigInt(last_result?.rowsAffected ?? 0),
};
}
/**
* @param {kysely.CompiledQuery} compiledQuery
* @returns {Promise<QueryResult>}
*/
async executeQuery(compiledQuery) {
// console.log('this.isBatch', this.isBatch)
if(this.isBatch) {
this.batch.push(compiledQuery);
return Promise.resolve(
{
rows: []
}
)
} else {
return this.#internal_executeQuery([compiledQuery]);
}
}
async beginTransaction() {
// console.log('beginTransaction')
if(this.config.prefers_batch_over_transactions) {
this.isBatch = true;
this.batch = [];
return;
}
if (this.#transaction) {
throw new Error("Transaction already in progress");
}
this.#transaction = await this.client.transaction();
}
async commitTransaction() {
// console.log('commitTransaction')
if(this.isBatch) {
// console.trace()
await this.#internal_executeQuery(this.batch);
this.isBatch = false;
this.batch = [];
return;
}
if (!this.#transaction) {
throw new Error("No transaction to commit");
}
await this.#transaction.commit();
this.#transaction = undefined;
}
async rollbackTransaction() {
if(this.isBatch) {
this.isBatch = false;
this.batch = [];
return;
}
if (!this.#transaction) {
throw new Error("No transaction to rollback");
}
await this.#transaction.rollback();
this.#transaction = undefined;
}
/**
* @template R result type
*
* @param {kysely.CompiledQuery} compiledQuery
* @param {number} chunkSize
*
* @returns {AsyncIterableIterator<QueryResult<R>>}
*/
async *streamQuery(compiledQuery, chunkSize) {
throw new Error("Libsql Driver does not support streaming yet");
}
}