@tursodatabase/serverless 0.1.1 → 0.1.3

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 CHANGED
@@ -1,9 +1,22 @@
1
- # Turso serverless JavaScript driver
1
+ <p align="center">
2
+ <h1 align="center">Turso Serverless Driver for JavaScript</h1>
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a title="JavaScript" target="_blank" href="https://www.npmjs.com/package/@tursodatabase/serverless"><img alt="npm" src="https://img.shields.io/npm/v/@tursodatabase/serverless"></a>
7
+ <a title="MIT" target="_blank" href="https://github.com/tursodatabase/turso/blob/main/LICENSE.md"><img src="http://img.shields.io/badge/license-MIT-orange.svg?style=flat-square"></a>
8
+ </p>
9
+ <p align="center">
10
+ <a title="Users Discord" target="_blank" href="https://tur.so/discord"><img alt="Chat with other users of Turso on Discord" src="https://img.shields.io/discord/933071162680958986?label=Discord&logo=Discord&style=social"></a>
11
+ </p>
12
+
13
+ ---
14
+
15
+ ## About
2
16
 
3
17
  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
18
 
5
- > [!NOTE]
6
- > This driver is experimental and, therefore, subject to change at any time.
19
+ > **📝 Note:** This driver is experimental and, therefore, subject to change at any time.
7
20
 
8
21
  ## Installation
9
22
 
@@ -11,7 +24,9 @@ A serverless database driver for Turso Cloud, using only `fetch()`. Connect to y
11
24
  npm install @tursodatabase/serverless
12
25
  ```
13
26
 
14
- ## Usage
27
+ ## Getting Started
28
+
29
+ ### Basic Usage
15
30
 
16
31
  ```javascript
17
32
  import { connect } from "@tursodatabase/serverless";
@@ -36,7 +51,11 @@ console.log(rows);
36
51
  for await (const row of stmt.iterate([123])) {
37
52
  console.log(row);
38
53
  }
54
+ ```
55
+
56
+ ### Batch Operations
39
57
 
58
+ ```javascript
40
59
  // Execute multiple statements in a batch
41
60
  await conn.batch([
42
61
  "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, email TEXT)",
@@ -45,9 +64,9 @@ await conn.batch([
45
64
  ]);
46
65
  ```
47
66
 
48
- ### Compatibility layer for libSQL API
67
+ ### libSQL Compatibility Layer
49
68
 
50
- This driver supports the libSQL API as a compatibility layer.
69
+ For existing libSQL applications, use the compatibility layer:
51
70
 
52
71
  ```javascript
53
72
  import { createClient } from "@tursodatabase/serverless/compat";
@@ -73,6 +92,21 @@ await client.batch([
73
92
 
74
93
  Check out the `examples/` directory for complete usage examples.
75
94
 
95
+ ## API Reference
96
+
97
+ For complete API documentation, see [JavaScript API Reference](../../docs/javascript-api-reference.md).
98
+
99
+ ## Related Packages
100
+
101
+ * The [@tursodatabase/database](https://www.npmjs.com/package/@tursodatabase/database) package provides the Turso in-memory database, compatible with SQLite.
102
+ * The [@tursodatabase/sync](https://www.npmjs.com/package/@tursodatabase/sync) package provides bidirectional sync between a local Turso database and Turso Cloud.
103
+
76
104
  ## License
77
105
 
78
- MIT
106
+ This project is licensed under the [MIT license](../../LICENSE.md).
107
+
108
+ ## Support
109
+
110
+ - [GitHub Issues](https://github.com/tursodatabase/turso/issues)
111
+ - [Documentation](https://docs.turso.tech)
112
+ - [Discord Community](https://tur.so/discord)
package/dist/compat.js CHANGED
@@ -1,4 +1,4 @@
1
- import { connect } from './connection.js';
1
+ import { Session } from './session.js';
2
2
  /**
3
3
  * libSQL-compatible error class with error codes.
4
4
  */
@@ -14,11 +14,11 @@ class LibSQLClient {
14
14
  constructor(config) {
15
15
  this._closed = false;
16
16
  this.validateConfig(config);
17
- const tursoConfig = {
17
+ const sessionConfig = {
18
18
  url: config.url,
19
19
  authToken: config.authToken || ''
20
20
  };
21
- this.connection = connect(tursoConfig);
21
+ this.session = new Session(sessionConfig);
22
22
  }
23
23
  validateConfig(config) {
24
24
  // Check for unsupported config options
@@ -108,8 +108,15 @@ class LibSQLClient {
108
108
  else {
109
109
  normalizedStmt = this.normalizeStatement(stmtOrSql);
110
110
  }
111
- const result = await this.connection.execute(normalizedStmt.sql, normalizedStmt.args);
112
- return this.convertResult(result);
111
+ await this.session.sequence(normalizedStmt.sql);
112
+ // Return empty result set for sequence execution
113
+ return this.convertResult({
114
+ columns: [],
115
+ columnTypes: [],
116
+ rows: [],
117
+ rowsAffected: 0,
118
+ lastInsertRowid: undefined
119
+ });
113
120
  }
114
121
  catch (error) {
115
122
  throw new LibsqlError(error.message, "EXECUTE_ERROR");
@@ -124,7 +131,7 @@ class LibSQLClient {
124
131
  const normalized = this.normalizeStatement(stmt);
125
132
  return normalized.sql; // For now, ignore args in batch
126
133
  });
127
- const result = await this.connection.batch(sqlStatements, mode);
134
+ const result = await this.session.batch(sqlStatements);
128
135
  // Return array of result sets (simplified - actual implementation would be more complex)
129
136
  return [this.convertResult(result)];
130
137
  }
@@ -140,13 +147,26 @@ class LibSQLClient {
140
147
  throw new LibsqlError("Transactions not implemented", "NOT_IMPLEMENTED");
141
148
  }
142
149
  async executeMultiple(sql) {
143
- throw new LibsqlError("Execute multiple not implemented", "NOT_IMPLEMENTED");
150
+ try {
151
+ if (this._closed) {
152
+ throw new LibsqlError("Client is closed", "CLIENT_CLOSED");
153
+ }
154
+ await this.session.sequence(sql);
155
+ }
156
+ catch (error) {
157
+ throw new LibsqlError(error.message, "EXECUTE_MULTIPLE_ERROR");
158
+ }
144
159
  }
145
160
  async sync() {
146
161
  throw new LibsqlError("Sync not supported for remote databases", "NOT_SUPPORTED");
147
162
  }
148
163
  close() {
149
164
  this._closed = true;
165
+ // Note: The libSQL client interface expects synchronous close,
166
+ // but our underlying session needs async close. We'll fire and forget.
167
+ this.session.close().catch(error => {
168
+ console.error('Error closing session:', error);
169
+ });
150
170
  }
151
171
  }
152
172
  /**
@@ -14,6 +14,7 @@ export interface Config extends SessionConfig {
14
14
  export declare class Connection {
15
15
  private config;
16
16
  private session;
17
+ private isOpen;
17
18
  constructor(config: Config);
18
19
  /**
19
20
  * Prepare a SQL statement for execution.
@@ -35,7 +36,6 @@ export declare class Connection {
35
36
  * Execute a SQL statement and return all results.
36
37
  *
37
38
  * @param sql - The SQL statement to execute
38
- * @param args - Optional array of parameter values
39
39
  * @returns Promise resolving to the complete result set
40
40
  *
41
41
  * @example
@@ -44,7 +44,7 @@ export declare class Connection {
44
44
  * console.log(result.rows);
45
45
  * ```
46
46
  */
47
- execute(sql: string, args?: any[]): Promise<any>;
47
+ exec(sql: string): Promise<any>;
48
48
  /**
49
49
  * Execute multiple SQL statements in a batch.
50
50
  *
@@ -62,6 +62,19 @@ export declare class Connection {
62
62
  * ```
63
63
  */
64
64
  batch(statements: string[], mode?: string): Promise<any>;
65
+ /**
66
+ * Execute a pragma.
67
+ *
68
+ * @param pragma - The pragma to execute
69
+ * @returns Promise resolving to the result of the pragma
70
+ */
71
+ pragma(pragma: string): Promise<any>;
72
+ /**
73
+ * Close the connection.
74
+ *
75
+ * This sends a close request to the server to properly clean up the stream.
76
+ */
77
+ close(): Promise<void>;
65
78
  }
66
79
  /**
67
80
  * Create a new connection to a Turso database.
@@ -8,6 +8,10 @@ import { Statement } from './statement.js';
8
8
  */
9
9
  export class Connection {
10
10
  constructor(config) {
11
+ this.isOpen = true;
12
+ if (!config.url) {
13
+ throw new Error("invalid config: url is required");
14
+ }
11
15
  this.config = config;
12
16
  this.session = new Session(config);
13
17
  }
@@ -27,13 +31,15 @@ export class Connection {
27
31
  * ```
28
32
  */
29
33
  prepare(sql) {
34
+ if (!this.isOpen) {
35
+ throw new TypeError("The database connection is not open");
36
+ }
30
37
  return new Statement(this.config, sql);
31
38
  }
32
39
  /**
33
40
  * Execute a SQL statement and return all results.
34
41
  *
35
42
  * @param sql - The SQL statement to execute
36
- * @param args - Optional array of parameter values
37
43
  * @returns Promise resolving to the complete result set
38
44
  *
39
45
  * @example
@@ -42,8 +48,11 @@ export class Connection {
42
48
  * console.log(result.rows);
43
49
  * ```
44
50
  */
45
- async execute(sql, args = []) {
46
- return this.session.execute(sql, args);
51
+ async exec(sql) {
52
+ if (!this.isOpen) {
53
+ throw new TypeError("The database connection is not open");
54
+ }
55
+ return this.session.sequence(sql);
47
56
  }
48
57
  /**
49
58
  * Execute multiple SQL statements in a batch.
@@ -64,6 +73,28 @@ export class Connection {
64
73
  async batch(statements, mode) {
65
74
  return this.session.batch(statements);
66
75
  }
76
+ /**
77
+ * Execute a pragma.
78
+ *
79
+ * @param pragma - The pragma to execute
80
+ * @returns Promise resolving to the result of the pragma
81
+ */
82
+ async pragma(pragma) {
83
+ if (!this.isOpen) {
84
+ throw new TypeError("The database connection is not open");
85
+ }
86
+ const sql = `PRAGMA ${pragma}`;
87
+ return this.session.execute(sql);
88
+ }
89
+ /**
90
+ * Close the connection.
91
+ *
92
+ * This sends a close request to the server to properly clean up the stream.
93
+ */
94
+ async close() {
95
+ this.isOpen = false;
96
+ await this.session.close();
97
+ }
67
98
  }
68
99
  /**
69
100
  * Create a new connection to a Turso database.
@@ -0,0 +1,3 @@
1
+ export declare class DatabaseError extends Error {
2
+ constructor(message: string);
3
+ }
package/dist/error.js ADDED
@@ -0,0 +1,7 @@
1
+ export class DatabaseError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = 'DatabaseError';
5
+ Object.setPrototypeOf(this, DatabaseError.prototype);
6
+ }
7
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { Connection, connect, type Config } from './connection.js';
2
2
  export { Statement } from './statement.js';
3
+ export { DatabaseError } from './error.js';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  // Turso serverless driver entry point
2
2
  export { Connection, connect } from './connection.js';
3
3
  export { Statement } from './statement.js';
4
+ export { DatabaseError } from './error.js';
@@ -13,12 +13,16 @@ export interface ExecuteResult {
13
13
  affected_row_count: number;
14
14
  last_insert_rowid?: string;
15
15
  }
16
+ export interface NamedArg {
17
+ name: string;
18
+ value: Value;
19
+ }
16
20
  export interface ExecuteRequest {
17
21
  type: 'execute';
18
22
  stmt: {
19
23
  sql: string;
20
24
  args: Value[];
21
- named_args: Value[];
25
+ named_args: NamedArg[];
22
26
  want_rows: boolean;
23
27
  };
24
28
  }
@@ -26,6 +30,7 @@ export interface BatchStep {
26
30
  stmt: {
27
31
  sql: string;
28
32
  args: Value[];
33
+ named_args?: NamedArg[];
29
34
  want_rows: boolean;
30
35
  };
31
36
  condition?: {
@@ -39,9 +44,16 @@ export interface BatchRequest {
39
44
  steps: BatchStep[];
40
45
  };
41
46
  }
47
+ export interface SequenceRequest {
48
+ type: 'sequence';
49
+ sql: string;
50
+ }
51
+ export interface CloseRequest {
52
+ type: 'close';
53
+ }
42
54
  export interface PipelineRequest {
43
55
  baton: string | null;
44
- requests: (ExecuteRequest | BatchRequest)[];
56
+ requests: (ExecuteRequest | BatchRequest | SequenceRequest | CloseRequest)[];
45
57
  }
46
58
  export interface PipelineResponse {
47
59
  baton: string | null;
@@ -49,8 +61,8 @@ export interface PipelineResponse {
49
61
  results: Array<{
50
62
  type: 'ok' | 'error';
51
63
  response?: {
52
- type: 'execute' | 'batch';
53
- result: ExecuteResult;
64
+ type: 'execute' | 'batch' | 'sequence' | 'close';
65
+ result?: ExecuteResult;
54
66
  };
55
67
  error?: {
56
68
  message: string;
package/dist/protocol.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { DatabaseError } from './error.js';
1
2
  export function encodeValue(value) {
2
3
  if (value === null || value === undefined) {
3
4
  return { type: 'null' };
@@ -62,55 +63,69 @@ export async function executeCursor(url, authToken, request) {
62
63
  catch {
63
64
  // If we can't parse the error body, use the default HTTP error message
64
65
  }
65
- throw new Error(errorMessage);
66
+ throw new DatabaseError(errorMessage);
66
67
  }
67
68
  const reader = response.body?.getReader();
68
69
  if (!reader) {
69
- throw new Error('No response body');
70
+ throw new DatabaseError('No response body');
70
71
  }
71
72
  const decoder = new TextDecoder();
72
73
  let buffer = '';
73
- let isFirstLine = true;
74
74
  let cursorResponse;
75
+ // First, read until we get the cursor response (first line)
76
+ while (!cursorResponse) {
77
+ const { done, value } = await reader.read();
78
+ if (done)
79
+ break;
80
+ buffer += decoder.decode(value, { stream: true });
81
+ const newlineIndex = buffer.indexOf('\n');
82
+ if (newlineIndex !== -1) {
83
+ const line = buffer.slice(0, newlineIndex).trim();
84
+ buffer = buffer.slice(newlineIndex + 1);
85
+ if (line) {
86
+ cursorResponse = JSON.parse(line);
87
+ break;
88
+ }
89
+ }
90
+ }
91
+ if (!cursorResponse) {
92
+ throw new DatabaseError('No cursor response received');
93
+ }
75
94
  async function* parseEntries() {
76
95
  try {
96
+ // Process any remaining data in the buffer
97
+ let newlineIndex;
98
+ while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
99
+ const line = buffer.slice(0, newlineIndex).trim();
100
+ buffer = buffer.slice(newlineIndex + 1);
101
+ if (line) {
102
+ yield JSON.parse(line);
103
+ }
104
+ }
105
+ // Continue reading from the stream
77
106
  while (true) {
78
107
  const { done, value } = await reader.read();
79
108
  if (done)
80
109
  break;
81
110
  buffer += decoder.decode(value, { stream: true });
82
- let newlineIndex;
83
111
  while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
84
112
  const line = buffer.slice(0, newlineIndex).trim();
85
113
  buffer = buffer.slice(newlineIndex + 1);
86
114
  if (line) {
87
- if (isFirstLine) {
88
- cursorResponse = JSON.parse(line);
89
- isFirstLine = false;
90
- }
91
- else {
92
- yield JSON.parse(line);
93
- }
115
+ yield JSON.parse(line);
94
116
  }
95
117
  }
96
118
  }
119
+ // Process any remaining data in the buffer
120
+ if (buffer.trim()) {
121
+ yield JSON.parse(buffer.trim());
122
+ }
97
123
  }
98
124
  finally {
99
125
  reader.releaseLock();
100
126
  }
101
127
  }
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 };
128
+ return { response: cursorResponse, entries: parseEntries() };
114
129
  }
115
130
  export async function executePipeline(url, authToken, request) {
116
131
  const response = await fetch(`${url}/v3/pipeline`, {
@@ -122,7 +137,7 @@ export async function executePipeline(url, authToken, request) {
122
137
  body: JSON.stringify(request),
123
138
  });
124
139
  if (!response.ok) {
125
- throw new Error(`HTTP error! status: ${response.status}`);
140
+ throw new DatabaseError(`HTTP error! status: ${response.status}`);
126
141
  }
127
142
  return response.json();
128
143
  }
package/dist/session.d.ts CHANGED
@@ -23,18 +23,18 @@ export declare class Session {
23
23
  * Execute a SQL statement and return all results.
24
24
  *
25
25
  * @param sql - The SQL statement to execute
26
- * @param args - Optional array of parameter values
26
+ * @param args - Optional array of parameter values or object with named parameters
27
27
  * @returns Promise resolving to the complete result set
28
28
  */
29
- execute(sql: string, args?: any[]): Promise<any>;
29
+ execute(sql: string, args?: any[] | Record<string, any>): Promise<any>;
30
30
  /**
31
31
  * Execute a SQL statement and return the raw response and entries.
32
32
  *
33
33
  * @param sql - The SQL statement to execute
34
- * @param args - Optional array of parameter values
34
+ * @param args - Optional array of parameter values or object with named parameters
35
35
  * @returns Promise resolving to the raw response and cursor entries
36
36
  */
37
- executeRaw(sql: string, args?: any[]): Promise<{
37
+ executeRaw(sql: string, args?: any[] | Record<string, any>): Promise<{
38
38
  response: CursorResponse;
39
39
  entries: AsyncGenerator<CursorEntry>;
40
40
  }>;
@@ -60,4 +60,18 @@ export declare class Session {
60
60
  * @returns Promise resolving to batch execution results
61
61
  */
62
62
  batch(statements: string[]): Promise<any>;
63
+ /**
64
+ * Execute a sequence of SQL statements separated by semicolons.
65
+ *
66
+ * @param sql - SQL string containing multiple statements separated by semicolons
67
+ * @returns Promise resolving when all statements are executed
68
+ */
69
+ sequence(sql: string): Promise<void>;
70
+ /**
71
+ * Close the session.
72
+ *
73
+ * This sends a close request to the server to properly clean up the stream
74
+ * before resetting the local state.
75
+ */
76
+ close(): Promise<void>;
63
77
  }
package/dist/session.js CHANGED
@@ -1,4 +1,5 @@
1
- import { executeCursor, encodeValue, decodeValue } from './protocol.js';
1
+ import { executeCursor, executePipeline, encodeValue, decodeValue } from './protocol.js';
2
+ import { DatabaseError } from './error.js';
2
3
  function normalizeUrl(url) {
3
4
  return url.replace(/^libsql:\/\//, 'https://');
4
5
  }
@@ -21,7 +22,7 @@ export class Session {
21
22
  * Execute a SQL statement and return all results.
22
23
  *
23
24
  * @param sql - The SQL statement to execute
24
- * @param args - Optional array of parameter values
25
+ * @param args - Optional array of parameter values or object with named parameters
25
26
  * @returns Promise resolving to the complete result set
26
27
  */
27
28
  async execute(sql, args = []) {
@@ -33,17 +34,53 @@ export class Session {
33
34
  * Execute a SQL statement and return the raw response and entries.
34
35
  *
35
36
  * @param sql - The SQL statement to execute
36
- * @param args - Optional array of parameter values
37
+ * @param args - Optional array of parameter values or object with named parameters
37
38
  * @returns Promise resolving to the raw response and cursor entries
38
39
  */
39
40
  async executeRaw(sql, args = []) {
41
+ let positionalArgs = [];
42
+ let namedArgs = [];
43
+ if (Array.isArray(args)) {
44
+ positionalArgs = args.map(encodeValue);
45
+ }
46
+ else {
47
+ // Check if this is an object with numeric keys (for ?1, ?2 style parameters)
48
+ const keys = Object.keys(args);
49
+ const isNumericKeys = keys.length > 0 && keys.every(key => /^\d+$/.test(key));
50
+ if (isNumericKeys) {
51
+ // Convert numeric-keyed object to positional args
52
+ // Sort keys numerically to ensure correct order
53
+ const sortedKeys = keys.sort((a, b) => parseInt(a) - parseInt(b));
54
+ const maxIndex = parseInt(sortedKeys[sortedKeys.length - 1]);
55
+ // Create array with undefined for missing indices
56
+ positionalArgs = new Array(maxIndex);
57
+ for (const key of sortedKeys) {
58
+ const index = parseInt(key) - 1; // Convert to 0-based index
59
+ positionalArgs[index] = encodeValue(args[key]);
60
+ }
61
+ // Fill any undefined values with null
62
+ for (let i = 0; i < positionalArgs.length; i++) {
63
+ if (positionalArgs[i] === undefined) {
64
+ positionalArgs[i] = { type: 'null' };
65
+ }
66
+ }
67
+ }
68
+ else {
69
+ // Convert object with named parameters to NamedArg array
70
+ namedArgs = Object.entries(args).map(([name, value]) => ({
71
+ name,
72
+ value: encodeValue(value)
73
+ }));
74
+ }
75
+ }
40
76
  const request = {
41
77
  baton: this.baton,
42
78
  batch: {
43
79
  steps: [{
44
80
  stmt: {
45
81
  sql,
46
- args: args.map(encodeValue),
82
+ args: positionalArgs,
83
+ named_args: namedArgs,
47
84
  want_rows: true
48
85
  }
49
86
  }]
@@ -93,7 +130,7 @@ export class Session {
93
130
  break;
94
131
  case 'step_error':
95
132
  case 'error':
96
- throw new Error(entry.error?.message || 'SQL execution failed');
133
+ throw new DatabaseError(entry.error?.message || 'SQL execution failed');
97
134
  }
98
135
  }
99
136
  return {
@@ -141,6 +178,7 @@ export class Session {
141
178
  stmt: {
142
179
  sql,
143
180
  args: [],
181
+ named_args: [],
144
182
  want_rows: false
145
183
  }
146
184
  }))
@@ -165,7 +203,7 @@ export class Session {
165
203
  break;
166
204
  case 'step_error':
167
205
  case 'error':
168
- throw new Error(entry.error?.message || 'Batch execution failed');
206
+ throw new DatabaseError(entry.error?.message || 'Batch execution failed');
169
207
  }
170
208
  }
171
209
  return {
@@ -173,4 +211,58 @@ export class Session {
173
211
  lastInsertRowid
174
212
  };
175
213
  }
214
+ /**
215
+ * Execute a sequence of SQL statements separated by semicolons.
216
+ *
217
+ * @param sql - SQL string containing multiple statements separated by semicolons
218
+ * @returns Promise resolving when all statements are executed
219
+ */
220
+ async sequence(sql) {
221
+ const request = {
222
+ baton: this.baton,
223
+ requests: [{
224
+ type: "sequence",
225
+ sql: sql
226
+ }]
227
+ };
228
+ const response = await executePipeline(this.baseUrl, this.config.authToken, request);
229
+ this.baton = response.baton;
230
+ if (response.base_url) {
231
+ this.baseUrl = response.base_url;
232
+ }
233
+ // Check for errors in the response
234
+ if (response.results && response.results[0]) {
235
+ const result = response.results[0];
236
+ if (result.type === "error") {
237
+ throw new DatabaseError(result.error?.message || 'Sequence execution failed');
238
+ }
239
+ }
240
+ }
241
+ /**
242
+ * Close the session.
243
+ *
244
+ * This sends a close request to the server to properly clean up the stream
245
+ * before resetting the local state.
246
+ */
247
+ async close() {
248
+ // Only send close request if we have an active baton
249
+ if (this.baton) {
250
+ try {
251
+ const request = {
252
+ baton: this.baton,
253
+ requests: [{
254
+ type: "close"
255
+ }]
256
+ };
257
+ await executePipeline(this.baseUrl, this.config.authToken, request);
258
+ }
259
+ catch (error) {
260
+ // Ignore errors during close, as the connection might already be closed
261
+ console.error('Error closing session:', error);
262
+ }
263
+ }
264
+ // Reset local state
265
+ this.baton = null;
266
+ this.baseUrl = '';
267
+ }
176
268
  }