@lucidcms/libsql-adapter
Version:
The official LibSQL adapter for Lucid CMS
268 lines (261 loc) • 8.18 kB
JavaScript
import { DatabaseAdapter } from "@lucidcms/core";
import { createClient } from "@libsql/client/web";
import { ParseJSONResultsPlugin, SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler, sql } from "kysely";
import { jsonArrayFrom } from "kysely/helpers/sqlite";
//#region src/lib/kysely-libsql.ts
var LibsqlDialect = class {
#config;
constructor(config) {
this.#config = config;
}
createAdapter() {
return new SqliteAdapter();
}
createDriver() {
let client;
let closeClient;
if ("client" in this.#config) {
client = this.#config.client;
closeClient = false;
} else if (this.#config.url !== void 0) {
client = createClient(this.#config);
closeClient = true;
} else throw new Error("Please specify either `client` or `url` in the LibsqlDialect config");
return new LibsqlDriver(client, closeClient);
}
createIntrospector(db) {
return new SqliteIntrospector(db);
}
createQueryCompiler() {
return new SqliteQueryCompiler();
}
};
var LibsqlDriver = class {
client;
#closeClient;
constructor(client, closeClient) {
this.client = client;
this.#closeClient = closeClient;
}
async init() {}
async acquireConnection() {
return new LibsqlConnection(this.client);
}
async beginTransaction(connection, _settings) {
await connection.beginTransaction();
}
async commitTransaction(connection) {
await connection.commitTransaction();
}
async rollbackTransaction(connection) {
await connection.rollbackTransaction();
}
async releaseConnection(_conn) {}
async destroy() {
if (this.#closeClient) this.client.close();
}
};
var LibsqlConnection = class {
client;
#transaction;
constructor(client) {
this.client = client;
}
async executeQuery(compiledQuery) {
const target = this.#transaction ?? this.client;
const result = await target.execute({
sql: compiledQuery.sql,
args: compiledQuery.parameters
});
return {
insertId: result.lastInsertRowid,
numAffectedRows: BigInt(result.rowsAffected),
rows: result.rows
};
}
async beginTransaction() {
if (this.#transaction) throw new Error("Transaction already in progress");
this.#transaction = await this.client.transaction();
}
async commitTransaction() {
if (!this.#transaction) throw new Error("No transaction to commit");
await this.#transaction.commit();
this.#transaction = void 0;
}
async rollbackTransaction() {
if (!this.#transaction) throw new Error("No transaction to rollback");
await this.#transaction.rollback();
this.#transaction = void 0;
}
async *streamQuery(_compiledQuery, _chunkSize) {
throw new Error("Libsql Driver does not support streaming yet");
}
};
//#endregion
//#region src/utils/format-default-value.ts
const formatDefaultValue = (type, defaultValue) => {
if (defaultValue === null) return null;
if (defaultValue === "''") return "";
if (type === "json" && defaultValue.startsWith("'") && defaultValue.endsWith("'")) try {
return JSON.parse(defaultValue.slice(1, -1));
} catch {
return null;
}
if (defaultValue.startsWith("'") && defaultValue.endsWith("'")) return defaultValue.slice(1, -1);
if (/^-?\d+(\.\d+)?$/.test(defaultValue)) return type === "integer" || type === "bigint" ? Number.parseInt(defaultValue, 10) : Number.parseFloat(defaultValue);
return defaultValue;
};
var format_default_value_default = formatDefaultValue;
//#endregion
//#region src/utils/format-on-delete.ts
const formatOnDelete = (value) => {
return value?.toLowerCase() ?? "no action";
};
var format_on_delete_default = formatOnDelete;
//#endregion
//#region src/utils/format-on-update.ts
const formatOnUpdate = (value) => {
return value?.toLowerCase() ?? "no action";
};
var format_on_update_default = formatOnUpdate;
//#endregion
//#region src/utils/format-type.ts
const formatType = (type) => {
return type.toLowerCase();
};
var format_type_default = formatType;
//#endregion
//#region src/index.ts
var LibSQLAdapter = class extends DatabaseAdapter {
constructor(config) {
super({
adapter: "libsql",
dialect: new LibsqlDialect(config),
plugins: [new ParseJSONResultsPlugin()]
});
}
async initialise() {
await sql`PRAGMA foreign_keys = ON`.execute(this.client);
}
get jsonArrayFrom() {
return jsonArrayFrom;
}
get config() {
return {
support: {
alterColumn: false,
multipleAlterTables: false,
boolean: false,
autoIncrement: true
},
dataTypes: {
primary: "integer",
integer: "integer",
boolean: "integer",
json: "json",
text: "text",
timestamp: "timestamp",
char: "text",
varchar: "text"
},
defaults: {
timestamp: { now: "CURRENT_TIMESTAMP" },
boolean: {
true: 1,
false: 0
}
},
fuzzOperator: "like"
};
}
async inferSchema(tx) {
const res = await sql`
WITH RECURSIVE
tables AS (
SELECT name as table_name
FROM sqlite_master
WHERE type='table'
AND name LIKE 'lucid_%'
AND name NOT LIKE 'sqlite_%'
),
table_info AS (
SELECT
tables.table_name,
p.*
FROM tables
CROSS JOIN pragma_table_info(tables.table_name) as p
),
foreign_keys AS (
SELECT
tables.table_name,
fk.'from' as column_name,
fk.'table' as referenced_table,
fk.'to' as referenced_column,
fk.'on_update' as on_update,
fk.'on_delete' as on_delete
FROM tables
CROSS JOIN pragma_foreign_key_list(tables.table_name) as fk
),
unique_constraints AS (
SELECT
tables.table_name,
idx.name as index_name,
idx.'unique' as is_unique,
info.name as column_name
FROM tables
CROSS JOIN pragma_index_list(tables.table_name) as idx
CROSS JOIN pragma_index_info(idx.name) as info
WHERE idx.'unique' = 1
)
SELECT
t.*,
fk.referenced_table as fk_table,
fk.referenced_column as fk_column,
fk.on_update as fk_on_update,
fk.on_delete as fk_on_delete,
CASE WHEN uc.column_name IS NOT NULL THEN 1 ELSE 0 END as is_unique
FROM table_info t
LEFT JOIN foreign_keys fk ON
t.table_name = fk.table_name AND
t.name = fk.column_name
LEFT JOIN unique_constraints uc ON
t.table_name = uc.table_name AND
t.name = uc.column_name
`.execute(tx ?? this.client);
const tableMap = /* @__PURE__ */ new Map();
for (const row of res.rows) {
let table = tableMap.get(row.table_name);
if (!table) {
table = {
name: row.table_name,
columns: []
};
tableMap.set(row.table_name, table);
}
table.columns.push({
name: row.name,
type: format_type_default(row.type),
nullable: !row.notnull,
default: format_default_value_default(format_type_default(row.type), row.dflt_value),
primary: Boolean(row.pk),
unique: Boolean(row.is_unique),
foreignKey: row.fk_table && row.fk_column ? {
table: row.fk_table,
column: row.fk_column,
onUpdate: format_on_update_default(row.fk_on_update),
onDelete: format_on_delete_default(row.fk_on_delete)
} : void 0
});
}
return Array.from(tableMap.values());
}
formatDefaultValue(type, value) {
if (type === "timestamp" && typeof value === "string") return sql.raw(value);
if (typeof value === "object" && value !== null) return JSON.stringify(value);
return value;
}
};
var src_default = LibSQLAdapter;
//#endregion
export { src_default as default };
//# sourceMappingURL=index.js.map