mysql-memory-server 1.0.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/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024-present, Sebastian Webster.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # MySQL Memory Server
2
+
3
+ This package allows you to create ephemeral MySQL databases inside of Node.js, great for testing. When creating a new database, if the version selected is not installed on the system, the binary is downloaded from MySQL's CDN (cdn.mysql.com)
4
+
5
+ You can run multiple MySQL databases with this package at the same time. Each database will use a random free port. The databases will automatically shutdown when the Node.js process exits. A `stop()` method is also provided to stop each database instance.
6
+
7
+ ## Installation
8
+
9
+ Download with your package manager of choice. The package name is `mysql-memory-server`. If using npm, the install command will be `npm install mysql-memory-server`.
10
+
11
+ #### Requirements
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. Ubuntu 24.04 and newer is not fully supported at this time - go to the bottom of this file to learn more)
15
+
16
+ Windows only requirements:
17
+ - `Microsoft Visual C++ 2019 Redistributable Package` needs to be installed
18
+
19
+ Linux only requirements:
20
+ - The `libaio1` package needs to be installed
21
+ - The `tar` package needs to be installed
22
+
23
+ ## Usage
24
+
25
+ ```javascript
26
+ import { createDB } from 'mysql-memory-server';
27
+ import sql from 'mysql2/promise'
28
+
29
+ // Create a new database
30
+ const db = await createDB()
31
+
32
+ // Connect to the new database with the port provided
33
+ const connection = await sql.createConnection({
34
+ host: '127.0.0.1',
35
+ user: 'root',
36
+ port: db.port,
37
+ database: db.dbName
38
+ })
39
+
40
+ // Run your queries here
41
+ // ...
42
+
43
+ // Once done, disconnect from the database
44
+ await connection.end()
45
+
46
+ // Then stop the database
47
+ await db.stop()
48
+ ```
49
+
50
+ ## Documentation
51
+
52
+ ##### `createDB(options: ServerOptions): Promise<MySQLDB>`
53
+ ###### On success, resolves with an object with the following properties:
54
+
55
+ - `port: number`
56
+ The port that the MySQL database is listening on
57
+ - `xPort: number`
58
+ The port that MySQLX is listening on
59
+ - `dbName: string`
60
+ The database that was created on database initialization
61
+ - `stop: () => Promise<void>`
62
+ The method to stop the database. The returned promise resolves when the database has successfully stopped.
63
+
64
+ ###### Options:
65
+ - `version: string`
66
+
67
+ Required: No
68
+
69
+ Default: undefined
70
+
71
+ 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.
72
+
73
+ If left undefined and the system has MySQL already installed, the system installed version of MySQL will be used. If left undefined and the system does not have MySQL installed, the latest version of MySQL in the `versions.json` file in this package will be downloaded. If defined and the system has that version of MySQL installed, the system installed version will be used. If defined and the system does not have that version of MySQL installed, that version will be downloaded as long as it is found in the `versions.json` file in this package.
74
+
75
+ - `dbName: string`
76
+
77
+ Required: No
78
+
79
+ Default: "dbdata"
80
+
81
+ Description: The name of the database to create when initializing MySQL. You'd use this name to connect to the database.
82
+
83
+ - `logLevel: "LOG" | "WARN" | "ERROR"`
84
+
85
+ Required: No
86
+
87
+ Default: "ERROR"
88
+
89
+ Description: Log level for the database. If "ERROR" is used, only errors will show up in the console. If "WARN" is used, warnings and errors will show up in the console. If "LOG" is used, everything will show up in the console.
90
+
91
+ - `portRetries: number`
92
+
93
+ Required: No
94
+
95
+ Default: 10
96
+
97
+ Description: Number of times to try connecting MySQL to a randomly generated port before giving up. According to the [MySQL Documentation](https://dev.mysql.com/doc/refman/en/server-options.html#option_mysqld_port "MySQL Documentation") if port 0 is used as the MySQL server port, the default value (3306) will be used. To get around this, a random number between 1025 - 65535 (inclusive) is generated and used for the database's port. If MySQL cannot successfully listen on a randomly generated port after `portRetries` then the `createDB()` promise is rejected. A warning is created when MySQL tries connecting to a port that is already in use.
98
+
99
+ - `downloadBinaryOnce: boolean`
100
+
101
+ Required: No
102
+
103
+ Default: true
104
+
105
+ Description: If set to true, all versions requested that need to be downloaded from MySQL's CDN will be downloaded once and will stay on the system after the database stops. If set to false, the binaries that need to be downloaded will be downloaded for each database creation and will be deleted when the database is stopped.
106
+
107
+ Use `false` to save disk space after the databases have been stopped, or use `true` to save bandwidth
108
+
109
+ - `lockRetries: number`
110
+
111
+ Required: No
112
+
113
+ Default: 1,000
114
+
115
+ Description: If `downloadBinaryOnce` is set to `true`, `lockRetries` is the number of times to check to see if the lock for the binary has been released (meaning it has been successfully downloaded and extracted). If the number of retries exceeds `lockRetries`, the `createDB()` promise gets rejected.
116
+
117
+ - `lockRetryWait: number`
118
+
119
+ Required: No
120
+
121
+ Default: 1,000
122
+
123
+ Description: If `downloadBinaryOnce` is set to `true`, `lockRetryWait` is the number of milliseconds to wait before checking if the lock has been released.
124
+
125
+ ## If using Ubuntu 24.04 and newer
126
+
127
+ Selecting what MySQL version to use is not currently supported on Ubuntu 24.04 and newer. To use this package on Ubuntu 24.04 and newer you must have the `mysql-server` package installed on your system and `ServerOptions.version` must either be the version that is installed on the system or undefined.
@@ -0,0 +1,2 @@
1
+ import { ServerOptions } from '../types';
2
+ export declare function createDB(opts?: ServerOptions): Promise<import("../types").MySQLDB>;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.createDB = createDB;
30
+ const Logger_1 = __importDefault(require("./libraries/Logger"));
31
+ const os = __importStar(require("node:os"));
32
+ const Executor_1 = __importDefault(require("./libraries/Executor"));
33
+ const semver_1 = require("semver");
34
+ const AbortSignal_1 = __importDefault(require("./libraries/AbortSignal"));
35
+ const Version_1 = __importDefault(require("./libraries/Version"));
36
+ const versions_json_1 = __importDefault(require("./versions.json"));
37
+ const Downloader_1 = require("./libraries/Downloader");
38
+ const defaultOptions = {
39
+ dbName: 'dbdata',
40
+ logLevel: 'ERROR',
41
+ portRetries: 10,
42
+ downloadBinaryOnce: true,
43
+ lockRetries: 1000,
44
+ lockRetryWait: 1000
45
+ };
46
+ process.on('exit', () => {
47
+ AbortSignal_1.default.abort('Process is exiting');
48
+ });
49
+ async function createDB(opts = defaultOptions) {
50
+ const options = { ...defaultOptions, ...opts };
51
+ const logger = new Logger_1.default(options.logLevel);
52
+ const executor = new Executor_1.default(logger);
53
+ const version = await executor.getMySQLVersion(options.version);
54
+ logger.log('Version currently installed:', version);
55
+ if (version === null || (options.version && !(0, semver_1.satisfies)(version.version, options.version))) {
56
+ let binaryInfo;
57
+ let binaryFilepath;
58
+ try {
59
+ binaryInfo = (0, Version_1.default)(versions_json_1.default, options.version);
60
+ logger.log('Downloading binary:', binaryInfo.version, 'from URL:', binaryInfo.url);
61
+ }
62
+ catch (e) {
63
+ logger.error(e);
64
+ if (options.version) {
65
+ 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!`;
66
+ }
67
+ 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!`;
68
+ }
69
+ try {
70
+ binaryFilepath = await (0, Downloader_1.downloadBinary)(binaryInfo, options, logger);
71
+ }
72
+ catch (error) {
73
+ logger.error('Failed to download binary');
74
+ throw error;
75
+ }
76
+ logger.log('Running downloaded binary');
77
+ return await executor.startMySQL(options, binaryFilepath);
78
+ }
79
+ else {
80
+ logger.log(version);
81
+ return await executor.startMySQL(options, version.path);
82
+ }
83
+ }
@@ -0,0 +1,2 @@
1
+ declare const DBDestroySignal: AbortController;
2
+ export default DBDestroySignal;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const DBDestroySignal = new AbortController();
4
+ exports.default = DBDestroySignal;
@@ -0,0 +1,4 @@
1
+ import Logger from './Logger';
2
+ import { BinaryInfo, ServerOptions } from '../../types';
3
+ export declare function downloadVersions(): Promise<string>;
4
+ export declare function downloadBinary(binaryInfo: BinaryInfo, options: ServerOptions, logger: Logger): Promise<string>;
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.downloadVersions = downloadVersions;
30
+ exports.downloadBinary = downloadBinary;
31
+ const https = __importStar(require("https"));
32
+ const fs = __importStar(require("fs"));
33
+ const fsPromises = __importStar(require("fs/promises"));
34
+ const os = __importStar(require("os"));
35
+ const adm_zip_1 = __importDefault(require("adm-zip"));
36
+ const path_1 = require("path");
37
+ const crypto_1 = require("crypto");
38
+ const child_process_1 = require("child_process");
39
+ const proper_lockfile_1 = require("proper-lockfile");
40
+ function getZipData(entry) {
41
+ return new Promise((resolve, reject) => {
42
+ entry.getDataAsync((data, err) => {
43
+ if (err) {
44
+ reject(err);
45
+ }
46
+ else {
47
+ resolve(data);
48
+ }
49
+ });
50
+ });
51
+ }
52
+ function handleTarExtraction(filepath, extractedPath) {
53
+ return new Promise((resolve, reject) => {
54
+ (0, child_process_1.exec)(`tar -xf ${filepath} -C ${extractedPath}`, (error, stdout, stderr) => {
55
+ if (error || stderr) {
56
+ return reject(error || stderr);
57
+ }
58
+ resolve();
59
+ });
60
+ });
61
+ }
62
+ function downloadVersions() {
63
+ return new Promise((resolve, reject) => {
64
+ let json = "";
65
+ https.get("https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/raw/main/versions.json", function (response) {
66
+ response
67
+ .on("data", append => json += append)
68
+ .on("error", e => {
69
+ reject(e);
70
+ })
71
+ .on("end", () => {
72
+ resolve(json);
73
+ });
74
+ });
75
+ });
76
+ }
77
+ function downloadFromCDN(url, downloadLocation, logger) {
78
+ return new Promise((resolve, reject) => {
79
+ const fileStream = fs.createWriteStream(downloadLocation);
80
+ fileStream.on('open', () => {
81
+ const request = https.get(url, (response) => {
82
+ response.pipe(fileStream);
83
+ });
84
+ request.on('error', (err) => {
85
+ logger.error(err);
86
+ fileStream.close();
87
+ fs.unlink(downloadLocation, (err) => {
88
+ reject(err);
89
+ });
90
+ });
91
+ });
92
+ fileStream.on('finish', () => {
93
+ resolve();
94
+ });
95
+ fileStream.on('error', (err) => {
96
+ logger.error(err);
97
+ fileStream.end();
98
+ fs.unlink(downloadLocation, () => {
99
+ reject(err);
100
+ });
101
+ });
102
+ });
103
+ }
104
+ function extractBinary(url, archiveLocation, extractedLocation) {
105
+ return new Promise(async (resolve, reject) => {
106
+ const lastDashIndex = url.lastIndexOf('-');
107
+ const fileExtension = url.slice(lastDashIndex).split('.').splice(1).join('.');
108
+ await fsPromises.mkdir(extractedLocation, { recursive: true });
109
+ const folderName = url.split('/').at(-1).replace(`.${fileExtension}`, '');
110
+ if (fileExtension === 'zip') {
111
+ //Only Windows MySQL files use the .zip extension
112
+ const zip = new adm_zip_1.default(archiveLocation);
113
+ const entries = zip.getEntries();
114
+ for (const entry of entries) {
115
+ if (entry.isDirectory) {
116
+ if (entry.name === folderName) {
117
+ await fsPromises.mkdir(`${extractedLocation}/mysql`, { recursive: true });
118
+ }
119
+ else {
120
+ await fsPromises.mkdir(`${extractedLocation}/${entry.entryName}`, { recursive: true });
121
+ }
122
+ }
123
+ else {
124
+ const data = await getZipData(entry);
125
+ await fsPromises.writeFile(`${extractedLocation}/${entry.entryName}`, data);
126
+ }
127
+ }
128
+ try {
129
+ await fsPromises.rm(archiveLocation);
130
+ }
131
+ finally {
132
+ fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
133
+ return resolve((0, path_1.normalize)(`${extractedLocation}/mysql/bin/mysqld.exe`));
134
+ }
135
+ }
136
+ handleTarExtraction(archiveLocation, extractedLocation).then(async () => {
137
+ try {
138
+ await fsPromises.rm(archiveLocation);
139
+ }
140
+ finally {
141
+ fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
142
+ resolve(`${extractedLocation}/mysql/bin/mysqld`);
143
+ }
144
+ }).catch(error => {
145
+ reject(`An error occurred while extracting the tar file. Please make sure tar is installed and there is enough storage space for the extraction. The error was: ${error}`);
146
+ });
147
+ });
148
+ }
149
+ function waitForLock(path, options) {
150
+ return new Promise(async (resolve, reject) => {
151
+ let retries = 0;
152
+ while (retries <= options.lockRetries) {
153
+ retries++;
154
+ try {
155
+ const locked = (0, proper_lockfile_1.checkSync)(path);
156
+ if (!locked) {
157
+ return resolve();
158
+ }
159
+ else {
160
+ await new Promise(resolve => setTimeout(resolve, options.lockRetryWait));
161
+ }
162
+ }
163
+ catch (e) {
164
+ return reject(e);
165
+ }
166
+ }
167
+ reject(`lockRetries has been exceeded. Lock had not been released after ${options.lockRetryWait} * ${options.lockRetries} milliseconds.`);
168
+ });
169
+ }
170
+ function downloadBinary(binaryInfo, options, logger) {
171
+ return new Promise(async (resolve, reject) => {
172
+ const { url, version } = binaryInfo;
173
+ const dirpath = `${os.tmpdir()}/mysqlmsn/binaries`;
174
+ logger.log('Binary path:', dirpath);
175
+ await fsPromises.mkdir(dirpath, { recursive: true });
176
+ const lastDashIndex = url.lastIndexOf('-');
177
+ const fileExtension = url.slice(lastDashIndex).split('.').splice(1).join('.');
178
+ if (options.downloadBinaryOnce) {
179
+ const extractedPath = `${dirpath}/${version}`;
180
+ await fsPromises.mkdir(extractedPath, { recursive: true });
181
+ const binaryPath = (0, path_1.normalize)(`${extractedPath}/mysql/bin/mysqld${process.platform === 'win32' ? '.exe' : ''}`);
182
+ const binaryExists = fs.existsSync(binaryPath);
183
+ if (binaryExists) {
184
+ return resolve(binaryPath);
185
+ }
186
+ try {
187
+ (0, proper_lockfile_1.lockSync)(extractedPath);
188
+ const archivePath = `${dirpath}/${version}.${fileExtension}`;
189
+ await downloadFromCDN(url, archivePath, logger);
190
+ await extractBinary(url, archivePath, extractedPath);
191
+ try {
192
+ (0, proper_lockfile_1.unlockSync)(extractedPath);
193
+ }
194
+ catch (e) {
195
+ return reject(e);
196
+ }
197
+ return resolve(binaryPath);
198
+ }
199
+ catch (e) {
200
+ if (String(e) === 'Error: Lock file is already being held') {
201
+ logger.log('Waiting for lock for MySQL version', version);
202
+ await waitForLock(extractedPath, options);
203
+ logger.log('Lock is gone for version', version);
204
+ return resolve(binaryPath);
205
+ }
206
+ return reject(e);
207
+ }
208
+ }
209
+ const uuid = (0, crypto_1.randomUUID)();
210
+ const zipFilepath = `${dirpath}/${uuid}.${fileExtension}`;
211
+ logger.log('Binary filepath:', zipFilepath);
212
+ const extractedPath = `${dirpath}/${uuid}`;
213
+ try {
214
+ await downloadFromCDN(url, zipFilepath, logger);
215
+ }
216
+ catch (e) {
217
+ reject(e);
218
+ }
219
+ try {
220
+ const binaryPath = await extractBinary(url, zipFilepath, extractedPath);
221
+ resolve(binaryPath);
222
+ }
223
+ catch (e) {
224
+ reject(e);
225
+ }
226
+ });
227
+ }
@@ -0,0 +1,10 @@
1
+ import Logger from "./Logger";
2
+ import { InstalledMySQLVersion, InternalServerOptions, MySQLDB } from "../../types";
3
+ declare class Executor {
4
+ #private;
5
+ logger: Logger;
6
+ constructor(logger: Logger);
7
+ getMySQLVersion(preferredVersion?: string): Promise<InstalledMySQLVersion | null>;
8
+ startMySQL(options: InternalServerOptions, binaryFilepath: string): Promise<MySQLDB>;
9
+ }
10
+ export default Executor;
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
26
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
27
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
28
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
29
+ };
30
+ var __importDefault = (this && this.__importDefault) || function (mod) {
31
+ return (mod && mod.__esModule) ? mod : { "default": mod };
32
+ };
33
+ var _Executor_instances, _Executor_execute, _Executor_startMySQLProcess;
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ const child_process_1 = require("child_process");
36
+ const semver_1 = require("semver");
37
+ const os = __importStar(require("os"));
38
+ const fsPromises = __importStar(require("fs/promises"));
39
+ const fs = __importStar(require("fs"));
40
+ const Port_1 = require("./Port");
41
+ const AbortSignal_1 = __importDefault(require("./AbortSignal"));
42
+ const path_1 = require("path");
43
+ const crypto_1 = require("crypto");
44
+ class Executor {
45
+ constructor(logger) {
46
+ _Executor_instances.add(this);
47
+ this.logger = logger;
48
+ }
49
+ getMySQLVersion(preferredVersion) {
50
+ return new Promise(async (resolve, reject) => {
51
+ if (process.platform === 'win32') {
52
+ try {
53
+ const dirs = await fsPromises.readdir(`${process.env.PROGRAMFILES}\\MySQL`);
54
+ const servers = dirs.filter(dirname => dirname.includes('MySQL Server'));
55
+ if (servers.length === 0) {
56
+ return resolve(null);
57
+ }
58
+ const versions = [];
59
+ for (const dir of servers) {
60
+ const path = `${process.env.PROGRAMFILES}\\MySQL\\${dir}\\bin\\mysqld`;
61
+ const { error, stdout, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, `"${path}" --version`);
62
+ if (error || stderr) {
63
+ return reject(error || stderr);
64
+ }
65
+ const verIndex = stdout.indexOf('Ver');
66
+ const version = (0, semver_1.coerce)(stdout.slice(verIndex));
67
+ if (version === null) {
68
+ return reject('Could not get MySQL version');
69
+ }
70
+ else {
71
+ versions.push({ version: version.version, path });
72
+ }
73
+ }
74
+ if (preferredVersion) {
75
+ resolve(versions.find(version => (0, semver_1.satisfies)(version.version, preferredVersion)) || null);
76
+ }
77
+ else {
78
+ versions.sort();
79
+ resolve(versions[0]);
80
+ }
81
+ }
82
+ catch (e) {
83
+ this.logger.error('Error occurred while getting installed MySQL version:', e);
84
+ resolve(null);
85
+ }
86
+ }
87
+ else {
88
+ const { error, stdout, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, 'mysqld --version');
89
+ if (stderr && stderr.includes('not found')) {
90
+ resolve(null);
91
+ }
92
+ else if (error || stderr) {
93
+ reject(error || stderr);
94
+ }
95
+ else {
96
+ const version = (0, semver_1.coerce)(stdout);
97
+ if (version === null) {
98
+ reject('Could not get installed MySQL version');
99
+ }
100
+ else {
101
+ resolve({ version: version.version, path: 'mysqld' });
102
+ }
103
+ }
104
+ }
105
+ });
106
+ }
107
+ startMySQL(options, binaryFilepath) {
108
+ return new Promise(async (resolve, reject) => {
109
+ //mysqlmsn = MySQL Memory Server Node.js
110
+ const dbPath = (0, path_1.normalize)(`${os.tmpdir()}/mysqlmsn/dbs/${(0, crypto_1.randomUUID)().replace(/-/g, '')}`);
111
+ const datadir = (0, path_1.normalize)(`${dbPath}/data`);
112
+ this.logger.log('Created data directory for database at:', datadir);
113
+ await fsPromises.mkdir(datadir, { recursive: true });
114
+ const { error: err, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, `"${binaryFilepath}" --no-defaults --datadir=${datadir} --initialize-insecure`);
115
+ if (err || (stderr && !stderr.includes('InnoDB initialization has ended'))) {
116
+ if (process.platform === 'win32' && err.message.includes('Command failed')) {
117
+ this.logger.error(err || stderr);
118
+ 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.');
119
+ }
120
+ if (process.platform === 'linux' && err.message.includes('libaio.so')) {
121
+ this.logger.error(err || stderr);
122
+ 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');
123
+ }
124
+ return reject(err || stderr);
125
+ }
126
+ await fsPromises.writeFile(`${dbPath}/init.sql`, `CREATE DATABASE ${options.dbName};`, { encoding: 'utf8' });
127
+ let retries = 0;
128
+ do {
129
+ const port = (0, Port_1.GenerateRandomPort)();
130
+ const mySQLXPort = (0, Port_1.GenerateRandomPort)();
131
+ this.logger.log('Using port:', port, 'on retry:', retries);
132
+ try {
133
+ const resolved = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_startMySQLProcess).call(this, options, port, mySQLXPort, datadir, dbPath, binaryFilepath);
134
+ return resolve(resolved);
135
+ }
136
+ catch (e) {
137
+ if (e !== 'Port is already in use') {
138
+ return reject(e);
139
+ }
140
+ retries++;
141
+ if (retries < options.portRetries) {
142
+ this.logger.warn(`One or both of these ports are already in use: ${port} or ${mySQLXPort}. Now retrying... ${retries}/${options.portRetries} possible retries.`);
143
+ }
144
+ }
145
+ } while (retries < options.portRetries);
146
+ 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.`);
147
+ });
148
+ }
149
+ }
150
+ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_execute(command) {
151
+ return new Promise(resolve => {
152
+ (0, child_process_1.exec)(command, { signal: AbortSignal_1.default.signal }, (error, stdout, stderr) => {
153
+ resolve({ error, stdout, stderr });
154
+ });
155
+ });
156
+ }, _Executor_startMySQLProcess = function _Executor_startMySQLProcess(options, port, mySQLXPort, datadir, dbPath, binaryFilepath) {
157
+ const errors = [];
158
+ const logFile = `${dbPath}/log.log`;
159
+ return new Promise(async (resolve, reject) => {
160
+ await fsPromises.rm(logFile, { force: true });
161
+ 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' });
162
+ //resolveFunction is the function that will be called to resolve the promise that stops the database.
163
+ //If resolveFunction is not undefined, the database has received a kill signal and data cleanup procedures should run.
164
+ //Once ran, resolveFunction will be called.
165
+ let resolveFunction;
166
+ process.on('close', async (code, signal) => {
167
+ try {
168
+ await fsPromises.rm(dbPath, { recursive: true, force: true });
169
+ if (binaryFilepath.includes(os.tmpdir()) && !options.downloadBinaryOnce) {
170
+ const splitPath = binaryFilepath.split(os.platform() === 'win32' ? '\\' : '/');
171
+ const binariesIndex = splitPath.indexOf('binaries');
172
+ //The path will be the directory path for the binary download
173
+ splitPath.splice(binariesIndex + 2);
174
+ //Delete the binary folder
175
+ await fsPromises.rm(splitPath.join('/'), { force: true, recursive: true });
176
+ }
177
+ }
178
+ finally {
179
+ if (resolveFunction) {
180
+ resolveFunction();
181
+ return;
182
+ }
183
+ if (code === 0) {
184
+ return reject('Database exited early');
185
+ }
186
+ const errorString = errors.join('\n');
187
+ this.logger.error(errorString);
188
+ if (errorString.includes('Address already in use')) {
189
+ return reject('Port is already in use');
190
+ }
191
+ if (code) {
192
+ return reject(errorString);
193
+ }
194
+ }
195
+ });
196
+ process.stderr.on('data', (data) => {
197
+ if (!resolveFunction) {
198
+ if (Buffer.isBuffer(data)) {
199
+ errors.push(data.toString());
200
+ }
201
+ else {
202
+ errors.push(data);
203
+ }
204
+ }
205
+ });
206
+ fs.watchFile(logFile, async (curr) => {
207
+ if (curr.dev !== 0) {
208
+ //File exists
209
+ const file = await fsPromises.readFile(logFile, { encoding: 'utf8' });
210
+ if (file.includes('started with:')) {
211
+ fs.unwatchFile(logFile);
212
+ resolve({
213
+ port,
214
+ xPort: mySQLXPort,
215
+ dbName: options.dbName,
216
+ stop: () => {
217
+ return new Promise(async (resolve, reject) => {
218
+ resolveFunction = resolve;
219
+ let killed = false;
220
+ if (os.platform() === 'win32') {
221
+ const { error, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, `taskkill /pid ${process.pid} /t /f`);
222
+ if (!error && !stderr) {
223
+ killed = true;
224
+ }
225
+ else {
226
+ this.logger.error(error || stderr);
227
+ }
228
+ }
229
+ else {
230
+ killed = process.kill();
231
+ }
232
+ if (!killed) {
233
+ reject();
234
+ }
235
+ });
236
+ }
237
+ });
238
+ }
239
+ }
240
+ });
241
+ });
242
+ };
243
+ exports.default = Executor;
@@ -0,0 +1,9 @@
1
+ import { LOG_LEVEL } from "../../types";
2
+ declare class Logger {
3
+ LOG_LEVEL: number;
4
+ constructor(level: LOG_LEVEL);
5
+ log(...args: any): void;
6
+ warn(...args: any): void;
7
+ error(...args: any): void;
8
+ }
9
+ export default Logger;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const LOG_LEVELS = {
4
+ 'LOG': 0,
5
+ 'WARN': 1,
6
+ 'ERROR': 2
7
+ };
8
+ class Logger {
9
+ constructor(level) {
10
+ this.LOG_LEVEL = LOG_LEVELS[level];
11
+ }
12
+ log(...args) {
13
+ if (this.LOG_LEVEL === 0) {
14
+ console.log.apply(null, args);
15
+ }
16
+ }
17
+ warn(...args) {
18
+ if (this.LOG_LEVEL <= 1) {
19
+ console.warn.apply(null, args);
20
+ }
21
+ }
22
+ error(...args) {
23
+ if (this.LOG_LEVEL <= 2) {
24
+ console.error.apply(null, args);
25
+ }
26
+ }
27
+ }
28
+ exports.default = Logger;
@@ -0,0 +1 @@
1
+ export declare function GenerateRandomPort(): number;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GenerateRandomPort = GenerateRandomPort;
4
+ function GenerateRandomPort() {
5
+ return Math.floor(Math.random() * (65535 - 1024) + 1025);
6
+ }
@@ -0,0 +1,5 @@
1
+ import { MySQLVersion } from "../../types";
2
+ export default function getBinaryURL(versions: MySQLVersion[], versionToGet?: string): {
3
+ url: string;
4
+ version: string;
5
+ };
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.default = getBinaryURL;
27
+ const os = __importStar(require("os"));
28
+ const semver_1 = require("semver");
29
+ function getBinaryURL(versions, versionToGet = "9.x") {
30
+ let availableVersions = versions;
31
+ availableVersions = availableVersions.filter(v => v.arch === process.arch);
32
+ if (availableVersions.length === 0)
33
+ throw `No MySQL binary could be found for your CPU architecture: ${process.arch}`;
34
+ availableVersions = availableVersions.filter(v => v.os === process.platform);
35
+ if (availableVersions.length === 0)
36
+ throw `No MySQL binary could be found for your OS: ${process.platform}`;
37
+ availableVersions = availableVersions.filter(v => (0, semver_1.satisfies)((0, semver_1.coerce)(os.release()).version, v.osKernelVersionsSupported));
38
+ if (availableVersions.length === 0)
39
+ throw `No MySQL binary could be found that supports your OS version: ${os.release()} | ${os.version()}`;
40
+ const wantedVersions = availableVersions.filter(v => (0, semver_1.satisfies)(v.version, versionToGet));
41
+ if (wantedVersions.length === 0)
42
+ throw `No MySQL binary could be found that meets your version requirement: ${versionToGet} for OS ${process.platform} version ${os.release()} on arch ${process.arch}. The available versions for download are: ${availableVersions.map(v => v.version)}`;
43
+ //Sorts versions in descending order
44
+ wantedVersions.sort((a, b) => a.version < b.version ? 1 : a.version === b.version ? 0 : -1);
45
+ const v = wantedVersions[0];
46
+ return {
47
+ url: v.url,
48
+ version: v.version
49
+ };
50
+ }
@@ -0,0 +1,212 @@
1
+ [
2
+ {
3
+ "version": "9.0.1",
4
+ "arch": "arm64",
5
+ "os": "darwin",
6
+ "osKernelVersionsSupported": ">22",
7
+ "url": "https://cdn.mysql.com//Downloads/MySQL-9.0/mysql-9.0.1-macos14-arm64.tar.gz"
8
+ },
9
+ {
10
+ "version": "9.0.1",
11
+ "arch": "x64",
12
+ "os": "darwin",
13
+ "osKernelVersionsSupported": ">22",
14
+ "url": "https://cdn.mysql.com//Downloads/MySQL-9.0/mysql-9.0.1-macos14-x86_64.tar.gz"
15
+ },
16
+ {
17
+ "version": "9.0.1",
18
+ "arch": "x64",
19
+ "os": "linux",
20
+ "osKernelVersionsSupported": "*",
21
+ "url": "https://cdn.mysql.com//Downloads/MySQL-9.0/mysql-9.0.1-linux-glibc2.17-x86_64-minimal.tar.xz"
22
+ },
23
+ {
24
+ "version": "9.0.1",
25
+ "arch": "arm64",
26
+ "os": "linux",
27
+ "osKernelVersionsSupported": "*",
28
+ "url": "https://cdn.mysql.com//Downloads/MySQL-9.0/mysql-9.0.1-linux-glibc2.17-aarch64-minimal.tar.xz"
29
+ },
30
+ {
31
+ "version": "9.0.1",
32
+ "arch": "x64",
33
+ "os": "win32",
34
+ "osKernelVersionsSupported": "*",
35
+ "url": "https://cdn.mysql.com//Downloads/MySQL-9.0/mysql-9.0.1-winx64.zip"
36
+ },
37
+ {
38
+ "version": "8.4.2",
39
+ "arch": "arm64",
40
+ "os": "darwin",
41
+ "osKernelVersionsSupported": ">22",
42
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.4/mysql-8.4.2-macos14-arm64.tar.gz"
43
+ },
44
+ {
45
+ "version": "8.4.2",
46
+ "arch": "x64",
47
+ "os": "darwin",
48
+ "osKernelVersionsSupported": ">22",
49
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.4/mysql-8.4.2-macos14-x86_64.tar.gz"
50
+ },
51
+ {
52
+ "version": "8.4.2",
53
+ "arch": "x64",
54
+ "os": "linux",
55
+ "osKernelVersionsSupported": "*",
56
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.4/mysql-8.4.2-linux-glibc2.17-x86_64-minimal.tar.xz"
57
+ },
58
+ {
59
+ "version": "8.4.2",
60
+ "arch": "arm64",
61
+ "os": "linux",
62
+ "osKernelVersionsSupported": "*",
63
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.4/mysql-8.4.2-linux-glibc2.17-aarch64-minimal.tar.xz"
64
+ },
65
+ {
66
+ "version": "8.4.2",
67
+ "arch": "x64",
68
+ "os": "win32",
69
+ "osKernelVersionsSupported": "*",
70
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.4/mysql-8.4.2-winx64.zip"
71
+ },
72
+ {
73
+ "version": "8.0.39",
74
+ "arch": "arm64",
75
+ "os": "darwin",
76
+ "osKernelVersionsSupported": ">22",
77
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.39-macos14-arm64.tar.gz"
78
+ },
79
+ {
80
+ "version": "8.0.39",
81
+ "arch": "x64",
82
+ "os": "darwin",
83
+ "osKernelVersionsSupported": ">22",
84
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.39-macos14-x86_64.tar.gz"
85
+ },
86
+ {
87
+ "version": "8.0.39",
88
+ "arch": "x64",
89
+ "os": "linux",
90
+ "osKernelVersionsSupported": "*",
91
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.39-linux-glibc2.17-x86_64-minimal.tar.xz"
92
+ },
93
+ {
94
+ "version": "8.0.39",
95
+ "arch": "arm64",
96
+ "os": "linux",
97
+ "osKernelVersionsSupported": "*",
98
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.39-linux-glibc2.17-aarch64-minimal.tar.xz"
99
+ },
100
+ {
101
+ "version": "8.0.39",
102
+ "arch": "x64",
103
+ "os": "win32",
104
+ "osKernelVersionsSupported": "*",
105
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.39-winx64.zip"
106
+ },
107
+ {
108
+ "version": "8.1.0",
109
+ "arch": "arm64",
110
+ "os": "darwin",
111
+ "osKernelVersionsSupported": ">22",
112
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.1/mysql-8.1.0-macos13-arm64.tar.gz"
113
+ },
114
+ {
115
+ "version": "8.1.0",
116
+ "arch": "x64",
117
+ "os": "darwin",
118
+ "osKernelVersionsSupported": ">22",
119
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.1/mysql-8.1.0-macos13-x86_64.tar.gz"
120
+ },
121
+ {
122
+ "version": "8.1.0",
123
+ "arch": "x64",
124
+ "os": "linux",
125
+ "osKernelVersionsSupported": "*",
126
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.1/mysql-8.1.0-linux-glibc2.17-x86_64-minimal.tar.xz"
127
+ },
128
+ {
129
+ "version": "8.1.0",
130
+ "arch": "arm64",
131
+ "os": "linux",
132
+ "osKernelVersionsSupported": "*",
133
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.1/mysql-8.1.0-linux-glibc2.17-aarch64-minimal.tar.xz"
134
+ },
135
+ {
136
+ "version": "8.1.0",
137
+ "arch": "x64",
138
+ "os": "win32",
139
+ "osKernelVersionsSupported": "*",
140
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.1/mysql-8.1.0-winx64.zip"
141
+ },
142
+ {
143
+ "version": "8.2.0",
144
+ "arch": "arm64",
145
+ "os": "darwin",
146
+ "osKernelVersionsSupported": ">22",
147
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.2/mysql-8.2.0-macos13-arm64.tar.gz"
148
+ },
149
+ {
150
+ "version": "8.2.0",
151
+ "arch": "x64",
152
+ "os": "darwin",
153
+ "osKernelVersionsSupported": ">22",
154
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.2/mysql-8.2.0-macos13-x86_64.tar.gz"
155
+ },
156
+ {
157
+ "version": "8.2.0",
158
+ "arch": "x64",
159
+ "os": "linux",
160
+ "osKernelVersionsSupported": "*",
161
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.2/mysql-8.2.0-linux-glibc2.17-x86_64-minimal.tar.xz"
162
+ },
163
+ {
164
+ "version": "8.2.0",
165
+ "arch": "arm64",
166
+ "os": "linux",
167
+ "osKernelVersionsSupported": "*",
168
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.2/mysql-8.2.0-linux-glibc2.17-aarch64-minimal.tar.xz"
169
+ },
170
+ {
171
+ "version": "8.2.0",
172
+ "arch": "x64",
173
+ "os": "win32",
174
+ "osKernelVersionsSupported": "*",
175
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.2/mysql-8.2.0-winx64.zip"
176
+ },
177
+ {
178
+ "version": "8.3.0",
179
+ "arch": "arm64",
180
+ "os": "darwin",
181
+ "osKernelVersionsSupported": ">22",
182
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.3/mysql-8.3.0-macos14-arm64.tar.gz"
183
+ },
184
+ {
185
+ "version": "8.3.0",
186
+ "arch": "x64",
187
+ "os": "darwin",
188
+ "osKernelVersionsSupported": ">22",
189
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.3/mysql-8.3.0-macos14-x86_64.tar.gz"
190
+ },
191
+ {
192
+ "version": "8.3.0",
193
+ "arch": "x64",
194
+ "os": "linux",
195
+ "osKernelVersionsSupported": "*",
196
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.3/mysql-8.3.0-linux-glibc2.17-x86_64-minimal.tar.xz"
197
+ },
198
+ {
199
+ "version": "8.3.0",
200
+ "arch": "arm64",
201
+ "os": "linux",
202
+ "osKernelVersionsSupported": "*",
203
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.3/mysql-8.3.0-linux-glibc2.17-aarch64-minimal.tar.xz"
204
+ },
205
+ {
206
+ "version": "8.3.0",
207
+ "arch": "x64",
208
+ "os": "win32",
209
+ "osKernelVersionsSupported": "*",
210
+ "url": "https://cdn.mysql.com//Downloads/MySQL-8.3/mysql-8.3.0-winx64.zip"
211
+ }
212
+ ]
@@ -0,0 +1,49 @@
1
+ import { ExecException } from "child_process";
2
+ export type LOG_LEVEL = 'LOG' | 'WARN' | 'ERROR';
3
+ export type ServerOptions = {
4
+ version?: string;
5
+ dbName?: string;
6
+ logLevel?: LOG_LEVEL;
7
+ portRetries?: number;
8
+ downloadBinaryOnce?: boolean;
9
+ lockRetries?: number;
10
+ lockRetryWait?: number;
11
+ };
12
+ export type InternalServerOptions = {
13
+ version?: string;
14
+ dbName: string;
15
+ logLevel: LOG_LEVEL;
16
+ portRetries: number;
17
+ downloadBinaryOnce: boolean;
18
+ lockRetries: number;
19
+ lockRetryWait: number;
20
+ };
21
+ export type ExecutorOptions = {
22
+ logLevel: LOG_LEVEL;
23
+ };
24
+ export type ExecuteReturn = {
25
+ error: ExecException | null;
26
+ stdout: string;
27
+ stderr: string;
28
+ };
29
+ export type MySQLDB = {
30
+ port: number;
31
+ xPort: number;
32
+ dbName: string;
33
+ stop: () => Promise<void>;
34
+ };
35
+ export type MySQLVersion = {
36
+ version: string;
37
+ arch: string;
38
+ os: string;
39
+ osKernelVersionsSupported: string;
40
+ url: string;
41
+ };
42
+ export type InstalledMySQLVersion = {
43
+ version: string;
44
+ path: string;
45
+ };
46
+ export type BinaryInfo = {
47
+ url: string;
48
+ version: string;
49
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "mysql-memory-server",
3
+ "version": "1.0.0",
4
+ "description": "Spin up an ephemeral MySQL database from your JavaScript code",
5
+ "main": "dist/src/index.js",
6
+ "types": "dist/src/index.d.ts",
7
+ "files": [
8
+ "/dist"
9
+ ],
10
+ "keywords": ["MySQL", "ephemeral database", "continuous integration", "CI", "CI/CD", "memory database"],
11
+ "scripts": {
12
+ "test": "jest"
13
+ },
14
+ "author": "Sebastian-Webster",
15
+ "license": "MIT",
16
+ "devDependencies": {
17
+ "@babel/core": "^7.24.9",
18
+ "@babel/preset-env": "^7.24.8",
19
+ "@babel/preset-typescript": "^7.24.7",
20
+ "@types/adm-zip": "^0.5.5",
21
+ "@types/node": "^22.1.0",
22
+ "@types/proper-lockfile": "^4.1.4",
23
+ "@types/semver": "^7.5.8",
24
+ "babel-jest": "^29.7.0",
25
+ "jest": "^29.7.0",
26
+ "mysql2": "^3.10.3",
27
+ "typescript": "^5.5.4"
28
+ },
29
+ "dependencies": {
30
+ "adm-zip": "^0.5.14",
31
+ "proper-lockfile": "^4.1.2",
32
+ "semver": "^7.6.3"
33
+ }
34
+ }