@cequrebackends/plugin-turso 0.14.0
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 +21 -0
- package/dist/adapter.d.ts +47 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/ddl/schema.d.ts +27 -0
- package/dist/ddl/schema.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +590 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @cequrebackends/plugin-turso
|
|
2
|
+
|
|
3
|
+
Turso LibSQL database driver plugin for Cequre backends.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @cequrebackends/plugin-turso @libsql/client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { CequreRuntime } from "@cequrebackends/cequre-ts";
|
|
15
|
+
import { turso } from "@cequrebackends/plugin-turso";
|
|
16
|
+
|
|
17
|
+
const runtime = new CequreRuntime(schema, {
|
|
18
|
+
adapter: turso({ url: process.env.TURSO_DATABASE_URL, authToken: process.env.TURSO_AUTH_TOKEN }),
|
|
19
|
+
plugins: []
|
|
20
|
+
});
|
|
21
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CequreAdapter, QueryArgs, PaginatedResult as QueryResult } from "@cequrebackends/cequre-ts";
|
|
2
|
+
import { BaseSqlAdapter } from "@cequrebackends/cequre-ts";
|
|
3
|
+
export interface TursoConfig {
|
|
4
|
+
/** libSQL URL — e.g. `libsql://your-db.turso.io` or `file:./local.db` for embedded */
|
|
5
|
+
url: string;
|
|
6
|
+
/** Auth token for remote Turso/libSQL servers. Omit for local file mode. */
|
|
7
|
+
authToken?: string;
|
|
8
|
+
/** Optional sync interval in ms for embedded replicas. 0 = no auto-sync. */
|
|
9
|
+
syncIntervalMs?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare class TursoAdapter extends BaseSqlAdapter {
|
|
12
|
+
readonly adapterType = "turso";
|
|
13
|
+
readonly supportsGeneratedConstraintSQL = true;
|
|
14
|
+
private client;
|
|
15
|
+
private config;
|
|
16
|
+
private _syncTimer;
|
|
17
|
+
constructor(config: TursoConfig);
|
|
18
|
+
connect(): Promise<void>;
|
|
19
|
+
disconnect(): Promise<void>;
|
|
20
|
+
transaction<R>(callback: (trxAdapter: CequreAdapter) => Promise<R>): Promise<R>;
|
|
21
|
+
ping(): Promise<void>;
|
|
22
|
+
getSystemTableStatements(): string[];
|
|
23
|
+
getCurrentSchema(): Promise<{
|
|
24
|
+
tables: string[];
|
|
25
|
+
columns: Record<string, string[]>;
|
|
26
|
+
}>;
|
|
27
|
+
recordMigration(version: string, description: string): Promise<void>;
|
|
28
|
+
getCurrentMigrationVersion(): Promise<string | null>;
|
|
29
|
+
createTableDDL(table: unknown): string;
|
|
30
|
+
renameColumnDDL(table: string, oldName: string, newName: string): string;
|
|
31
|
+
addColumnDDL(table: string, column: unknown): string | null;
|
|
32
|
+
dropColumnDDL(table: string, column: string): string;
|
|
33
|
+
dropTableDDL(table: string): string;
|
|
34
|
+
private deserializeRow;
|
|
35
|
+
private buildWhere;
|
|
36
|
+
find(collection: string, query: QueryArgs<any>): Promise<QueryResult>;
|
|
37
|
+
findById(collection: string, id: string): Promise<Record<string, unknown> | null>;
|
|
38
|
+
findByIds(collection: string, ids: string[]): Promise<Record<string, unknown>[]>;
|
|
39
|
+
create(collection: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
40
|
+
createMany(collection: string, docs: Record<string, unknown>[]): Promise<Record<string, unknown>[]>;
|
|
41
|
+
update(collection: string, id: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
42
|
+
delete(collection: string, id: string): Promise<void>;
|
|
43
|
+
deleteMany(collection: string, ids: string[]): Promise<void>;
|
|
44
|
+
count(collection: string, query?: Pick<QueryArgs<any>, "where">): Promise<number>;
|
|
45
|
+
raw(sql: string, params?: unknown[]): Promise<unknown>;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CollectionConfig, FieldConfig } from "@cequrebackends/cequre-ts";
|
|
2
|
+
export type SQLiteType = "TEXT" | "INTEGER" | "REAL" | "BLOB";
|
|
3
|
+
export interface ColumnDefinition {
|
|
4
|
+
name: string;
|
|
5
|
+
type: SQLiteType;
|
|
6
|
+
required: boolean;
|
|
7
|
+
unique: boolean;
|
|
8
|
+
defaultValue?: any;
|
|
9
|
+
foreignKey?: {
|
|
10
|
+
table: string;
|
|
11
|
+
column: string;
|
|
12
|
+
onDelete?: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface TableDefinition {
|
|
16
|
+
name: string;
|
|
17
|
+
columns: ColumnDefinition[];
|
|
18
|
+
primaryKey: string[];
|
|
19
|
+
}
|
|
20
|
+
export declare function fieldTypeToSQLite(field: FieldConfig): SQLiteType;
|
|
21
|
+
export declare function buildColumns(collection: CollectionConfig): ColumnDefinition[];
|
|
22
|
+
export declare function buildTable(collection: CollectionConfig): TableDefinition;
|
|
23
|
+
export declare function generateCreateTable(table: TableDefinition): string;
|
|
24
|
+
export declare function generateRenameColumn(tableName: string, oldName: string, newName: string): string;
|
|
25
|
+
export declare function generateAddColumn(tableName: string, column: ColumnDefinition): string;
|
|
26
|
+
export declare function generateSystemTables(): string[];
|
|
27
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/ddl/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE/E,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACnE;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,UAAU,CAUhE;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAiD7E;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,gBAAgB,GAAG,eAAe,CAMxE;AAsBD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAuBlE;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEhG;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAErF;AAED,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAsB/C"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAE3D,wBAAgB,KAAK,CAAC,MAAM,EAAE,WAAW,gBAExC;AAED,OAAO,EAAE,YAAY,EAAE,CAAC;AACxB,YAAY,EAAE,WAAW,EAAE,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __require = import.meta.require;
|
|
3
|
+
|
|
4
|
+
// src/adapter.ts
|
|
5
|
+
import { ulid, BaseSqlAdapter, sqlIdentifier } from "@cequrebackends/cequre-ts";
|
|
6
|
+
import { createLogger } from "@cequrebackends/cequre-ts";
|
|
7
|
+
|
|
8
|
+
// src/ddl/schema.ts
|
|
9
|
+
function fieldTypeToSQLite(field) {
|
|
10
|
+
switch (field.type) {
|
|
11
|
+
case "boolean":
|
|
12
|
+
case "integer":
|
|
13
|
+
return "INTEGER";
|
|
14
|
+
case "number":
|
|
15
|
+
return "REAL";
|
|
16
|
+
default:
|
|
17
|
+
return "TEXT";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function buildColumns(collection) {
|
|
21
|
+
const columns = [];
|
|
22
|
+
columns.push({
|
|
23
|
+
name: "id",
|
|
24
|
+
type: "TEXT",
|
|
25
|
+
required: true,
|
|
26
|
+
unique: false
|
|
27
|
+
});
|
|
28
|
+
for (const field of collection.fields) {
|
|
29
|
+
if (field.type === "link")
|
|
30
|
+
continue;
|
|
31
|
+
const col = {
|
|
32
|
+
name: field.name,
|
|
33
|
+
type: fieldTypeToSQLite(field),
|
|
34
|
+
required: !field.optional,
|
|
35
|
+
unique: field.unique || false,
|
|
36
|
+
defaultValue: field.default
|
|
37
|
+
};
|
|
38
|
+
if (field.type === "relationship" && field.target) {
|
|
39
|
+
col.foreignKey = {
|
|
40
|
+
table: field.target,
|
|
41
|
+
column: "id",
|
|
42
|
+
onDelete: "SET NULL"
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
columns.push(col);
|
|
46
|
+
}
|
|
47
|
+
columns.push({
|
|
48
|
+
name: "createdAt",
|
|
49
|
+
type: "TEXT",
|
|
50
|
+
required: true,
|
|
51
|
+
unique: false,
|
|
52
|
+
defaultValue: "CURRENT_TIMESTAMP"
|
|
53
|
+
});
|
|
54
|
+
columns.push({
|
|
55
|
+
name: "updatedAt",
|
|
56
|
+
type: "TEXT",
|
|
57
|
+
required: true,
|
|
58
|
+
unique: false,
|
|
59
|
+
defaultValue: "CURRENT_TIMESTAMP"
|
|
60
|
+
});
|
|
61
|
+
return columns;
|
|
62
|
+
}
|
|
63
|
+
function buildTable(collection) {
|
|
64
|
+
return {
|
|
65
|
+
name: collection.slug,
|
|
66
|
+
columns: buildColumns(collection),
|
|
67
|
+
primaryKey: ["id"]
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function formatDefaultValue(value) {
|
|
71
|
+
if (value === "CURRENT_TIMESTAMP")
|
|
72
|
+
return "CURRENT_TIMESTAMP";
|
|
73
|
+
if (value === undefined || value === null)
|
|
74
|
+
return "NULL";
|
|
75
|
+
if (typeof value === "string")
|
|
76
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
77
|
+
if (typeof value === "boolean")
|
|
78
|
+
return value ? "1" : "0";
|
|
79
|
+
if (typeof value === "number")
|
|
80
|
+
return String(value);
|
|
81
|
+
if (value instanceof Date)
|
|
82
|
+
return `'${value.toISOString()}'`;
|
|
83
|
+
return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
|
|
84
|
+
}
|
|
85
|
+
function generateColumnDefinition(column) {
|
|
86
|
+
let def = `"${column.name}" ${column.type}`;
|
|
87
|
+
if (column.required)
|
|
88
|
+
def += " NOT NULL";
|
|
89
|
+
if (column.defaultValue !== undefined) {
|
|
90
|
+
def += ` DEFAULT ${formatDefaultValue(column.defaultValue)}`;
|
|
91
|
+
}
|
|
92
|
+
if (column.unique)
|
|
93
|
+
def += " UNIQUE";
|
|
94
|
+
return def;
|
|
95
|
+
}
|
|
96
|
+
function generateCreateTable(table) {
|
|
97
|
+
const columnDefs = table.columns.map((col) => generateColumnDefinition(col)).join(`,
|
|
98
|
+
`);
|
|
99
|
+
const fkConstraints = table.columns.filter((col) => col.foreignKey).map((col) => {
|
|
100
|
+
const fk = col.foreignKey;
|
|
101
|
+
return ` CONSTRAINT ${table.name}_${col.name}_fkey FOREIGN KEY ("${col.name}") REFERENCES ${fk.table}("${fk.column}") ON DELETE ${fk.onDelete || "SET NULL"}`;
|
|
102
|
+
});
|
|
103
|
+
const allDefs = [
|
|
104
|
+
` ${columnDefs}`,
|
|
105
|
+
` PRIMARY KEY (${table.primaryKey.map((pk) => `"${pk}"`).join(", ")})`,
|
|
106
|
+
...fkConstraints
|
|
107
|
+
].join(`,
|
|
108
|
+
`);
|
|
109
|
+
const fkIndexes = table.columns.filter((col) => col.foreignKey).map((col) => `CREATE INDEX IF NOT EXISTS ${table.name}_${col.name}_idx ON ${table.name}("${col.name}");`);
|
|
110
|
+
return [
|
|
111
|
+
`CREATE TABLE IF NOT EXISTS ${table.name} (
|
|
112
|
+
${allDefs}
|
|
113
|
+
);`,
|
|
114
|
+
...fkIndexes
|
|
115
|
+
].join(`
|
|
116
|
+
`);
|
|
117
|
+
}
|
|
118
|
+
function generateRenameColumn(tableName, oldName, newName) {
|
|
119
|
+
return `ALTER TABLE ${tableName} RENAME COLUMN "${oldName}" TO "${newName}";`;
|
|
120
|
+
}
|
|
121
|
+
function generateAddColumn(tableName, column) {
|
|
122
|
+
return `ALTER TABLE ${tableName} ADD COLUMN ${generateColumnDefinition(column)};`;
|
|
123
|
+
}
|
|
124
|
+
function generateSystemTables() {
|
|
125
|
+
const statements = [];
|
|
126
|
+
statements.push(`CREATE TABLE IF NOT EXISTS cequre_migrations (
|
|
127
|
+
id TEXT PRIMARY KEY,
|
|
128
|
+
version TEXT NOT NULL UNIQUE,
|
|
129
|
+
applied_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
130
|
+
description TEXT
|
|
131
|
+
);`);
|
|
132
|
+
statements.push(`CREATE TABLE IF NOT EXISTS cequre_refresh_tokens (
|
|
133
|
+
id TEXT PRIMARY KEY,
|
|
134
|
+
"tokenHash" TEXT NOT NULL,
|
|
135
|
+
"userId" TEXT NOT NULL,
|
|
136
|
+
collection TEXT NOT NULL,
|
|
137
|
+
role TEXT,
|
|
138
|
+
"expiresAt" INTEGER NOT NULL,
|
|
139
|
+
"createdAt" TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
140
|
+
"updatedAt" TEXT DEFAULT CURRENT_TIMESTAMP
|
|
141
|
+
);`);
|
|
142
|
+
return statements;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/adapter.ts
|
|
146
|
+
var _libsqlModule = null;
|
|
147
|
+
async function loadLibSQL() {
|
|
148
|
+
if (_libsqlModule)
|
|
149
|
+
return _libsqlModule;
|
|
150
|
+
try {
|
|
151
|
+
_libsqlModule = await import("@libsql/client");
|
|
152
|
+
return _libsqlModule;
|
|
153
|
+
} catch {
|
|
154
|
+
throw new Error("@libsql/client is not installed. Run: bun add @libsql/client");
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
var logger = createLogger("TursoAdapter");
|
|
158
|
+
|
|
159
|
+
class TursoAdapter extends BaseSqlAdapter {
|
|
160
|
+
adapterType = "turso";
|
|
161
|
+
supportsGeneratedConstraintSQL = true;
|
|
162
|
+
client = null;
|
|
163
|
+
config;
|
|
164
|
+
_syncTimer = null;
|
|
165
|
+
constructor(config) {
|
|
166
|
+
super();
|
|
167
|
+
this.config = config;
|
|
168
|
+
}
|
|
169
|
+
async connect() {
|
|
170
|
+
const libsql = await loadLibSQL();
|
|
171
|
+
const isDev = process.env.CEQURE_DEV === "1";
|
|
172
|
+
const cacheKey = `__cequre_turso_${this.config.url}`;
|
|
173
|
+
const globalState = isDev ? globalThis[cacheKey] : null;
|
|
174
|
+
if (globalState && globalState.client) {
|
|
175
|
+
this.client = globalState.client;
|
|
176
|
+
this._syncTimer = globalState.syncTimer;
|
|
177
|
+
logger.info({ url: this.config.url }, "Reusing existing Turso/libSQL connection");
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
this.client = libsql.createClient({
|
|
181
|
+
url: this.config.url,
|
|
182
|
+
authToken: this.config.authToken
|
|
183
|
+
});
|
|
184
|
+
await this.client.execute("SELECT 1");
|
|
185
|
+
logger.info({ url: this.config.url }, "Connected to Turso/libSQL");
|
|
186
|
+
if (this.config.syncIntervalMs && this.config.syncIntervalMs > 0) {
|
|
187
|
+
this._syncTimer = setInterval(() => {
|
|
188
|
+
this.client?.sync().catch((err) => logger.error({ err }, "Turso embedded replica sync failed"));
|
|
189
|
+
}, this.config.syncIntervalMs);
|
|
190
|
+
}
|
|
191
|
+
if (isDev) {
|
|
192
|
+
globalThis[cacheKey] = {
|
|
193
|
+
client: this.client,
|
|
194
|
+
syncTimer: this._syncTimer
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async disconnect() {
|
|
199
|
+
const isDev = process.env.CEQURE_DEV === "1";
|
|
200
|
+
if (isDev) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (this._syncTimer) {
|
|
204
|
+
clearInterval(this._syncTimer);
|
|
205
|
+
this._syncTimer = null;
|
|
206
|
+
}
|
|
207
|
+
if (this.client) {
|
|
208
|
+
await this.client.close();
|
|
209
|
+
this.client = null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async transaction(callback) {
|
|
213
|
+
if (!this.client)
|
|
214
|
+
throw new Error("Database not connected");
|
|
215
|
+
const tx = await this.client.transaction();
|
|
216
|
+
try {
|
|
217
|
+
const trxAdapter = Object.create(this);
|
|
218
|
+
trxAdapter.client = tx;
|
|
219
|
+
const result = await callback(trxAdapter);
|
|
220
|
+
await tx.commit();
|
|
221
|
+
return result;
|
|
222
|
+
} catch (err) {
|
|
223
|
+
await tx.rollback();
|
|
224
|
+
throw err;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async ping() {
|
|
228
|
+
await this.client.execute("SELECT 1");
|
|
229
|
+
}
|
|
230
|
+
getSystemTableStatements() {
|
|
231
|
+
return generateSystemTables();
|
|
232
|
+
}
|
|
233
|
+
async getCurrentSchema() {
|
|
234
|
+
const tablesResult = await this.client.execute("SELECT name FROM sqlite_master WHERE type='table'");
|
|
235
|
+
const tables = tablesResult.rows.map((r) => r.name);
|
|
236
|
+
const columns = {};
|
|
237
|
+
for (const table of tables) {
|
|
238
|
+
const colsResult = await this.client.execute({
|
|
239
|
+
sql: `PRAGMA table_info(${sqlIdentifier(table)})`,
|
|
240
|
+
args: []
|
|
241
|
+
});
|
|
242
|
+
columns[table] = colsResult.rows.map((c) => c.name);
|
|
243
|
+
}
|
|
244
|
+
return { tables, columns };
|
|
245
|
+
}
|
|
246
|
+
async recordMigration(version, description) {
|
|
247
|
+
const id = ulid();
|
|
248
|
+
await this.client.execute({
|
|
249
|
+
sql: "INSERT INTO cequre_migrations (id, version, description) VALUES (?, ?, ?)",
|
|
250
|
+
args: [id, version, description]
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
async getCurrentMigrationVersion() {
|
|
254
|
+
const result = await this.client.execute("SELECT version FROM cequre_migrations ORDER BY applied_at DESC LIMIT 1");
|
|
255
|
+
return result.rows[0]?.version ?? null;
|
|
256
|
+
}
|
|
257
|
+
createTableDDL(table) {
|
|
258
|
+
return generateCreateTable(buildTable(table));
|
|
259
|
+
}
|
|
260
|
+
renameColumnDDL(table, oldName, newName) {
|
|
261
|
+
return generateRenameColumn(table, oldName, newName);
|
|
262
|
+
}
|
|
263
|
+
addColumnDDL(table, column) {
|
|
264
|
+
const cols = buildColumns({ slug: table, fields: [column] });
|
|
265
|
+
const colDef = cols.find((c) => c.name === column.name);
|
|
266
|
+
if (!colDef)
|
|
267
|
+
return null;
|
|
268
|
+
return generateAddColumn(table, colDef);
|
|
269
|
+
}
|
|
270
|
+
dropColumnDDL(table, column) {
|
|
271
|
+
return `ALTER TABLE ${sqlIdentifier(table)} DROP COLUMN "${column}";`;
|
|
272
|
+
}
|
|
273
|
+
dropTableDDL(table) {
|
|
274
|
+
return `DROP TABLE IF EXISTS ${sqlIdentifier(table)};`;
|
|
275
|
+
}
|
|
276
|
+
deserializeRow(collection, row) {
|
|
277
|
+
if (!row)
|
|
278
|
+
return row;
|
|
279
|
+
const nums = this._numericFields.get(collection);
|
|
280
|
+
const jsons = this._jsonFields.get(collection);
|
|
281
|
+
const obj = { ...row };
|
|
282
|
+
if (nums) {
|
|
283
|
+
for (const k of nums) {
|
|
284
|
+
if (typeof obj[k] === "string") {
|
|
285
|
+
const parsed = Number(obj[k]);
|
|
286
|
+
if (!isNaN(parsed))
|
|
287
|
+
obj[k] = parsed;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (jsons) {
|
|
292
|
+
for (const k of jsons) {
|
|
293
|
+
if (typeof obj[k] === "string") {
|
|
294
|
+
try {
|
|
295
|
+
obj[k] = JSON.parse(obj[k]);
|
|
296
|
+
} catch {}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return obj;
|
|
301
|
+
}
|
|
302
|
+
buildWhere(where, collection, params) {
|
|
303
|
+
if (!where || Object.keys(where).length === 0)
|
|
304
|
+
return "";
|
|
305
|
+
const clauses = [];
|
|
306
|
+
if (where.OR) {
|
|
307
|
+
const orClauses = where.OR.map((cond) => this.buildWhere(cond, collection, params)).filter(Boolean);
|
|
308
|
+
if (orClauses.length > 0)
|
|
309
|
+
clauses.push(`(${orClauses.join(" OR ")})`);
|
|
310
|
+
}
|
|
311
|
+
if (where.AND) {
|
|
312
|
+
const andClauses = where.AND.map((cond) => this.buildWhere(cond, collection, params)).filter(Boolean);
|
|
313
|
+
if (andClauses.length > 0)
|
|
314
|
+
clauses.push(`(${andClauses.join(" AND ")})`);
|
|
315
|
+
}
|
|
316
|
+
for (const [key, value] of Object.entries(where)) {
|
|
317
|
+
if (key === "OR" || key === "AND")
|
|
318
|
+
continue;
|
|
319
|
+
let columnRef = `"${key}"`;
|
|
320
|
+
if (key.includes(".")) {
|
|
321
|
+
const parts = key.split(".");
|
|
322
|
+
const root = parts.shift();
|
|
323
|
+
const path = `$.${parts.join(".")}`;
|
|
324
|
+
columnRef = `json_extract("${root}", '${path}')`;
|
|
325
|
+
}
|
|
326
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
327
|
+
const ops = value;
|
|
328
|
+
const isSensitive = ops.mode === "sensitive";
|
|
329
|
+
for (const [op, val] of Object.entries(ops)) {
|
|
330
|
+
if (op === "mode")
|
|
331
|
+
continue;
|
|
332
|
+
if (op === "eq") {
|
|
333
|
+
params.push(val);
|
|
334
|
+
clauses.push(`${columnRef} = ?`);
|
|
335
|
+
} else if (op === "neq") {
|
|
336
|
+
params.push(val);
|
|
337
|
+
clauses.push(`${columnRef} != ?`);
|
|
338
|
+
} else if (op === "gt") {
|
|
339
|
+
params.push(val);
|
|
340
|
+
clauses.push(`${columnRef} > ?`);
|
|
341
|
+
} else if (op === "gte") {
|
|
342
|
+
params.push(val);
|
|
343
|
+
clauses.push(`${columnRef} >= ?`);
|
|
344
|
+
} else if (op === "lt") {
|
|
345
|
+
params.push(val);
|
|
346
|
+
clauses.push(`${columnRef} < ?`);
|
|
347
|
+
} else if (op === "lte") {
|
|
348
|
+
params.push(val);
|
|
349
|
+
clauses.push(`${columnRef} <= ?`);
|
|
350
|
+
} else if (op === "in" && Array.isArray(val) && val.length > 0) {
|
|
351
|
+
const placeholders = val.map((v) => {
|
|
352
|
+
params.push(v);
|
|
353
|
+
return "?";
|
|
354
|
+
}).join(", ");
|
|
355
|
+
clauses.push(`${columnRef} IN (${placeholders})`);
|
|
356
|
+
} else if (op === "nin" && Array.isArray(val) && val.length > 0) {
|
|
357
|
+
const placeholders = val.map((v) => {
|
|
358
|
+
params.push(v);
|
|
359
|
+
return "?";
|
|
360
|
+
}).join(", ");
|
|
361
|
+
clauses.push(`${columnRef} NOT IN (${placeholders})`);
|
|
362
|
+
} else if (op === "any" && Array.isArray(val) && val.length > 0) {
|
|
363
|
+
const placeholders = val.map((v) => {
|
|
364
|
+
params.push(v);
|
|
365
|
+
return "?";
|
|
366
|
+
}).join(", ");
|
|
367
|
+
clauses.push(`EXISTS (SELECT 1 FROM json_each(${columnRef}) WHERE value IN (${placeholders}))`);
|
|
368
|
+
} else if (op === "all" && Array.isArray(val) && val.length > 0) {
|
|
369
|
+
const allClauses = val.map((v) => {
|
|
370
|
+
params.push(v);
|
|
371
|
+
return `EXISTS (SELECT 1 FROM json_each(${columnRef}) WHERE value = ?)`;
|
|
372
|
+
});
|
|
373
|
+
clauses.push(`(${allClauses.join(" AND ")})`);
|
|
374
|
+
} else if (op === "like") {
|
|
375
|
+
params.push(val);
|
|
376
|
+
clauses.push(`${columnRef} ${isSensitive ? "GLOB" : "LIKE"} ?`);
|
|
377
|
+
} else if (op === "contains") {
|
|
378
|
+
params.push(isSensitive ? `*${val}*` : `%${val}%`);
|
|
379
|
+
clauses.push(`${columnRef} ${isSensitive ? "GLOB" : "LIKE"} ?`);
|
|
380
|
+
} else if (op === "startsWith") {
|
|
381
|
+
params.push(isSensitive ? `${val}*` : `${val}%`);
|
|
382
|
+
clauses.push(`${columnRef} ${isSensitive ? "GLOB" : "LIKE"} ?`);
|
|
383
|
+
} else if (op === "endsWith") {
|
|
384
|
+
params.push(isSensitive ? `*${val}` : `%${val}`);
|
|
385
|
+
clauses.push(`${columnRef} ${isSensitive ? "GLOB" : "LIKE"} ?`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
} else {
|
|
389
|
+
params.push(value);
|
|
390
|
+
clauses.push(`"${key}" = ?`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return clauses.join(" AND ");
|
|
394
|
+
}
|
|
395
|
+
async find(collection, query) {
|
|
396
|
+
if (!this.client)
|
|
397
|
+
throw new Error("Database not connected");
|
|
398
|
+
const params = [];
|
|
399
|
+
let sqlStr = `SELECT * FROM ${sqlIdentifier(collection)}`;
|
|
400
|
+
const whereStr = this.buildWhere(query.where, collection, params);
|
|
401
|
+
if (whereStr)
|
|
402
|
+
sqlStr += ` WHERE ${whereStr}`;
|
|
403
|
+
if (query.sort) {
|
|
404
|
+
const isDesc = query.sort.startsWith("-");
|
|
405
|
+
const field = isDesc ? query.sort.substring(1) : query.sort;
|
|
406
|
+
sqlStr += ` ORDER BY "${field}" ${isDesc ? "DESC" : "ASC"}`;
|
|
407
|
+
}
|
|
408
|
+
const limit = query.limit || 10;
|
|
409
|
+
const page = query.page || 1;
|
|
410
|
+
const offset = (page - 1) * limit;
|
|
411
|
+
sqlStr += ` LIMIT ${limit} OFFSET ${offset}`;
|
|
412
|
+
const result = await this.client.execute({ sql: sqlStr, args: params });
|
|
413
|
+
let countSql = `SELECT COUNT(*) as count FROM ${sqlIdentifier(collection)}`;
|
|
414
|
+
if (whereStr)
|
|
415
|
+
countSql += ` WHERE ${whereStr}`;
|
|
416
|
+
const countResult = await this.client.execute({ sql: countSql, args: params });
|
|
417
|
+
const totalDocs = Number(countResult.rows[0]?.count || 0);
|
|
418
|
+
const totalPages = Math.ceil(totalDocs / limit);
|
|
419
|
+
return {
|
|
420
|
+
docs: result.rows.map((r) => this.deserializeRow(collection, r)),
|
|
421
|
+
totalDocs,
|
|
422
|
+
limit,
|
|
423
|
+
page,
|
|
424
|
+
totalPages,
|
|
425
|
+
hasNextPage: page < totalPages,
|
|
426
|
+
hasPrevPage: page > 1
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
async findById(collection, id) {
|
|
430
|
+
if (!this.client)
|
|
431
|
+
throw new Error("Database not connected");
|
|
432
|
+
const result = await this.client.execute({
|
|
433
|
+
sql: `SELECT * FROM ${sqlIdentifier(collection)} WHERE id = ?`,
|
|
434
|
+
args: [id]
|
|
435
|
+
});
|
|
436
|
+
if (!result.rows || result.rows.length === 0)
|
|
437
|
+
return null;
|
|
438
|
+
return this.deserializeRow(collection, result.rows[0]);
|
|
439
|
+
}
|
|
440
|
+
async findByIds(collection, ids) {
|
|
441
|
+
if (!this.client)
|
|
442
|
+
throw new Error("Database not connected");
|
|
443
|
+
if (!ids.length)
|
|
444
|
+
return [];
|
|
445
|
+
const placeholders = ids.map(() => "?").join(", ");
|
|
446
|
+
const result = await this.client.execute({
|
|
447
|
+
sql: `SELECT * FROM ${sqlIdentifier(collection)} WHERE id IN (${placeholders})`,
|
|
448
|
+
args: ids
|
|
449
|
+
});
|
|
450
|
+
return result.rows.map((r) => this.deserializeRow(collection, r));
|
|
451
|
+
}
|
|
452
|
+
async create(collection, data) {
|
|
453
|
+
if (!this.client)
|
|
454
|
+
throw new Error("Database not connected");
|
|
455
|
+
if (!("id" in data) || data.id === undefined || data.id === null) {
|
|
456
|
+
data = { id: ulid(), ...data };
|
|
457
|
+
}
|
|
458
|
+
const keys = Object.keys(data);
|
|
459
|
+
const values = Object.values(data).map((v) => typeof v === "object" && v !== null && !(v instanceof Date) ? JSON.stringify(v) : v);
|
|
460
|
+
const keyStr = keys.map((k) => `"${k}"`).join(", ");
|
|
461
|
+
const valStr = keys.map(() => "?").join(", ");
|
|
462
|
+
const sqlStr = `INSERT INTO ${sqlIdentifier(collection)} (${keyStr}) VALUES (${valStr}) RETURNING *`;
|
|
463
|
+
const result = await this.client.execute({ sql: sqlStr, args: values });
|
|
464
|
+
return this.deserializeRow(collection, result.rows[0]);
|
|
465
|
+
}
|
|
466
|
+
async createMany(collection, docs) {
|
|
467
|
+
if (!this.client)
|
|
468
|
+
throw new Error("Database not connected");
|
|
469
|
+
if (!docs.length)
|
|
470
|
+
return [];
|
|
471
|
+
const knownFields = this._knownFields.get(collection);
|
|
472
|
+
const now = new Date().toISOString();
|
|
473
|
+
docs = docs.map((doc) => {
|
|
474
|
+
const paddedDoc = { ...doc };
|
|
475
|
+
if (!("id" in paddedDoc) || paddedDoc.id === undefined || paddedDoc.id === null) {
|
|
476
|
+
paddedDoc.id = ulid();
|
|
477
|
+
}
|
|
478
|
+
if (knownFields) {
|
|
479
|
+
for (const field of knownFields) {
|
|
480
|
+
if (!(field in paddedDoc))
|
|
481
|
+
paddedDoc[field] = null;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (!knownFields || knownFields.has("createdAt"))
|
|
485
|
+
paddedDoc.createdAt = paddedDoc.createdAt ?? now;
|
|
486
|
+
if (!knownFields || knownFields.has("updatedAt"))
|
|
487
|
+
paddedDoc.updatedAt = now;
|
|
488
|
+
return paddedDoc;
|
|
489
|
+
});
|
|
490
|
+
const keys = Object.keys(docs[0]);
|
|
491
|
+
const keyStr = keys.map((k) => `"${k}"`).join(", ");
|
|
492
|
+
const allValues = [];
|
|
493
|
+
const valStrs = docs.map((doc) => {
|
|
494
|
+
keys.forEach((k) => {
|
|
495
|
+
const v = doc[k];
|
|
496
|
+
allValues.push(typeof v === "object" && v !== null && !(v instanceof Date) ? JSON.stringify(v) : v);
|
|
497
|
+
});
|
|
498
|
+
return `(${keys.map(() => "?").join(", ")})`;
|
|
499
|
+
});
|
|
500
|
+
const sqlStr = `INSERT INTO ${sqlIdentifier(collection)} (${keyStr}) VALUES ${valStrs.join(", ")} RETURNING *`;
|
|
501
|
+
const result = await this.client.execute({ sql: sqlStr, args: allValues });
|
|
502
|
+
return result.rows.map((r) => this.deserializeRow(collection, r));
|
|
503
|
+
}
|
|
504
|
+
async update(collection, id, data) {
|
|
505
|
+
if (!this.client)
|
|
506
|
+
throw new Error("Database not connected");
|
|
507
|
+
const keys = Object.keys(data);
|
|
508
|
+
if (!keys.length)
|
|
509
|
+
return this.findById(collection, id);
|
|
510
|
+
const jsonFields = this._jsonFields.get(collection);
|
|
511
|
+
const setStrs = [];
|
|
512
|
+
const values = [];
|
|
513
|
+
for (const [k, v] of Object.entries(data)) {
|
|
514
|
+
const isJson = jsonFields?.has(k);
|
|
515
|
+
const isObject = typeof v === "object" && v !== null && !(v instanceof Date);
|
|
516
|
+
if (isJson && isObject) {
|
|
517
|
+
setStrs.push(`"${k}" = json_patch(IFNULL("${k}", '{}'), ?)`);
|
|
518
|
+
values.push(JSON.stringify(v));
|
|
519
|
+
} else {
|
|
520
|
+
setStrs.push(`"${k}" = ?`);
|
|
521
|
+
values.push(isObject ? JSON.stringify(v) : v);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
values.push(id);
|
|
525
|
+
const setStr = setStrs.join(", ");
|
|
526
|
+
const sqlStr = `UPDATE ${sqlIdentifier(collection)} SET ${setStr} WHERE id = ? RETURNING *`;
|
|
527
|
+
const result = await this.client.execute({ sql: sqlStr, args: values });
|
|
528
|
+
if (!result.rows || result.rows.length === 0)
|
|
529
|
+
throw new Error("Document not found");
|
|
530
|
+
return this.deserializeRow(collection, result.rows[0]);
|
|
531
|
+
}
|
|
532
|
+
async delete(collection, id) {
|
|
533
|
+
if (!this.client)
|
|
534
|
+
throw new Error("Database not connected");
|
|
535
|
+
await this.client.execute({
|
|
536
|
+
sql: `DELETE FROM ${sqlIdentifier(collection)} WHERE id = ?`,
|
|
537
|
+
args: [id]
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
async deleteMany(collection, ids) {
|
|
541
|
+
if (!this.client)
|
|
542
|
+
throw new Error("Database not connected");
|
|
543
|
+
if (!ids.length)
|
|
544
|
+
return;
|
|
545
|
+
const placeholders = ids.map(() => "?").join(", ");
|
|
546
|
+
await this.client.execute({
|
|
547
|
+
sql: `DELETE FROM ${sqlIdentifier(collection)} WHERE id IN (${placeholders})`,
|
|
548
|
+
args: ids
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
async count(collection, query) {
|
|
552
|
+
if (!this.client)
|
|
553
|
+
throw new Error("Database not connected");
|
|
554
|
+
const params = [];
|
|
555
|
+
let sqlStr = `SELECT COUNT(*) as count FROM ${sqlIdentifier(collection)}`;
|
|
556
|
+
if (query?.where) {
|
|
557
|
+
const whereStr = this.buildWhere(query.where, collection, params);
|
|
558
|
+
if (whereStr)
|
|
559
|
+
sqlStr += ` WHERE ${whereStr}`;
|
|
560
|
+
}
|
|
561
|
+
const result = await this.client.execute({ sql: sqlStr, args: params });
|
|
562
|
+
return Number(result.rows[0]?.count || 0);
|
|
563
|
+
}
|
|
564
|
+
async raw(sql, params) {
|
|
565
|
+
if (!this.client)
|
|
566
|
+
throw new Error("Database not connected");
|
|
567
|
+
try {
|
|
568
|
+
const result = await this.client.execute({ sql, args: params || [] });
|
|
569
|
+
return result.rows;
|
|
570
|
+
} catch (e) {
|
|
571
|
+
if (e.code === "SQL_MANY_STATEMENTS" || e.message && e.message.includes("more than one statement")) {
|
|
572
|
+
if (params && params.length > 0) {
|
|
573
|
+
throw new Error("Cannot execute multiple statements with parameterized queries.");
|
|
574
|
+
}
|
|
575
|
+
await this.client.executeMultiple(sql);
|
|
576
|
+
return [];
|
|
577
|
+
}
|
|
578
|
+
throw e;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/index.ts
|
|
584
|
+
function turso(config) {
|
|
585
|
+
return new TursoAdapter(config);
|
|
586
|
+
}
|
|
587
|
+
export {
|
|
588
|
+
turso,
|
|
589
|
+
TursoAdapter
|
|
590
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cequrebackends/plugin-turso",
|
|
3
|
+
"version": "0.14.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "rm -rf dist && bun build ./src/index.ts --outdir ./dist --target bun --packages external && tsc --emitDeclarationOnly",
|
|
13
|
+
"dev": "bun build ./src/index.ts --outdir ./dist --target bun --packages external --watch"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"@libsql/client": "^0.17.4",
|
|
17
|
+
"@cequrebackends/cequre-ts": "0.13.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"typescript": "^7.0.1-rc",
|
|
21
|
+
"@cequrebackends/cequre-ts": "0.13.0"
|
|
22
|
+
},
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public",
|
|
33
|
+
"registry": "https://registry.npmjs.org/"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/cequrebackends/cequre.git"
|
|
38
|
+
}
|
|
39
|
+
}
|