@sqlitecloud/drivers 1.0.422 → 1.0.507

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.
@@ -156,12 +156,12 @@ class SQLiteCloudTlsConnection extends connection_1.SQLiteCloudConnection {
156
156
  (_a = this.socket) === null || _a === void 0 ? void 0 : _a.destroy();
157
157
  this.socket = undefined;
158
158
  }, timeoutMs);
159
- (_d = this.socket) === null || _d === void 0 ? void 0 : _d.write(formattedCommands, 'utf-8', () => {
159
+ (_d = this.socket) === null || _d === void 0 ? void 0 : _d.write(formattedCommands, () => {
160
160
  clearTimeout(timeout); // Clear the timeout on successful write
161
161
  });
162
162
  }
163
163
  else {
164
- (_e = this.socket) === null || _e === void 0 ? void 0 : _e.write(formattedCommands, 'utf-8');
164
+ (_e = this.socket) === null || _e === void 0 ? void 0 : _e.write(formattedCommands);
165
165
  }
166
166
  return this;
167
167
  }
@@ -114,6 +114,7 @@ class Database extends eventemitter3_1.default {
114
114
  })
115
115
  .catch(error => {
116
116
  this.handleError(error, callback);
117
+ this.close();
117
118
  done(error);
118
119
  });
119
120
  });
@@ -134,6 +135,7 @@ class Database extends eventemitter3_1.default {
134
135
  })
135
136
  .catch(error => {
136
137
  this.handleError(error, callback);
138
+ this.close();
137
139
  done(error);
138
140
  });
139
141
  });
@@ -144,20 +146,20 @@ class Database extends eventemitter3_1.default {
144
146
  let error = null;
145
147
  // we don't wont to silently open a new connection after a disconnession
146
148
  if (this.connection && this.connection.connected) {
147
- this.connection.sendCommands(command, callback);
149
+ this.connection.sendCommands(command, (error, results) => {
150
+ callback === null || callback === void 0 ? void 0 : callback.call(this, error, results);
151
+ done(error);
152
+ });
148
153
  }
149
154
  else {
150
155
  error = new types_1.SQLiteCloudError('Connection unavailable. Maybe it got disconnected?', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' });
151
- this.handleError(error, callback);
156
+ callback === null || callback === void 0 ? void 0 : callback.call(this, error, null);
157
+ done(error);
152
158
  }
153
- done(error);
154
159
  });
155
160
  }
156
161
  /** Handles an error by closing the connection, calling the callback and/or emitting an error event */
157
162
  handleError(error, callback) {
158
- var _a;
159
- // an errored connection is thrown out
160
- (_a = this.connection) === null || _a === void 0 ? void 0 : _a.close();
161
163
  if (callback) {
162
164
  callback.call(this, error);
163
165
  }
@@ -340,11 +342,14 @@ class Database extends eventemitter3_1.default {
340
342
  * parameters is emitted, regardless of whether a callback was provided or not.
341
343
  */
342
344
  close(callback) {
343
- var _a;
344
- this.operations.clear();
345
- (_a = this.connection) === null || _a === void 0 ? void 0 : _a.close();
346
- callback === null || callback === void 0 ? void 0 : callback.call(this, null);
347
- this.emitEvent('close');
345
+ this.operations.enqueue(done => {
346
+ var _a;
347
+ (_a = this.connection) === null || _a === void 0 ? void 0 : _a.close();
348
+ callback === null || callback === void 0 ? void 0 : callback.call(this, null);
349
+ this.emitEvent('close');
350
+ this.operations.clear();
351
+ done(null);
352
+ });
348
353
  }
349
354
  /**
350
355
  * Loads a compiled SQLite extension into the database connection object.
@@ -1,5 +1,5 @@
1
- import { SQLiteCloudCommand, type SQLCloudRowsetMetadata, type SQLiteCloudDataTypes } from './types';
2
1
  import { SQLiteCloudRowset } from './rowset';
2
+ import { SQLiteCloudCommand, type SQLCloudRowsetMetadata, type SQLiteCloudDataTypes } from './types';
3
3
  import { Buffer } from 'buffer';
4
4
  export declare const CMD_STRING = "+";
5
5
  export declare const CMD_ZEROSTRING = "!";
@@ -50,4 +50,4 @@ export declare function popData(buffer: Buffer): {
50
50
  fwdBuffer: Buffer;
51
51
  };
52
52
  /** Format a command to be sent via SCSP protocol */
53
- export declare function formatCommand(command: SQLiteCloudCommand): string;
53
+ export declare function formatCommand(command: SQLiteCloudCommand): Buffer;
@@ -15,8 +15,8 @@ exports.bufferEndsWith = bufferEndsWith;
15
15
  exports.parseRowsetChunks = parseRowsetChunks;
16
16
  exports.popData = popData;
17
17
  exports.formatCommand = formatCommand;
18
- const types_1 = require("./types");
19
18
  const rowset_1 = require("./rowset");
19
+ const types_1 = require("./types");
20
20
  // explicitly importing buffer library to allow cross-platform support by replacing it
21
21
  const buffer_1 = require("buffer");
22
22
  // https://www.npmjs.com/package/lz4js
@@ -272,7 +272,17 @@ function popData(buffer) {
272
272
  // console.debug(`popData - dataType: ${dataType}, spaceIndex: ${spaceIndex}, commandLength: ${commandLength}, commandEnd: ${commandEnd}`)
273
273
  switch (dataType) {
274
274
  case exports.CMD_INT:
275
- return popResults(parseInt(buffer.subarray(1, spaceIndex).toString()));
275
+ // SQLite uses 64-bit INTEGER, but JS uses 53-bit Number
276
+ const value = BigInt(buffer.subarray(1, spaceIndex).toString());
277
+ if (types_1.SAFE_INTEGER_MODE === 'bigint') {
278
+ return popResults(value);
279
+ }
280
+ if (types_1.SAFE_INTEGER_MODE === 'mixed') {
281
+ if (value <= BigInt(Number.MIN_SAFE_INTEGER) || BigInt(Number.MAX_SAFE_INTEGER) <= value) {
282
+ return popResults(value);
283
+ }
284
+ }
285
+ return popResults(Number(value));
276
286
  case exports.CMD_FLOAT:
277
287
  return popResults(parseFloat(buffer.subarray(1, spaceIndex).toString()));
278
288
  case exports.CMD_NULL:
@@ -314,15 +324,15 @@ function formatCommand(command) {
314
324
  }
315
325
  function serializeCommand(data, zeroString = false) {
316
326
  const n = data.length;
317
- let serializedData = `${n} `;
327
+ let serializedData = buffer_1.Buffer.from(`${n} `);
318
328
  for (let i = 0; i < n; i++) {
319
329
  // the first string is the sql and it must be zero-terminated
320
330
  const zs = i == 0 || zeroString;
321
- serializedData += serializeData(data[i], zs);
331
+ serializedData = buffer_1.Buffer.concat([serializedData, serializeData(data[i], zs)]);
322
332
  }
323
- const bytesTotal = buffer_1.Buffer.byteLength(serializedData, 'utf-8');
324
- const header = `${exports.CMD_ARRAY}${bytesTotal} `;
325
- return header + serializedData;
333
+ const bytesTotal = serializedData.byteLength;
334
+ const header = buffer_1.Buffer.from(`${exports.CMD_ARRAY}${bytesTotal} `);
335
+ return buffer_1.Buffer.concat([header, serializedData]);
326
336
  }
327
337
  function serializeData(data, zeroString = false) {
328
338
  if (typeof data === 'string') {
@@ -332,22 +342,25 @@ function serializeData(data, zeroString = false) {
332
342
  data += '\0';
333
343
  }
334
344
  const header = `${cmd}${buffer_1.Buffer.byteLength(data, 'utf-8')} `;
335
- return header + data;
345
+ return buffer_1.Buffer.from(header + data);
336
346
  }
337
347
  if (typeof data === 'number') {
338
348
  if (Number.isInteger(data)) {
339
- return `${exports.CMD_INT}${data} `;
349
+ return buffer_1.Buffer.from(`${exports.CMD_INT}${data} `);
340
350
  }
341
351
  else {
342
- return `${exports.CMD_FLOAT}${data} `;
352
+ return buffer_1.Buffer.from(`${exports.CMD_FLOAT}${data} `);
343
353
  }
344
354
  }
355
+ if (typeof data === 'bigint') {
356
+ return buffer_1.Buffer.from(`${exports.CMD_INT}${data} `);
357
+ }
345
358
  if (buffer_1.Buffer.isBuffer(data)) {
346
- const header = `${exports.CMD_BLOB}${data.length} `;
347
- return header + data.toString('utf-8');
359
+ const header = `${exports.CMD_BLOB}${data.byteLength} `;
360
+ return buffer_1.Buffer.concat([buffer_1.Buffer.from(header), data]);
348
361
  }
349
362
  if (data === null || data === undefined) {
350
- return `${exports.CMD_NULL} `;
363
+ return buffer_1.Buffer.from(`${exports.CMD_NULL} `);
351
364
  }
352
365
  if (Array.isArray(data)) {
353
366
  return serializeCommand(data, zeroString);
@@ -6,6 +6,16 @@ import tls from 'tls';
6
6
  export declare const DEFAULT_TIMEOUT: number;
7
7
  /** Default tls connection port */
8
8
  export declare const DEFAULT_PORT = 8860;
9
+ /**
10
+ * Support to SQLite 64bit integer
11
+ *
12
+ * number - (default) always return Number type (max: 2^53 - 1)
13
+ * Precision is lost when selecting greater numbers from SQLite
14
+ * bigint - always return BigInt type (max: 2^63 - 1) for all numbers from SQLite
15
+ * (inlcuding `lastID` from WRITE statements)
16
+ * mixed - use BigInt and Number types depending on the value size
17
+ */
18
+ export declare let SAFE_INTEGER_MODE: string;
9
19
  /**
10
20
  * Configuration for SQLite cloud connection
11
21
  * @note Options are all lowecase so they 1:1 compatible with C SDK
@@ -21,6 +31,8 @@ export interface SQLiteCloudConfig {
21
31
  password_hashed?: boolean;
22
32
  /** API key can be provided instead of username and password */
23
33
  apikey?: string;
34
+ /** Access Token provided in place of API Key or username/password */
35
+ token?: string;
24
36
  /** Host name is required unless connectionstring is provided, eg: xxx.sqlitecloud.io */
25
37
  host?: string;
26
38
  /** Port number for tls socket */
@@ -2,12 +2,32 @@
2
2
  /**
3
3
  * types.ts - shared types and interfaces
4
4
  */
5
+ var _a;
5
6
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SQLiteCloudArrayType = exports.SQLiteCloudError = exports.DEFAULT_PORT = exports.DEFAULT_TIMEOUT = void 0;
7
+ exports.SQLiteCloudArrayType = exports.SQLiteCloudError = exports.SAFE_INTEGER_MODE = exports.DEFAULT_PORT = exports.DEFAULT_TIMEOUT = void 0;
7
8
  /** Default timeout value for queries */
8
9
  exports.DEFAULT_TIMEOUT = 300 * 1000;
9
10
  /** Default tls connection port */
10
11
  exports.DEFAULT_PORT = 8860;
12
+ /**
13
+ * Support to SQLite 64bit integer
14
+ *
15
+ * number - (default) always return Number type (max: 2^53 - 1)
16
+ * Precision is lost when selecting greater numbers from SQLite
17
+ * bigint - always return BigInt type (max: 2^63 - 1) for all numbers from SQLite
18
+ * (inlcuding `lastID` from WRITE statements)
19
+ * mixed - use BigInt and Number types depending on the value size
20
+ */
21
+ exports.SAFE_INTEGER_MODE = 'number';
22
+ if (typeof process !== 'undefined') {
23
+ exports.SAFE_INTEGER_MODE = ((_a = process.env['SAFE_INTEGER_MODE']) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || 'number';
24
+ }
25
+ if (exports.SAFE_INTEGER_MODE == 'bigint') {
26
+ console.debug('BigInt mode: Using Number for all INTEGER values from SQLite, including meta information from WRITE statements.');
27
+ }
28
+ if (exports.SAFE_INTEGER_MODE == 'mixed') {
29
+ console.debug('Mixed mode: Using BigInt for INTEGER values from SQLite (including meta information from WRITE statements) bigger then 2^53, Number otherwise.');
30
+ }
11
31
  /** Custom error reported by SQLiteCloud drivers */
12
32
  class SQLiteCloudError extends Error {
13
33
  constructor(message, args) {
@@ -15,7 +15,6 @@ exports.parseconnectionstring = parseconnectionstring;
15
15
  exports.parseBoolean = parseBoolean;
16
16
  exports.parseBooleanToZeroOne = parseBooleanToZeroOne;
17
17
  const types_1 = require("./types");
18
- const types_2 = require("./types");
19
18
  // explicitly importing these libraries to allow cross-platform support by replacing them
20
19
  const whatwg_url_1 = require("whatwg-url");
21
20
  //
@@ -46,42 +45,45 @@ function anonimizeError(error) {
46
45
  function getInitializationCommands(config) {
47
46
  // we check the credentials using non linearizable so we're quicker
48
47
  // then we bring back linearizability unless specified otherwise
49
- let commands = 'SET CLIENT KEY NONLINEARIZABLE TO 1; ';
48
+ let commands = 'SET CLIENT KEY NONLINEARIZABLE TO 1;';
50
49
  // first user authentication, then all other commands
51
50
  if (config.apikey) {
52
- commands += `AUTH APIKEY ${config.apikey}; `;
51
+ commands += `AUTH APIKEY ${config.apikey};`;
52
+ }
53
+ else if (config.token) {
54
+ commands += `AUTH TOKEN ${config.token};`;
53
55
  }
54
56
  else {
55
- commands += `AUTH USER ${config.username || ''} ${config.password_hashed ? 'HASH' : 'PASSWORD'} ${config.password || ''}; `;
57
+ commands += `AUTH USER ${config.username || ''} ${config.password_hashed ? 'HASH' : 'PASSWORD'} ${config.password || ''};`;
56
58
  }
57
59
  if (config.compression) {
58
- commands += 'SET CLIENT KEY COMPRESSION TO 1; ';
60
+ commands += 'SET CLIENT KEY COMPRESSION TO 1;';
59
61
  }
60
62
  if (config.zerotext) {
61
- commands += 'SET CLIENT KEY ZEROTEXT TO 1; ';
63
+ commands += 'SET CLIENT KEY ZEROTEXT TO 1;';
62
64
  }
63
65
  if (config.noblob) {
64
- commands += 'SET CLIENT KEY NOBLOB TO 1; ';
66
+ commands += 'SET CLIENT KEY NOBLOB TO 1;';
65
67
  }
66
68
  if (config.maxdata) {
67
- commands += `SET CLIENT KEY MAXDATA TO ${config.maxdata}; `;
69
+ commands += `SET CLIENT KEY MAXDATA TO ${config.maxdata};`;
68
70
  }
69
71
  if (config.maxrows) {
70
- commands += `SET CLIENT KEY MAXROWS TO ${config.maxrows}; `;
72
+ commands += `SET CLIENT KEY MAXROWS TO ${config.maxrows};`;
71
73
  }
72
74
  if (config.maxrowset) {
73
- commands += `SET CLIENT KEY MAXROWSET TO ${config.maxrowset}; `;
75
+ commands += `SET CLIENT KEY MAXROWSET TO ${config.maxrowset};`;
74
76
  }
75
77
  // we ALWAYS set non linearizable to 1 when we start so we can be quicker on login
76
78
  // but then we need to put it back to its default value if "linearizable" unless set
77
79
  if (!config.non_linearizable) {
78
- commands += 'SET CLIENT KEY NONLINEARIZABLE TO 0; ';
80
+ commands += 'SET CLIENT KEY NONLINEARIZABLE TO 0;';
79
81
  }
80
82
  if (config.database) {
81
83
  if (config.create && !config.memory) {
82
- commands += `CREATE DATABASE ${config.database} IF NOT EXISTS; `;
84
+ commands += `CREATE DATABASE ${config.database} IF NOT EXISTS;`;
83
85
  }
84
- commands += `USE DATABASE ${config.database}; `;
86
+ commands += `USE DATABASE ${config.database};`;
85
87
  }
86
88
  return commands;
87
89
  }
@@ -102,15 +104,14 @@ function getUpdateResults(results) {
102
104
  if (results) {
103
105
  if (Array.isArray(results) && results.length > 0) {
104
106
  switch (results[0]) {
105
- case types_2.SQLiteCloudArrayType.ARRAY_TYPE_SQLITE_EXEC:
107
+ case types_1.SQLiteCloudArrayType.ARRAY_TYPE_SQLITE_EXEC:
106
108
  return {
107
- type: results[0],
108
- index: results[1],
109
+ type: Number(results[0]),
110
+ index: Number(results[1]),
109
111
  lastID: results[2], // ROWID (sqlite3_last_insert_rowid)
110
112
  changes: results[3], // CHANGES(sqlite3_changes)
111
113
  totalChanges: results[4], // TOTAL_CHANGES (sqlite3_total_changes)
112
- finalized: results[5], // FINALIZED
113
- //
114
+ finalized: Number(results[5]), // FINALIZED
114
115
  rowId: results[2] // same as lastId
115
116
  };
116
117
  }
@@ -159,15 +160,19 @@ function validateConfiguration(config) {
159
160
  config.create = parseBoolean(config.create);
160
161
  config.non_linearizable = parseBoolean(config.non_linearizable);
161
162
  config.insecure = parseBoolean(config.insecure);
162
- const hasCredentials = (config.username && config.password) || config.apikey;
163
+ const hasCredentials = (config.username && config.password) || config.apikey || config.token;
163
164
  if (!config.host || !hasCredentials) {
164
165
  console.error('SQLiteCloudConnection.validateConfiguration - missing arguments', config);
165
- throw new types_1.SQLiteCloudError('The user, password and host arguments or the ?apikey= must be specified.', { errorCode: 'ERR_MISSING_ARGS' });
166
+ throw new types_1.SQLiteCloudError('The user, password and host arguments, the ?apikey= or the ?token= must be specified.', { errorCode: 'ERR_MISSING_ARGS' });
166
167
  }
167
168
  if (!config.connectionstring) {
168
169
  // build connection string from configuration, values are already validated
170
+ config.connectionstring = `sqlitecloud://${config.host}:${config.port}/${config.database || ''}`;
169
171
  if (config.apikey) {
170
- config.connectionstring = `sqlitecloud://${config.host}:${config.port}/${config.database || ''}?apikey=${config.apikey}`;
172
+ config.connectionstring += `?apikey=${config.apikey}`;
173
+ }
174
+ else if (config.token) {
175
+ config.connectionstring += `?token=${config.token}`;
171
176
  }
172
177
  else {
173
178
  config.connectionstring = `sqlitecloud://${encodeURIComponent(config.username || '')}:${encodeURIComponent(config.password || '')}@${config.host}:${config.port}/${config.database}`;
@@ -192,16 +197,15 @@ function parseconnectionstring(connectionstring) {
192
197
  // all lowecase options
193
198
  const options = {};
194
199
  url.searchParams.forEach((value, key) => {
195
- options[key.toLowerCase().replace(/-/g, '_')] = value;
200
+ options[key.toLowerCase().replace(/-/g, '_')] = value.trim();
196
201
  });
197
- const config = Object.assign({ username: decodeURIComponent(url.username), password: decodeURIComponent(url.password), host: url.hostname, port: url.port ? parseInt(url.port) : undefined }, options);
198
- // either you use an apikey or username and password
199
- if (config.apikey) {
200
- if (config.username || config.password) {
201
- console.warn('SQLiteCloudConnection.parseconnectionstring - apikey and username/password are both specified, using apikey');
202
- }
203
- delete config.username;
204
- delete config.password;
202
+ const config = Object.assign(Object.assign({}, options), { username: url.username ? decodeURIComponent(url.username) : undefined, password: url.password ? decodeURIComponent(url.password) : undefined, password_hashed: options.password_hashed ? parseBoolean(options.password_hashed) : undefined, host: url.hostname,
203
+ // type cast values
204
+ port: url.port ? parseInt(url.port) : undefined, insecure: options.insecure ? parseBoolean(options.insecure) : undefined, timeout: options.timeout ? parseInt(options.timeout) : undefined, zerotext: options.zerotext ? parseBoolean(options.zerotext) : undefined, create: options.create ? parseBoolean(options.create) : undefined, memory: options.memory ? parseBoolean(options.memory) : undefined, compression: options.compression ? parseBoolean(options.compression) : undefined, non_linearizable: options.non_linearizable ? parseBoolean(options.non_linearizable) : undefined, noblob: options.noblob ? parseBoolean(options.noblob) : undefined, maxdata: options.maxdata ? parseInt(options.maxdata) : undefined, maxrows: options.maxrows ? parseInt(options.maxrows) : undefined, maxrowset: options.maxrowset ? parseInt(options.maxrowset) : undefined, usewebsocket: options.usewebsocket ? parseBoolean(options.usewebsocket) : undefined, verbose: options.verbose ? parseBoolean(options.verbose) : undefined });
205
+ // either you use an apikey, token or username and password
206
+ if (Number(!!config.apikey) + Number(!!config.token) + Number(!!(config.username || config.password)) > 1) {
207
+ console.error('SQLiteCloudConnection.parseconnectionstring - choose between apikey, token or username/password');
208
+ throw new types_1.SQLiteCloudError('Choose between apikey, token or username/password');
205
209
  }
206
210
  const database = url.pathname.replace('/', ''); // pathname is database name, remove the leading slash
207
211
  if (database) {
@@ -210,7 +214,7 @@ function parseconnectionstring(connectionstring) {
210
214
  return config;
211
215
  }
212
216
  catch (error) {
213
- throw new types_1.SQLiteCloudError(`Invalid connection string: ${connectionstring}`);
217
+ throw new types_1.SQLiteCloudError(`Invalid connection string: ${connectionstring} - error: ${error}`);
214
218
  }
215
219
  }
216
220
  /** Returns true if value is 1 or true */