@sqlitecloud/drivers 0.0.50 → 1.0.122

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
@@ -17,7 +17,7 @@ npm install @sqlitecloud/drivers
17
17
  ```ts
18
18
  import { Database } from '@sqlitecloud/drivers'
19
19
 
20
- let database = new Database('sqlitecloud://user:password@xxx.sqlite.cloud:8860/chinook.db')
20
+ let database = new Database('sqlitecloud://user:password@xxx.sqlite.cloud:8860/chinook.sqlite')
21
21
 
22
22
  let name = 'Breaking The Rules'
23
23
 
@@ -1,25 +1,29 @@
1
1
  /**
2
- * connection-tls.ts - handles low level communication with sqlitecloud server via tls socket and binary protocol
2
+ * connection-tls.ts - connection via tls socket and sqlitecloud protocol
3
3
  */
4
- import { SQLiteCloudConfig, ErrorCallback, ResultsCallback } from './types';
4
+ import { type SQLiteCloudConfig, type ErrorCallback, type ResultsCallback } from './types';
5
5
  import { SQLiteCloudConnection } from './connection';
6
6
  /**
7
- * Implementation of SQLiteCloudConnection that connects directly to the database via tls socket and raw, binary protocol.
8
- * Connects with plain socket with no encryption is the ?insecure=1 parameter is specified.
9
- * SQLiteCloud low-level connection, will do messaging, handle socket, authentication, etc.
10
- * A connection socket is established when the connection is created and closed when the connection is closed.
11
- * All operations are serialized by waiting for any pending operations to complete. Once a connection is closed,
12
- * it cannot be reopened and you must create a new connection.
7
+ * Implementation of SQLiteCloudConnection that connects to the database using specific Bun APIs
8
+ * that connect to native sockets or tls sockets and communicates via raw, binary protocol.
13
9
  */
14
10
  export declare class SQLiteCloudTlsConnection extends SQLiteCloudConnection {
15
- /** Currently opened tls socket used to communicated with SQLiteCloud server */
11
+ /** Currently opened bun socket used to communicated with SQLiteCloud server */
16
12
  private socket?;
17
13
  /** True if connection is open */
18
14
  get connected(): boolean;
19
15
  connectTransport(config: SQLiteCloudConfig, callback?: ErrorCallback): this;
20
16
  /** Will send a command immediately (no queueing), return the rowset/result or throw an error */
21
17
  transportCommands(commands: string, callback?: ResultsCallback): this;
22
- /** Disconnect from server, release connection. */
18
+ private buffer;
19
+ private startedOn;
20
+ private executingCommands?;
21
+ private processCallback?;
22
+ /** Handles data received in response to an outbound command sent by processCommands */
23
+ private processCommandsData;
24
+ /** Completes a transaction initiated by processCommands */
25
+ private processCommandsFinish;
26
+ /** Disconnect immediately, release connection, no events. */
23
27
  close(): this;
24
28
  }
25
29
  export default SQLiteCloudTlsConnection;
@@ -1,234 +1,218 @@
1
1
  "use strict";
2
2
  /**
3
- * connection-tls.ts - handles low level communication with sqlitecloud server via tls socket and binary protocol
3
+ * connection-tls.ts - connection via tls socket and sqlitecloud protocol
4
4
  */
5
- var __importDefault = (this && this.__importDefault) || function (mod) {
6
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || function (mod) {
22
+ if (mod && mod.__esModule) return mod;
23
+ var result = {};
24
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
25
+ __setModuleDefault(result, mod);
26
+ return result;
7
27
  };
8
28
  Object.defineProperty(exports, "__esModule", { value: true });
9
29
  exports.SQLiteCloudTlsConnection = void 0;
10
30
  const types_1 = require("./types");
11
31
  const connection_1 = require("./connection");
12
- const protocol_1 = require("./protocol");
13
32
  const utilities_1 = require("./utilities");
14
- const protocol_2 = require("./protocol");
15
- const net_1 = __importDefault(require("net"));
16
- const tls_1 = __importDefault(require("tls"));
33
+ const protocol_1 = require("./protocol");
34
+ const tls = __importStar(require("tls"));
17
35
  /**
18
- * Implementation of SQLiteCloudConnection that connects directly to the database via tls socket and raw, binary protocol.
19
- * Connects with plain socket with no encryption is the ?insecure=1 parameter is specified.
20
- * SQLiteCloud low-level connection, will do messaging, handle socket, authentication, etc.
21
- * A connection socket is established when the connection is created and closed when the connection is closed.
22
- * All operations are serialized by waiting for any pending operations to complete. Once a connection is closed,
23
- * it cannot be reopened and you must create a new connection.
36
+ * Implementation of SQLiteCloudConnection that connects to the database using specific Bun APIs
37
+ * that connect to native sockets or tls sockets and communicates via raw, binary protocol.
24
38
  */
25
39
  class SQLiteCloudTlsConnection extends connection_1.SQLiteCloudConnection {
40
+ constructor() {
41
+ super(...arguments);
42
+ // processCommands sets up empty buffers, results callback then send the command to the server via socket.write
43
+ // onData is called when data is received, it will process the data until all data is retrieved for a response
44
+ // when response is complete or there's an error, finish is called to call the results callback set by processCommands...
45
+ // buffer to accumulate incoming data until an whole command is received and can be parsed
46
+ this.buffer = Buffer.alloc(0);
47
+ this.startedOn = new Date();
48
+ }
26
49
  /** True if connection is open */
27
50
  get connected() {
28
51
  return !!this.socket;
29
52
  }
30
53
  /* Opens a connection with the server and sends the initialization commands. Will throw in case of errors. */
54
+ /* eslint-disable @typescript-eslint/no-unused-vars */
31
55
  connectTransport(config, callback) {
32
- // connection established while we were waiting in line?
33
- console.assert(!this.connected, 'Connection already established');
34
- // clear all listeners and call done in the operations queue
35
- const finish = error => {
36
- if (this.socket) {
37
- this.socket.removeAllListeners('data');
38
- this.socket.removeAllListeners('error');
39
- this.socket.removeAllListeners('close');
40
- if (error) {
41
- this.close();
42
- }
43
- }
44
- callback === null || callback === void 0 ? void 0 : callback.call(this, error);
45
- };
56
+ console.assert(!this.connected, 'SQLiteCloudTlsConnection.connect - connection already established');
57
+ if (this.config.verbose) {
58
+ console.debug(`-> connecting ${config === null || config === void 0 ? void 0 : config.host}:${config === null || config === void 0 ? void 0 : config.port}`);
59
+ }
46
60
  this.config = config;
47
61
  const initializationCommands = (0, utilities_1.getInitializationCommands)(config);
48
- if (config.insecure) {
49
- // connect to plain socket, without encryption, only if insecure parameter specified
50
- // this option is mainly for testing purposes and is not available on production nodes
51
- // which would need to connect using tls and proper certificates as per code below
52
- const connectionOptions = {
53
- host: config.host,
54
- port: config.port
55
- };
56
- this.socket = net_1.default.connect(connectionOptions, () => {
57
- console.warn(`TlsConnection.connectTransport - connected to ${config.host}:${config.port} using insecure protocol`);
58
- // send initialization commands
59
- console.assert(this.socket, 'Connection already closed');
60
- this.transportCommands(initializationCommands, error => {
61
- if (error && this.socket) {
62
- this.close();
63
- }
64
- if (callback) {
65
- callback === null || callback === void 0 ? void 0 : callback.call(this, error);
66
- callback = undefined;
67
- }
68
- finish(error);
69
- });
70
- });
71
- }
72
- else {
73
- // connect to tls socket, initialize connection, setup event handlers
74
- this.socket = tls_1.default.connect(this.config.port, this.config.host, this.config.tlsOptions, () => {
75
- const tlsSocket = this.socket;
76
- if (!(tlsSocket === null || tlsSocket === void 0 ? void 0 : tlsSocket.authorized)) {
77
- const anonimizedError = (0, utilities_1.anonimizeError)(tlsSocket.authorizationError);
78
- console.error('Connection was not authorized', anonimizedError);
79
- this.close();
80
- finish(new types_1.SQLiteCloudError('Connection was not authorized', { cause: anonimizedError }));
81
- }
82
- else {
83
- // the connection was closed before it was even opened,
84
- // eg. client closed the connection before the server accepted it
85
- if (this.socket === null) {
86
- finish(new types_1.SQLiteCloudError('Connection was closed before it was done opening'));
87
- return;
88
- }
89
- // send initialization commands
90
- console.assert(this.socket, 'Connection already closed');
91
- this.transportCommands(initializationCommands, error => {
92
- if (error && this.socket) {
93
- this.close();
94
- }
95
- if (callback) {
96
- callback === null || callback === void 0 ? void 0 : callback.call(this, error);
97
- callback = undefined;
98
- }
99
- finish(error);
100
- });
62
+ // connect to plain socket, without encryption, only if insecure parameter specified
63
+ // this option is mainly for testing purposes and is not available on production nodes
64
+ // which would need to connect using tls and proper certificates as per code below
65
+ const connectionOptions = {
66
+ host: config.host,
67
+ port: config.port,
68
+ rejectUnauthorized: false,
69
+ // Server name for the SNI (Server Name Indication) TLS extension.
70
+ // https://r2.nodejs.org/docs/v6.11.4/api/tls.html#tls_class_tls_tlssocket
71
+ servername: config.host
72
+ };
73
+ this.socket = tls.connect(connectionOptions, () => {
74
+ var _a;
75
+ if (this.config.verbose) {
76
+ console.debug(`SQLiteCloudTlsConnection - connected to ${this.config.host}, authorized: ${(_a = this.socket) === null || _a === void 0 ? void 0 : _a.authorized}`);
77
+ }
78
+ this.transportCommands(initializationCommands, error => {
79
+ if (this.config.verbose) {
80
+ console.debug(`SQLiteCloudTlsConnection - initialized connection`);
101
81
  }
82
+ callback === null || callback === void 0 ? void 0 : callback.call(this, error);
102
83
  });
103
- }
104
- this.socket.on('close', () => {
105
- this.socket = null;
106
- finish(new types_1.SQLiteCloudError('Connection was closed'));
107
84
  });
108
- this.socket.once('error', (error) => {
109
- console.error('Connection error', error);
110
- finish(new types_1.SQLiteCloudError('Connection error', { cause: error }));
85
+ this.socket.on('data', data => {
86
+ this.processCommandsData(data);
87
+ });
88
+ this.socket.on('error', error => {
89
+ this.close();
90
+ this.processCommandsFinish(new types_1.SQLiteCloudError('Connection error', { errorCode: 'ERR_CONNECTION_ERROR', cause: error }));
91
+ });
92
+ this.socket.on('end', () => {
93
+ this.close();
94
+ if (this.processCallback)
95
+ this.processCommandsFinish(new types_1.SQLiteCloudError('Server ended the connection', { errorCode: 'ERR_CONNECTION_ENDED' }));
96
+ });
97
+ this.socket.on('close', () => {
98
+ this.close();
99
+ this.processCommandsFinish(new types_1.SQLiteCloudError('Connection closed', { errorCode: 'ERR_CONNECTION_CLOSED' }));
111
100
  });
112
101
  return this;
113
102
  }
114
103
  /** Will send a command immediately (no queueing), return the rowset/result or throw an error */
115
104
  transportCommands(commands, callback) {
116
- var _a, _b, _c;
105
+ var _a, _b, _c, _d, _e;
117
106
  // connection needs to be established?
118
107
  if (!this.socket) {
119
108
  callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Connection not established', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' }));
120
109
  return this;
121
110
  }
111
+ // reset buffer and rowset chunks, define response callback
112
+ this.buffer = Buffer.alloc(0);
113
+ this.startedOn = new Date();
114
+ this.processCallback = callback;
115
+ this.executingCommands = commands;
122
116
  // compose commands following SCPC protocol
123
- commands = (0, protocol_1.formatCommand)(commands);
124
- let buffer = Buffer.alloc(0);
125
- const rowsetChunks = [];
126
- // const startedOn = new Date()
127
- // define what to do if an answer does not arrive within the set timeout
128
- let socketTimeout;
129
- // clear all listeners and call done in the operations queue
130
- const finish = (error, result) => {
131
- clearTimeout(socketTimeout);
132
- if (this.socket) {
133
- this.socket.removeAllListeners('data');
134
- this.socket.removeAllListeners('error');
135
- this.socket.removeAllListeners('close');
136
- }
137
- if (callback) {
138
- callback === null || callback === void 0 ? void 0 : callback.call(this, error, result);
139
- callback = undefined;
117
+ const formattedCommands = (0, protocol_1.formatCommand)(commands);
118
+ if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.verbose) {
119
+ console.debug(`-> ${formattedCommands}`);
120
+ }
121
+ const timeoutMs = (_c = (_b = this.config) === null || _b === void 0 ? void 0 : _b.timeout) !== null && _c !== void 0 ? _c : 0;
122
+ if (timeoutMs > 0) {
123
+ const timeout = setTimeout(() => {
124
+ var _a;
125
+ callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Connection timeout out', { errorCode: 'ERR_CONNECTION_TIMEOUT' }));
126
+ (_a = this.socket) === null || _a === void 0 ? void 0 : _a.destroy();
127
+ this.socket = undefined;
128
+ }, timeoutMs);
129
+ (_d = this.socket) === null || _d === void 0 ? void 0 : _d.write(formattedCommands, 'utf-8', () => {
130
+ clearTimeout(timeout); // Clear the timeout on successful write
131
+ });
132
+ }
133
+ else {
134
+ (_e = this.socket) === null || _e === void 0 ? void 0 : _e.write(formattedCommands, 'utf-8');
135
+ }
136
+ return this;
137
+ }
138
+ /** Handles data received in response to an outbound command sent by processCommands */
139
+ processCommandsData(data) {
140
+ var _a, _b, _c, _d, _e, _f;
141
+ try {
142
+ // append data to buffer as it arrives
143
+ if (data.length && data.length > 0) {
144
+ this.buffer = Buffer.concat([this.buffer, data]);
140
145
  }
141
- };
142
- // define the Promise that waits for the server response
143
- const readData = (data) => {
144
- var _a, _b;
145
- try {
146
- // on first ondata event, dataType is read from data, on subsequent ondata event, is read from buffer that is the concatanations of data received on each ondata event
147
- let dataType = buffer.length === 0 ? data.subarray(0, 1).toString() : buffer.subarray(0, 1).toString('utf8');
148
- buffer = Buffer.concat([buffer, data]);
149
- const commandLength = (0, protocol_1.hasCommandLength)(dataType);
150
- if (commandLength) {
151
- const commandLength = (0, protocol_1.parseCommandLength)(buffer);
152
- const hasReceivedEntireCommand = buffer.length - buffer.indexOf(' ') - 1 >= commandLength ? true : false;
153
- if (hasReceivedEntireCommand) {
154
- if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.verbose) {
155
- let bufferString = buffer.toString('utf8');
156
- if (bufferString.length > 1000) {
157
- bufferString = bufferString.substring(0, 100) + '...' + bufferString.substring(bufferString.length - 40);
158
- }
159
- // const elapsedMs = new Date().getTime() - startedOn.getTime()
160
- // console.debug(`Receive: ${bufferString} - ${elapsedMs}ms`)
161
- }
162
- // need to decompress this buffer before decoding?
163
- if (dataType === protocol_1.CMD_COMPRESSED) {
164
- ;
165
- ({ buffer, dataType } = (0, protocol_1.decompressBuffer)(buffer));
166
- }
167
- if (dataType !== protocol_1.CMD_ROWSET_CHUNK) {
168
- (_b = this.socket) === null || _b === void 0 ? void 0 : _b.off('data', readData);
169
- const { data } = (0, protocol_1.popData)(buffer);
170
- finish(null, data);
171
- }
172
- else {
173
- // check if rowset received the ending chunk
174
- if ((0, protocol_1.bufferEndsWith)(buffer, protocol_1.ROWSET_CHUNKS_END)) {
175
- rowsetChunks.push(buffer);
176
- const parsedData = (0, protocol_2.parseRowsetChunks)(rowsetChunks);
177
- finish === null || finish === void 0 ? void 0 : finish.call(this, null, parsedData);
178
- }
179
- else {
180
- // no ending string? ask server for another chunk
181
- rowsetChunks.push(buffer);
182
- buffer = Buffer.alloc(0);
183
- }
146
+ let dataType = (_a = this.buffer) === null || _a === void 0 ? void 0 : _a.subarray(0, 1).toString();
147
+ if ((0, protocol_1.hasCommandLength)(dataType)) {
148
+ const commandLength = (0, protocol_1.parseCommandLength)(this.buffer);
149
+ const hasReceivedEntireCommand = this.buffer.length - this.buffer.indexOf(' ') - 1 >= commandLength ? true : false;
150
+ if (hasReceivedEntireCommand) {
151
+ if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.verbose) {
152
+ let bufferString = this.buffer.toString('utf8');
153
+ if (bufferString.length > 1000) {
154
+ bufferString = bufferString.substring(0, 100) + '...' + bufferString.substring(bufferString.length - 40);
184
155
  }
156
+ const elapsedMs = new Date().getTime() - this.startedOn.getTime();
157
+ console.debug(`<- ${bufferString} (${elapsedMs}ms)`);
185
158
  }
186
- }
187
- else {
188
- // command with no explicit len so make sure that the final character is a space
189
- const lastChar = buffer.subarray(buffer.length - 1, buffer.length).toString('utf8');
190
- if (lastChar == ' ') {
191
- const { data } = (0, protocol_1.popData)(buffer);
192
- finish(null, data);
159
+ // need to decompress this buffer before decoding?
160
+ if (dataType === protocol_1.CMD_COMPRESSED) {
161
+ ;
162
+ ({ buffer: this.buffer, dataType } = (0, protocol_1.decompressBuffer)(this.buffer));
163
+ }
164
+ if (dataType !== protocol_1.CMD_ROWSET_CHUNK) {
165
+ const { data } = (0, protocol_1.popData)(this.buffer);
166
+ (_c = this.processCommandsFinish) === null || _c === void 0 ? void 0 : _c.call(this, null, data);
167
+ }
168
+ else {
169
+ // check if rowset received the ending chunk in which case it can be unpacked
170
+ if ((0, protocol_1.bufferEndsWith)(this.buffer, protocol_1.ROWSET_CHUNKS_END)) {
171
+ const parsedData = (0, protocol_1.parseRowsetChunks)([this.buffer]);
172
+ (_d = this.processCommandsFinish) === null || _d === void 0 ? void 0 : _d.call(this, null, parsedData);
173
+ }
193
174
  }
194
175
  }
195
176
  }
196
- catch (error) {
197
- console.assert(error instanceof Error);
198
- if (error instanceof Error) {
199
- finish(error);
177
+ else {
178
+ // command with no explicit len so make sure that the final character is a space
179
+ const lastChar = this.buffer.subarray(this.buffer.length - 1, this.buffer.length).toString('utf8');
180
+ if (lastChar == ' ') {
181
+ const { data } = (0, protocol_1.popData)(this.buffer);
182
+ (_e = this.processCommandsFinish) === null || _e === void 0 ? void 0 : _e.call(this, null, data);
200
183
  }
201
184
  }
202
- };
203
- (_a = this.socket) === null || _a === void 0 ? void 0 : _a.once('close', () => {
204
- finish(new types_1.SQLiteCloudError('Connection was closed', { cause: (0, utilities_1.anonimizeCommand)(commands) }));
205
- });
206
- (_b = this.socket) === null || _b === void 0 ? void 0 : _b.write(commands, 'utf8', () => {
207
- var _a, _b;
208
- // @ts-ignore
209
- socketTimeout = setTimeout(() => {
210
- const timeoutError = new types_1.SQLiteCloudError('Request timed out', { cause: (0, utilities_1.anonimizeCommand)(commands) });
211
- // console.debug(`Request timed out, config.timeout is ${this.config?.timeout as number}ms`, timeoutError)
212
- finish(timeoutError);
213
- }, (_a = this.config) === null || _a === void 0 ? void 0 : _a.timeout);
214
- (_b = this.socket) === null || _b === void 0 ? void 0 : _b.on('data', readData);
215
- });
216
- (_c = this.socket) === null || _c === void 0 ? void 0 : _c.once('error', (error) => {
217
- console.error('Socket error', error);
218
- this.close();
219
- finish(new types_1.SQLiteCloudError('Socket error', { cause: (0, utilities_1.anonimizeError)(error) }));
220
- });
221
- return this;
185
+ }
186
+ catch (error) {
187
+ console.assert(error instanceof Error);
188
+ if (error instanceof Error) {
189
+ (_f = this.processCommandsFinish) === null || _f === void 0 ? void 0 : _f.call(this, error);
190
+ }
191
+ }
222
192
  }
223
- /** Disconnect from server, release connection. */
193
+ /** Completes a transaction initiated by processCommands */
194
+ processCommandsFinish(error, result) {
195
+ if (error) {
196
+ if (this.processCallback) {
197
+ console.error('processCommandsFinish - error', error);
198
+ }
199
+ else {
200
+ console.warn('processCommandsFinish - error with no registered callback', error);
201
+ }
202
+ }
203
+ if (this.processCallback) {
204
+ this.processCallback(error, result);
205
+ // this.processCallback = undefined
206
+ }
207
+ }
208
+ /** Disconnect immediately, release connection, no events. */
224
209
  close() {
225
- console.assert(this.socket !== null, 'TlsConnection.close - connection already closed');
226
- this.operations.clear();
227
210
  if (this.socket) {
211
+ this.socket.removeAllListeners();
228
212
  this.socket.destroy();
229
- this.socket = null;
213
+ this.socket = undefined;
230
214
  }
231
- this.socket = undefined;
215
+ this.operations.clear();
232
216
  return this;
233
217
  }
234
218
  }
@@ -27,9 +27,9 @@ class SQLiteCloudWebsocketConnection extends connection_1.SQLiteCloudConnection
27
27
  console.assert(!this.connected, 'Connection already established');
28
28
  if (!this.socket) {
29
29
  this.config = config;
30
- const connectionString = this.config.connectionString;
31
- const gatewayUrl = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.gatewayUrl) || `ws://${this.config.host}:4000`;
32
- this.socket = (0, socket_io_client_1.io)(gatewayUrl, { auth: { token: connectionString } });
30
+ const connectionstring = this.config.connectionstring;
31
+ const gatewayUrl = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.gatewayurl) || `${this.config.host === 'localhost' ? 'ws' : 'wss'}://${this.config.host}:4000`;
32
+ this.socket = (0, socket_io_client_1.io)(gatewayUrl, { auth: { token: connectionstring } });
33
33
  }
34
34
  callback === null || callback === void 0 ? void 0 : callback.call(this, null);
35
35
  }
@@ -8,10 +8,12 @@ import { OperationsQueue } from './queue';
8
8
  * Actual connection management and communication with the server in concrete classes.
9
9
  */
10
10
  export declare abstract class SQLiteCloudConnection {
11
- /** Parse and validate provided connectionString or configuration */
11
+ /** Parse and validate provided connectionstring or configuration */
12
12
  constructor(config: SQLiteCloudConfig | string, callback?: ErrorCallback);
13
13
  /** Configuration passed by client or extracted from connection string */
14
14
  protected config: SQLiteCloudConfig;
15
+ /** Returns the connection's configuration */
16
+ getConfig(): SQLiteCloudConfig;
15
17
  /** Operations are serialized by waiting an any pending promises */
16
18
  protected operations: OperationsQueue;
17
19
  /** Connect will establish a tls or websocket transport to the server based on configuration and environment */
@@ -27,6 +29,16 @@ export declare abstract class SQLiteCloudConnection {
27
29
  verbose(): void;
28
30
  /** Will enquee a command to be executed and callback with the resulting rowset/result/error */
29
31
  sendCommands(commands: string, callback?: ResultsCallback): this;
32
+ /**
33
+ * Sql is a promise based API for executing SQL statements. You can
34
+ * pass a simple string with a SQL statement or a template string
35
+ * using backticks and parameters in ${parameter} format. These parameters
36
+ * will be properly escaped and quoted like when using a prepared statement.
37
+ * @param sql A sql string or a template string in `backticks` format
38
+ * @returns An array of rows in case of selections or an object with
39
+ * metadata in case of insert, update, delete.
40
+ */
41
+ sql(sql: TemplateStringsArray | string, ...values: any[]): Promise<any>;
30
42
  /** Disconnect from server, release transport. */
31
43
  abstract close(): this;
32
44
  }
@@ -2,6 +2,15 @@
2
2
  /**
3
3
  * connection.ts - base abstract class for sqlitecloud server connections
4
4
  */
5
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
6
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7
+ return new (P || (P = Promise))(function (resolve, reject) {
8
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
9
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
10
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
11
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
12
+ });
13
+ };
5
14
  Object.defineProperty(exports, "__esModule", { value: true });
6
15
  exports.SQLiteCloudConnection = void 0;
7
16
  const types_1 = require("./types");
@@ -13,12 +22,12 @@ const utilities_2 = require("./utilities");
13
22
  * Actual connection management and communication with the server in concrete classes.
14
23
  */
15
24
  class SQLiteCloudConnection {
16
- /** Parse and validate provided connectionString or configuration */
25
+ /** Parse and validate provided connectionstring or configuration */
17
26
  constructor(config, callback) {
18
27
  /** Operations are serialized by waiting an any pending promises */
19
28
  this.operations = new queue_1.OperationsQueue();
20
29
  if (typeof config === 'string') {
21
- this.config = (0, utilities_1.validateConfiguration)({ connectionString: config });
30
+ this.config = (0, utilities_1.validateConfiguration)({ connectionstring: config });
22
31
  }
23
32
  else {
24
33
  this.config = (0, utilities_1.validateConfiguration)(config);
@@ -26,6 +35,10 @@ class SQLiteCloudConnection {
26
35
  // connect transport layer to server
27
36
  this.connect(callback);
28
37
  }
38
+ /** Returns the connection's configuration */
39
+ getConfig() {
40
+ return Object.assign({}, this.config);
41
+ }
29
42
  //
30
43
  // internal methods (some are implemented in concrete classes using different transport layers)
31
44
  //
@@ -37,7 +50,9 @@ class SQLiteCloudConnection {
37
50
  console.error(`SQLiteCloudConnection.connect - error connecting ${this.config.host}:${this.config.port} ${error.toString()}`, error);
38
51
  this.close();
39
52
  }
40
- callback === null || callback === void 0 ? void 0 : callback.call(this, error || null);
53
+ if (callback) {
54
+ callback.call(this, error || null);
55
+ }
41
56
  done(error);
42
57
  });
43
58
  });
@@ -47,7 +62,7 @@ class SQLiteCloudConnection {
47
62
  log(message, ...optionalParams) {
48
63
  if (this.config.verbose) {
49
64
  message = (0, utilities_2.anonimizeCommand)(message);
50
- console.log(`${new Date().toISOString()} ${this.config.clientId}: ${message}`, ...optionalParams);
65
+ console.log(`${new Date().toISOString()} ${this.config.clientid}: ${message}`, ...optionalParams);
51
66
  }
52
67
  }
53
68
  /** Enable verbose logging for debug purposes */
@@ -62,12 +77,60 @@ class SQLiteCloudConnection {
62
77
  callback === null || callback === void 0 ? void 0 : callback.call(this, error);
63
78
  done(error);
64
79
  }
65
- this.transportCommands(commands, (error, result) => {
66
- callback === null || callback === void 0 ? void 0 : callback.call(this, error, result);
67
- done(error);
68
- });
80
+ else {
81
+ this.transportCommands(commands, (error, result) => {
82
+ callback === null || callback === void 0 ? void 0 : callback.call(this, error, result);
83
+ done(error);
84
+ });
85
+ }
69
86
  });
70
87
  return this;
71
88
  }
89
+ /**
90
+ * Sql is a promise based API for executing SQL statements. You can
91
+ * pass a simple string with a SQL statement or a template string
92
+ * using backticks and parameters in ${parameter} format. These parameters
93
+ * will be properly escaped and quoted like when using a prepared statement.
94
+ * @param sql A sql string or a template string in `backticks` format
95
+ * @returns An array of rows in case of selections or an object with
96
+ * metadata in case of insert, update, delete.
97
+ */
98
+ sql(sql, ...values) {
99
+ return __awaiter(this, void 0, void 0, function* () {
100
+ let preparedSql = '';
101
+ // sql is a TemplateStringsArray, the 'raw' property is specific to TemplateStringsArray
102
+ if (Array.isArray(sql) && 'raw' in sql) {
103
+ sql.forEach((string, i) => {
104
+ preparedSql += string + (i < values.length ? '?' : '');
105
+ });
106
+ preparedSql = (0, utilities_1.prepareSql)(preparedSql, ...values);
107
+ }
108
+ else {
109
+ if (typeof sql === 'string') {
110
+ if ((values === null || values === void 0 ? void 0 : values.length) > 0) {
111
+ preparedSql = (0, utilities_1.prepareSql)(sql, ...values);
112
+ }
113
+ else {
114
+ preparedSql = sql;
115
+ }
116
+ }
117
+ else {
118
+ throw new Error('Invalid sql');
119
+ }
120
+ }
121
+ return new Promise((resolve, reject) => {
122
+ this.sendCommands(preparedSql, (error, results) => {
123
+ if (error) {
124
+ reject(error);
125
+ }
126
+ else {
127
+ // metadata for operations like insert, update, delete?
128
+ const context = (0, utilities_2.getUpdateResults)(results);
129
+ resolve(context ? context : results);
130
+ }
131
+ });
132
+ });
133
+ });
134
+ }
72
135
  }
73
136
  exports.SQLiteCloudConnection = SQLiteCloudConnection;