@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
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Session } from './session.js';
|
|
2
|
+
import { Statement } from './statement.js';
|
|
3
|
+
/**
|
|
4
|
+
* A connection to a Turso database.
|
|
5
|
+
*
|
|
6
|
+
* Provides methods for executing SQL statements and managing prepared statements.
|
|
7
|
+
* Uses the SQL over HTTP protocol with streaming cursor support for optimal performance.
|
|
8
|
+
*/
|
|
9
|
+
export class Connection {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.session = new Session(config);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Prepare a SQL statement for execution.
|
|
16
|
+
*
|
|
17
|
+
* Each prepared statement gets its own session to avoid conflicts during concurrent execution.
|
|
18
|
+
*
|
|
19
|
+
* @param sql - The SQL statement to prepare
|
|
20
|
+
* @returns A Statement object that can be executed multiple ways
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const stmt = client.prepare("SELECT * FROM users WHERE id = ?");
|
|
25
|
+
* const user = await stmt.get([123]);
|
|
26
|
+
* const allUsers = await stmt.all();
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
prepare(sql) {
|
|
30
|
+
return new Statement(this.config, sql);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Execute a SQL statement and return all results.
|
|
34
|
+
*
|
|
35
|
+
* @param sql - The SQL statement to execute
|
|
36
|
+
* @param args - Optional array of parameter values
|
|
37
|
+
* @returns Promise resolving to the complete result set
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const result = await client.execute("SELECT * FROM users");
|
|
42
|
+
* console.log(result.rows);
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
async execute(sql, args = []) {
|
|
46
|
+
return this.session.execute(sql, args);
|
|
47
|
+
}
|
|
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
|
+
async batch(statements, mode) {
|
|
65
|
+
return this.session.batch(statements);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create a new connection to a Turso database.
|
|
70
|
+
*
|
|
71
|
+
* @param config - Configuration object with database URL and auth token
|
|
72
|
+
* @returns A new Connection instance
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* import { connect } from "@tursodatabase/serverless";
|
|
77
|
+
*
|
|
78
|
+
* const client = connect({
|
|
79
|
+
* url: process.env.TURSO_DATABASE_URL,
|
|
80
|
+
* authToken: process.env.TURSO_AUTH_TOKEN
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export function connect(config) {
|
|
85
|
+
return new Connection(config);
|
|
86
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
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 ExecuteRequest {
|
|
17
|
+
type: 'execute';
|
|
18
|
+
stmt: {
|
|
19
|
+
sql: string;
|
|
20
|
+
args: Value[];
|
|
21
|
+
named_args: Value[];
|
|
22
|
+
want_rows: boolean;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface BatchStep {
|
|
26
|
+
stmt: {
|
|
27
|
+
sql: string;
|
|
28
|
+
args: Value[];
|
|
29
|
+
want_rows: boolean;
|
|
30
|
+
};
|
|
31
|
+
condition?: {
|
|
32
|
+
type: 'ok';
|
|
33
|
+
step: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface BatchRequest {
|
|
37
|
+
type: 'batch';
|
|
38
|
+
batch: {
|
|
39
|
+
steps: BatchStep[];
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export interface PipelineRequest {
|
|
43
|
+
baton: string | null;
|
|
44
|
+
requests: (ExecuteRequest | BatchRequest)[];
|
|
45
|
+
}
|
|
46
|
+
export interface PipelineResponse {
|
|
47
|
+
baton: string | null;
|
|
48
|
+
base_url: string | null;
|
|
49
|
+
results: Array<{
|
|
50
|
+
type: 'ok' | 'error';
|
|
51
|
+
response?: {
|
|
52
|
+
type: 'execute' | 'batch';
|
|
53
|
+
result: ExecuteResult;
|
|
54
|
+
};
|
|
55
|
+
error?: {
|
|
56
|
+
message: string;
|
|
57
|
+
code: string;
|
|
58
|
+
};
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
export declare function encodeValue(value: any): Value;
|
|
62
|
+
export declare function decodeValue(value: Value): any;
|
|
63
|
+
export interface CursorRequest {
|
|
64
|
+
baton: string | null;
|
|
65
|
+
batch: {
|
|
66
|
+
steps: BatchStep[];
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export interface CursorResponse {
|
|
70
|
+
baton: string | null;
|
|
71
|
+
base_url: string | null;
|
|
72
|
+
}
|
|
73
|
+
export interface CursorEntry {
|
|
74
|
+
type: 'step_begin' | 'step_end' | 'step_error' | 'row' | 'error';
|
|
75
|
+
step?: number;
|
|
76
|
+
cols?: Column[];
|
|
77
|
+
row?: Value[];
|
|
78
|
+
affected_row_count?: number;
|
|
79
|
+
last_insert_rowid?: string;
|
|
80
|
+
error?: {
|
|
81
|
+
message: string;
|
|
82
|
+
code: string;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export declare function executeCursor(url: string, authToken: string, request: CursorRequest): Promise<{
|
|
86
|
+
response: CursorResponse;
|
|
87
|
+
entries: AsyncGenerator<CursorEntry>;
|
|
88
|
+
}>;
|
|
89
|
+
export declare function executePipeline(url: string, authToken: string, request: PipelineRequest): Promise<PipelineResponse>;
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
export function encodeValue(value) {
|
|
2
|
+
if (value === null || value === undefined) {
|
|
3
|
+
return { type: 'null' };
|
|
4
|
+
}
|
|
5
|
+
if (typeof value === 'number') {
|
|
6
|
+
if (Number.isInteger(value)) {
|
|
7
|
+
return { type: 'integer', value: value.toString() };
|
|
8
|
+
}
|
|
9
|
+
return { type: 'float', value };
|
|
10
|
+
}
|
|
11
|
+
if (typeof value === 'string') {
|
|
12
|
+
return { type: 'text', value };
|
|
13
|
+
}
|
|
14
|
+
if (value instanceof ArrayBuffer || value instanceof Uint8Array) {
|
|
15
|
+
const base64 = btoa(String.fromCharCode(...new Uint8Array(value)));
|
|
16
|
+
return { type: 'blob', base64 };
|
|
17
|
+
}
|
|
18
|
+
return { type: 'text', value: String(value) };
|
|
19
|
+
}
|
|
20
|
+
export function decodeValue(value) {
|
|
21
|
+
switch (value.type) {
|
|
22
|
+
case 'null':
|
|
23
|
+
return null;
|
|
24
|
+
case 'integer':
|
|
25
|
+
return parseInt(value.value, 10);
|
|
26
|
+
case 'float':
|
|
27
|
+
return value.value;
|
|
28
|
+
case 'text':
|
|
29
|
+
return value.value;
|
|
30
|
+
case 'blob':
|
|
31
|
+
if (value.base64) {
|
|
32
|
+
const binaryString = atob(value.base64);
|
|
33
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
34
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
35
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
36
|
+
}
|
|
37
|
+
return bytes;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
default:
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export async function executeCursor(url, authToken, request) {
|
|
45
|
+
const response = await fetch(`${url}/v3/cursor`, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: {
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
'Authorization': `Bearer ${authToken}`,
|
|
50
|
+
},
|
|
51
|
+
body: JSON.stringify(request),
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
let errorMessage = `HTTP error! status: ${response.status}`;
|
|
55
|
+
try {
|
|
56
|
+
const errorBody = await response.text();
|
|
57
|
+
const errorData = JSON.parse(errorBody);
|
|
58
|
+
if (errorData.message) {
|
|
59
|
+
errorMessage = errorData.message;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// If we can't parse the error body, use the default HTTP error message
|
|
64
|
+
}
|
|
65
|
+
throw new Error(errorMessage);
|
|
66
|
+
}
|
|
67
|
+
const reader = response.body?.getReader();
|
|
68
|
+
if (!reader) {
|
|
69
|
+
throw new Error('No response body');
|
|
70
|
+
}
|
|
71
|
+
const decoder = new TextDecoder();
|
|
72
|
+
let buffer = '';
|
|
73
|
+
let isFirstLine = true;
|
|
74
|
+
let cursorResponse;
|
|
75
|
+
async function* parseEntries() {
|
|
76
|
+
try {
|
|
77
|
+
while (true) {
|
|
78
|
+
const { done, value } = await reader.read();
|
|
79
|
+
if (done)
|
|
80
|
+
break;
|
|
81
|
+
buffer += decoder.decode(value, { stream: true });
|
|
82
|
+
let newlineIndex;
|
|
83
|
+
while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
|
|
84
|
+
const line = buffer.slice(0, newlineIndex).trim();
|
|
85
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
86
|
+
if (line) {
|
|
87
|
+
if (isFirstLine) {
|
|
88
|
+
cursorResponse = JSON.parse(line);
|
|
89
|
+
isFirstLine = false;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
yield JSON.parse(line);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
reader.releaseLock();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const entries = parseEntries();
|
|
103
|
+
// Get the first entry to parse the cursor response
|
|
104
|
+
const firstEntry = await entries.next();
|
|
105
|
+
if (!firstEntry.done) {
|
|
106
|
+
// Put the first entry back
|
|
107
|
+
const generator = (async function* () {
|
|
108
|
+
yield firstEntry.value;
|
|
109
|
+
yield* entries;
|
|
110
|
+
})();
|
|
111
|
+
return { response: cursorResponse, entries: generator };
|
|
112
|
+
}
|
|
113
|
+
return { response: cursorResponse, entries };
|
|
114
|
+
}
|
|
115
|
+
export async function executePipeline(url, authToken, request) {
|
|
116
|
+
const response = await fetch(`${url}/v3/pipeline`, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: {
|
|
119
|
+
'Content-Type': 'application/json',
|
|
120
|
+
'Authorization': `Bearer ${authToken}`,
|
|
121
|
+
},
|
|
122
|
+
body: JSON.stringify(request),
|
|
123
|
+
});
|
|
124
|
+
if (!response.ok) {
|
|
125
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
126
|
+
}
|
|
127
|
+
return response.json();
|
|
128
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type CursorResponse, type CursorEntry } from './protocol.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration options for a session.
|
|
4
|
+
*/
|
|
5
|
+
export interface SessionConfig {
|
|
6
|
+
/** Database URL */
|
|
7
|
+
url: string;
|
|
8
|
+
/** Authentication token */
|
|
9
|
+
authToken: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A database session that manages the connection state and baton.
|
|
13
|
+
*
|
|
14
|
+
* Each session maintains its own connection state and can execute SQL statements
|
|
15
|
+
* independently without interfering with other sessions.
|
|
16
|
+
*/
|
|
17
|
+
export declare class Session {
|
|
18
|
+
private config;
|
|
19
|
+
private baton;
|
|
20
|
+
private baseUrl;
|
|
21
|
+
constructor(config: SessionConfig);
|
|
22
|
+
/**
|
|
23
|
+
* Execute a SQL statement and return all results.
|
|
24
|
+
*
|
|
25
|
+
* @param sql - The SQL statement to execute
|
|
26
|
+
* @param args - Optional array of parameter values
|
|
27
|
+
* @returns Promise resolving to the complete result set
|
|
28
|
+
*/
|
|
29
|
+
execute(sql: string, args?: any[]): Promise<any>;
|
|
30
|
+
/**
|
|
31
|
+
* Execute a SQL statement and return the raw response and entries.
|
|
32
|
+
*
|
|
33
|
+
* @param sql - The SQL statement to execute
|
|
34
|
+
* @param args - Optional array of parameter values
|
|
35
|
+
* @returns Promise resolving to the raw response and cursor entries
|
|
36
|
+
*/
|
|
37
|
+
executeRaw(sql: string, args?: any[]): Promise<{
|
|
38
|
+
response: CursorResponse;
|
|
39
|
+
entries: AsyncGenerator<CursorEntry>;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Process cursor entries into a structured result.
|
|
43
|
+
*
|
|
44
|
+
* @param entries - Async generator of cursor entries
|
|
45
|
+
* @returns Promise resolving to the processed result
|
|
46
|
+
*/
|
|
47
|
+
processCursorEntries(entries: AsyncGenerator<CursorEntry>): Promise<any>;
|
|
48
|
+
/**
|
|
49
|
+
* Create a row object with both array and named property access.
|
|
50
|
+
*
|
|
51
|
+
* @param values - Array of column values
|
|
52
|
+
* @param columns - Array of column names
|
|
53
|
+
* @returns Row object with dual access patterns
|
|
54
|
+
*/
|
|
55
|
+
createRowObject(values: any[], columns: string[]): any;
|
|
56
|
+
/**
|
|
57
|
+
* Execute multiple SQL statements in a batch.
|
|
58
|
+
*
|
|
59
|
+
* @param statements - Array of SQL statements to execute
|
|
60
|
+
* @returns Promise resolving to batch execution results
|
|
61
|
+
*/
|
|
62
|
+
batch(statements: string[]): Promise<any>;
|
|
63
|
+
}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* A database session that manages the connection state and baton.
|
|
10
|
+
*
|
|
11
|
+
* Each session maintains its own connection state and can execute SQL statements
|
|
12
|
+
* independently without interfering with other sessions.
|
|
13
|
+
*/
|
|
14
|
+
export class Session {
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.baton = null;
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.baseUrl = normalizeUrl(config.url);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Execute a SQL statement and return all results.
|
|
22
|
+
*
|
|
23
|
+
* @param sql - The SQL statement to execute
|
|
24
|
+
* @param args - Optional array of parameter values
|
|
25
|
+
* @returns Promise resolving to the complete result set
|
|
26
|
+
*/
|
|
27
|
+
async execute(sql, args = []) {
|
|
28
|
+
const { response, entries } = await this.executeRaw(sql, args);
|
|
29
|
+
const result = await this.processCursorEntries(entries);
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Execute a SQL statement and return the raw response and entries.
|
|
34
|
+
*
|
|
35
|
+
* @param sql - The SQL statement to execute
|
|
36
|
+
* @param args - Optional array of parameter values
|
|
37
|
+
* @returns Promise resolving to the raw response and cursor entries
|
|
38
|
+
*/
|
|
39
|
+
async executeRaw(sql, args = []) {
|
|
40
|
+
const request = {
|
|
41
|
+
baton: this.baton,
|
|
42
|
+
batch: {
|
|
43
|
+
steps: [{
|
|
44
|
+
stmt: {
|
|
45
|
+
sql,
|
|
46
|
+
args: args.map(encodeValue),
|
|
47
|
+
want_rows: true
|
|
48
|
+
}
|
|
49
|
+
}]
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const { response, entries } = await executeCursor(this.baseUrl, this.config.authToken, request);
|
|
53
|
+
this.baton = response.baton;
|
|
54
|
+
if (response.base_url) {
|
|
55
|
+
this.baseUrl = response.base_url;
|
|
56
|
+
}
|
|
57
|
+
return { response, entries };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Process cursor entries into a structured result.
|
|
61
|
+
*
|
|
62
|
+
* @param entries - Async generator of cursor entries
|
|
63
|
+
* @returns Promise resolving to the processed result
|
|
64
|
+
*/
|
|
65
|
+
async processCursorEntries(entries) {
|
|
66
|
+
let columns = [];
|
|
67
|
+
let columnTypes = [];
|
|
68
|
+
let rows = [];
|
|
69
|
+
let rowsAffected = 0;
|
|
70
|
+
let lastInsertRowid;
|
|
71
|
+
for await (const entry of entries) {
|
|
72
|
+
switch (entry.type) {
|
|
73
|
+
case 'step_begin':
|
|
74
|
+
if (entry.cols) {
|
|
75
|
+
columns = entry.cols.map(col => col.name);
|
|
76
|
+
columnTypes = entry.cols.map(col => col.decltype || '');
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
case 'row':
|
|
80
|
+
if (entry.row) {
|
|
81
|
+
const decodedRow = entry.row.map(decodeValue);
|
|
82
|
+
const rowObject = this.createRowObject(decodedRow, columns);
|
|
83
|
+
rows.push(rowObject);
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
case 'step_end':
|
|
87
|
+
if (entry.affected_row_count !== undefined) {
|
|
88
|
+
rowsAffected = entry.affected_row_count;
|
|
89
|
+
}
|
|
90
|
+
if (entry.last_insert_rowid) {
|
|
91
|
+
lastInsertRowid = parseInt(entry.last_insert_rowid, 10);
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
case 'step_error':
|
|
95
|
+
case 'error':
|
|
96
|
+
throw new Error(entry.error?.message || 'SQL execution failed');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
columns,
|
|
101
|
+
columnTypes,
|
|
102
|
+
rows,
|
|
103
|
+
rowsAffected,
|
|
104
|
+
lastInsertRowid
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Create a row object with both array and named property access.
|
|
109
|
+
*
|
|
110
|
+
* @param values - Array of column values
|
|
111
|
+
* @param columns - Array of column names
|
|
112
|
+
* @returns Row object with dual access patterns
|
|
113
|
+
*/
|
|
114
|
+
createRowObject(values, columns) {
|
|
115
|
+
const row = [...values];
|
|
116
|
+
// Add column name properties to the array as non-enumerable
|
|
117
|
+
// Only add valid identifier names to avoid conflicts
|
|
118
|
+
columns.forEach((column, index) => {
|
|
119
|
+
if (column && isValidIdentifier(column)) {
|
|
120
|
+
Object.defineProperty(row, column, {
|
|
121
|
+
value: values[index],
|
|
122
|
+
enumerable: false,
|
|
123
|
+
writable: false,
|
|
124
|
+
configurable: true
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
return row;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Execute multiple SQL statements in a batch.
|
|
132
|
+
*
|
|
133
|
+
* @param statements - Array of SQL statements to execute
|
|
134
|
+
* @returns Promise resolving to batch execution results
|
|
135
|
+
*/
|
|
136
|
+
async batch(statements) {
|
|
137
|
+
const request = {
|
|
138
|
+
baton: this.baton,
|
|
139
|
+
batch: {
|
|
140
|
+
steps: statements.map(sql => ({
|
|
141
|
+
stmt: {
|
|
142
|
+
sql,
|
|
143
|
+
args: [],
|
|
144
|
+
want_rows: false
|
|
145
|
+
}
|
|
146
|
+
}))
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const { response, entries } = await executeCursor(this.baseUrl, this.config.authToken, request);
|
|
150
|
+
this.baton = response.baton;
|
|
151
|
+
if (response.base_url) {
|
|
152
|
+
this.baseUrl = response.base_url;
|
|
153
|
+
}
|
|
154
|
+
let totalRowsAffected = 0;
|
|
155
|
+
let lastInsertRowid;
|
|
156
|
+
for await (const entry of entries) {
|
|
157
|
+
switch (entry.type) {
|
|
158
|
+
case 'step_end':
|
|
159
|
+
if (entry.affected_row_count !== undefined) {
|
|
160
|
+
totalRowsAffected += entry.affected_row_count;
|
|
161
|
+
}
|
|
162
|
+
if (entry.last_insert_rowid) {
|
|
163
|
+
lastInsertRowid = parseInt(entry.last_insert_rowid, 10);
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
case 'step_error':
|
|
167
|
+
case 'error':
|
|
168
|
+
throw new Error(entry.error?.message || 'Batch execution failed');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
rowsAffected: totalRowsAffected,
|
|
173
|
+
lastInsertRowid
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type SessionConfig } from './session.js';
|
|
2
|
+
/**
|
|
3
|
+
* A prepared SQL statement that can be executed in multiple ways.
|
|
4
|
+
*
|
|
5
|
+
* Each statement has its own session to avoid conflicts during concurrent execution.
|
|
6
|
+
* Provides three execution modes:
|
|
7
|
+
* - `get(args?)`: Returns the first row or null
|
|
8
|
+
* - `all(args?)`: Returns all rows as an array
|
|
9
|
+
* - `iterate(args?)`: Returns an async iterator for streaming results
|
|
10
|
+
*/
|
|
11
|
+
export declare class Statement {
|
|
12
|
+
private session;
|
|
13
|
+
private sql;
|
|
14
|
+
constructor(sessionConfig: SessionConfig, sql: string);
|
|
15
|
+
/**
|
|
16
|
+
* Execute the statement and return the first row.
|
|
17
|
+
*
|
|
18
|
+
* @param args - Optional array of parameter values for the SQL statement
|
|
19
|
+
* @returns Promise resolving to the first row or null if no results
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const stmt = client.prepare("SELECT * FROM users WHERE id = ?");
|
|
24
|
+
* const user = await stmt.get([123]);
|
|
25
|
+
* if (user) {
|
|
26
|
+
* console.log(user.name);
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
get(args?: any[]): Promise<any>;
|
|
31
|
+
/**
|
|
32
|
+
* Execute the statement and return all rows.
|
|
33
|
+
*
|
|
34
|
+
* @param args - Optional array of parameter values for the SQL statement
|
|
35
|
+
* @returns Promise resolving to an array of all result rows
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const stmt = client.prepare("SELECT * FROM users WHERE active = ?");
|
|
40
|
+
* const activeUsers = await stmt.all([true]);
|
|
41
|
+
* console.log(`Found ${activeUsers.length} active users`);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
all(args?: any[]): Promise<any[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Execute the statement and return an async iterator for streaming results.
|
|
47
|
+
*
|
|
48
|
+
* This method provides memory-efficient processing of large result sets
|
|
49
|
+
* by streaming rows one at a time instead of loading everything into memory.
|
|
50
|
+
*
|
|
51
|
+
* @param args - Optional array of parameter values for the SQL statement
|
|
52
|
+
* @returns AsyncGenerator that yields individual rows
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const stmt = client.prepare("SELECT * FROM large_table WHERE category = ?");
|
|
57
|
+
* for await (const row of stmt.iterate(['electronics'])) {
|
|
58
|
+
* // Process each row individually
|
|
59
|
+
* console.log(row.id, row.name);
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
iterate(args?: any[]): AsyncGenerator<any>;
|
|
64
|
+
}
|