@tursodatabase/serverless 0.1.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 +78 -0
- package/dist/client.d.ts +31 -0
- package/dist/client.js +184 -0
- package/dist/compat/index.d.ts +1 -0
- package/dist/compat/index.js +1 -0
- package/dist/compat.d.ts +136 -0
- package/dist/compat.js +177 -0
- package/dist/connection.d.ts +82 -0
- package/dist/connection.js +86 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/protocol.d.ts +89 -0
- package/dist/protocol.js +128 -0
- package/dist/session.d.ts +63 -0
- package/dist/session.js +176 -0
- package/dist/statement.d.ts +64 -0
- package/dist/statement.js +94 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Turso serverless JavaScript driver
|
|
2
|
+
|
|
3
|
+
A serverless database driver for Turso Cloud, using only `fetch()`. Connect to your database from serverless and edge functions, such as Cloudflare Workers and Vercel.
|
|
4
|
+
|
|
5
|
+
> [!NOTE]
|
|
6
|
+
> This driver is experimental and, therefore, subject to change at any time.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @tursodatabase/serverless
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import { connect } from "@tursodatabase/serverless";
|
|
18
|
+
|
|
19
|
+
const conn = connect({
|
|
20
|
+
url: process.env.TURSO_DATABASE_URL,
|
|
21
|
+
authToken: process.env.TURSO_AUTH_TOKEN,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Prepare a statement
|
|
25
|
+
const stmt = conn.prepare("SELECT * FROM users WHERE id = ?");
|
|
26
|
+
|
|
27
|
+
// Get first row
|
|
28
|
+
const row = await stmt.get([123]);
|
|
29
|
+
console.log(row);
|
|
30
|
+
|
|
31
|
+
// Get all rows
|
|
32
|
+
const rows = await stmt.all([123]);
|
|
33
|
+
console.log(rows);
|
|
34
|
+
|
|
35
|
+
// Iterate through rows (streaming)
|
|
36
|
+
for await (const row of stmt.iterate([123])) {
|
|
37
|
+
console.log(row);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Execute multiple statements in a batch
|
|
41
|
+
await conn.batch([
|
|
42
|
+
"CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, email TEXT)",
|
|
43
|
+
"INSERT INTO users (email) VALUES ('user@example.com')",
|
|
44
|
+
"INSERT INTO users (email) VALUES ('admin@example.com')",
|
|
45
|
+
]);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Compatibility layer for libSQL API
|
|
49
|
+
|
|
50
|
+
This driver supports the libSQL API as a compatibility layer.
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
import { createClient } from "@tursodatabase/serverless/compat";
|
|
54
|
+
|
|
55
|
+
const client = createClient({
|
|
56
|
+
url: process.env.TURSO_DATABASE_URL,
|
|
57
|
+
authToken: process.env.TURSO_AUTH_TOKEN,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Execute a single SQL statement
|
|
61
|
+
const result = await client.execute("SELECT * FROM users WHERE id = ?", [123]);
|
|
62
|
+
console.log(result.rows);
|
|
63
|
+
|
|
64
|
+
// Execute multiple statements in a batch
|
|
65
|
+
await client.batch([
|
|
66
|
+
"CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, email TEXT)",
|
|
67
|
+
"INSERT INTO users (email) VALUES ('user@example.com')",
|
|
68
|
+
"INSERT INTO users (email) VALUES ('admin@example.com')",
|
|
69
|
+
]);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Examples
|
|
73
|
+
|
|
74
|
+
Check out the `examples/` directory for complete usage examples.
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type CursorResponse, type CursorEntry } from './protocol.js';
|
|
2
|
+
export interface Config {
|
|
3
|
+
url: string;
|
|
4
|
+
authToken: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class Statement {
|
|
7
|
+
private connection;
|
|
8
|
+
private sql;
|
|
9
|
+
private args;
|
|
10
|
+
constructor(connection: Connection, sql: string, args?: any[]);
|
|
11
|
+
get(): Promise<any>;
|
|
12
|
+
all(): Promise<any[]>;
|
|
13
|
+
iterate(): AsyncGenerator<any>;
|
|
14
|
+
private execute;
|
|
15
|
+
}
|
|
16
|
+
export declare class Connection {
|
|
17
|
+
private config;
|
|
18
|
+
private baton;
|
|
19
|
+
private baseUrl;
|
|
20
|
+
constructor(config: Config);
|
|
21
|
+
prepare(sql: string, args?: any[]): Statement;
|
|
22
|
+
execute(sql: string, args?: any[]): Promise<any>;
|
|
23
|
+
executeRaw(sql: string, args?: any[]): Promise<{
|
|
24
|
+
response: CursorResponse;
|
|
25
|
+
entries: AsyncGenerator<CursorEntry>;
|
|
26
|
+
}>;
|
|
27
|
+
private processCursorEntries;
|
|
28
|
+
createRowObject(values: any[], columns: string[]): any;
|
|
29
|
+
batch(statements: string[], mode?: string): Promise<any>;
|
|
30
|
+
}
|
|
31
|
+
export declare function connect(config: Config): Connection;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { executeCursor, encodeValue, decodeValue } from './protocol.js';
|
|
2
|
+
function normalizeUrl(url) {
|
|
3
|
+
return url.replace(/^libsql:\/\//, 'https://');
|
|
4
|
+
}
|
|
5
|
+
function isValidIdentifier(str) {
|
|
6
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str);
|
|
7
|
+
}
|
|
8
|
+
export class Statement {
|
|
9
|
+
constructor(connection, sql, args = []) {
|
|
10
|
+
this.connection = connection;
|
|
11
|
+
this.sql = sql;
|
|
12
|
+
this.args = args;
|
|
13
|
+
}
|
|
14
|
+
async get() {
|
|
15
|
+
const result = await this.execute();
|
|
16
|
+
return result.rows[0] || null;
|
|
17
|
+
}
|
|
18
|
+
async all() {
|
|
19
|
+
const result = await this.execute();
|
|
20
|
+
return result.rows;
|
|
21
|
+
}
|
|
22
|
+
async *iterate() {
|
|
23
|
+
const { response, entries } = await this.connection.executeRaw(this.sql, this.args);
|
|
24
|
+
let columns = [];
|
|
25
|
+
for await (const entry of entries) {
|
|
26
|
+
switch (entry.type) {
|
|
27
|
+
case 'step_begin':
|
|
28
|
+
if (entry.cols) {
|
|
29
|
+
columns = entry.cols.map(col => col.name);
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
case 'row':
|
|
33
|
+
if (entry.row) {
|
|
34
|
+
const decodedRow = entry.row.map(decodeValue);
|
|
35
|
+
const rowObject = this.connection.createRowObject(decodedRow, columns);
|
|
36
|
+
yield rowObject;
|
|
37
|
+
}
|
|
38
|
+
break;
|
|
39
|
+
case 'step_error':
|
|
40
|
+
case 'error':
|
|
41
|
+
throw new Error(entry.error?.message || 'SQL execution failed');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async execute() {
|
|
46
|
+
return this.connection.execute(this.sql, this.args);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export class Connection {
|
|
50
|
+
constructor(config) {
|
|
51
|
+
this.baton = null;
|
|
52
|
+
this.config = config;
|
|
53
|
+
this.baseUrl = normalizeUrl(config.url);
|
|
54
|
+
}
|
|
55
|
+
prepare(sql, args = []) {
|
|
56
|
+
return new Statement(this, sql, args);
|
|
57
|
+
}
|
|
58
|
+
async execute(sql, args = []) {
|
|
59
|
+
const { response, entries } = await this.executeRaw(sql, args);
|
|
60
|
+
const result = await this.processCursorEntries(entries);
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
async executeRaw(sql, args = []) {
|
|
64
|
+
const request = {
|
|
65
|
+
baton: this.baton,
|
|
66
|
+
batch: {
|
|
67
|
+
steps: [{
|
|
68
|
+
stmt: {
|
|
69
|
+
sql,
|
|
70
|
+
args: args.map(encodeValue),
|
|
71
|
+
want_rows: true
|
|
72
|
+
}
|
|
73
|
+
}]
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const { response, entries } = await executeCursor(this.baseUrl, this.config.authToken, request);
|
|
77
|
+
this.baton = response.baton;
|
|
78
|
+
if (response.base_url) {
|
|
79
|
+
this.baseUrl = response.base_url;
|
|
80
|
+
}
|
|
81
|
+
return { response, entries };
|
|
82
|
+
}
|
|
83
|
+
async processCursorEntries(entries) {
|
|
84
|
+
let columns = [];
|
|
85
|
+
let columnTypes = [];
|
|
86
|
+
let rows = [];
|
|
87
|
+
let rowsAffected = 0;
|
|
88
|
+
let lastInsertRowid;
|
|
89
|
+
for await (const entry of entries) {
|
|
90
|
+
switch (entry.type) {
|
|
91
|
+
case 'step_begin':
|
|
92
|
+
if (entry.cols) {
|
|
93
|
+
columns = entry.cols.map(col => col.name);
|
|
94
|
+
columnTypes = entry.cols.map(col => col.decltype || '');
|
|
95
|
+
}
|
|
96
|
+
break;
|
|
97
|
+
case 'row':
|
|
98
|
+
if (entry.row) {
|
|
99
|
+
const decodedRow = entry.row.map(decodeValue);
|
|
100
|
+
const rowObject = this.createRowObject(decodedRow, columns);
|
|
101
|
+
rows.push(rowObject);
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
case 'step_end':
|
|
105
|
+
if (entry.affected_row_count !== undefined) {
|
|
106
|
+
rowsAffected = entry.affected_row_count;
|
|
107
|
+
}
|
|
108
|
+
if (entry.last_insert_rowid) {
|
|
109
|
+
lastInsertRowid = parseInt(entry.last_insert_rowid, 10);
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
case 'step_error':
|
|
113
|
+
case 'error':
|
|
114
|
+
throw new Error(entry.error?.message || 'SQL execution failed');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
columns,
|
|
119
|
+
columnTypes,
|
|
120
|
+
rows,
|
|
121
|
+
rowsAffected,
|
|
122
|
+
lastInsertRowid
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
createRowObject(values, columns) {
|
|
126
|
+
const row = [...values];
|
|
127
|
+
// Add column name properties to the array as non-enumerable
|
|
128
|
+
// Only add valid identifier names to avoid conflicts
|
|
129
|
+
columns.forEach((column, index) => {
|
|
130
|
+
if (column && isValidIdentifier(column)) {
|
|
131
|
+
Object.defineProperty(row, column, {
|
|
132
|
+
value: values[index],
|
|
133
|
+
enumerable: false,
|
|
134
|
+
writable: false,
|
|
135
|
+
configurable: true
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
return row;
|
|
140
|
+
}
|
|
141
|
+
async batch(statements, mode) {
|
|
142
|
+
const request = {
|
|
143
|
+
baton: this.baton,
|
|
144
|
+
batch: {
|
|
145
|
+
steps: statements.map(sql => ({
|
|
146
|
+
stmt: {
|
|
147
|
+
sql,
|
|
148
|
+
args: [],
|
|
149
|
+
want_rows: false
|
|
150
|
+
}
|
|
151
|
+
}))
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
const { response, entries } = await executeCursor(this.baseUrl, this.config.authToken, request);
|
|
155
|
+
this.baton = response.baton;
|
|
156
|
+
if (response.base_url) {
|
|
157
|
+
this.baseUrl = response.base_url;
|
|
158
|
+
}
|
|
159
|
+
let totalRowsAffected = 0;
|
|
160
|
+
let lastInsertRowid;
|
|
161
|
+
for await (const entry of entries) {
|
|
162
|
+
switch (entry.type) {
|
|
163
|
+
case 'step_end':
|
|
164
|
+
if (entry.affected_row_count !== undefined) {
|
|
165
|
+
totalRowsAffected += entry.affected_row_count;
|
|
166
|
+
}
|
|
167
|
+
if (entry.last_insert_rowid) {
|
|
168
|
+
lastInsertRowid = parseInt(entry.last_insert_rowid, 10);
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
case 'step_error':
|
|
172
|
+
case 'error':
|
|
173
|
+
throw new Error(entry.error?.message || 'Batch execution failed');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
rowsAffected: totalRowsAffected,
|
|
178
|
+
lastInsertRowid
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export function connect(config) {
|
|
183
|
+
return new Connection(config);
|
|
184
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../compat.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../compat.js';
|
package/dist/compat.d.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for creating a libSQL-compatible client.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* This interface matches the libSQL client configuration but only `url` and `authToken`
|
|
6
|
+
* are supported in the serverless compatibility layer. Other options will throw validation errors.
|
|
7
|
+
*/
|
|
8
|
+
export interface Config {
|
|
9
|
+
/** Database URL (required) */
|
|
10
|
+
url: string;
|
|
11
|
+
/** Authentication token for the database */
|
|
12
|
+
authToken?: string;
|
|
13
|
+
/** @deprecated Database encryption key - not supported in serverless mode */
|
|
14
|
+
encryptionKey?: string;
|
|
15
|
+
/** @deprecated Sync server URL - not supported in serverless mode */
|
|
16
|
+
syncUrl?: string;
|
|
17
|
+
/** @deprecated Sync frequency in seconds - not supported in serverless mode */
|
|
18
|
+
syncInterval?: number;
|
|
19
|
+
/** @deprecated Consistency mode - not supported in serverless mode */
|
|
20
|
+
readYourWrites?: boolean;
|
|
21
|
+
/** @deprecated Offline mode support - not supported in serverless mode */
|
|
22
|
+
offline?: boolean;
|
|
23
|
+
/** @deprecated TLS settings - not supported in serverless mode */
|
|
24
|
+
tls?: boolean;
|
|
25
|
+
/** @deprecated Integer handling mode - not supported in serverless mode */
|
|
26
|
+
intMode?: "number" | "bigint" | "string";
|
|
27
|
+
/** @deprecated Custom fetch implementation - not supported in serverless mode */
|
|
28
|
+
fetch?: Function;
|
|
29
|
+
/** @deprecated Concurrent request limit - not supported in serverless mode */
|
|
30
|
+
concurrency?: number;
|
|
31
|
+
}
|
|
32
|
+
/** Input value types accepted by libSQL statements */
|
|
33
|
+
export type InValue = null | string | number | bigint | ArrayBuffer | boolean | Uint8Array | Date;
|
|
34
|
+
/** Input arguments - either positional array or named object */
|
|
35
|
+
export type InArgs = Array<InValue> | Record<string, InValue>;
|
|
36
|
+
/** Input statement - either SQL string or object with sql and args */
|
|
37
|
+
export type InStatement = {
|
|
38
|
+
sql: string;
|
|
39
|
+
args?: InArgs;
|
|
40
|
+
} | string;
|
|
41
|
+
/** Transaction execution modes */
|
|
42
|
+
export type TransactionMode = "write" | "read" | "deferred";
|
|
43
|
+
/**
|
|
44
|
+
* A result row that can be accessed both as an array and as an object.
|
|
45
|
+
* Supports both numeric indexing (row[0]) and column name access (row.column_name).
|
|
46
|
+
*/
|
|
47
|
+
export interface Row {
|
|
48
|
+
length: number;
|
|
49
|
+
[index: number]: InValue;
|
|
50
|
+
[name: string]: InValue;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Result set returned from SQL statement execution.
|
|
54
|
+
*/
|
|
55
|
+
export interface ResultSet {
|
|
56
|
+
/** Column names in the result set */
|
|
57
|
+
columns: Array<string>;
|
|
58
|
+
/** Column type information */
|
|
59
|
+
columnTypes: Array<string>;
|
|
60
|
+
/** Result rows */
|
|
61
|
+
rows: Array<Row>;
|
|
62
|
+
/** Number of rows affected by the statement */
|
|
63
|
+
rowsAffected: number;
|
|
64
|
+
/** ID of the last inserted row (for INSERT statements) */
|
|
65
|
+
lastInsertRowid: bigint | undefined;
|
|
66
|
+
/** Convert result set to JSON */
|
|
67
|
+
toJSON(): any;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* libSQL-compatible error class with error codes.
|
|
71
|
+
*/
|
|
72
|
+
export declare class LibsqlError extends Error {
|
|
73
|
+
/** Machine-readable error code */
|
|
74
|
+
code: string;
|
|
75
|
+
/** Raw numeric error code (if available) */
|
|
76
|
+
rawCode?: number;
|
|
77
|
+
constructor(message: string, code: string, rawCode?: number);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Interactive transaction interface (not implemented in serverless mode).
|
|
81
|
+
*
|
|
82
|
+
* @remarks
|
|
83
|
+
* Transactions are not supported in the serverless compatibility layer.
|
|
84
|
+
* Calling transaction() will throw a LibsqlError.
|
|
85
|
+
*/
|
|
86
|
+
export interface Transaction {
|
|
87
|
+
execute(stmt: InStatement): Promise<ResultSet>;
|
|
88
|
+
batch(stmts: Array<InStatement>): Promise<Array<ResultSet>>;
|
|
89
|
+
executeMultiple(sql: string): Promise<void>;
|
|
90
|
+
commit(): Promise<void>;
|
|
91
|
+
rollback(): Promise<void>;
|
|
92
|
+
close(): void;
|
|
93
|
+
closed: boolean;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* libSQL-compatible client interface.
|
|
97
|
+
*
|
|
98
|
+
* This interface matches the standard libSQL client API for drop-in compatibility.
|
|
99
|
+
* Some methods are not implemented in the serverless compatibility layer.
|
|
100
|
+
*/
|
|
101
|
+
export interface Client {
|
|
102
|
+
execute(stmt: InStatement): Promise<ResultSet>;
|
|
103
|
+
execute(sql: string, args?: InArgs): Promise<ResultSet>;
|
|
104
|
+
batch(stmts: Array<InStatement>, mode?: TransactionMode): Promise<Array<ResultSet>>;
|
|
105
|
+
migrate(stmts: Array<InStatement>): Promise<Array<ResultSet>>;
|
|
106
|
+
transaction(mode?: TransactionMode): Promise<Transaction>;
|
|
107
|
+
executeMultiple(sql: string): Promise<void>;
|
|
108
|
+
sync(): Promise<any>;
|
|
109
|
+
close(): void;
|
|
110
|
+
closed: boolean;
|
|
111
|
+
protocol: string;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Create a libSQL-compatible client for Turso database access.
|
|
115
|
+
*
|
|
116
|
+
* This function provides compatibility with the standard libSQL client API
|
|
117
|
+
* while using the Turso serverless driver under the hood.
|
|
118
|
+
*
|
|
119
|
+
* @param config - Configuration object (only url and authToken are supported)
|
|
120
|
+
* @returns A Client instance compatible with libSQL API
|
|
121
|
+
* @throws LibsqlError if unsupported configuration options are provided
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* import { createClient } from "@tursodatabase/serverless/compat";
|
|
126
|
+
*
|
|
127
|
+
* const client = createClient({
|
|
128
|
+
* url: process.env.TURSO_DATABASE_URL,
|
|
129
|
+
* authToken: process.env.TURSO_AUTH_TOKEN
|
|
130
|
+
* });
|
|
131
|
+
*
|
|
132
|
+
* const result = await client.execute("SELECT * FROM users");
|
|
133
|
+
* console.log(result.rows);
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export declare function createClient(config: Config): Client;
|
package/dist/compat.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { connect } from './connection.js';
|
|
2
|
+
/**
|
|
3
|
+
* libSQL-compatible error class with error codes.
|
|
4
|
+
*/
|
|
5
|
+
export class LibsqlError extends Error {
|
|
6
|
+
constructor(message, code, rawCode) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'LibsqlError';
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.rawCode = rawCode;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
class LibSQLClient {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this._closed = false;
|
|
16
|
+
this.validateConfig(config);
|
|
17
|
+
const tursoConfig = {
|
|
18
|
+
url: config.url,
|
|
19
|
+
authToken: config.authToken || ''
|
|
20
|
+
};
|
|
21
|
+
this.connection = connect(tursoConfig);
|
|
22
|
+
}
|
|
23
|
+
validateConfig(config) {
|
|
24
|
+
// Check for unsupported config options
|
|
25
|
+
const unsupportedOptions = [];
|
|
26
|
+
if (config.encryptionKey !== undefined) {
|
|
27
|
+
unsupportedOptions.push({ key: 'encryptionKey', value: config.encryptionKey });
|
|
28
|
+
}
|
|
29
|
+
if (config.syncUrl !== undefined) {
|
|
30
|
+
unsupportedOptions.push({ key: 'syncUrl', value: config.syncUrl });
|
|
31
|
+
}
|
|
32
|
+
if (config.syncInterval !== undefined) {
|
|
33
|
+
unsupportedOptions.push({ key: 'syncInterval', value: config.syncInterval });
|
|
34
|
+
}
|
|
35
|
+
if (config.readYourWrites !== undefined) {
|
|
36
|
+
unsupportedOptions.push({ key: 'readYourWrites', value: config.readYourWrites });
|
|
37
|
+
}
|
|
38
|
+
if (config.offline !== undefined) {
|
|
39
|
+
unsupportedOptions.push({ key: 'offline', value: config.offline });
|
|
40
|
+
}
|
|
41
|
+
if (config.tls !== undefined) {
|
|
42
|
+
unsupportedOptions.push({ key: 'tls', value: config.tls });
|
|
43
|
+
}
|
|
44
|
+
if (config.intMode !== undefined) {
|
|
45
|
+
unsupportedOptions.push({ key: 'intMode', value: config.intMode });
|
|
46
|
+
}
|
|
47
|
+
if (config.fetch !== undefined) {
|
|
48
|
+
unsupportedOptions.push({ key: 'fetch', value: config.fetch });
|
|
49
|
+
}
|
|
50
|
+
if (config.concurrency !== undefined) {
|
|
51
|
+
unsupportedOptions.push({ key: 'concurrency', value: config.concurrency });
|
|
52
|
+
}
|
|
53
|
+
if (unsupportedOptions.length > 0) {
|
|
54
|
+
const optionsList = unsupportedOptions.map(opt => `'${opt.key}'`).join(', ');
|
|
55
|
+
throw new LibsqlError(`Unsupported configuration options: ${optionsList}. Only 'url' and 'authToken' are supported in the serverless compatibility layer.`, "UNSUPPORTED_CONFIG");
|
|
56
|
+
}
|
|
57
|
+
// Validate required options
|
|
58
|
+
if (!config.url) {
|
|
59
|
+
throw new LibsqlError("Missing required 'url' configuration option", "MISSING_URL");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
get closed() {
|
|
63
|
+
return this._closed;
|
|
64
|
+
}
|
|
65
|
+
get protocol() {
|
|
66
|
+
return "http";
|
|
67
|
+
}
|
|
68
|
+
normalizeStatement(stmt) {
|
|
69
|
+
if (typeof stmt === 'string') {
|
|
70
|
+
return { sql: stmt, args: [] };
|
|
71
|
+
}
|
|
72
|
+
const args = stmt.args || [];
|
|
73
|
+
if (Array.isArray(args)) {
|
|
74
|
+
return { sql: stmt.sql, args };
|
|
75
|
+
}
|
|
76
|
+
// Convert named args to positional args (simplified)
|
|
77
|
+
return { sql: stmt.sql, args: Object.values(args) };
|
|
78
|
+
}
|
|
79
|
+
convertResult(result) {
|
|
80
|
+
const resultSet = {
|
|
81
|
+
columns: result.columns || [],
|
|
82
|
+
columnTypes: result.columnTypes || [],
|
|
83
|
+
rows: result.rows || [],
|
|
84
|
+
rowsAffected: result.rowsAffected || 0,
|
|
85
|
+
lastInsertRowid: result.lastInsertRowid ? BigInt(result.lastInsertRowid) : undefined,
|
|
86
|
+
toJSON() {
|
|
87
|
+
return {
|
|
88
|
+
columns: this.columns,
|
|
89
|
+
columnTypes: this.columnTypes,
|
|
90
|
+
rows: this.rows,
|
|
91
|
+
rowsAffected: this.rowsAffected,
|
|
92
|
+
lastInsertRowid: this.lastInsertRowid?.toString()
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return resultSet;
|
|
97
|
+
}
|
|
98
|
+
async execute(stmtOrSql, args) {
|
|
99
|
+
try {
|
|
100
|
+
if (this._closed) {
|
|
101
|
+
throw new LibsqlError("Client is closed", "CLIENT_CLOSED");
|
|
102
|
+
}
|
|
103
|
+
let normalizedStmt;
|
|
104
|
+
if (typeof stmtOrSql === 'string') {
|
|
105
|
+
const normalizedArgs = args ? (Array.isArray(args) ? args : Object.values(args)) : [];
|
|
106
|
+
normalizedStmt = { sql: stmtOrSql, args: normalizedArgs };
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
normalizedStmt = this.normalizeStatement(stmtOrSql);
|
|
110
|
+
}
|
|
111
|
+
const result = await this.connection.execute(normalizedStmt.sql, normalizedStmt.args);
|
|
112
|
+
return this.convertResult(result);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
throw new LibsqlError(error.message, "EXECUTE_ERROR");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async batch(stmts, mode) {
|
|
119
|
+
try {
|
|
120
|
+
if (this._closed) {
|
|
121
|
+
throw new LibsqlError("Client is closed", "CLIENT_CLOSED");
|
|
122
|
+
}
|
|
123
|
+
const sqlStatements = stmts.map(stmt => {
|
|
124
|
+
const normalized = this.normalizeStatement(stmt);
|
|
125
|
+
return normalized.sql; // For now, ignore args in batch
|
|
126
|
+
});
|
|
127
|
+
const result = await this.connection.batch(sqlStatements, mode);
|
|
128
|
+
// Return array of result sets (simplified - actual implementation would be more complex)
|
|
129
|
+
return [this.convertResult(result)];
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
throw new LibsqlError(error.message, "BATCH_ERROR");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async migrate(stmts) {
|
|
136
|
+
// For now, just call batch - in a real implementation this would disable foreign keys
|
|
137
|
+
return this.batch(stmts, "write");
|
|
138
|
+
}
|
|
139
|
+
async transaction(mode) {
|
|
140
|
+
throw new LibsqlError("Transactions not implemented", "NOT_IMPLEMENTED");
|
|
141
|
+
}
|
|
142
|
+
async executeMultiple(sql) {
|
|
143
|
+
throw new LibsqlError("Execute multiple not implemented", "NOT_IMPLEMENTED");
|
|
144
|
+
}
|
|
145
|
+
async sync() {
|
|
146
|
+
throw new LibsqlError("Sync not supported for remote databases", "NOT_SUPPORTED");
|
|
147
|
+
}
|
|
148
|
+
close() {
|
|
149
|
+
this._closed = true;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Create a libSQL-compatible client for Turso database access.
|
|
154
|
+
*
|
|
155
|
+
* This function provides compatibility with the standard libSQL client API
|
|
156
|
+
* while using the Turso serverless driver under the hood.
|
|
157
|
+
*
|
|
158
|
+
* @param config - Configuration object (only url and authToken are supported)
|
|
159
|
+
* @returns A Client instance compatible with libSQL API
|
|
160
|
+
* @throws LibsqlError if unsupported configuration options are provided
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* import { createClient } from "@tursodatabase/serverless/compat";
|
|
165
|
+
*
|
|
166
|
+
* const client = createClient({
|
|
167
|
+
* url: process.env.TURSO_DATABASE_URL,
|
|
168
|
+
* authToken: process.env.TURSO_AUTH_TOKEN
|
|
169
|
+
* });
|
|
170
|
+
*
|
|
171
|
+
* const result = await client.execute("SELECT * FROM users");
|
|
172
|
+
* console.log(result.rows);
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
export function createClient(config) {
|
|
176
|
+
return new LibSQLClient(config);
|
|
177
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type SessionConfig } from './session.js';
|
|
2
|
+
import { Statement } from './statement.js';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration options for connecting to a Turso database.
|
|
5
|
+
*/
|
|
6
|
+
export interface Config extends SessionConfig {
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A connection to a Turso database.
|
|
10
|
+
*
|
|
11
|
+
* Provides methods for executing SQL statements and managing prepared statements.
|
|
12
|
+
* Uses the SQL over HTTP protocol with streaming cursor support for optimal performance.
|
|
13
|
+
*/
|
|
14
|
+
export declare class Connection {
|
|
15
|
+
private config;
|
|
16
|
+
private session;
|
|
17
|
+
constructor(config: Config);
|
|
18
|
+
/**
|
|
19
|
+
* Prepare a SQL statement for execution.
|
|
20
|
+
*
|
|
21
|
+
* Each prepared statement gets its own session to avoid conflicts during concurrent execution.
|
|
22
|
+
*
|
|
23
|
+
* @param sql - The SQL statement to prepare
|
|
24
|
+
* @returns A Statement object that can be executed multiple ways
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const stmt = client.prepare("SELECT * FROM users WHERE id = ?");
|
|
29
|
+
* const user = await stmt.get([123]);
|
|
30
|
+
* const allUsers = await stmt.all();
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
prepare(sql: string): Statement;
|
|
34
|
+
/**
|
|
35
|
+
* Execute a SQL statement and return all results.
|
|
36
|
+
*
|
|
37
|
+
* @param sql - The SQL statement to execute
|
|
38
|
+
* @param args - Optional array of parameter values
|
|
39
|
+
* @returns Promise resolving to the complete result set
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const result = await client.execute("SELECT * FROM users");
|
|
44
|
+
* console.log(result.rows);
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
execute(sql: string, args?: any[]): Promise<any>;
|
|
48
|
+
/**
|
|
49
|
+
* Execute multiple SQL statements in a batch.
|
|
50
|
+
*
|
|
51
|
+
* @param statements - Array of SQL statements to execute
|
|
52
|
+
* @param mode - Optional transaction mode (currently unused)
|
|
53
|
+
* @returns Promise resolving to batch execution results
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* await client.batch([
|
|
58
|
+
* "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)",
|
|
59
|
+
* "INSERT INTO users (name) VALUES ('Alice')",
|
|
60
|
+
* "INSERT INTO users (name) VALUES ('Bob')"
|
|
61
|
+
* ]);
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
batch(statements: string[], mode?: string): Promise<any>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Create a new connection to a Turso database.
|
|
68
|
+
*
|
|
69
|
+
* @param config - Configuration object with database URL and auth token
|
|
70
|
+
* @returns A new Connection instance
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* import { connect } from "@tursodatabase/serverless";
|
|
75
|
+
*
|
|
76
|
+
* const client = connect({
|
|
77
|
+
* url: process.env.TURSO_DATABASE_URL,
|
|
78
|
+
* authToken: process.env.TURSO_AUTH_TOKEN
|
|
79
|
+
* });
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare function connect(config: Config): Connection;
|