@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 +1 -0
- package/lib/drivers/connection-tls.d.ts +1 -1
- package/lib/drivers/connection-tls.js +2 -2
- package/lib/drivers/connection-ws.js +3 -3
- package/lib/drivers/connection.d.ts +11 -1
- package/lib/drivers/connection.js +58 -3
- package/lib/drivers/database.js +3 -3
- package/lib/drivers/types.d.ts +24 -17
- package/lib/drivers/utilities.d.ts +10 -2
- package/lib/drivers/utilities.js +96 -33
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -2
- package/lib/sqlitecloud.drivers.dev.js +8 -8
- package/lib/sqlitecloud.drivers.js +1 -1
- package/package.json +24 -23
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
|
|
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
|
|
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:
|
|
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
|
|
31
|
-
const gatewayUrl = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.
|
|
32
|
-
this.socket = (0, socket_io_client_1.io)(gatewayUrl, { auth: { token:
|
|
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
|
|
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
|
|
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)({
|
|
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.
|
|
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;
|
package/lib/drivers/database.js
CHANGED
|
@@ -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' ? {
|
|
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.
|
|
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 ?
|
|
213
|
+
callback === null || callback === void 0 ? void 0 : callback.call(context || this, null, context ? context : results);
|
|
214
214
|
}
|
|
215
215
|
});
|
|
216
216
|
}
|
package/lib/drivers/types.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
|
|
15
|
-
/** User name is required unless
|
|
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
|
-
|
|
21
|
-
/**
|
|
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
|
-
|
|
39
|
+
create?: boolean;
|
|
33
40
|
/** Database will be created in memory */
|
|
34
|
-
|
|
41
|
+
memory?: boolean;
|
|
35
42
|
compression?: boolean;
|
|
36
43
|
/** Request for immediate responses from the server node without waiting for linerizability guarantees */
|
|
37
|
-
|
|
44
|
+
non_linearizable?: boolean;
|
|
38
45
|
/** Server should send BLOB columns */
|
|
39
|
-
|
|
46
|
+
noblob?: boolean;
|
|
40
47
|
/** Do not send columns with more than max_data bytes */
|
|
41
|
-
|
|
48
|
+
maxdata?: number;
|
|
42
49
|
/** Server should chunk responses with more than maxRows */
|
|
43
|
-
|
|
50
|
+
maxrows?: number;
|
|
44
51
|
/** Server should limit total number of rows in a set to maxRowset */
|
|
45
|
-
|
|
52
|
+
maxrowset?: number;
|
|
46
53
|
/** Custom options and configurations for tls socket, eg: additional certificates */
|
|
47
|
-
|
|
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
|
-
|
|
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
|
-
|
|
58
|
+
gatewayurl?: string;
|
|
52
59
|
/** Optional identifier used for verbose logging */
|
|
53
|
-
|
|
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
|
-
/**
|
|
29
|
-
|
|
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;
|
package/lib/drivers/utilities.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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.
|
|
48
|
-
commands += 'SET CLIENT KEY
|
|
50
|
+
if (config.zerotext) {
|
|
51
|
+
commands += 'SET CLIENT KEY ZEROTEXT TO 1; ';
|
|
49
52
|
}
|
|
50
|
-
if (config.
|
|
53
|
+
if (config.noblob) {
|
|
51
54
|
commands += 'SET CLIENT KEY NOBLOB TO 1; ';
|
|
52
55
|
}
|
|
53
|
-
if (config.
|
|
54
|
-
commands += `SET CLIENT KEY MAXDATA TO ${config.
|
|
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.
|
|
57
|
-
commands += `SET CLIENT KEY
|
|
62
|
+
if (config.maxrowset) {
|
|
63
|
+
commands += `SET CLIENT KEY MAXROWSET TO ${config.maxrowset}; `;
|
|
58
64
|
}
|
|
59
|
-
|
|
60
|
-
|
|
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.
|
|
158
|
-
config = Object.assign(Object.assign(Object.assign({}, config),
|
|
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.
|
|
200
|
+
config.clientid || (config.clientid = 'SQLiteCloud');
|
|
165
201
|
config.verbose = parseBoolean(config.verbose);
|
|
166
|
-
config.
|
|
202
|
+
config.noblob = parseBoolean(config.noblob);
|
|
167
203
|
config.compression = parseBoolean(config.compression);
|
|
168
|
-
config.
|
|
169
|
-
config.
|
|
204
|
+
config.create = parseBoolean(config.create);
|
|
205
|
+
config.non_linearizable = parseBoolean(config.non_linearizable);
|
|
170
206
|
config.insecure = parseBoolean(config.insecure);
|
|
171
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
/**
|
|
184
|
-
|
|
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 =
|
|
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: ${
|
|
260
|
+
throw new types_1.SQLiteCloudError(`Invalid connection string: ${connectionstring}`);
|
|
206
261
|
}
|
|
207
262
|
}
|
|
208
|
-
exports.
|
|
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,
|
|
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.
|
|
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, "
|
|
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; } });
|