mysql-memory-server 1.3.0 → 1.4.1
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
CHANGED
|
@@ -11,7 +11,7 @@ Download with your package manager of choice. The package name is `mysql-memory-
|
|
|
11
11
|
#### Requirements
|
|
12
12
|
|
|
13
13
|
- Node.js 16.6.0 and newer
|
|
14
|
-
- macOS 13+, Windows, or Linux (
|
|
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
|
Windows only requirements:
|
|
17
17
|
- `Microsoft Visual C++ 2019 Redistributable Package` needs to be installed
|
|
@@ -143,6 +143,22 @@ Default: root
|
|
|
143
143
|
|
|
144
144
|
Description: The username of the user that is used to login to the database.
|
|
145
145
|
|
|
146
|
+
- `port: number`
|
|
147
|
+
|
|
148
|
+
Required: No
|
|
149
|
+
|
|
150
|
+
Default: 0
|
|
151
|
+
|
|
152
|
+
Description: The port that the database will listen on. If set to 0, a randomly generated port is used.
|
|
153
|
+
|
|
154
|
+
- `xPort: number`
|
|
155
|
+
|
|
156
|
+
Required: No
|
|
157
|
+
|
|
158
|
+
Default: 0
|
|
159
|
+
|
|
160
|
+
Description: The port that the MySQL X Plugin will listen on. If set to 0, a randomly generated port is used.
|
|
161
|
+
|
|
146
162
|
- `ignoreUnsupportedSystemVersion: boolean`
|
|
147
163
|
|
|
148
164
|
Required: No
|
package/dist/src/index.js
CHANGED
|
@@ -31,16 +31,12 @@ const Logger_1 = __importDefault(require("./libraries/Logger"));
|
|
|
31
31
|
const os = __importStar(require("node:os"));
|
|
32
32
|
const Executor_1 = __importDefault(require("./libraries/Executor"));
|
|
33
33
|
const semver_1 = require("semver");
|
|
34
|
-
const AbortSignal_1 = __importDefault(require("./libraries/AbortSignal"));
|
|
35
34
|
const Version_1 = __importDefault(require("./libraries/Version"));
|
|
36
35
|
const versions_json_1 = __importDefault(require("./versions.json"));
|
|
37
36
|
const Downloader_1 = require("./libraries/Downloader");
|
|
38
37
|
const crypto_1 = require("crypto");
|
|
39
38
|
const path_1 = require("path");
|
|
40
39
|
const constants_1 = __importDefault(require("./constants"));
|
|
41
|
-
process.on('exit', () => {
|
|
42
|
-
AbortSignal_1.default.abort('Process is exiting');
|
|
43
|
-
});
|
|
44
40
|
async function createDB(opts) {
|
|
45
41
|
const defaultOptions = {
|
|
46
42
|
dbName: 'dbdata',
|
|
@@ -53,7 +49,9 @@ async function createDB(opts) {
|
|
|
53
49
|
deleteDBAfterStopped: true,
|
|
54
50
|
//mysqlmsn = MySQL Memory Server Node.js
|
|
55
51
|
dbPath: (0, path_1.normalize)(`${os.tmpdir()}/mysqlmsn/dbs/${(0, crypto_1.randomUUID)().replace(/-/g, '')}`),
|
|
56
|
-
ignoreUnsupportedSystemVersion: false
|
|
52
|
+
ignoreUnsupportedSystemVersion: false,
|
|
53
|
+
port: 0,
|
|
54
|
+
xPort: 0
|
|
57
55
|
};
|
|
58
56
|
const options = { ...defaultOptions, ...opts };
|
|
59
57
|
const logger = new Logger_1.default(options.logLevel);
|
|
@@ -88,7 +86,7 @@ async function createDB(opts) {
|
|
|
88
86
|
binaryFilepath = await (0, Downloader_1.downloadBinary)(binaryInfo, options, logger);
|
|
89
87
|
}
|
|
90
88
|
catch (error) {
|
|
91
|
-
logger.error('Failed to download binary');
|
|
89
|
+
logger.error('Failed to download binary:', error);
|
|
92
90
|
throw `Failed to download binary. The error was: "${error}"`;
|
|
93
91
|
}
|
|
94
92
|
logger.log('Running downloaded binary');
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const DBDestroySignal = new AbortController();
|
|
4
|
+
function abortSignal() {
|
|
5
|
+
if (!DBDestroySignal.signal.aborted) {
|
|
6
|
+
DBDestroySignal.abort('Process is exiting');
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
process.on('beforeExit', abortSignal);
|
|
10
|
+
process.on('exit', abortSignal);
|
|
4
11
|
exports.default = DBDestroySignal;
|
|
@@ -107,7 +107,7 @@ function downloadFromCDN(url, downloadLocation, logger) {
|
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
|
-
function extractBinary(url, archiveLocation, extractedLocation) {
|
|
110
|
+
function extractBinary(url, archiveLocation, extractedLocation, logger) {
|
|
111
111
|
return new Promise(async (resolve, reject) => {
|
|
112
112
|
const lastDashIndex = url.lastIndexOf('-');
|
|
113
113
|
const fileExtension = url.slice(lastDashIndex).split('.').splice(1).join('.');
|
|
@@ -140,8 +140,11 @@ function extractBinary(url, archiveLocation, extractedLocation) {
|
|
|
140
140
|
try {
|
|
141
141
|
await fsPromises.rm(archiveLocation);
|
|
142
142
|
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
logger.error('A non-fatal error occurred while removing no longer needed archive file:', e);
|
|
145
|
+
}
|
|
143
146
|
finally {
|
|
144
|
-
fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
|
|
147
|
+
await fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
|
|
145
148
|
return resolve((0, path_1.normalize)(`${extractedLocation}/mysql/bin/mysqld.exe`));
|
|
146
149
|
}
|
|
147
150
|
}
|
|
@@ -149,8 +152,11 @@ function extractBinary(url, archiveLocation, extractedLocation) {
|
|
|
149
152
|
try {
|
|
150
153
|
await fsPromises.rm(archiveLocation);
|
|
151
154
|
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
logger.error('A non-fatal error occurred while removing no longer needed archive file:', e);
|
|
157
|
+
}
|
|
152
158
|
finally {
|
|
153
|
-
fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
|
|
159
|
+
await fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
|
|
154
160
|
resolve(`${extractedLocation}/mysql/bin/mysqld`);
|
|
155
161
|
}
|
|
156
162
|
}).catch(error => {
|
|
@@ -170,15 +176,17 @@ function downloadBinary(binaryInfo, options, logger) {
|
|
|
170
176
|
const extractedPath = `${dirpath}/${version}`;
|
|
171
177
|
await fsPromises.mkdir(extractedPath, { recursive: true });
|
|
172
178
|
const binaryPath = (0, path_1.normalize)(`${extractedPath}/mysql/bin/mysqld${process.platform === 'win32' ? '.exe' : ''}`);
|
|
179
|
+
const archivePath = `${dirpath}/${version}.${fileExtension}`;
|
|
173
180
|
const binaryExists = fs.existsSync(binaryPath);
|
|
174
181
|
if (binaryExists) {
|
|
175
182
|
return resolve(binaryPath);
|
|
176
183
|
}
|
|
184
|
+
let lockedByUs = false;
|
|
177
185
|
try {
|
|
178
186
|
(0, proper_lockfile_1.lockSync)(extractedPath);
|
|
179
|
-
|
|
187
|
+
lockedByUs = true;
|
|
180
188
|
await downloadFromCDN(url, archivePath, logger);
|
|
181
|
-
await extractBinary(url, archivePath, extractedPath);
|
|
189
|
+
await extractBinary(url, archivePath, extractedPath, logger);
|
|
182
190
|
try {
|
|
183
191
|
(0, proper_lockfile_1.unlockSync)(extractedPath);
|
|
184
192
|
}
|
|
@@ -188,12 +196,28 @@ function downloadBinary(binaryInfo, options, logger) {
|
|
|
188
196
|
return resolve(binaryPath);
|
|
189
197
|
}
|
|
190
198
|
catch (e) {
|
|
191
|
-
if (String(e)
|
|
199
|
+
if (String(e).includes('Lock file is already being held')) {
|
|
192
200
|
logger.log('Waiting for lock for MySQL version', version);
|
|
193
201
|
await (0, FileLock_1.waitForLock)(extractedPath, options);
|
|
194
202
|
logger.log('Lock is gone for version', version);
|
|
195
203
|
return resolve(binaryPath);
|
|
196
204
|
}
|
|
205
|
+
if (lockedByUs) {
|
|
206
|
+
try {
|
|
207
|
+
await Promise.all([
|
|
208
|
+
fsPromises.rm(extractedPath, { force: true, recursive: true }),
|
|
209
|
+
fsPromises.rm(archivePath, { force: true, recursive: true })
|
|
210
|
+
]);
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
try {
|
|
214
|
+
(0, proper_lockfile_1.unlockSync)(extractedPath, { realpath: false });
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
logger.error('An error occurred while unlocking path:', e);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
197
221
|
return reject(e);
|
|
198
222
|
}
|
|
199
223
|
}
|
|
@@ -208,7 +232,7 @@ function downloadBinary(binaryInfo, options, logger) {
|
|
|
208
232
|
reject(e);
|
|
209
233
|
}
|
|
210
234
|
try {
|
|
211
|
-
const binaryPath = await extractBinary(url, zipFilepath, extractedPath);
|
|
235
|
+
const binaryPath = await extractBinary(url, zipFilepath, extractedPath, logger);
|
|
212
236
|
resolve(binaryPath);
|
|
213
237
|
}
|
|
214
238
|
catch (e) {
|
|
@@ -135,8 +135,8 @@ class Executor {
|
|
|
135
135
|
do {
|
|
136
136
|
await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, binaryFilepath, datadir, true);
|
|
137
137
|
this.logger.log('Setting up directories was successful');
|
|
138
|
-
const port = (0, Port_1.GenerateRandomPort)();
|
|
139
|
-
const mySQLXPort = (0, Port_1.GenerateRandomPort)();
|
|
138
|
+
const port = options.port || (0, Port_1.GenerateRandomPort)();
|
|
139
|
+
const mySQLXPort = options.xPort || (0, Port_1.GenerateRandomPort)();
|
|
140
140
|
this.logger.log('Using port:', port, 'and MySQLX port:', mySQLXPort, 'on retry:', retries);
|
|
141
141
|
try {
|
|
142
142
|
this.logger.log('Starting MySQL process');
|
|
@@ -194,7 +194,7 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
194
194
|
const errorLogFile = `${datadir}/errorlog.err`;
|
|
195
195
|
return new Promise(async (resolve, reject) => {
|
|
196
196
|
await fsPromises.rm(logFile, { force: true });
|
|
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' });
|
|
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}`, `--user=${os.userInfo().username}`], { signal: AbortSignal_1.default.signal, killSignal: 'SIGKILL' });
|
|
198
198
|
//resolveFunction is the function that will be called to resolve the promise that stops the database.
|
|
199
199
|
//If resolveFunction is not undefined, the database has received a kill signal and data cleanup procedures should run.
|
|
200
200
|
//Once ran, resolveFunction will be called.
|
|
@@ -226,6 +226,9 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
226
226
|
await this.deleteDatabaseDirectory(dbPath);
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
|
+
catch (e) {
|
|
230
|
+
this.logger.error('An erorr occurred while deleting database directory at path:', dbPath, '| The error was:', e);
|
|
231
|
+
}
|
|
229
232
|
finally {
|
|
230
233
|
try {
|
|
231
234
|
if (binaryFilepath.includes(os.tmpdir()) && !options.downloadBinaryOnce) {
|
|
@@ -237,6 +240,9 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
237
240
|
await fsPromises.rm(splitPath.join('/'), { force: true, recursive: true });
|
|
238
241
|
}
|
|
239
242
|
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
this.logger.error('An error occurred while deleting no longer needed MySQL binary:', e);
|
|
245
|
+
}
|
|
240
246
|
finally {
|
|
241
247
|
if (resolveFunction) {
|
|
242
248
|
resolveFunction();
|
package/dist/types/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export type ServerOptions = {
|
|
|
12
12
|
deleteDBAfterStopped?: boolean;
|
|
13
13
|
dbPath?: string;
|
|
14
14
|
ignoreUnsupportedSystemVersion?: boolean;
|
|
15
|
+
port?: number;
|
|
16
|
+
xPort?: number;
|
|
15
17
|
};
|
|
16
18
|
export type InternalServerOptions = {
|
|
17
19
|
version?: string;
|
|
@@ -25,6 +27,8 @@ export type InternalServerOptions = {
|
|
|
25
27
|
deleteDBAfterStopped: boolean;
|
|
26
28
|
dbPath: string;
|
|
27
29
|
ignoreUnsupportedSystemVersion: boolean;
|
|
30
|
+
port: number;
|
|
31
|
+
xPort: number;
|
|
28
32
|
};
|
|
29
33
|
export type ExecutorOptions = {
|
|
30
34
|
logLevel: LOG_LEVEL;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mysql-memory-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
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",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@babel/core": "^7.24.9",
|
|
27
|
-
"@babel/preset-env": "^7.
|
|
27
|
+
"@babel/preset-env": "^7.25.4",
|
|
28
28
|
"@babel/preset-typescript": "^7.24.7",
|
|
29
29
|
"@types/adm-zip": "^0.5.5",
|
|
30
|
-
"@types/node": "^22.
|
|
30
|
+
"@types/node": "^22.5.0",
|
|
31
31
|
"@types/proper-lockfile": "^4.1.4",
|
|
32
32
|
"@types/semver": "^7.5.8",
|
|
33
33
|
"babel-jest": "^29.7.0",
|