@sqlitecloud/drivers 0.0.56 → 1.0.178

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
@@ -18,6 +18,7 @@ npm install @sqlitecloud/drivers
18
18
  import { Database } from '@sqlitecloud/drivers'
19
19
 
20
20
  let database = new Database('sqlitecloud://user:password@xxx.sqlite.cloud:8860/chinook.sqlite')
21
+ // or use sqlitecloud://xxx.sqlite.cloud:8860?apikey=xxxxxxx
21
22
 
22
23
  let name = 'Breaking The Rules'
23
24
 
@@ -4,7 +4,7 @@
4
4
  import { type SQLiteCloudConfig, type ErrorCallback, type ResultsCallback } from './types';
5
5
  import { SQLiteCloudConnection } from './connection';
6
6
  /**
7
- * Implementation of SQLiteCloudConnection that connects to the database using specific Bun APIs
7
+ * Implementation of SQLiteCloudConnection that connects to the database using specific tls APIs
8
8
  * that connect to native sockets or tls sockets and communicates via raw, binary protocol.
9
9
  */
10
10
  export declare class SQLiteCloudTlsConnection extends SQLiteCloudConnection {
@@ -33,7 +33,7 @@ const utilities_1 = require("./utilities");
33
33
  const protocol_1 = require("./protocol");
34
34
  const tls = __importStar(require("tls"));
35
35
  /**
36
- * Implementation of SQLiteCloudConnection that connects to the database using specific Bun APIs
36
+ * Implementation of SQLiteCloudConnection that connects to the database using specific tls APIs
37
37
  * that connect to native sockets or tls sockets and communicates via raw, binary protocol.
38
38
  */
39
39
  class SQLiteCloudTlsConnection extends connection_1.SQLiteCloudConnection {
@@ -65,7 +65,7 @@ class SQLiteCloudTlsConnection extends connection_1.SQLiteCloudConnection {
65
65
  const connectionOptions = {
66
66
  host: config.host,
67
67
  port: config.port,
68
- rejectUnauthorized: false,
68
+ rejectUnauthorized: config.host != 'localhost',
69
69
  // Server name for the SNI (Server Name Indication) TLS extension.
70
70
  // https://r2.nodejs.org/docs/v6.11.4/api/tls.html#tls_class_tls_tlssocket
71
71
  servername: config.host
@@ -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) || `${this.config.host === 'localhost' ? 'ws' : 'wss'}://${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,7 +8,7 @@ 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;
@@ -29,6 +29,16 @@ export declare abstract class SQLiteCloudConnection {
29
29
  verbose(): void;
30
30
  /** Will enquee a command to be executed and callback with the resulting rowset/result/error */
31
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>;
32
42
  /** Disconnect from server, release transport. */
33
43
  abstract close(): this;
34
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);
@@ -53,7 +62,7 @@ class SQLiteCloudConnection {
53
62
  log(message, ...optionalParams) {
54
63
  if (this.config.verbose) {
55
64
  message = (0, utilities_2.anonimizeCommand)(message);
56
- console.log(`${new Date().toISOString()} ${this.config.clientId}: ${message}`, ...optionalParams);
65
+ console.log(`${new Date().toISOString()} ${this.config.clientid}: ${message}`, ...optionalParams);
57
66
  }
58
67
  }
59
68
  /** Enable verbose logging for debug purposes */
@@ -77,5 +86,51 @@ class SQLiteCloudConnection {
77
86
  });
78
87
  return this;
79
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
+ }
80
135
  }
81
136
  exports.SQLiteCloudConnection = SQLiteCloudConnection;
@@ -58,7 +58,7 @@ class Database extends eventemitter3_1.default {
58
58
  super();
59
59
  /** Database connections */
60
60
  this.connections = [];
61
- this.config = typeof config === 'string' ? { connectionString: config } : config;
61
+ this.config = typeof config === 'string' ? { connectionstring: config } : config;
62
62
  // mode is optional and so is callback
63
63
  // https://github.com/TryGhost/node-sqlite3/wiki/API#new-sqlite3databasefilename--mode--callback
64
64
  if (typeof mode === 'function') {
@@ -85,7 +85,7 @@ class Database extends eventemitter3_1.default {
85
85
  }
86
86
  else {
87
87
  // connect using websocket if tls is not supported or if explicitly requested
88
- const useWebsocket = utilities_2.isBrowser || ((_b = this.config) === null || _b === void 0 ? void 0 : _b.useWebsocket) || ((_c = this.config) === null || _c === void 0 ? void 0 : _c.gatewayUrl);
88
+ const useWebsocket = utilities_2.isBrowser || ((_b = this.config) === null || _b === void 0 ? void 0 : _b.usewebsocket) || ((_c = this.config) === null || _c === void 0 ? void 0 : _c.gatewayurl);
89
89
  if (useWebsocket) {
90
90
  // socket.io transport works in both node.js and browser environments and connects via SQLite Cloud Gateway
91
91
  Promise.resolve().then(() => __importStar(require('./connection-ws'))).then(module => {
@@ -210,7 +210,7 @@ class Database extends eventemitter3_1.default {
210
210
  else {
211
211
  // context may include id of last row inserted, total changes, etc...
212
212
  const context = this.processContext(results);
213
- callback === null || callback === void 0 ? void 0 : callback.call(context || this, null, context ? undefined : results);
213
+ callback === null || callback === void 0 ? void 0 : callback.call(context || this, null, context ? context : results);
214
214
  }
215
215
  });
216
216
  }
@@ -8,17 +8,22 @@ import tls from 'tls';
8
8
  export declare const DEFAULT_TIMEOUT: number;
9
9
  /** Default tls connection port */
10
10
  export declare const DEFAULT_PORT = 9960;
11
- /** Configuration for SQLite cloud connection */
11
+ /**
12
+ * Configuration for SQLite cloud connection
13
+ * @note Options are all lowecase so they 1:1 compatible with C SDK
14
+ */
12
15
  export interface SQLiteCloudConfig {
13
16
  /** Connection string in the form of sqlitecloud://user:password@host:port/database?options */
14
- connectionString?: string;
15
- /** User name is required unless connectionString is provided */
17
+ connectionstring?: string;
18
+ /** User name is required unless connectionstring is provided */
16
19
  username?: string;
17
20
  /** Password is required unless connection string is provided */
18
21
  password?: string;
19
22
  /** True if password is hashed, default is false */
20
- passwordHashed?: boolean;
21
- /** Host name is required unless connectionString is provided, eg: xxx.sqlitecloud.io */
23
+ password_hashed?: boolean;
24
+ /** API key can be provided instead of username and password */
25
+ apikey?: string;
26
+ /** Host name is required unless connectionstring is provided, eg: xxx.sqlitecloud.io */
22
27
  host?: string;
23
28
  /** Port number for tls socket */
24
29
  port?: number;
@@ -28,29 +33,31 @@ export interface SQLiteCloudConfig {
28
33
  timeout?: number;
29
34
  /** Name of database to open */
30
35
  database?: string;
36
+ /** Flag to tell the server to zero-terminate strings */
37
+ zerotext?: boolean;
31
38
  /** Create the database if it doesn't exist? */
32
- createDatabase?: boolean;
39
+ create?: boolean;
33
40
  /** Database will be created in memory */
34
- dbMemory?: boolean;
41
+ memory?: boolean;
35
42
  compression?: boolean;
36
43
  /** Request for immediate responses from the server node without waiting for linerizability guarantees */
37
- nonlinearizable?: boolean;
44
+ non_linearizable?: boolean;
38
45
  /** Server should send BLOB columns */
39
- noBlob?: boolean;
46
+ noblob?: boolean;
40
47
  /** Do not send columns with more than max_data bytes */
41
- maxData?: number;
48
+ maxdata?: number;
42
49
  /** Server should chunk responses with more than maxRows */
43
- maxRows?: number;
50
+ maxrows?: number;
44
51
  /** Server should limit total number of rows in a set to maxRowset */
45
- maxRowset?: number;
52
+ maxrowset?: number;
46
53
  /** Custom options and configurations for tls socket, eg: additional certificates */
47
- tlsOptions?: tls.ConnectionOptions;
54
+ tlsoptions?: tls.ConnectionOptions;
48
55
  /** True if we should force use of SQLite Cloud Gateway and websocket connections, default: true in browsers, false in node.js */
49
- useWebsocket?: boolean;
56
+ usewebsocket?: boolean;
50
57
  /** Url where we can connect to a SQLite Cloud Gateway that has a socket.io deamon waiting to connect, eg. wss://host:4000 */
51
- gatewayUrl?: string;
58
+ gatewayurl?: string;
52
59
  /** Optional identifier used for verbose logging */
53
- clientId?: string;
60
+ clientid?: string;
54
61
  /** True if connection should enable debug logs */
55
62
  verbose?: boolean;
56
63
  }
@@ -83,7 +90,7 @@ export interface SQLCloudRowsetMetadata {
83
90
  }[];
84
91
  }
85
92
  /** Basic types that can be returned by SQLiteCloud APIs */
86
- export type SQLiteCloudDataTypes = string | number | boolean | Record<string | number, unknown> | Buffer | null | undefined;
93
+ export type SQLiteCloudDataTypes = string | number | bigint | boolean | Record<string | number, unknown> | Buffer | null | undefined;
87
94
  /** Custom error reported by SQLiteCloud drivers */
88
95
  export declare class SQLiteCloudError extends Error {
89
96
  constructor(message: string, args?: Partial<SQLiteCloudError>);
@@ -11,6 +11,8 @@ export declare function getInitializationCommands(config: SQLiteCloudConfig): st
11
11
  export declare function escapeSqlParameter(param: SQLiteCloudDataTypes): string;
12
12
  /** Take a sql statement and replaces ? or $named parameters that are properly serialized and escaped. */
13
13
  export declare function prepareSql(sql: string, ...params: (SQLiteCloudDataTypes | SQLiteCloudDataTypes[])[]): string;
14
+ /** Converts results of an update or insert call into a more meaning full result set */
15
+ export declare function getUpdateResults(results?: any): Record<string, any> | undefined;
14
16
  /**
15
17
  * Many of the methods in our API may contain a callback as their last argument.
16
18
  * This method will take the arguments array passed to the method and return an object
@@ -25,7 +27,13 @@ export declare function popCallback<T extends ErrorCallback = ErrorCallback>(arg
25
27
  };
26
28
  /** Validate configuration, apply defaults, throw if something is missing or misconfigured */
27
29
  export declare function validateConfiguration(config: SQLiteCloudConfig): SQLiteCloudConfig;
28
- /** Parse connectionString like sqlitecloud://username:password@host:port/database?option1=xxx&option2=xxx into its components */
29
- export declare function parseConnectionString(connectionString: string): SQLiteCloudConfig;
30
+ /**
31
+ * Parse connectionstring like sqlitecloud://username:password@host:port/database?option1=xxx&option2=xxx
32
+ * or sqlitecloud://host.sqlite.cloud:8860/chinook.sqlite?apikey=mIiLARzKm9XBVllbAzkB1wqrgijJ3Gx0X5z1Agm3xBo
33
+ * into its basic components.
34
+ */
35
+ export declare function parseconnectionstring(connectionstring: string): SQLiteCloudConfig;
30
36
  /** Returns true if value is 1 or true */
31
37
  export declare function parseBoolean(value: string | boolean | null | undefined): boolean;
38
+ /** Returns true if value is 1 or true */
39
+ export declare function parseBooleanToZeroOne(value: string | boolean | null | undefined): 0 | 1;
@@ -3,8 +3,9 @@
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.validateConfiguration = exports.popCallback = exports.prepareSql = exports.escapeSqlParameter = exports.getInitializationCommands = exports.anonimizeError = exports.anonimizeCommand = exports.isNode = exports.isBrowser = void 0;
6
+ exports.parseBooleanToZeroOne = exports.parseBoolean = exports.parseconnectionstring = exports.validateConfiguration = exports.popCallback = exports.getUpdateResults = exports.prepareSql = exports.escapeSqlParameter = exports.getInitializationCommands = exports.anonimizeError = exports.anonimizeCommand = exports.isNode = exports.isBrowser = void 0;
7
7
  const types_1 = require("./types");
8
+ const types_2 = require("./types");
8
9
  //
9
10
  // determining running environment, thanks to browser-or-node
10
11
  // https://www.npmjs.com/package/browser-or-node
@@ -33,31 +34,44 @@ function anonimizeError(error) {
33
34
  exports.anonimizeError = anonimizeError;
34
35
  /** Initialization commands sent to database when connection is established */
35
36
  function getInitializationCommands(config) {
37
+ // we check the credentials using non linearizable so we're quicker
38
+ // then we bring back linearizability unless specified otherwise
39
+ let commands = 'SET CLIENT KEY NONLINEARIZABLE TO 1; ';
36
40
  // 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}; `;
41
+ if (config.apikey) {
42
+ commands += `AUTH APIKEY ${config.apikey}; `;
43
+ }
44
+ else {
45
+ commands += `AUTH USER ${config.username || ''} ${config.password_hashed ? 'HASH' : 'PASSWORD'} ${config.password || ''}; `;
43
46
  }
44
47
  if (config.compression) {
45
48
  commands += 'SET CLIENT KEY COMPRESSION TO 1; ';
46
49
  }
47
- if (config.nonlinearizable) {
48
- commands += 'SET CLIENT KEY NONLINEARIZABLE TO 1; ';
50
+ if (config.zerotext) {
51
+ commands += 'SET CLIENT KEY ZEROTEXT TO 1; ';
49
52
  }
50
- if (config.noBlob) {
53
+ if (config.noblob) {
51
54
  commands += 'SET CLIENT KEY NOBLOB TO 1; ';
52
55
  }
53
- if (config.maxData) {
54
- commands += `SET CLIENT KEY MAXDATA TO ${config.maxData}; `;
56
+ if (config.maxdata) {
57
+ commands += `SET CLIENT KEY MAXDATA TO ${config.maxdata}; `;
58
+ }
59
+ if (config.maxrows) {
60
+ commands += `SET CLIENT KEY MAXROWS TO ${config.maxrows}; `;
55
61
  }
56
- if (config.maxRows) {
57
- commands += `SET CLIENT KEY MAXROWS TO ${config.maxRows}; `;
62
+ if (config.maxrowset) {
63
+ commands += `SET CLIENT KEY MAXROWSET TO ${config.maxrowset}; `;
58
64
  }
59
- if (config.maxRowset) {
60
- commands += `SET CLIENT KEY MAXROWSET TO ${config.maxRowset}; `;
65
+ // we ALWAYS set non linearizable to 1 when we start so we can be quicker on login
66
+ // but then we need to put it back to its default value if "linearizable" unless set
67
+ if (!config.non_linearizable) {
68
+ commands += 'SET CLIENT KEY NONLINEARIZABLE TO 0; ';
69
+ }
70
+ if (config.database) {
71
+ if (config.create && !config.memory) {
72
+ commands += `CREATE DATABASE ${config.database} IF NOT EXISTS; `;
73
+ }
74
+ commands += `USE DATABASE ${config.database}; `;
61
75
  }
62
76
  return commands;
63
77
  }
@@ -72,7 +86,7 @@ function escapeSqlParameter(param) {
72
86
  param = param.replace(/'/g, "''");
73
87
  return `'${param}'`;
74
88
  }
75
- if (typeof param === 'number') {
89
+ if (typeof param === 'number' || typeof param === 'bigint') {
76
90
  return param.toString();
77
91
  }
78
92
  if (typeof param === 'boolean') {
@@ -128,6 +142,28 @@ function prepareSql(sql, ...params) {
128
142
  return preparedSql;
129
143
  }
130
144
  exports.prepareSql = prepareSql;
145
+ /** Converts results of an update or insert call into a more meaning full result set */
146
+ function getUpdateResults(results) {
147
+ if (results) {
148
+ if (Array.isArray(results) && results.length > 0) {
149
+ switch (results[0]) {
150
+ case types_2.SQLiteCloudArrayType.ARRAY_TYPE_SQLITE_EXEC:
151
+ return {
152
+ type: results[0],
153
+ index: results[1],
154
+ lastID: results[2],
155
+ changes: results[3],
156
+ totalChanges: results[4],
157
+ finalized: results[5],
158
+ //
159
+ rowId: results[2] // same as lastId
160
+ };
161
+ }
162
+ }
163
+ }
164
+ return undefined;
165
+ }
166
+ exports.getUpdateResults = getUpdateResults;
131
167
  /**
132
168
  * Many of the methods in our API may contain a callback as their last argument.
133
169
  * This method will take the arguments array passed to the method and return an object
@@ -154,47 +190,66 @@ exports.popCallback = popCallback;
154
190
  /** Validate configuration, apply defaults, throw if something is missing or misconfigured */
155
191
  function validateConfiguration(config) {
156
192
  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
193
+ if (config.connectionstring) {
194
+ config = Object.assign(Object.assign(Object.assign({}, config), parseconnectionstring(config.connectionstring)), { connectionstring: config.connectionstring // keep original connection string
159
195
  });
160
196
  }
161
197
  // apply defaults where needed
162
198
  config.port || (config.port = types_1.DEFAULT_PORT);
163
199
  config.timeout = config.timeout && config.timeout > 0 ? config.timeout : types_1.DEFAULT_TIMEOUT;
164
- config.clientId || (config.clientId = 'SQLiteCloud');
200
+ config.clientid || (config.clientid = 'SQLiteCloud');
165
201
  config.verbose = parseBoolean(config.verbose);
166
- config.noBlob = parseBoolean(config.noBlob);
202
+ config.noblob = parseBoolean(config.noblob);
167
203
  config.compression = parseBoolean(config.compression);
168
- config.createDatabase = parseBoolean(config.createDatabase);
169
- config.nonlinearizable = parseBoolean(config.nonlinearizable);
204
+ config.create = parseBoolean(config.create);
205
+ config.non_linearizable = parseBoolean(config.non_linearizable);
170
206
  config.insecure = parseBoolean(config.insecure);
171
- if (!config.username || !config.password || !config.host) {
207
+ const hasCredentials = (config.username && config.password) || config.apikey;
208
+ if (!config.host || !hasCredentials) {
172
209
  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' });
210
+ throw new types_1.SQLiteCloudError('The user, password and host arguments or the ?apikey= must be specified.', { errorCode: 'ERR_MISSING_ARGS' });
174
211
  }
175
- if (!config.connectionString) {
212
+ if (!config.connectionstring) {
176
213
  // build connection string from configuration, values are already validated
177
214
  // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
178
- config.connectionString = `sqlitecloud://${encodeURIComponent(config.username)}:${encodeURIComponent(config.password)}@${config.host}:${config.port}/${config.database}`;
215
+ if (config.apikey) {
216
+ config.connectionstring = `sqlitecloud://${config.host}:${config.port}/${config.database || ''}?apikey=${config.apikey}`;
217
+ }
218
+ else {
219
+ config.connectionstring = `sqlitecloud://${encodeURIComponent(config.username || '')}:${encodeURIComponent(config.password || '')}@${config.host}:${config.port}/${config.database}`;
220
+ }
179
221
  }
180
222
  return config;
181
223
  }
182
224
  exports.validateConfiguration = validateConfiguration;
183
- /** Parse connectionString like sqlitecloud://username:password@host:port/database?option1=xxx&option2=xxx into its components */
184
- function parseConnectionString(connectionString) {
225
+ /**
226
+ * Parse connectionstring like sqlitecloud://username:password@host:port/database?option1=xxx&option2=xxx
227
+ * or sqlitecloud://host.sqlite.cloud:8860/chinook.sqlite?apikey=mIiLARzKm9XBVllbAzkB1wqrgijJ3Gx0X5z1Agm3xBo
228
+ * into its basic components.
229
+ */
230
+ function parseconnectionstring(connectionstring) {
185
231
  try {
186
232
  // The URL constructor throws a TypeError if the URL is not valid.
187
233
  // in spite of having the same structure as a regular url
188
234
  // protocol://username:password@host:port/database?option1=xxx&option2=xxx)
189
235
  // the sqlitecloud: protocol is not recognized by the URL constructor in browsers
190
236
  // so we need to replace it with https: to make it work
191
- const knownProtocolUrl = connectionString.replace('sqlitecloud:', 'https:');
237
+ const knownProtocolUrl = connectionstring.replace('sqlitecloud:', 'https:');
192
238
  const url = new URL(knownProtocolUrl);
239
+ // all lowecase options
193
240
  const options = {};
194
241
  url.searchParams.forEach((value, key) => {
195
- options[key] = value;
242
+ options[key.toLowerCase().replaceAll('-', '_')] = value;
196
243
  });
197
244
  const config = Object.assign({ username: decodeURIComponent(url.username), password: decodeURIComponent(url.password), host: url.hostname, port: url.port ? parseInt(url.port) : undefined }, options);
245
+ // either you use an apikey or username and password
246
+ if (config.apikey) {
247
+ if (config.username || config.password) {
248
+ console.warn('SQLiteCloudConnection.parseconnectionstring - apikey and username/password are both specified, using apikey');
249
+ }
250
+ delete config.username;
251
+ delete config.password;
252
+ }
198
253
  const database = url.pathname.replace('/', ''); // pathname is database name, remove the leading slash
199
254
  if (database) {
200
255
  config.database = database;
@@ -202,10 +257,10 @@ function parseConnectionString(connectionString) {
202
257
  return config;
203
258
  }
204
259
  catch (error) {
205
- throw new types_1.SQLiteCloudError(`Invalid connection string: ${connectionString}`);
260
+ throw new types_1.SQLiteCloudError(`Invalid connection string: ${connectionstring}`);
206
261
  }
207
262
  }
208
- exports.parseConnectionString = parseConnectionString;
263
+ exports.parseconnectionstring = parseconnectionstring;
209
264
  /** Returns true if value is 1 or true */
210
265
  function parseBoolean(value) {
211
266
  if (typeof value === 'string') {
@@ -214,3 +269,11 @@ function parseBoolean(value) {
214
269
  return value ? true : false;
215
270
  }
216
271
  exports.parseBoolean = parseBoolean;
272
+ /** Returns true if value is 1 or true */
273
+ function parseBooleanToZeroOne(value) {
274
+ if (typeof value === 'string') {
275
+ return value.toLowerCase() === 'true' || value === '1' ? 1 : 0;
276
+ }
277
+ return value ? 1 : 0;
278
+ }
279
+ exports.parseBooleanToZeroOne = parseBooleanToZeroOne;
package/lib/index.d.ts CHANGED
@@ -3,4 +3,4 @@ export { Statement } from './drivers/statement';
3
3
  export { SQLiteCloudConnection } from './drivers/connection';
4
4
  export { type SQLiteCloudConfig, type SQLCloudRowsetMetadata, SQLiteCloudError, type ResultsCallback, type ErrorCallback } from './drivers/types';
5
5
  export { SQLiteCloudRowset, SQLiteCloudRow } from './drivers/rowset';
6
- export { escapeSqlParameter, prepareSql, parseConnectionString, validateConfiguration } from './drivers/utilities';
6
+ export { escapeSqlParameter, prepareSql, parseconnectionstring, validateConfiguration } from './drivers/utilities';
package/lib/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // index.ts - export drivers classes, utilities, types
4
4
  //
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.validateConfiguration = exports.parseConnectionString = exports.prepareSql = exports.escapeSqlParameter = exports.SQLiteCloudRow = exports.SQLiteCloudRowset = exports.SQLiteCloudError = exports.SQLiteCloudConnection = exports.Statement = exports.Database = void 0;
6
+ exports.validateConfiguration = exports.parseconnectionstring = exports.prepareSql = exports.escapeSqlParameter = exports.SQLiteCloudRow = exports.SQLiteCloudRowset = exports.SQLiteCloudError = exports.SQLiteCloudConnection = exports.Statement = exports.Database = void 0;
7
7
  // include ONLY packages used by drivers
8
8
  // do NOT include anything related to gateway or bun or express
9
9
  // connection-tls does not want/need to load on browser and is loaded dynamically by Database
@@ -22,5 +22,5 @@ Object.defineProperty(exports, "SQLiteCloudRow", { enumerable: true, get: functi
22
22
  var utilities_1 = require("./drivers/utilities");
23
23
  Object.defineProperty(exports, "escapeSqlParameter", { enumerable: true, get: function () { return utilities_1.escapeSqlParameter; } });
24
24
  Object.defineProperty(exports, "prepareSql", { enumerable: true, get: function () { return utilities_1.prepareSql; } });
25
- Object.defineProperty(exports, "parseConnectionString", { enumerable: true, get: function () { return utilities_1.parseConnectionString; } });
25
+ Object.defineProperty(exports, "parseconnectionstring", { enumerable: true, get: function () { return utilities_1.parseconnectionstring; } });
26
26
  Object.defineProperty(exports, "validateConfiguration", { enumerable: true, get: function () { return utilities_1.validateConfiguration; } });