@sqlitecloud/drivers 1.0.438 → 1.0.653

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.
@@ -89,6 +89,7 @@ class SQLiteCloudTlsConnection extends connection_1.SQLiteCloudConnection {
89
89
  // @ts-ignore
90
90
  connector = tls.connectTLS;
91
91
  }
92
+ this.processCallback = callback;
92
93
  this.socket = connector(connectionOptions, () => {
93
94
  var _a;
94
95
  if (this.config.verbose) {
@@ -156,12 +157,12 @@ class SQLiteCloudTlsConnection extends connection_1.SQLiteCloudConnection {
156
157
  (_a = this.socket) === null || _a === void 0 ? void 0 : _a.destroy();
157
158
  this.socket = undefined;
158
159
  }, timeoutMs);
159
- (_d = this.socket) === null || _d === void 0 ? void 0 : _d.write(formattedCommands, 'utf-8', () => {
160
+ (_d = this.socket) === null || _d === void 0 ? void 0 : _d.write(formattedCommands, () => {
160
161
  clearTimeout(timeout); // Clear the timeout on successful write
161
162
  });
162
163
  }
163
164
  else {
164
- (_e = this.socket) === null || _e === void 0 ? void 0 : _e.write(formattedCommands, 'utf-8');
165
+ (_e = this.socket) === null || _e === void 0 ? void 0 : _e.write(formattedCommands);
165
166
  }
166
167
  return this;
167
168
  }
@@ -34,10 +34,14 @@ class SQLiteCloudWebsocketConnection extends connection_1.SQLiteCloudConnection
34
34
  this.socket.on('connect', () => {
35
35
  callback === null || callback === void 0 ? void 0 : callback.call(this, null);
36
36
  });
37
- this.socket.on('disconnect', (reason) => {
37
+ this.socket.on('disconnect', reason => {
38
38
  this.close();
39
39
  callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Disconnected', { errorCode: 'ERR_CONNECTION_ENDED', cause: reason }));
40
40
  });
41
+ this.socket.on('connect_error', (error) => {
42
+ this.close();
43
+ callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError(JSON.parse(error.context.responseText).message || 'Connection error', { errorCode: 'ERR_CONNECTION_ERROR' }));
44
+ });
41
45
  this.socket.on('error', (error) => {
42
46
  this.close();
43
47
  callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Connection error', { errorCode: 'ERR_CONNECTION_ERROR', cause: error }));
@@ -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,18 +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(Object.assign({}, options), { username: decodeURIComponent(url.username), password: decodeURIComponent(url.password), password_hashed: options.password_hashed ? parseBoolean(options.password_hashed) : undefined, host: url.hostname,
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,
198
203
  // type cast values
199
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 });
200
- // either you use an apikey or username and password
201
- if (config.apikey) {
202
- if (config.username || config.password) {
203
- console.warn('SQLiteCloudConnection.parseconnectionstring - apikey and username/password are both specified, using apikey');
204
- }
205
- delete config.username;
206
- delete config.password;
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');
207
209
  }
208
210
  const database = url.pathname.replace('/', ''); // pathname is database name, remove the leading slash
209
211
  if (database) {
@@ -212,7 +214,7 @@ function parseconnectionstring(connectionstring) {
212
214
  return config;
213
215
  }
214
216
  catch (error) {
215
- throw new types_1.SQLiteCloudError(`Invalid connection string: ${connectionstring}`);
217
+ throw new types_1.SQLiteCloudError(`Invalid connection string: ${connectionstring} - error: ${error}`);
216
218
  }
217
219
  }
218
220
  /** Returns true if value is 1 or true */