@capacitor-community/sqlite
Version:
Community plugin for native & electron SQLite databases
1,445 lines (1,415 loc) • 231 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var require$$0 = require('node:fs/promises');
var require$$1 = require('path');
var require$$2 = require('fs');
var require$$3 = require('node-fetch');
var require$$4 = require('os');
var require$$5 = require('jszip');
var require$$6 = require('electron');
var require$$4$1 = require('better-sqlite3-multiple-ciphers');
var require$$3$1 = require('electron-json-storage');
var require$$1$1 = require('crypto');
var require$$2$1 = require('crypto-js');
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var src = {};
var GlobalSQLite = {};
var hasRequiredGlobalSQLite;
function requireGlobalSQLite () {
if (hasRequiredGlobalSQLite) return GlobalSQLite;
hasRequiredGlobalSQLite = 1;
Object.defineProperty(GlobalSQLite, "__esModule", { value: true });
GlobalSQLite.GlobalSQLite = undefined;
let GlobalSQLite$1 = class GlobalSQLite {
constructor() {
this.secret = 'sqlite secret';
this.newsecret = 'sqlite new secret';
}
};
GlobalSQLite.GlobalSQLite = GlobalSQLite$1;
return GlobalSQLite;
}
var Database = {};
var exportToJson = {};
var utilsSQLite = {};
var UtilsSQL92Compatibility = {};
var hasRequiredUtilsSQL92Compatibility;
function requireUtilsSQL92Compatibility () {
if (hasRequiredUtilsSQL92Compatibility) return UtilsSQL92Compatibility;
hasRequiredUtilsSQL92Compatibility = 1;
Object.defineProperty(UtilsSQL92Compatibility, "__esModule", { value: true });
UtilsSQL92Compatibility.UtilsSQL92Compatibility = undefined;
let UtilsSQL92Compatibility$1 = class UtilsSQL92Compatibility {
compatibleSQL92(statement) {
let newStatement = '';
const action = statement.trim().split(' ')[0].toUpperCase();
switch (action) {
case 'INSERT':
newStatement = this.insertSQL92(statement);
break;
case 'UPDATE':
newStatement = this.updateSQL92(statement);
break;
case 'DELETE':
case 'SELECT':
newStatement = this.whereSQL92(statement);
break;
default:
throw new Error(`Error: ${action} not implemented`);
}
return newStatement;
}
insertSQL92(insertStatement) {
// Split the statement into parts
const inStmt = insertStatement.trim();
const valuesStartIndex = inStmt.indexOf('VALUES');
const columnsPart = inStmt.substring(0, valuesStartIndex);
const valuesPart = inStmt.substring(valuesStartIndex);
// Extract values and replace double quotes with single quotes
const modifiedValuesPart = valuesPart.replace(/"([^"]+)"/g, "'$1'");
// Reconstruct the modified statement
const modifiedStatement = columnsPart + modifiedValuesPart;
return modifiedStatement;
}
updateSQL92(updateStatement) {
// Split the statement into SET and WHERE parts
let isWhere = true;
const setWhereSplit = updateStatement.toUpperCase().split('WHERE');
if (setWhereSplit.length <= 1)
isWhere = false;
const setUpdate = setWhereSplit[0].toUpperCase().split('SET')[0].trim();
const setPart = setWhereSplit[0].toUpperCase().split('SET')[1].trim();
const modifiedSetPart = this.modSetPart(setPart);
let modifiedStatement = `${setUpdate} SET ${modifiedSetPart}`;
if (isWhere) {
for (let i = 1; i < setWhereSplit.length; i++) {
const wherePart = setWhereSplit[i].trim();
const modifiedWherePart = this.modWherePart(wherePart);
modifiedStatement += ` WHERE ${modifiedWherePart}`;
}
}
return modifiedStatement;
}
whereSQL92(statement) {
// Split the statement into SET and WHERE parts
const setWhereSplit = statement.toUpperCase().split('WHERE');
if (setWhereSplit.length <= 1)
return statement;
let modifiedStatement = `${setWhereSplit[0].trim()}`;
for (let i = 1; i < setWhereSplit.length; i++) {
const wherePart = setWhereSplit[1].trim();
const modifiedWherePart = this.modWherePart(wherePart);
modifiedStatement += ` WHERE ${modifiedWherePart}`;
}
return modifiedStatement;
}
modSetPart(setStatement) {
const commaPart = setStatement.split(',');
const modCommaPart = [];
for (const com of commaPart) {
const equalPart = com.split('=');
const value = equalPart[1].replaceAll(`"`, `'`);
modCommaPart.push(`${equalPart[0].trim()} = ${value.trim()}`);
}
return modCommaPart.toString();
}
modWherePart(whereStatement) {
const keywords = new Set([
'=',
'<>',
'>',
'>=',
'<',
'<=',
'IN',
'VALUES',
'(',
',',
')',
'BETWEEN',
'LIKE',
'AND',
'OR',
'NOT',
]);
const newTokens = [];
const tokens = whereStatement
.split(/(\s|,|\(|\))/)
.filter((item) => item !== ' ')
.filter((item) => item !== '');
let inClause = false;
let inValues = false;
let modValue = false;
let betwClause = false;
let opsClause = false;
let inValValues = false;
let inValPar = false;
for (const token of tokens) {
if (new Set(['=', '<>', '>', '>=', '<', '<=']).has(token)) {
newTokens.push(token);
modValue = true;
opsClause = false;
}
else if (token.toUpperCase() === 'BETWEEN') {
newTokens.push(token);
betwClause = true;
modValue = true;
opsClause = false;
}
else if (betwClause && token.toUpperCase() === 'AND') {
newTokens.push(token);
modValue = true;
betwClause = false;
}
else if (token.toUpperCase() === 'LIKE') {
newTokens.push(token);
opsClause = false;
modValue = true;
}
else if (token.toUpperCase() === 'AND' || token.toUpperCase() === 'OR' || token.toUpperCase() === 'NOT') {
newTokens.push(token);
opsClause = true;
}
else if (token.toUpperCase() === 'IN') {
newTokens.push(token);
opsClause = false;
inClause = true;
}
else if (inClause && token === '(') {
newTokens.push(token);
modValue = true;
inValues = true;
}
else if (inValues && token.toUpperCase() === ',') {
newTokens.push(token);
modValue = true;
}
else if (inValues && token.toUpperCase() === 'VALUES') {
newTokens.push(token);
inValues = false;
inValValues = true;
inClause = false;
}
else if (inValValues && token === '(') {
newTokens.push(token);
inValPar = true;
modValue = true;
}
else if (inValPar && token.toUpperCase() === ',') {
newTokens.push(token);
modValue = true;
}
else if (inValPar && inValValues && token === ')') {
newTokens.push(token);
inValPar = false;
inValues = true;
}
else if ((inValues || inValValues) && token === ')') {
newTokens.push(token);
inValValues = false;
inValues = false;
inClause = false;
}
else if (modValue && !opsClause && !keywords.has(token.toUpperCase())) {
if (token.length > 0) {
const nwToken = token.replaceAll(`"`, `'`);
newTokens.push(nwToken);
modValue = false;
}
}
else {
newTokens.push(token);
}
}
const ns = newTokens.join(' ');
return ns;
}
};
UtilsSQL92Compatibility.UtilsSQL92Compatibility = UtilsSQL92Compatibility$1;
return UtilsSQL92Compatibility;
}
var utilsDelete = {};
var hasRequiredUtilsDelete;
function requireUtilsDelete () {
if (hasRequiredUtilsDelete) return utilsDelete;
hasRequiredUtilsDelete = 1;
Object.defineProperty(utilsDelete, "__esModule", { value: true });
utilsDelete.UtilsDelete = undefined;
class UtilsDeleteError {
constructor(message) {
this.message = message;
}
static upDateWhereForDefault(message) {
return new UtilsDeleteError(message);
}
static upDateWhereForRestrict(message) {
return new UtilsDeleteError(message);
}
static upDateWhereForCascade(message) {
return new UtilsDeleteError(message);
}
static executeUpdateForDelete(message) {
return new UtilsDeleteError(message);
}
}
class UtilsDelete {
getReferencedTableName(refValue) {
let tableName = '';
if (refValue.length > 0) {
const arr = refValue.split(new RegExp('REFERENCES', 'i'));
if (arr.length === 2) {
const oPar = arr[1].indexOf('(');
tableName = arr[1].substring(0, oPar).trim();
}
}
return tableName;
}
upDateWhereForDefault(withRefsNames, results) {
let setStmt = '';
let uWhereStmt = '';
try {
const key = results.key;
const cols = [];
for (const relItem of results.relatedItems) {
const mVal = relItem[key];
if (mVal !== undefined) {
cols.push(mVal);
}
}
// Create the set statement
for (const name of withRefsNames) {
setStmt += `${name} = NULL, `;
}
setStmt += 'sql_deleted = 0';
// Create the where statement
uWhereStmt = `WHERE ${key} IN (`;
for (const col of cols) {
uWhereStmt += `${col},`;
}
if (uWhereStmt.endsWith(',')) {
uWhereStmt = uWhereStmt.slice(0, -1);
}
uWhereStmt += ');';
}
catch (error) {
const msg = error.message ? error.message : error;
throw UtilsDeleteError.upDateWhereForDefault(msg);
}
return { setStmt, uWhereStmt };
}
upDateWhereForRestrict(results) {
try {
const setStmt = '';
const uWhereStmt = '';
if (results.relatedItems.length > 0) {
const msg = 'Restrict mode related items exist, please delete them first';
throw UtilsDeleteError.upDateWhereForRestrict(msg);
}
return { setStmt, uWhereStmt };
}
catch (error) {
const msg = error.message ? error.message : error;
throw UtilsDeleteError.upDateWhereForRestrict(msg);
}
}
upDateWhereForCascade(results) {
let setStmt = '';
let uWhereStmt = '';
try {
const key = results.key;
const cols = [];
for (const relItem of results.relatedItems) {
const mVal = relItem[key];
if (mVal !== undefined) {
cols.push(mVal);
}
}
setStmt += 'sql_deleted = 1';
// Create the where statement
uWhereStmt = `WHERE ${key} IN (`;
for (const col of cols) {
uWhereStmt += `${col},`;
}
if (uWhereStmt.endsWith(',')) {
uWhereStmt = uWhereStmt.slice(0, -1);
}
uWhereStmt += ');';
}
catch (error) {
const msg = error.message ? error.message : error;
throw UtilsDeleteError.upDateWhereForCascade(msg);
}
return { setStmt, uWhereStmt };
}
getCurrentTimeAsInteger() {
const currentTime = Math.floor(Date.now() / 1000);
return currentTime;
}
checkValuesMatch(array1, array2) {
for (const value of array1) {
if (!array2.includes(value)) {
return false;
}
}
return true;
}
}
utilsDelete.UtilsDelete = UtilsDelete;
return utilsDelete;
}
var utilsFile = {};
var hasRequiredUtilsFile;
function requireUtilsFile () {
if (hasRequiredUtilsFile) return utilsFile;
hasRequiredUtilsFile = 1;
Object.defineProperty(utilsFile, "__esModule", { value: true });
utilsFile.UtilsFile = undefined;
const promises_1 = require$$0;
class UtilsFile {
constructor() {
this.pathDB = 'Databases';
this.Path = null;
this.NodeFs = null;
this.NodeFetch = null;
this.JSZip = null;
this.Os = null;
this.Electron = null;
this.AppName = '';
this.HomeDir = '';
this.sep = '/';
this.isEncryption = false;
this.Path = require$$1;
this.NodeFs = require$$2;
this.NodeFetch = require$$3;
this.Os = require$$4;
this.JSZip = require$$5;
this.Electron = require$$6;
this.HomeDir = this.Os.homedir();
const dir = __dirname;
const idx = dir.indexOf('\\');
if (idx != -1)
this.sep = '\\';
this.appPath = this.Electron.app.getAppPath();
const rawdata = this.NodeFs.readFileSync(this.Path.resolve(this.appPath, 'package.json'));
this.AppName = JSON.parse(rawdata).name;
const pathToBuild = this.Path.join(this.appPath, 'build');
if (this.NodeFs.existsSync(this.Path.join(pathToBuild, 'capacitor.config.js'))) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
this.capConfig = require(this.Path.join(pathToBuild, 'capacitor.config.js')).default;
}
else {
this.capConfig = JSON.parse(this.NodeFs.readFileSync(this.Path.join(this.appPath, 'capacitor.config.json')).toString());
}
this.isEncryption = this.capConfig.plugins.CapacitorSQLite.electronIsEncryption
? this.capConfig.plugins.CapacitorSQLite.electronIsEncryption
: false;
this.osType = this.Os.type();
switch (this.osType) {
case 'Darwin':
this.pathDB = this.capConfig.plugins.CapacitorSQLite.electronMacLocation
? this.capConfig.plugins.CapacitorSQLite.electronMacLocation
: 'Databases';
break;
case 'Linux':
this.pathDB = this.capConfig.plugins.CapacitorSQLite.electronLinuxLocation
? this.capConfig.plugins.CapacitorSQLite.electronLinuxLocation
: 'Databases';
break;
case 'Windows_NT':
this.pathDB = this.capConfig.plugins.CapacitorSQLite.electronWindowsLocation
? this.capConfig.plugins.CapacitorSQLite.electronWindowsLocation
: 'Databases';
break;
default:
console.log('other operating system');
}
}
/**
* Get isEncryption from config
* @returns
*/
getIsEncryption() {
return this.isEncryption;
}
/**
* GetExtName
* @param filePath
* @returns
*/
getExtName(filePath) {
const matches = filePath.match(/\.([a-zA-Z0-9]+)(?:[\\?#]|$)/);
return matches ? `.${matches[1].toLowerCase()}` : ''; // returns the matched extension in lowercase
// return this.Path.extname(filePath);
}
getBaseName(filePath) {
const decodedUrl = decodeURIComponent(filePath); // Decode the URL component
const baseName = this.Path.basename(decodedUrl, this.Path.extname(filePath));
return baseName;
}
/**
* IsPathExists
* @param filePath
*/
isPathExists(filePath) {
let ret = false;
try {
if (this.NodeFs.existsSync(filePath)) {
ret = true;
}
}
catch (err) {
console.error('Error isFileExist: ' + err);
ret = false;
}
return ret;
}
/**
* IsFileExists
* @param fileName
*/
isFileExists(fileName) {
let ret = false;
const filePath = this.getFilePath(fileName);
if (filePath.length > 0) {
ret = this.isPathExists(filePath);
}
return ret;
}
/**
* GetFilePath
* get the file path
* @param fileName
*/
getFilePath(fileName) {
return this.Path.join(this.getDatabasesPath(), fileName);
}
/**
* GetDatabasesPath
* get the database folder path
*/
getDatabasesPath() {
let retPath = '';
const sep = this.Path.sep;
const dbFolder = this.pathDB;
if (dbFolder.includes(sep)) {
retPath = dbFolder;
if (this.Path.basename(dbFolder) !== this.AppName) {
retPath = this.Path.join(dbFolder, this.AppName);
}
}
else {
retPath = this.Path.join(this.HomeDir, dbFolder, this.AppName);
}
const retB = this._createFolderIfNotExists(retPath);
if (!retB)
retPath = '';
return retPath;
}
/**
* GetCachePath
* get the database cache folder path
*/
getCachePath() {
let retPath = '';
const databasePath = this.getDatabasesPath();
retPath = this.Path.join(databasePath, 'cache');
const retB = this._createFolderIfNotExists(retPath);
if (!retB)
retPath = '';
return retPath;
}
/**
* GetAssetsDatabasesPath
* get the assets databases folder path
*/
getAssetsDatabasesPath() {
let retPath = '';
const webDir = this.capConfig.webDir;
const dir = webDir === 'www' ? 'src' : 'public';
let mAppPath = this.appPath;
if (this.Path.basename(this.appPath) === 'electron') {
mAppPath = this.Path.dirname(this.appPath);
}
retPath = this.Path.resolve(mAppPath, dir, 'assets', 'databases');
return retPath;
}
/**
* SetPathSuffix
* @param db
*/
setPathSuffix(db) {
let toDb = db;
const ext = '.db';
const dirName = this.Path.dirname(db);
const baseName = this.getBaseName(db);
if (this.getExtName(db) === ext) {
if (!baseName.includes('SQLite')) {
const dbName = `${baseName}SQLite`;
toDb = `${this.Path.join(dirName, dbName)}${ext}`;
}
}
return toDb;
}
/**
* GetFileList
* get the file list for a given folder
* @param path
*/
async getFileList(path) {
const filenames = this.NodeFs.readdirSync(path);
const dbs = [];
filenames.forEach((file) => {
if (this.getExtName(file) == '.db' || this.getExtName(file) == '.zip')
dbs.push(file);
});
return Promise.resolve(dbs);
}
/**
* CopyFromAssetToDatabase
* @param db
* @param overwrite
*/
async copyFromAssetToDatabase(db, overwrite) {
const pAsset = this.Path.join(this.getAssetsDatabasesPath(), db);
const toDb = this.setPathSuffix(db);
const pDb = this.Path.join(this.getDatabasesPath(), toDb);
await this.copyFilePath(pAsset, pDb, overwrite);
return Promise.resolve();
}
/**
* unzipDatabase
* @param db
* @param overwrite
*/
async unzipDatabase(db, fPath, overwrite) {
const pZip = this.Path.join(fPath, db);
try {
// Read the Zip file
const data = await this.NodeFs.promises.readFile(pZip);
const zip = new this.JSZip();
const contents = await zip.loadAsync(data);
// Create an array to store promises for writing files
const writePromises = [];
Object.keys(contents.files).forEach((filename) => {
writePromises.push(zip
.file(filename)
.async('nodebuffer')
.then(async (content) => {
const toDb = this.setPathSuffix(filename);
const pDb = this.Path.join(this.getDatabasesPath(), toDb);
// check filePath exists
const isPath = this.isPathExists(pDb);
if (!isPath || overwrite) {
if (overwrite && isPath) {
await this.deleteFilePath(pDb);
}
await this.NodeFs.promises.writeFile(pDb, content);
}
}));
});
// Wait for all write promises to resolve
await Promise.all(writePromises);
return Promise.resolve();
}
catch (err) {
console.log(err);
return Promise.reject(`unzipDatabase ${JSON.stringify(err)}`);
}
}
/**
* CopyFileName
* Copy file name
* @param fileName
* @param toFileName
*/
async copyFileName(fileName, toFileName) {
// get File Paths
const filePath = this.getFilePath(fileName);
const toFilePath = this.getFilePath(toFileName);
if (filePath.length !== 0 && toFilePath.length !== 0) {
try {
await this.copyFilePath(filePath, toFilePath, true);
return Promise.resolve();
}
catch (err) {
return Promise.reject(`CopyFileName: ${err}`);
}
}
else {
return Promise.reject('CopyFileName: cannot get the ' + 'filePath');
}
}
/**
* CopyFilePath
* Copy file Path
* @param filePath
* @param toFilePath
*/
async copyFilePath(filePath, toFilePath, overwrite) {
if (filePath.length !== 0 && toFilePath.length !== 0) {
// check filePath exists
const isPath = this.isPathExists(toFilePath);
if (!isPath || overwrite) {
try {
if (overwrite && isPath) {
await this.deleteFilePath(toFilePath);
}
this.NodeFs.copyFileSync(filePath, toFilePath);
}
catch (err) {
return Promise.reject(`CopyFilePath: ${err}`);
}
}
return Promise.resolve();
}
else {
return Promise.reject('CopyFilePath: cannot get the ' + 'filePath');
}
}
async copyFile(fromPath, fromFile, toPath, toFile) {
const fPath = this.Path.join(fromPath, fromFile);
const tPath = this.Path.join(toPath, toFile);
try {
this.NodeFs.copyFileSync(fPath, tPath);
return Promise.resolve();
}
catch (err) {
return Promise.reject(`CopyFile: ${err}`);
}
}
/**
* DeleteFileName
* Delete a file by its name
* @param fileName
*/
async deleteFileName(fileName) {
// get file path
const filePath = this.getFilePath(fileName);
if (filePath.length !== 0) {
try {
await this.deleteFilePath(filePath);
return Promise.resolve();
}
catch (err) {
return Promise.reject('DeleteFileName: delete filePath ' + `failed ${err}`);
}
}
else {
return Promise.reject('DeleteFileName: get filePath ' + 'failed');
}
}
/**
* DeleteFilePath
* Delete a file by its path
* @param filePath
*/
async deleteFilePath(filePath) {
let unlinkRetries = 50000;
/**
* On windows, the file lock behaves unpredictable. Often it claims a databsae file is locked / busy, although
* the file stream is already closed.
* Even though we already checked the status with the `waitForFilePathLock()` method previously.
*
* The only way to handle this reliably is to retry deletion until it works.
*/
const deleteFile = async () => {
try {
await promises_1.unlink(filePath);
}
catch (err) {
unlinkRetries--;
if (unlinkRetries > 0) {
await deleteFile();
}
else {
throw err;
}
}
};
if (filePath.length !== 0) {
// check if path exists
const isPath = this.isPathExists(filePath);
if (isPath) {
try {
await this.waitForFilePathLock(filePath);
// actually delete the file
await deleteFile();
return Promise.resolve();
}
catch (err) {
return Promise.reject(`DeleteFilePath: ${err}`);
}
}
else {
return Promise.resolve();
}
}
else {
return Promise.reject('DeleteFilePath: delete filePath' + 'failed');
}
}
async waitForFilePathLock(filePath, timeoutMS = 4000) {
let timeIsOver = false;
setTimeout(() => {
timeIsOver = true;
}, timeoutMS);
return new Promise((resolve, reject) => {
const check = async () => {
if (timeIsOver) {
reject(new Error(`WaitForFilePathLock: The resource is still locked / busy after ${timeoutMS} milliseconds.`));
return;
}
// check if path exists
const isPath = this.isPathExists(filePath);
// The file path does not exist. A non existant path cannot be locked.
if (!isPath) {
resolve();
return;
}
try {
const stream = await promises_1.open(filePath, 'r+');
// We need to close the stream afterwards, because otherwise, we're locking the file
await stream.close();
resolve();
}
catch (err) {
if (err.code === 'EBUSY') {
// The resource is busy. Retry in 100ms
setTimeout(() => {
check();
}, 100);
return;
}
else if (err.code === 'ENOENT') {
// The file does not exist (anymore). So it cannot be locked.
resolve();
return;
}
else {
// Something else went wrong.
reject(new Error(`WaitForFilePathLock: Error while checking the file: ${err}`));
}
}
};
check();
});
}
/**
* RenameFileName
* @param fileName
* @param toFileName
*/
async renameFileName(fileName, toFileName) {
// get File Paths
const filePath = this.getFilePath(fileName);
const toFilePath = this.getFilePath(toFileName);
if (filePath.length !== 0 && toFilePath.length !== 0) {
try {
await this.renameFilePath(filePath, toFilePath);
return Promise.resolve();
}
catch (err) {
return Promise.reject(`RenameFileName: ${err}`);
}
}
else {
return Promise.reject('RenameFileName: filePaths do not ' + 'exist');
}
}
/**
* RenameFilePath
* @param filePath
* @param toFilePath
*/
async renameFilePath(filePath, toFilePath) {
if (filePath.length !== 0 && toFilePath.length !== 0) {
// check filePath exists
const isPath = this.isPathExists(filePath);
if (isPath) {
// delete toFilePath if exists
try {
await this.deleteFilePath(toFilePath);
this.NodeFs.renameSync(filePath, toFilePath);
return Promise.resolve();
}
catch (err) {
return Promise.reject('RenameFilePath: ' + `${err}`);
}
}
else {
return Promise.reject(`RenameFilePath: ${filePath} does not exist`);
}
}
else {
return Promise.reject('RenameFilePath: filePath not found');
}
}
async moveDatabaseFromCache() {
const cachePath = this.getCachePath();
const databasePath = this.getDatabasesPath();
const dbCacheList = await this.getFileList(cachePath);
for (const name of dbCacheList) {
const ext = this.getExtName(name);
const fromDBName = this.Path.join(cachePath, name);
if (ext === '.db') {
const pDb = this.setPathSuffix(this.Path.join(databasePath, name));
try {
await this.renameFilePath(fromDBName, pDb);
}
catch (err) {
return Promise.reject('moveDatabaseFromCache: ' + `${err}`);
}
}
if (ext === '.zip') {
try {
await this.deleteFilePath(fromDBName);
}
catch (err) {
return Promise.reject('moveDatabaseFromCache: ' + `${err}`);
}
}
}
return Promise.resolve();
}
/**
* RestoreFileName
* @param fileName
* @param prefix
*/
async restoreFileName(fileName, prefix) {
const mFileName = `${prefix}-${fileName}`;
// check if file exists
const isFilePre = this.isFileExists(mFileName);
if (isFilePre) {
const isFile = this.isFileExists(fileName);
if (isFile) {
try {
await this.deleteFileName(fileName);
await this.renameFileName(mFileName, fileName);
return Promise.resolve();
}
catch (err) {
return Promise.reject('RestoreFileName: ' + `${err}`);
}
}
else {
return Promise.reject(`RestoreFileName: ${fileName} ` + 'does not exist');
}
}
else {
return Promise.reject(`RestoreFileName: ${mFileName} ` + 'does not exist');
}
}
/**
* DownloadFileFromHTTP
* @param url
* @param path
*/
async downloadFileFromHTTP(url, pathFolder) {
const res = await this.NodeFetch(url);
const ext = this.getExtName(url);
const dbName = this.getBaseName(url);
const filePath = `${this.Path.join(pathFolder, dbName)}${ext}`;
const fileStream = this.NodeFs.createWriteStream(filePath);
await new Promise((resolve, reject) => {
res.body.pipe(fileStream);
res.body.on('error', reject);
fileStream.on('finish', resolve);
});
}
readFileAsPromise(path, options) {
return new Promise((resolve, reject) => {
const fileStream = this.NodeFs.createReadStream(path, options);
const chunks = [];
fileStream.on('data', (data) => {
chunks.push(data);
});
fileStream.on('close', () => {
resolve(chunks.toString());
});
fileStream.on('error', (err) => {
const msg = err.message ? err.message : err;
reject(msg);
});
});
}
/**
* CreateFolderIfNotExists
* Create directory
* @param folder
*/
_createFolderIfNotExists(folder) {
let ret;
try {
if (!this.NodeFs.existsSync(folder)) {
this._mkdirSyncRecursive(folder);
}
ret = true;
}
catch (e) {
console.log('Error: in getDBPath', e);
ret = false;
}
return ret;
}
/**
* MkdirSyncRecursive
* Create directories recursively
* @param directory
*/
_mkdirSyncRecursive(directory) {
const sep = this.Path.sep;
const path = directory.replace(/\/$/, '').split(sep);
for (let i = 1; i <= path.length; i++) {
const segment = path.slice(0, i).join(sep);
segment.length > 0 && !this.NodeFs.existsSync(segment) ? this.NodeFs.mkdirSync(segment) : null;
}
return;
}
}
utilsFile.UtilsFile = UtilsFile;
return utilsFile;
}
var utilsSqlstatement = {};
var hasRequiredUtilsSqlstatement;
function requireUtilsSqlstatement () {
if (hasRequiredUtilsSqlstatement) return utilsSqlstatement;
hasRequiredUtilsSqlstatement = 1;
Object.defineProperty(utilsSqlstatement, "__esModule", { value: true });
utilsSqlstatement.UtilsSQLStatement = undefined;
class UtilsSQLStatement {
constructor() {
this.replaceString = (originalStr, searchStr, replaceStr) => {
const range = originalStr.indexOf(searchStr);
if (range !== -1) {
const modifiedStr = originalStr.substring(0, range) + replaceStr + originalStr.substring(range + searchStr.length);
return modifiedStr;
}
return originalStr;
};
}
extractTableName(statement) {
const pattern = /(?:INSERT\s+INTO|UPDATE|DELETE\s+FROM)\s+([^\s]+)/i;
const match = statement.match(pattern);
if (match?.[1]) {
const tableName = match[1];
return tableName;
}
return null;
}
extractWhereClause(statement) {
const pattern = /WHERE(.+?)(?:ORDER\s+BY|LIMIT|$)/i;
const match = statement.match(pattern);
if (match?.[1]) {
const whereClause = match[1].trim();
return whereClause;
}
return null;
}
addPrefixToWhereClause(whereClause, colNames, refNames, prefix) {
let columnValuePairs;
if (whereClause.includes('AND')) {
// Split the WHERE clause based on the "AND" keyword
const subSequenceArray = whereClause.split('AND');
columnValuePairs = subSequenceArray.map((pair) => pair.trim());
}
else {
columnValuePairs = [whereClause];
}
const modifiedPairs = columnValuePairs.map((pair) => {
const match = pair.match(/(\w+)\s*(=|IN|BETWEEN|LIKE)\s*(.+)/);
if (!match) {
return pair;
}
const column = match[1].trim();
const operator = match[2].trim();
const value = match[3].trim();
let newColumn = column;
const index = this.findIndexOfStringInArray(column, refNames);
if (index !== -1) {
newColumn = this.getStringAtIndex(colNames, index);
}
const modifiedColumn = `${prefix}${newColumn}`;
const ret = `${modifiedColumn} ${operator} ${value}`;
return ret;
});
return modifiedPairs.join(' AND ');
}
findIndexOfStringInArray(target, array) {
return array.indexOf(target);
}
getStringAtIndex(array, index) {
if (index >= 0 && index < array.length) {
return array[index];
}
else {
return undefined;
}
}
extractForeignKeyInfo(sqlStatement) {
// Define the regular expression pattern for extracting the FOREIGN KEY clause
const foreignKeyPattern = /\bFOREIGN\s+KEY\s*\(([^)]+)\)\s+REFERENCES\s+(\w+)\s*\(([^)]+)\)\s+(ON\s+DELETE\s+(RESTRICT|CASCADE|SET\s+NULL|SET\s+DEFAULT|NO\s+ACTION))?/;
const matches = sqlStatement.match(foreignKeyPattern);
if (matches) {
const foreignKeyInfo = {
forKeys: matches[1].split(',').map((key) => key.trim()),
tableName: matches[2],
refKeys: matches[3].split(',').map((key) => key.trim()),
action: matches[5] ? matches[5] : 'NO ACTION',
};
return foreignKeyInfo;
}
else {
throw new Error('extractForeignKeyInfo: No FOREIGN KEY found');
}
}
extractColumnNames(whereClause) {
const keywords = new Set(['AND', 'OR', 'IN', 'VALUES', 'LIKE', 'BETWEEN', 'NOT']);
const regex = /\b[a-zA-Z]\w*\b(?=\s*(?:<=?|>=?|<>?|=|AND|OR|BETWEEN|NOT|IN|LIKE))|\b[a-zA-Z]\w*\b\s+BETWEEN\s+'[^']+'\s+AND\s+'[^']+'|\(([^)]+)\)\s+IN\s+\(?\s*VALUES\s*\(/g;
let match;
const columns = [];
while ((match = regex.exec(whereClause)) !== null) {
const columnList = match[1];
if (columnList) {
const columnNamesArray = columnList.split(',');
for (const columnName of columnNamesArray) {
columns.push(columnName.trim());
}
}
else {
const matchedText = match[0];
if (!keywords.has(matchedText.trim().toUpperCase())) {
columns.push(matchedText.trim());
}
}
}
return columns;
}
flattenMultilineString(input) {
const lines = input.split(/\r?\n/);
return lines.join(' ');
}
extractCombinedPrimaryKey(whereClause) {
const pattern = /WHERE\s*\((.+?)\)\s*(?:=|IN)\s*\((.+?)\)/g;
const regex = new RegExp(pattern);
const matches = whereClause.matchAll(regex);
const primaryKeySets = [];
for (const match of matches) {
const keysString = match[1].trim();
const keys = keysString.split(',').map((key) => key.trim());
primaryKeySets.push(keys);
}
return primaryKeySets.length === 0 ? null : primaryKeySets;
}
getWhereStmtForCombinedPK(whStmt, withRefs, colNames, keys) {
let retWhere = whStmt;
for (const grpKeys of keys) {
const repKeys = grpKeys.join(',') === withRefs.join(',') ? colNames : withRefs;
for (const [index, key] of grpKeys.entries()) {
retWhere = this.replaceAllString(retWhere, key, repKeys[index]);
}
}
return retWhere;
}
replaceAllString(originalStr, searchStr, replaceStr) {
return originalStr.split(searchStr).join(replaceStr);
}
indicesOf(str, searchStr, fromIndex = 0) {
// Helper function to find indices of a substring within a string
const indices = [];
let currentIndex = str.indexOf(searchStr, fromIndex);
while (currentIndex !== -1) {
indices.push(currentIndex);
currentIndex = str.indexOf(searchStr, currentIndex + 1);
}
return indices;
}
getWhereStmtForNonCombinedPK(whStmt, withRefs, colNames) {
let whereStmt = '';
let stmt = whStmt.substring(6);
for (let idx = 0; idx < withRefs.length; idx++) {
let colType = 'withRefsNames';
let idxs = this.indicesOf(stmt, withRefs[idx]);
if (idxs.length === 0) {
idxs = this.indicesOf(stmt, colNames[idx]);
colType = 'colNames';
}
if (idxs.length > 0) {
let valStr = '';
const indicesEqual = this.indicesOf(stmt, '=', idxs[0]);
if (indicesEqual.length > 0) {
const indicesAnd = this.indicesOf(stmt, 'AND', indicesEqual[0]);
if (indicesAnd.length > 0) {
valStr = stmt.substring(indicesEqual[0] + 1, indicesAnd[0] - 1);
stmt = stmt.substring(indicesAnd[0] + 3);
}
else {
valStr = stmt.substring(indicesEqual[0] + 1);
}
if (idx > 0) {
whereStmt += ' AND ';
}
if (colType === 'withRefsNames') {
whereStmt += colNames[idx] + ' = ' + valStr;
}
else {
whereStmt += withRefs[idx] + ' = ' + valStr;
}
}
}
}
whereStmt = 'WHERE ' + whereStmt;
return whereStmt;
}
updateWhere(whStmt, withRefs, colNames) {
let whereStmt = '';
if (whStmt.length <= 0) {
return whereStmt;
}
if (whStmt.toUpperCase().substring(0, 5) !== 'WHERE') {
return whereStmt;
}
if (withRefs.length === colNames.length) {
// get whereStmt for primary combined key
const keys = this.extractCombinedPrimaryKey(whStmt);
if (keys) {
whereStmt = this.getWhereStmtForCombinedPK(whStmt, withRefs, colNames, keys);
}
else {
// get for non primary combined key
whereStmt = this.getWhereStmtForNonCombinedPK(whStmt, withRefs, colNames);
}
}
return whereStmt;
}
}
utilsSqlstatement.UtilsSQLStatement = UtilsSQLStatement;
return utilsSqlstatement;
}
var hasRequiredUtilsSQLite;
function requireUtilsSQLite () {
if (hasRequiredUtilsSQLite) return utilsSQLite;
hasRequiredUtilsSQLite = 1;
Object.defineProperty(utilsSQLite, "__esModule", { value: true });
utilsSQLite.UtilsSQLite = undefined;
const UtilsSQL92Compatibility_1 = requireUtilsSQL92Compatibility();
const utilsDelete_1 = requireUtilsDelete();
const utilsFile_1 = requireUtilsFile();
const utilsSqlstatement_1 = requireUtilsSqlstatement();
//const SQLITE_OPEN_READONLY = 1;
class UtilsSQLite {
constructor() {
this.fileUtil = new utilsFile_1.UtilsFile();
this.statUtil = new utilsSqlstatement_1.UtilsSQLStatement();
this.delUtil = new utilsDelete_1.UtilsDelete();
this.sql92Utils = new UtilsSQL92Compatibility_1.UtilsSQL92Compatibility();
this.BCSQLite3 = require$$4$1;
}
/**
* OpenOrCreateDatabase
* @param pathDB
* @param password
*/
openOrCreateDatabase(pathDB, password, readonly) {
const msg = 'OpenOrCreateDatabase';
// open sqlite3 database
let mDB;
if (!readonly) {
mDB = new this.BCSQLite3(pathDB, {
// verbose: console.log,
fileMustExist: false,
});
}
else {
mDB = new this.BCSQLite3(pathDB, {
// verbose: console.log,
readonly: true,
fileMustExist: true,
});
}
if (mDB != null) {
try {
this.dbChanges(mDB);
}
catch (err) {
const errmsg = err.message ? err.message : err;
throw new Error(`${msg} ${errmsg}`);
}
try {
// set the password
if (password.length > 0) {
this.setCipherPragma(mDB, password);
}
// set Foreign Keys On
this.setForeignKeyConstraintsEnabled(mDB, true);
}
catch (err) {
const errmsg = err.message ? err.message : err;
throw new Error(`${msg} ${errmsg}`);
}
return mDB;
}
else {
throw new Error(msg + 'open database failed');
}
}
/**
* SetCipherPragma
* @param mDB
* @param password
*/
setCipherPragma(mDB, passphrase) {
const msg = 'setCipherPragma';
try {
mDB.pragma(`cipher='sqlcipher'`);
mDB.pragma(`legacy=4`);
mDB.pragma(`key='${passphrase}'`);
return;
}
catch (err) {
const errmsg = err.message ? err.message : err;
throw new Error(`${msg} ${errmsg}`);
}
}
/**
* SetForeignKeyConstraintsEnabled
* @param mDB
* @param toggle
*/
setForeignKeyConstraintsEnabled(mDB, toggle) {
const msg = 'SetForeignKeyConstraintsEnabled';
let key = 'OFF';
if (toggle) {
key = 'ON';
}
try {
mDB.pragma(`foreign_keys = '${key}'`);
return;
}
catch (err) {
const errmsg = err.message ? err.message : err;
throw new Error(`${msg} ${errmsg}`);
}
}
/**
* CloseDB
* @param mDB
*/
closeDB(mDB) {
const msg = 'closeDB';
try {
mDB.close();
return;
}
catch (err) {
const errmsg = err.message ? err.message : err;
throw new Error(`${msg} ${errmsg}`);
}
}
/**
* GetVersion
* @param mDB
*/
getVersion(mDB) {
const msg = 'GetVersion';
try {
const result = mDB.pragma('user_version');
return result[0].user_version;
}
catch (err) {
const errmsg = err.message ? err.message : err;
throw new Error(`${msg} ${errmsg}`);
}
}
/**
* SetVersion
* @param mDB
* @param version
*/
setVersion(mDB, version) {
const msg = 'SetVersion';
try {
mDB.pragma(`user_version = '${version}'`);
return;
}
catch (err) {
const errmsg = err.message ? err.message : err;
throw new Error(`${msg} ${errmsg}`);
}
}
/**
* ChangePassword
* @param pathDB
* @param password
* @param newpassword
*/
changePassword(pathDB, password, newpassword) {
let mDB;
const msg = 'ChangePassword';
try {
mDB = this.openOrCreateDatabase(pathDB, password, false);
this.pragmaReKey(mDB, password, newpassword);
}
catch (err) {
const errmsg = err.message ? err.message : err;
throw new Error(`${msg} ${errmsg}`);
}
finally {
this.closeDB(mDB);
}
return;
}
/**
* PragmaReKey
* @param mDB
* @param passphrase
* @param newpassphrase
*/
pragmaReKey(mDB, passphrase, newpassphrase) {
const msg = 'PragmaReKey: ';
try {
mDB.pragma(`cipher='sqlcipher'`);
mDB.pragma(`legacy=4`);
mDB.pragma(`key='${passphrase}'`);
mDB.pragma(`rekey='${newpassphrase}'`);
return;
}
catch (err) {
const errmsg = err.message ? err.message : err;
throw new Error(`${msg} ${errmsg}`);
}
}
/**
* BeginTransaction
* @param db
* @param isOpen
*/
beginTransaction(db, isOpen) {
// eslint-disable-next-line no-async-promise-executor
const msg = 'BeginTransaction: ';
if (!isOpen) {
throw new Error(`${msg} database not opened`);
}
const sql = 'BEGIN TRANSACTION;';
try {
db.exec(sql);
return;
}
catch (err) {
const errmsg = err.message ? err.message : err;
throw new Error(`${msg} ${errmsg}`);
}
}
/**
* RollbackTransaction
* @param db
* @param isOpen
*/
rollbackTransaction(db, isOpen) {
const msg = 'RollbackTransaction: ';
if (!isOpen) {
throw new Error(`${msg} database not opened`);
}
const sql = 'ROLLBACK TRANSACTION;';