Skip to content

Commit

Permalink
build - 1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian-Webster committed Aug 5, 2024
1 parent 05e9192 commit 9148238
Show file tree
Hide file tree
Showing 17 changed files with 937 additions and 0 deletions.
2 changes: 2 additions & 0 deletions dist/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { ServerOptions } from '../types';
export declare function createDB(opts?: ServerOptions): Promise<import("../types").MySQLDB>;
83 changes: 83 additions & 0 deletions dist/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDB = createDB;
const Logger_1 = __importDefault(require("./libraries/Logger"));
const os = __importStar(require("node:os"));
const Executor_1 = __importDefault(require("./libraries/Executor"));
const semver_1 = require("semver");
const AbortSignal_1 = __importDefault(require("./libraries/AbortSignal"));
const Version_1 = __importDefault(require("./libraries/Version"));
const versions_json_1 = __importDefault(require("./versions.json"));
const Downloader_1 = require("./libraries/Downloader");
const defaultOptions = {
dbName: 'dbdata',
logLevel: 'ERROR',
portRetries: 10,
downloadBinaryOnce: true,
lockRetries: 1000,
lockRetryWait: 1000
};
process.on('exit', () => {
AbortSignal_1.default.abort('Process is exiting');
});
async function createDB(opts = defaultOptions) {
const options = { ...defaultOptions, ...opts };
const logger = new Logger_1.default(options.logLevel);
const executor = new Executor_1.default(logger);
const version = await executor.getMySQLVersion(options.version);
logger.log('Version currently installed:', version);
if (version === null || (options.version && !(0, semver_1.satisfies)(version.version, options.version))) {
let binaryInfo;
let binaryFilepath;
try {
binaryInfo = (0, Version_1.default)(versions_json_1.default, options.version);
logger.log('Downloading binary:', binaryInfo.version, 'from URL:', binaryInfo.url);
}
catch (e) {
logger.error(e);
if (options.version) {
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!`;
}
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!`;
}
try {
binaryFilepath = await (0, Downloader_1.downloadBinary)(binaryInfo, options, logger);
}
catch (error) {
logger.error('Failed to download binary');
throw error;
}
logger.log('Running downloaded binary');
return await executor.startMySQL(options, binaryFilepath);
}
else {
logger.log(version);
return await executor.startMySQL(options, version.path);
}
}
2 changes: 2 additions & 0 deletions dist/src/libraries/AbortSignal.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare const DBDestroySignal: AbortController;
export default DBDestroySignal;
4 changes: 4 additions & 0 deletions dist/src/libraries/AbortSignal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const DBDestroySignal = new AbortController();
exports.default = DBDestroySignal;
4 changes: 4 additions & 0 deletions dist/src/libraries/Downloader.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Logger from './Logger';
import { BinaryInfo, ServerOptions } from '../../types';
export declare function downloadVersions(): Promise<string>;
export declare function downloadBinary(binaryInfo: BinaryInfo, options: ServerOptions, logger: Logger): Promise<string>;
227 changes: 227 additions & 0 deletions dist/src/libraries/Downloader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.downloadVersions = downloadVersions;
exports.downloadBinary = downloadBinary;
const https = __importStar(require("https"));
const fs = __importStar(require("fs"));
const fsPromises = __importStar(require("fs/promises"));
const os = __importStar(require("os"));
const adm_zip_1 = __importDefault(require("adm-zip"));
const path_1 = require("path");
const crypto_1 = require("crypto");
const child_process_1 = require("child_process");
const proper_lockfile_1 = require("proper-lockfile");
function getZipData(entry) {
return new Promise((resolve, reject) => {
entry.getDataAsync((data, err) => {
if (err) {
reject(err);
}
else {
resolve(data);
}
});
});
}
function handleTarExtraction(filepath, extractedPath) {
return new Promise((resolve, reject) => {
(0, child_process_1.exec)(`tar -xf ${filepath} -C ${extractedPath}`, (error, stdout, stderr) => {
if (error || stderr) {
return reject(error || stderr);
}
resolve();
});
});
}
function downloadVersions() {
return new Promise((resolve, reject) => {
let json = "";
https.get("https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/raw/main/versions.json", function (response) {
response
.on("data", append => json += append)
.on("error", e => {
reject(e);
})
.on("end", () => {
resolve(json);
});
});
});
}
function downloadFromCDN(url, downloadLocation, logger) {
return new Promise((resolve, reject) => {
const fileStream = fs.createWriteStream(downloadLocation);
fileStream.on('open', () => {
const request = https.get(url, (response) => {
response.pipe(fileStream);
});
request.on('error', (err) => {
logger.error(err);
fileStream.close();
fs.unlink(downloadLocation, (err) => {
reject(err);
});
});
});
fileStream.on('finish', () => {
resolve();
});
fileStream.on('error', (err) => {
logger.error(err);
fileStream.end();
fs.unlink(downloadLocation, () => {
reject(err);
});
});
});
}
function extractBinary(url, archiveLocation, extractedLocation) {
return new Promise(async (resolve, reject) => {
const lastDashIndex = url.lastIndexOf('-');
const fileExtension = url.slice(lastDashIndex).split('.').splice(1).join('.');
await fsPromises.mkdir(extractedLocation, { recursive: true });
const folderName = url.split('/').at(-1).replace(`.${fileExtension}`, '');
if (fileExtension === 'zip') {
//Only Windows MySQL files use the .zip extension
const zip = new adm_zip_1.default(archiveLocation);
const entries = zip.getEntries();
for (const entry of entries) {
if (entry.isDirectory) {
if (entry.name === folderName) {
await fsPromises.mkdir(`${extractedLocation}/mysql`, { recursive: true });
}
else {
await fsPromises.mkdir(`${extractedLocation}/${entry.entryName}`, { recursive: true });
}
}
else {
const data = await getZipData(entry);
await fsPromises.writeFile(`${extractedLocation}/${entry.entryName}`, data);
}
}
try {
await fsPromises.rm(archiveLocation);
}
finally {
fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
return resolve((0, path_1.normalize)(`${extractedLocation}/mysql/bin/mysqld.exe`));
}
}
handleTarExtraction(archiveLocation, extractedLocation).then(async () => {
try {
await fsPromises.rm(archiveLocation);
}
finally {
fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
resolve(`${extractedLocation}/mysql/bin/mysqld`);
}
}).catch(error => {
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}`);
});
});
}
function waitForLock(path, options) {
return new Promise(async (resolve, reject) => {
let retries = 0;
while (retries <= options.lockRetries) {
retries++;
try {
const locked = (0, proper_lockfile_1.checkSync)(path);
if (!locked) {
return resolve();
}
else {
await new Promise(resolve => setTimeout(resolve, options.lockRetryWait));
}
}
catch (e) {
return reject(e);
}
}
reject(`lockRetries has been exceeded. Lock had not been released after ${options.lockRetryWait} * ${options.lockRetries} milliseconds.`);
});
}
function downloadBinary(binaryInfo, options, logger) {
return new Promise(async (resolve, reject) => {
const { url, version } = binaryInfo;
const dirpath = `${os.tmpdir()}/mysqlmsn/binaries`;
logger.log('Binary path:', dirpath);
await fsPromises.mkdir(dirpath, { recursive: true });
const lastDashIndex = url.lastIndexOf('-');
const fileExtension = url.slice(lastDashIndex).split('.').splice(1).join('.');
if (options.downloadBinaryOnce) {
const extractedPath = `${dirpath}/${version}`;
await fsPromises.mkdir(extractedPath, { recursive: true });
const binaryPath = (0, path_1.normalize)(`${extractedPath}/mysql/bin/mysqld${process.platform === 'win32' ? '.exe' : ''}`);
const binaryExists = fs.existsSync(binaryPath);
if (binaryExists) {
return resolve(binaryPath);
}
try {
(0, proper_lockfile_1.lockSync)(extractedPath);
const archivePath = `${dirpath}/${version}.${fileExtension}`;
await downloadFromCDN(url, archivePath, logger);
await extractBinary(url, archivePath, extractedPath);
try {
(0, proper_lockfile_1.unlockSync)(extractedPath);
}
catch (e) {
return reject(e);
}
return resolve(binaryPath);
}
catch (e) {
if (String(e) === 'Error: Lock file is already being held') {
logger.log('Waiting for lock for MySQL version', version);
await waitForLock(extractedPath, options);
logger.log('Lock is gone for version', version);
return resolve(binaryPath);
}
return reject(e);
}
}
const uuid = (0, crypto_1.randomUUID)();
const zipFilepath = `${dirpath}/${uuid}.${fileExtension}`;
logger.log('Binary filepath:', zipFilepath);
const extractedPath = `${dirpath}/${uuid}`;
try {
await downloadFromCDN(url, zipFilepath, logger);
}
catch (e) {
reject(e);
}
try {
const binaryPath = await extractBinary(url, zipFilepath, extractedPath);
resolve(binaryPath);
}
catch (e) {
reject(e);
}
});
}
10 changes: 10 additions & 0 deletions dist/src/libraries/Executor.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Logger from "./Logger";
import { InstalledMySQLVersion, InternalServerOptions, MySQLDB } from "../../types";
declare class Executor {
#private;
logger: Logger;
constructor(logger: Logger);
getMySQLVersion(preferredVersion?: string): Promise<InstalledMySQLVersion | null>;
startMySQL(options: InternalServerOptions, binaryFilepath: string): Promise<MySQLDB>;
}
export default Executor;
Loading

0 comments on commit 9148238

Please sign in to comment.