@sqlitecloud/drivers 1.0.417 → 1.0.422

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
@@ -54,7 +54,7 @@ let database = new Database('sqlitecloud://user:password@xxx.sqlite.cloud:8860/c
54
54
 
55
55
  let name = 'Breaking The Rules'
56
56
 
57
- let results = await database.sql`SELECT * FROM tracks WHERE name = ${name}`
57
+ let results = await database.sql('SELECT * FROM tracks WHERE name = ?', name)
58
58
  // => returns [{ AlbumId: 1, Name: 'Breaking The Rules', Composer: 'Angus Young... }]
59
59
  ```
60
60
 
@@ -82,7 +82,7 @@ await pubSub.listen(PUBSUB_ENTITY_TYPE.TABLE, 'albums', (error, results, data) =
82
82
  }
83
83
  })
84
84
 
85
- await database.sql`INSERT INTO albums (Title, ArtistId) values ('Brand new song', 1)`
85
+ await database.sql("INSERT INTO albums (Title, ArtistId) values ('Brand new song', 1)")
86
86
 
87
87
  // Stop listening changes on the table
88
88
  await pubSub.unlisten(PUBSUB_ENTITY_TYPE.TABLE, 'albums')
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * connection-tls.ts - connection via tls socket and sqlitecloud protocol
3
3
  */
4
- import { type SQLiteCloudConfig, type ErrorCallback, type ResultsCallback, SQLiteCloudCommand } from './types';
5
4
  import { SQLiteCloudConnection } from './connection';
5
+ import { type ErrorCallback, type ResultsCallback, SQLiteCloudCommand, type SQLiteCloudConfig } from './types';
6
6
  /**
7
7
  * Implementation of SQLiteCloudConnection that connects to the database using specific tls APIs
8
8
  * that connect to native sockets or tls sockets and communicates via raw, binary protocol.
@@ -37,10 +37,10 @@ var __importStar = (this && this.__importStar) || (function () {
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.SQLiteCloudTlsConnection = void 0;
40
- const types_1 = require("./types");
41
40
  const connection_1 = require("./connection");
42
- const utilities_1 = require("./utilities");
43
41
  const protocol_1 = require("./protocol");
42
+ const types_1 = require("./types");
43
+ const utilities_1 = require("./utilities");
44
44
  // explicitly importing buffer library to allow cross-platform support by replacing it
45
45
  const buffer_1 = require("buffer");
46
46
  const tls = __importStar(require("tls"));
@@ -101,6 +101,10 @@ class SQLiteCloudTlsConnection extends connection_1.SQLiteCloudConnection {
101
101
  callback === null || callback === void 0 ? void 0 : callback.call(this, error);
102
102
  });
103
103
  });
104
+ this.socket.setKeepAlive(true);
105
+ // disable Nagle algorithm because we want our writes to be sent ASAP
106
+ // https://brooker.co.za/blog/2024/05/09/nagle.html
107
+ this.socket.setNoDelay(true);
104
108
  this.socket.on('data', data => {
105
109
  this.processCommandsData(data);
106
110
  });
@@ -117,6 +121,10 @@ class SQLiteCloudTlsConnection extends connection_1.SQLiteCloudConnection {
117
121
  this.close();
118
122
  this.processCommandsFinish(new types_1.SQLiteCloudError('Connection closed', { errorCode: 'ERR_CONNECTION_CLOSED' }));
119
123
  });
124
+ this.socket.on('timeout', () => {
125
+ this.close();
126
+ this.processCommandsFinish(new types_1.SQLiteCloudError('Connection ened due to timeout', { errorCode: 'ERR_CONNECTION_TIMEOUT' }));
127
+ });
120
128
  return this;
121
129
  }
122
130
  /** Will send a command immediately (no queueing), return the rowset/result or throw an error */
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * transport-ws.ts - handles low level communication with sqlitecloud server via socket.io websocket
3
3
  */
4
- import { SQLiteCloudConfig, ErrorCallback, ResultsCallback, SQLiteCloudCommand } from './types';
5
4
  import { SQLiteCloudConnection } from './connection';
5
+ import { ErrorCallback, ResultsCallback, SQLiteCloudCommand, SQLiteCloudConfig } from './types';
6
6
  /**
7
7
  * Implementation of TransportConnection that connects to the database indirectly
8
8
  * via SQLite Cloud Gateway, a socket.io based deamon that responds to sql query
@@ -4,10 +4,10 @@
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.SQLiteCloudWebsocketConnection = void 0;
7
- const types_1 = require("./types");
8
- const rowset_1 = require("./rowset");
9
- const connection_1 = require("./connection");
10
7
  const socket_io_client_1 = require("socket.io-client");
8
+ const connection_1 = require("./connection");
9
+ const rowset_1 = require("./rowset");
10
+ const types_1 = require("./types");
11
11
  /**
12
12
  * Implementation of TransportConnection that connects to the database indirectly
13
13
  * via SQLite Cloud Gateway, a socket.io based deamon that responds to sql query
@@ -17,7 +17,8 @@ const socket_io_client_1 = require("socket.io-client");
17
17
  class SQLiteCloudWebsocketConnection extends connection_1.SQLiteCloudConnection {
18
18
  /** True if connection is open */
19
19
  get connected() {
20
- return !!this.socket;
20
+ var _a;
21
+ return !!(this.socket && ((_a = this.socket) === null || _a === void 0 ? void 0 : _a.connected));
21
22
  }
22
23
  /* Opens a connection with the server and sends the initialization commands. Will throw in case of errors. */
23
24
  connectTransport(config, callback) {
@@ -30,8 +31,18 @@ class SQLiteCloudWebsocketConnection extends connection_1.SQLiteCloudConnection
30
31
  const connectionstring = this.config.connectionstring;
31
32
  const gatewayUrl = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.gatewayurl) || `${this.config.host === 'localhost' ? 'ws' : 'wss'}://${this.config.host}:4000`;
32
33
  this.socket = (0, socket_io_client_1.io)(gatewayUrl, { auth: { token: connectionstring } });
34
+ this.socket.on('connect', () => {
35
+ callback === null || callback === void 0 ? void 0 : callback.call(this, null);
36
+ });
37
+ this.socket.on('disconnect', (reason) => {
38
+ this.close();
39
+ callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Disconnected', { errorCode: 'ERR_CONNECTION_ENDED', cause: reason }));
40
+ });
41
+ this.socket.on('error', (error) => {
42
+ this.close();
43
+ callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Connection error', { errorCode: 'ERR_CONNECTION_ERROR', cause: error }));
44
+ });
33
45
  }
34
- callback === null || callback === void 0 ? void 0 : callback.call(this, null);
35
46
  }
36
47
  catch (error) {
37
48
  callback === null || callback === void 0 ? void 0 : callback.call(this, error);
@@ -71,14 +82,14 @@ class SQLiteCloudWebsocketConnection extends connection_1.SQLiteCloudConnection
71
82
  }
72
83
  /** Disconnect socket.io from server */
73
84
  close() {
74
- var _a;
85
+ var _a, _b;
75
86
  console.assert(this.socket !== null, 'SQLiteCloudWebsocketConnection.close - connection already closed');
76
87
  if (this.socket) {
77
- (_a = this.socket) === null || _a === void 0 ? void 0 : _a.close();
88
+ (_a = this.socket) === null || _a === void 0 ? void 0 : _a.removeAllListeners();
89
+ (_b = this.socket) === null || _b === void 0 ? void 0 : _b.close();
78
90
  this.socket = undefined;
79
91
  }
80
92
  this.operations.clear();
81
- this.socket = undefined;
82
93
  return this;
83
94
  }
84
95
  }
@@ -1,7 +1,7 @@
1
1
  import EventEmitter from 'eventemitter3';
2
2
  import { PubSub } from './pubsub';
3
3
  import { Statement } from './statement';
4
- import { ErrorCallback, ResultsCallback, RowCallback, RowCountCallback, RowsCallback, SQLiteCloudCommand, SQLiteCloudConfig } from './types';
4
+ import { ErrorCallback as ConnectionCallback, ResultsCallback, RowCallback, RowCountCallback, RowsCallback, SQLiteCloudCommand, SQLiteCloudConfig } from './types';
5
5
  /**
6
6
  * Creating a Database object automatically opens a connection to the SQLite database.
7
7
  * When the connection is established the Database object emits an open event and calls
@@ -10,14 +10,17 @@ import { ErrorCallback, ResultsCallback, RowCallback, RowCountCallback, RowsCall
10
10
  */
11
11
  export declare class Database extends EventEmitter {
12
12
  /** Create and initialize a database from a full configuration object, or connection string */
13
- constructor(config: SQLiteCloudConfig | string, callback?: ErrorCallback);
14
- constructor(config: SQLiteCloudConfig | string, mode?: number, callback?: ErrorCallback);
13
+ constructor(config: SQLiteCloudConfig | string, callback?: ConnectionCallback);
14
+ constructor(config: SQLiteCloudConfig | string, mode?: number, callback?: ConnectionCallback);
15
15
  /** Configuration used to open database connections */
16
16
  private config;
17
- /** Database connections */
18
- private connections;
17
+ /** Database connection */
18
+ private connection;
19
+ /** Used to syncronize opening of connection and commands */
20
+ private operations;
19
21
  /** Returns first available connection from connection pool */
20
- private getConnection;
22
+ private createConnection;
23
+ private enqueueCommand;
21
24
  /** Handles an error by closing the connection, calling the callback and/or emitting an error event */
22
25
  private handleError;
23
26
  /**
@@ -114,7 +117,7 @@ export declare class Database extends EventEmitter {
114
117
  * object. When no callback is provided and an error occurs, an error event
115
118
  * will be emitted on the database object.
116
119
  */
117
- exec(sql: string, callback?: ErrorCallback): this;
120
+ exec(sql: string, callback?: ConnectionCallback): this;
118
121
  /**
119
122
  * If the optional callback is provided, this function will be called when the
120
123
  * database was closed successfully or when an error occurred. The first argument
@@ -123,7 +126,7 @@ export declare class Database extends EventEmitter {
123
126
  * will be emitted on the database object. If closing succeeded, a close event with no
124
127
  * parameters is emitted, regardless of whether a callback was provided or not.
125
128
  */
126
- close(callback?: ErrorCallback): void;
129
+ close(callback?: ConnectionCallback): void;
127
130
  /**
128
131
  * Loads a compiled SQLite extension into the database connection object.
129
132
  * @param path Filename of the extension to load.
@@ -133,7 +136,7 @@ export declare class Database extends EventEmitter {
133
136
  * and an error occurred, an error event with the error object as the only parameter
134
137
  * will be emitted on the database object.
135
138
  */
136
- loadExtension(_path: string, callback?: ErrorCallback): this;
139
+ loadExtension(_path: string, callback?: ConnectionCallback): this;
137
140
  /**
138
141
  * Allows the user to interrupt long-running queries. Wrapper around
139
142
  * sqlite3_interrupt and causes other data-fetching functions to be
@@ -55,6 +55,7 @@ exports.Database = void 0;
55
55
  // https://github.com/TryGhost/node-sqlite3/blob/master/lib/sqlite3.d.ts
56
56
  const eventemitter3_1 = __importDefault(require("eventemitter3"));
57
57
  const pubsub_1 = require("./pubsub");
58
+ const queue_1 = require("./queue");
58
59
  const rowset_1 = require("./rowset");
59
60
  const statement_1 = require("./statement");
60
61
  const types_1 = require("./types");
@@ -70,9 +71,10 @@ const utilities_1 = require("./utilities");
70
71
  class Database extends eventemitter3_1.default {
71
72
  constructor(config, mode, callback) {
72
73
  super();
73
- /** Database connections */
74
- this.connections = [];
74
+ /** Used to syncronize opening of connection and commands */
75
+ this.operations = new queue_1.OperationsQueue();
75
76
  this.config = typeof config === 'string' ? { connectionstring: config } : config;
77
+ this.connection = null;
76
78
  // mode is optional and so is callback
77
79
  // https://github.com/TryGhost/node-sqlite3/wiki/API#new-sqlite3databasefilename--mode--callback
78
80
  if (typeof mode === 'function') {
@@ -80,8 +82,8 @@ class Database extends eventemitter3_1.default {
80
82
  mode = undefined;
81
83
  }
82
84
  // mode is ignored for now
83
- // opens first connection to the database automatically
84
- this.getConnection((error, _connection) => {
85
+ // opens the connection to the database automatically
86
+ this.createConnection(error => {
85
87
  if (callback) {
86
88
  callback.call(this, error);
87
89
  }
@@ -91,60 +93,71 @@ class Database extends eventemitter3_1.default {
91
93
  // private methods
92
94
  //
93
95
  /** Returns first available connection from connection pool */
94
- getConnection(callback) {
95
- var _a, _b, _c;
96
- // TODO sqlitecloud-js / implement database connection pool #10
97
- if (((_a = this.connections) === null || _a === void 0 ? void 0 : _a.length) > 0) {
98
- callback === null || callback === void 0 ? void 0 : callback.call(this, null, this.connections[0]);
99
- }
100
- else {
101
- // connect using websocket if tls is not supported or if explicitly requested
102
- const useWebsocket = utilities_1.isBrowser || ((_b = this.config) === null || _b === void 0 ? void 0 : _b.usewebsocket) || ((_c = this.config) === null || _c === void 0 ? void 0 : _c.gatewayurl);
103
- if (useWebsocket) {
104
- // socket.io transport works in both node.js and browser environments and connects via SQLite Cloud Gateway
96
+ createConnection(callback) {
97
+ var _a, _b;
98
+ // connect using websocket if tls is not supported or if explicitly requested
99
+ const useWebsocket = utilities_1.isBrowser || ((_a = this.config) === null || _a === void 0 ? void 0 : _a.usewebsocket) || ((_b = this.config) === null || _b === void 0 ? void 0 : _b.gatewayurl);
100
+ if (useWebsocket) {
101
+ // socket.io transport works in both node.js and browser environments and connects via SQLite Cloud Gateway
102
+ this.operations.enqueue(done => {
105
103
  Promise.resolve().then(() => __importStar(require('./connection-ws'))).then(module => {
106
- this.connections.push(new module.default(this.config, error => {
104
+ this.connection = new module.default(this.config, (error) => {
107
105
  if (error) {
108
- this.handleError(this.connections[0], error, callback);
106
+ this.handleError(error, callback);
109
107
  }
110
108
  else {
111
- console.assert;
112
- callback === null || callback === void 0 ? void 0 : callback.call(this, null, this.connections[0]);
109
+ callback === null || callback === void 0 ? void 0 : callback.call(this, null);
113
110
  this.emitEvent('open');
114
111
  }
115
- }));
112
+ done(error);
113
+ });
116
114
  })
117
115
  .catch(error => {
118
- this.handleError(null, error, callback);
116
+ this.handleError(error, callback);
117
+ done(error);
119
118
  });
120
- }
121
- else {
122
- // tls sockets work only in node.js environments
119
+ });
120
+ }
121
+ else {
122
+ this.operations.enqueue(done => {
123
123
  Promise.resolve().then(() => __importStar(require('./connection-tls'))).then(module => {
124
- this.connections.push(new module.default(this.config, error => {
124
+ this.connection = new module.default(this.config, (error) => {
125
125
  if (error) {
126
- this.handleError(this.connections[0], error, callback);
126
+ this.handleError(error, callback);
127
127
  }
128
128
  else {
129
- console.assert;
130
- callback === null || callback === void 0 ? void 0 : callback.call(this, null, this.connections[0]);
129
+ callback === null || callback === void 0 ? void 0 : callback.call(this, null);
131
130
  this.emitEvent('open');
132
131
  }
133
- }));
132
+ done(error);
133
+ });
134
134
  })
135
135
  .catch(error => {
136
- this.handleError(null, error, callback);
136
+ this.handleError(error, callback);
137
+ done(error);
137
138
  });
138
- }
139
+ });
139
140
  }
140
141
  }
142
+ enqueueCommand(command, callback) {
143
+ this.operations.enqueue(done => {
144
+ let error = null;
145
+ // we don't wont to silently open a new connection after a disconnession
146
+ if (this.connection && this.connection.connected) {
147
+ this.connection.sendCommands(command, callback);
148
+ }
149
+ else {
150
+ error = new types_1.SQLiteCloudError('Connection unavailable. Maybe it got disconnected?', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' });
151
+ this.handleError(error, callback);
152
+ }
153
+ done(error);
154
+ });
155
+ }
141
156
  /** Handles an error by closing the connection, calling the callback and/or emitting an error event */
142
- handleError(connection, error, callback) {
157
+ handleError(error, callback) {
158
+ var _a;
143
159
  // an errored connection is thrown out
144
- if (connection) {
145
- this.connections = this.connections.filter(c => c !== connection);
146
- connection.close();
147
- }
160
+ (_a = this.connection) === null || _a === void 0 ? void 0 : _a.close();
148
161
  if (callback) {
149
162
  callback.call(this, error);
150
163
  }
@@ -198,10 +211,9 @@ class Database extends eventemitter3_1.default {
198
211
  }
199
212
  /** Enable verbose mode */
200
213
  verbose() {
214
+ var _a;
201
215
  this.config.verbose = true;
202
- for (const connection of this.connections) {
203
- connection.verbose();
204
- }
216
+ (_a = this.connection) === null || _a === void 0 ? void 0 : _a.verbose();
205
217
  return this;
206
218
  }
207
219
  /** Set a configuration option for the database */
@@ -212,21 +224,14 @@ class Database extends eventemitter3_1.default {
212
224
  run(sql, ...params) {
213
225
  const { args, callback } = (0, utilities_1.popCallback)(params);
214
226
  const command = { query: sql, parameters: args };
215
- this.getConnection((error, connection) => {
216
- if (error || !connection) {
217
- this.handleError(null, error, callback);
227
+ this.enqueueCommand(command, (error, results) => {
228
+ if (error) {
229
+ this.handleError(error, callback);
218
230
  }
219
231
  else {
220
- connection.sendCommands(command, (error, results) => {
221
- if (error) {
222
- this.handleError(connection, error, callback);
223
- }
224
- else {
225
- // context may include id of last row inserted, total changes, etc...
226
- const context = this.processContext(results);
227
- callback === null || callback === void 0 ? void 0 : callback.call(context || this, null, context ? context : results);
228
- }
229
- });
232
+ // context may include id of last row inserted, total changes, etc...
233
+ const context = this.processContext(results);
234
+ callback === null || callback === void 0 ? void 0 : callback.call(context || this, null, context ? context : results);
230
235
  }
231
236
  });
232
237
  return this;
@@ -234,24 +239,17 @@ class Database extends eventemitter3_1.default {
234
239
  get(sql, ...params) {
235
240
  const { args, callback } = (0, utilities_1.popCallback)(params);
236
241
  const command = { query: sql, parameters: args };
237
- this.getConnection((error, connection) => {
238
- if (error || !connection) {
239
- this.handleError(null, error, callback);
242
+ this.enqueueCommand(command, (error, results) => {
243
+ if (error) {
244
+ this.handleError(error, callback);
240
245
  }
241
246
  else {
242
- connection.sendCommands(command, (error, results) => {
243
- if (error) {
244
- this.handleError(connection, error, callback);
245
- }
246
- else {
247
- if (results && results instanceof rowset_1.SQLiteCloudRowset && results.length > 0) {
248
- callback === null || callback === void 0 ? void 0 : callback.call(this, null, results[0]);
249
- }
250
- else {
251
- callback === null || callback === void 0 ? void 0 : callback.call(this, null);
252
- }
253
- }
254
- });
247
+ if (results && results instanceof rowset_1.SQLiteCloudRowset && results.length > 0) {
248
+ callback === null || callback === void 0 ? void 0 : callback.call(this, null, results[0]);
249
+ }
250
+ else {
251
+ callback === null || callback === void 0 ? void 0 : callback.call(this, null);
252
+ }
255
253
  }
256
254
  });
257
255
  return this;
@@ -259,24 +257,17 @@ class Database extends eventemitter3_1.default {
259
257
  all(sql, ...params) {
260
258
  const { args, callback } = (0, utilities_1.popCallback)(params);
261
259
  const command = { query: sql, parameters: args };
262
- this.getConnection((error, connection) => {
263
- if (error || !connection) {
264
- this.handleError(null, error, callback);
260
+ this.enqueueCommand(command, (error, results) => {
261
+ if (error) {
262
+ this.handleError(error, callback);
265
263
  }
266
264
  else {
267
- connection.sendCommands(command, (error, results) => {
268
- if (error) {
269
- this.handleError(connection, error, callback);
270
- }
271
- else {
272
- if (results && results instanceof rowset_1.SQLiteCloudRowset) {
273
- callback === null || callback === void 0 ? void 0 : callback.call(this, null, results);
274
- }
275
- else {
276
- callback === null || callback === void 0 ? void 0 : callback.call(this, null);
277
- }
278
- }
279
- });
265
+ if (results && results instanceof rowset_1.SQLiteCloudRowset) {
266
+ callback === null || callback === void 0 ? void 0 : callback.call(this, null, results);
267
+ }
268
+ else {
269
+ callback === null || callback === void 0 ? void 0 : callback.call(this, null);
270
+ }
280
271
  }
281
272
  });
282
273
  return this;
@@ -285,32 +276,25 @@ class Database extends eventemitter3_1.default {
285
276
  // extract optional parameters and one or two callbacks
286
277
  const { args, callback, complete } = (0, utilities_1.popCallback)(params);
287
278
  const command = { query: sql, parameters: args };
288
- this.getConnection((error, connection) => {
289
- if (error || !connection) {
290
- this.handleError(null, error, callback);
279
+ this.enqueueCommand(command, (error, rowset) => {
280
+ if (error) {
281
+ this.handleError(error, callback);
291
282
  }
292
283
  else {
293
- connection.sendCommands(command, (error, rowset) => {
294
- if (error) {
295
- this.handleError(connection, error, callback);
296
- }
297
- else {
298
- if (rowset && rowset instanceof rowset_1.SQLiteCloudRowset) {
299
- if (callback) {
300
- for (const row of rowset) {
301
- callback.call(this, null, row);
302
- }
303
- }
304
- if (complete) {
305
- ;
306
- complete.call(this, null, rowset.numberOfRows);
307
- }
308
- }
309
- else {
310
- callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Invalid rowset'));
284
+ if (rowset && rowset instanceof rowset_1.SQLiteCloudRowset) {
285
+ if (callback) {
286
+ for (const row of rowset) {
287
+ callback.call(this, null, row);
311
288
  }
312
289
  }
313
- });
290
+ if (complete) {
291
+ ;
292
+ complete.call(this, null, rowset.numberOfRows);
293
+ }
294
+ }
295
+ else {
296
+ callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Invalid rowset'));
297
+ }
314
298
  }
315
299
  });
316
300
  return this;
@@ -336,20 +320,13 @@ class Database extends eventemitter3_1.default {
336
320
  * will be emitted on the database object.
337
321
  */
338
322
  exec(sql, callback) {
339
- this.getConnection((error, connection) => {
340
- if (error || !connection) {
341
- this.handleError(null, error, callback);
323
+ this.enqueueCommand(sql, (error, results) => {
324
+ if (error) {
325
+ this.handleError(error, callback);
342
326
  }
343
327
  else {
344
- connection.sendCommands(sql, (error, results) => {
345
- if (error) {
346
- this.handleError(connection, error, callback);
347
- }
348
- else {
349
- const context = this.processContext(results);
350
- callback === null || callback === void 0 ? void 0 : callback.call(context ? context : this, null);
351
- }
352
- });
328
+ const context = this.processContext(results);
329
+ callback === null || callback === void 0 ? void 0 : callback.call(context ? context : this, null);
353
330
  }
354
331
  });
355
332
  return this;
@@ -364,11 +341,8 @@ class Database extends eventemitter3_1.default {
364
341
  */
365
342
  close(callback) {
366
343
  var _a;
367
- if (((_a = this.connections) === null || _a === void 0 ? void 0 : _a.length) > 0) {
368
- for (const connection of this.connections) {
369
- connection.close();
370
- }
371
- }
344
+ this.operations.clear();
345
+ (_a = this.connection) === null || _a === void 0 ? void 0 : _a.close();
372
346
  callback === null || callback === void 0 ? void 0 : callback.call(this, null);
373
347
  this.emitEvent('close');
374
348
  }
@@ -436,21 +410,14 @@ class Database extends eventemitter3_1.default {
436
410
  throw new Error('Invalid sql');
437
411
  }
438
412
  return new Promise((resolve, reject) => {
439
- this.getConnection((error, connection) => {
440
- if (error || !connection) {
413
+ this.enqueueCommand(commands, (error, results) => {
414
+ if (error) {
441
415
  reject(error);
442
416
  }
443
417
  else {
444
- connection.sendCommands(commands, (error, results) => {
445
- if (error) {
446
- reject(error);
447
- }
448
- else {
449
- // metadata for operations like insert, update, delete?
450
- const context = this.processContext(results);
451
- resolve(context ? context : results);
452
- }
453
- });
418
+ // metadata for operations like insert, update, delete?
419
+ const context = this.processContext(results);
420
+ resolve(context ? context : results);
454
421
  }
455
422
  });
456
423
  });
@@ -460,8 +427,7 @@ class Database extends eventemitter3_1.default {
460
427
  * Returns true if the database connection is open.
461
428
  */
462
429
  isConnected() {
463
- var _a;
464
- return ((_a = this.connections) === null || _a === void 0 ? void 0 : _a.length) > 0 && this.connections[0].connected;
430
+ return this.connection != null && this.connection.connected;
465
431
  }
466
432
  /**
467
433
  * PubSub class provides a Pub/Sub real-time updates and notifications system to
@@ -474,12 +440,19 @@ class Database extends eventemitter3_1.default {
474
440
  getPubSub() {
475
441
  return __awaiter(this, void 0, void 0, function* () {
476
442
  return new Promise((resolve, reject) => {
477
- this.getConnection((error, connection) => {
478
- if (error || !connection) {
479
- reject(error);
443
+ this.operations.enqueue(done => {
444
+ let error = null;
445
+ try {
446
+ if (!this.connection) {
447
+ error = new types_1.SQLiteCloudError('Connection not established', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' });
448
+ reject(error);
449
+ }
450
+ else {
451
+ resolve(new pubsub_1.PubSub(this.connection));
452
+ }
480
453
  }
481
- else {
482
- resolve(new pubsub_1.PubSub(connection));
454
+ finally {
455
+ done(error);
483
456
  }
484
457
  });
485
458
  });
@@ -86,14 +86,14 @@ class PubSub {
86
86
  */
87
87
  removeChannel(name) {
88
88
  return __awaiter(this, void 0, void 0, function* () {
89
- return this.connection.sql(`REMOVE CHANNEL ?;`, name);
89
+ return this.connection.sql('REMOVE CHANNEL ?;', name);
90
90
  });
91
91
  }
92
92
  /**
93
93
  * Send a message to the channel.
94
94
  */
95
95
  notifyChannel(channelName, message) {
96
- return this.connection.sql `NOTIFY ${channelName} ${message};`;
96
+ return this.connection.sql('NOTIFY ? ?;', channelName, message);
97
97
  }
98
98
  /**
99
99
  * Ask the server to close the connection to the database and