From 16ccba6725be0d41192ec169a2e1e69425429ec5 Mon Sep 17 00:00:00 2001 From: Sebastian-Webster <84299475+Sebastian-Webster@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:21:05 +1200 Subject: [PATCH] build - 1.2.0 --- dist/src/index.js | 31 ++-- dist/src/libraries/Downloader.d.ts | 4 +- dist/src/libraries/Downloader.js | 19 ++- dist/src/libraries/Executor.d.ts | 1 + dist/src/libraries/Executor.js | 228 +++++++++++++++++++---------- dist/src/libraries/Logger.js | 6 +- dist/src/libraries/Version.js | 7 +- dist/types/index.d.ts | 4 + package.json | 2 +- 9 files changed, 196 insertions(+), 106 deletions(-) diff --git a/dist/src/index.js b/dist/src/index.js index 80fbc7b..8110960 100644 --- a/dist/src/index.js +++ b/dist/src/index.js @@ -35,19 +35,24 @@ const AbortSignal_1 = __importDefault(require("./libraries/AbortSignal")); const Version_1 = __importDefault(require("./libraries/Version")); const versions_json_1 = __importDefault(require("./versions.json")); const Downloader_1 = require("./libraries/Downloader"); -const defaultOptions = { - dbName: 'dbdata', - logLevel: 'ERROR', - portRetries: 10, - downloadBinaryOnce: true, - lockRetries: 1000, - lockRetryWait: 1000, - username: 'root' -}; +const crypto_1 = require("crypto"); +const path_1 = require("path"); process.on('exit', () => { AbortSignal_1.default.abort('Process is exiting'); }); -async function createDB(opts = defaultOptions) { +async function createDB(opts) { + const defaultOptions = { + dbName: 'dbdata', + logLevel: 'ERROR', + portRetries: 10, + downloadBinaryOnce: true, + lockRetries: 1000, + lockRetryWait: 1000, + username: 'root', + deleteDBAfterStopped: true, + //mysqlmsn = MySQL Memory Server Node.js + dbPath: (0, path_1.normalize)(`${os.tmpdir()}/mysqlmsn/dbs/${(0, crypto_1.randomUUID)().replace(/-/g, '')}`) + }; const options = { ...defaultOptions, ...opts }; const logger = new Logger_1.default(options.logLevel); const executor = new Executor_1.default(logger); @@ -63,16 +68,16 @@ async function createDB(opts = defaultOptions) { catch (e) { logger.error(e); if (options.version) { - throw `A MySQL version ${options.version} binary could not be found that supports your OS (${os.platform()} | ${os.version()}) and CPU architecture (${os.arch()}). Please check you have the latest version of mysql-memory-server. If the latest version still doesn't support the version you want to use, feel free to make a pull request to add support!`; + throw `A MySQL version ${options.version} binary could not be found that supports your OS (${os.platform()} | ${os.version()} | ${os.release()}) and CPU architecture (${os.arch()}). Please check you have the latest version of mysql-memory-server. If the latest version still doesn't support the version you want to use, feel free to make a pull request to add support!`; } - throw `A MySQL binary could not be found that supports your OS (${os.platform()} | ${os.version()}) and CPU architecture (${os.arch()}). Please check you have the latest version of mysql-memory-server. If the latest version still doesn't support your OS and CPU architecture, feel free to make a pull request to add support!`; + throw `A MySQL binary could not be found that supports your OS (${os.platform()} | ${os.version()} | ${os.release()}) and CPU architecture (${os.arch()}). Please check you have the latest version of mysql-memory-server. If the latest version still doesn't support your OS and CPU architecture, feel free to make a pull request to add support!`; } try { binaryFilepath = await (0, Downloader_1.downloadBinary)(binaryInfo, options, logger); } catch (error) { logger.error('Failed to download binary'); - throw error; + throw `Failed to download binary. The error was: "${error}"`; } logger.log('Running downloaded binary'); return await executor.startMySQL(options, binaryFilepath); diff --git a/dist/src/libraries/Downloader.d.ts b/dist/src/libraries/Downloader.d.ts index 7883c17..efe82bf 100644 --- a/dist/src/libraries/Downloader.d.ts +++ b/dist/src/libraries/Downloader.d.ts @@ -1,4 +1,4 @@ import Logger from './Logger'; -import { BinaryInfo, ServerOptions } from '../../types'; +import { BinaryInfo, InternalServerOptions } from '../../types'; export declare function downloadVersions(): Promise; -export declare function downloadBinary(binaryInfo: BinaryInfo, options: ServerOptions, logger: Logger): Promise; +export declare function downloadBinary(binaryInfo: BinaryInfo, options: InternalServerOptions, logger: Logger): Promise; diff --git a/dist/src/libraries/Downloader.js b/dist/src/libraries/Downloader.js index 3f7861e..cd7789f 100644 --- a/dist/src/libraries/Downloader.js +++ b/dist/src/libraries/Downloader.js @@ -77,26 +77,31 @@ function downloadVersions() { function downloadFromCDN(url, downloadLocation, logger) { return new Promise((resolve, reject) => { const fileStream = fs.createWriteStream(downloadLocation); + let error; fileStream.on('open', () => { const request = https.get(url, (response) => { response.pipe(fileStream); }); request.on('error', (err) => { + error = err; logger.error(err); fileStream.close(); - fs.unlink(downloadLocation, (err) => { - reject(err); + fs.unlink(downloadLocation, () => { + reject(err.message); }); }); }); fileStream.on('finish', () => { - resolve(); + if (!error) { + resolve(); + } }); fileStream.on('error', (err) => { + error = err; logger.error(err); fileStream.end(); fs.unlink(downloadLocation, () => { - reject(err); + reject(err.message); }); }); }); @@ -106,7 +111,11 @@ function extractBinary(url, archiveLocation, extractedLocation) { const lastDashIndex = url.lastIndexOf('-'); const fileExtension = url.slice(lastDashIndex).split('.').splice(1).join('.'); await fsPromises.mkdir(extractedLocation, { recursive: true }); - const folderName = url.split('/').at(-1).replace(`.${fileExtension}`, ''); + const mySQLFolderName = url.split('/').at(-1); + if (!mySQLFolderName) { + return reject(`Folder name is undefined for url: ${url}`); + } + const folderName = mySQLFolderName.replace(`.${fileExtension}`, ''); if (fileExtension === 'zip') { //Only Windows MySQL files use the .zip extension const zip = new adm_zip_1.default(archiveLocation); diff --git a/dist/src/libraries/Executor.d.ts b/dist/src/libraries/Executor.d.ts index b766546..0cf1a3e 100644 --- a/dist/src/libraries/Executor.d.ts +++ b/dist/src/libraries/Executor.d.ts @@ -4,6 +4,7 @@ declare class Executor { #private; logger: Logger; constructor(logger: Logger); + deleteDatabaseDirectory(path: string): Promise; getMySQLVersion(preferredVersion?: string): Promise; startMySQL(options: InternalServerOptions, binaryFilepath: string): Promise; } diff --git a/dist/src/libraries/Executor.js b/dist/src/libraries/Executor.js index e11a9e7..3384c96 100644 --- a/dist/src/libraries/Executor.js +++ b/dist/src/libraries/Executor.js @@ -30,7 +30,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function ( var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; -var _Executor_instances, _Executor_execute, _Executor_startMySQLProcess; +var _Executor_instances, _Executor_execute, _Executor_killProcess, _Executor_startMySQLProcess, _Executor_setupDataDirectories; Object.defineProperty(exports, "__esModule", { value: true }); const child_process_1 = require("child_process"); const semver_1 = require("semver"); @@ -40,12 +40,34 @@ const fs = __importStar(require("fs")); const Port_1 = require("./Port"); const AbortSignal_1 = __importDefault(require("./AbortSignal")); const path_1 = require("path"); -const crypto_1 = require("crypto"); class Executor { constructor(logger) { _Executor_instances.add(this); this.logger = logger; } + async deleteDatabaseDirectory(path) { + let retries = 0; + //Maximum wait of 10 seconds | 500ms * 20 retries = 10,000ms = 10 seconds + const waitTime = 500; + const maxRetries = 20; + //Since the database processes are killed instantly (SIGKILL) sometimes the database file handles may still be open + //This would cause an EBUSY error. Retrying the deletions for 10 seconds should give the OS enough time to close + //the file handles. + while (retries <= maxRetries) { + try { + await fsPromises.rm(path, { recursive: true, force: true }); + return; + } + catch (e) { + if (retries === maxRetries) { + throw e; + } + await new Promise(resolve => setTimeout(resolve, waitTime)); + retries++; + this.logger.log('DB data directory deletion failed. Now on retry', retries); + } + } + } getMySQLVersion(preferredVersion) { return new Promise(async (resolve, reject) => { if (process.platform === 'win32') { @@ -55,6 +77,7 @@ class Executor { if (servers.length === 0) { return resolve(null); } + this.logger.log(servers); const versions = []; for (const dir of servers) { const path = `${process.env.PROGRAMFILES}\\MySQL\\${dir}\\bin\\mysqld`; @@ -104,51 +127,35 @@ class Executor { } }); } - startMySQL(options, binaryFilepath) { - return new Promise(async (resolve, reject) => { - //mysqlmsn = MySQL Memory Server Node.js - const dbPath = (0, path_1.normalize)(`${os.tmpdir()}/mysqlmsn/dbs/${(0, crypto_1.randomUUID)().replace(/-/g, '')}`); - const datadir = (0, path_1.normalize)(`${dbPath}/data`); - this.logger.log('Created data directory for database at:', datadir); - await fsPromises.mkdir(datadir, { recursive: true }); - const { error: err, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, `"${binaryFilepath}" --no-defaults --datadir=${datadir} --initialize-insecure`); - if (err || (stderr && !stderr.includes('InnoDB initialization has ended'))) { - if (process.platform === 'win32' && err.message.includes('Command failed')) { - this.logger.error(err || stderr); - return reject('The mysqld command failed to run. A possible cause is that the Microsoft Visual C++ Redistributable Package is not installed. MySQL 5.7.40 and newer requires Microsoft Visual C++ Redistributable Package 2019 to be installed. Check the MySQL docs for Microsoft Visual C++ requirements for other MySQL versions. If you are sure you have this installed, check the error message in the console for more details.'); - } - if (process.platform === 'linux' && err.message.includes('libaio.so')) { - this.logger.error(err || stderr); - return reject('The mysqld command failed to run. MySQL needs the libaio package installed on Linux systems to run. Do you have this installed? Learn more at https://dev.mysql.com/doc/refman/en/binary-installation.html'); - } - return reject(err || stderr); - } - let initText = `CREATE DATABASE ${options.dbName};`; - if (options.username !== 'root') { - initText += `RENAME USER 'root'@'localhost' TO '${options.username}'@'localhost';`; + async startMySQL(options, binaryFilepath) { + let retries = 0; + const datadir = (0, path_1.normalize)(`${options.dbPath}/data`); + do { + await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, binaryFilepath, datadir); + const port = (0, Port_1.GenerateRandomPort)(); + const mySQLXPort = (0, Port_1.GenerateRandomPort)(); + this.logger.log('Using port:', port, 'and MySQLX port:', mySQLXPort, 'on retry:', retries); + try { + this.logger.log('Starting MySQL process'); + const resolved = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_startMySQLProcess).call(this, options, port, mySQLXPort, datadir, options.dbPath, binaryFilepath); + this.logger.log('Starting process was successful'); + return resolved; } - await fsPromises.writeFile(`${dbPath}/init.sql`, initText, { encoding: 'utf8' }); - let retries = 0; - do { - const port = (0, Port_1.GenerateRandomPort)(); - const mySQLXPort = (0, Port_1.GenerateRandomPort)(); - this.logger.log('Using port:', port, 'on retry:', retries); - try { - const resolved = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_startMySQLProcess).call(this, options, port, mySQLXPort, datadir, dbPath, binaryFilepath); - return resolve(resolved); + catch (e) { + this.logger.warn('Caught error:', e, `\nRetries: ${retries} | options.portRetries: ${options.portRetries}`); + if (e !== 'Port is already in use') { + this.logger.error('Error:', e); + throw e; } - catch (e) { - if (e !== 'Port is already in use') { - return reject(e); - } - retries++; - if (retries < options.portRetries) { - this.logger.warn(`One or both of these ports are already in use: ${port} or ${mySQLXPort}. Now retrying... ${retries}/${options.portRetries} possible retries.`); - } + retries++; + if (retries <= options.portRetries) { + this.logger.warn(`One or both of these ports are already in use: ${port} or ${mySQLXPort}. Now retrying... ${retries}/${options.portRetries} possible retries.`); } - } while (retries < options.portRetries); - reject(`The port has been retried ${options.portRetries} times and a free port could not be found.\nEither try again, or if this is a common issue, increase options.portRetries.`); - }); + else { + throw `The port has been retried ${options.portRetries} times and a free port could not be found.\nEither try again, or if this is a common issue, increase options.portRetries.`; + } + } + } while (retries <= options.portRetries); } } _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_execute(command) { @@ -157,43 +164,84 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec resolve({ error, stdout, stderr }); }); }); +}, _Executor_killProcess = async function _Executor_killProcess(process) { + let killed = false; + if (os.platform() === 'win32') { + const { error, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, `taskkill /pid ${process.pid} /t /f`); + if (!error && !stderr) { + killed = true; + } + else { + this.logger.error(error || stderr); + } + } + else { + killed = process.kill(); + } + return killed; }, _Executor_startMySQLProcess = function _Executor_startMySQLProcess(options, port, mySQLXPort, datadir, dbPath, binaryFilepath) { const errors = []; const logFile = `${dbPath}/log.log`; + const errorLogFile = `${datadir}/errorlog.err`; return new Promise(async (resolve, reject) => { await fsPromises.rm(logFile, { force: true }); - const process = (0, child_process_1.spawn)(binaryFilepath, ['--no-defaults', `--port=${port}`, `--datadir=${datadir}`, `--mysqlx-port=${mySQLXPort}`, `--mysqlx-socket=${dbPath}/x.sock`, `--socket=${dbPath}/m.sock`, `--general-log-file=${logFile}`, '--general-log=1', `--init-file=${dbPath}/init.sql`, '--bind-address=127.0.0.1', '--innodb-doublewrite=OFF'], { signal: AbortSignal_1.default.signal, killSignal: 'SIGKILL' }); + const process = (0, child_process_1.spawn)(binaryFilepath, ['--no-defaults', `--port=${port}`, `--datadir=${datadir}`, `--mysqlx-port=${mySQLXPort}`, `--mysqlx-socket=${dbPath}/x.sock`, `--socket=${dbPath}/m.sock`, `--general-log-file=${logFile}`, '--general-log=1', `--init-file=${dbPath}/init.sql`, '--bind-address=127.0.0.1', '--innodb-doublewrite=OFF', '--mysqlx=FORCE', `--log-error=${errorLogFile}`], { signal: AbortSignal_1.default.signal, killSignal: 'SIGKILL' }); //resolveFunction is the function that will be called to resolve the promise that stops the database. //If resolveFunction is not undefined, the database has received a kill signal and data cleanup procedures should run. //Once ran, resolveFunction will be called. let resolveFunction; process.on('close', async (code, signal) => { - const errorString = errors.join('\n'); - if (errorString.includes('Address already in use')) { + let errorLog; + try { + errorLog = await fsPromises.readFile(errorLogFile, { encoding: 'utf-8' }); + } + catch (e) { + errorLog = `ERROR WHILE READING LOG: ${e}`; + } + const portIssue = errorLog.includes("Do you already have another mysqld server running"); + const xPortIssue = errorLog.includes("X Plugin can't bind to it"); + this.logger.log('Exiting because of a port issue:', portIssue, '. MySQL X Plugin failed to bind:', xPortIssue); + if (portIssue || xPortIssue) { + this.logger.log('Error log when exiting for port in use error:', errorLog); + try { + await this.deleteDatabaseDirectory(options.dbPath); + } + catch (e) { + this.logger.error(e); + return reject(`MySQL failed to listen on a certain port. To restart MySQL with a different port, the database directory needed to be deleted. An error occurred while deleting the database directory. Aborting. The error was: ${e}`); + } return reject('Port is already in use'); } try { - await fsPromises.rm(dbPath, { recursive: true, force: true }); - if (binaryFilepath.includes(os.tmpdir()) && !options.downloadBinaryOnce) { - const splitPath = binaryFilepath.split(os.platform() === 'win32' ? '\\' : '/'); - const binariesIndex = splitPath.indexOf('binaries'); - //The path will be the directory path for the binary download - splitPath.splice(binariesIndex + 2); - //Delete the binary folder - await fsPromises.rm(splitPath.join('/'), { force: true, recursive: true }); + if (options.deleteDBAfterStopped) { + await this.deleteDatabaseDirectory(dbPath); } } finally { - if (resolveFunction) { - resolveFunction(); - return; - } - if (code === 0) { - return reject('Database exited early'); + try { + if (binaryFilepath.includes(os.tmpdir()) && !options.downloadBinaryOnce) { + const splitPath = binaryFilepath.split(os.platform() === 'win32' ? '\\' : '/'); + const binariesIndex = splitPath.indexOf('binaries'); + //The path will be the directory path for the binary download + splitPath.splice(binariesIndex + 2); + //Delete the binary folder + await fsPromises.rm(splitPath.join('/'), { force: true, recursive: true }); + } } - if (code) { - this.logger.error(errorString); - return reject(errorString); + finally { + if (resolveFunction) { + resolveFunction(); + return; + } + const errorString = errors.join('\n'); + if (code === 0) { + return reject(`Database exited early.\nError log: ${errorLog}\nError string: "${errorString}`); + } + if (code) { + const errorMessage = `The database exited early with code ${code}. The error log was:\n${errorLog}\nThe error string was: "${errorString}".`; + this.logger.error(errorMessage); + return reject(errorMessage); + } } } }); @@ -207,12 +255,22 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec } } }); - fs.watchFile(logFile, async (curr) => { + fs.watchFile(errorLogFile, async (curr) => { if (curr.dev !== 0) { //File exists - const file = await fsPromises.readFile(logFile, { encoding: 'utf8' }); - if (file.includes('started with:')) { - fs.unwatchFile(logFile); + const file = await fsPromises.readFile(errorLogFile, { encoding: 'utf8' }); + if (file.includes("X Plugin can't bind to it")) { + //As stated in the MySQL X Plugin documentation at https://dev.mysql.com/doc/refman/8.4/en/x-plugin-options-system-variables.html#sysvar_mysqlx_bind_address + //when the MySQL X Plugin fails to bind to an address, it does not prevent the MySQL server startup because MySQL X is not a mandatory plugin. + //It doesn't seem like there is a way to prevent server startup when that happens. The workaround to that is to shutdown the MySQL server ourselves when the X plugin + //cannot bind to an address. If there is a way to prevent server startup when binding fails, this workaround can be removed. + const killed = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_killProcess).call(this, process); + if (!killed) { + reject('Failed to kill MySQL process to retry listening on a free port.'); + } + } + else if (file.includes('ready for connections. Version:')) { + fs.unwatchFile(errorLogFile); resolve({ port, xPort: mySQLXPort, @@ -221,19 +279,7 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec stop: () => { return new Promise(async (resolve, reject) => { resolveFunction = resolve; - let killed = false; - if (os.platform() === 'win32') { - const { error, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, `taskkill /pid ${process.pid} /t /f`); - if (!error && !stderr) { - killed = true; - } - else { - this.logger.error(error || stderr); - } - } - else { - killed = process.kill(); - } + const killed = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_killProcess).call(this, process); if (!killed) { reject(); } @@ -244,5 +290,25 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec } }); }); +}, _Executor_setupDataDirectories = async function _Executor_setupDataDirectories(options, binaryFilepath, datadir) { + this.logger.log('Created data directory for database at:', datadir); + await fsPromises.mkdir(datadir, { recursive: true }); + const { error: err, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, `"${binaryFilepath}" --no-defaults --datadir=${datadir} --initialize-insecure`); + if (err || (stderr && !stderr.includes('InnoDB initialization has ended'))) { + if (process.platform === 'win32' && ((err === null || err === void 0 ? void 0 : err.message.includes('Command failed')) || stderr.includes('Command failed'))) { + this.logger.error(err || stderr); + throw 'The mysqld command failed to run. A possible cause is that the Microsoft Visual C++ Redistributable Package is not installed. MySQL 5.7.40 and newer requires Microsoft Visual C++ Redistributable Package 2019 to be installed. Check the MySQL docs for Microsoft Visual C++ requirements for other MySQL versions. If you are sure you have this installed, check the error message in the console for more details.'; + } + if (process.platform === 'linux' && ((err === null || err === void 0 ? void 0 : err.message.includes('libaio.so')) || stderr.includes('libaio.so'))) { + this.logger.error(err || stderr); + throw 'The mysqld command failed to run. MySQL needs the libaio package installed on Linux systems to run. Do you have this installed? Learn more at https://dev.mysql.com/doc/refman/en/binary-installation.html'; + } + throw err || stderr; + } + let initText = `CREATE DATABASE ${options.dbName};`; + if (options.username !== 'root') { + initText += `\nRENAME USER 'root'@'localhost' TO '${options.username}'@'localhost';`; + } + await fsPromises.writeFile(`${options.dbPath}/init.sql`, initText, { encoding: 'utf8' }); }; exports.default = Executor; diff --git a/dist/src/libraries/Logger.js b/dist/src/libraries/Logger.js index 708bc9f..50bae0c 100644 --- a/dist/src/libraries/Logger.js +++ b/dist/src/libraries/Logger.js @@ -11,17 +11,17 @@ class Logger { } log(...args) { if (this.LOG_LEVEL === 0) { - console.log.apply(null, args); + console.log(...args); } } warn(...args) { if (this.LOG_LEVEL <= 1) { - console.warn.apply(null, args); + console.warn(...args); } } error(...args) { if (this.LOG_LEVEL <= 2) { - console.error.apply(null, args); + console.error(...args); } } } diff --git a/dist/src/libraries/Version.js b/dist/src/libraries/Version.js index 43dbe50..b05ec9b 100644 --- a/dist/src/libraries/Version.js +++ b/dist/src/libraries/Version.js @@ -34,7 +34,12 @@ function getBinaryURL(versions, versionToGet = "9.x") { availableVersions = availableVersions.filter(v => v.os === process.platform); if (availableVersions.length === 0) throw `No MySQL binary could be found for your OS: ${process.platform}`; - availableVersions = availableVersions.filter(v => (0, semver_1.satisfies)((0, semver_1.coerce)(os.release()).version, v.osKernelVersionsSupported)); + availableVersions = availableVersions.filter(v => { + const release = (0, semver_1.coerce)(os.release()); + if (!release) + return false; + return (0, semver_1.satisfies)(release.version, v.osKernelVersionsSupported); + }); if (availableVersions.length === 0) throw `No MySQL binary could be found that supports your OS version: ${os.release()} | ${os.version()}`; const wantedVersions = availableVersions.filter(v => (0, semver_1.satisfies)(v.version, versionToGet)); diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts index e2cac48..274ef56 100644 --- a/dist/types/index.d.ts +++ b/dist/types/index.d.ts @@ -9,6 +9,8 @@ export type ServerOptions = { lockRetries?: number; lockRetryWait?: number; username?: string; + deleteDBAfterStopped?: boolean; + dbPath?: string; }; export type InternalServerOptions = { version?: string; @@ -19,6 +21,8 @@ export type InternalServerOptions = { lockRetries: number; lockRetryWait: number; username: string; + deleteDBAfterStopped: boolean; + dbPath: string; }; export type ExecutorOptions = { logLevel: LOG_LEVEL; diff --git a/package.json b/package.json index e3d6eb2..242d426 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mysql-memory-server", - "version": "1.1.0", + "version": "1.2.0", "description": "Spin up an ephemeral MySQL database from your JavaScript code", "main": "dist/src/index.js", "types": "dist/src/index.d.ts",