mysql-memory-server 1.4.1 → 1.4.3
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 +39 -11
- package/dist/src/index.js +3 -2
- package/dist/src/libraries/Downloader.js +56 -35
- package/dist/src/libraries/Executor.js +39 -28
- package/dist/src/libraries/FileLock.js +1 -1
- package/dist/src/libraries/Logger.js +3 -3
- package/dist/types/index.d.ts +5 -3
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -13,15 +13,20 @@ Download with your package manager of choice. The package name is `mysql-memory-
|
|
|
13
13
|
- Node.js 16.6.0 and newer
|
|
14
14
|
- macOS 13+, Windows, or Linux (This package is only tested on Ubuntu 20.04, 22.04, 24.04, and Fedora 40. Other Linux distributions may or may not work at this time.)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
#### Requirements for downloaded MySQL versions
|
|
17
|
+
|
|
18
|
+
The following requirements only apply if you intend to have `mysql-memory-server` download a version of MySQL to use instead of using an already installed version of MySQL on the system:
|
|
19
|
+
|
|
20
|
+
Requirements for Windows:
|
|
17
21
|
- `Microsoft Visual C++ 2019 Redistributable Package` needs to be installed
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
Requirements for Linux:
|
|
20
24
|
- The `libaio1` or `libaio1t64` package needs to be installed
|
|
21
25
|
- 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
|
|
26
|
+
- The `tar` package needs to be installed
|
|
27
|
+
|
|
28
|
+
#### Currently supported MySQL versions
|
|
23
29
|
|
|
24
|
-
Currently supported MySQL versions:
|
|
25
30
|
- If using the system installed MySQL server: 8.0.20 and newer
|
|
26
31
|
- 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
|
|
27
32
|
|
|
@@ -31,15 +36,26 @@ Currently supported MySQL versions:
|
|
|
31
36
|
import { createDB } from 'mysql-memory-server';
|
|
32
37
|
import sql from 'mysql2/promise'
|
|
33
38
|
|
|
34
|
-
// Create a new database
|
|
39
|
+
// Create a new database with default options
|
|
35
40
|
const db = await createDB()
|
|
36
41
|
|
|
42
|
+
//OR
|
|
43
|
+
|
|
44
|
+
//Create a new database with custom options set
|
|
45
|
+
const db = await createDB({
|
|
46
|
+
// see Options for the options you can use in this object and their default values
|
|
47
|
+
// for example:
|
|
48
|
+
version: '8.4.x'
|
|
49
|
+
})
|
|
50
|
+
|
|
37
51
|
// Connect to the new database with the port provided
|
|
52
|
+
// The database is initialized with an empty password so use an empty string for the password
|
|
38
53
|
const connection = await sql.createConnection({
|
|
39
54
|
host: '127.0.0.1',
|
|
40
55
|
user: db.username,
|
|
41
56
|
port: db.port,
|
|
42
|
-
database: db.dbName
|
|
57
|
+
database: db.dbName,
|
|
58
|
+
password: ''
|
|
43
59
|
})
|
|
44
60
|
|
|
45
61
|
// Run your queries here
|
|
@@ -52,6 +68,9 @@ await connection.end()
|
|
|
52
68
|
await db.stop()
|
|
53
69
|
```
|
|
54
70
|
|
|
71
|
+
MySQL database initialization can take some time. If you run into a "Timeout exceeded" error with your tests, the timeout should be extended.
|
|
72
|
+
If using Jest, information about how to do this can be found here: https://jestjs.io/docs/jest-object#jestsettimeouttimeout
|
|
73
|
+
|
|
55
74
|
## Documentation
|
|
56
75
|
|
|
57
76
|
##### `createDB(options: ServerOptions): Promise<MySQLDB>`
|
|
@@ -167,22 +186,31 @@ Default: false
|
|
|
167
186
|
|
|
168
187
|
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.
|
|
169
188
|
|
|
189
|
+
***
|
|
190
|
+
### :warning: Internal Options :warning:
|
|
191
|
+
|
|
192
|
+
The following options are only meant for internal debugging use. Their behaviour may change or they may get removed between major/minor/patch versions and they are not to be considered stable. The options below will not follow Semantic Versioning so it is advised to not use them.
|
|
193
|
+
|
|
170
194
|
- `deleteDBAfterStopped: boolean`
|
|
171
195
|
|
|
172
196
|
Required: No
|
|
173
197
|
|
|
174
198
|
Default: true
|
|
175
199
|
|
|
176
|
-
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.
|
|
177
|
-
|
|
178
200
|
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.
|
|
179
201
|
|
|
180
202
|
- `dataPath: string`
|
|
181
203
|
|
|
182
204
|
Required: No
|
|
183
205
|
|
|
184
|
-
Default: `TMPDIR/mysqlmsn/dbs/UUID` (replacing TMPDIR with the OS temp directory and UUID with a UUIDv4 without seperating dashes)
|
|
185
|
-
|
|
186
|
-
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.
|
|
206
|
+
Default: `TMPDIR/mysqlmsn/dbs/UUID` (replacing TMPDIR with the OS temp directory and UUID with a UUIDv4 without seperating dashes).
|
|
187
207
|
|
|
188
208
|
Description: The folder to store database-related data in
|
|
209
|
+
|
|
210
|
+
- `binaryDirectoryPath: string`
|
|
211
|
+
|
|
212
|
+
Required: No
|
|
213
|
+
|
|
214
|
+
Default: `TMPDIR/mysqlmsn/binaries` (replacing TMPDIR with the OS temp directory)
|
|
215
|
+
|
|
216
|
+
Description: The folder to store the MySQL binaries when they are downloaded from the CDN.
|
package/dist/src/index.js
CHANGED
|
@@ -51,7 +51,8 @@ async function createDB(opts) {
|
|
|
51
51
|
dbPath: (0, path_1.normalize)(`${os.tmpdir()}/mysqlmsn/dbs/${(0, crypto_1.randomUUID)().replace(/-/g, '')}`),
|
|
52
52
|
ignoreUnsupportedSystemVersion: false,
|
|
53
53
|
port: 0,
|
|
54
|
-
xPort: 0
|
|
54
|
+
xPort: 0,
|
|
55
|
+
binaryDirectoryPath: `${os.tmpdir()}/mysqlmsn/binaries`
|
|
55
56
|
};
|
|
56
57
|
const options = { ...defaultOptions, ...opts };
|
|
57
58
|
const logger = new Logger_1.default(options.logLevel);
|
|
@@ -73,7 +74,7 @@ async function createDB(opts) {
|
|
|
73
74
|
let binaryFilepath;
|
|
74
75
|
try {
|
|
75
76
|
binaryInfo = (0, Version_1.default)(versions_json_1.default, options.version);
|
|
76
|
-
logger.log('
|
|
77
|
+
logger.log('Using MySQL binary version:', binaryInfo.version, 'from URL:', binaryInfo.url);
|
|
77
78
|
}
|
|
78
79
|
catch (e) {
|
|
79
80
|
logger.error(e);
|
|
@@ -31,7 +31,6 @@ exports.downloadBinary = downloadBinary;
|
|
|
31
31
|
const https = __importStar(require("https"));
|
|
32
32
|
const fs = __importStar(require("fs"));
|
|
33
33
|
const fsPromises = __importStar(require("fs/promises"));
|
|
34
|
-
const os = __importStar(require("os"));
|
|
35
34
|
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
36
35
|
const path_1 = require("path");
|
|
37
36
|
const crypto_1 = require("crypto");
|
|
@@ -52,7 +51,7 @@ function getZipData(entry) {
|
|
|
52
51
|
}
|
|
53
52
|
function handleTarExtraction(filepath, extractedPath) {
|
|
54
53
|
return new Promise((resolve, reject) => {
|
|
55
|
-
(0, child_process_1.
|
|
54
|
+
(0, child_process_1.execFile)(`tar`, ['-xf', filepath, '-C', extractedPath], (error, stdout, stderr) => {
|
|
56
55
|
if (error || stderr) {
|
|
57
56
|
return reject(error || stderr);
|
|
58
57
|
}
|
|
@@ -86,9 +85,13 @@ function downloadFromCDN(url, downloadLocation, logger) {
|
|
|
86
85
|
request.on('error', (err) => {
|
|
87
86
|
error = err;
|
|
88
87
|
logger.error(err);
|
|
89
|
-
fileStream.
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
fileStream.end(() => {
|
|
89
|
+
fs.unlink(downloadLocation, (unlinkError) => {
|
|
90
|
+
if (unlinkError) {
|
|
91
|
+
logger.error('An error occurred while deleting downloadLocation after an error occurred with the MySQL server binary download. The error was:', unlinkError);
|
|
92
|
+
}
|
|
93
|
+
reject(err.message);
|
|
94
|
+
});
|
|
92
95
|
});
|
|
93
96
|
});
|
|
94
97
|
});
|
|
@@ -100,9 +103,13 @@ function downloadFromCDN(url, downloadLocation, logger) {
|
|
|
100
103
|
fileStream.on('error', (err) => {
|
|
101
104
|
error = err;
|
|
102
105
|
logger.error(err);
|
|
103
|
-
fileStream.end()
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
fileStream.end(() => {
|
|
107
|
+
fs.unlink(downloadLocation, (unlinkError) => {
|
|
108
|
+
if (unlinkError) {
|
|
109
|
+
logger.error('An error occurred while deleting downloadLocation after an error occurred with the fileStream. The error was:', unlinkError);
|
|
110
|
+
}
|
|
111
|
+
reject(err.message);
|
|
112
|
+
});
|
|
106
113
|
});
|
|
107
114
|
});
|
|
108
115
|
});
|
|
@@ -167,59 +174,73 @@ function extractBinary(url, archiveLocation, extractedLocation, logger) {
|
|
|
167
174
|
function downloadBinary(binaryInfo, options, logger) {
|
|
168
175
|
return new Promise(async (resolve, reject) => {
|
|
169
176
|
const { url, version } = binaryInfo;
|
|
170
|
-
const dirpath =
|
|
177
|
+
const dirpath = options.binaryDirectoryPath;
|
|
171
178
|
logger.log('Binary path:', dirpath);
|
|
172
179
|
await fsPromises.mkdir(dirpath, { recursive: true });
|
|
173
180
|
const lastDashIndex = url.lastIndexOf('-');
|
|
174
181
|
const fileExtension = url.slice(lastDashIndex).split('.').splice(1).join('.');
|
|
175
182
|
if (options.downloadBinaryOnce) {
|
|
176
183
|
const extractedPath = `${dirpath}/${version}`;
|
|
177
|
-
await fsPromises.mkdir(extractedPath, { recursive: true });
|
|
178
184
|
const binaryPath = (0, path_1.normalize)(`${extractedPath}/mysql/bin/mysqld${process.platform === 'win32' ? '.exe' : ''}`);
|
|
179
185
|
const archivePath = `${dirpath}/${version}.${fileExtension}`;
|
|
180
186
|
const binaryExists = fs.existsSync(binaryPath);
|
|
181
187
|
if (binaryExists) {
|
|
182
188
|
return resolve(binaryPath);
|
|
183
189
|
}
|
|
184
|
-
let
|
|
185
|
-
|
|
186
|
-
(0, proper_lockfile_1.lockSync)(extractedPath);
|
|
187
|
-
lockedByUs = true;
|
|
188
|
-
await downloadFromCDN(url, archivePath, logger);
|
|
189
|
-
await extractBinary(url, archivePath, extractedPath, logger);
|
|
190
|
+
let releaseFunction;
|
|
191
|
+
while (true) {
|
|
190
192
|
try {
|
|
191
|
-
|
|
193
|
+
await fsPromises.mkdir(extractedPath, { recursive: true });
|
|
194
|
+
releaseFunction = (0, proper_lockfile_1.lockSync)(extractedPath);
|
|
195
|
+
break;
|
|
192
196
|
}
|
|
193
197
|
catch (e) {
|
|
198
|
+
if (e.code === 'ELOCKED') {
|
|
199
|
+
logger.log('Waiting for lock for MySQL version', version);
|
|
200
|
+
await (0, FileLock_1.waitForLock)(extractedPath, options);
|
|
201
|
+
logger.log('Lock is gone for version', version);
|
|
202
|
+
//If the binary does not exist after lock has been released (like if the download fails and the binary got deleted as a result)
|
|
203
|
+
//then the lock acquisition process should start again
|
|
204
|
+
const binaryExists = fs.existsSync(binaryPath);
|
|
205
|
+
if (!binaryExists)
|
|
206
|
+
continue;
|
|
207
|
+
return resolve(binaryPath);
|
|
208
|
+
}
|
|
194
209
|
return reject(e);
|
|
195
210
|
}
|
|
196
|
-
|
|
211
|
+
}
|
|
212
|
+
//The code below only runs if the lock has been acquired by us
|
|
213
|
+
try {
|
|
214
|
+
await downloadFromCDN(url, archivePath, logger);
|
|
215
|
+
await extractBinary(url, archivePath, extractedPath, logger);
|
|
197
216
|
}
|
|
198
217
|
catch (e) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
218
|
+
try {
|
|
219
|
+
await Promise.all([
|
|
220
|
+
fsPromises.rm(extractedPath, { force: true, recursive: true }),
|
|
221
|
+
fsPromises.rm(archivePath, { force: true, recursive: true })
|
|
222
|
+
]);
|
|
204
223
|
}
|
|
205
|
-
|
|
224
|
+
catch (e) {
|
|
225
|
+
logger.error('An error occurred while deleting extractedPath and/or archivePath:', e);
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
206
228
|
try {
|
|
207
|
-
|
|
208
|
-
fsPromises.rm(extractedPath, { force: true, recursive: true }),
|
|
209
|
-
fsPromises.rm(archivePath, { force: true, recursive: true })
|
|
210
|
-
]);
|
|
229
|
+
releaseFunction();
|
|
211
230
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
(0, proper_lockfile_1.unlockSync)(extractedPath, { realpath: false });
|
|
215
|
-
}
|
|
216
|
-
catch (e) {
|
|
217
|
-
logger.error('An error occurred while unlocking path:', e);
|
|
218
|
-
}
|
|
231
|
+
catch (e) {
|
|
232
|
+
logger.error('An error occurred while unlocking path:', e);
|
|
219
233
|
}
|
|
234
|
+
return reject(e);
|
|
220
235
|
}
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
releaseFunction();
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
221
241
|
return reject(e);
|
|
222
242
|
}
|
|
243
|
+
return resolve(binaryPath);
|
|
223
244
|
}
|
|
224
245
|
const uuid = (0, crypto_1.randomUUID)();
|
|
225
246
|
const zipFilepath = `${dirpath}/${uuid}.${fileExtension}`;
|
|
@@ -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,
|
|
33
|
+
var _Executor_instances, _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");
|
|
@@ -83,7 +83,7 @@ class Executor {
|
|
|
83
83
|
const versions = [];
|
|
84
84
|
for (const dir of servers) {
|
|
85
85
|
const path = `${process.env.PROGRAMFILES}\\MySQL\\${dir}\\bin\\mysqld`;
|
|
86
|
-
const { error, stdout, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m",
|
|
86
|
+
const { error, stdout, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, path, ['--version']);
|
|
87
87
|
if (error || stderr) {
|
|
88
88
|
return reject(error || stderr);
|
|
89
89
|
}
|
|
@@ -110,8 +110,8 @@ class Executor {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
else {
|
|
113
|
-
const { error, stdout, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m",
|
|
114
|
-
if (
|
|
113
|
+
const { error, stdout, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, 'mysqld', ['--version']);
|
|
114
|
+
if (error && error.code === 'ENOENT') {
|
|
115
115
|
resolve(null);
|
|
116
116
|
}
|
|
117
117
|
else if (error || stderr) {
|
|
@@ -161,22 +161,16 @@ class Executor {
|
|
|
161
161
|
} while (retries <= options.portRetries);
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
-
_Executor_instances = new WeakSet(),
|
|
164
|
+
_Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_executeFile(command, args) {
|
|
165
165
|
return new Promise(resolve => {
|
|
166
|
-
(0, child_process_1.
|
|
166
|
+
(0, child_process_1.execFile)(command, args, { signal: AbortSignal_1.default.signal }, (error, stdout, stderr) => {
|
|
167
167
|
resolve({ error, stdout, stderr });
|
|
168
168
|
});
|
|
169
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
170
|
}, _Executor_killProcess = async function _Executor_killProcess(process) {
|
|
177
171
|
let killed = false;
|
|
178
172
|
if (os.platform() === 'win32') {
|
|
179
|
-
const { error, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m",
|
|
173
|
+
const { error, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, 'taskkill', ['/pid', String(process.pid), '/t', '/f']);
|
|
180
174
|
if (!error && !stderr) {
|
|
181
175
|
killed = true;
|
|
182
176
|
}
|
|
@@ -310,7 +304,7 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
310
304
|
await fsPromises.mkdir(datadir, { recursive: true });
|
|
311
305
|
let stderr;
|
|
312
306
|
if (binaryFilepath === 'mysqld') {
|
|
313
|
-
const { error, stderr: output } = await __classPrivateFieldGet(this, _Executor_instances, "m",
|
|
307
|
+
const { error, stderr: output } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, 'mysqld', ['--no-defaults', `--datadir=${datadir}`, '--initialize-insecure']);
|
|
314
308
|
stderr = output;
|
|
315
309
|
if (error) {
|
|
316
310
|
this.logger.error('An error occurred while initializing database with system-installed MySQL:', error);
|
|
@@ -320,7 +314,7 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
320
314
|
else {
|
|
321
315
|
let result;
|
|
322
316
|
try {
|
|
323
|
-
result = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, `${binaryFilepath}`, [`--no-defaults`, `--datadir=${datadir}`, `--initialize-insecure`]
|
|
317
|
+
result = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, `${binaryFilepath}`, [`--no-defaults`, `--datadir=${datadir}`, `--initialize-insecure`]);
|
|
324
318
|
}
|
|
325
319
|
catch (e) {
|
|
326
320
|
this.logger.error('Error occurred from executeFile:', e);
|
|
@@ -342,7 +336,7 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
342
336
|
throw 'Tried to copy libaio into lib folder and MySQL is still failing to initialize. Please check the console for more information.';
|
|
343
337
|
}
|
|
344
338
|
if (binaryFilepath.slice(-16) === 'mysql/bin/mysqld') {
|
|
345
|
-
const { error: lderror, stdout, stderr: ldstderr } = await __classPrivateFieldGet(this, _Executor_instances, "m",
|
|
339
|
+
const { error: lderror, stdout, stderr: ldstderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, 'ldconfig', ['-p']);
|
|
346
340
|
if (lderror || ldstderr) {
|
|
347
341
|
this.logger.error('The following libaio error occurred:', stderr);
|
|
348
342
|
this.logger.error('After the libaio error, an ldconfig error occurred:', lderror || ldstderr);
|
|
@@ -358,8 +352,34 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
358
352
|
const libaioSymlinkPath = libaioEntry.slice(libaioPathIndex + 3);
|
|
359
353
|
const libaioPath = await fsPromises.realpath(libaioSymlinkPath);
|
|
360
354
|
const copyPath = (0, path_1.resolve)(`${binaryFilepath}/../../lib/private/libaio.so.1`);
|
|
361
|
-
|
|
362
|
-
|
|
355
|
+
let lockRelease;
|
|
356
|
+
while (true) {
|
|
357
|
+
try {
|
|
358
|
+
lockRelease = (0, proper_lockfile_1.lockSync)(copyPath, { realpath: false });
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
catch (e) {
|
|
362
|
+
if (e.code === 'ELOCKED') {
|
|
363
|
+
this.logger.log('Waiting for lock for libaio copy');
|
|
364
|
+
await (0, FileLock_1.waitForLock)(copyPath, options);
|
|
365
|
+
this.logger.log('Lock is gone for libaio copy');
|
|
366
|
+
//If libaio does not exist after the lock has been released (like if the copy fails)
|
|
367
|
+
//then the lock acquisition process should start again
|
|
368
|
+
const binaryExists = fs.existsSync(copyPath);
|
|
369
|
+
if (!binaryExists)
|
|
370
|
+
continue;
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
this.logger.error('An error occurred from locking libaio section:', e);
|
|
375
|
+
throw e;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
if (lockRelease) {
|
|
380
|
+
//This code will only run if the lock has been acquired successfully.
|
|
381
|
+
//If the lock was already acquired by some other process, this process would have already waited for the lock, so no copying needs to be done since it's already happened.
|
|
382
|
+
//If the lock failed to acquire for some other reason, the error would've already been thrown.
|
|
363
383
|
this.logger.log('libaio copy path:', copyPath, '| libaio symlink path:', libaioSymlinkPath, '| libaio actual path:', libaioPath);
|
|
364
384
|
let copyError;
|
|
365
385
|
try {
|
|
@@ -377,7 +397,7 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
377
397
|
}
|
|
378
398
|
finally {
|
|
379
399
|
try {
|
|
380
|
-
(
|
|
400
|
+
lockRelease();
|
|
381
401
|
}
|
|
382
402
|
catch (e) {
|
|
383
403
|
this.logger.error('Error unlocking libaio file:', e);
|
|
@@ -392,15 +412,6 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
392
412
|
return;
|
|
393
413
|
}
|
|
394
414
|
}
|
|
395
|
-
catch (error) {
|
|
396
|
-
if (String(error) === 'Error: Lock file is already being held') {
|
|
397
|
-
this.logger.log('Waiting for lock for libaio copy');
|
|
398
|
-
await (0, FileLock_1.waitForLock)(copyPath, options);
|
|
399
|
-
this.logger.log('Lock is gone for libaio copy');
|
|
400
|
-
}
|
|
401
|
-
this.logger.error('An error occurred from locking libaio section:', error);
|
|
402
|
-
throw error;
|
|
403
|
-
}
|
|
404
415
|
}
|
|
405
416
|
else {
|
|
406
417
|
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';
|
|
@@ -8,7 +8,7 @@ function waitForLock(path, options) {
|
|
|
8
8
|
while (retries <= options.lockRetries) {
|
|
9
9
|
retries++;
|
|
10
10
|
try {
|
|
11
|
-
const locked = (0, proper_lockfile_1.checkSync)(path);
|
|
11
|
+
const locked = (0, proper_lockfile_1.checkSync)(path, { realpath: false });
|
|
12
12
|
if (!locked) {
|
|
13
13
|
return resolve();
|
|
14
14
|
}
|
|
@@ -11,17 +11,17 @@ class Logger {
|
|
|
11
11
|
}
|
|
12
12
|
log(...args) {
|
|
13
13
|
if (this.LOG_LEVEL === 0) {
|
|
14
|
-
console.log(...args);
|
|
14
|
+
console.log('[mysql-memory-server - LOG]:', ...args);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
warn(...args) {
|
|
18
18
|
if (this.LOG_LEVEL <= 1) {
|
|
19
|
-
console.warn(...args);
|
|
19
|
+
console.warn('[mysql-memory-server - WARN]:', ...args);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
error(...args) {
|
|
23
23
|
if (this.LOG_LEVEL <= 2) {
|
|
24
|
-
console.error(...args);
|
|
24
|
+
console.error('[mysql-memory-server - ERROR]:', ...args);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ExecFileException } from "child_process";
|
|
2
2
|
export type LOG_LEVEL = 'LOG' | 'WARN' | 'ERROR';
|
|
3
3
|
export type ServerOptions = {
|
|
4
4
|
version?: string;
|
|
@@ -14,6 +14,7 @@ export type ServerOptions = {
|
|
|
14
14
|
ignoreUnsupportedSystemVersion?: boolean;
|
|
15
15
|
port?: number;
|
|
16
16
|
xPort?: number;
|
|
17
|
+
binaryDirectoryPath?: string;
|
|
17
18
|
};
|
|
18
19
|
export type InternalServerOptions = {
|
|
19
20
|
version?: string;
|
|
@@ -29,12 +30,13 @@ export type InternalServerOptions = {
|
|
|
29
30
|
ignoreUnsupportedSystemVersion: boolean;
|
|
30
31
|
port: number;
|
|
31
32
|
xPort: number;
|
|
33
|
+
binaryDirectoryPath: string;
|
|
32
34
|
};
|
|
33
35
|
export type ExecutorOptions = {
|
|
34
36
|
logLevel: LOG_LEVEL;
|
|
35
37
|
};
|
|
36
|
-
export type
|
|
37
|
-
error:
|
|
38
|
+
export type ExecuteFileReturn = {
|
|
39
|
+
error: ExecFileException | null;
|
|
38
40
|
stdout: string;
|
|
39
41
|
stderr: string;
|
|
40
42
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mysql-memory-server",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
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",
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
"test:ci": "jest --testPathIgnorePatterns=/stress-tests/ --setupFilesAfterEnv ./ciSetup.js --verbose",
|
|
21
21
|
"stress": "jest --runTestsByPath stress-tests/stress.test.ts --setupFilesAfterEnv ./ciSetup.js --verbose"
|
|
22
22
|
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=16.6.0"
|
|
25
|
+
},
|
|
23
26
|
"author": "Sebastian-Webster",
|
|
24
27
|
"license": "MIT",
|
|
25
28
|
"devDependencies": {
|
|
@@ -27,16 +30,16 @@
|
|
|
27
30
|
"@babel/preset-env": "^7.25.4",
|
|
28
31
|
"@babel/preset-typescript": "^7.24.7",
|
|
29
32
|
"@types/adm-zip": "^0.5.5",
|
|
30
|
-
"@types/node": "^22.5.
|
|
33
|
+
"@types/node": "^22.5.4",
|
|
31
34
|
"@types/proper-lockfile": "^4.1.4",
|
|
32
35
|
"@types/semver": "^7.5.8",
|
|
33
36
|
"babel-jest": "^29.7.0",
|
|
34
37
|
"jest": "^29.7.0",
|
|
35
|
-
"mysql2": "^3.
|
|
36
|
-
"typescript": "^5.
|
|
38
|
+
"mysql2": "^3.11.2",
|
|
39
|
+
"typescript": "^5.6.2"
|
|
37
40
|
},
|
|
38
41
|
"dependencies": {
|
|
39
|
-
"adm-zip": "^0.5.
|
|
42
|
+
"adm-zip": "^0.5.16",
|
|
40
43
|
"proper-lockfile": "^4.1.2",
|
|
41
44
|
"semver": "^7.6.3"
|
|
42
45
|
},
|