mysql-memory-server 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -7
- package/dist/src/constants.d.ts +4 -0
- package/dist/src/constants.js +6 -0
- package/dist/src/index.js +31 -14
- package/dist/src/libraries/Downloader.d.ts +2 -2
- package/dist/src/libraries/Downloader.js +16 -27
- package/dist/src/libraries/Executor.d.ts +1 -0
- package/dist/src/libraries/Executor.js +248 -82
- package/dist/src/libraries/FileLock.d.ts +2 -0
- package/dist/src/libraries/FileLock.js +25 -0
- package/dist/src/libraries/Logger.js +3 -3
- package/dist/src/libraries/Version.js +6 -1
- package/dist/types/index.d.ts +6 -0
- package/package.json +14 -5
package/README.md
CHANGED
|
@@ -10,15 +10,20 @@ Download with your package manager of choice. The package name is `mysql-memory-
|
|
|
10
10
|
|
|
11
11
|
#### Requirements
|
|
12
12
|
|
|
13
|
-
- Node.js 16
|
|
14
|
-
- macOS 13+, Windows, or Linux (Only Ubuntu has been tested. Other Linux distributions may or may not work at this time.
|
|
13
|
+
- Node.js 16.6.0 and newer
|
|
14
|
+
- macOS 13+, Windows, or Linux (Only Ubuntu has been tested. Other Linux distributions may or may not work at this time.)
|
|
15
15
|
|
|
16
16
|
Windows only requirements:
|
|
17
17
|
- `Microsoft Visual C++ 2019 Redistributable Package` needs to be installed
|
|
18
18
|
|
|
19
19
|
Linux only requirements:
|
|
20
|
-
- The `libaio1` package needs to be installed
|
|
21
|
-
-
|
|
20
|
+
- The `libaio1` or `libaio1t64` package needs to be installed
|
|
21
|
+
- If `libaio1` is not available but `libaio1t64` is, the `ldconfig` command needs to be available to run
|
|
22
|
+
- The `tar` package needs to be installed if you want to use MySQL versions that aren't system installed
|
|
23
|
+
|
|
24
|
+
Currently supported MySQL versions:
|
|
25
|
+
- If using the system installed MySQL server: 8.0.20 and newer
|
|
26
|
+
- If not using the system installed MySQL server: 8.0.39, 8.1.0, 8.2.0, 8.3.0, 8.4.2, 9.0.1
|
|
22
27
|
|
|
23
28
|
## Usage
|
|
24
29
|
|
|
@@ -72,7 +77,13 @@ Default: undefined
|
|
|
72
77
|
|
|
73
78
|
Description: Version of MySQL to use for the database. Uses semver for getting the version, so valid semver versions are allowed. For example, `8.x` is a valid version and will use the latest 8.x MySQL version.
|
|
74
79
|
|
|
75
|
-
If left undefined
|
|
80
|
+
If left undefined:
|
|
81
|
+
- If the system has MySQL installed, the system-installed version will be used. If the installed version is not supported by this package (currently <8.0.20), an error will be thrown unless `ignoreUnsupportedSystemVersion` is set to `true`.
|
|
82
|
+
- If the system does not have MySQL installed, the latest version of MySQL in the `versions.json` file in this package will be downloaded.
|
|
83
|
+
|
|
84
|
+
If defined:
|
|
85
|
+
- If the version is 8.0.19 or older, an error will be thrown as this package does not currently support those versions of MySQL.
|
|
86
|
+
- If MySQL is installed on the system, the installed version will be used. Otherwise the selected version will be downloaded from the MySQL CDN as long as it can be found in the `versions.json` file. If it cannot be found in that file, an error will be thrown.
|
|
76
87
|
|
|
77
88
|
- `dbName: string`
|
|
78
89
|
|
|
@@ -132,6 +143,30 @@ Default: root
|
|
|
132
143
|
|
|
133
144
|
Description: The username of the user that is used to login to the database.
|
|
134
145
|
|
|
135
|
-
|
|
146
|
+
- `ignoreUnsupportedSystemVersion: boolean`
|
|
147
|
+
|
|
148
|
+
Required: No
|
|
149
|
+
|
|
150
|
+
Default: false
|
|
151
|
+
|
|
152
|
+
Description: This option only applies if the system-installed MySQL version is lower than the oldest supported MySQL version for this package (8.0.20) and the `version` option is not defined. If set to `true`, this package will use the latest version of MySQL instead of the system-installed version. If `false`, the package will throw an error.
|
|
153
|
+
|
|
154
|
+
- `deleteDBAfterStopped: boolean`
|
|
155
|
+
|
|
156
|
+
Required: No
|
|
157
|
+
|
|
158
|
+
Default: true
|
|
159
|
+
|
|
160
|
+
Gotchas: This option is intended to be for internal debugging purposes only and not meant for people to use. As such, this option will not follow Semantic Versioning.
|
|
161
|
+
|
|
162
|
+
Description: Changes whether or not the database will be deleted after it has been stopped. If set to `true`, the database WILL be deleted after it has been stopped.
|
|
163
|
+
|
|
164
|
+
- `dataPath: string`
|
|
165
|
+
|
|
166
|
+
Required: No
|
|
167
|
+
|
|
168
|
+
Default: `TMPDIR/mysqlmsn/dbs/UUID` (replacing TMPDIR with the OS temp directory and UUID with a UUIDv4 without seperating dashes)
|
|
169
|
+
|
|
170
|
+
Gotchas: This option is intended to be for internal debugging purposes only and not meant for people to use. As such, this option will not follow Semantic Versioning.
|
|
136
171
|
|
|
137
|
-
|
|
172
|
+
Description: The folder to store database-related data in
|
package/dist/src/index.js
CHANGED
|
@@ -35,25 +35,42 @@ const AbortSignal_1 = __importDefault(require("./libraries/AbortSignal"));
|
|
|
35
35
|
const Version_1 = __importDefault(require("./libraries/Version"));
|
|
36
36
|
const versions_json_1 = __importDefault(require("./versions.json"));
|
|
37
37
|
const Downloader_1 = require("./libraries/Downloader");
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
portRetries: 10,
|
|
42
|
-
downloadBinaryOnce: true,
|
|
43
|
-
lockRetries: 1000,
|
|
44
|
-
lockRetryWait: 1000,
|
|
45
|
-
username: 'root'
|
|
46
|
-
};
|
|
38
|
+
const crypto_1 = require("crypto");
|
|
39
|
+
const path_1 = require("path");
|
|
40
|
+
const constants_1 = __importDefault(require("./constants"));
|
|
47
41
|
process.on('exit', () => {
|
|
48
42
|
AbortSignal_1.default.abort('Process is exiting');
|
|
49
43
|
});
|
|
50
|
-
async function createDB(opts
|
|
44
|
+
async function createDB(opts) {
|
|
45
|
+
const defaultOptions = {
|
|
46
|
+
dbName: 'dbdata',
|
|
47
|
+
logLevel: 'ERROR',
|
|
48
|
+
portRetries: 10,
|
|
49
|
+
downloadBinaryOnce: true,
|
|
50
|
+
lockRetries: 1000,
|
|
51
|
+
lockRetryWait: 1000,
|
|
52
|
+
username: 'root',
|
|
53
|
+
deleteDBAfterStopped: true,
|
|
54
|
+
//mysqlmsn = MySQL Memory Server Node.js
|
|
55
|
+
dbPath: (0, path_1.normalize)(`${os.tmpdir()}/mysqlmsn/dbs/${(0, crypto_1.randomUUID)().replace(/-/g, '')}`),
|
|
56
|
+
ignoreUnsupportedSystemVersion: false
|
|
57
|
+
};
|
|
51
58
|
const options = { ...defaultOptions, ...opts };
|
|
52
59
|
const logger = new Logger_1.default(options.logLevel);
|
|
53
60
|
const executor = new Executor_1.default(logger);
|
|
54
61
|
const version = await executor.getMySQLVersion(options.version);
|
|
62
|
+
const unsupportedMySQLIsInstalled = version && (0, semver_1.lt)(version.version, constants_1.default.MIN_SUPPORTED_MYSQL);
|
|
63
|
+
const throwUnsupportedError = unsupportedMySQLIsInstalled && !options.ignoreUnsupportedSystemVersion && !options.version;
|
|
64
|
+
if (throwUnsupportedError) {
|
|
65
|
+
throw `A version of MySQL is installed on your system that is not supported by this package. If you want to download a MySQL binary instead of getting this error, please set the option "ignoreUnsupportedSystemVersion" to true.`;
|
|
66
|
+
}
|
|
67
|
+
if (options.version && (0, semver_1.lt)(options.version, constants_1.default.MIN_SUPPORTED_MYSQL)) {
|
|
68
|
+
//The difference between the throw here and the throw above is this throw is because the selected "version" is not supported.
|
|
69
|
+
//The throw above is because the system-installed MySQL is out of date and "ignoreUnsupportedSystemVersion" is not set to true.
|
|
70
|
+
throw `The selected version of MySQL (${options.version}) is not currently supported by this package. Please choose a different version to use.`;
|
|
71
|
+
}
|
|
55
72
|
logger.log('Version currently installed:', version);
|
|
56
|
-
if (version === null || (options.version && !(0, semver_1.satisfies)(version.version, options.version))) {
|
|
73
|
+
if (version === null || (options.version && !(0, semver_1.satisfies)(version.version, options.version)) || unsupportedMySQLIsInstalled) {
|
|
57
74
|
let binaryInfo;
|
|
58
75
|
let binaryFilepath;
|
|
59
76
|
try {
|
|
@@ -63,16 +80,16 @@ async function createDB(opts = defaultOptions) {
|
|
|
63
80
|
catch (e) {
|
|
64
81
|
logger.error(e);
|
|
65
82
|
if (options.version) {
|
|
66
|
-
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!`;
|
|
83
|
+
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!`;
|
|
67
84
|
}
|
|
68
|
-
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!`;
|
|
85
|
+
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!`;
|
|
69
86
|
}
|
|
70
87
|
try {
|
|
71
88
|
binaryFilepath = await (0, Downloader_1.downloadBinary)(binaryInfo, options, logger);
|
|
72
89
|
}
|
|
73
90
|
catch (error) {
|
|
74
91
|
logger.error('Failed to download binary');
|
|
75
|
-
throw error
|
|
92
|
+
throw `Failed to download binary. The error was: "${error}"`;
|
|
76
93
|
}
|
|
77
94
|
logger.log('Running downloaded binary');
|
|
78
95
|
return await executor.startMySQL(options, binaryFilepath);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import Logger from './Logger';
|
|
2
|
-
import { BinaryInfo,
|
|
2
|
+
import { BinaryInfo, InternalServerOptions } from '../../types';
|
|
3
3
|
export declare function downloadVersions(): Promise<string>;
|
|
4
|
-
export declare function downloadBinary(binaryInfo: BinaryInfo, options:
|
|
4
|
+
export declare function downloadBinary(binaryInfo: BinaryInfo, options: InternalServerOptions, logger: Logger): Promise<string>;
|
|
@@ -37,6 +37,7 @@ const path_1 = require("path");
|
|
|
37
37
|
const crypto_1 = require("crypto");
|
|
38
38
|
const child_process_1 = require("child_process");
|
|
39
39
|
const proper_lockfile_1 = require("proper-lockfile");
|
|
40
|
+
const FileLock_1 = require("./FileLock");
|
|
40
41
|
function getZipData(entry) {
|
|
41
42
|
return new Promise((resolve, reject) => {
|
|
42
43
|
entry.getDataAsync((data, err) => {
|
|
@@ -77,26 +78,31 @@ function downloadVersions() {
|
|
|
77
78
|
function downloadFromCDN(url, downloadLocation, logger) {
|
|
78
79
|
return new Promise((resolve, reject) => {
|
|
79
80
|
const fileStream = fs.createWriteStream(downloadLocation);
|
|
81
|
+
let error;
|
|
80
82
|
fileStream.on('open', () => {
|
|
81
83
|
const request = https.get(url, (response) => {
|
|
82
84
|
response.pipe(fileStream);
|
|
83
85
|
});
|
|
84
86
|
request.on('error', (err) => {
|
|
87
|
+
error = err;
|
|
85
88
|
logger.error(err);
|
|
86
89
|
fileStream.close();
|
|
87
|
-
fs.unlink(downloadLocation, (
|
|
88
|
-
reject(err);
|
|
90
|
+
fs.unlink(downloadLocation, () => {
|
|
91
|
+
reject(err.message);
|
|
89
92
|
});
|
|
90
93
|
});
|
|
91
94
|
});
|
|
92
95
|
fileStream.on('finish', () => {
|
|
93
|
-
|
|
96
|
+
if (!error) {
|
|
97
|
+
resolve();
|
|
98
|
+
}
|
|
94
99
|
});
|
|
95
100
|
fileStream.on('error', (err) => {
|
|
101
|
+
error = err;
|
|
96
102
|
logger.error(err);
|
|
97
103
|
fileStream.end();
|
|
98
104
|
fs.unlink(downloadLocation, () => {
|
|
99
|
-
reject(err);
|
|
105
|
+
reject(err.message);
|
|
100
106
|
});
|
|
101
107
|
});
|
|
102
108
|
});
|
|
@@ -106,7 +112,11 @@ function extractBinary(url, archiveLocation, extractedLocation) {
|
|
|
106
112
|
const lastDashIndex = url.lastIndexOf('-');
|
|
107
113
|
const fileExtension = url.slice(lastDashIndex).split('.').splice(1).join('.');
|
|
108
114
|
await fsPromises.mkdir(extractedLocation, { recursive: true });
|
|
109
|
-
const
|
|
115
|
+
const mySQLFolderName = url.split('/').at(-1);
|
|
116
|
+
if (!mySQLFolderName) {
|
|
117
|
+
return reject(`Folder name is undefined for url: ${url}`);
|
|
118
|
+
}
|
|
119
|
+
const folderName = mySQLFolderName.replace(`.${fileExtension}`, '');
|
|
110
120
|
if (fileExtension === 'zip') {
|
|
111
121
|
//Only Windows MySQL files use the .zip extension
|
|
112
122
|
const zip = new adm_zip_1.default(archiveLocation);
|
|
@@ -148,27 +158,6 @@ function extractBinary(url, archiveLocation, extractedLocation) {
|
|
|
148
158
|
});
|
|
149
159
|
});
|
|
150
160
|
}
|
|
151
|
-
function waitForLock(path, options) {
|
|
152
|
-
return new Promise(async (resolve, reject) => {
|
|
153
|
-
let retries = 0;
|
|
154
|
-
while (retries <= options.lockRetries) {
|
|
155
|
-
retries++;
|
|
156
|
-
try {
|
|
157
|
-
const locked = (0, proper_lockfile_1.checkSync)(path);
|
|
158
|
-
if (!locked) {
|
|
159
|
-
return resolve();
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
await new Promise(resolve => setTimeout(resolve, options.lockRetryWait));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
catch (e) {
|
|
166
|
-
return reject(e);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
reject(`lockRetries has been exceeded. Lock had not been released after ${options.lockRetryWait} * ${options.lockRetries} milliseconds.`);
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
161
|
function downloadBinary(binaryInfo, options, logger) {
|
|
173
162
|
return new Promise(async (resolve, reject) => {
|
|
174
163
|
const { url, version } = binaryInfo;
|
|
@@ -201,7 +190,7 @@ function downloadBinary(binaryInfo, options, logger) {
|
|
|
201
190
|
catch (e) {
|
|
202
191
|
if (String(e) === 'Error: Lock file is already being held') {
|
|
203
192
|
logger.log('Waiting for lock for MySQL version', version);
|
|
204
|
-
await waitForLock(extractedPath, options);
|
|
193
|
+
await (0, FileLock_1.waitForLock)(extractedPath, options);
|
|
205
194
|
logger.log('Lock is gone for version', version);
|
|
206
195
|
return resolve(binaryPath);
|
|
207
196
|
}
|
|
@@ -4,6 +4,7 @@ declare class Executor {
|
|
|
4
4
|
#private;
|
|
5
5
|
logger: Logger;
|
|
6
6
|
constructor(logger: Logger);
|
|
7
|
+
deleteDatabaseDirectory(path: string): Promise<void>;
|
|
7
8
|
getMySQLVersion(preferredVersion?: string): Promise<InstalledMySQLVersion | null>;
|
|
8
9
|
startMySQL(options: InternalServerOptions, binaryFilepath: string): Promise<MySQLDB>;
|
|
9
10
|
}
|
|
@@ -30,7 +30,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
30
30
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
31
31
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
32
|
};
|
|
33
|
-
var _Executor_instances, _Executor_execute, _Executor_startMySQLProcess;
|
|
33
|
+
var _Executor_instances, _Executor_execute, _Executor_executeFile, _Executor_killProcess, _Executor_startMySQLProcess, _Executor_setupDataDirectories;
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
35
|
const child_process_1 = require("child_process");
|
|
36
36
|
const semver_1 = require("semver");
|
|
@@ -40,12 +40,36 @@ const fs = __importStar(require("fs"));
|
|
|
40
40
|
const Port_1 = require("./Port");
|
|
41
41
|
const AbortSignal_1 = __importDefault(require("./AbortSignal"));
|
|
42
42
|
const path_1 = require("path");
|
|
43
|
-
const
|
|
43
|
+
const proper_lockfile_1 = require("proper-lockfile");
|
|
44
|
+
const FileLock_1 = require("./FileLock");
|
|
44
45
|
class Executor {
|
|
45
46
|
constructor(logger) {
|
|
46
47
|
_Executor_instances.add(this);
|
|
47
48
|
this.logger = logger;
|
|
48
49
|
}
|
|
50
|
+
async deleteDatabaseDirectory(path) {
|
|
51
|
+
let retries = 0;
|
|
52
|
+
//Maximum wait of 10 seconds | 500ms * 20 retries = 10,000ms = 10 seconds
|
|
53
|
+
const waitTime = 500;
|
|
54
|
+
const maxRetries = 20;
|
|
55
|
+
//Since the database processes are killed instantly (SIGKILL) sometimes the database file handles may still be open
|
|
56
|
+
//This would cause an EBUSY error. Retrying the deletions for 10 seconds should give the OS enough time to close
|
|
57
|
+
//the file handles.
|
|
58
|
+
while (retries <= maxRetries) {
|
|
59
|
+
try {
|
|
60
|
+
await fsPromises.rm(path, { recursive: true, force: true });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
if (retries === maxRetries) {
|
|
65
|
+
throw e;
|
|
66
|
+
}
|
|
67
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
68
|
+
retries++;
|
|
69
|
+
this.logger.log('DB data directory deletion failed. Now on retry', retries);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
49
73
|
getMySQLVersion(preferredVersion) {
|
|
50
74
|
return new Promise(async (resolve, reject) => {
|
|
51
75
|
if (process.platform === 'win32') {
|
|
@@ -55,6 +79,7 @@ class Executor {
|
|
|
55
79
|
if (servers.length === 0) {
|
|
56
80
|
return resolve(null);
|
|
57
81
|
}
|
|
82
|
+
this.logger.log(servers);
|
|
58
83
|
const versions = [];
|
|
59
84
|
for (const dir of servers) {
|
|
60
85
|
const path = `${process.env.PROGRAMFILES}\\MySQL\\${dir}\\bin\\mysqld`;
|
|
@@ -104,51 +129,36 @@ class Executor {
|
|
|
104
129
|
}
|
|
105
130
|
});
|
|
106
131
|
}
|
|
107
|
-
startMySQL(options, binaryFilepath) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this.logger.log('
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (options.username !== 'root') {
|
|
128
|
-
initText += `RENAME USER 'root'@'localhost' TO '${options.username}'@'localhost';`;
|
|
129
|
-
}
|
|
130
|
-
await fsPromises.writeFile(`${dbPath}/init.sql`, initText, { encoding: 'utf8' });
|
|
131
|
-
let retries = 0;
|
|
132
|
-
do {
|
|
133
|
-
const port = (0, Port_1.GenerateRandomPort)();
|
|
134
|
-
const mySQLXPort = (0, Port_1.GenerateRandomPort)();
|
|
135
|
-
this.logger.log('Using port:', port, 'on retry:', retries);
|
|
136
|
-
try {
|
|
137
|
-
const resolved = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_startMySQLProcess).call(this, options, port, mySQLXPort, datadir, dbPath, binaryFilepath);
|
|
138
|
-
return resolve(resolved);
|
|
132
|
+
async startMySQL(options, binaryFilepath) {
|
|
133
|
+
let retries = 0;
|
|
134
|
+
const datadir = (0, path_1.normalize)(`${options.dbPath}/data`);
|
|
135
|
+
do {
|
|
136
|
+
await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, binaryFilepath, datadir, true);
|
|
137
|
+
this.logger.log('Setting up directories was successful');
|
|
138
|
+
const port = (0, Port_1.GenerateRandomPort)();
|
|
139
|
+
const mySQLXPort = (0, Port_1.GenerateRandomPort)();
|
|
140
|
+
this.logger.log('Using port:', port, 'and MySQLX port:', mySQLXPort, 'on retry:', retries);
|
|
141
|
+
try {
|
|
142
|
+
this.logger.log('Starting MySQL process');
|
|
143
|
+
const resolved = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_startMySQLProcess).call(this, options, port, mySQLXPort, datadir, options.dbPath, binaryFilepath);
|
|
144
|
+
this.logger.log('Starting process was successful');
|
|
145
|
+
return resolved;
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
this.logger.warn('Caught error:', e, `\nRetries: ${retries} | options.portRetries: ${options.portRetries}`);
|
|
149
|
+
if (e !== 'Port is already in use') {
|
|
150
|
+
this.logger.error('Error:', e);
|
|
151
|
+
throw e;
|
|
139
152
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
retries++;
|
|
145
|
-
if (retries < options.portRetries) {
|
|
146
|
-
this.logger.warn(`One or both of these ports are already in use: ${port} or ${mySQLXPort}. Now retrying... ${retries}/${options.portRetries} possible retries.`);
|
|
147
|
-
}
|
|
153
|
+
retries++;
|
|
154
|
+
if (retries <= options.portRetries) {
|
|
155
|
+
this.logger.warn(`One or both of these ports are already in use: ${port} or ${mySQLXPort}. Now retrying... ${retries}/${options.portRetries} possible retries.`);
|
|
148
156
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
157
|
+
else {
|
|
158
|
+
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.`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} while (retries <= options.portRetries);
|
|
152
162
|
}
|
|
153
163
|
}
|
|
154
164
|
_Executor_instances = new WeakSet(), _Executor_execute = function _Executor_execute(command) {
|
|
@@ -157,43 +167,90 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
157
167
|
resolve({ error, stdout, stderr });
|
|
158
168
|
});
|
|
159
169
|
});
|
|
170
|
+
}, _Executor_executeFile = function _Executor_executeFile(command, args, cwd) {
|
|
171
|
+
return new Promise(resolve => {
|
|
172
|
+
(0, child_process_1.execFile)(command, args, { signal: AbortSignal_1.default.signal, cwd }, (error, stdout, stderr) => {
|
|
173
|
+
resolve({ stdout, stderr: (error === null || error === void 0 ? void 0 : error.message) || stderr });
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
}, _Executor_killProcess = async function _Executor_killProcess(process) {
|
|
177
|
+
let killed = false;
|
|
178
|
+
if (os.platform() === 'win32') {
|
|
179
|
+
const { error, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, `taskkill /pid ${process.pid} /t /f`);
|
|
180
|
+
if (!error && !stderr) {
|
|
181
|
+
killed = true;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
this.logger.error(error || stderr);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
killed = process.kill();
|
|
189
|
+
}
|
|
190
|
+
return killed;
|
|
160
191
|
}, _Executor_startMySQLProcess = function _Executor_startMySQLProcess(options, port, mySQLXPort, datadir, dbPath, binaryFilepath) {
|
|
161
192
|
const errors = [];
|
|
162
193
|
const logFile = `${dbPath}/log.log`;
|
|
194
|
+
const errorLogFile = `${datadir}/errorlog.err`;
|
|
163
195
|
return new Promise(async (resolve, reject) => {
|
|
164
196
|
await fsPromises.rm(logFile, { force: true });
|
|
165
|
-
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' });
|
|
197
|
+
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' });
|
|
166
198
|
//resolveFunction is the function that will be called to resolve the promise that stops the database.
|
|
167
199
|
//If resolveFunction is not undefined, the database has received a kill signal and data cleanup procedures should run.
|
|
168
200
|
//Once ran, resolveFunction will be called.
|
|
169
201
|
let resolveFunction;
|
|
170
202
|
process.on('close', async (code, signal) => {
|
|
171
|
-
|
|
172
|
-
|
|
203
|
+
let errorLog;
|
|
204
|
+
try {
|
|
205
|
+
errorLog = await fsPromises.readFile(errorLogFile, { encoding: 'utf-8' });
|
|
206
|
+
}
|
|
207
|
+
catch (e) {
|
|
208
|
+
errorLog = `ERROR WHILE READING LOG: ${e}`;
|
|
209
|
+
}
|
|
210
|
+
const portIssue = errorLog.includes("Do you already have another mysqld server running");
|
|
211
|
+
const xPortIssue = errorLog.includes("X Plugin can't bind to it");
|
|
212
|
+
this.logger.log('Exiting because of a port issue:', portIssue, '. MySQL X Plugin failed to bind:', xPortIssue);
|
|
213
|
+
if (portIssue || xPortIssue) {
|
|
214
|
+
this.logger.log('Error log when exiting for port in use error:', errorLog);
|
|
215
|
+
try {
|
|
216
|
+
await this.deleteDatabaseDirectory(options.dbPath);
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
this.logger.error(e);
|
|
220
|
+
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}`);
|
|
221
|
+
}
|
|
173
222
|
return reject('Port is already in use');
|
|
174
223
|
}
|
|
175
224
|
try {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const splitPath = binaryFilepath.split(os.platform() === 'win32' ? '\\' : '/');
|
|
179
|
-
const binariesIndex = splitPath.indexOf('binaries');
|
|
180
|
-
//The path will be the directory path for the binary download
|
|
181
|
-
splitPath.splice(binariesIndex + 2);
|
|
182
|
-
//Delete the binary folder
|
|
183
|
-
await fsPromises.rm(splitPath.join('/'), { force: true, recursive: true });
|
|
225
|
+
if (options.deleteDBAfterStopped) {
|
|
226
|
+
await this.deleteDatabaseDirectory(dbPath);
|
|
184
227
|
}
|
|
185
228
|
}
|
|
186
229
|
finally {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
230
|
+
try {
|
|
231
|
+
if (binaryFilepath.includes(os.tmpdir()) && !options.downloadBinaryOnce) {
|
|
232
|
+
const splitPath = binaryFilepath.split(os.platform() === 'win32' ? '\\' : '/');
|
|
233
|
+
const binariesIndex = splitPath.indexOf('binaries');
|
|
234
|
+
//The path will be the directory path for the binary download
|
|
235
|
+
splitPath.splice(binariesIndex + 2);
|
|
236
|
+
//Delete the binary folder
|
|
237
|
+
await fsPromises.rm(splitPath.join('/'), { force: true, recursive: true });
|
|
238
|
+
}
|
|
193
239
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
240
|
+
finally {
|
|
241
|
+
if (resolveFunction) {
|
|
242
|
+
resolveFunction();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const errorString = errors.join('\n');
|
|
246
|
+
if (code === 0) {
|
|
247
|
+
return reject(`Database exited early.\nError log: ${errorLog}\nError string: "${errorString}`);
|
|
248
|
+
}
|
|
249
|
+
if (code) {
|
|
250
|
+
const errorMessage = `The database exited early with code ${code}. The error log was:\n${errorLog}\nThe error string was: "${errorString}".`;
|
|
251
|
+
this.logger.error(errorMessage);
|
|
252
|
+
return reject(errorMessage);
|
|
253
|
+
}
|
|
197
254
|
}
|
|
198
255
|
}
|
|
199
256
|
});
|
|
@@ -207,12 +264,22 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
207
264
|
}
|
|
208
265
|
}
|
|
209
266
|
});
|
|
210
|
-
fs.watchFile(
|
|
267
|
+
fs.watchFile(errorLogFile, async (curr) => {
|
|
211
268
|
if (curr.dev !== 0) {
|
|
212
269
|
//File exists
|
|
213
|
-
const file = await fsPromises.readFile(
|
|
214
|
-
if (file.includes('
|
|
215
|
-
|
|
270
|
+
const file = await fsPromises.readFile(errorLogFile, { encoding: 'utf8' });
|
|
271
|
+
if (file.includes("X Plugin can't bind to it")) {
|
|
272
|
+
//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
|
|
273
|
+
//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.
|
|
274
|
+
//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
|
|
275
|
+
//cannot bind to an address. If there is a way to prevent server startup when binding fails, this workaround can be removed.
|
|
276
|
+
const killed = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_killProcess).call(this, process);
|
|
277
|
+
if (!killed) {
|
|
278
|
+
reject('Failed to kill MySQL process to retry listening on a free port.');
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else if (file.includes('ready for connections. Version:')) {
|
|
282
|
+
fs.unwatchFile(errorLogFile);
|
|
216
283
|
resolve({
|
|
217
284
|
port,
|
|
218
285
|
xPort: mySQLXPort,
|
|
@@ -221,19 +288,7 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
221
288
|
stop: () => {
|
|
222
289
|
return new Promise(async (resolve, reject) => {
|
|
223
290
|
resolveFunction = resolve;
|
|
224
|
-
|
|
225
|
-
if (os.platform() === 'win32') {
|
|
226
|
-
const { error, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, `taskkill /pid ${process.pid} /t /f`);
|
|
227
|
-
if (!error && !stderr) {
|
|
228
|
-
killed = true;
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
this.logger.error(error || stderr);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
killed = process.kill();
|
|
236
|
-
}
|
|
291
|
+
const killed = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_killProcess).call(this, process);
|
|
237
292
|
if (!killed) {
|
|
238
293
|
reject();
|
|
239
294
|
}
|
|
@@ -244,5 +299,116 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
244
299
|
}
|
|
245
300
|
});
|
|
246
301
|
});
|
|
302
|
+
}, _Executor_setupDataDirectories = async function _Executor_setupDataDirectories(options, binaryFilepath, datadir, retry) {
|
|
303
|
+
this.logger.log('Created data directory for database at:', datadir);
|
|
304
|
+
await fsPromises.mkdir(datadir, { recursive: true });
|
|
305
|
+
let stderr;
|
|
306
|
+
if (binaryFilepath === 'mysqld') {
|
|
307
|
+
const { error, stderr: output } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, `mysqld --no-defaults --datadir=${datadir} --initialize-insecure`);
|
|
308
|
+
stderr = output;
|
|
309
|
+
if (error) {
|
|
310
|
+
this.logger.error('An error occurred while initializing database with system-installed MySQL:', error);
|
|
311
|
+
throw 'An error occurred while initializing database with system-installed MySQL. Please check the console for more information.';
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
let result;
|
|
316
|
+
try {
|
|
317
|
+
result = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, `${binaryFilepath}`, [`--no-defaults`, `--datadir=${datadir}`, `--initialize-insecure`], (0, path_1.resolve)(`${binaryFilepath}/..`));
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
this.logger.error('Error occurred from executeFile:', e);
|
|
321
|
+
throw e;
|
|
322
|
+
}
|
|
323
|
+
stderr = result === null || result === void 0 ? void 0 : result.stderr;
|
|
324
|
+
}
|
|
325
|
+
if (stderr && !stderr.includes('InnoDB initialization has ended')) {
|
|
326
|
+
if (process.platform === 'win32' && stderr.includes('Command failed')) {
|
|
327
|
+
this.logger.error(stderr);
|
|
328
|
+
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.';
|
|
329
|
+
}
|
|
330
|
+
if (process.platform === 'linux' && stderr.includes('libaio.so')) {
|
|
331
|
+
if (binaryFilepath === 'mysqld') {
|
|
332
|
+
throw 'libaio could not be found while running system-installed MySQL. libaio must be installed on this system for MySQL to run. To learn more, please check out https://dev.mysql.com/doc/refman/en/binary-installation.html';
|
|
333
|
+
}
|
|
334
|
+
if (retry === false) {
|
|
335
|
+
this.logger.error('An error occurred while initializing database:', stderr);
|
|
336
|
+
throw 'Tried to copy libaio into lib folder and MySQL is still failing to initialize. Please check the console for more information.';
|
|
337
|
+
}
|
|
338
|
+
if (binaryFilepath.slice(-16) === 'mysql/bin/mysqld') {
|
|
339
|
+
const { error: lderror, stdout, stderr: ldstderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, 'ldconfig -p');
|
|
340
|
+
if (lderror || ldstderr) {
|
|
341
|
+
this.logger.error('The following libaio error occurred:', stderr);
|
|
342
|
+
this.logger.error('After the libaio error, an ldconfig error occurred:', lderror || ldstderr);
|
|
343
|
+
throw 'The ldconfig command failed to run. This command was ran to find libaio because libaio could not be found on the system. libaio is needed for MySQL to run. Do you have ldconfig and libaio installed? Learn more about libaio at Learn more at https://dev.mysql.com/doc/refman/en/binary-installation.html';
|
|
344
|
+
}
|
|
345
|
+
const libaioFound = stdout.split('\n').filter(lib => lib.includes('libaio.so.1t64'));
|
|
346
|
+
if (!libaioFound.length) {
|
|
347
|
+
this.logger.error('Error from launching MySQL:', stderr);
|
|
348
|
+
throw 'An error occurred while launching MySQL. The most likely cause is that libaio1 and libaio1t64 could not be found. Either libaio1 or libaio1t64 must be installed on this system for MySQL to run. To learn more, please check out https://dev.mysql.com/doc/refman/en/binary-installation.html. Check error in console for more information.';
|
|
349
|
+
}
|
|
350
|
+
const libaioEntry = libaioFound[0];
|
|
351
|
+
const libaioPathIndex = libaioEntry.indexOf('=>');
|
|
352
|
+
const libaioSymlinkPath = libaioEntry.slice(libaioPathIndex + 3);
|
|
353
|
+
const libaioPath = await fsPromises.realpath(libaioSymlinkPath);
|
|
354
|
+
const copyPath = (0, path_1.resolve)(`${binaryFilepath}/../../lib/private/libaio.so.1`);
|
|
355
|
+
try {
|
|
356
|
+
(0, proper_lockfile_1.lockSync)(copyPath, { realpath: false });
|
|
357
|
+
this.logger.log('libaio copy path:', copyPath, '| libaio symlink path:', libaioSymlinkPath, '| libaio actual path:', libaioPath);
|
|
358
|
+
let copyError;
|
|
359
|
+
try {
|
|
360
|
+
await fsPromises.copyFile(libaioPath, copyPath);
|
|
361
|
+
}
|
|
362
|
+
catch (e) {
|
|
363
|
+
copyError = e;
|
|
364
|
+
this.logger.error('An error occurred while copying libaio1t64 to lib folder:', e);
|
|
365
|
+
try {
|
|
366
|
+
await fsPromises.rm(copyPath, { force: true });
|
|
367
|
+
}
|
|
368
|
+
catch (e) {
|
|
369
|
+
this.logger.error('An error occurred while deleting libaio file:', e);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
finally {
|
|
373
|
+
try {
|
|
374
|
+
(0, proper_lockfile_1.unlockSync)(copyPath, { realpath: false });
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
this.logger.error('Error unlocking libaio file:', e);
|
|
378
|
+
}
|
|
379
|
+
if (copyError) {
|
|
380
|
+
throw 'An error occurred while copying libaio1t64 to the MySQL lib folder. Please check the console for more details.';
|
|
381
|
+
}
|
|
382
|
+
//Retry setting up directory now that libaio has been copied
|
|
383
|
+
this.logger.log('Retrying directory setup');
|
|
384
|
+
await this.deleteDatabaseDirectory(datadir);
|
|
385
|
+
await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, binaryFilepath, datadir, false);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
if (String(error) === 'Error: Lock file is already being held') {
|
|
391
|
+
this.logger.log('Waiting for lock for libaio copy');
|
|
392
|
+
await (0, FileLock_1.waitForLock)(copyPath, options);
|
|
393
|
+
this.logger.log('Lock is gone for libaio copy');
|
|
394
|
+
}
|
|
395
|
+
this.logger.error('An error occurred from locking libaio section:', error);
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
throw 'Cannot recognize file structure for the MySQL binary folder. This was caused by not being able to find libaio. Try installing libaio. Learn more at https://dev.mysql.com/doc/refman/en/binary-installation.html';
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
throw stderr;
|
|
404
|
+
}
|
|
405
|
+
this.logger.log('Creating init text');
|
|
406
|
+
let initText = `CREATE DATABASE ${options.dbName};`;
|
|
407
|
+
if (options.username !== 'root') {
|
|
408
|
+
initText += `\nRENAME USER 'root'@'localhost' TO '${options.username}'@'localhost';`;
|
|
409
|
+
}
|
|
410
|
+
this.logger.log('Writing init file');
|
|
411
|
+
await fsPromises.writeFile(`${options.dbPath}/init.sql`, initText, { encoding: 'utf8' });
|
|
412
|
+
this.logger.log('Finished writing init file');
|
|
247
413
|
};
|
|
248
414
|
exports.default = Executor;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.waitForLock = waitForLock;
|
|
4
|
+
const proper_lockfile_1 = require("proper-lockfile");
|
|
5
|
+
function waitForLock(path, options) {
|
|
6
|
+
return new Promise(async (resolve, reject) => {
|
|
7
|
+
let retries = 0;
|
|
8
|
+
while (retries <= options.lockRetries) {
|
|
9
|
+
retries++;
|
|
10
|
+
try {
|
|
11
|
+
const locked = (0, proper_lockfile_1.checkSync)(path);
|
|
12
|
+
if (!locked) {
|
|
13
|
+
return resolve();
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
await new Promise(resolve => setTimeout(resolve, options.lockRetryWait));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
return reject(e);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
reject(`lockRetries has been exceeded. Lock had not been released after ${options.lockRetryWait} * ${options.lockRetries} milliseconds.`);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -11,17 +11,17 @@ class Logger {
|
|
|
11
11
|
}
|
|
12
12
|
log(...args) {
|
|
13
13
|
if (this.LOG_LEVEL === 0) {
|
|
14
|
-
console.log
|
|
14
|
+
console.log(...args);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
warn(...args) {
|
|
18
18
|
if (this.LOG_LEVEL <= 1) {
|
|
19
|
-
console.warn
|
|
19
|
+
console.warn(...args);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
error(...args) {
|
|
23
23
|
if (this.LOG_LEVEL <= 2) {
|
|
24
|
-
console.error
|
|
24
|
+
console.error(...args);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -34,7 +34,12 @@ function getBinaryURL(versions, versionToGet = "9.x") {
|
|
|
34
34
|
availableVersions = availableVersions.filter(v => v.os === process.platform);
|
|
35
35
|
if (availableVersions.length === 0)
|
|
36
36
|
throw `No MySQL binary could be found for your OS: ${process.platform}`;
|
|
37
|
-
availableVersions = availableVersions.filter(v =>
|
|
37
|
+
availableVersions = availableVersions.filter(v => {
|
|
38
|
+
const release = (0, semver_1.coerce)(os.release());
|
|
39
|
+
if (!release)
|
|
40
|
+
return false;
|
|
41
|
+
return (0, semver_1.satisfies)(release.version, v.osKernelVersionsSupported);
|
|
42
|
+
});
|
|
38
43
|
if (availableVersions.length === 0)
|
|
39
44
|
throw `No MySQL binary could be found that supports your OS version: ${os.release()} | ${os.version()}`;
|
|
40
45
|
const wantedVersions = availableVersions.filter(v => (0, semver_1.satisfies)(v.version, versionToGet));
|
package/dist/types/index.d.ts
CHANGED
|
@@ -9,6 +9,9 @@ export type ServerOptions = {
|
|
|
9
9
|
lockRetries?: number;
|
|
10
10
|
lockRetryWait?: number;
|
|
11
11
|
username?: string;
|
|
12
|
+
deleteDBAfterStopped?: boolean;
|
|
13
|
+
dbPath?: string;
|
|
14
|
+
ignoreUnsupportedSystemVersion?: boolean;
|
|
12
15
|
};
|
|
13
16
|
export type InternalServerOptions = {
|
|
14
17
|
version?: string;
|
|
@@ -19,6 +22,9 @@ export type InternalServerOptions = {
|
|
|
19
22
|
lockRetries: number;
|
|
20
23
|
lockRetryWait: number;
|
|
21
24
|
username: string;
|
|
25
|
+
deleteDBAfterStopped: boolean;
|
|
26
|
+
dbPath: string;
|
|
27
|
+
ignoreUnsupportedSystemVersion: boolean;
|
|
22
28
|
};
|
|
23
29
|
export type ExecutorOptions = {
|
|
24
30
|
logLevel: LOG_LEVEL;
|
package/package.json
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mysql-memory-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Spin up an ephemeral MySQL database from your JavaScript code",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"/dist"
|
|
9
9
|
],
|
|
10
|
-
"keywords": [
|
|
10
|
+
"keywords": [
|
|
11
|
+
"MySQL",
|
|
12
|
+
"ephemeral database",
|
|
13
|
+
"continuous integration",
|
|
14
|
+
"CI",
|
|
15
|
+
"CI/CD",
|
|
16
|
+
"memory database"
|
|
17
|
+
],
|
|
11
18
|
"scripts": {
|
|
12
|
-
"test": "jest"
|
|
19
|
+
"test": "jest --testPathIgnorePatterns=/stress-tests/ --verbose",
|
|
20
|
+
"test:ci": "jest --testPathIgnorePatterns=/stress-tests/ --setupFilesAfterEnv ./ciSetup.js --verbose",
|
|
21
|
+
"stress": "jest --runTestsByPath stress-tests/stress.test.ts --setupFilesAfterEnv ./ciSetup.js --verbose"
|
|
13
22
|
},
|
|
14
23
|
"author": "Sebastian-Webster",
|
|
15
24
|
"license": "MIT",
|
|
@@ -18,7 +27,7 @@
|
|
|
18
27
|
"@babel/preset-env": "^7.24.8",
|
|
19
28
|
"@babel/preset-typescript": "^7.24.7",
|
|
20
29
|
"@types/adm-zip": "^0.5.5",
|
|
21
|
-
"@types/node": "^22.
|
|
30
|
+
"@types/node": "^22.2.0",
|
|
22
31
|
"@types/proper-lockfile": "^4.1.4",
|
|
23
32
|
"@types/semver": "^7.5.8",
|
|
24
33
|
"babel-jest": "^29.7.0",
|
|
@@ -27,7 +36,7 @@
|
|
|
27
36
|
"typescript": "^5.5.4"
|
|
28
37
|
},
|
|
29
38
|
"dependencies": {
|
|
30
|
-
"adm-zip": "^0.5.
|
|
39
|
+
"adm-zip": "^0.5.15",
|
|
31
40
|
"proper-lockfile": "^4.1.2",
|
|
32
41
|
"semver": "^7.6.3"
|
|
33
42
|
},
|