@tursodatabase/serverless 1.1.0 → 1.1.2-pre.1
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/dist/compat/index.cjs +885 -0
- package/dist/{compat.d.ts → compat/index.d.cts} +13 -11
- package/dist/compat/index.d.ts +147 -1
- package/dist/compat/index.js +882 -1
- package/dist/index.cjs +1064 -0
- package/dist/index.d.cts +516 -0
- package/dist/index.d.ts +516 -5
- package/dist/index.js +1056 -6
- package/package.json +26 -10
- package/dist/async-lock.d.ts +0 -6
- package/dist/async-lock.js +0 -22
- package/dist/compat.js +0 -395
- package/dist/connection.d.ts +0 -197
- package/dist/connection.js +0 -312
- package/dist/error.d.ts +0 -19
- package/dist/error.js +0 -24
- package/dist/protocol.d.ts +0 -120
- package/dist/protocol.js +0 -199
- package/dist/session.d.ts +0 -93
- package/dist/session.js +0 -307
- package/dist/statement.d.ts +0 -161
- package/dist/statement.js +0 -308
package/dist/connection.js
DELETED
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
import { AsyncLock } from './async-lock.js';
|
|
2
|
-
import { Session } from './session.js';
|
|
3
|
-
import { Statement } from './statement.js';
|
|
4
|
-
/**
|
|
5
|
-
* A connection to a Turso database.
|
|
6
|
-
*
|
|
7
|
-
* Provides methods for executing SQL statements and managing prepared statements.
|
|
8
|
-
* Uses the SQL over HTTP protocol with streaming cursor support.
|
|
9
|
-
*
|
|
10
|
-
* ## Concurrency model
|
|
11
|
-
*
|
|
12
|
-
* A Connection is **single-stream**: it can only run one statement at a time.
|
|
13
|
-
* This is not an implementation quirk — it follows from the SQL over HTTP protocol,
|
|
14
|
-
* where each request carries a baton from the previous response to sequence operations
|
|
15
|
-
* on the server. Concurrent calls on the same connection would race on that baton
|
|
16
|
-
* and corrupt the stream. This is the same model as SQLite itself (one execution
|
|
17
|
-
* at a time per connection).
|
|
18
|
-
*
|
|
19
|
-
* If you call `execute()` while another is in flight, the call automatically
|
|
20
|
-
* waits for the previous one to finish — just like the native
|
|
21
|
-
* `@tursodatabase/database` binding.
|
|
22
|
-
*
|
|
23
|
-
* ## Parallel queries
|
|
24
|
-
*
|
|
25
|
-
* For parallelism, create multiple connections. `connect()` is cheap — it just
|
|
26
|
-
* allocates a config object. No TCP connection is opened until the first `execute()`,
|
|
27
|
-
* and the underlying `fetch()` runtime automatically pools and reuses TCP/TLS
|
|
28
|
-
* connections to the same origin.
|
|
29
|
-
*
|
|
30
|
-
* ```typescript
|
|
31
|
-
* import { connect } from "@tursodatabase/serverless";
|
|
32
|
-
*
|
|
33
|
-
* const config = { url: process.env.TURSO_URL, authToken: process.env.TURSO_TOKEN };
|
|
34
|
-
*
|
|
35
|
-
* // Option 1: one connection per parallel query
|
|
36
|
-
* const [users, orders] = await Promise.all([
|
|
37
|
-
* connect(config).execute("SELECT * FROM users WHERE active = 1"),
|
|
38
|
-
* connect(config).execute("SELECT * FROM orders WHERE status = 'pending'"),
|
|
39
|
-
* ]);
|
|
40
|
-
*
|
|
41
|
-
* // Option 2: reusable pool for repeated parallel work
|
|
42
|
-
* const pool = Array.from({ length: 4 }, () => connect(config));
|
|
43
|
-
* const results = await Promise.all(
|
|
44
|
-
* queries.map((sql, i) => pool[i % pool.length].execute(sql))
|
|
45
|
-
* );
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
|
-
export class Connection {
|
|
49
|
-
constructor(config) {
|
|
50
|
-
this.isOpen = true;
|
|
51
|
-
this.defaultSafeIntegerMode = false;
|
|
52
|
-
this._inTransaction = false;
|
|
53
|
-
this.execLock = new AsyncLock();
|
|
54
|
-
if (!config.url) {
|
|
55
|
-
throw new Error("invalid config: url is required");
|
|
56
|
-
}
|
|
57
|
-
this.config = config;
|
|
58
|
-
this.session = new Session(config);
|
|
59
|
-
// Define inTransaction property
|
|
60
|
-
Object.defineProperty(this, 'inTransaction', {
|
|
61
|
-
get: () => this._inTransaction,
|
|
62
|
-
enumerable: true
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Whether the database is currently in a transaction.
|
|
67
|
-
*/
|
|
68
|
-
get inTransaction() {
|
|
69
|
-
return this._inTransaction;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Prepare a SQL statement for execution.
|
|
73
|
-
*
|
|
74
|
-
* Prepared statements created from a Connection use the same underlying session so transaction boundaries are preserved.
|
|
75
|
-
* This method fetches column metadata using the describe functionality.
|
|
76
|
-
*
|
|
77
|
-
* @param sql - The SQL statement to prepare
|
|
78
|
-
* @returns A Promise that resolves to a Statement object with column metadata
|
|
79
|
-
*
|
|
80
|
-
* @example
|
|
81
|
-
* ```typescript
|
|
82
|
-
* const stmt = await client.prepare("SELECT * FROM users WHERE id = ?");
|
|
83
|
-
* const columns = stmt.columns();
|
|
84
|
-
* const user = await stmt.get([123]);
|
|
85
|
-
* ```
|
|
86
|
-
*/
|
|
87
|
-
async prepare(sql) {
|
|
88
|
-
if (!this.isOpen) {
|
|
89
|
-
throw new TypeError("The database connection is not open");
|
|
90
|
-
}
|
|
91
|
-
// Create a session to get column metadata via describe
|
|
92
|
-
const session = new Session(this.config);
|
|
93
|
-
const description = await session.describe(sql);
|
|
94
|
-
await session.close();
|
|
95
|
-
const stmt = Statement.fromSession(this.session, sql, description.cols, this.execLock);
|
|
96
|
-
if (this.defaultSafeIntegerMode) {
|
|
97
|
-
stmt.safeIntegers(true);
|
|
98
|
-
}
|
|
99
|
-
return stmt;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Execute a SQL statement and return all results.
|
|
103
|
-
*
|
|
104
|
-
* @param sql - The SQL statement to execute
|
|
105
|
-
* @param args - Optional array of parameter values
|
|
106
|
-
* @returns Promise resolving to the complete result set
|
|
107
|
-
*
|
|
108
|
-
* @example
|
|
109
|
-
* ```typescript
|
|
110
|
-
* const result = await client.execute("SELECT * FROM users WHERE id = ?", [123]);
|
|
111
|
-
* console.log(result.rows);
|
|
112
|
-
* ```
|
|
113
|
-
*/
|
|
114
|
-
async execute(sql, args, queryOptions) {
|
|
115
|
-
if (!this.isOpen) {
|
|
116
|
-
throw new TypeError("The database connection is not open");
|
|
117
|
-
}
|
|
118
|
-
await this.execLock.acquire();
|
|
119
|
-
try {
|
|
120
|
-
return await this.session.execute(sql, args || [], this.defaultSafeIntegerMode, queryOptions);
|
|
121
|
-
}
|
|
122
|
-
finally {
|
|
123
|
-
this.execLock.release();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Execute a SQL statement and return all results.
|
|
128
|
-
*
|
|
129
|
-
* @param sql - The SQL statement to execute
|
|
130
|
-
* @returns Promise resolving to the complete result set
|
|
131
|
-
*
|
|
132
|
-
* @example
|
|
133
|
-
* ```typescript
|
|
134
|
-
* const result = await client.exec("SELECT * FROM users");
|
|
135
|
-
* console.log(result.rows);
|
|
136
|
-
* ```
|
|
137
|
-
*/
|
|
138
|
-
async exec(sql, queryOptions) {
|
|
139
|
-
if (!this.isOpen) {
|
|
140
|
-
throw new TypeError("The database connection is not open");
|
|
141
|
-
}
|
|
142
|
-
await this.execLock.acquire();
|
|
143
|
-
try {
|
|
144
|
-
return await this.session.sequence(sql, queryOptions);
|
|
145
|
-
}
|
|
146
|
-
finally {
|
|
147
|
-
this.execLock.release();
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Execute multiple SQL statements in a batch.
|
|
152
|
-
*
|
|
153
|
-
* @param statements - Array of SQL statements to execute
|
|
154
|
-
* @param mode - Optional transaction mode (currently unused)
|
|
155
|
-
* @returns Promise resolving to batch execution results
|
|
156
|
-
*
|
|
157
|
-
* @example
|
|
158
|
-
* ```typescript
|
|
159
|
-
* await client.batch([
|
|
160
|
-
* "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)",
|
|
161
|
-
* "INSERT INTO users (name) VALUES ('Alice')",
|
|
162
|
-
* "INSERT INTO users (name) VALUES ('Bob')"
|
|
163
|
-
* ]);
|
|
164
|
-
* ```
|
|
165
|
-
*/
|
|
166
|
-
async batch(statements, mode, queryOptions) {
|
|
167
|
-
if (!this.isOpen) {
|
|
168
|
-
throw new TypeError("The database connection is not open");
|
|
169
|
-
}
|
|
170
|
-
await this.execLock.acquire();
|
|
171
|
-
try {
|
|
172
|
-
return await this.session.batch(statements, queryOptions);
|
|
173
|
-
}
|
|
174
|
-
finally {
|
|
175
|
-
this.execLock.release();
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Execute a pragma.
|
|
180
|
-
*
|
|
181
|
-
* @param pragma - The pragma to execute
|
|
182
|
-
* @returns Promise resolving to the result of the pragma
|
|
183
|
-
*/
|
|
184
|
-
async pragma(pragma, queryOptions) {
|
|
185
|
-
if (!this.isOpen) {
|
|
186
|
-
throw new TypeError("The database connection is not open");
|
|
187
|
-
}
|
|
188
|
-
await this.execLock.acquire();
|
|
189
|
-
try {
|
|
190
|
-
const sql = `PRAGMA ${pragma}`;
|
|
191
|
-
return await this.session.execute(sql, [], false, queryOptions);
|
|
192
|
-
}
|
|
193
|
-
finally {
|
|
194
|
-
this.execLock.release();
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Sets the default safe integers mode for all statements from this connection.
|
|
199
|
-
*
|
|
200
|
-
* @param toggle - Whether to use safe integers by default.
|
|
201
|
-
*/
|
|
202
|
-
defaultSafeIntegers(toggle) {
|
|
203
|
-
this.defaultSafeIntegerMode = toggle === false ? false : true;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Returns a function that executes the given function in a transaction.
|
|
207
|
-
*
|
|
208
|
-
* @param fn - The function to wrap in a transaction
|
|
209
|
-
* @returns A function that will execute fn within a transaction
|
|
210
|
-
*
|
|
211
|
-
* @example
|
|
212
|
-
* ```typescript
|
|
213
|
-
* const insert = await client.prepare("INSERT INTO users (name) VALUES (?)");
|
|
214
|
-
* const insertMany = client.transaction((users) => {
|
|
215
|
-
* for (const user of users) {
|
|
216
|
-
* insert.run([user]);
|
|
217
|
-
* }
|
|
218
|
-
* });
|
|
219
|
-
*
|
|
220
|
-
* await insertMany(['Alice', 'Bob', 'Charlie']);
|
|
221
|
-
* ```
|
|
222
|
-
*/
|
|
223
|
-
transaction(fn) {
|
|
224
|
-
if (typeof fn !== "function") {
|
|
225
|
-
throw new TypeError("Expected first argument to be a function");
|
|
226
|
-
}
|
|
227
|
-
const db = this;
|
|
228
|
-
const wrapTxn = (mode) => {
|
|
229
|
-
return async (...bindParameters) => {
|
|
230
|
-
await db.exec("BEGIN " + mode);
|
|
231
|
-
db._inTransaction = true;
|
|
232
|
-
try {
|
|
233
|
-
const result = await fn(...bindParameters);
|
|
234
|
-
await db.exec("COMMIT");
|
|
235
|
-
db._inTransaction = false;
|
|
236
|
-
return result;
|
|
237
|
-
}
|
|
238
|
-
catch (err) {
|
|
239
|
-
await db.exec("ROLLBACK");
|
|
240
|
-
db._inTransaction = false;
|
|
241
|
-
throw err;
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
};
|
|
245
|
-
const properties = {
|
|
246
|
-
default: { value: wrapTxn("") },
|
|
247
|
-
deferred: { value: wrapTxn("DEFERRED") },
|
|
248
|
-
immediate: { value: wrapTxn("IMMEDIATE") },
|
|
249
|
-
exclusive: { value: wrapTxn("EXCLUSIVE") },
|
|
250
|
-
database: { value: this, enumerable: true },
|
|
251
|
-
};
|
|
252
|
-
Object.defineProperties(properties.default.value, properties);
|
|
253
|
-
Object.defineProperties(properties.deferred.value, properties);
|
|
254
|
-
Object.defineProperties(properties.immediate.value, properties);
|
|
255
|
-
Object.defineProperties(properties.exclusive.value, properties);
|
|
256
|
-
return properties.default.value;
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Close the connection.
|
|
260
|
-
*
|
|
261
|
-
* This sends a close request to the server to properly clean up the stream.
|
|
262
|
-
*/
|
|
263
|
-
async close() {
|
|
264
|
-
this.isOpen = false;
|
|
265
|
-
await this.session.close();
|
|
266
|
-
}
|
|
267
|
-
async reconnect() {
|
|
268
|
-
try {
|
|
269
|
-
if (this.isOpen) {
|
|
270
|
-
await this.close();
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
finally {
|
|
274
|
-
this.session = new Session(this.config);
|
|
275
|
-
this.isOpen = true;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Create a new connection to a Turso database.
|
|
281
|
-
*
|
|
282
|
-
* This is a lightweight operation — it only allocates a config object. No network
|
|
283
|
-
* I/O happens until the first query. The underlying `fetch()` implementation
|
|
284
|
-
* automatically pools TCP/TLS connections to the same origin, so creating many
|
|
285
|
-
* connections is cheap.
|
|
286
|
-
*
|
|
287
|
-
* Each connection is single-stream: concurrent calls on the same connection are
|
|
288
|
-
* automatically serialized. For true parallelism, create multiple connections:
|
|
289
|
-
*
|
|
290
|
-
* ```typescript
|
|
291
|
-
* import { connect } from "@tursodatabase/serverless";
|
|
292
|
-
*
|
|
293
|
-
* const config = { url: process.env.TURSO_URL, authToken: process.env.TURSO_TOKEN };
|
|
294
|
-
*
|
|
295
|
-
* // Sequential (single connection is fine)
|
|
296
|
-
* const conn = connect(config);
|
|
297
|
-
* const a = await conn.execute("SELECT 1");
|
|
298
|
-
* const b = await conn.execute("SELECT 2");
|
|
299
|
-
*
|
|
300
|
-
* // Parallel (use separate connections)
|
|
301
|
-
* const [x, y] = await Promise.all([
|
|
302
|
-
* connect(config).execute("SELECT 1"),
|
|
303
|
-
* connect(config).execute("SELECT 2"),
|
|
304
|
-
* ]);
|
|
305
|
-
* ```
|
|
306
|
-
*
|
|
307
|
-
* @param config - Configuration object with database URL and auth token
|
|
308
|
-
* @returns A new Connection instance
|
|
309
|
-
*/
|
|
310
|
-
export function connect(config) {
|
|
311
|
-
return new Connection(config);
|
|
312
|
-
}
|
package/dist/error.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export declare class DatabaseError extends Error {
|
|
2
|
-
/** Machine-readable error code (e.g., "SQLITE_CONSTRAINT") */
|
|
3
|
-
code?: string;
|
|
4
|
-
/** Raw numeric error code */
|
|
5
|
-
rawCode?: number;
|
|
6
|
-
/** Original error that caused this error */
|
|
7
|
-
cause?: Error;
|
|
8
|
-
constructor(message: string, code?: string, rawCode?: number, cause?: Error);
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Error thrown when a query exceeds the configured timeout.
|
|
12
|
-
*
|
|
13
|
-
* This is a subclass of `DatabaseError` with `code` set to `"TIMEOUT"`.
|
|
14
|
-
* Catch this type to distinguish timeouts from other database errors
|
|
15
|
-
* and decide whether to retry or fail gracefully.
|
|
16
|
-
*/
|
|
17
|
-
export declare class TimeoutError extends DatabaseError {
|
|
18
|
-
constructor(message?: string, cause?: Error);
|
|
19
|
-
}
|
package/dist/error.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export class DatabaseError extends Error {
|
|
2
|
-
constructor(message, code, rawCode, cause) {
|
|
3
|
-
super(message);
|
|
4
|
-
this.name = 'DatabaseError';
|
|
5
|
-
this.code = code;
|
|
6
|
-
this.rawCode = rawCode;
|
|
7
|
-
this.cause = cause;
|
|
8
|
-
Object.setPrototypeOf(this, DatabaseError.prototype);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Error thrown when a query exceeds the configured timeout.
|
|
13
|
-
*
|
|
14
|
-
* This is a subclass of `DatabaseError` with `code` set to `"TIMEOUT"`.
|
|
15
|
-
* Catch this type to distinguish timeouts from other database errors
|
|
16
|
-
* and decide whether to retry or fail gracefully.
|
|
17
|
-
*/
|
|
18
|
-
export class TimeoutError extends DatabaseError {
|
|
19
|
-
constructor(message = 'Query timed out', cause) {
|
|
20
|
-
super(message, 'TIMEOUT', undefined, cause);
|
|
21
|
-
this.name = 'TimeoutError';
|
|
22
|
-
Object.setPrototypeOf(this, TimeoutError.prototype);
|
|
23
|
-
}
|
|
24
|
-
}
|
package/dist/protocol.d.ts
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
export interface Value {
|
|
2
|
-
type: 'null' | 'integer' | 'float' | 'text' | 'blob';
|
|
3
|
-
value?: string | number;
|
|
4
|
-
base64?: string;
|
|
5
|
-
}
|
|
6
|
-
export interface Column {
|
|
7
|
-
name: string;
|
|
8
|
-
decltype: string;
|
|
9
|
-
}
|
|
10
|
-
export interface ExecuteResult {
|
|
11
|
-
cols: Column[];
|
|
12
|
-
rows: Value[][];
|
|
13
|
-
affected_row_count: number;
|
|
14
|
-
last_insert_rowid?: string;
|
|
15
|
-
}
|
|
16
|
-
export interface NamedArg {
|
|
17
|
-
name: string;
|
|
18
|
-
value: Value;
|
|
19
|
-
}
|
|
20
|
-
export interface ExecuteRequest {
|
|
21
|
-
type: 'execute';
|
|
22
|
-
stmt: {
|
|
23
|
-
sql: string;
|
|
24
|
-
args: Value[];
|
|
25
|
-
named_args: NamedArg[];
|
|
26
|
-
want_rows: boolean;
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
export interface BatchStep {
|
|
30
|
-
stmt: {
|
|
31
|
-
sql: string;
|
|
32
|
-
args: Value[];
|
|
33
|
-
named_args?: NamedArg[];
|
|
34
|
-
want_rows: boolean;
|
|
35
|
-
};
|
|
36
|
-
condition?: {
|
|
37
|
-
type: 'ok';
|
|
38
|
-
step: number;
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
export interface BatchRequest {
|
|
42
|
-
type: 'batch';
|
|
43
|
-
batch: {
|
|
44
|
-
steps: BatchStep[];
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
export interface SequenceRequest {
|
|
48
|
-
type: 'sequence';
|
|
49
|
-
sql: string;
|
|
50
|
-
}
|
|
51
|
-
export interface CloseRequest {
|
|
52
|
-
type: 'close';
|
|
53
|
-
}
|
|
54
|
-
export interface DescribeRequest {
|
|
55
|
-
type: 'describe';
|
|
56
|
-
sql: string;
|
|
57
|
-
}
|
|
58
|
-
export interface DescribeResult {
|
|
59
|
-
params: Array<{
|
|
60
|
-
name?: string;
|
|
61
|
-
}>;
|
|
62
|
-
cols: Column[];
|
|
63
|
-
is_explain: boolean;
|
|
64
|
-
is_readonly: boolean;
|
|
65
|
-
}
|
|
66
|
-
export interface PipelineRequest {
|
|
67
|
-
baton: string | null;
|
|
68
|
-
requests: (ExecuteRequest | BatchRequest | SequenceRequest | CloseRequest | DescribeRequest)[];
|
|
69
|
-
}
|
|
70
|
-
export interface PipelineResponse {
|
|
71
|
-
baton: string | null;
|
|
72
|
-
base_url: string | null;
|
|
73
|
-
results: Array<{
|
|
74
|
-
type: 'ok' | 'error';
|
|
75
|
-
response?: {
|
|
76
|
-
type: 'execute' | 'batch' | 'sequence' | 'close' | 'describe';
|
|
77
|
-
result?: ExecuteResult | DescribeResult;
|
|
78
|
-
};
|
|
79
|
-
error?: {
|
|
80
|
-
message: string;
|
|
81
|
-
code: string;
|
|
82
|
-
};
|
|
83
|
-
}>;
|
|
84
|
-
}
|
|
85
|
-
export declare function encodeValue(value: any): Value;
|
|
86
|
-
export declare function decodeValue(value: Value, safeIntegers?: boolean): any;
|
|
87
|
-
export interface CursorRequest {
|
|
88
|
-
baton: string | null;
|
|
89
|
-
batch: {
|
|
90
|
-
steps: BatchStep[];
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
export interface CursorResponse {
|
|
94
|
-
baton: string | null;
|
|
95
|
-
base_url: string | null;
|
|
96
|
-
}
|
|
97
|
-
export interface CursorEntry {
|
|
98
|
-
type: 'step_begin' | 'step_end' | 'step_error' | 'row' | 'error';
|
|
99
|
-
step?: number;
|
|
100
|
-
cols?: Column[];
|
|
101
|
-
row?: Value[];
|
|
102
|
-
affected_row_count?: number;
|
|
103
|
-
last_insert_rowid?: string;
|
|
104
|
-
error?: {
|
|
105
|
-
message: string;
|
|
106
|
-
code: string;
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
/** HTTP header key for the encryption key */
|
|
110
|
-
export declare const ENCRYPTION_KEY_HEADER = "x-turso-encryption-key";
|
|
111
|
-
/** Per-query timeout options. Overrides defaultQueryTimeout for this call. */
|
|
112
|
-
export interface QueryOptions {
|
|
113
|
-
/** Per-query timeout in milliseconds. Overrides defaultQueryTimeout for this call. */
|
|
114
|
-
queryTimeout?: number;
|
|
115
|
-
}
|
|
116
|
-
export declare function executeCursor(url: string, authToken: string | undefined, request: CursorRequest, remoteEncryptionKey?: string, signal?: AbortSignal): Promise<{
|
|
117
|
-
response: CursorResponse;
|
|
118
|
-
entries: AsyncGenerator<CursorEntry>;
|
|
119
|
-
}>;
|
|
120
|
-
export declare function executePipeline(url: string, authToken: string | undefined, request: PipelineRequest, remoteEncryptionKey?: string, signal?: AbortSignal): Promise<PipelineResponse>;
|
package/dist/protocol.js
DELETED
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { DatabaseError, TimeoutError } from './error.js';
|
|
2
|
-
export function encodeValue(value) {
|
|
3
|
-
if (value === null || value === undefined) {
|
|
4
|
-
return { type: 'null' };
|
|
5
|
-
}
|
|
6
|
-
if (typeof value === 'number') {
|
|
7
|
-
if (!Number.isFinite(value)) {
|
|
8
|
-
throw new Error("Only finite numbers (not Infinity or NaN) can be passed as arguments");
|
|
9
|
-
}
|
|
10
|
-
return { type: 'float', value };
|
|
11
|
-
}
|
|
12
|
-
if (typeof value === 'bigint') {
|
|
13
|
-
return { type: 'integer', value: value.toString() };
|
|
14
|
-
}
|
|
15
|
-
if (typeof value === 'boolean') {
|
|
16
|
-
return { type: 'integer', value: value ? '1' : '0' };
|
|
17
|
-
}
|
|
18
|
-
if (typeof value === 'string') {
|
|
19
|
-
return { type: 'text', value };
|
|
20
|
-
}
|
|
21
|
-
if (value instanceof ArrayBuffer || value instanceof Uint8Array) {
|
|
22
|
-
const base64 = btoa(String.fromCharCode(...new Uint8Array(value)));
|
|
23
|
-
return { type: 'blob', base64 };
|
|
24
|
-
}
|
|
25
|
-
return { type: 'text', value: String(value) };
|
|
26
|
-
}
|
|
27
|
-
export function decodeValue(value, safeIntegers = false) {
|
|
28
|
-
switch (value.type) {
|
|
29
|
-
case 'null':
|
|
30
|
-
return null;
|
|
31
|
-
case 'integer':
|
|
32
|
-
if (safeIntegers) {
|
|
33
|
-
return BigInt(value.value);
|
|
34
|
-
}
|
|
35
|
-
return parseInt(value.value, 10);
|
|
36
|
-
case 'float':
|
|
37
|
-
return value.value;
|
|
38
|
-
case 'text':
|
|
39
|
-
return value.value;
|
|
40
|
-
case 'blob':
|
|
41
|
-
if (value.base64) {
|
|
42
|
-
const binaryString = atob(value.base64);
|
|
43
|
-
const bytes = new Uint8Array(binaryString.length);
|
|
44
|
-
for (let i = 0; i < binaryString.length; i++) {
|
|
45
|
-
bytes[i] = binaryString.charCodeAt(i);
|
|
46
|
-
}
|
|
47
|
-
return Buffer.from(bytes);
|
|
48
|
-
}
|
|
49
|
-
return null;
|
|
50
|
-
default:
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/** HTTP header key for the encryption key */
|
|
55
|
-
export const ENCRYPTION_KEY_HEADER = 'x-turso-encryption-key';
|
|
56
|
-
function wrapAbortError(error) {
|
|
57
|
-
if (error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError')) {
|
|
58
|
-
throw new TimeoutError('Query timed out');
|
|
59
|
-
}
|
|
60
|
-
throw error;
|
|
61
|
-
}
|
|
62
|
-
export async function executeCursor(url, authToken, request, remoteEncryptionKey, signal) {
|
|
63
|
-
const headers = {
|
|
64
|
-
'Content-Type': 'application/json',
|
|
65
|
-
};
|
|
66
|
-
if (authToken) {
|
|
67
|
-
headers['Authorization'] = `Bearer ${authToken}`;
|
|
68
|
-
}
|
|
69
|
-
if (remoteEncryptionKey) {
|
|
70
|
-
headers[ENCRYPTION_KEY_HEADER] = remoteEncryptionKey;
|
|
71
|
-
}
|
|
72
|
-
let response;
|
|
73
|
-
try {
|
|
74
|
-
response = await fetch(`${url}/v3/cursor`, {
|
|
75
|
-
method: 'POST',
|
|
76
|
-
headers,
|
|
77
|
-
body: JSON.stringify(request),
|
|
78
|
-
signal,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
catch (error) {
|
|
82
|
-
wrapAbortError(error);
|
|
83
|
-
}
|
|
84
|
-
if (!response.ok) {
|
|
85
|
-
let errorMessage = `HTTP error! status: ${response.status}`;
|
|
86
|
-
try {
|
|
87
|
-
const errorBody = await response.text();
|
|
88
|
-
const errorData = JSON.parse(errorBody);
|
|
89
|
-
if (errorData.message) {
|
|
90
|
-
errorMessage = errorData.message;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
// If we can't parse the error body, use the default HTTP error message
|
|
95
|
-
}
|
|
96
|
-
throw new DatabaseError(errorMessage);
|
|
97
|
-
}
|
|
98
|
-
const reader = response.body?.getReader();
|
|
99
|
-
if (!reader) {
|
|
100
|
-
throw new DatabaseError('No response body');
|
|
101
|
-
}
|
|
102
|
-
const decoder = new TextDecoder();
|
|
103
|
-
let buffer = '';
|
|
104
|
-
let cursorResponse;
|
|
105
|
-
// First, read until we get the cursor response (first line)
|
|
106
|
-
try {
|
|
107
|
-
while (!cursorResponse) {
|
|
108
|
-
const { done, value } = await reader.read();
|
|
109
|
-
if (done)
|
|
110
|
-
break;
|
|
111
|
-
buffer += decoder.decode(value, { stream: true });
|
|
112
|
-
const newlineIndex = buffer.indexOf('\n');
|
|
113
|
-
if (newlineIndex !== -1) {
|
|
114
|
-
const line = buffer.slice(0, newlineIndex).trim();
|
|
115
|
-
buffer = buffer.slice(newlineIndex + 1);
|
|
116
|
-
if (line) {
|
|
117
|
-
cursorResponse = JSON.parse(line);
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
reader.releaseLock();
|
|
125
|
-
wrapAbortError(error);
|
|
126
|
-
}
|
|
127
|
-
if (!cursorResponse) {
|
|
128
|
-
reader.releaseLock();
|
|
129
|
-
throw new DatabaseError('No cursor response received');
|
|
130
|
-
}
|
|
131
|
-
async function* parseEntries() {
|
|
132
|
-
try {
|
|
133
|
-
// Process any remaining data in the buffer
|
|
134
|
-
let newlineIndex;
|
|
135
|
-
while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
|
|
136
|
-
const line = buffer.slice(0, newlineIndex).trim();
|
|
137
|
-
buffer = buffer.slice(newlineIndex + 1);
|
|
138
|
-
if (line) {
|
|
139
|
-
yield JSON.parse(line);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
// Continue reading from the stream
|
|
143
|
-
while (true) {
|
|
144
|
-
let readResult;
|
|
145
|
-
try {
|
|
146
|
-
readResult = await reader.read();
|
|
147
|
-
}
|
|
148
|
-
catch (error) {
|
|
149
|
-
wrapAbortError(error);
|
|
150
|
-
}
|
|
151
|
-
if (readResult.done)
|
|
152
|
-
break;
|
|
153
|
-
buffer += decoder.decode(readResult.value, { stream: true });
|
|
154
|
-
while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
|
|
155
|
-
const line = buffer.slice(0, newlineIndex).trim();
|
|
156
|
-
buffer = buffer.slice(newlineIndex + 1);
|
|
157
|
-
if (line) {
|
|
158
|
-
yield JSON.parse(line);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
// Process any remaining data in the buffer
|
|
163
|
-
if (buffer.trim()) {
|
|
164
|
-
yield JSON.parse(buffer.trim());
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
finally {
|
|
168
|
-
reader.releaseLock();
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return { response: cursorResponse, entries: parseEntries() };
|
|
172
|
-
}
|
|
173
|
-
export async function executePipeline(url, authToken, request, remoteEncryptionKey, signal) {
|
|
174
|
-
const headers = {
|
|
175
|
-
'Content-Type': 'application/json',
|
|
176
|
-
};
|
|
177
|
-
if (authToken) {
|
|
178
|
-
headers['Authorization'] = `Bearer ${authToken}`;
|
|
179
|
-
}
|
|
180
|
-
if (remoteEncryptionKey) {
|
|
181
|
-
headers[ENCRYPTION_KEY_HEADER] = remoteEncryptionKey;
|
|
182
|
-
}
|
|
183
|
-
let response;
|
|
184
|
-
try {
|
|
185
|
-
response = await fetch(`${url}/v3/pipeline`, {
|
|
186
|
-
method: 'POST',
|
|
187
|
-
headers,
|
|
188
|
-
body: JSON.stringify(request),
|
|
189
|
-
signal,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
catch (error) {
|
|
193
|
-
wrapAbortError(error);
|
|
194
|
-
}
|
|
195
|
-
if (!response.ok) {
|
|
196
|
-
throw new DatabaseError(`HTTP error! status: ${response.status}`);
|
|
197
|
-
}
|
|
198
|
-
return response.json();
|
|
199
|
-
}
|