@tursodatabase/serverless 1.2.0-pre.2 → 1.2.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/dist/async-lock.d.ts +6 -0
- package/dist/async-lock.js +22 -0
- package/dist/compat/index.d.ts +1 -147
- package/dist/compat/index.js +1 -966
- package/dist/{compat/index.d.cts → compat.d.ts} +11 -13
- package/dist/compat.js +395 -0
- package/dist/connection.d.ts +197 -0
- package/dist/connection.js +312 -0
- package/dist/error.d.ts +19 -0
- package/dist/error.js +24 -0
- package/dist/index.d.ts +5 -593
- package/dist/index.js +6 -1264
- package/dist/protocol.d.ts +120 -0
- package/dist/protocol.js +199 -0
- package/dist/session.d.ts +93 -0
- package/dist/session.js +307 -0
- package/dist/statement.d.ts +161 -0
- package/dist/statement.js +308 -0
- package/package.json +1 -1
- package/dist/compat/index.cjs +0 -969
- package/dist/index.cjs +0 -1272
- package/dist/index.d.cts +0 -593
|
@@ -0,0 +1,120 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type CursorResponse, type CursorEntry, type DescribeResult, type QueryOptions } from './protocol.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration options for a session.
|
|
4
|
+
*/
|
|
5
|
+
export interface SessionConfig {
|
|
6
|
+
/** Database URL */
|
|
7
|
+
url: string;
|
|
8
|
+
/** Authentication token (optional for local development with turso dev) */
|
|
9
|
+
authToken?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Encryption key for the remote database (base64 encoded)
|
|
12
|
+
* to enable access to encrypted Turso Cloud databases.
|
|
13
|
+
*/
|
|
14
|
+
remoteEncryptionKey?: string;
|
|
15
|
+
/** Default maximum query execution time in milliseconds before interruption. */
|
|
16
|
+
defaultQueryTimeout?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A database session that manages the connection state and baton.
|
|
20
|
+
*
|
|
21
|
+
* Each session maintains its own connection state and can execute SQL statements
|
|
22
|
+
* independently without interfering with other sessions.
|
|
23
|
+
*/
|
|
24
|
+
export declare class Session {
|
|
25
|
+
private config;
|
|
26
|
+
private baton;
|
|
27
|
+
private baseUrl;
|
|
28
|
+
constructor(config: SessionConfig);
|
|
29
|
+
private createAbortSignal;
|
|
30
|
+
/**
|
|
31
|
+
* Describe a SQL statement to get its column metadata.
|
|
32
|
+
*
|
|
33
|
+
* @param sql - The SQL statement to describe
|
|
34
|
+
* @returns Promise resolving to the statement description
|
|
35
|
+
*/
|
|
36
|
+
describe(sql: string, queryOptions?: QueryOptions): Promise<DescribeResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Execute a SQL statement and return all results.
|
|
39
|
+
*
|
|
40
|
+
* @param sql - The SQL statement to execute
|
|
41
|
+
* @param args - Optional array of parameter values or object with named parameters
|
|
42
|
+
* @param safeIntegers - Whether to return integers as BigInt
|
|
43
|
+
* @returns Promise resolving to the complete result set
|
|
44
|
+
*/
|
|
45
|
+
execute(sql: string, args?: any[] | Record<string, any>, safeIntegers?: boolean, queryOptions?: QueryOptions): Promise<any>;
|
|
46
|
+
/**
|
|
47
|
+
* Execute a SQL statement and return the raw response and entries.
|
|
48
|
+
*
|
|
49
|
+
* @param sql - The SQL statement to execute
|
|
50
|
+
* @param args - Optional array of parameter values or object with named parameters
|
|
51
|
+
* @returns Promise resolving to the raw response and cursor entries
|
|
52
|
+
*/
|
|
53
|
+
executeRaw(sql: string, args?: any[] | Record<string, any>, queryOptions?: QueryOptions): Promise<{
|
|
54
|
+
response: CursorResponse;
|
|
55
|
+
entries: AsyncGenerator<CursorEntry>;
|
|
56
|
+
}>;
|
|
57
|
+
/**
|
|
58
|
+
* Process cursor entries into a structured result.
|
|
59
|
+
*
|
|
60
|
+
* @param entries - Async generator of cursor entries
|
|
61
|
+
* @returns Promise resolving to the processed result
|
|
62
|
+
*/
|
|
63
|
+
processCursorEntries(entries: AsyncGenerator<CursorEntry>, safeIntegers?: boolean): Promise<any>;
|
|
64
|
+
/**
|
|
65
|
+
* Create a row object with both array and named property access.
|
|
66
|
+
*
|
|
67
|
+
* @param values - Array of column values
|
|
68
|
+
* @param columns - Array of column names
|
|
69
|
+
* @returns Row object with dual access patterns
|
|
70
|
+
*/
|
|
71
|
+
createRowObject(values: any[], columns: string[]): any;
|
|
72
|
+
/**
|
|
73
|
+
* Execute multiple SQL statements in a batch.
|
|
74
|
+
*
|
|
75
|
+
* @param statements - Array of SQL statements to execute
|
|
76
|
+
* @returns Promise resolving to batch execution results
|
|
77
|
+
*/
|
|
78
|
+
batch(statements: string[], queryOptions?: QueryOptions): Promise<any>;
|
|
79
|
+
/**
|
|
80
|
+
* Execute a sequence of SQL statements separated by semicolons.
|
|
81
|
+
*
|
|
82
|
+
* @param sql - SQL string containing multiple statements separated by semicolons
|
|
83
|
+
* @returns Promise resolving when all statements are executed
|
|
84
|
+
*/
|
|
85
|
+
sequence(sql: string, queryOptions?: QueryOptions): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Close the session.
|
|
88
|
+
*
|
|
89
|
+
* This sends a close request to the server to properly clean up the stream
|
|
90
|
+
* before resetting the local state.
|
|
91
|
+
*/
|
|
92
|
+
close(): Promise<void>;
|
|
93
|
+
}
|