@sqlitecloud/drivers 0.0.39 → 0.0.50

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.
@@ -0,0 +1,291 @@
1
+ "use strict";
2
+ //
3
+ // protocol.ts - low level protocol handling for SQLiteCloud transport
4
+ //
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.formatCommand = exports.popData = exports.parseRowsetChunks = exports.bufferEndsWith = exports.bufferStartsWith = exports.parseRowsetHeader = exports.parseArray = exports.parseError = exports.decompressBuffer = exports.parseCommandLength = exports.hasCommandLength = exports.ROWSET_CHUNKS_END = exports.CMD_ARRAY = exports.CMD_COMMAND = exports.CMD_COMPRESSED = exports.CMD_BLOB = exports.CMD_NULL = exports.CMD_JSON = exports.CMD_ROWSET_CHUNK = exports.CMD_ROWSET = exports.CMD_FLOAT = exports.CMD_INT = exports.CMD_ERROR = exports.CMD_ZEROSTRING = exports.CMD_STRING = void 0;
7
+ const types_1 = require("./types");
8
+ const rowset_1 = require("./rowset");
9
+ const lz4 = require('lz4js');
10
+ // The server communicates with clients via commands defined in
11
+ // SQLiteCloud Server Protocol (SCSP), see more at:
12
+ // https://github.com/sqlitecloud/sdk/blob/master/PROTOCOL.md
13
+ exports.CMD_STRING = '+';
14
+ exports.CMD_ZEROSTRING = '!';
15
+ exports.CMD_ERROR = '-';
16
+ exports.CMD_INT = ':';
17
+ exports.CMD_FLOAT = ',';
18
+ exports.CMD_ROWSET = '*';
19
+ exports.CMD_ROWSET_CHUNK = '/';
20
+ exports.CMD_JSON = '#';
21
+ exports.CMD_NULL = '_';
22
+ exports.CMD_BLOB = '$';
23
+ exports.CMD_COMPRESSED = '%';
24
+ exports.CMD_COMMAND = '^';
25
+ exports.CMD_ARRAY = '=';
26
+ // const CMD_RAWJSON = '{'
27
+ // const CMD_PUBSUB = '|'
28
+ // const CMD_RECONNECT = '@'
29
+ // To mark the end of the Rowset, the special string /LEN 0 0 0 is sent (LEN is always 6 in this case)
30
+ // https://github.com/sqlitecloud/sdk/blob/master/PROTOCOL.md#scsp-rowset-chunk
31
+ exports.ROWSET_CHUNKS_END = '/6 0 0 0 ';
32
+ //
33
+ // utility functions
34
+ //
35
+ /** Analyze first character to check if corresponding data type has LEN */
36
+ function hasCommandLength(firstCharacter) {
37
+ return firstCharacter == exports.CMD_INT || firstCharacter == exports.CMD_FLOAT || firstCharacter == exports.CMD_NULL ? false : true;
38
+ }
39
+ exports.hasCommandLength = hasCommandLength;
40
+ /** Analyze a command with explict LEN and extract it */
41
+ function parseCommandLength(data) {
42
+ return parseInt(data.subarray(1, data.indexOf(' ')).toString('utf8'));
43
+ }
44
+ exports.parseCommandLength = parseCommandLength;
45
+ /** Receive a compressed buffer, decompress with lz4, return buffer and datatype */
46
+ function decompressBuffer(buffer) {
47
+ const spaceIndex = buffer.indexOf(' ');
48
+ buffer = buffer.subarray(spaceIndex + 1);
49
+ // extract compressed size
50
+ const compressedSize = parseInt(buffer.subarray(0, buffer.indexOf(' ') + 1).toString('utf8'));
51
+ buffer = buffer.subarray(buffer.indexOf(' ') + 1);
52
+ // extract decompressed size
53
+ const decompressedSize = parseInt(buffer.subarray(0, buffer.indexOf(' ') + 1).toString('utf8'));
54
+ buffer = buffer.subarray(buffer.indexOf(' ') + 1);
55
+ // extract compressed dataType
56
+ const dataType = buffer.subarray(0, 1).toString('utf8');
57
+ const decompressedBuffer = Buffer.alloc(decompressedSize);
58
+ const compressedBuffer = buffer.subarray(buffer.length - compressedSize);
59
+ // lz4js library is javascript and doesn't have types so we silence the type check
60
+ // eslint-disable-next-line
61
+ const decompressionResult = lz4.decompressBlock(compressedBuffer, decompressedBuffer, 0, compressedSize, 0);
62
+ buffer = Buffer.concat([buffer.subarray(0, buffer.length - compressedSize), decompressedBuffer]);
63
+ if (decompressionResult <= 0 || decompressionResult !== decompressedSize) {
64
+ throw new Error(`lz4 decompression error at offset ${decompressionResult}`);
65
+ }
66
+ return { buffer, dataType };
67
+ }
68
+ exports.decompressBuffer = decompressBuffer;
69
+ /** Parse error message or extended error message */
70
+ function parseError(buffer, spaceIndex) {
71
+ const errorBuffer = buffer.subarray(spaceIndex + 1);
72
+ const errorString = errorBuffer.toString('utf8');
73
+ const parts = errorString.split(' ');
74
+ let errorCodeStr = parts.shift() || '0'; // Default errorCode is '0' if not present
75
+ let extErrCodeStr = '0'; // Default extended error code
76
+ let offsetCodeStr = '-1'; // Default offset code
77
+ // Split the errorCode by ':' to check for extended error codes
78
+ const errorCodeParts = errorCodeStr.split(':');
79
+ errorCodeStr = errorCodeParts[0];
80
+ if (errorCodeParts.length > 1) {
81
+ extErrCodeStr = errorCodeParts[1];
82
+ if (errorCodeParts.length > 2) {
83
+ offsetCodeStr = errorCodeParts[2];
84
+ }
85
+ }
86
+ // Rest of the error string is the error message
87
+ const errorMessage = parts.join(' ');
88
+ // Parse error codes to integers safely, defaulting to 0 if NaN
89
+ const errorCode = parseInt(errorCodeStr);
90
+ const extErrCode = parseInt(extErrCodeStr);
91
+ const offsetCode = parseInt(offsetCodeStr);
92
+ // create an Error object and add the custom properties
93
+ throw new types_1.SQLiteCloudError(errorMessage, {
94
+ errorCode: errorCode.toString(),
95
+ externalErrorCode: extErrCode.toString(),
96
+ offsetCode
97
+ });
98
+ }
99
+ exports.parseError = parseError;
100
+ /** Parse an array of items (each of which will be parsed by type separately) */
101
+ function parseArray(buffer, spaceIndex) {
102
+ const parsedData = [];
103
+ const array = buffer.subarray(spaceIndex + 1, buffer.length);
104
+ const numberOfItems = parseInt(array.subarray(0, spaceIndex - 2).toString('utf8'));
105
+ let arrayItems = array.subarray(array.indexOf(' ') + 1, array.length);
106
+ for (let i = 0; i < numberOfItems; i++) {
107
+ const { data, fwdBuffer: buffer } = popData(arrayItems);
108
+ parsedData.push(data);
109
+ arrayItems = buffer;
110
+ }
111
+ return parsedData;
112
+ }
113
+ exports.parseArray = parseArray;
114
+ /** Parse header in a rowset or chunk of a chunked rowset */
115
+ function parseRowsetHeader(buffer) {
116
+ const index = parseInt(buffer.subarray(0, buffer.indexOf(':') + 1).toString());
117
+ buffer = buffer.subarray(buffer.indexOf(':') + 1);
118
+ // extract rowset header
119
+ const { data, fwdBuffer } = popIntegers(buffer, 3);
120
+ return {
121
+ index,
122
+ metadata: {
123
+ version: data[0],
124
+ numberOfRows: data[1],
125
+ numberOfColumns: data[2],
126
+ columns: []
127
+ },
128
+ fwdBuffer
129
+ };
130
+ }
131
+ exports.parseRowsetHeader = parseRowsetHeader;
132
+ /** Extract column names and, optionally, more metadata out of a rowset's header */
133
+ function parseRowsetColumnsMetadata(buffer, metadata) {
134
+ function popForward() {
135
+ const { data, fwdBuffer: fwdBuffer } = popData(buffer); // buffer in parent scope
136
+ buffer = fwdBuffer;
137
+ return data;
138
+ }
139
+ for (let i = 0; i < metadata.numberOfColumns; i++) {
140
+ metadata.columns.push({ name: popForward() });
141
+ }
142
+ // extract additional metadata if rowset has version 2
143
+ if (metadata.version == 2) {
144
+ for (let i = 0; i < metadata.numberOfColumns; i++)
145
+ metadata.columns[i].type = popForward();
146
+ for (let i = 0; i < metadata.numberOfColumns; i++)
147
+ metadata.columns[i].database = popForward();
148
+ for (let i = 0; i < metadata.numberOfColumns; i++)
149
+ metadata.columns[i].table = popForward();
150
+ for (let i = 0; i < metadata.numberOfColumns; i++)
151
+ metadata.columns[i].column = popForward(); // original column name
152
+ for (let i = 0; i < metadata.numberOfColumns; i++)
153
+ metadata.columns[i].notNull = popForward();
154
+ for (let i = 0; i < metadata.numberOfColumns; i++)
155
+ metadata.columns[i].primaryKey = popForward();
156
+ for (let i = 0; i < metadata.numberOfColumns; i++)
157
+ metadata.columns[i].autoIncrement = popForward();
158
+ }
159
+ return buffer;
160
+ }
161
+ /** Parse a regular rowset (no chunks) */
162
+ function parseRowset(buffer, spaceIndex) {
163
+ buffer = buffer.subarray(spaceIndex + 1, buffer.length);
164
+ const { metadata, fwdBuffer } = parseRowsetHeader(buffer);
165
+ buffer = parseRowsetColumnsMetadata(fwdBuffer, metadata);
166
+ // decode each rowset item
167
+ const data = [];
168
+ for (let j = 0; j < metadata.numberOfRows * metadata.numberOfColumns; j++) {
169
+ const { data: rowData, fwdBuffer } = popData(buffer);
170
+ data.push(rowData);
171
+ buffer = fwdBuffer;
172
+ }
173
+ console.assert(data && data.length === metadata.numberOfRows * metadata.numberOfColumns, 'SQLiteCloudConnection.parseRowset - invalid rowset data');
174
+ return new rowset_1.SQLiteCloudRowset(metadata, data);
175
+ }
176
+ function bufferStartsWith(buffer, prefix) {
177
+ return buffer.length >= prefix.length && buffer.subarray(0, prefix.length).toString('utf8') === prefix;
178
+ }
179
+ exports.bufferStartsWith = bufferStartsWith;
180
+ function bufferEndsWith(buffer, suffix) {
181
+ return buffer.length >= suffix.length && buffer.subarray(buffer.length - suffix.length, buffer.length).toString('utf8') === suffix;
182
+ }
183
+ exports.bufferEndsWith = bufferEndsWith;
184
+ /**
185
+ * Parse a chunk of a chunked rowset command, eg:
186
+ * *LEN 0:VERS NROWS NCOLS DATA
187
+ * @see https://github.com/sqlitecloud/sdk/blob/master/PROTOCOL.md#scsp-rowset-chunk
188
+ */
189
+ function parseRowsetChunks(buffers) {
190
+ let buffer = Buffer.concat(buffers);
191
+ if (!bufferStartsWith(buffer, exports.CMD_ROWSET_CHUNK) || !bufferEndsWith(buffer, exports.ROWSET_CHUNKS_END)) {
192
+ throw new Error('SQLiteCloudConnection.parseRowsetChunks - invalid chunks buffer');
193
+ }
194
+ let metadata = { version: 1, numberOfColumns: 0, numberOfRows: 0, columns: [] };
195
+ const data = [];
196
+ // validate and skip data type
197
+ const dataType = buffer.subarray(0, 1).toString();
198
+ console.assert(dataType === exports.CMD_ROWSET_CHUNK);
199
+ buffer = buffer.subarray(buffer.indexOf(' ') + 1);
200
+ while (buffer.length > 0 && !bufferStartsWith(buffer, exports.ROWSET_CHUNKS_END)) {
201
+ // chunk header, eg: 0:VERS NROWS NCOLS
202
+ const { index: chunkIndex, metadata: chunkMetadata, fwdBuffer } = parseRowsetHeader(buffer);
203
+ buffer = fwdBuffer;
204
+ // first chunk? extract columns metadata
205
+ if (chunkIndex === 1) {
206
+ metadata = chunkMetadata;
207
+ buffer = parseRowsetColumnsMetadata(buffer, metadata);
208
+ }
209
+ else {
210
+ metadata.numberOfRows += chunkMetadata.numberOfRows;
211
+ }
212
+ // extract single rowset row
213
+ for (let k = 0; k < chunkMetadata.numberOfRows * metadata.numberOfColumns; k++) {
214
+ const { data: itemData, fwdBuffer } = popData(buffer);
215
+ data.push(itemData);
216
+ buffer = fwdBuffer;
217
+ }
218
+ }
219
+ console.assert(data && data.length === metadata.numberOfRows * metadata.numberOfColumns, 'parseRowsetChunks - invalid rowset data');
220
+ const rowset = new rowset_1.SQLiteCloudRowset(metadata, data);
221
+ // console.debug(`parseRowsetChunks - ${rowset.numberOfRows} rows, ${rowset.numberOfColumns} columns`)
222
+ return rowset;
223
+ }
224
+ exports.parseRowsetChunks = parseRowsetChunks;
225
+ /** Pop one or more space separated integers from beginning of buffer, move buffer forward */
226
+ function popIntegers(buffer, numberOfIntegers = 1) {
227
+ const data = [];
228
+ for (let i = 0; i < numberOfIntegers; i++) {
229
+ const spaceIndex = buffer.indexOf(' ');
230
+ data[i] = parseInt(buffer.subarray(0, spaceIndex).toString());
231
+ buffer = buffer.subarray(spaceIndex + 1);
232
+ }
233
+ return { data, fwdBuffer: buffer };
234
+ }
235
+ /** Parse command, extract its data, return the data and the buffer moved to the first byte after the command */
236
+ function popData(buffer) {
237
+ function popResults(data) {
238
+ const fwdBuffer = buffer.subarray(commandEnd);
239
+ return { data, fwdBuffer };
240
+ }
241
+ // first character is the data type
242
+ console.assert(buffer && buffer instanceof Buffer);
243
+ const dataType = buffer.subarray(0, 1).toString('utf8');
244
+ console.assert(dataType !== exports.CMD_COMPRESSED, "Compressed data shouldn't be decompressed before parsing");
245
+ console.assert(dataType !== exports.CMD_ROWSET_CHUNK, 'Chunked data should be parsed by parseRowsetChunks');
246
+ let spaceIndex = buffer.indexOf(' ');
247
+ if (spaceIndex === -1) {
248
+ spaceIndex = buffer.length - 1;
249
+ }
250
+ let commandEnd = -1;
251
+ if (dataType === exports.CMD_INT || dataType === exports.CMD_FLOAT || dataType === exports.CMD_NULL) {
252
+ commandEnd = spaceIndex + 1;
253
+ }
254
+ else {
255
+ const commandLength = parseInt(buffer.subarray(1, spaceIndex).toString());
256
+ commandEnd = spaceIndex + 1 + commandLength;
257
+ }
258
+ switch (dataType) {
259
+ case exports.CMD_INT:
260
+ return popResults(parseInt(buffer.subarray(1, spaceIndex).toString()));
261
+ case exports.CMD_FLOAT:
262
+ return popResults(parseFloat(buffer.subarray(1, spaceIndex).toString()));
263
+ case exports.CMD_NULL:
264
+ return popResults(null);
265
+ case exports.CMD_STRING:
266
+ return popResults(buffer.subarray(spaceIndex + 1, commandEnd).toString('utf8'));
267
+ case exports.CMD_ZEROSTRING:
268
+ return popResults(buffer.subarray(spaceIndex + 1, commandEnd - 1).toString('utf8'));
269
+ case exports.CMD_COMMAND:
270
+ return popResults(buffer.subarray(spaceIndex + 1, commandEnd).toString('utf8'));
271
+ case exports.CMD_JSON:
272
+ return popResults(JSON.parse(buffer.subarray(spaceIndex + 1, commandEnd).toString('utf8')));
273
+ case exports.CMD_BLOB:
274
+ return popResults(buffer.subarray(spaceIndex + 1, commandEnd));
275
+ case exports.CMD_ARRAY:
276
+ return popResults(parseArray(buffer, spaceIndex));
277
+ case exports.CMD_ROWSET:
278
+ return popResults(parseRowset(buffer, spaceIndex));
279
+ case exports.CMD_ERROR:
280
+ parseError(buffer, spaceIndex); // throws custom error
281
+ break;
282
+ }
283
+ throw new TypeError(`Data type: ${dataType} is not defined in SCSP`);
284
+ }
285
+ exports.popData = popData;
286
+ /** Format a command to be sent via SCSP protocol */
287
+ function formatCommand(command) {
288
+ const commandLength = Buffer.byteLength(command, 'utf-8');
289
+ return `+${commandLength} ${command}`;
290
+ }
291
+ exports.formatCommand = formatCommand;
@@ -0,0 +1,12 @@
1
+ export type OperationCallback = (error: Error | null) => void;
2
+ export type Operation = (done: OperationCallback) => void;
3
+ export declare class OperationsQueue {
4
+ private queue;
5
+ private isProcessing;
6
+ /** Add operations to the queue, process immediately if possible, else wait for previous operations to complete */
7
+ enqueue(operation: Operation): void;
8
+ /** Clear the queue */
9
+ clear(): void;
10
+ /** Process the next operation in the queue */
11
+ private processNext;
12
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ //
3
+ // queue.ts - simple task queue used to linearize async operations
4
+ //
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.OperationsQueue = void 0;
7
+ class OperationsQueue {
8
+ constructor() {
9
+ this.queue = [];
10
+ this.isProcessing = false;
11
+ }
12
+ /** Add operations to the queue, process immediately if possible, else wait for previous operations to complete */
13
+ enqueue(operation) {
14
+ this.queue.push(operation);
15
+ if (!this.isProcessing) {
16
+ this.processNext();
17
+ }
18
+ }
19
+ /** Clear the queue */
20
+ clear() {
21
+ this.queue = [];
22
+ this.isProcessing = false;
23
+ }
24
+ /** Process the next operation in the queue */
25
+ processNext() {
26
+ if (this.queue.length === 0) {
27
+ this.isProcessing = false;
28
+ return;
29
+ }
30
+ this.isProcessing = true;
31
+ const operation = this.queue.shift();
32
+ operation === null || operation === void 0 ? void 0 : operation(() => {
33
+ // could receive (error) => { ...
34
+ // if (error) {
35
+ // console.warn('OperationQueue.processNext - error in operation', error)
36
+ // }
37
+ // process the next operation in the queue
38
+ this.processNext();
39
+ });
40
+ }
41
+ }
42
+ exports.OperationsQueue = OperationsQueue;
@@ -4,6 +4,10 @@
4
4
  /// <reference types="node" />
5
5
  /// <reference types="node" />
6
6
  import tls from 'tls';
7
+ /** Default timeout value for queries */
8
+ export declare const DEFAULT_TIMEOUT: number;
9
+ /** Default tls connection port */
10
+ export declare const DEFAULT_PORT = 9960;
7
11
  /** Configuration for SQLite cloud connection */
8
12
  export interface SQLiteCloudConfig {
9
13
  /** Connection string in the form of sqlitecloud://user:password@host:port/database?options */
@@ -18,6 +22,8 @@ export interface SQLiteCloudConfig {
18
22
  host?: string;
19
23
  /** Port number for tls socket */
20
24
  port?: number;
25
+ /** Connect using plain TCP port, without TLS encryption, NOT RECOMMENDED, TEST ONLY */
26
+ insecure?: boolean;
21
27
  /** Optional query timeout passed directly to TLS socket */
22
28
  timeout?: number;
23
29
  /** Name of database to open */
@@ -3,7 +3,11 @@
3
3
  * types.ts - shared types and interfaces
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SQLiteCloudArrayType = exports.SQLiteCloudError = void 0;
6
+ exports.SQLiteCloudArrayType = exports.SQLiteCloudError = exports.DEFAULT_PORT = exports.DEFAULT_TIMEOUT = void 0;
7
+ /** Default timeout value for queries */
8
+ exports.DEFAULT_TIMEOUT = 300 * 1000;
9
+ /** Default tls connection port */
10
+ exports.DEFAULT_PORT = 9960;
7
11
  /** Custom error reported by SQLiteCloud drivers */
8
12
  class SQLiteCloudError extends Error {
9
13
  constructor(message, args) {
@@ -1,6 +1,12 @@
1
1
  import { SQLiteCloudConfig, SQLiteCloudDataTypes } from './types';
2
2
  export declare const isBrowser: boolean;
3
3
  export declare const isNode: boolean;
4
+ /** Messages going to the server are sometimes logged when error conditions occour and need to be stripped of user credentials */
5
+ export declare function anonimizeCommand(message: string): string;
6
+ /** Strip message code in error of user credentials */
7
+ export declare function anonimizeError(error: Error): Error;
8
+ /** Initialization commands sent to database when connection is established */
9
+ export declare function getInitializationCommands(config: SQLiteCloudConfig): string;
4
10
  /** Takes a generic value and escapes it so it can replace ? as a binding in a prepared SQL statement */
5
11
  export declare function escapeSqlParameter(param: SQLiteCloudDataTypes): string;
6
12
  /** Take a sql statement and replaces ? or $named parameters that are properly serialized and escaped. */
@@ -17,6 +23,8 @@ export declare function popCallback<T extends ErrorCallback = ErrorCallback>(arg
17
23
  callback?: T | undefined;
18
24
  complete?: ErrorCallback;
19
25
  };
26
+ /** Validate configuration, apply defaults, throw if something is missing or misconfigured */
27
+ export declare function validateConfiguration(config: SQLiteCloudConfig): SQLiteCloudConfig;
20
28
  /** Parse connectionString like sqlitecloud://username:password@host:port/database?option1=xxx&option2=xxx into its components */
21
29
  export declare function parseConnectionString(connectionString: string): SQLiteCloudConfig;
22
30
  /** Returns true if value is 1 or true */
@@ -3,7 +3,7 @@
3
3
  // utilities.ts - utility methods to manipulate SQL statements
4
4
  //
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.parseBoolean = exports.parseConnectionString = exports.popCallback = exports.prepareSql = exports.escapeSqlParameter = exports.isNode = exports.isBrowser = void 0;
6
+ exports.parseBoolean = exports.parseConnectionString = exports.validateConfiguration = exports.popCallback = exports.prepareSql = exports.escapeSqlParameter = exports.getInitializationCommands = exports.anonimizeError = exports.anonimizeCommand = exports.isNode = exports.isBrowser = void 0;
7
7
  const types_1 = require("./types");
8
8
  //
9
9
  // determining running environment, thanks to browser-or-node
@@ -14,6 +14,54 @@ exports.isNode = typeof process !== 'undefined' && process.versions != null && p
14
14
  //
15
15
  // utility methods
16
16
  //
17
+ /** Messages going to the server are sometimes logged when error conditions occour and need to be stripped of user credentials */
18
+ function anonimizeCommand(message) {
19
+ // hide password in AUTH command if needed
20
+ message = message.replace(/USER \S+/, 'USER ******');
21
+ message = message.replace(/PASSWORD \S+?(?=;)/, 'PASSWORD ******');
22
+ message = message.replace(/HASH \S+?(?=;)/, 'HASH ******');
23
+ return message;
24
+ }
25
+ exports.anonimizeCommand = anonimizeCommand;
26
+ /** Strip message code in error of user credentials */
27
+ function anonimizeError(error) {
28
+ if (error === null || error === void 0 ? void 0 : error.message) {
29
+ error.message = anonimizeCommand(error.message);
30
+ }
31
+ return error;
32
+ }
33
+ exports.anonimizeError = anonimizeError;
34
+ /** Initialization commands sent to database when connection is established */
35
+ function getInitializationCommands(config) {
36
+ // first user authentication, then all other commands
37
+ let commands = `AUTH USER ${config.username || ''} ${config.passwordHashed ? 'HASH' : 'PASSWORD'} ${config.password || ''}; `;
38
+ if (config.database) {
39
+ if (config.createDatabase && !config.dbMemory) {
40
+ commands += `CREATE DATABASE ${config.database} IF NOT EXISTS; `;
41
+ }
42
+ commands += `USE DATABASE ${config.database}; `;
43
+ }
44
+ if (config.compression) {
45
+ commands += 'SET CLIENT KEY COMPRESSION TO 1; ';
46
+ }
47
+ if (config.nonlinearizable) {
48
+ commands += 'SET CLIENT KEY NONLINEARIZABLE TO 1; ';
49
+ }
50
+ if (config.noBlob) {
51
+ commands += 'SET CLIENT KEY NOBLOB TO 1; ';
52
+ }
53
+ if (config.maxData) {
54
+ commands += `SET CLIENT KEY MAXDATA TO ${config.maxData}; `;
55
+ }
56
+ if (config.maxRows) {
57
+ commands += `SET CLIENT KEY MAXROWS TO ${config.maxRows}; `;
58
+ }
59
+ if (config.maxRowset) {
60
+ commands += `SET CLIENT KEY MAXROWSET TO ${config.maxRowset}; `;
61
+ }
62
+ return commands;
63
+ }
64
+ exports.getInitializationCommands = getInitializationCommands;
17
65
  /** Takes a generic value and escapes it so it can replace ? as a binding in a prepared SQL statement */
18
66
  function escapeSqlParameter(param) {
19
67
  if (param === null || param === undefined) {
@@ -100,6 +148,38 @@ function popCallback(args) {
100
148
  return { args: remaining };
101
149
  }
102
150
  exports.popCallback = popCallback;
151
+ //
152
+ // configuration validation
153
+ //
154
+ /** Validate configuration, apply defaults, throw if something is missing or misconfigured */
155
+ function validateConfiguration(config) {
156
+ console.assert(config, 'SQLiteCloudConnection.validateConfiguration - missing config');
157
+ if (config.connectionString) {
158
+ config = Object.assign(Object.assign(Object.assign({}, config), parseConnectionString(config.connectionString)), { connectionString: config.connectionString // keep original connection string
159
+ });
160
+ }
161
+ // apply defaults where needed
162
+ config.port || (config.port = types_1.DEFAULT_PORT);
163
+ config.timeout = config.timeout && config.timeout > 0 ? config.timeout : types_1.DEFAULT_TIMEOUT;
164
+ config.clientId || (config.clientId = 'SQLiteCloud');
165
+ config.verbose = parseBoolean(config.verbose);
166
+ config.noBlob = parseBoolean(config.noBlob);
167
+ config.compression = parseBoolean(config.compression);
168
+ config.createDatabase = parseBoolean(config.createDatabase);
169
+ config.nonlinearizable = parseBoolean(config.nonlinearizable);
170
+ config.insecure = parseBoolean(config.insecure);
171
+ if (!config.username || !config.password || !config.host) {
172
+ console.error('SQLiteCloudConnection.validateConfiguration - missing arguments', config);
173
+ throw new types_1.SQLiteCloudError('The user, password and host arguments must be specified.', { errorCode: 'ERR_MISSING_ARGS' });
174
+ }
175
+ if (!config.connectionString) {
176
+ // build connection string from configuration, values are already validated
177
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
178
+ config.connectionString = `sqlitecloud://${config.username}:${config.password}@${config.host}:${config.port}/${config.database}`;
179
+ }
180
+ return config;
181
+ }
182
+ exports.validateConfiguration = validateConfiguration;
103
183
  /** Parse connectionString like sqlitecloud://username:password@host:port/database?option1=xxx&option2=xxx into its components */
104
184
  function parseConnectionString(connectionString) {
105
185
  try {
package/lib/index.d.ts CHANGED
@@ -1,8 +1,6 @@
1
- export { Database } from './database';
2
- export { Statement } from './statement';
3
- export { SQLiteCloudConfig, SQLCloudRowsetMetadata, SQLiteCloudError, ErrorCallback } from './types';
4
- export { SQLiteCloudRowset, SQLiteCloudRow } from './rowset';
5
- export { SQLiteCloudConnection } from './connection';
6
- export { escapeSqlParameter, prepareSql, parseConnectionString } from './utilities';
7
- export { WebSocketTransport } from './transport-ws';
8
- export { TlsSocketTransport } from './transport-tls';
1
+ export { Database } from './drivers/database';
2
+ export { Statement } from './drivers/statement';
3
+ export { SQLiteCloudConnection } from './drivers/connection';
4
+ export { type SQLiteCloudConfig, type SQLCloudRowsetMetadata, SQLiteCloudError, type ErrorCallback } from './drivers/types';
5
+ export { SQLiteCloudRowset, SQLiteCloudRow } from './drivers/rowset';
6
+ export { escapeSqlParameter, prepareSql, parseConnectionString, validateConfiguration } from './drivers/utilities';
package/lib/index.js CHANGED
@@ -1,25 +1,26 @@
1
1
  "use strict";
2
2
  //
3
- // index.ts - re-export public APIs
3
+ // index.ts - export drivers classes, utilities, types
4
4
  //
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.TlsSocketTransport = exports.WebSocketTransport = exports.parseConnectionString = exports.prepareSql = exports.escapeSqlParameter = exports.SQLiteCloudConnection = exports.SQLiteCloudRow = exports.SQLiteCloudRowset = exports.SQLiteCloudError = exports.Statement = exports.Database = void 0;
7
- var database_1 = require("./database");
6
+ exports.validateConfiguration = exports.parseConnectionString = exports.prepareSql = exports.escapeSqlParameter = exports.SQLiteCloudRow = exports.SQLiteCloudRowset = exports.SQLiteCloudError = exports.SQLiteCloudConnection = exports.Statement = exports.Database = void 0;
7
+ // include ONLY packages used by drivers
8
+ // do NOT include anything related to gateway or bun or express
9
+ // connection-tls does not want/need to load on browser and is loaded dynamically by Database
10
+ // connection-ws does not want/need to load on node and is loaded dynamically by Database
11
+ var database_1 = require("./drivers/database");
8
12
  Object.defineProperty(exports, "Database", { enumerable: true, get: function () { return database_1.Database; } });
9
- var statement_1 = require("./statement");
13
+ var statement_1 = require("./drivers/statement");
10
14
  Object.defineProperty(exports, "Statement", { enumerable: true, get: function () { return statement_1.Statement; } });
11
- var types_1 = require("./types");
15
+ var connection_1 = require("./drivers/connection");
16
+ Object.defineProperty(exports, "SQLiteCloudConnection", { enumerable: true, get: function () { return connection_1.SQLiteCloudConnection; } });
17
+ var types_1 = require("./drivers/types");
12
18
  Object.defineProperty(exports, "SQLiteCloudError", { enumerable: true, get: function () { return types_1.SQLiteCloudError; } });
13
- var rowset_1 = require("./rowset");
19
+ var rowset_1 = require("./drivers/rowset");
14
20
  Object.defineProperty(exports, "SQLiteCloudRowset", { enumerable: true, get: function () { return rowset_1.SQLiteCloudRowset; } });
15
21
  Object.defineProperty(exports, "SQLiteCloudRow", { enumerable: true, get: function () { return rowset_1.SQLiteCloudRow; } });
16
- var connection_1 = require("./connection");
17
- Object.defineProperty(exports, "SQLiteCloudConnection", { enumerable: true, get: function () { return connection_1.SQLiteCloudConnection; } });
18
- var utilities_1 = require("./utilities");
22
+ var utilities_1 = require("./drivers/utilities");
19
23
  Object.defineProperty(exports, "escapeSqlParameter", { enumerable: true, get: function () { return utilities_1.escapeSqlParameter; } });
20
24
  Object.defineProperty(exports, "prepareSql", { enumerable: true, get: function () { return utilities_1.prepareSql; } });
21
25
  Object.defineProperty(exports, "parseConnectionString", { enumerable: true, get: function () { return utilities_1.parseConnectionString; } });
22
- var transport_ws_1 = require("./transport-ws");
23
- Object.defineProperty(exports, "WebSocketTransport", { enumerable: true, get: function () { return transport_ws_1.WebSocketTransport; } });
24
- var transport_tls_1 = require("./transport-tls");
25
- Object.defineProperty(exports, "TlsSocketTransport", { enumerable: true, get: function () { return transport_tls_1.TlsSocketTransport; } });
26
+ Object.defineProperty(exports, "validateConfiguration", { enumerable: true, get: function () { return utilities_1.validateConfiguration; } });