mysql-memory-server 1.8.2 → 1.9.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.
@@ -36,7 +36,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.downloadVersions = downloadVersions;
40
39
  exports.downloadBinary = downloadBinary;
41
40
  const https = __importStar(require("https"));
42
41
  const fs = __importStar(require("fs"));
@@ -47,18 +46,6 @@ const crypto_1 = require("crypto");
47
46
  const child_process_1 = require("child_process");
48
47
  const FileLock_1 = require("./FileLock");
49
48
  const constants_1 = require("../constants");
50
- function getZipData(entry) {
51
- return new Promise((resolve, reject) => {
52
- entry.getDataAsync((data, err) => {
53
- if (err) {
54
- reject(err);
55
- }
56
- else {
57
- resolve(data);
58
- }
59
- });
60
- });
61
- }
62
49
  function handleTarExtraction(filepath, extractedPath) {
63
50
  return new Promise((resolve, reject) => {
64
51
  (0, child_process_1.execFile)(`tar`, ['-xf', filepath, '-C', extractedPath], (error, stdout, stderr) => {
@@ -69,21 +56,6 @@ function handleTarExtraction(filepath, extractedPath) {
69
56
  });
70
57
  });
71
58
  }
72
- function downloadVersions() {
73
- return new Promise((resolve, reject) => {
74
- let json = "";
75
- https.get("https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/raw/main/versions.json", function (response) {
76
- response
77
- .on("data", append => json += append)
78
- .on("error", e => {
79
- reject(e);
80
- })
81
- .on("end", () => {
82
- resolve(json);
83
- });
84
- });
85
- });
86
- }
87
59
  function downloadFromCDN(url, downloadLocation, logger) {
88
60
  return new Promise(async (resolve, reject) => {
89
61
  if (fs.existsSync(downloadLocation)) {
@@ -95,15 +67,15 @@ function downloadFromCDN(url, downloadLocation, logger) {
95
67
  fileStream.on('open', () => {
96
68
  const request = https.get(url, (response) => {
97
69
  if (response.statusCode !== 200) {
70
+ request.destroy();
98
71
  fileStream.close((err) => {
99
72
  if (err) {
100
73
  logger.error('An error occurred while closing the fileStream for non-200 status code. The error was:', err);
101
74
  }
102
- fs.unlink(downloadLocation, (unlinkErr) => {
103
- if (unlinkErr) {
104
- logger.error('An error occurred while deleting downloadLocation after non-200 status code download attempt. The error was:', err);
75
+ fs.rm(downloadLocation, { force: true }, (rmError) => {
76
+ if (rmError) {
77
+ logger.error('An error occurred while deleting downloadLocation after non-200 status code download attempt. The error was:', rmError);
105
78
  }
106
- logger.error('Received status code:', response.statusCode, 'while downloading MySQL binary.');
107
79
  reject(`Received status code ${response.statusCode} while downloading MySQL binary.`);
108
80
  });
109
81
  });
@@ -111,39 +83,62 @@ function downloadFromCDN(url, downloadLocation, logger) {
111
83
  else {
112
84
  response.pipe(fileStream);
113
85
  fileStream.on('finish', () => {
114
- if (!error) {
115
- resolve();
116
- }
86
+ request.end(() => {
87
+ if (!error) {
88
+ resolve();
89
+ }
90
+ });
117
91
  });
118
92
  }
119
93
  });
120
94
  request.on('error', (err) => {
121
95
  error = err;
122
96
  logger.error(err);
123
- fileStream.end(() => {
124
- fs.unlink(downloadLocation, (unlinkError) => {
125
- if (unlinkError) {
126
- logger.error('An error occurred while deleting downloadLocation after an error occurred with the MySQL server binary download. The error was:', unlinkError);
97
+ request.destroy();
98
+ fileStream.close((fsErr) => {
99
+ if (fsErr) {
100
+ logger.error('An error occurred while closing the fileStream on request error. The error was:', fsErr);
101
+ }
102
+ fs.rm(downloadLocation, { force: true }, (rmError) => {
103
+ if (rmError) {
104
+ logger.error('An error occurred while deleting downloadLocation after an error occurred with the MySQL server binary download. The error was:', rmError);
127
105
  }
128
106
  reject(err.message);
129
107
  });
130
108
  });
131
109
  });
132
- });
133
- fileStream.on('error', (err) => {
134
- error = err;
135
- logger.error(err);
136
- fileStream.end(() => {
137
- fs.unlink(downloadLocation, (unlinkError) => {
138
- if (unlinkError) {
139
- logger.error('An error occurred while deleting downloadLocation after an error occurred with the fileStream. The error was:', unlinkError);
110
+ fileStream.on('error', (err) => {
111
+ error = err;
112
+ logger.error(err);
113
+ request.destroy();
114
+ fileStream.close((fsErr) => {
115
+ if (fsErr) {
116
+ logger.error('An error occurred while closing the fileStream on fileStream error. The error was:', fsErr);
140
117
  }
141
- reject(err.message);
118
+ fs.rm(downloadLocation, { force: true }, (rmError) => {
119
+ if (rmError) {
120
+ logger.error('An error occurred while deleting downloadLocation after an error occurred with the fileStream. The error was:', rmError);
121
+ }
122
+ reject(err.message);
123
+ });
142
124
  });
143
125
  });
144
126
  });
145
127
  });
146
128
  }
129
+ function promisifiedZipExtraction(archiveLocation, extractedLocation) {
130
+ return new Promise((resolve, reject) => {
131
+ const zip = new adm_zip_1.default(archiveLocation);
132
+ zip.extractAllToAsync(extractedLocation, false, false, (err) => {
133
+ if (err) {
134
+ reject(err);
135
+ }
136
+ else {
137
+ resolve();
138
+ }
139
+ });
140
+ });
141
+ }
147
142
  function extractBinary(url, archiveLocation, extractedLocation, logger) {
148
143
  return new Promise(async (resolve, reject) => {
149
144
  if (fs.existsSync(extractedLocation)) {
@@ -158,52 +153,64 @@ function extractBinary(url, archiveLocation, extractedLocation, logger) {
158
153
  return reject(`Folder name is undefined for url: ${url}`);
159
154
  }
160
155
  const folderName = mySQLFolderName.replace(`.${fileExtension}`, '');
156
+ let extractionError = undefined;
161
157
  if (fileExtension === 'zip') {
162
158
  //Only Windows MySQL files use the .zip extension
163
- const zip = new adm_zip_1.default(archiveLocation);
164
- const entries = zip.getEntries();
165
- for (const entry of entries) {
166
- if (entry.entryName.indexOf('..') === -1) {
167
- if (entry.isDirectory) {
168
- if (entry.name === folderName) {
169
- await fsPromises.mkdir(`${extractedLocation}/mysql`, { recursive: true });
170
- }
171
- else {
172
- await fsPromises.mkdir(`${extractedLocation}/${entry.entryName}`, { recursive: true });
173
- }
174
- }
175
- else {
176
- const data = await getZipData(entry);
177
- await fsPromises.writeFile(`${extractedLocation}/${entry.entryName}`, data);
178
- }
179
- }
180
- }
181
159
  try {
182
- await fsPromises.rm(archiveLocation);
160
+ await promisifiedZipExtraction(archiveLocation, extractedLocation);
183
161
  }
184
162
  catch (e) {
185
- logger.error('A non-fatal error occurred while removing no longer needed archive file:', e);
163
+ extractionError = e;
164
+ logger.log('An error occurred while extracting the ZIP file. The error was:', e);
186
165
  }
187
166
  finally {
167
+ try {
168
+ await fsPromises.rm(archiveLocation);
169
+ }
170
+ catch (e) {
171
+ logger.error('A non-fatal error occurred while deleting archive location. The error was:', e);
172
+ }
173
+ }
174
+ if (extractionError) {
175
+ return reject(extractionError);
176
+ }
177
+ try {
188
178
  await fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
189
- return resolve((0, path_1.normalize)(`${extractedLocation}/mysql/bin/mysqld.exe`));
190
179
  }
180
+ catch (e) {
181
+ logger.error('An error occurred while moving MySQL binary into correct folder (mysql folder). The error was:', e);
182
+ return reject(e);
183
+ }
184
+ resolve((0, path_1.normalize)(`${extractedLocation}/mysql/bin/mysqld.exe`));
191
185
  }
192
- handleTarExtraction(archiveLocation, extractedLocation).then(async () => {
186
+ else {
193
187
  try {
194
- await fsPromises.rm(archiveLocation);
188
+ await handleTarExtraction(archiveLocation, extractedLocation);
195
189
  }
196
190
  catch (e) {
197
- logger.error('A non-fatal error occurred while removing no longer needed archive file:', e);
191
+ extractionError = e;
192
+ logger.error('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:', e);
198
193
  }
199
194
  finally {
195
+ try {
196
+ await fsPromises.rm(archiveLocation);
197
+ }
198
+ catch (e) {
199
+ logger.error('A non-fatal error occurred while deleting archive location. The error was:', e);
200
+ }
201
+ }
202
+ if (extractionError) {
203
+ return reject(extractionError);
204
+ }
205
+ try {
200
206
  await fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
201
- resolve(`${extractedLocation}/mysql/bin/mysqld`);
202
207
  }
203
- }).catch(error => {
204
- logger.error(`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}`);
205
- reject(error);
206
- });
208
+ catch (e) {
209
+ logger.error('An error occurred while moving MySQL binary into correct folder (mysql folder). The error was:', e);
210
+ return reject(e);
211
+ }
212
+ resolve(`${extractedLocation}/mysql/bin/mysqld`);
213
+ }
207
214
  });
208
215
  }
209
216
  function downloadBinary(binaryInfo, options, logger) {
@@ -245,11 +252,16 @@ function downloadBinary(binaryInfo, options, logger) {
245
252
  }
246
253
  //The code below only runs if the lock has been acquired by us
247
254
  let downloadTries = 0;
255
+ let useDownloadsURL = false;
248
256
  do {
249
257
  try {
250
258
  downloadTries++;
251
- await downloadFromCDN(url, archivePath, logger);
252
- await extractBinary(url, archivePath, extractedPath, logger);
259
+ const downloadURL = useDownloadsURL ? url.replace(constants_1.archiveBaseURL, constants_1.downloadsBaseURL) : url;
260
+ logger.log(`Starting download for MySQL version ${version} from ${downloadURL}.`);
261
+ await downloadFromCDN(downloadURL, archivePath, logger);
262
+ logger.log(`Finished downloading MySQL version ${version} from ${downloadURL}. Now starting binary extraction.`);
263
+ await extractBinary(downloadURL, archivePath, extractedPath, logger);
264
+ logger.log(`Finished extraction for version ${version}`);
253
265
  break;
254
266
  }
255
267
  catch (e) {
@@ -263,6 +275,24 @@ function downloadBinary(binaryInfo, options, logger) {
263
275
  catch (e) {
264
276
  logger.error('An error occurred while deleting extractedPath and/or archivePath:', e);
265
277
  }
278
+ if (e?.includes?.('status code 404')) {
279
+ if (!useDownloadsURL) {
280
+ //Retry with downloads URL
281
+ downloadTries--;
282
+ useDownloadsURL = true;
283
+ logger.log(`Encountered error 404 when using archives URL for version ${version}. Now retrying with the downloads URL.`);
284
+ continue;
285
+ }
286
+ else {
287
+ try {
288
+ await releaseFunction();
289
+ }
290
+ catch (e) {
291
+ logger.error('An error occurred while releasing lock after receiving a 404 error on both downloads and archives URLs. The error was:', e);
292
+ }
293
+ return reject(`Both URLs for MySQL version ${binaryInfo.version} returned status code 404. Aborting download.`);
294
+ }
295
+ }
266
296
  if (downloadTries > options.downloadRetries) {
267
297
  //Only reject if we have met the downloadRetries limit
268
298
  try {
@@ -275,12 +305,12 @@ function downloadBinary(binaryInfo, options, logger) {
275
305
  return reject(e);
276
306
  }
277
307
  else {
278
- console.warn(`An error was encountered during the binary download process. Retrying for retry ${downloadTries}/${options.downloadRetries}. The error was:`, e);
308
+ logger.warn(`An error was encountered during the binary download process. Retrying for retry ${downloadTries}/${options.downloadRetries}. The error was:`, e);
279
309
  }
280
310
  }
281
311
  } while (downloadTries <= options.downloadRetries);
282
312
  try {
283
- releaseFunction();
313
+ await releaseFunction();
284
314
  }
285
315
  catch (e) {
286
316
  logger.error('An error occurred while releasing lock after successful binary download. The error was:', e);
@@ -289,6 +319,7 @@ function downloadBinary(binaryInfo, options, logger) {
289
319
  }
290
320
  else {
291
321
  let downloadTries = 0;
322
+ let useDownloadsURL = false;
292
323
  do {
293
324
  const uuid = (0, crypto_1.randomUUID)();
294
325
  const zipFilepath = `${dirpath}/${uuid}.${fileExtension}`;
@@ -296,8 +327,12 @@ function downloadBinary(binaryInfo, options, logger) {
296
327
  const extractedPath = `${dirpath}/${uuid}`;
297
328
  try {
298
329
  downloadTries++;
299
- await downloadFromCDN(url, zipFilepath, logger);
300
- const binaryPath = await extractBinary(url, zipFilepath, extractedPath, logger);
330
+ const downloadURL = useDownloadsURL ? url.replace(constants_1.archiveBaseURL, constants_1.downloadsBaseURL) : url;
331
+ logger.log(`Starting download for MySQL version ${version} from ${downloadURL}.`);
332
+ await downloadFromCDN(downloadURL, zipFilepath, logger);
333
+ logger.log(`Finished downloading MySQL version ${version} from ${downloadURL}. Now starting binary extraction.`);
334
+ const binaryPath = await extractBinary(downloadURL, zipFilepath, extractedPath, logger);
335
+ logger.log(`Finished extraction for version ${version}`);
301
336
  return resolve(binaryPath);
302
337
  }
303
338
  catch (e) {
@@ -311,6 +346,18 @@ function downloadBinary(binaryInfo, options, logger) {
311
346
  catch (e) {
312
347
  logger.error('An error occurred while deleting extractedPath and/or archivePath:', e);
313
348
  }
349
+ if (e?.includes?.('status code 404')) {
350
+ if (!useDownloadsURL) {
351
+ //Retry with downloads URL
352
+ downloadTries--;
353
+ useDownloadsURL = true;
354
+ logger.log(`Encountered error 404 when using archives URL for version ${version}. Now retrying with the downloads URL.`);
355
+ continue;
356
+ }
357
+ else {
358
+ return reject(`Both URLs for MySQL version ${binaryInfo.version} returned status code 404. Aborting download.`);
359
+ }
360
+ }
314
361
  if (downloadTries > options.downloadRetries) {
315
362
  //Only reject if we have met the downloadRetries limit
316
363
  return reject(e);
@@ -7,6 +7,8 @@ declare class Executor {
7
7
  removeExitHandler: () => void;
8
8
  version: string;
9
9
  versionInstalledOnSystem: boolean;
10
+ databasePath: string;
11
+ killedFromPortIssue: boolean;
10
12
  constructor(logger: Logger);
11
13
  getMySQLVersion(preferredVersion?: string): Promise<DownloadedMySQLVersion | null>;
12
14
  startMySQL(options: InternalServerOptions, installedMySQLBinary: DownloadedMySQLVersion): Promise<MySQLDB>;