mysql-memory-server 1.11.0 → 1.12.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
@@ -15,9 +15,9 @@ Download with your package manager of choice. The package name is `mysql-memory-
15
15
  #### Currently supported MySQL versions
16
16
 
17
17
  - ```mysql-memory-server``` can run MySQL versions 5.7.19 and newer (if the version is already installed on your system)
18
- - ```mysql-memory-server``` can download and run MySQL versions 5.7.19 - 5.7.44, 8.0.0 - 8.0.43, 8.1.0 - 8.4.6, and 9.0.1 - 9.4.0 (if the version is not already installed on your system - all version numbers are inclusive)
18
+ - ```mysql-memory-server``` can download and run MySQL versions (inclusive) 5.7.19 - 5.7.44, 8.0.0 - 8.0.43, 8.1.0 - 8.4.6, and 9.0.1 - 9.4.0 (if the version is not already installed on your system)
19
19
 
20
- Extra system requirements apply when ```mysql-memory-server``` is downloading MySQL instead of running a version of MySQL that is already installed on the system. To see the requirements and learn more about the supported versions for download, [read the doc](https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/blob/v1.11.0/docs/SUPPORTED_MYSQL_DOWNLOADS.md)
20
+ Extra system requirements apply when ```mysql-memory-server``` is downloading MySQL instead of running a version of MySQL that is already installed on the system. To see the requirements and learn more about the supported versions for download, [read the doc](https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/blob/v1.12.1/docs/SUPPORTED_MYSQL_DOWNLOADS.md)
21
21
 
22
22
  ## Example Usage - Application Code
23
23
 
@@ -78,7 +78,7 @@ npx mysql-memory-server@latest --version 8.4.x
78
78
  - `port: number`
79
79
  The port that the MySQL database is listening on
80
80
  - `xPort: number`
81
- The port that MySQLX is listening on
81
+ The port that MySQLX is listening on. If ```options.xEnabled``` is set to "OFF", this value will be -1.
82
82
  - `dbName: string`
83
83
  The database that was created on database initialization
84
84
  - `username: string`
@@ -86,7 +86,7 @@ The name of the user to use to login to the database
86
86
  - `socket: string`
87
87
  If on Windows, this is the name of the named pipe that MySQL is listening on. If not on Windows, this is the path to the socket that MySQL is listening on.
88
88
  - `xSocket: string`
89
- If on Windows, this is the name of the named pipe that the MySQL X Plugin is listening on. If not on Windows, this is the path that the MySQL X Plugin is listening on.
89
+ If on Windows, this is the name of the named pipe that the MySQL X Plugin is listening on. If not on Windows, this is the path that the MySQL X Plugin is listening on. If ```options.xEnabled``` is set to "OFF", this value will be an empty string.
90
90
  - `mysql: {version: string, versionIsInstalledOnSystem: boolean}`
91
91
  An object with two properties. ```version``` is the version of MySQL used to create the database. ```versionIsInstalledOnSystem``` will be true if the MySQL version used is already installed on the system and false if the version had to be downloaded from MySQL's CDN.
92
92
  - `stop: () => Promise<void>`
@@ -102,11 +102,11 @@ Description: Version of MySQL to use for the database. Uses semver for getting t
102
102
 
103
103
  If left undefined:
104
104
  - If the system has MySQL installed, the system-installed version will be used. If the installed version is not supported by this package (currently <5.7.19), an error will be thrown unless `ignoreUnsupportedSystemVersion` is set to `true`.
105
- - If the system does not have MySQL installed, the latest version of MySQL that [this package supports downloading](https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/blob/v1.11.0/docs/SUPPORTED_MYSQL_DOWNLOADS.md) will be downloaded.
105
+ - If the system does not have MySQL installed, the latest version of MySQL that [this package supports downloading](https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/blob/v1.12.1/docs/SUPPORTED_MYSQL_DOWNLOADS.md) will be downloaded.
106
106
 
107
107
  If defined:
108
108
  - If the version is older than 5.7.19, an error will be thrown as this package does not currently support those versions of MySQL.
109
- - If the desired version of MySQL is already installed on the system, the installed version will be used. Otherwise the selected version will be downloaded as long as this package [supports downloading it](https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/blob/v1.11.0/docs/SUPPORTED_MYSQL_DOWNLOADS.md). If this package does not support downloading the selected version, or your system does not [meet the requirements](https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/blob/v1.11.0/docs/SUPPORTED_MYSQL_DOWNLOADS.md) for that version to be ran, an error will be thrown.
109
+ - If the desired version of MySQL is already installed on the system, the installed version will be used. Otherwise the selected version will be downloaded as long as this package [supports downloading it](https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/blob/v1.12.1/docs/SUPPORTED_MYSQL_DOWNLOADS.md). If this package does not support downloading the selected version, or your system does not [meet the requirements](https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/blob/v1.12.1/docs/SUPPORTED_MYSQL_DOWNLOADS.md) for that version to be ran, an error will be thrown.
110
110
 
111
111
  - `dbName: string`
112
112
 
@@ -189,3 +189,9 @@ The internal queries that are ran before the queries in ```initSQLString``` are
189
189
  Default: process.arch
190
190
 
191
191
  Description: The MySQL binary architecture to execute. MySQL does not offer server builds for Windows on ARM, so to get this package working on Windows on ARM, set the arch option to "x64" and Windows will emulate MySQL.
192
+
193
+ - `xEnabled: "OFF" | "FORCE"`
194
+
195
+ Default: "FORCE"
196
+
197
+ Description: This option follows the convention set out by the [MySQL Documentation](https://dev.mysql.com/doc/refman/en/plugin-loading.html). If set to "OFF", the MySQL X Plugin will not initialise. If set to "FORCE", the MySQL Server will either start up with the MySQL X Plugin guaranteed to have successfully initialised, or if initialisation fails, the server will fail to start up.
@@ -8,15 +8,13 @@ export declare const LOG_LEVELS: {
8
8
  };
9
9
  declare const internalOptions: {
10
10
  deleteDBAfterStopped: string;
11
- databaseDirectoryPath: string;
12
- binaryDirectoryPath: string;
13
11
  cli: string;
14
12
  };
15
13
  export declare function getInternalEnvVariable(envVar: keyof typeof internalOptions): string;
16
14
  export declare const OPTION_TYPE_CHECKS: OptionTypeChecks;
17
15
  export declare const MIN_SUPPORTED_MYSQL = "5.7.19";
18
- export declare const downloadsBaseURL = "https://cdn.mysql.com//Downloads/MySQL-";
19
- export declare const archiveBaseURL = "https://cdn.mysql.com/archives/mysql-";
16
+ export declare const MySQLCDNDownloadsBaseURL = "https://cdn.mysql.com//Downloads/MySQL-";
17
+ export declare const MySQLCDNArchivesBaseURL = "https://cdn.mysql.com/archives/mysql-";
20
18
  export declare const DOWNLOADABLE_MYSQL_VERSIONS: readonly ["5.7.19", "5.7.20", "5.7.21", "5.7.22", "5.7.23", "5.7.24", "5.7.25", "5.7.26", "5.7.27", "5.7.28", "5.7.29", "5.7.30", "5.7.31", "5.7.32", "5.7.33", "5.7.34", "5.7.35", "5.7.36", "5.7.37", "5.7.38", "5.7.39", "5.7.40", "5.7.41", "5.7.42", "5.7.43", "5.7.44", "8.0.0", "8.0.1", "8.0.2", "8.0.3", "8.0.4", "8.0.11", "8.0.12", "8.0.13", "8.0.14", "8.0.15", "8.0.16", "8.0.17", "8.0.18", "8.0.19", "8.0.20", "8.0.21", "8.0.22", "8.0.23", "8.0.24", "8.0.25", "8.0.26", "8.0.27", "8.0.28", "8.0.30", "8.0.31", "8.0.32", "8.0.33", "8.0.34", "8.0.35", "8.0.36", "8.0.37", "8.0.39", "8.0.40", "8.0.41", "8.0.42", "8.0.43", "8.1.0", "8.2.0", "8.3.0", "8.4.0", "8.4.2", "8.4.3", "8.4.4", "8.4.5", "8.4.6", "9.0.1", "9.1.0", "9.2.0", "9.3.0", "9.4.0"];
21
19
  export declare const MYSQL_ARCH_SUPPORT: {
22
20
  readonly darwin: {
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MYSQL_LINUX_MINIMAL_REBUILD_VERSIONS = exports.MYSQL_LINUX_FILE_EXTENSIONS = exports.MYSQL_LINUX_MINIMAL_INSTALL_AVAILABLE_ARM64 = exports.MYSQL_LINUX_MINIMAL_INSTALL_AVAILABLE = exports.MYSQL_LINUX_GLIBC_VERSIONS = exports.MYSQL_MACOS_VERSIONS_IN_FILENAME = exports.RC_MYSQL_VERSIONS = exports.DMR_MYSQL_VERSIONS = exports.MYSQL_MIN_OS_SUPPORT = exports.MYSQL_ARCH_SUPPORT = exports.DOWNLOADABLE_MYSQL_VERSIONS = exports.archiveBaseURL = exports.downloadsBaseURL = exports.MIN_SUPPORTED_MYSQL = exports.OPTION_TYPE_CHECKS = exports.LOG_LEVELS = exports.DEFAULT_OPTIONS_KEYS = exports.DEFAULT_OPTIONS = void 0;
3
+ exports.MYSQL_LINUX_MINIMAL_REBUILD_VERSIONS = exports.MYSQL_LINUX_FILE_EXTENSIONS = exports.MYSQL_LINUX_MINIMAL_INSTALL_AVAILABLE_ARM64 = exports.MYSQL_LINUX_MINIMAL_INSTALL_AVAILABLE = exports.MYSQL_LINUX_GLIBC_VERSIONS = exports.MYSQL_MACOS_VERSIONS_IN_FILENAME = exports.RC_MYSQL_VERSIONS = exports.DMR_MYSQL_VERSIONS = exports.MYSQL_MIN_OS_SUPPORT = exports.MYSQL_ARCH_SUPPORT = exports.DOWNLOADABLE_MYSQL_VERSIONS = exports.MySQLCDNArchivesBaseURL = exports.MySQLCDNDownloadsBaseURL = exports.MIN_SUPPORTED_MYSQL = exports.OPTION_TYPE_CHECKS = exports.LOG_LEVELS = exports.DEFAULT_OPTIONS_KEYS = exports.DEFAULT_OPTIONS = void 0;
4
4
  exports.getInternalEnvVariable = getInternalEnvVariable;
5
- const path_1 = require("path");
6
- const os_1 = require("os");
7
5
  const semver_1 = require("semver");
8
6
  exports.DEFAULT_OPTIONS = {
9
7
  version: undefined,
@@ -19,7 +17,8 @@ exports.DEFAULT_OPTIONS = {
19
17
  xPort: 0,
20
18
  downloadRetries: 10,
21
19
  initSQLString: '',
22
- arch: process.arch
20
+ arch: process.arch,
21
+ xEnabled: 'FORCE'
23
22
  };
24
23
  exports.DEFAULT_OPTIONS_KEYS = Object.freeze(Object.keys(exports.DEFAULT_OPTIONS));
25
24
  exports.LOG_LEVELS = {
@@ -29,15 +28,13 @@ exports.LOG_LEVELS = {
29
28
  };
30
29
  const internalOptions = {
31
30
  deleteDBAfterStopped: 'true',
32
- //mysqlmsn = MySQL Memory Server Node.js
33
- databaseDirectoryPath: (0, path_1.normalize)(`${(0, os_1.tmpdir)()}/mysqlmsn/dbs`),
34
- binaryDirectoryPath: `${(0, os_1.tmpdir)()}/mysqlmsn/binaries`,
35
31
  cli: 'false'
36
32
  };
37
33
  function getInternalEnvVariable(envVar) {
38
34
  return process.env['mysqlmsn_internal_DO_NOT_USE_' + envVar] || internalOptions[envVar];
39
35
  }
40
36
  const allowedArches = ['x64', 'arm64'];
37
+ const pluginActivationStates = ['OFF', 'FORCE'];
41
38
  exports.OPTION_TYPE_CHECKS = {
42
39
  version: {
43
40
  check: (opt) => opt === undefined || typeof opt === 'string' && (0, semver_1.valid)((0, semver_1.coerce)(opt)) !== null,
@@ -108,11 +105,16 @@ exports.OPTION_TYPE_CHECKS = {
108
105
  check: (opt) => opt === undefined || allowedArches.includes(opt),
109
106
  errorMessage: `Option arch must be either of the following: ${allowedArches.join(', ')}`,
110
107
  definedType: 'string'
108
+ },
109
+ xEnabled: {
110
+ check: (opt) => opt === undefined || pluginActivationStates.includes(opt),
111
+ errorMessage: `xEnabled must be either undefined or one of the following: ${pluginActivationStates.join(', ')}`,
112
+ definedType: 'boolean'
111
113
  }
112
114
  };
113
115
  exports.MIN_SUPPORTED_MYSQL = '5.7.19';
114
- exports.downloadsBaseURL = 'https://cdn.mysql.com//Downloads/MySQL-';
115
- exports.archiveBaseURL = 'https://cdn.mysql.com/archives/mysql-';
116
+ exports.MySQLCDNDownloadsBaseURL = 'https://cdn.mysql.com//Downloads/MySQL-';
117
+ exports.MySQLCDNArchivesBaseURL = 'https://cdn.mysql.com/archives/mysql-';
116
118
  // Versions 8.0.29, 8.0.38, 8.4.1, and 9.0.0 have been purposefully left out of this list as MySQL has removed them from the CDN due to critical issues.
117
119
  exports.DOWNLOADABLE_MYSQL_VERSIONS = [
118
120
  '5.7.19', '5.7.20', '5.7.21', '5.7.22', '5.7.23', '5.7.24', '5.7.25', '5.7.26', '5.7.27', '5.7.28', '5.7.29', '5.7.30', '5.7.31', '5.7.32', '5.7.33', '5.7.34', '5.7.35', '5.7.36', '5.7.37', '5.7.38', '5.7.39', '5.7.40', '5.7.41', '5.7.42', '5.7.43', '5.7.44',
package/dist/src/index.js CHANGED
@@ -10,6 +10,7 @@ const semver_1 = require("semver");
10
10
  const Version_1 = __importDefault(require("./libraries/Version"));
11
11
  const Downloader_1 = require("./libraries/Downloader");
12
12
  const constants_1 = require("./constants");
13
+ const LinuxOSRelease_1 = __importDefault(require("./libraries/LinuxOSRelease"));
13
14
  async function createDB(opts) {
14
15
  const suppliedOpts = opts || {};
15
16
  const suppliedOptsKeys = Object.keys(suppliedOpts);
@@ -34,7 +35,7 @@ async function createDB(opts) {
34
35
  if (throwUnsupportedError) {
35
36
  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.`;
36
37
  }
37
- logger.log('Version currently installed:', version);
38
+ logger.log('Version currently installed:', version, 'Platform:', process.platform, 'etcOSRelease:', LinuxOSRelease_1.default);
38
39
  if (version === null || (options.version && !(0, semver_1.satisfies)(version.version, options.version)) || unsupportedMySQLIsInstalled) {
39
40
  let binaryInfo;
40
41
  let binaryFilepath;
@@ -47,7 +48,7 @@ async function createDB(opts) {
47
48
  throw `Failed to download binary. The error was: "${error}"`;
48
49
  }
49
50
  logger.log('Running downloaded binary');
50
- return await executor.startMySQL(options, { path: binaryFilepath, version: binaryInfo.version, installedOnSystem: false });
51
+ return await executor.startMySQL(options, { path: binaryFilepath, version: binaryInfo.version, installedOnSystem: false, xPluginSupported: binaryInfo.xPluginSupported });
51
52
  }
52
53
  else {
53
54
  logger.log(version);
@@ -46,6 +46,7 @@ const crypto_1 = require("crypto");
46
46
  const child_process_1 = require("child_process");
47
47
  const FileLock_1 = require("./FileLock");
48
48
  const constants_1 = require("../constants");
49
+ const os_1 = __importDefault(require("os"));
49
50
  function handleTarExtraction(filepath, extractedPath) {
50
51
  return new Promise((resolve, reject) => {
51
52
  (0, child_process_1.execFile)(`tar`, ['-xf', filepath, '-C', extractedPath], (error, stdout, stderr) => {
@@ -56,6 +57,36 @@ function handleTarExtraction(filepath, extractedPath) {
56
57
  });
57
58
  });
58
59
  }
60
+ function getFileDownloadURLRedirect(url) {
61
+ const options = {
62
+ headers: {
63
+ 'accept': '*/*',
64
+ 'connection': 'keep-alive'
65
+ }
66
+ };
67
+ return new Promise((resolve, reject) => {
68
+ const request = https.get(url, options, response => {
69
+ const statusCode = response.statusCode;
70
+ const location = response.headers.location;
71
+ if (statusCode !== 302) {
72
+ request.destroy();
73
+ reject(`Received status code ${statusCode} while getting redirect URL for binary download. Used URL: ${url}`);
74
+ return;
75
+ }
76
+ if (typeof location === 'string' && location.length > 0) {
77
+ request.destroy();
78
+ resolve(location);
79
+ return;
80
+ }
81
+ request.destroy();
82
+ reject(`Received incorrect URL information. Expected a non-empty string. Received: ${JSON.stringify(location)}`);
83
+ });
84
+ request.on('error', (err) => {
85
+ request.destroy();
86
+ reject(err.message);
87
+ });
88
+ });
89
+ }
59
90
  function downloadFromCDN(url, downloadLocation, logger) {
60
91
  return new Promise(async (resolve, reject) => {
61
92
  if (fs.existsSync(downloadLocation)) {
@@ -139,7 +170,7 @@ function promisifiedZipExtraction(archiveLocation, extractedLocation) {
139
170
  });
140
171
  });
141
172
  }
142
- function extractBinary(url, archiveLocation, extractedLocation, logger) {
173
+ function extractBinary(url, archiveLocation, extractedLocation, binaryInfo, logger) {
143
174
  return new Promise(async (resolve, reject) => {
144
175
  if (fs.existsSync(extractedLocation)) {
145
176
  logger.warn('Removing item at extractedLocation:', extractedLocation, 'so the MySQL binary can be stored there. This is probably because a previous download/extraction failed.');
@@ -148,12 +179,18 @@ function extractBinary(url, archiveLocation, extractedLocation, logger) {
148
179
  const lastDashIndex = url.lastIndexOf('-');
149
180
  const fileExtension = url.slice(lastDashIndex).split('.').splice(1).join('.');
150
181
  await fsPromises.mkdir(extractedLocation, { recursive: true });
151
- const splitURL = url.split('/');
152
- const mySQLFolderName = splitURL[splitURL.length - 1];
153
- if (!mySQLFolderName) {
154
- return reject(`Folder name is undefined for url: ${url}`);
182
+ let folderName = '';
183
+ if (binaryInfo.hostedByOracle) {
184
+ const splitURL = url.split('/');
185
+ const mySQLFolderName = splitURL[splitURL.length - 1];
186
+ if (!mySQLFolderName) {
187
+ return reject(`Folder name is undefined for url: ${url}`);
188
+ }
189
+ folderName = mySQLFolderName.replace(`.${fileExtension}`, '');
190
+ }
191
+ else {
192
+ folderName = `mysql-${binaryInfo.version}`;
155
193
  }
156
- const folderName = mySQLFolderName.replace(`.${fileExtension}`, '');
157
194
  let extractionError = undefined;
158
195
  if (fileExtension === 'zip') {
159
196
  //Only Windows MySQL files use the .zip extension
@@ -217,7 +254,7 @@ function extractBinary(url, archiveLocation, extractedLocation, logger) {
217
254
  function downloadBinary(binaryInfo, options, logger) {
218
255
  return new Promise(async (resolve, reject) => {
219
256
  const { url, version } = binaryInfo;
220
- const dirpath = (0, constants_1.getInternalEnvVariable)('binaryDirectoryPath');
257
+ const dirpath = `${os_1.default.tmpdir()}/mysqlmsn/binaries`;
221
258
  logger.log('Binary path:', dirpath);
222
259
  await fsPromises.mkdir(dirpath, { recursive: true });
223
260
  const lastDashIndex = url.lastIndexOf('-');
@@ -255,13 +292,13 @@ function downloadBinary(binaryInfo, options, logger) {
255
292
  let downloadTries = 0;
256
293
  let useDownloadsURL = false;
257
294
  do {
295
+ const downloadURL = binaryInfo.hostedByOracle ? `${useDownloadsURL ? constants_1.MySQLCDNDownloadsBaseURL : constants_1.MySQLCDNArchivesBaseURL}${url}` : await getFileDownloadURLRedirect(url);
258
296
  try {
259
297
  downloadTries++;
260
- const downloadURL = useDownloadsURL ? url.replace(constants_1.archiveBaseURL, constants_1.downloadsBaseURL) : url;
261
298
  logger.log(`Starting download for MySQL version ${version} from ${downloadURL}.`);
262
299
  await downloadFromCDN(downloadURL, archivePath, logger);
263
300
  logger.log(`Finished downloading MySQL version ${version} from ${downloadURL}. Now starting binary extraction.`);
264
- await extractBinary(downloadURL, archivePath, extractedPath, logger);
301
+ await extractBinary(downloadURL, archivePath, extractedPath, binaryInfo, logger);
265
302
  logger.log(`Finished extraction for version ${version}`);
266
303
  break;
267
304
  }
@@ -276,12 +313,12 @@ function downloadBinary(binaryInfo, options, logger) {
276
313
  catch (e) {
277
314
  logger.error('An error occurred while deleting extractedPath and/or archivePath:', e);
278
315
  }
279
- if (e?.includes?.('status code 404')) {
280
- if (!useDownloadsURL) {
281
- //Retry with downloads URL
282
- downloadTries--;
316
+ // If we got a 404 error while downloading a binary from Oracle and have not retried with the Downloads URL yet
317
+ // then retry with the Downloads URL. Otherwise, reject.
318
+ if (e?.includes('status code 404')) {
319
+ if (binaryInfo.hostedByOracle && useDownloadsURL === false) {
283
320
  useDownloadsURL = true;
284
- logger.log(`Encountered error 404 when using archives URL for version ${version}. Now retrying with the downloads URL.`);
321
+ downloadTries--;
285
322
  continue;
286
323
  }
287
324
  else {
@@ -289,9 +326,11 @@ function downloadBinary(binaryInfo, options, logger) {
289
326
  await releaseFunction();
290
327
  }
291
328
  catch (e) {
292
- logger.error('An error occurred while releasing lock after receiving a 404 error on both downloads and archives URLs. The error was:', e);
329
+ logger.error('An error occurred while releasing lock after receiving a 404 error on download. The error was:', e);
330
+ }
331
+ finally {
332
+ return reject(`Binary download for MySQL version ${binaryInfo.version} returned status code 404 at URL ${downloadURL}. Aborting download.`);
293
333
  }
294
- return reject(`Both URLs for MySQL version ${binaryInfo.version} returned status code 404. Aborting download.`);
295
334
  }
296
335
  }
297
336
  if (downloadTries > options.downloadRetries) {
@@ -322,17 +361,17 @@ function downloadBinary(binaryInfo, options, logger) {
322
361
  let downloadTries = 0;
323
362
  let useDownloadsURL = false;
324
363
  do {
364
+ const downloadURL = binaryInfo.hostedByOracle ? `${useDownloadsURL ? constants_1.MySQLCDNDownloadsBaseURL : constants_1.MySQLCDNArchivesBaseURL}${url}` : await getFileDownloadURLRedirect(url);
325
365
  const uuid = (0, crypto_1.randomUUID)();
326
366
  const zipFilepath = `${dirpath}/${uuid}.${fileExtension}`;
327
367
  logger.log('Binary filepath:', zipFilepath);
328
368
  const extractedPath = `${dirpath}/${uuid}`;
329
369
  try {
330
370
  downloadTries++;
331
- const downloadURL = useDownloadsURL ? url.replace(constants_1.archiveBaseURL, constants_1.downloadsBaseURL) : url;
332
371
  logger.log(`Starting download for MySQL version ${version} from ${downloadURL}.`);
333
372
  await downloadFromCDN(downloadURL, zipFilepath, logger);
334
373
  logger.log(`Finished downloading MySQL version ${version} from ${downloadURL}. Now starting binary extraction.`);
335
- const binaryPath = await extractBinary(downloadURL, zipFilepath, extractedPath, logger);
374
+ const binaryPath = await extractBinary(downloadURL, zipFilepath, extractedPath, binaryInfo, logger);
336
375
  logger.log(`Finished extraction for version ${version}`);
337
376
  return resolve(binaryPath);
338
377
  }
@@ -347,16 +386,16 @@ function downloadBinary(binaryInfo, options, logger) {
347
386
  catch (e) {
348
387
  logger.error('An error occurred while deleting extractedPath and/or archivePath:', e);
349
388
  }
350
- if (e?.includes?.('status code 404')) {
351
- if (!useDownloadsURL) {
352
- //Retry with downloads URL
353
- downloadTries--;
389
+ // If we got a 404 error while downloading a binary from Oracle and have not retried with the Downloads URL yet
390
+ // then retry with the Downloads URL. Otherwise, reject.
391
+ if (e?.includes('status code 404')) {
392
+ if (binaryInfo.hostedByOracle && useDownloadsURL === false) {
354
393
  useDownloadsURL = true;
355
- logger.log(`Encountered error 404 when using archives URL for version ${version}. Now retrying with the downloads URL.`);
394
+ downloadTries--;
356
395
  continue;
357
396
  }
358
397
  else {
359
- return reject(`Both URLs for MySQL version ${binaryInfo.version} returned status code 404. Aborting download.`);
398
+ return reject(`Binary download for MySQL version ${binaryInfo.version} returned status code 404 at URL ${downloadURL}. Aborting download.`);
360
399
  }
361
400
  }
362
401
  if (downloadTries > options.downloadRetries) {
@@ -7,6 +7,7 @@ declare class Executor {
7
7
  removeExitHandler: () => void;
8
8
  version: string;
9
9
  versionInstalledOnSystem: boolean;
10
+ versionSupportsMySQLX: boolean;
10
11
  databasePath: string;
11
12
  killedFromPortIssue: boolean;
12
13
  constructor(logger: Logger);
@@ -37,9 +37,6 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
37
37
  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");
38
38
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
39
39
  };
40
- var __importDefault = (this && this.__importDefault) || function (mod) {
41
- return (mod && mod.__esModule) ? mod : { "default": mod };
42
- };
43
40
  var _Executor_instances, _Executor_executeFile, _Executor_killProcess, _Executor_returnBinaryPathToDelete, _Executor_startMySQLProcess, _Executor_setupDataDirectories;
44
41
  Object.defineProperty(exports, "__esModule", { value: true });
45
42
  const child_process_1 = require("child_process");
@@ -53,7 +50,7 @@ const FileLock_1 = require("./FileLock");
53
50
  const signal_exit_1 = require("signal-exit");
54
51
  const crypto_1 = require("crypto");
55
52
  const constants_1 = require("../constants");
56
- const LinuxOSRelease_1 = __importDefault(require("./LinuxOSRelease"));
53
+ const LinuxOSRelease_1 = __importStar(require("./LinuxOSRelease"));
57
54
  class Executor {
58
55
  constructor(logger) {
59
56
  _Executor_instances.add(this);
@@ -94,7 +91,7 @@ class Executor {
94
91
  return reject('Could not get MySQL version');
95
92
  }
96
93
  else {
97
- versions.push({ version: version.version, path, installedOnSystem: true });
94
+ versions.push({ version: version.version, path, installedOnSystem: true, xPluginSupported: (0, semver_1.gte)(version, '5.7.19') });
98
95
  }
99
96
  }
100
97
  if (preferredVersion) {
@@ -124,15 +121,22 @@ class Executor {
124
121
  reject('Could not get installed MySQL version');
125
122
  }
126
123
  else {
127
- resolve({ version: version.version, path: 'mysqld', installedOnSystem: true });
124
+ resolve({ version: version.version, path: 'mysqld', installedOnSystem: true, xPluginSupported: (0, semver_1.gte)(version, '5.7.19') });
128
125
  }
129
126
  }
130
127
  }
131
128
  });
132
129
  }
133
130
  async startMySQL(options, installedMySQLBinary) {
131
+ if (installedMySQLBinary.xPluginSupported === false && options.xEnabled === 'FORCE') {
132
+ if (LinuxOSRelease_1.isOnAlpineLinux) {
133
+ throw "options.xEnabled is set to 'FORCE'. You are running this package on Alpine Linux. The MySQL binaries for Alpine Linux do not support the MySQL X Plugin. If you don't want to use the MySQL X Plugin, please set options.xEnabled to 'OFF' and that will remove this error message. If you must have MySQL X enabled for each database creation, please use a different OS to run this package.";
134
+ }
135
+ throw "options.xEnabled is set to 'FORCE'. The version of MySQL you are using does not support MySQL X. If you don't want to use the MySQL X Plugin, please set options.xEnabled to 'OFF'. Otherwise, if MySQL X is required, please use a different version of MySQL that supports the X plugin.";
136
+ }
134
137
  this.version = installedMySQLBinary.version;
135
138
  this.versionInstalledOnSystem = installedMySQLBinary.installedOnSystem;
139
+ this.versionSupportsMySQLX = installedMySQLBinary.xPluginSupported;
136
140
  this.removeExitHandler = (0, signal_exit_1.onExit)(() => {
137
141
  if ((0, constants_1.getInternalEnvVariable)('cli') === 'true') {
138
142
  console.log('\nShutting down the ephemeral MySQL database and cleaning all related files...');
@@ -160,17 +164,14 @@ class Executor {
160
164
  }
161
165
  });
162
166
  let retries = 0;
163
- this.databasePath = (0, path_1.normalize)(`${(0, constants_1.getInternalEnvVariable)('databaseDirectoryPath')}/${(0, crypto_1.randomUUID)().replaceAll("-", '')}`);
167
+ this.databasePath = (0, path_1.normalize)(`${os.tmpdir()}/mysqlmsn/dbs/${(0, crypto_1.randomUUID)().replaceAll("-", '')}`);
164
168
  const datadir = (0, path_1.normalize)(`${this.databasePath}/data`);
165
169
  do {
166
170
  await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, installedMySQLBinary, datadir, true);
167
171
  this.logger.log('Setting up directories was successful');
168
- const port = options.port || (0, Port_1.GenerateRandomPort)();
169
- const mySQLXPort = options.xPort || (0, Port_1.GenerateRandomPort)();
170
- this.logger.log('Using port:', port, 'and MySQLX port:', mySQLXPort, 'on retry:', retries);
171
172
  try {
172
173
  this.logger.log('Starting MySQL process');
173
- const resolved = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_startMySQLProcess).call(this, options, port, mySQLXPort, datadir, this.databasePath, installedMySQLBinary.path);
174
+ const resolved = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_startMySQLProcess).call(this, options, datadir, this.databasePath, installedMySQLBinary.path);
174
175
  this.logger.log('Starting process was successful');
175
176
  return resolved;
176
177
  }
@@ -182,7 +183,7 @@ class Executor {
182
183
  }
183
184
  retries++;
184
185
  if (retries <= options.portRetries) {
185
- this.logger.warn(`One or both of these ports are already in use: ${port} or ${mySQLXPort}. Now retrying... ${retries}/${options.portRetries} possible retries.`);
186
+ this.logger.warn(`Tried a port that is already in use. Now retrying... ${retries}/${options.portRetries} possible retries.`);
186
187
  }
187
188
  else {
188
189
  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.`;
@@ -198,20 +199,25 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
198
199
  });
199
200
  });
200
201
  }, _Executor_killProcess = async function _Executor_killProcess(process) {
201
- let killed = false;
202
+ // If the process has already been killed, return true
203
+ if (process.kill(0) === false) {
204
+ this.logger.warn('Called #killProcess to kill mysqld but it has already been killed.');
205
+ return true;
206
+ }
202
207
  if (os.platform() === 'win32') {
203
208
  const { error, stderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, 'taskkill', ['/pid', String(process.pid), '/t', '/f']);
204
- if (!error && !stderr) {
205
- killed = true;
209
+ const message = error || stderr;
210
+ if (!message) {
211
+ return true;
206
212
  }
207
- else {
208
- this.logger.error(error || stderr);
213
+ if (message.toString().includes('There is no running instance of the task')) {
214
+ this.logger.warn('Called #killProcess and tried to kill mysqld process but taskkill could not because it is not running. Error received:', message);
215
+ return true;
209
216
  }
217
+ this.logger.error(message, '| Error toString:', message.toString());
218
+ return false;
210
219
  }
211
- else {
212
- killed = process.kill();
213
- }
214
- return killed;
220
+ return process.kill('SIGKILL');
215
221
  }, _Executor_returnBinaryPathToDelete = function _Executor_returnBinaryPathToDelete(binaryFilepath, options) {
216
222
  if (binaryFilepath.includes(os.tmpdir()) && !options.downloadBinaryOnce) {
217
223
  const splitPath = binaryFilepath.split(os.platform() === 'win32' ? '\\' : '/');
@@ -221,21 +227,20 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
221
227
  return splitPath.join('/');
222
228
  }
223
229
  return null;
224
- }, _Executor_startMySQLProcess = function _Executor_startMySQLProcess(options, port, mySQLXPort, datadir, dbPath, binaryFilepath) {
230
+ }, _Executor_startMySQLProcess = function _Executor_startMySQLProcess(options, datadir, dbPath, binaryFilepath) {
225
231
  const errors = [];
226
232
  const logFile = `${dbPath}/log.log`;
227
233
  const errorLogFile = `${datadir}/errorlog.err`;
234
+ const port = options.port || (0, Port_1.GenerateRandomPort)();
235
+ const mySQLXPort = options.xPort || (0, Port_1.GenerateRandomPort)();
228
236
  return new Promise(async (resolve, reject) => {
229
237
  await fsPromises.rm(logFile, { force: true });
230
238
  const socket = os.platform() === 'win32' ? `MySQL-${(0, crypto_1.randomUUID)()}` : `${dbPath}/m.sock`;
231
239
  const xSocket = os.platform() === 'win32' ? `MySQLX-${(0, crypto_1.randomUUID)()}` : `${dbPath}/x.sock`;
232
240
  const mysqlArguments = [
233
241
  '--no-defaults',
234
- '--mysqlx=FORCE',
235
242
  `--port=${port}`,
236
243
  `--datadir=${datadir}`,
237
- `--mysqlx-port=${mySQLXPort}`,
238
- `--mysqlx-socket=${xSocket}`,
239
244
  `--socket=${socket}`,
240
245
  `--general-log-file=${logFile}`,
241
246
  '--general-log=1',
@@ -245,24 +250,31 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
245
250
  `--log-error=${errorLogFile}`,
246
251
  `--user=${os.userInfo().username}`
247
252
  ];
248
- //<8.0.11 does not have MySQL X turned on by default so we will be installing the X Plugin in this if statement.
249
- //MySQL 5.7.12 introduced the X plugin, but according to https://dev.mysql.com/doc/refman/5.7/en/document-store-setting-up.html, the database needs to be initialised with version 5.7.19.
250
- //If the MySQL version is >=5.7.19 & <8.0.11 then install the X Plugin
251
- if ((0, semver_1.lt)(this.version, '8.0.11') && (0, semver_1.gte)(this.version, '5.7.19')) {
252
- const pluginExtension = os.platform() === 'win32' ? 'dll' : 'so';
253
- let pluginPath;
254
- const firstPath = (0, path_1.resolve)(`${binaryFilepath}/../../lib/plugin`);
255
- const secondPath = '/usr/lib/mysql/plugin';
256
- if (fs.existsSync(`${firstPath}/mysqlx.${pluginExtension}`)) {
257
- pluginPath = firstPath;
258
- }
259
- else if (os.platform() === 'linux' && fs.existsSync(`${secondPath}/mysqlx.so`)) {
260
- pluginPath = secondPath;
261
- }
262
- else {
263
- throw 'Could not install MySQL X as the path to the plugin cannot be found.';
253
+ if (this.versionSupportsMySQLX) {
254
+ mysqlArguments.push(`--mysqlx=${options.xEnabled}`);
255
+ }
256
+ if (options.xEnabled !== 'OFF') {
257
+ mysqlArguments.push(`--mysqlx-port=${mySQLXPort}`);
258
+ mysqlArguments.push(`--mysqlx-socket=${xSocket}`);
259
+ //<8.0.11 does not have MySQL X turned on by default so we will be installing the X Plugin in this if statement.
260
+ //MySQL 5.7.12 introduced the X plugin, but according to https://dev.mysql.com/doc/refman/5.7/en/document-store-setting-up.html, the database needs to be initialised with version 5.7.19.
261
+ //If the MySQL version is >=5.7.19 & <8.0.11 then install the X Plugin
262
+ if ((0, semver_1.lt)(this.version, '8.0.11') && (0, semver_1.gte)(this.version, '5.7.19')) {
263
+ const pluginExtension = os.platform() === 'win32' ? 'dll' : 'so';
264
+ let pluginPath;
265
+ const firstPath = (0, path_1.resolve)(`${binaryFilepath}/../../lib/plugin`);
266
+ const secondPath = '/usr/lib/mysql/plugin';
267
+ if (fs.existsSync(`${firstPath}/mysqlx.${pluginExtension}`)) {
268
+ pluginPath = firstPath;
269
+ }
270
+ else if (os.platform() === 'linux' && fs.existsSync(`${secondPath}/mysqlx.so`)) {
271
+ pluginPath = secondPath;
272
+ }
273
+ else {
274
+ throw 'Could not install MySQL X as the path to the plugin cannot be found.';
275
+ }
276
+ mysqlArguments.splice(1, 0, `--plugin-dir=${pluginPath}`, `--early-plugin-load=mysqlx=mysqlx.${pluginExtension};`);
264
277
  }
265
- mysqlArguments.splice(1, 0, `--plugin-dir=${pluginPath}`, `--early-plugin-load=mysqlx=mysqlx.${pluginExtension};`);
266
278
  }
267
279
  const process = (0, child_process_1.spawn)(binaryFilepath, mysqlArguments, { signal: this.DBDestroySignal.signal, killSignal: 'SIGKILL' });
268
280
  //resolveFunction is the function that will be called to resolve the promise that stops the database.
@@ -270,9 +282,15 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
270
282
  //Once ran, resolveFunction will be called.
271
283
  let resolveFunction;
272
284
  process.on('close', async (code, signal) => {
285
+ // Prevent open file watches when MySQL has been exited
286
+ // The file watch is only stopped if MySQL starts successfully
287
+ // This unwatch will prevent the watch from watching when the process has stopped.
288
+ fs.unwatchFile(errorLogFile);
273
289
  if (signal) {
274
- this.logger.log('Exiting because of aborted signal.');
275
- return;
290
+ this.logger.log('Exiting because the process received a signal:', signal);
291
+ }
292
+ else {
293
+ this.logger.log('Exiting with code:', code);
276
294
  }
277
295
  let errorLog;
278
296
  try {
@@ -303,7 +321,9 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
303
321
  }
304
322
  try {
305
323
  if ((0, constants_1.getInternalEnvVariable)('deleteDBAfterStopped') === 'true') {
324
+ this.logger.log('Deleting database path as deleteDBAfterStopped is true...');
306
325
  await fsPromises.rm(dbPath, { recursive: true, force: true, maxRetries: 50, retryDelay: 100 });
326
+ this.logger.log('Database deletion was successful.');
307
327
  }
308
328
  }
309
329
  catch (e) {
@@ -329,7 +349,13 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
329
349
  return reject(`Database exited early.\nError log: ${errorLog}\nError string: "${errorString}`);
330
350
  }
331
351
  if (code) {
332
- const errorMessage = `The database exited early with code ${code}. The error log was:\n${errorLog}\nThe error string was: "${errorString}".`;
352
+ let errorMessage = '';
353
+ if (os.platform() === 'win32' && code === 3221225781) {
354
+ errorMessage = `The MySQL database exited early with code 3221225781. A possible cause is that the Microsoft Visual C++ Redistributable Package is not installed. Please refer to the following link for this package's requirements on your system - this may help solve this error: https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/blob/v1.12.1/docs/SUPPORTED_MYSQL_DOWNLOADS.md#required-dependencies. If you are sure you have this installed, check the following for more details: The error log was:\n${errorLog}\nThe error string was: "${errorString}".`;
355
+ }
356
+ else {
357
+ errorMessage = `The database exited early with code ${code}. The error log was:\n${errorLog}\nThe error string was: "${errorString}".`;
358
+ }
333
359
  this.logger.error(errorMessage);
334
360
  return reject(errorMessage);
335
361
  }
@@ -361,11 +387,11 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
361
387
  }
362
388
  return; //Promise rejection will be handled in the process.on('close') section because this.killedFromPortIssue is being set to true
363
389
  }
364
- resolve({
390
+ const result = {
365
391
  port,
366
- xPort: mySQLXPort,
392
+ xPort: options.xEnabled === 'FORCE' ? mySQLXPort : -1,
367
393
  socket,
368
- xSocket,
394
+ xSocket: options.xEnabled === 'FORCE' ? xSocket : '',
369
395
  dbName: options.dbName,
370
396
  username: options.username,
371
397
  mysql: {
@@ -382,7 +408,8 @@ _Executor_instances = new WeakSet(), _Executor_executeFile = function _Executor_
382
408
  }
383
409
  });
384
410
  }
385
- });
411
+ };
412
+ resolve(result);
386
413
  }
387
414
  }
388
415
  });
@@ -1,3 +1,4 @@
1
1
  import { LinuxEtcOSRelease } from '../../types';
2
- declare const _default: LinuxEtcOSRelease;
3
- export default _default;
2
+ declare const releaseDetails: LinuxEtcOSRelease;
3
+ export declare const isOnAlpineLinux: boolean;
4
+ export default releaseDetails;
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isOnAlpineLinux = void 0;
6
7
  const fs_1 = __importDefault(require("fs"));
7
8
  const releaseDetails = {};
8
9
  if (process.platform === 'linux') {
@@ -15,4 +16,5 @@ if (process.platform === 'linux') {
15
16
  }
16
17
  }
17
18
  }
19
+ exports.isOnAlpineLinux = process.platform === 'linux' && releaseDetails?.ID === 'alpine';
18
20
  exports.default = releaseDetails;
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GenerateRandomPort = GenerateRandomPort;
4
+ const crypto_1 = require("crypto");
4
5
  function GenerateRandomPort() {
5
- return Math.floor(Math.random() * (65535 - 1024) + 1025);
6
+ //Min is inclusive and max is exclusive. Inclusive range would be 1025 - 65535
7
+ return (0, crypto_1.randomInt)(1025, 65536);
6
8
  }
@@ -32,15 +32,12 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
36
  exports.default = getBinaryURL;
40
37
  const os = __importStar(require("os"));
41
38
  const semver_1 = require("semver");
42
39
  const constants_1 = require("../constants");
43
- const LinuxOSRelease_1 = __importDefault(require("./LinuxOSRelease"));
40
+ const LinuxOSRelease_1 = __importStar(require("./LinuxOSRelease"));
44
41
  function getBinaryURL(versionToGet = "x", currentArch) {
45
42
  let selectedVersions = constants_1.DOWNLOADABLE_MYSQL_VERSIONS.filter(version => (0, semver_1.satisfies)(version, versionToGet));
46
43
  if (selectedVersions.length === 0) {
@@ -85,16 +82,25 @@ function getBinaryURL(versionToGet = "x", currentArch) {
85
82
  const minVersion = minVersions[0];
86
83
  throw `Your operating system is too out of date to run a version of MySQL that fits the following requirement: ${versionToGet}. The oldest version for your operating system that you would need to get a version that satisfies the version requirement is ${minVersion} but your current operating system is ${coercedOSRelease.version}. Please try changing your MySQL version requirement, updating your OS to a newer version, or if you believe this is a bug, please report this on GitHub.`;
87
84
  }
88
- if (process.platform === 'linux' && LinuxOSRelease_1.default.NAME === 'Ubuntu' && LinuxOSRelease_1.default.VERSION_ID >= '24.04') {
89
- //Since Ubuntu >= 24.04 uses libaio1t64 instead of libaio, this package has to copy libaio1t64 into a folder that MySQL looks in for dynamically linked libraries with the filename "libaio.so.1".
90
- //I have not been able to find a suitable folder for libaio1t64 to be copied into for MySQL < 8.0.4, so here we are filtering all versions lower than 8.0.4 since they fail to launch in Ubuntu 24.04.
91
- //If there is a suitable filepath for libaio1t64 to be copied into for MySQL < 8.0.4 then this check can be removed and these older MySQL versions can run on Ubuntu.
92
- //Pull requests are welcome for adding >= Ubuntu 24.04 support for MySQL < 8.0.4.
93
- //A way to get MySQL running on Ubuntu >= 24.04 is to symlink libaio1t64 to the location libaio would be. It is not suitable for this package to be doing that automatically, so instead this package has been copying libaio1t64 into the MySQL binary folder.
94
- selectedVersions = selectedVersions.filter(v => !(0, semver_1.lt)(v, '8.0.4'));
95
- }
96
- if (selectedVersions.length === 0) {
97
- throw `You are running a version of Ubuntu that is too modern to run any MySQL versions with this package that match the following version requirement: ${versionToGet}. Please choose a newer version of MySQL to use, or if you believe this is a bug please report this on GitHub.`;
85
+ if (process.platform === 'linux') {
86
+ if (LinuxOSRelease_1.default.NAME === 'Ubuntu' && LinuxOSRelease_1.default.VERSION_ID >= '24.04') {
87
+ //Since Ubuntu >= 24.04 uses libaio1t64 instead of libaio, this package has to copy libaio1t64 into a folder that MySQL looks in for dynamically linked libraries with the filename "libaio.so.1".
88
+ //I have not been able to find a suitable folder for libaio1t64 to be copied into for MySQL < 8.0.4, so here we are filtering all versions lower than 8.0.4 since they fail to launch in Ubuntu 24.04.
89
+ //If there is a suitable filepath for libaio1t64 to be copied into for MySQL < 8.0.4 then this check can be removed and these older MySQL versions can run on Ubuntu.
90
+ //Pull requests are welcome for adding >= Ubuntu 24.04 support for MySQL < 8.0.4.
91
+ //A way to get MySQL running on Ubuntu >= 24.04 is to symlink libaio1t64 to the location libaio would be. It is not suitable for this package to be doing that automatically, so instead this package has been copying libaio1t64 into the MySQL binary folder.
92
+ selectedVersions = selectedVersions.filter(v => !(0, semver_1.lt)(v, '8.0.4'));
93
+ if (selectedVersions.length === 0) {
94
+ throw `You are running a version of Ubuntu that is too modern to run any MySQL versions with this package that match the following version requirement: ${versionToGet}. Please choose a newer version of MySQL to use, or if you believe this is a bug please report this on GitHub.`;
95
+ }
96
+ }
97
+ else if (LinuxOSRelease_1.isOnAlpineLinux) {
98
+ //https://github.com/Sebastian-Webster/mysql-server-musl-binaries only has support for v8.4.x and 9.x binaries
99
+ selectedVersions = selectedVersions.filter(v => (0, semver_1.satisfies)(v, '8.4.x') || (0, semver_1.satisfies)(v, '9.x'));
100
+ if (selectedVersions.length === 0) {
101
+ throw 'mysql-memory-server has detected you are running this package on Alpine Linux. The source for MySQL with musl libc only provides binaries for MySQL 8.4.x and 9.x and as such only those versions can be used with this package. Please use 8.4.x or 9.x.';
102
+ }
103
+ }
98
104
  }
99
105
  //Sorts versions in descending order
100
106
  selectedVersions.sort((a, b) => a < b ? 1 : -1);
@@ -102,6 +108,7 @@ function getBinaryURL(versionToGet = "x", currentArch) {
102
108
  const isRC = (0, semver_1.satisfies)(selectedVersion, constants_1.RC_MYSQL_VERSIONS);
103
109
  const isDMR = (0, semver_1.satisfies)(selectedVersion, constants_1.DMR_MYSQL_VERSIONS);
104
110
  let fileLocation = '';
111
+ let xPluginSupported = true;
105
112
  if (currentOS === 'win32') {
106
113
  fileLocation = `${(0, semver_1.major)(selectedVersion)}.${(0, semver_1.minor)(selectedVersion)}/mysql-${selectedVersion}${isRC ? '-rc' : isDMR ? '-dmr' : ''}-winx64.zip`;
107
114
  }
@@ -110,6 +117,10 @@ function getBinaryURL(versionToGet = "x", currentArch) {
110
117
  const macOSVersionNameKey = MySQLmacOSVersionNameKeys.find(range => (0, semver_1.satisfies)(selectedVersion, range));
111
118
  fileLocation = `${(0, semver_1.major)(selectedVersion)}.${(0, semver_1.minor)(selectedVersion)}/mysql-${selectedVersion}${isRC ? '-rc' : isDMR ? '-dmr' : ''}-${constants_1.MYSQL_MACOS_VERSIONS_IN_FILENAME[macOSVersionNameKey]}-${currentArch === 'x64' ? 'x86_64' : 'arm64'}.tar.gz`;
112
119
  }
120
+ else if (LinuxOSRelease_1.isOnAlpineLinux) {
121
+ fileLocation = `https://github.com/Sebastian-Webster/mysql-server-musl-binaries/releases/download/current/mysql-musl-${selectedVersion}-${currentArch === 'x64' ? 'x86_64' : 'arm64'}.tar.gz`;
122
+ xPluginSupported = false;
123
+ }
113
124
  else if (currentOS === 'linux') {
114
125
  const glibcObject = constants_1.MYSQL_LINUX_GLIBC_VERSIONS[currentArch];
115
126
  const glibcVersionKeys = Object.keys(glibcObject);
@@ -124,8 +135,13 @@ function getBinaryURL(versionToGet = "x", currentArch) {
124
135
  const fileExtension = constants_1.MYSQL_LINUX_FILE_EXTENSIONS[currentArch][fileExtensionKey];
125
136
  fileLocation = `${(0, semver_1.major)(selectedVersion)}.${(0, semver_1.minor)(selectedVersion)}/mysql-${selectedVersion}${isRC ? '-rc' : isDMR ? '-dmr' : ''}-linux-${minimalInstallAvailable !== 'no-glibc-tag' ? `glibc${glibcVersion}-` : ''}${currentArch === 'x64' ? 'x86_64' : 'aarch64'}${minimalInstallAvailable !== 'no' && (process.arch !== 'arm64' ? true : (0, semver_1.satisfies)(selectedVersion, constants_1.MYSQL_LINUX_MINIMAL_INSTALL_AVAILABLE_ARM64)) ? `-minimal${(0, semver_1.satisfies)(selectedVersion, constants_1.MYSQL_LINUX_MINIMAL_REBUILD_VERSIONS) ? '-rebuild' : ''}` : ''}.tar.${fileExtension}`;
126
137
  }
138
+ else {
139
+ throw 'You are running this package on an unsupported OS. Please use either Windows, macOS, or a Linux-based OS.';
140
+ }
127
141
  return {
128
142
  version: selectedVersion,
129
- url: constants_1.archiveBaseURL + fileLocation
143
+ url: fileLocation,
144
+ hostedByOracle: !LinuxOSRelease_1.isOnAlpineLinux, // Only the Alpine Linux binaries are not hosted on the MySQL CDN.
145
+ xPluginSupported
130
146
  };
131
147
  }
@@ -1,5 +1,6 @@
1
1
  import { ExecFileException } from "child_process";
2
2
  export type LOG_LEVEL = 'LOG' | 'WARN' | 'ERROR';
3
+ export type PluginActivationState = 'OFF' | 'FORCE';
3
4
  export type ServerOptions = {
4
5
  version?: string | undefined;
5
6
  dbName?: string | undefined;
@@ -15,6 +16,7 @@ export type ServerOptions = {
15
16
  downloadRetries?: number | undefined;
16
17
  initSQLString?: string | undefined;
17
18
  arch?: "arm64" | "x64" | undefined;
19
+ xEnabled?: PluginActivationState | undefined;
18
20
  };
19
21
  export type InternalServerOptions = {
20
22
  version?: string | undefined;
@@ -31,6 +33,7 @@ export type InternalServerOptions = {
31
33
  downloadRetries: number;
32
34
  initSQLString: string;
33
35
  arch: string;
36
+ xEnabled: PluginActivationState;
34
37
  };
35
38
  export type ExecuteFileReturn = {
36
39
  error: ExecFileException | null;
@@ -54,10 +57,13 @@ export type DownloadedMySQLVersion = {
54
57
  version: string;
55
58
  path: string;
56
59
  installedOnSystem: boolean;
60
+ xPluginSupported: boolean;
57
61
  };
58
62
  export type BinaryInfo = {
59
63
  url: string;
60
64
  version: string;
65
+ hostedByOracle: boolean;
66
+ xPluginSupported: boolean;
61
67
  };
62
68
  export type OptionTypeChecks = {
63
69
  [key in keyof Required<ServerOptions>]: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mysql-memory-server",
3
- "version": "1.11.0",
3
+ "version": "1.12.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",
@@ -17,8 +17,8 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "test": "jest --verbose --colors",
20
- "test:ci": "jest --setupFilesAfterEnv ./ciSetup.js --verbose --colors --runTestsByPath tests/concurrency.test.ts",
21
- "os-compat:ci": "jest --setupFilesAfterEnv ./ciSetup.js --verbose --colors --runTestsByPath tests/versions.test.ts"
20
+ "test:ci": "jest tests/ci --setupFilesAfterEnv ./ciSetup.js --verbose --colors",
21
+ "os-compat:ci": "jest tests/versions.test.ts --setupFilesAfterEnv ./ciSetup.js --verbose --colors"
22
22
  },
23
23
  "engines": {
24
24
  "node": ">=16.6.0",
@@ -31,12 +31,12 @@
31
31
  "@babel/preset-env": "^7.28.0",
32
32
  "@babel/preset-typescript": "^7.27.1",
33
33
  "@types/adm-zip": "^0.5.5",
34
- "@types/node": "^24.0.10",
34
+ "@types/node": "^24.1.0",
35
35
  "@types/semver": "^7.7.0",
36
- "babel-jest": "^30.0.4",
37
- "jest": "^30.0.4",
38
- "mysql2": "^3.14.1",
39
- "typescript": "^5.8.3"
36
+ "babel-jest": "^30.0.5",
37
+ "jest": "^30.0.5",
38
+ "mysql2": "^3.14.3",
39
+ "typescript": "^5.9.2"
40
40
  },
41
41
  "dependencies": {
42
42
  "adm-zip": "^0.5.16",