@sqlitecloud/drivers 0.0.40 → 0.0.56
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 -1
- package/lib/drivers/connection-tls.d.ts +29 -0
- package/lib/drivers/connection-tls.js +220 -0
- package/lib/{transport-ws.d.ts → drivers/connection-ws.d.ts} +5 -6
- package/lib/{transport-ws.js → drivers/connection-ws.js} +11 -8
- package/lib/drivers/connection.d.ts +34 -0
- package/lib/drivers/connection.js +81 -0
- package/lib/{database.js → drivers/database.js} +68 -19
- package/lib/drivers/protocol.d.ts +51 -0
- package/lib/drivers/protocol.js +291 -0
- package/lib/drivers/queue.d.ts +12 -0
- package/lib/drivers/queue.js +42 -0
- package/lib/{types.d.ts → drivers/types.d.ts} +4 -2
- package/lib/{utilities.d.ts → drivers/utilities.d.ts} +6 -0
- package/lib/{utilities.js → drivers/utilities.js} +53 -3
- package/lib/index.d.ts +6 -8
- package/lib/index.js +13 -13
- package/lib/sqlitecloud.drivers.dev.js +619 -0
- package/lib/sqlitecloud.drivers.js +1 -0
- package/package.json +12 -3
- package/lib/connection.d.ts +0 -59
- package/lib/connection.js +0 -230
- package/lib/sqlitecloud.v0.0.40.dev.js +0 -597
- package/lib/sqlitecloud.v0.0.40.js +0 -1
- package/lib/transport-tls.d.ts +0 -25
- package/lib/transport-tls.js +0 -465
- /package/lib/{database.d.ts → drivers/database.d.ts} +0 -0
- /package/lib/{rowset.d.ts → drivers/rowset.d.ts} +0 -0
- /package/lib/{rowset.js → drivers/rowset.js} +0 -0
- /package/lib/{statement.d.ts → drivers/statement.d.ts} +0 -0
- /package/lib/{statement.js → drivers/statement.js} +0 -0
- /package/lib/{types.js → drivers/types.js} +0 -0
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ npm install @sqlitecloud/drivers
|
|
|
17
17
|
```ts
|
|
18
18
|
import { Database } from '@sqlitecloud/drivers'
|
|
19
19
|
|
|
20
|
-
let database = new Database('sqlitecloud://user:password@xxx.sqlite.cloud:8860/chinook.
|
|
20
|
+
let database = new Database('sqlitecloud://user:password@xxx.sqlite.cloud:8860/chinook.sqlite')
|
|
21
21
|
|
|
22
22
|
let name = 'Breaking The Rules'
|
|
23
23
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connection-tls.ts - connection via tls socket and sqlitecloud protocol
|
|
3
|
+
*/
|
|
4
|
+
import { type SQLiteCloudConfig, type ErrorCallback, type ResultsCallback } from './types';
|
|
5
|
+
import { SQLiteCloudConnection } from './connection';
|
|
6
|
+
/**
|
|
7
|
+
* Implementation of SQLiteCloudConnection that connects to the database using specific Bun APIs
|
|
8
|
+
* that connect to native sockets or tls sockets and communicates via raw, binary protocol.
|
|
9
|
+
*/
|
|
10
|
+
export declare class SQLiteCloudTlsConnection extends SQLiteCloudConnection {
|
|
11
|
+
/** Currently opened bun socket used to communicated with SQLiteCloud server */
|
|
12
|
+
private socket?;
|
|
13
|
+
/** True if connection is open */
|
|
14
|
+
get connected(): boolean;
|
|
15
|
+
connectTransport(config: SQLiteCloudConfig, callback?: ErrorCallback): this;
|
|
16
|
+
/** Will send a command immediately (no queueing), return the rowset/result or throw an error */
|
|
17
|
+
transportCommands(commands: string, callback?: ResultsCallback): this;
|
|
18
|
+
private buffer;
|
|
19
|
+
private startedOn;
|
|
20
|
+
private executingCommands?;
|
|
21
|
+
private processCallback?;
|
|
22
|
+
/** Handles data received in response to an outbound command sent by processCommands */
|
|
23
|
+
private processCommandsData;
|
|
24
|
+
/** Completes a transaction initiated by processCommands */
|
|
25
|
+
private processCommandsFinish;
|
|
26
|
+
/** Disconnect immediately, release connection, no events. */
|
|
27
|
+
close(): this;
|
|
28
|
+
}
|
|
29
|
+
export default SQLiteCloudTlsConnection;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* connection-tls.ts - connection via tls socket and sqlitecloud protocol
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
22
|
+
if (mod && mod.__esModule) return mod;
|
|
23
|
+
var result = {};
|
|
24
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
25
|
+
__setModuleDefault(result, mod);
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.SQLiteCloudTlsConnection = void 0;
|
|
30
|
+
const types_1 = require("./types");
|
|
31
|
+
const connection_1 = require("./connection");
|
|
32
|
+
const utilities_1 = require("./utilities");
|
|
33
|
+
const protocol_1 = require("./protocol");
|
|
34
|
+
const tls = __importStar(require("tls"));
|
|
35
|
+
/**
|
|
36
|
+
* Implementation of SQLiteCloudConnection that connects to the database using specific Bun APIs
|
|
37
|
+
* that connect to native sockets or tls sockets and communicates via raw, binary protocol.
|
|
38
|
+
*/
|
|
39
|
+
class SQLiteCloudTlsConnection extends connection_1.SQLiteCloudConnection {
|
|
40
|
+
constructor() {
|
|
41
|
+
super(...arguments);
|
|
42
|
+
// processCommands sets up empty buffers, results callback then send the command to the server via socket.write
|
|
43
|
+
// onData is called when data is received, it will process the data until all data is retrieved for a response
|
|
44
|
+
// when response is complete or there's an error, finish is called to call the results callback set by processCommands...
|
|
45
|
+
// buffer to accumulate incoming data until an whole command is received and can be parsed
|
|
46
|
+
this.buffer = Buffer.alloc(0);
|
|
47
|
+
this.startedOn = new Date();
|
|
48
|
+
}
|
|
49
|
+
/** True if connection is open */
|
|
50
|
+
get connected() {
|
|
51
|
+
return !!this.socket;
|
|
52
|
+
}
|
|
53
|
+
/* Opens a connection with the server and sends the initialization commands. Will throw in case of errors. */
|
|
54
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
55
|
+
connectTransport(config, callback) {
|
|
56
|
+
console.assert(!this.connected, 'SQLiteCloudTlsConnection.connect - connection already established');
|
|
57
|
+
if (this.config.verbose) {
|
|
58
|
+
console.debug(`-> connecting ${config === null || config === void 0 ? void 0 : config.host}:${config === null || config === void 0 ? void 0 : config.port}`);
|
|
59
|
+
}
|
|
60
|
+
this.config = config;
|
|
61
|
+
const initializationCommands = (0, utilities_1.getInitializationCommands)(config);
|
|
62
|
+
// connect to plain socket, without encryption, only if insecure parameter specified
|
|
63
|
+
// this option is mainly for testing purposes and is not available on production nodes
|
|
64
|
+
// which would need to connect using tls and proper certificates as per code below
|
|
65
|
+
const connectionOptions = {
|
|
66
|
+
host: config.host,
|
|
67
|
+
port: config.port,
|
|
68
|
+
rejectUnauthorized: false,
|
|
69
|
+
// Server name for the SNI (Server Name Indication) TLS extension.
|
|
70
|
+
// https://r2.nodejs.org/docs/v6.11.4/api/tls.html#tls_class_tls_tlssocket
|
|
71
|
+
servername: config.host
|
|
72
|
+
};
|
|
73
|
+
this.socket = tls.connect(connectionOptions, () => {
|
|
74
|
+
var _a;
|
|
75
|
+
if (this.config.verbose) {
|
|
76
|
+
console.debug(`SQLiteCloudTlsConnection - connected to ${this.config.host}, authorized: ${(_a = this.socket) === null || _a === void 0 ? void 0 : _a.authorized}`);
|
|
77
|
+
}
|
|
78
|
+
this.transportCommands(initializationCommands, error => {
|
|
79
|
+
if (this.config.verbose) {
|
|
80
|
+
console.debug(`SQLiteCloudTlsConnection - initialized connection`);
|
|
81
|
+
}
|
|
82
|
+
callback === null || callback === void 0 ? void 0 : callback.call(this, error);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
this.socket.on('data', data => {
|
|
86
|
+
this.processCommandsData(data);
|
|
87
|
+
});
|
|
88
|
+
this.socket.on('error', error => {
|
|
89
|
+
this.close();
|
|
90
|
+
this.processCommandsFinish(new types_1.SQLiteCloudError('Connection error', { errorCode: 'ERR_CONNECTION_ERROR', cause: error }));
|
|
91
|
+
});
|
|
92
|
+
this.socket.on('end', () => {
|
|
93
|
+
this.close();
|
|
94
|
+
if (this.processCallback)
|
|
95
|
+
this.processCommandsFinish(new types_1.SQLiteCloudError('Server ended the connection', { errorCode: 'ERR_CONNECTION_ENDED' }));
|
|
96
|
+
});
|
|
97
|
+
this.socket.on('close', () => {
|
|
98
|
+
this.close();
|
|
99
|
+
this.processCommandsFinish(new types_1.SQLiteCloudError('Connection closed', { errorCode: 'ERR_CONNECTION_CLOSED' }));
|
|
100
|
+
});
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
/** Will send a command immediately (no queueing), return the rowset/result or throw an error */
|
|
104
|
+
transportCommands(commands, callback) {
|
|
105
|
+
var _a, _b, _c, _d, _e;
|
|
106
|
+
// connection needs to be established?
|
|
107
|
+
if (!this.socket) {
|
|
108
|
+
callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Connection not established', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' }));
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
// reset buffer and rowset chunks, define response callback
|
|
112
|
+
this.buffer = Buffer.alloc(0);
|
|
113
|
+
this.startedOn = new Date();
|
|
114
|
+
this.processCallback = callback;
|
|
115
|
+
this.executingCommands = commands;
|
|
116
|
+
// compose commands following SCPC protocol
|
|
117
|
+
const formattedCommands = (0, protocol_1.formatCommand)(commands);
|
|
118
|
+
if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.verbose) {
|
|
119
|
+
console.debug(`-> ${formattedCommands}`);
|
|
120
|
+
}
|
|
121
|
+
const timeoutMs = (_c = (_b = this.config) === null || _b === void 0 ? void 0 : _b.timeout) !== null && _c !== void 0 ? _c : 0;
|
|
122
|
+
if (timeoutMs > 0) {
|
|
123
|
+
const timeout = setTimeout(() => {
|
|
124
|
+
var _a;
|
|
125
|
+
callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Connection timeout out', { errorCode: 'ERR_CONNECTION_TIMEOUT' }));
|
|
126
|
+
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
127
|
+
this.socket = undefined;
|
|
128
|
+
}, timeoutMs);
|
|
129
|
+
(_d = this.socket) === null || _d === void 0 ? void 0 : _d.write(formattedCommands, 'utf-8', () => {
|
|
130
|
+
clearTimeout(timeout); // Clear the timeout on successful write
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
(_e = this.socket) === null || _e === void 0 ? void 0 : _e.write(formattedCommands, 'utf-8');
|
|
135
|
+
}
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
/** Handles data received in response to an outbound command sent by processCommands */
|
|
139
|
+
processCommandsData(data) {
|
|
140
|
+
var _a, _b, _c, _d, _e, _f;
|
|
141
|
+
try {
|
|
142
|
+
// append data to buffer as it arrives
|
|
143
|
+
if (data.length && data.length > 0) {
|
|
144
|
+
this.buffer = Buffer.concat([this.buffer, data]);
|
|
145
|
+
}
|
|
146
|
+
let dataType = (_a = this.buffer) === null || _a === void 0 ? void 0 : _a.subarray(0, 1).toString();
|
|
147
|
+
if ((0, protocol_1.hasCommandLength)(dataType)) {
|
|
148
|
+
const commandLength = (0, protocol_1.parseCommandLength)(this.buffer);
|
|
149
|
+
const hasReceivedEntireCommand = this.buffer.length - this.buffer.indexOf(' ') - 1 >= commandLength ? true : false;
|
|
150
|
+
if (hasReceivedEntireCommand) {
|
|
151
|
+
if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.verbose) {
|
|
152
|
+
let bufferString = this.buffer.toString('utf8');
|
|
153
|
+
if (bufferString.length > 1000) {
|
|
154
|
+
bufferString = bufferString.substring(0, 100) + '...' + bufferString.substring(bufferString.length - 40);
|
|
155
|
+
}
|
|
156
|
+
const elapsedMs = new Date().getTime() - this.startedOn.getTime();
|
|
157
|
+
console.debug(`<- ${bufferString} (${elapsedMs}ms)`);
|
|
158
|
+
}
|
|
159
|
+
// need to decompress this buffer before decoding?
|
|
160
|
+
if (dataType === protocol_1.CMD_COMPRESSED) {
|
|
161
|
+
;
|
|
162
|
+
({ buffer: this.buffer, dataType } = (0, protocol_1.decompressBuffer)(this.buffer));
|
|
163
|
+
}
|
|
164
|
+
if (dataType !== protocol_1.CMD_ROWSET_CHUNK) {
|
|
165
|
+
const { data } = (0, protocol_1.popData)(this.buffer);
|
|
166
|
+
(_c = this.processCommandsFinish) === null || _c === void 0 ? void 0 : _c.call(this, null, data);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// check if rowset received the ending chunk in which case it can be unpacked
|
|
170
|
+
if ((0, protocol_1.bufferEndsWith)(this.buffer, protocol_1.ROWSET_CHUNKS_END)) {
|
|
171
|
+
const parsedData = (0, protocol_1.parseRowsetChunks)([this.buffer]);
|
|
172
|
+
(_d = this.processCommandsFinish) === null || _d === void 0 ? void 0 : _d.call(this, null, parsedData);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
// command with no explicit len so make sure that the final character is a space
|
|
179
|
+
const lastChar = this.buffer.subarray(this.buffer.length - 1, this.buffer.length).toString('utf8');
|
|
180
|
+
if (lastChar == ' ') {
|
|
181
|
+
const { data } = (0, protocol_1.popData)(this.buffer);
|
|
182
|
+
(_e = this.processCommandsFinish) === null || _e === void 0 ? void 0 : _e.call(this, null, data);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
console.assert(error instanceof Error);
|
|
188
|
+
if (error instanceof Error) {
|
|
189
|
+
(_f = this.processCommandsFinish) === null || _f === void 0 ? void 0 : _f.call(this, error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/** Completes a transaction initiated by processCommands */
|
|
194
|
+
processCommandsFinish(error, result) {
|
|
195
|
+
if (error) {
|
|
196
|
+
if (this.processCallback) {
|
|
197
|
+
console.error('processCommandsFinish - error', error);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
console.warn('processCommandsFinish - error with no registered callback', error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (this.processCallback) {
|
|
204
|
+
this.processCallback(error, result);
|
|
205
|
+
// this.processCallback = undefined
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** Disconnect immediately, release connection, no events. */
|
|
209
|
+
close() {
|
|
210
|
+
if (this.socket) {
|
|
211
|
+
this.socket.removeAllListeners();
|
|
212
|
+
this.socket.destroy();
|
|
213
|
+
this.socket = undefined;
|
|
214
|
+
}
|
|
215
|
+
this.operations.clear();
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
exports.SQLiteCloudTlsConnection = SQLiteCloudTlsConnection;
|
|
220
|
+
exports.default = SQLiteCloudTlsConnection;
|
|
@@ -2,23 +2,22 @@
|
|
|
2
2
|
* transport-ws.ts - handles low level communication with sqlitecloud server via socket.io websocket
|
|
3
3
|
*/
|
|
4
4
|
import { SQLiteCloudConfig, ErrorCallback, ResultsCallback } from './types';
|
|
5
|
-
import {
|
|
5
|
+
import { SQLiteCloudConnection } from './connection';
|
|
6
6
|
/**
|
|
7
7
|
* Implementation of TransportConnection that connects to the database indirectly
|
|
8
8
|
* via SQLite Cloud Gateway, a socket.io based deamon that responds to sql query
|
|
9
9
|
* requests by returning results and rowsets in json format. The gateway handles
|
|
10
10
|
* connect, disconnect, retries, order of operations, timeouts, etc.
|
|
11
11
|
*/
|
|
12
|
-
export declare class
|
|
13
|
-
/** Configuration passed to connect */
|
|
14
|
-
private config?;
|
|
12
|
+
export declare class SQLiteCloudWebsocketConnection extends SQLiteCloudConnection {
|
|
15
13
|
/** Socket.io used to communicated with SQLiteCloud server */
|
|
16
14
|
private socket?;
|
|
17
15
|
/** True if connection is open */
|
|
18
16
|
get connected(): boolean;
|
|
19
|
-
|
|
17
|
+
connectTransport(config: SQLiteCloudConfig, callback?: ErrorCallback): this;
|
|
20
18
|
/** Will send a command immediately (no queueing), return the rowset/result or throw an error */
|
|
21
|
-
|
|
19
|
+
transportCommands(commands: string, callback?: ResultsCallback): this;
|
|
22
20
|
/** Disconnect socket.io from server */
|
|
23
21
|
close(): this;
|
|
24
22
|
}
|
|
23
|
+
export default SQLiteCloudWebsocketConnection;
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* transport-ws.ts - handles low level communication with sqlitecloud server via socket.io websocket
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.SQLiteCloudWebsocketConnection = void 0;
|
|
7
7
|
const types_1 = require("./types");
|
|
8
8
|
const rowset_1 = require("./rowset");
|
|
9
|
+
const connection_1 = require("./connection");
|
|
9
10
|
const socket_io_client_1 = require("socket.io-client");
|
|
10
11
|
/**
|
|
11
12
|
* Implementation of TransportConnection that connects to the database indirectly
|
|
@@ -13,13 +14,13 @@ const socket_io_client_1 = require("socket.io-client");
|
|
|
13
14
|
* requests by returning results and rowsets in json format. The gateway handles
|
|
14
15
|
* connect, disconnect, retries, order of operations, timeouts, etc.
|
|
15
16
|
*/
|
|
16
|
-
class
|
|
17
|
+
class SQLiteCloudWebsocketConnection extends connection_1.SQLiteCloudConnection {
|
|
17
18
|
/** True if connection is open */
|
|
18
19
|
get connected() {
|
|
19
20
|
return !!this.socket;
|
|
20
21
|
}
|
|
21
22
|
/* Opens a connection with the server and sends the initialization commands. Will throw in case of errors. */
|
|
22
|
-
|
|
23
|
+
connectTransport(config, callback) {
|
|
23
24
|
var _a;
|
|
24
25
|
try {
|
|
25
26
|
// connection established while we were waiting in line?
|
|
@@ -27,7 +28,7 @@ class WebSocketTransport {
|
|
|
27
28
|
if (!this.socket) {
|
|
28
29
|
this.config = config;
|
|
29
30
|
const connectionString = this.config.connectionString;
|
|
30
|
-
const gatewayUrl = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.gatewayUrl) ||
|
|
31
|
+
const gatewayUrl = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.gatewayUrl) || `${this.config.host === 'localhost' ? 'ws' : 'wss'}://${this.config.host}:4000`;
|
|
31
32
|
this.socket = (0, socket_io_client_1.io)(gatewayUrl, { auth: { token: connectionString } });
|
|
32
33
|
}
|
|
33
34
|
callback === null || callback === void 0 ? void 0 : callback.call(this, null);
|
|
@@ -38,7 +39,7 @@ class WebSocketTransport {
|
|
|
38
39
|
return this;
|
|
39
40
|
}
|
|
40
41
|
/** Will send a command immediately (no queueing), return the rowset/result or throw an error */
|
|
41
|
-
|
|
42
|
+
transportCommands(commands, callback) {
|
|
42
43
|
// connection needs to be established?
|
|
43
44
|
if (!this.socket) {
|
|
44
45
|
callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Connection not established', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' }));
|
|
@@ -53,7 +54,7 @@ class WebSocketTransport {
|
|
|
53
54
|
const { data, metadata } = response;
|
|
54
55
|
if (data && metadata) {
|
|
55
56
|
if (metadata.numberOfRows !== undefined && metadata.numberOfColumns !== undefined && metadata.columns !== undefined) {
|
|
56
|
-
console.assert(Array.isArray(data), 'SQLiteCloudWebsocketConnection.
|
|
57
|
+
console.assert(Array.isArray(data), 'SQLiteCloudWebsocketConnection.transportCommands - data is not an array');
|
|
57
58
|
// we can recreate a SQLiteCloudRowset from the response which we know to be an array of arrays
|
|
58
59
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
59
60
|
const rowset = new rowset_1.SQLiteCloudRowset(metadata, data.flat());
|
|
@@ -69,13 +70,15 @@ class WebSocketTransport {
|
|
|
69
70
|
/** Disconnect socket.io from server */
|
|
70
71
|
close() {
|
|
71
72
|
var _a;
|
|
72
|
-
console.assert(this.socket !== null, '
|
|
73
|
+
console.assert(this.socket !== null, 'SQLiteCloudWebsocketConnection.close - connection already closed');
|
|
73
74
|
if (this.socket) {
|
|
74
75
|
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.close();
|
|
75
76
|
this.socket = undefined;
|
|
76
77
|
}
|
|
78
|
+
this.operations.clear();
|
|
77
79
|
this.socket = undefined;
|
|
78
80
|
return this;
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
|
-
exports.
|
|
83
|
+
exports.SQLiteCloudWebsocketConnection = SQLiteCloudWebsocketConnection;
|
|
84
|
+
exports.default = SQLiteCloudWebsocketConnection;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connection.ts - base abstract class for sqlitecloud server connections
|
|
3
|
+
*/
|
|
4
|
+
import { SQLiteCloudConfig, ErrorCallback, ResultsCallback } from './types';
|
|
5
|
+
import { OperationsQueue } from './queue';
|
|
6
|
+
/**
|
|
7
|
+
* Base class for SQLiteCloudConnection handles basics and defines methods.
|
|
8
|
+
* Actual connection management and communication with the server in concrete classes.
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class SQLiteCloudConnection {
|
|
11
|
+
/** Parse and validate provided connectionString or configuration */
|
|
12
|
+
constructor(config: SQLiteCloudConfig | string, callback?: ErrorCallback);
|
|
13
|
+
/** Configuration passed by client or extracted from connection string */
|
|
14
|
+
protected config: SQLiteCloudConfig;
|
|
15
|
+
/** Returns the connection's configuration */
|
|
16
|
+
getConfig(): SQLiteCloudConfig;
|
|
17
|
+
/** Operations are serialized by waiting an any pending promises */
|
|
18
|
+
protected operations: OperationsQueue;
|
|
19
|
+
/** Connect will establish a tls or websocket transport to the server based on configuration and environment */
|
|
20
|
+
protected connect(callback?: ErrorCallback): this;
|
|
21
|
+
protected abstract connectTransport(config: SQLiteCloudConfig, callback?: ErrorCallback): this;
|
|
22
|
+
/** Send a command, return the rowset/result or throw an error */
|
|
23
|
+
protected abstract transportCommands(commands: string, callback?: ResultsCallback): this;
|
|
24
|
+
/** Will log to console if verbose mode is enabled */
|
|
25
|
+
protected log(message: string, ...optionalParams: any[]): void;
|
|
26
|
+
/** Returns true if connection is open */
|
|
27
|
+
abstract get connected(): boolean;
|
|
28
|
+
/** Enable verbose logging for debug purposes */
|
|
29
|
+
verbose(): void;
|
|
30
|
+
/** Will enquee a command to be executed and callback with the resulting rowset/result/error */
|
|
31
|
+
sendCommands(commands: string, callback?: ResultsCallback): this;
|
|
32
|
+
/** Disconnect from server, release transport. */
|
|
33
|
+
abstract close(): this;
|
|
34
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* connection.ts - base abstract class for sqlitecloud server connections
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SQLiteCloudConnection = void 0;
|
|
7
|
+
const types_1 = require("./types");
|
|
8
|
+
const utilities_1 = require("./utilities");
|
|
9
|
+
const queue_1 = require("./queue");
|
|
10
|
+
const utilities_2 = require("./utilities");
|
|
11
|
+
/**
|
|
12
|
+
* Base class for SQLiteCloudConnection handles basics and defines methods.
|
|
13
|
+
* Actual connection management and communication with the server in concrete classes.
|
|
14
|
+
*/
|
|
15
|
+
class SQLiteCloudConnection {
|
|
16
|
+
/** Parse and validate provided connectionString or configuration */
|
|
17
|
+
constructor(config, callback) {
|
|
18
|
+
/** Operations are serialized by waiting an any pending promises */
|
|
19
|
+
this.operations = new queue_1.OperationsQueue();
|
|
20
|
+
if (typeof config === 'string') {
|
|
21
|
+
this.config = (0, utilities_1.validateConfiguration)({ connectionString: config });
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
this.config = (0, utilities_1.validateConfiguration)(config);
|
|
25
|
+
}
|
|
26
|
+
// connect transport layer to server
|
|
27
|
+
this.connect(callback);
|
|
28
|
+
}
|
|
29
|
+
/** Returns the connection's configuration */
|
|
30
|
+
getConfig() {
|
|
31
|
+
return Object.assign({}, this.config);
|
|
32
|
+
}
|
|
33
|
+
//
|
|
34
|
+
// internal methods (some are implemented in concrete classes using different transport layers)
|
|
35
|
+
//
|
|
36
|
+
/** Connect will establish a tls or websocket transport to the server based on configuration and environment */
|
|
37
|
+
connect(callback) {
|
|
38
|
+
this.operations.enqueue(done => {
|
|
39
|
+
this.connectTransport(this.config, error => {
|
|
40
|
+
if (error) {
|
|
41
|
+
console.error(`SQLiteCloudConnection.connect - error connecting ${this.config.host}:${this.config.port} ${error.toString()}`, error);
|
|
42
|
+
this.close();
|
|
43
|
+
}
|
|
44
|
+
if (callback) {
|
|
45
|
+
callback.call(this, error || null);
|
|
46
|
+
}
|
|
47
|
+
done(error);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/** Will log to console if verbose mode is enabled */
|
|
53
|
+
log(message, ...optionalParams) {
|
|
54
|
+
if (this.config.verbose) {
|
|
55
|
+
message = (0, utilities_2.anonimizeCommand)(message);
|
|
56
|
+
console.log(`${new Date().toISOString()} ${this.config.clientId}: ${message}`, ...optionalParams);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Enable verbose logging for debug purposes */
|
|
60
|
+
verbose() {
|
|
61
|
+
this.config.verbose = true;
|
|
62
|
+
}
|
|
63
|
+
/** Will enquee a command to be executed and callback with the resulting rowset/result/error */
|
|
64
|
+
sendCommands(commands, callback) {
|
|
65
|
+
this.operations.enqueue(done => {
|
|
66
|
+
if (!this.connected) {
|
|
67
|
+
const error = new types_1.SQLiteCloudError('Connection not established', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' });
|
|
68
|
+
callback === null || callback === void 0 ? void 0 : callback.call(this, error);
|
|
69
|
+
done(error);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.transportCommands(commands, (error, result) => {
|
|
73
|
+
callback === null || callback === void 0 ? void 0 : callback.call(this, error, result);
|
|
74
|
+
done(error);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.SQLiteCloudConnection = SQLiteCloudConnection;
|
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
//
|
|
3
3
|
// database.ts - database driver api, implements and extends sqlite3
|
|
4
4
|
//
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
22
|
+
if (mod && mod.__esModule) return mod;
|
|
23
|
+
var result = {};
|
|
24
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
25
|
+
__setModuleDefault(result, mod);
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
5
28
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
29
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
30
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -16,18 +39,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
16
39
|
};
|
|
17
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
41
|
exports.Database = void 0;
|
|
19
|
-
// Trying as much as possible to be a drop-in replacement for SQLite3 API
|
|
20
|
-
// https://github.com/TryGhost/node-sqlite3/wiki/API
|
|
21
|
-
// https://github.com/TryGhost/node-sqlite3
|
|
22
|
-
// https://github.com/TryGhost/node-sqlite3/blob/master/lib/sqlite3.d.ts
|
|
23
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
24
|
-
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
|
25
|
-
const connection_1 = require("./connection");
|
|
26
42
|
const rowset_1 = require("./rowset");
|
|
27
43
|
const types_1 = require("./types");
|
|
28
44
|
const utilities_1 = require("./utilities");
|
|
29
45
|
const statement_1 = require("./statement");
|
|
30
46
|
const eventemitter3_1 = __importDefault(require("eventemitter3"));
|
|
47
|
+
const utilities_2 = require("./utilities");
|
|
31
48
|
// Uses eventemitter3 instead of node events for browser compatibility
|
|
32
49
|
// https://github.com/primus/eventemitter3
|
|
33
50
|
/**
|
|
@@ -50,29 +67,61 @@ class Database extends eventemitter3_1.default {
|
|
|
50
67
|
}
|
|
51
68
|
// mode is ignored for now
|
|
52
69
|
// opens first connection to the database automatically
|
|
53
|
-
this.getConnection(
|
|
70
|
+
this.getConnection((error, _connection) => {
|
|
71
|
+
if (callback) {
|
|
72
|
+
callback.call(this, error);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
54
75
|
}
|
|
55
76
|
//
|
|
56
77
|
// private methods
|
|
57
78
|
//
|
|
58
79
|
/** Returns first available connection from connection pool */
|
|
59
80
|
getConnection(callback) {
|
|
60
|
-
var _a;
|
|
81
|
+
var _a, _b, _c;
|
|
61
82
|
// TODO sqlitecloud-js / implement database connection pool #10
|
|
62
83
|
if (((_a = this.connections) === null || _a === void 0 ? void 0 : _a.length) > 0) {
|
|
63
84
|
callback === null || callback === void 0 ? void 0 : callback.call(this, null, this.connections[0]);
|
|
64
85
|
}
|
|
65
86
|
else {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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);
|
|
89
|
+
if (useWebsocket) {
|
|
90
|
+
// socket.io transport works in both node.js and browser environments and connects via SQLite Cloud Gateway
|
|
91
|
+
Promise.resolve().then(() => __importStar(require('./connection-ws'))).then(module => {
|
|
92
|
+
this.connections.push(new module.default(this.config, error => {
|
|
93
|
+
if (error) {
|
|
94
|
+
this.handleError(this.connections[0], error, callback);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
console.assert;
|
|
98
|
+
callback === null || callback === void 0 ? void 0 : callback.call(this, null, this.connections[0]);
|
|
99
|
+
this.emitEvent('open');
|
|
100
|
+
}
|
|
101
|
+
}));
|
|
102
|
+
})
|
|
103
|
+
.catch(error => {
|
|
104
|
+
this.handleError(null, error, callback);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// tls sockets work only in node.js environments
|
|
109
|
+
Promise.resolve().then(() => __importStar(require('./connection-tls'))).then(module => {
|
|
110
|
+
this.connections.push(new module.default(this.config, error => {
|
|
111
|
+
if (error) {
|
|
112
|
+
this.handleError(this.connections[0], error, callback);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
console.assert;
|
|
116
|
+
callback === null || callback === void 0 ? void 0 : callback.call(this, null, this.connections[0]);
|
|
117
|
+
this.emitEvent('open');
|
|
118
|
+
}
|
|
119
|
+
}));
|
|
120
|
+
})
|
|
121
|
+
.catch(error => {
|
|
122
|
+
this.handleError(null, error, callback);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
76
125
|
}
|
|
77
126
|
}
|
|
78
127
|
/** Handles an error by closing the connection, calling the callback and/or emitting an error event */
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { type SQLCloudRowsetMetadata, type SQLiteCloudDataTypes } from './types';
|
|
3
|
+
import { SQLiteCloudRowset } from './rowset';
|
|
4
|
+
export declare const CMD_STRING = "+";
|
|
5
|
+
export declare const CMD_ZEROSTRING = "!";
|
|
6
|
+
export declare const CMD_ERROR = "-";
|
|
7
|
+
export declare const CMD_INT = ":";
|
|
8
|
+
export declare const CMD_FLOAT = ",";
|
|
9
|
+
export declare const CMD_ROWSET = "*";
|
|
10
|
+
export declare const CMD_ROWSET_CHUNK = "/";
|
|
11
|
+
export declare const CMD_JSON = "#";
|
|
12
|
+
export declare const CMD_NULL = "_";
|
|
13
|
+
export declare const CMD_BLOB = "$";
|
|
14
|
+
export declare const CMD_COMPRESSED = "%";
|
|
15
|
+
export declare const CMD_COMMAND = "^";
|
|
16
|
+
export declare const CMD_ARRAY = "=";
|
|
17
|
+
export declare const ROWSET_CHUNKS_END = "/6 0 0 0 ";
|
|
18
|
+
/** Analyze first character to check if corresponding data type has LEN */
|
|
19
|
+
export declare function hasCommandLength(firstCharacter: string): boolean;
|
|
20
|
+
/** Analyze a command with explict LEN and extract it */
|
|
21
|
+
export declare function parseCommandLength(data: Buffer): number;
|
|
22
|
+
/** Receive a compressed buffer, decompress with lz4, return buffer and datatype */
|
|
23
|
+
export declare function decompressBuffer(buffer: Buffer): {
|
|
24
|
+
buffer: Buffer;
|
|
25
|
+
dataType: string;
|
|
26
|
+
};
|
|
27
|
+
/** Parse error message or extended error message */
|
|
28
|
+
export declare function parseError(buffer: Buffer, spaceIndex: number): never;
|
|
29
|
+
/** Parse an array of items (each of which will be parsed by type separately) */
|
|
30
|
+
export declare function parseArray(buffer: Buffer, spaceIndex: number): SQLiteCloudDataTypes[];
|
|
31
|
+
/** Parse header in a rowset or chunk of a chunked rowset */
|
|
32
|
+
export declare function parseRowsetHeader(buffer: Buffer): {
|
|
33
|
+
index: number;
|
|
34
|
+
metadata: SQLCloudRowsetMetadata;
|
|
35
|
+
fwdBuffer: Buffer;
|
|
36
|
+
};
|
|
37
|
+
export declare function bufferStartsWith(buffer: Buffer, prefix: string): boolean;
|
|
38
|
+
export declare function bufferEndsWith(buffer: Buffer, suffix: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Parse a chunk of a chunked rowset command, eg:
|
|
41
|
+
* *LEN 0:VERS NROWS NCOLS DATA
|
|
42
|
+
* @see https://github.com/sqlitecloud/sdk/blob/master/PROTOCOL.md#scsp-rowset-chunk
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseRowsetChunks(buffers: Buffer[]): SQLiteCloudRowset;
|
|
45
|
+
/** Parse command, extract its data, return the data and the buffer moved to the first byte after the command */
|
|
46
|
+
export declare function popData(buffer: Buffer): {
|
|
47
|
+
data: SQLiteCloudDataTypes | SQLiteCloudRowset;
|
|
48
|
+
fwdBuffer: Buffer;
|
|
49
|
+
};
|
|
50
|
+
/** Format a command to be sent via SCSP protocol */
|
|
51
|
+
export declare function formatCommand(command: string): string;
|