@tursodatabase/serverless 0.1.0 → 0.1.2

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/statement.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { decodeValue } from './protocol.js';
2
2
  import { Session } from './session.js';
3
+ import { DatabaseError } from './error.js';
3
4
  /**
4
5
  * A prepared SQL statement that can be executed in multiple ways.
5
6
  *
@@ -11,14 +12,50 @@ import { Session } from './session.js';
11
12
  */
12
13
  export class Statement {
13
14
  constructor(sessionConfig, sql) {
15
+ this.presentationMode = 'expanded';
14
16
  this.session = new Session(sessionConfig);
15
17
  this.sql = sql;
16
18
  }
19
+ /**
20
+ * Enable raw mode to return arrays instead of objects.
21
+ *
22
+ * @param raw Enable or disable raw mode. If you don't pass the parameter, raw mode is enabled.
23
+ * @returns This statement instance for chaining
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const stmt = client.prepare("SELECT * FROM users WHERE id = ?");
28
+ * const row = await stmt.raw().get([1]);
29
+ * console.log(row); // [1, "Alice", "alice@example.org"]
30
+ * ```
31
+ */
32
+ raw(raw) {
33
+ this.presentationMode = raw === false ? 'expanded' : 'raw';
34
+ return this;
35
+ }
36
+ /**
37
+ * Executes the prepared statement.
38
+ *
39
+ * @param args - Optional array of parameter values or object with named parameters
40
+ * @returns Promise resolving to the result of the statement
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const stmt = client.prepare("INSERT INTO users (name, email) VALUES (?, ?)");
45
+ * const result = await stmt.run(['John Doe', 'john.doe@example.com']);
46
+ * console.log(`Inserted user with ID ${result.lastInsertRowid}`);
47
+ * ```
48
+ */
49
+ async run(args) {
50
+ const normalizedArgs = this.normalizeArgs(args);
51
+ const result = await this.session.execute(this.sql, normalizedArgs);
52
+ return { changes: result.rowsAffected, lastInsertRowid: result.lastInsertRowid };
53
+ }
17
54
  /**
18
55
  * Execute the statement and return the first row.
19
56
  *
20
- * @param args - Optional array of parameter values for the SQL statement
21
- * @returns Promise resolving to the first row or null if no results
57
+ * @param args - Optional array of parameter values or object with named parameters
58
+ * @returns Promise resolving to the first row or undefined if no results
22
59
  *
23
60
  * @example
24
61
  * ```typescript
@@ -29,14 +66,24 @@ export class Statement {
29
66
  * }
30
67
  * ```
31
68
  */
32
- async get(args = []) {
33
- const result = await this.session.execute(this.sql, args);
34
- return result.rows[0] || null;
69
+ async get(args) {
70
+ const normalizedArgs = this.normalizeArgs(args);
71
+ const result = await this.session.execute(this.sql, normalizedArgs);
72
+ const row = result.rows[0];
73
+ if (!row) {
74
+ return undefined;
75
+ }
76
+ if (this.presentationMode === 'raw') {
77
+ // In raw mode, return the row as a plain array (it already is one)
78
+ // The row object is already an array with column properties added
79
+ return [...row];
80
+ }
81
+ return row;
35
82
  }
36
83
  /**
37
84
  * Execute the statement and return all rows.
38
85
  *
39
- * @param args - Optional array of parameter values for the SQL statement
86
+ * @param args - Optional array of parameter values or object with named parameters
40
87
  * @returns Promise resolving to an array of all result rows
41
88
  *
42
89
  * @example
@@ -46,8 +93,14 @@ export class Statement {
46
93
  * console.log(`Found ${activeUsers.length} active users`);
47
94
  * ```
48
95
  */
49
- async all(args = []) {
50
- const result = await this.session.execute(this.sql, args);
96
+ async all(args) {
97
+ const normalizedArgs = this.normalizeArgs(args);
98
+ const result = await this.session.execute(this.sql, normalizedArgs);
99
+ if (this.presentationMode === 'raw') {
100
+ // In raw mode, return arrays of values
101
+ // Each row is already an array with column properties added
102
+ return result.rows.map((row) => [...row]);
103
+ }
51
104
  return result.rows;
52
105
  }
53
106
  /**
@@ -56,7 +109,7 @@ export class Statement {
56
109
  * This method provides memory-efficient processing of large result sets
57
110
  * by streaming rows one at a time instead of loading everything into memory.
58
111
  *
59
- * @param args - Optional array of parameter values for the SQL statement
112
+ * @param args - Optional array of parameter values or object with named parameters
60
113
  * @returns AsyncGenerator that yields individual rows
61
114
  *
62
115
  * @example
@@ -68,8 +121,9 @@ export class Statement {
68
121
  * }
69
122
  * ```
70
123
  */
71
- async *iterate(args = []) {
72
- const { response, entries } = await this.session.executeRaw(this.sql, args);
124
+ async *iterate(args) {
125
+ const normalizedArgs = this.normalizeArgs(args);
126
+ const { response, entries } = await this.session.executeRaw(this.sql, normalizedArgs);
73
127
  let columns = [];
74
128
  for await (const entry of entries) {
75
129
  switch (entry.type) {
@@ -81,14 +135,40 @@ export class Statement {
81
135
  case 'row':
82
136
  if (entry.row) {
83
137
  const decodedRow = entry.row.map(decodeValue);
84
- const rowObject = this.session.createRowObject(decodedRow, columns);
85
- yield rowObject;
138
+ if (this.presentationMode === 'raw') {
139
+ // In raw mode, yield arrays of values
140
+ yield decodedRow;
141
+ }
142
+ else {
143
+ const rowObject = this.session.createRowObject(decodedRow, columns);
144
+ yield rowObject;
145
+ }
86
146
  }
87
147
  break;
88
148
  case 'step_error':
89
149
  case 'error':
90
- throw new Error(entry.error?.message || 'SQL execution failed');
150
+ throw new DatabaseError(entry.error?.message || 'SQL execution failed');
91
151
  }
92
152
  }
93
153
  }
154
+ /**
155
+ * Normalize arguments to handle both single values and arrays.
156
+ * Matches the behavior of the native bindings.
157
+ */
158
+ normalizeArgs(args) {
159
+ // No arguments provided
160
+ if (args === undefined) {
161
+ return [];
162
+ }
163
+ // If it's an array, return as-is
164
+ if (Array.isArray(args)) {
165
+ return args;
166
+ }
167
+ // Check if it's a plain object (for named parameters)
168
+ if (args !== null && typeof args === 'object' && args.constructor === Object) {
169
+ return args;
170
+ }
171
+ // Single value - wrap in array
172
+ return [args];
173
+ }
94
174
  }
@@ -0,0 +1,131 @@
1
+ import { type SessionConfig } from "./session.js";
2
+ /**
3
+ * Transaction mode for controlling transaction behavior.
4
+ */
5
+ export type TransactionMode = "write" | "read" | "deferred";
6
+ /**
7
+ * Transactions use a dedicated session to maintain state across multiple operations.
8
+ * All operations within a transaction are executed atomically - they either all
9
+ * succeed or all fail.
10
+ */
11
+ export declare class Transaction {
12
+ private session;
13
+ private _closed;
14
+ private _committed;
15
+ private _rolledBack;
16
+ private constructor();
17
+ /**
18
+ * Create a new transaction instance.
19
+ *
20
+ * @param sessionConfig - Session configuration
21
+ * @param mode - Transaction mode
22
+ * @returns Promise resolving to a new Transaction instance
23
+ */
24
+ static create(sessionConfig: SessionConfig, mode?: TransactionMode): Promise<Transaction>;
25
+ private initializeTransaction;
26
+ /**
27
+ * Execute a SQL statement within the transaction.
28
+ *
29
+ * @param sql - The SQL statement to execute
30
+ * @param args - Optional array of parameter values
31
+ * @returns Promise resolving to the complete result set
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const tx = await client.transaction();
36
+ * const result = await tx.execute("INSERT INTO users (name) VALUES (?)", ["Alice"]);
37
+ * await tx.commit();
38
+ * ```
39
+ */
40
+ execute(sql: string, args?: any[]): Promise<any>;
41
+ /**
42
+ * Execute multiple SQL statements as a batch within the transaction.
43
+ *
44
+ * @param statements - Array of SQL statements to execute
45
+ * @returns Promise resolving to batch execution results
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const tx = await client.transaction();
50
+ * await tx.batch([
51
+ * "INSERT INTO users (name) VALUES ('Alice')",
52
+ * "INSERT INTO users (name) VALUES ('Bob')"
53
+ * ]);
54
+ * await tx.commit();
55
+ * ```
56
+ */
57
+ batch(statements: string[]): Promise<any>;
58
+ /**
59
+ * Execute a SQL statement and return the raw response and entries.
60
+ *
61
+ * @param sql - The SQL statement to execute
62
+ * @param args - Optional array of parameter values
63
+ * @returns Promise resolving to the raw response and cursor entries
64
+ */
65
+ executeRaw(sql: string, args?: any[]): Promise<{
66
+ response: any;
67
+ entries: AsyncGenerator<any>;
68
+ }>;
69
+ /**
70
+ * Commit the transaction, making all changes permanent.
71
+ *
72
+ * @returns Promise that resolves when the transaction is committed
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const tx = await client.transaction();
77
+ * await tx.execute("INSERT INTO users (name) VALUES (?)", ["Alice"]);
78
+ * await tx.commit(); // Changes are now permanent
79
+ * ```
80
+ */
81
+ commit(): Promise<void>;
82
+ /**
83
+ * Rollback the transaction, undoing all changes.
84
+ *
85
+ * @returns Promise that resolves when the transaction is rolled back
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * const tx = await client.transaction();
90
+ * try {
91
+ * await tx.execute("INSERT INTO users (name) VALUES (?)", ["Alice"]);
92
+ * await tx.execute("INSERT INTO invalid_table VALUES (1)"); // This will fail
93
+ * await tx.commit();
94
+ * } catch (error) {
95
+ * await tx.rollback(); // Undo the first INSERT
96
+ * }
97
+ * ```
98
+ */
99
+ rollback(): Promise<void>;
100
+ /**
101
+ * Close the transaction without committing or rolling back.
102
+ *
103
+ * This will automatically rollback any uncommitted changes.
104
+ * It's safe to call this method multiple times.
105
+ */
106
+ close(): void;
107
+ /**
108
+ * Check if the transaction is closed.
109
+ *
110
+ * @returns True if the transaction has been committed, rolled back, or closed
111
+ */
112
+ get closed(): boolean;
113
+ /**
114
+ * Check if the transaction has been committed.
115
+ *
116
+ * @returns True if the transaction has been successfully committed
117
+ */
118
+ get committed(): boolean;
119
+ /**
120
+ * Check if the transaction has been rolled back.
121
+ *
122
+ * @returns True if the transaction has been rolled back
123
+ */
124
+ get rolledBack(): boolean;
125
+ /**
126
+ * Check transaction state and throw if it's not valid for operations.
127
+ *
128
+ * @throws Error if the transaction is closed
129
+ */
130
+ private checkState;
131
+ }
@@ -0,0 +1,207 @@
1
+ import { Session } from "./session.js";
2
+ import { DatabaseError } from "./error.js";
3
+ /**
4
+ * Transactions use a dedicated session to maintain state across multiple operations.
5
+ * All operations within a transaction are executed atomically - they either all
6
+ * succeed or all fail.
7
+ */
8
+ export class Transaction {
9
+ constructor(sessionConfig, mode = "deferred") {
10
+ this._closed = false;
11
+ this._committed = false;
12
+ this._rolledBack = false;
13
+ this.session = new Session(sessionConfig);
14
+ }
15
+ /**
16
+ * Create a new transaction instance.
17
+ *
18
+ * @param sessionConfig - Session configuration
19
+ * @param mode - Transaction mode
20
+ * @returns Promise resolving to a new Transaction instance
21
+ */
22
+ static async create(sessionConfig, mode = "deferred") {
23
+ const transaction = new Transaction(sessionConfig, mode);
24
+ await transaction.initializeTransaction(mode);
25
+ return transaction;
26
+ }
27
+ async initializeTransaction(mode) {
28
+ let beginStatement;
29
+ switch (mode) {
30
+ case "write":
31
+ beginStatement = "BEGIN IMMEDIATE";
32
+ break;
33
+ case "read":
34
+ beginStatement = "BEGIN";
35
+ break;
36
+ case "deferred":
37
+ default:
38
+ beginStatement = "BEGIN DEFERRED";
39
+ break;
40
+ }
41
+ await this.session.execute(beginStatement);
42
+ }
43
+ /**
44
+ * Execute a SQL statement within the transaction.
45
+ *
46
+ * @param sql - The SQL statement to execute
47
+ * @param args - Optional array of parameter values
48
+ * @returns Promise resolving to the complete result set
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const tx = await client.transaction();
53
+ * const result = await tx.execute("INSERT INTO users (name) VALUES (?)", ["Alice"]);
54
+ * await tx.commit();
55
+ * ```
56
+ */
57
+ async execute(sql, args = []) {
58
+ this.checkState();
59
+ return this.session.execute(sql, args);
60
+ }
61
+ /**
62
+ * Execute multiple SQL statements as a batch within the transaction.
63
+ *
64
+ * @param statements - Array of SQL statements to execute
65
+ * @returns Promise resolving to batch execution results
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const tx = await client.transaction();
70
+ * await tx.batch([
71
+ * "INSERT INTO users (name) VALUES ('Alice')",
72
+ * "INSERT INTO users (name) VALUES ('Bob')"
73
+ * ]);
74
+ * await tx.commit();
75
+ * ```
76
+ */
77
+ async batch(statements) {
78
+ this.checkState();
79
+ return this.session.batch(statements);
80
+ }
81
+ /**
82
+ * Execute a SQL statement and return the raw response and entries.
83
+ *
84
+ * @param sql - The SQL statement to execute
85
+ * @param args - Optional array of parameter values
86
+ * @returns Promise resolving to the raw response and cursor entries
87
+ */
88
+ async executeRaw(sql, args = []) {
89
+ this.checkState();
90
+ return this.session.executeRaw(sql, args);
91
+ }
92
+ /**
93
+ * Commit the transaction, making all changes permanent.
94
+ *
95
+ * @returns Promise that resolves when the transaction is committed
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const tx = await client.transaction();
100
+ * await tx.execute("INSERT INTO users (name) VALUES (?)", ["Alice"]);
101
+ * await tx.commit(); // Changes are now permanent
102
+ * ```
103
+ */
104
+ async commit() {
105
+ this.checkState();
106
+ try {
107
+ await this.session.execute("COMMIT");
108
+ this._committed = true;
109
+ this._closed = true;
110
+ }
111
+ catch (error) {
112
+ // If commit fails, the transaction is still open
113
+ if (error instanceof Error) {
114
+ throw new DatabaseError(error.message);
115
+ }
116
+ throw new DatabaseError('Transaction commit failed');
117
+ }
118
+ }
119
+ /**
120
+ * Rollback the transaction, undoing all changes.
121
+ *
122
+ * @returns Promise that resolves when the transaction is rolled back
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * const tx = await client.transaction();
127
+ * try {
128
+ * await tx.execute("INSERT INTO users (name) VALUES (?)", ["Alice"]);
129
+ * await tx.execute("INSERT INTO invalid_table VALUES (1)"); // This will fail
130
+ * await tx.commit();
131
+ * } catch (error) {
132
+ * await tx.rollback(); // Undo the first INSERT
133
+ * }
134
+ * ```
135
+ */
136
+ async rollback() {
137
+ if (this._closed) {
138
+ return; // Already closed, nothing to rollback
139
+ }
140
+ try {
141
+ await this.session.execute("ROLLBACK");
142
+ }
143
+ catch (error) {
144
+ // Rollback errors are generally not critical - the transaction is abandoned anyway
145
+ }
146
+ finally {
147
+ this._rolledBack = true;
148
+ this._closed = true;
149
+ }
150
+ }
151
+ /**
152
+ * Close the transaction without committing or rolling back.
153
+ *
154
+ * This will automatically rollback any uncommitted changes.
155
+ * It's safe to call this method multiple times.
156
+ */
157
+ close() {
158
+ if (!this._closed) {
159
+ // Async rollback - don't wait for it to complete
160
+ this.rollback().catch(() => {
161
+ // Ignore rollback errors on close
162
+ });
163
+ }
164
+ }
165
+ /**
166
+ * Check if the transaction is closed.
167
+ *
168
+ * @returns True if the transaction has been committed, rolled back, or closed
169
+ */
170
+ get closed() {
171
+ return this._closed;
172
+ }
173
+ /**
174
+ * Check if the transaction has been committed.
175
+ *
176
+ * @returns True if the transaction has been successfully committed
177
+ */
178
+ get committed() {
179
+ return this._committed;
180
+ }
181
+ /**
182
+ * Check if the transaction has been rolled back.
183
+ *
184
+ * @returns True if the transaction has been rolled back
185
+ */
186
+ get rolledBack() {
187
+ return this._rolledBack;
188
+ }
189
+ /**
190
+ * Check transaction state and throw if it's not valid for operations.
191
+ *
192
+ * @throws Error if the transaction is closed
193
+ */
194
+ checkState() {
195
+ if (this._closed) {
196
+ if (this._committed) {
197
+ throw new DatabaseError("Transaction has already been committed");
198
+ }
199
+ else if (this._rolledBack) {
200
+ throw new DatabaseError("Transaction has already been rolled back");
201
+ }
202
+ else {
203
+ throw new DatabaseError("Transaction has been closed");
204
+ }
205
+ }
206
+ }
207
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tursodatabase/serverless",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/dist/client.d.ts DELETED
@@ -1,31 +0,0 @@
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;