UNPKG

@sqlitecloud/drivers

Version:

SQLiteCloud drivers for Typescript/Javascript in edge, web and node clients

62 lines (51 loc) 803 kB
/* * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). * This devtool is neither made for production nor for readable output files. * It uses "eval()" calls to create a separate source file in the browser devtools. * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) * or disable the default devtool with "devtool: false". * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["sqlitecloud"] = factory(); else root["sqlitecloud"] = factory(); })(this, () => { return /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ /***/ "./lib/drivers/connection-tls.js": /*!***************************************!*\ !*** ./lib/drivers/connection-tls.js ***! \***************************************/ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { "use strict"; eval("\n/**\n * connection-tls.ts - connection via tls socket and sqlitecloud protocol\n */\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.SQLiteCloudTlsConnection = void 0;\nconst connection_1 = __webpack_require__(/*! ./connection */ \"./lib/drivers/connection.js\");\nconst protocol_1 = __webpack_require__(/*! ./protocol */ \"./lib/drivers/protocol.js\");\nconst types_1 = __webpack_require__(/*! ./types */ \"./lib/drivers/types.js\");\nconst utilities_1 = __webpack_require__(/*! ./utilities */ \"./lib/drivers/utilities.js\");\nconst safe_imports_1 = __webpack_require__(/*! ./safe-imports */ \"./lib/drivers/safe-imports.js\");\n// explicitly importing buffer library to allow cross-platform support by replacing it\n// In React Native: Metro resolves 'buffer' to '@craftzdog/react-native-buffer' via package.json react-native field\n// In Web/Node: Uses standard buffer package\nconst Buffer = (0, safe_imports_1.getSafeBuffer)();\n// In React Native: Metro resolves 'tls' to 'react-native-tcp-socket' via package.json react-native field\n// In Node: Uses native tls module\n// In Browser: Returns null (browser field sets tls to false)\nconst tls = (0, safe_imports_1.getSafeTLS)();\n/**\n * Implementation of SQLiteCloudConnection that connects to the database using specific tls APIs\n * that connect to native sockets or tls sockets and communicates via raw, binary protocol.\n */\nclass SQLiteCloudTlsConnection extends connection_1.SQLiteCloudConnection {\n constructor() {\n super(...arguments);\n // processCommands sets up empty buffers, results callback then send the command to the server via socket.write\n // onData is called when data is received, it will process the data until all data is retrieved for a response\n // when response is complete or there's an error, finish is called to call the results callback set by processCommands...\n // buffer to accumulate incoming data until an whole command is received and can be parsed\n this.buffer = Buffer.alloc(0);\n this.startedOn = new Date();\n this.pendingChunks = [];\n }\n /** True if connection is open */\n get connected() {\n return !!this.socket;\n }\n /* Opens a connection with the server and sends the initialization commands. Will throw in case of errors. */\n connectTransport(config, callback) {\n console.assert(!this.connected, 'SQLiteCloudTlsConnection.connect - connection already established');\n // Check if tls is available (it's null in browser contexts)\n if (!tls) {\n const error = new types_1.SQLiteCloudError('TLS connections are not available in this environment. Use WebSocket connections instead by setting usewebsocket: true in your configuration.', { errorCode: 'ERR_TLS_NOT_AVAILABLE' });\n if (callback) {\n callback.call(this, error);\n return this;\n }\n throw error;\n }\n if (this.config.verbose) {\n console.debug(`-> connecting ${config === null || config === void 0 ? void 0 : config.host}:${config === null || config === void 0 ? void 0 : config.port}`);\n }\n this.config = config;\n const initializationCommands = (0, utilities_1.getInitializationCommands)(config);\n // connect to plain socket, without encryption, only if insecure parameter specified\n // this option is mainly for testing purposes and is not available on production nodes\n // which would need to connect using tls and proper certificates as per code below\n const connectionOptions = {\n host: config.host,\n port: config.port,\n rejectUnauthorized: config.host != 'localhost',\n // Server name for the SNI (Server Name Indication) TLS extension.\n // https://r2.nodejs.org/docs/v6.11.4/api/tls.html#tls_class_tls_tlssocket\n servername: config.host\n };\n // tls.connect in the react-native-tcp-socket library is tls.connectTLS\n let connector = tls.connect;\n // @ts-ignore\n if (typeof tls.connectTLS !== 'undefined') {\n // @ts-ignore\n connector = tls.connectTLS;\n }\n this.processCallback = callback;\n this.socket = connector(connectionOptions, () => {\n var _a;\n if (this.config.verbose) {\n console.debug(`SQLiteCloudTlsConnection - connected to ${this.config.host}, authorized: ${(_a = this.socket) === null || _a === void 0 ? void 0 : _a.authorized}`);\n }\n this.transportCommands(initializationCommands, error => {\n if (this.config.verbose) {\n console.debug(`SQLiteCloudTlsConnection - initialized connection`);\n }\n callback === null || callback === void 0 ? void 0 : callback.call(this, error);\n });\n });\n this.socket.setKeepAlive(true);\n // disable Nagle algorithm because we want our writes to be sent ASAP\n // https://brooker.co.za/blog/2024/05/09/nagle.html\n this.socket.setNoDelay(true);\n this.socket.on('data', (data) => {\n this.processCommandsData(data);\n });\n this.socket.on('error', (error) => {\n this.close();\n this.processCommandsFinish(new types_1.SQLiteCloudError('Connection error', { errorCode: 'ERR_CONNECTION_ERROR', cause: error }));\n });\n this.socket.on('end', () => {\n this.close();\n if (this.processCallback)\n this.processCommandsFinish(new types_1.SQLiteCloudError('Server ended the connection', { errorCode: 'ERR_CONNECTION_ENDED' }));\n });\n this.socket.on('close', () => {\n this.close();\n this.processCommandsFinish(new types_1.SQLiteCloudError('Connection closed', { errorCode: 'ERR_CONNECTION_CLOSED' }));\n });\n this.socket.on('timeout', () => {\n this.close();\n this.processCommandsFinish(new types_1.SQLiteCloudError('Connection ened due to timeout', { errorCode: 'ERR_CONNECTION_TIMEOUT' }));\n });\n return this;\n }\n /** Will send a command immediately (no queueing), return the rowset/result or throw an error */\n transportCommands(commands, callback) {\n var _a, _b, _c, _d, _e;\n // connection needs to be established?\n if (!this.socket) {\n callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Connection not established', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' }));\n return this;\n }\n if (typeof commands === 'string') {\n commands = { query: commands };\n }\n // reset buffer and rowset chunks, define response callback\n this.buffer = Buffer.alloc(0);\n this.startedOn = new Date();\n this.processCallback = callback;\n this.executingCommands = commands;\n // compose commands following SCPC protocol\n const formattedCommands = (0, protocol_1.formatCommand)(commands);\n if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.verbose) {\n console.debug(`-> ${formattedCommands}`);\n }\n const timeoutMs = (_c = (_b = this.config) === null || _b === void 0 ? void 0 : _b.timeout) !== null && _c !== void 0 ? _c : 0;\n if (timeoutMs > 0) {\n const timeout = setTimeout(() => {\n var _a;\n callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Connection timeout out', { errorCode: 'ERR_CONNECTION_TIMEOUT' }));\n (_a = this.socket) === null || _a === void 0 ? void 0 : _a.destroy();\n this.socket = undefined;\n }, timeoutMs);\n (_d = this.socket) === null || _d === void 0 ? void 0 : _d.write(formattedCommands, () => {\n clearTimeout(timeout); // Clear the timeout on successful write\n });\n }\n else {\n (_e = this.socket) === null || _e === void 0 ? void 0 : _e.write(formattedCommands);\n }\n return this;\n }\n /** Handles data received in response to an outbound command sent by processCommands */\n processCommandsData(data) {\n var _a, _b, _c, _d, _e, _f, _g;\n try {\n // append data to buffer as it arrives\n if (data.length && data.length > 0) {\n // console.debug(`processCommandsData - received ${data.length} bytes`)\n this.buffer = Buffer.concat([this.buffer, data]);\n }\n let dataType = (_a = this.buffer) === null || _a === void 0 ? void 0 : _a.subarray(0, 1).toString();\n if ((0, protocol_1.hasCommandLength)(dataType)) {\n const commandLength = (0, protocol_1.parseCommandLength)(this.buffer);\n const hasReceivedEntireCommand = this.buffer.length - this.buffer.indexOf(' ') - 1 >= commandLength ? true : false;\n if (hasReceivedEntireCommand) {\n if ((_b = this.config) === null || _b === void 0 ? void 0 : _b.verbose) {\n let bufferString = this.buffer.toString('utf8');\n if (bufferString.length > 1000) {\n bufferString = bufferString.substring(0, 100) + '...' + bufferString.substring(bufferString.length - 40);\n }\n const elapsedMs = new Date().getTime() - this.startedOn.getTime();\n console.debug(`<- ${bufferString} (${bufferString.length} bytes, ${elapsedMs}ms)`);\n }\n // need to decompress this buffer before decoding?\n if (dataType === protocol_1.CMD_COMPRESSED) {\n const decompressResults = (0, protocol_1.decompressBuffer)(this.buffer);\n if (decompressResults.dataType === protocol_1.CMD_ROWSET_CHUNK) {\n this.pendingChunks.push(decompressResults.buffer);\n this.buffer = decompressResults.remainingBuffer;\n this.processCommandsData(Buffer.alloc(0));\n return;\n }\n else {\n const { data } = (0, protocol_1.popData)(decompressResults.buffer, this.config.safe_integer_mode);\n (_c = this.processCommandsFinish) === null || _c === void 0 ? void 0 : _c.call(this, null, data);\n }\n }\n else {\n if (dataType !== protocol_1.CMD_ROWSET_CHUNK) {\n const { data } = (0, protocol_1.popData)(this.buffer, this.config.safe_integer_mode);\n (_d = this.processCommandsFinish) === null || _d === void 0 ? void 0 : _d.call(this, null, data);\n }\n else {\n const completeChunk = (0, protocol_1.bufferEndsWith)(this.buffer, protocol_1.ROWSET_CHUNKS_END);\n if (completeChunk) {\n const parsedData = (0, protocol_1.parseRowsetChunks)([...this.pendingChunks, this.buffer], this.config.safe_integer_mode);\n (_e = this.processCommandsFinish) === null || _e === void 0 ? void 0 : _e.call(this, null, parsedData);\n }\n }\n }\n }\n }\n else {\n // command with no explicit len so make sure that the final character is a space\n const lastChar = this.buffer.subarray(this.buffer.length - 1, this.buffer.length).toString('utf8');\n if (lastChar == ' ') {\n const { data } = (0, protocol_1.popData)(this.buffer, this.config.safe_integer_mode);\n (_f = this.processCommandsFinish) === null || _f === void 0 ? void 0 : _f.call(this, null, data);\n }\n }\n }\n catch (error) {\n console.error(`processCommandsData - error: ${error}`);\n console.assert(error instanceof Error, 'An error occoured while processing data');\n if (error instanceof Error) {\n (_g = this.processCommandsFinish) === null || _g === void 0 ? void 0 : _g.call(this, error);\n }\n }\n }\n /** Completes a transaction initiated by processCommands */\n processCommandsFinish(error, result) {\n if (error) {\n if (this.processCallback) {\n console.error('processCommandsFinish - error', error);\n }\n else {\n console.warn('processCommandsFinish - error with no registered callback', error);\n }\n }\n if (this.processCallback) {\n this.processCallback(error, result);\n }\n this.buffer = Buffer.alloc(0);\n this.pendingChunks = [];\n }\n /** Disconnect immediately, release connection, no events. */\n close() {\n if (this.socket) {\n this.socket.removeAllListeners();\n this.socket.destroy();\n this.socket = undefined;\n }\n this.operations.clear();\n return this;\n }\n}\nexports.SQLiteCloudTlsConnection = SQLiteCloudTlsConnection;\nexports[\"default\"] = SQLiteCloudTlsConnection;\n\n\n//# sourceURL=webpack://sqlitecloud/./lib/drivers/connection-tls.js?"); /***/ }), /***/ "./lib/drivers/connection-ws.js": /*!**************************************!*\ !*** ./lib/drivers/connection-ws.js ***! \**************************************/ /***/ ((__unused_webpack_module, exports, __webpack_require__) => { "use strict"; eval("\n/**\n * transport-ws.ts - handles low level communication with sqlitecloud server via socket.io websocket\n */\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.SQLiteCloudWebsocketConnection = void 0;\nconst socket_io_client_1 = __webpack_require__(/*! socket.io-client */ \"./node_modules/socket.io-client/build/cjs/index.js\");\nconst socket_io_parser_1 = __webpack_require__(/*! socket.io-parser */ \"./node_modules/socket.io-parser/build/cjs/index.js\");\nconst connection_1 = __webpack_require__(/*! ./connection */ \"./lib/drivers/connection.js\");\nconst rowset_1 = __webpack_require__(/*! ./rowset */ \"./lib/drivers/rowset.js\");\nconst types_1 = __webpack_require__(/*! ./types */ \"./lib/drivers/types.js\");\nconst utilities_1 = __webpack_require__(/*! ./utilities */ \"./lib/drivers/utilities.js\");\nconst SocketIODecoderBase = socket_io_parser_1.Decoder;\nfunction createSocketIOParser(maxAttachments) {\n class SQLiteCloudSocketIODecoder extends SocketIODecoderBase {\n constructor(opts) {\n var _a, _b;\n const decoderOptions = typeof opts === 'function' ? { reviver: opts } : opts;\n super(decoderOptions === null || decoderOptions === void 0 ? void 0 : decoderOptions.reviver);\n this.opts || (this.opts = {});\n this.opts.maxAttachments = Math.max((_b = (_a = this.opts.maxAttachments) !== null && _a !== void 0 ? _a : decoderOptions === null || decoderOptions === void 0 ? void 0 : decoderOptions.maxAttachments) !== null && _b !== void 0 ? _b : 0, maxAttachments);\n }\n }\n return {\n Encoder: socket_io_parser_1.Encoder,\n Decoder: SQLiteCloudSocketIODecoder\n };\n}\nfunction getResponseBlobTransferFormat(response) {\n var _a;\n return (0, utilities_1.parseWebsocketBlobTransferFormat)(((_a = response === null || response === void 0 ? void 0 : response.capabilities) === null || _a === void 0 ? void 0 : _a.blobTransferFormat) || (response === null || response === void 0 ? void 0 : response.blobTransferFormat), undefined);\n}\nfunction getAttachmentLimitError(description, limit) {\n const descriptionMessage = description instanceof Error ? description.message : typeof description === 'string' ? description : '';\n if (/illegal attachments/i.test(descriptionMessage)) {\n return new types_1.SQLiteCloudError(`WebSocket blob response exceeded the configured Socket.IO attachment limit (${limit}). Use websocketBlobFormat=base64-blobs-v1 or increase websocketMaxAttachments.`, {\n errorCode: 'ERR_WEBSOCKET_MAX_ATTACHMENTS_EXCEEDED',\n cause: description\n });\n }\n}\nfunction getGatewayResponseError(response) {\n if ((response === null || response === void 0 ? void 0 : response.error) && typeof response.error === 'object') {\n return response.error;\n }\n if (Array.isArray(response === null || response === void 0 ? void 0 : response.errors) && response.errors[0] && typeof response.errors[0] === 'object') {\n return response.errors[0];\n }\n}\n/**\n * Implementation of TransportConnection that connects to the database indirectly\n * via SQLite Cloud Gateway, a socket.io based deamon that responds to sql query\n * requests by returning results and rowsets in json format. The gateway handles\n * connect, disconnect, retries, order of operations, timeouts, etc.\n */\nclass SQLiteCloudWebsocketConnection extends connection_1.SQLiteCloudConnection {\n /** True if connection is open */\n get connected() {\n var _a;\n return !!(this.socket && ((_a = this.socket) === null || _a === void 0 ? void 0 : _a.connected));\n }\n /* Opens a connection with the server and sends the initialization commands. Will throw in case of errors. */\n connectTransport(config, callback) {\n var _a;\n try {\n // connection established while we were waiting in line?\n console.assert(!this.connected, 'Connection already established');\n if (!this.socket) {\n this.config = config;\n const websocketMaxAttachments = (0, utilities_1.parseWebsocketMaxAttachments)(this.config.websocketMaxAttachments);\n // Gateway tenant routing is derived from the Host header. In production, `gatewayurl` is\n // a domain suffix (eg `gateway.sqlite.cloud`) appended to the tenant prefix from the core\n // hostname (eg crvheg7dhk.g4 from crvheg7dhk.g4.sqlite.cloud) to form the gateway host\n // (→ crvheg7dhk.g4.gateway.sqlite.cloud). For local development, pass a `gatewayurl`\n // containing `localhost` — the driver routes TCP to it and injects the tenant Host header\n // (with the gateway marker label) so the gateway still tenant-routes correctly.\n const authToken = this.config.apikey || this.config.token;\n const ioOpts = { auth: { token: authToken }, parser: createSocketIOParser(websocketMaxAttachments) };\n let gatewayUrl;\n if ((_a = this.config.gatewayurl) === null || _a === void 0 ? void 0 : _a.includes('localhost')) {\n const raw = this.config.gatewayurl;\n gatewayUrl = raw.startsWith('ws://') || raw.startsWith('wss://') ? raw : `ws://${raw}`;\n ioOpts.extraHeaders = { Host: buildGatewayHost(this.config.host) };\n ioOpts.transports = ['websocket'];\n }\n else {\n const gatewayHost = buildGatewayHost(this.config.host, this.config.gatewayurl);\n gatewayUrl = `wss://${gatewayHost}:443`;\n }\n this.socket = (0, socket_io_client_1.io)(gatewayUrl, ioOpts);\n this.socket.on('connect', () => {\n callback === null || callback === void 0 ? void 0 : callback.call(this, null);\n });\n this.socket.on('disconnect', (reason, description) => {\n this.close();\n callback === null || callback === void 0 ? void 0 : callback.call(this, (reason === 'parse error' && getAttachmentLimitError(description, websocketMaxAttachments)) ||\n new types_1.SQLiteCloudError('Disconnected', { errorCode: 'ERR_CONNECTION_ENDED', cause: reason }));\n });\n this.socket.on('connect_error', (error) => {\n this.close();\n if ((error === null || error === void 0 ? void 0 : error.message) === 'parse error' || (error === null || error === void 0 ? void 0 : error.cause) === 'parse error') {\n callback === null || callback === void 0 ? void 0 : callback.call(this, getAttachmentLimitError((error === null || error === void 0 ? void 0 : error.description) || (error === null || error === void 0 ? void 0 : error.data) || (error === null || error === void 0 ? void 0 : error.cause), websocketMaxAttachments) ||\n new types_1.SQLiteCloudError('Connection error', { errorCode: 'ERR_CONNECTION_ERROR', cause: error }));\n return;\n }\n let message = error.message || 'Connection error';\n if (typeof error.context == 'object' && error.context.responseText) {\n try {\n const parsed = JSON.parse(error.context.responseText);\n message = (parsed === null || parsed === void 0 ? void 0 : parsed.message) || error.context.responseText;\n }\n catch (_a) {\n message = error.context.responseText;\n }\n }\n callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError(message, { errorCode: 'ERR_CONNECTION_ERROR' }));\n });\n this.socket.on('error', (error) => {\n this.close();\n callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Connection error', { errorCode: 'ERR_CONNECTION_ERROR', cause: error }));\n });\n }\n }\n catch (error) {\n callback === null || callback === void 0 ? void 0 : callback.call(this, error);\n }\n return this;\n }\n /** Will send a command immediately (no queueing), return the rowset/result or throw an error */\n transportCommands(commands, callback) {\n // connection needs to be established?\n if (!this.socket) {\n callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Connection not established', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' }));\n return this;\n }\n if (typeof commands === 'string') {\n commands = { query: commands };\n }\n this.socket.emit('GET /v2/weblite/sql', {\n sql: commands.query,\n bind: (0, utilities_1.encodeBigIntMarkers)(commands.parameters),\n database: this.config.database,\n row: 'array',\n safe_integer_mode: this.config.safe_integer_mode,\n capabilities: {\n blobTransferFormat: this.config.websocketBlobFormat || types_1.DEFAULT_WEBSOCKET_BLOB_TRANSFER_FORMAT\n }\n }, (response) => {\n const gatewayError = getGatewayResponseError(response);\n if (gatewayError) {\n // Gateway error fields (errorCode, externalErrorCode, offsetCode) live under `meta`\n // for the `errors[]` shape; fall back to top-level for the legacy `error` shape.\n const errorFields = (gatewayError.meta && typeof gatewayError.meta === 'object') ? gatewayError.meta : gatewayError;\n const error = new types_1.SQLiteCloudError(gatewayError.detail || gatewayError.message || 'Gateway error', Object.assign({}, errorFields));\n callback === null || callback === void 0 ? void 0 : callback.call(this, error);\n }\n else {\n const { metadata } = response;\n const blobTransferFormat = getResponseBlobTransferFormat(response);\n const data = metadata && metadata.numberOfRows !== undefined && metadata.numberOfColumns !== undefined && metadata.columns !== undefined\n ? (0, utilities_1.decodeWebsocketRowsetData)(response === null || response === void 0 ? void 0 : response.data, metadata, this.config.safe_integer_mode, blobTransferFormat)\n : (0, utilities_1.decodeBigIntMarkers)(response === null || response === void 0 ? void 0 : response.data, this.config.safe_integer_mode);\n if (data !== undefined && metadata) {\n if (metadata.numberOfRows !== undefined && metadata.numberOfColumns !== undefined && metadata.columns !== undefined) {\n console.assert(Array.isArray(data), 'SQLiteCloudWebsocketConnection.transportCommands - data is not an array');\n // we can recreate a SQLiteCloudRowset from the response which we know to be an array of arrays\n const rowset = new rowset_1.SQLiteCloudRowset(metadata, data.flat());\n callback === null || callback === void 0 ? void 0 : callback.call(this, null, rowset);\n return;\n }\n }\n callback === null || callback === void 0 ? void 0 : callback.call(this, null, data);\n }\n });\n return this;\n }\n /** Disconnect socket.io from server */\n close() {\n var _a, _b;\n console.assert(this.socket !== null, 'SQLiteCloudWebsocketConnection.close - connection already closed');\n if (this.socket) {\n (_a = this.socket) === null || _a === void 0 ? void 0 : _a.removeAllListeners();\n (_b = this.socket) === null || _b === void 0 ? void 0 : _b.close();\n this.socket = undefined;\n }\n this.operations.clear();\n return this;\n }\n}\nexports.SQLiteCloudWebsocketConnection = SQLiteCloudWebsocketConnection;\n/** Default gateway domain suffix used when `gatewayurl` is not provided. */\nconst DEFAULT_GATEWAY_DOMAIN = 'gateway.sqlite.cloud';\n/** Builds the gateway hostname from a core hostname, swapping its last two labels (the\n * TLD) with the given `gatewayurl` suffix.\n *\n * Example: buildGatewayHost('crvheg7dhk.g4.sqlite.cloud')\n * → 'crvheg7dhk.g4.gateway.sqlite.cloud'\n *\n * Returns `host` unchanged when it already ends with the suffix (idempotent) or when\n * it's too short to extract a tenant prefix (eg 'localhost'). */\nfunction buildGatewayHost(host, gatewayurl) {\n if (!host)\n return host;\n const suffix = gatewayurl || DEFAULT_GATEWAY_DOMAIN;\n if (host === suffix || host.endsWith('.' + suffix))\n return host;\n const parts = host.split('.');\n if (parts.length < 3)\n return host;\n return parts.slice(0, -2).join('.') + '.' + suffix;\n}\nexports[\"default\"] = SQLiteCloudWebsocketConnection;\n\n\n//# sourceURL=webpack://sqlitecloud/./lib/drivers/connection-ws.js?"); /***/ }), /***/ "./lib/drivers/connection.js": /*!***********************************!*\ !*** ./lib/drivers/connection.js ***! \***********************************/ /***/ (function(__unused_webpack_module, exports, __webpack_require__) { "use strict"; eval("\n/**\n * connection.ts - base abstract class for sqlitecloud server connections\n */\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.SQLiteCloudConnection = void 0;\nconst types_1 = __webpack_require__(/*! ./types */ \"./lib/drivers/types.js\");\nconst utilities_1 = __webpack_require__(/*! ./utilities */ \"./lib/drivers/utilities.js\");\nconst queue_1 = __webpack_require__(/*! ./queue */ \"./lib/drivers/queue.js\");\nconst utilities_2 = __webpack_require__(/*! ./utilities */ \"./lib/drivers/utilities.js\");\n/**\n * Base class for SQLiteCloudConnection handles basics and defines methods.\n * Actual connection management and communication with the server in concrete classes.\n */\nclass SQLiteCloudConnection {\n /** Parse and validate provided connectionstring or configuration */\n constructor(config, callback) {\n /** Operations are serialized by waiting an any pending promises */\n this.operations = new queue_1.OperationsQueue();\n if (typeof config === 'string') {\n this.config = (0, utilities_1.validateConfiguration)({ connectionstring: config });\n }\n else {\n this.config = (0, utilities_1.validateConfiguration)(config);\n }\n // connect transport layer to server\n this.connect(callback);\n }\n /** Returns the connection's configuration */\n getConfig() {\n return Object.assign({}, this.config);\n }\n //\n // internal methods (some are implemented in concrete classes using different transport layers)\n //\n /** Connect will establish a tls or websocket transport to the server based on configuration and environment */\n connect(callback) {\n this.operations.enqueue(done => {\n this.connectTransport(this.config, error => {\n if (error) {\n console.error(`SQLiteCloudConnection.connect - error connecting ${this.config.host}:${this.config.port} ${error.toString()}`, error);\n this.close();\n }\n if (callback) {\n callback.call(this, error || null);\n }\n done(error);\n });\n });\n return this;\n }\n /** Will log to console if verbose mode is enabled */\n log(message, ...optionalParams) {\n if (this.config.verbose) {\n message = (0, utilities_2.anonimizeCommand)(message);\n console.log(`${new Date().toISOString()} ${this.config.clientid}: ${message}`, ...optionalParams);\n }\n }\n /** Enable verbose logging for debug purposes */\n verbose() {\n this.config.verbose = true;\n }\n /** Will enquee a command to be executed and callback with the resulting rowset/result/error */\n sendCommands(commands, callback) {\n this.operations.enqueue(done => {\n if (!this.connected) {\n const error = new types_1.SQLiteCloudError('Connection not established', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' });\n callback === null || callback === void 0 ? void 0 : callback.call(this, error);\n done(error);\n }\n else {\n this.transportCommands(commands, (error, result) => {\n callback === null || callback === void 0 ? void 0 : callback.call(this, error, result);\n done(error);\n });\n }\n });\n return this;\n }\n /**\n * Sql is a promise based API for executing SQL statements. You can\n * pass a simple string with a SQL statement or a template string\n * using backticks and parameters in ${parameter} format. These parameters\n * will be properly escaped and quoted like when using a prepared statement.\n * @param sql A sql string or a template string in `backticks` format\n * A SQLiteCloudCommand when the query is defined with question marks and bindings.\n * @returns An array of rows in case of selections or an object with\n * metadata in case of insert, update, delete.\n */\n sql(sql, ...values) {\n return __awaiter(this, void 0, void 0, function* () {\n let commands = { query: '' };\n // sql is a TemplateStringsArray, the 'raw' property is specific to TemplateStringsArray\n if (Array.isArray(sql) && 'raw' in sql) {\n let query = '';\n sql.forEach((string, i) => {\n // TemplateStringsArray splits the string before each variable\n // used in the template. Add the question mark\n // to the end of the string for the number of used variables.\n query += string + (i < values.length ? '?' : '');\n });\n commands = { query, parameters: values };\n }\n else if (typeof sql === 'string') {\n commands = { query: sql, parameters: values };\n }\n else if (typeof sql === 'object') {\n commands = sql;\n }\n else {\n throw new Error('Invalid sql');\n }\n return new Promise((resolve, reject) => {\n this.sendCommands(commands, (error, results) => {\n if (error) {\n reject(error);\n }\n else {\n // metadata for operations like insert, update, delete?\n const context = (0, utilities_2.getUpdateResults)(results);\n resolve(context ? context : results);\n }\n });\n });\n });\n }\n}\nexports.SQLiteCloudConnection = SQLiteCloudConnection;\n\n\n//# sourceURL=webpack://sqlitecloud/./lib/drivers/connection.js?"); /***/ }), /***/ "./lib/drivers/database.js": /*!*********************************!*\ !*** ./lib/drivers/database.js ***! \*********************************/ /***/ (function(__unused_webpack_module, exports, __webpack_require__) { "use strict"; eval("\n//\n// database.ts - database driver api, implements and extends sqlite3\n//\nvar __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n}));\nvar __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n});\nvar __importStar = (this && this.__importStar) || (function () {\n var ownKeys = function(o) {\n ownKeys = Object.getOwnPropertyNames || function (o) {\n var ar = [];\n for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;\n return ar;\n };\n return ownKeys(o);\n };\n return function (mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== \"default\") __createBinding(result, mod, k[i]);\n __setModuleDefault(result, mod);\n return result;\n };\n})();\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Database = void 0;\n// Trying as much as possible to be a drop-in replacement for SQLite3 API\n// https://github.com/TryGhost/node-sqlite3/wiki/API\n// https://github.com/TryGhost/node-sqlite3\n// https://github.com/TryGhost/node-sqlite3/blob/master/lib/sqlite3.d.ts\nconst eventemitter3_1 = __importDefault(__webpack_require__(/*! eventemitter3 */ \"./node_modules/eventemitter3/index.js\"));\nconst queue_1 = __webpack_require__(/*! ./queue */ \"./lib/drivers/queue.js\");\nconst rowset_1 = __webpack_require__(/*! ./rowset */ \"./lib/drivers/rowset.js\");\nconst statement_1 = __webpack_require__(/*! ./statement */ \"./lib/drivers/statement.js\");\nconst types_1 = __webpack_require__(/*! ./types */ \"./lib/drivers/types.js\");\nconst utilities_1 = __webpack_require__(/*! ./utilities */ \"./lib/drivers/utilities.js\");\n// Uses eventemitter3 instead of node events for browser compatibility\n// https://github.com/primus/eventemitter3\n/**\n * Creating a Database object automatically opens a connection to the SQLite database.\n * When the connection is established the Database object emits an open event and calls\n * the optional provided callback. If the connection cannot be established an error event\n * will be emitted and the optional callback is called with the error information.\n */\nclass Database extends eventemitter3_1.default {\n constructor(config, mode, callback) {\n super();\n /** Used to syncronize opening of connection and commands */\n this.operations = new queue_1.OperationsQueue();\n this.config = typeof config === 'string' ? { connectionstring: config } : config;\n this.connection = null;\n // mode is optional and so is callback\n // https://github.com/TryGhost/node-sqlite3/wiki/API#new-sqlite3databasefilename--mode--callback\n if (typeof mode === 'function') {\n callback = mode;\n mode = undefined;\n }\n // mode is ignored for now\n // opens the connection to the database automatically\n this.createConnection(error => {\n if (callback) {\n callback.call(this, error);\n }\n });\n }\n //\n // private methods\n //\n /** Returns first available connection from connection pool */\n createConnection(callback) {\n var _a, _b;\n // connect using websocket if tls is not supported or if explicitly requested\n const useWebsocket = utilities_1.isBrowser || ((_a = this.config) === null || _a === void 0 ? void 0 : _a.usewebsocket) || ((_b = this.config) === null || _b === void 0 ? void 0 : _b.gatewayurl);\n if (useWebsocket) {\n // socket.io transport works in both node.js and browser environments and connects via SQLite Cloud Gateway\n this.operations.enqueue(done => {\n Promise.resolve().then(() => __importStar(__webpack_require__(/*! ./connection-ws */ \"./lib/drivers/connection-ws.js\"))).then(module => {\n this.connection = new module.default(this.config, (error) => {\n if (error) {\n this.handleError(error, callback);\n }\n else {\n callback === null || callback === void 0 ? void 0 : callback.call(this, null);\n this.emitEvent('open');\n }\n done(error);\n });\n })\n .catch(error => {\n this.handleError(error, callback);\n this.close();\n done(error);\n });\n });\n }\n else {\n this.operations.enqueue(done => {\n Promise.resolve().then(() => __importStar(__webpack_require__(/*! ./connection-tls */ \"./lib/drivers/connection-tls.js\"))).then(module => {\n this.connection = new module.default(this.config, (error) => {\n if (error) {\n this.handleError(error, callback);\n }\n else {\n callback === null || callback === void 0 ? void 0 : callback.call(this, null);\n this.emitEvent('open');\n }\n done(error);\n });\n })\n .catch(error => {\n this.handleError(error, callback);\n this.close();\n done(error);\n });\n });\n }\n }\n enqueueCommand(command, callback) {\n this.operations.enqueue(done => {\n let error = null;\n // we don't wont to silently open a new connection after a disconnession\n if (this.connection && this.connection.connected) {\n this.connection.sendCommands(command, (error, results) => {\n callback === null || callback === void 0 ? void 0 : callback.call(this, error, results);\n done(error);\n });\n }\n else {\n error = new types_1.SQLiteCloudError('Connection unavailable. Maybe it got disconnected?', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' });\n callback === null || callback === void 0 ? void 0 : callback.call(this, error, null);\n done(error);\n }\n });\n }\n /** Handles an error by closing the connection, calling the callback and/or emitting an error event */\n handleError(error, callback) {\n if (callback) {\n callback.call(this, error);\n }\n else {\n this.emitEvent('error', error);\n }\n }\n /**\n * Some queries like inserts or updates processed via run or exec may generate\n * an empty result (eg. no data was selected), but still have some metadata.\n * For example the server may pass the id of the last row that was modified.\n * In this case the callback results should be empty but the context may contain\n * additional information like lastID, etc.\n * @see https://github.com/TryGhost/node-sqlite3/wiki/API#runsql--param---callback\n * @param results Results received from the server\n * @returns A context object if one makes sense, otherwise undefined\n */\n processContext(results) {\n if (results) {\n if (Array.isArray(results) && results.length > 0) {\n switch (results[0]) {\n case types_1.SQLiteCloudArrayType.ARRAY_TYPE_SQLITE_EXEC:\n return {\n lastID: results[2], // ROWID (sqlite3_last_insert_rowid)\n changes: results[3], // CHANGES(sqlite3_changes)\n totalChanges: results[4], // TOTAL_CHANGES (sqlite3_total_changes)\n finalized: results[5] // FINALIZED\n };\n }\n }\n }\n return undefined;\n }\n /** Emits given event with optional arguments on the next tick so callbacks can complete first */\n emitEvent(event, ...args) {\n setTimeout(() => {\n this.emit(event, ...args);\n }, 0);\n }\n //\n // public methods\n //\n /**\n * Returns the configuration with which this database was opened.\n * The configuration is readonly and cannot be changed as there may\n * be multiple connections using the same configuration.\n * @returns {SQLiteCloudConfig} A configuration object\n */\n getConfiguration() {\n return JSON.parse(JSON.stringify(this.config));\n }\n /** Enable verbose mode */\n verbose() {\n var _a;\n this.config.verbose = true;\n (_a = this.connection) === null || _a === void 0 ? void 0 : _a.verbose();\n return this;\n }\n /** Set a configuration option for the database */\n configure(_option, _value) {\n // https://github.com/TryGhost/node-sqlite3/wiki/API#configureoption-value\n return this;\n }\n run(sql, ...params) {\n const { args, callback } = (0, utilities_1.popCallback)(params);\n const command = { query: sql, parameters: args };\n this.enqueueCommand(command, (error, results) => {\n if (error) {\n this.handleError(error, callback);\n }\n else {\n // context may include id of last row inserted, total changes, etc...\n const context = this.processContext(results);\n callback === null || callback === void 0 ? void 0 : callback.call(context || this, null, context ? context : results);\n }\n });\n return this;\n }\n get(sql, ...params) {\n const { args, callback } = (0, utilities_1.popCallback)(params);\n const command = { query: sql, parameters: args };\n this.enqueueCommand(command, (error, results) => {\n if (error) {\n this.handleError(error, callback);\n }\n else {\n if (results && results instanceof rowset_1.SQLiteCloudRowset && results.length > 0) {\n callback === null || callback === void 0 ? void 0 : callback.call(this, null, results[0]);\n }\n else {\n callback === null || callback === void 0 ? void 0 : callback.call(this, null);\n }\n }\n });\n return this;\n }\n all(sql, ...params) {\n const { args, callback } = (0, utilities_1.popCallback)(params);\n const command = { query: sql, parameters: args };\n this.enqueueCommand(command, (error, results) => {\n if (error) {\n this.handleError(error, callback);\n }\n else {\n if (results && results instanceof rowset_1.SQLiteCloudRowset) {\n callback === null || callback === void 0 ? void 0 : callback.call(this, null, results);\n }\n else {\n callback === null || callback === void 0 ? void 0 : callback.call(this, null);\n }\n }\n });\n return this;\n }\n each(sql, ...params) {\n // extract optional parameters and one or two callbacks\n const { args, callback, complete } = (0, utilities_1.popCallback)(params);\n const command = { query: sql, parameters: args };\n this.enqueueCommand(command, (error, rowset) => {\n if (error) {\n this.handleError(error, callback);\n }\n else {\n if (rowset && rowset instanceof rowset_1.SQLiteCloudRowset) {\n if (callback) {\n for (const row of rowset) {\n callback.call(this, null, row);\n }\n }\n if (complete) {\n ;\n complete.call(this, null, rowset.numberOfRows);\n }\n }\n else {\n callback === null || callback === void 0 ? void 0 : callback.call(this, new types_1.SQLiteCloudError('Invalid rowset'));\n }\n }\n });\n return this;\n }\n /**\n * Prepares the SQL statement and optionally binds the specified parameters and\n * calls the callback when done. The function returns a Statement object.\n