Skip to content

Commit 52baa9e

Browse files
Merge pull request #143 from Sebastian-Webster/ephemeral-cli-creation
Add cli creation capability
2 parents fa93c3f + 3a014c4 commit 52baa9e

File tree

8 files changed

+192
-63
lines changed

8 files changed

+192
-63
lines changed

.github/ISSUE_TEMPLATE/bug-report.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ assignees: ''
1616
--
1717
Write a clear and concise description of what the bug is here.
1818

19-
**To Reproduce**
20-
--
21-
Write the steps to reproduce the behavior here:
22-
1. ...
23-
2. ...
24-
3. ...
25-
4. ...
19+
**Command Line Invocation**
20+
<!-- If you are able to replicate this bug by running mysql-memory-server from the CLI, please replace "Not Applicable" with the command you used to invoke the package -->
21+
Not Applicable
22+
23+
**Reproducible Code Example**
24+
<!-- If you are able to replicate this bug with application code that uses this package, please replace "Not Applicable" with the code that reproduces the bug -->
25+
Not Applicable
2626

2727
**Expected behavior**
2828
--
@@ -42,6 +42,7 @@ None
4242
- OS and OS version (e.g macOS 15.0.1):
4343
- JS runtime and runtime version (E.g Node.js 22.9.0):
4444
- mysql-memory-server version:
45+
- In which environment does this bug occur? (Application Code, CLI, or both):
4546

4647
**Additional context**
4748
--

README.md

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# MySQL Memory Server
22

3-
This package allows you to create ephemeral MySQL databases from JavaScript and/or TypeScript code, 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)
3+
This package allows you to create ephemeral MySQL databases from JavaScript and/or TypeScript code and also the CLI, great for testing, CI, and learning MySQL. 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)
44

55
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 JS runtime process exits. A `stop()` method is also provided to stop each database instance.
66

@@ -30,7 +30,7 @@ Requirements for Linux:
3030
- If using the system installed MySQL server: 8.0.20 and newer
3131
- If not using the system installed MySQL server: 8.0.39, 8.0.40, 8.1.0, 8.2.0, 8.3.0, 8.4.2, 8.4.3, 9.0.1, 9.1.0
3232

33-
## Usage
33+
## Example Usage - Application Code
3434

3535
This package supports both ESM and CJS so you can use import or require.
3636

@@ -45,7 +45,7 @@ const db = await createDB()
4545

4646
//Create a new database with custom options set
4747
const db = await createDB({
48-
// see Options for the options you can use in this object and their default values
48+
// see Options below for the options you can use in this object and their default values
4949
// for example:
5050
version: '8.4.x'
5151
})
@@ -73,6 +73,14 @@ await db.stop()
7373
MySQL database initialization can take some time. If you run into a "Timeout exceeded" error with your tests, the timeout should be extended.
7474
If using Jest, information about how to do this can be found here: https://jestjs.io/docs/jest-object#jestsettimeouttimeout
7575

76+
## Example Usage - CLI
77+
78+
```sh
79+
# Options are added by doing --{optionName} {optionValue}
80+
# See Options below for the options you can use with this package
81+
npx mysql-memory-server --version 8.4.x
82+
```
83+
7684
## Documentation
7785

7886
##### `createDB(options: ServerOptions): Promise<MySQLDB>`
@@ -93,7 +101,7 @@ If on Windows, this is the name of the named pipe that the MySQL X Plugin is lis
93101
- `stop: () => Promise<void>`
94102
The method to stop the database. The returned promise resolves when the database has successfully stopped.
95103

96-
###### Options:
104+
#### Options:
97105
- `version: string`
98106

99107
Required: No
@@ -213,28 +221,38 @@ The internal queries that are ran before the queries in ```initSQLString``` are
213221
***
214222
### :warning: Internal Options :warning:
215223

216-
The following options are only meant for internal debugging use. Their behaviour may change or they may get removed between major/minor/patch versions and they are not to be considered stable. The options below will not follow Semantic Versioning so it is advised to not use them.
224+
The following options are only meant for internal use (such as CI or the internals for running this package via the CLI). Their behaviour may change or they may get removed between major/minor/patch versions and they are not to be considered stable. The options below will not follow Semantic Versioning so it is advised to not use them.
217225

218226
- `_DO_NOT_USE_deleteDBAfterStopped: boolean`
219227

220-
Required: No
221-
222228
Default: true
223229

224230
Description: Changes whether or not the database will be deleted after it has been stopped. If set to `true`, the database WILL be deleted after it has been stopped.
225231

226232
- `_DO_NOT_USE_dbPath: string`
227233

228-
Required: No
229-
230234
Default: `TMPDIR/mysqlmsn/dbs/UUID` (replacing TMPDIR with the OS temp directory and UUID with a UUIDv4 without seperating dashes).
231235

232236
Description: The folder to store database-related data in
233237

234238
- `_DO_NOT_USE_binaryDirectoryPath: string`
235239

236-
Required: No
237-
238240
Default: `TMPDIR/mysqlmsn/binaries` (replacing TMPDIR with the OS temp directory)
239241

240242
Description: The folder to store the MySQL binaries when they are downloaded from the CDN.
243+
244+
- `_DO_NOT_USE_beforeSignalCleanupMessage: string`
245+
246+
Required: No
247+
248+
Default: undefined
249+
250+
Description: The message to get displayed in the console before the cleanup that happens when the Node.js process is stopped without the ```stop()``` method being called first.
251+
252+
- `_DO_NOT_USE_afterSignalCleanupMessage: string`
253+
254+
Required: No
255+
256+
Default: undefined
257+
258+
Description: The message to get displayed in the console after the cleanup that happens when the Node.js process is stopped without the ```stop()``` method being called first.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@
5050
"bugs": {
5151
"url": "https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/issues"
5252
},
53-
"homepage": "https://github.com/Sebastian-Webster/mysql-memory-server-nodejs"
53+
"homepage": "https://github.com/Sebastian-Webster/mysql-memory-server-nodejs",
54+
"bin": "dist/src/cli.js"
5455
}

src/cli.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env node
2+
import { createDB } from "./index";
3+
import { OPTION_TYPE_CHECKS } from "./constants";
4+
5+
async function main() {
6+
const definedOptions = process.argv.filter((option) => option.startsWith('--'))
7+
const options = {
8+
_DO_NOT_USE_beforeSignalCleanupMessage: '\nShutting down the epehemeral MySQL database and cleaning all related files...',
9+
_DO_NOT_USE_afterSignalCleanupMessage: 'Shutdown and cleanup is complete.'
10+
}
11+
for (const opt of definedOptions) {
12+
const index = process.argv.indexOf(opt)
13+
const optionValue = process.argv[index + 1]
14+
15+
if (optionValue === undefined) {
16+
throw `Option ${opt} must have a value.`
17+
}
18+
19+
const optionName = opt.slice(2)
20+
const optionType = OPTION_TYPE_CHECKS[optionName].definedType;
21+
22+
//Try to convert the options to their correct types.
23+
//We do not need to do any proper type validation here as the library will make sure everything is correct.
24+
//Like for example, if a string is passed to a number option, it'll be converted to NaN here, but the library
25+
//will throw an error for it not being an actual number.
26+
if (optionType === 'boolean') {
27+
if (optionValue === 'true') {
28+
options[optionName] = true
29+
} else if (optionValue === 'false') {
30+
options[optionName] = false
31+
} else {
32+
options[optionName] = optionValue
33+
}
34+
} else if (optionType === 'number') {
35+
options[optionName] = parseInt(optionValue)
36+
} else {
37+
options[opt.slice(2)] = optionValue
38+
}
39+
}
40+
console.log('Creating ephemeral MySQL database...')
41+
const db = await createDB(options);
42+
console.log(`A MySQL databases has been successfully created with the following parameters:\n\nUsername: ${db.username} \nDatabase Name: ${db.dbName} \nPort: ${db.port} \nX Plugin Port: ${db.xPort} \nSocket: ${db.socket} \nX Plugin Socket: ${db.xSocket}\n`)
43+
console.log(`If you want to use the MySQL CLI client to connect to the database, you can use either commands: \nmysql -u ${db.username} -P ${db.port} --protocol tcp \nOR\nmysql -u ${db.username} --socket ${db.socket}`)
44+
}
45+
46+
main()

src/constants.ts

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export const DEFAULT_OPTIONS_GENERATOR: () => InternalServerOptions = () => ({
2323
_DO_NOT_USE_deleteDBAfterStopped: true,
2424
//mysqlmsn = MySQL Memory Server Node.js
2525
_DO_NOT_USE_dbPath: normalizePath(`${tmpdir()}/mysqlmsn/dbs/${randomUUID().replace(/-/g, '')}`),
26-
_DO_NOT_USE_binaryDirectoryPath: `${tmpdir()}/mysqlmsn/binaries`
26+
_DO_NOT_USE_binaryDirectoryPath: `${tmpdir()}/mysqlmsn/binaries`,
27+
_DO_NOT_USE_beforeSignalCleanupMessage: '',
28+
_DO_NOT_USE_afterSignalCleanupMessage: ''
2729
});
2830

2931
export const DEFAULT_OPTIONS_KEYS = Object.freeze(Object.keys(DEFAULT_OPTIONS_GENERATOR()))
@@ -34,71 +36,97 @@ export const LOG_LEVELS = {
3436
'ERROR': 2
3537
} as const;
3638

37-
export const INTERNAL_OPTIONS = ['_DO_NOT_USE_deleteDBAfterStopped', '_DO_NOT_USE_dbPath', '_DO_NOT_USE_binaryDirectoryPath'] as const;
39+
export const INTERNAL_OPTIONS = ['_DO_NOT_USE_deleteDBAfterStopped', '_DO_NOT_USE_dbPath', '_DO_NOT_USE_binaryDirectoryPath', '_DO_NOT_USE_beforeSignalCleanup', '_DO_NOT_USE_afterSignalCleanup'] as const;
3840

3941
export const OPTION_TYPE_CHECKS: OptionTypeChecks = {
4042
version: {
4143
check: (opt: any) => opt === undefined || typeof opt === 'string' && validSemver(opt) !== null,
42-
errorMessage: 'Option version must be either undefined or a valid semver string.'
44+
errorMessage: 'Option version must be either undefined or a valid semver string.',
45+
definedType: 'string'
4346
},
4447
dbName: {
4548
check: (opt: any) => opt === undefined || typeof opt === 'string' && opt.length <= 64,
46-
errorMessage: 'Option dbName must be either undefined or a string that is not longer than 64 characters.'
49+
errorMessage: 'Option dbName must be either undefined or a string that is not longer than 64 characters.',
50+
definedType: 'string'
4751
},
4852
logLevel: {
4953
check: (opt: any) => opt === undefined || Object.keys(LOG_LEVELS).includes(opt),
50-
errorMessage: 'Option logLevel must be either undefined or "LOG", "WARN", or "ERROR".'
54+
errorMessage: 'Option logLevel must be either undefined or "LOG", "WARN", or "ERROR".',
55+
definedType: 'string'
5156
},
5257
portRetries: {
5358
check: (opt: any) => opt === undefined || typeof opt === 'number' && opt >= 0,
54-
errorMessage: 'Option portRetries must be either undefined, a positive number, or 0.'
59+
errorMessage: 'Option portRetries must be either undefined, a positive number, or 0.',
60+
definedType: 'number'
5561
},
5662
downloadBinaryOnce: {
5763
check: (opt: any) => opt === undefined || typeof opt === 'boolean',
58-
errorMessage: 'Option downloadBinaryOnce must be either undefined or a boolean.'
64+
errorMessage: 'Option downloadBinaryOnce must be either undefined or a boolean.',
65+
definedType: 'boolean'
5966
},
6067
lockRetries: {
6168
check: (opt: any) => opt === undefined || typeof opt === 'number' && opt >= 0,
62-
errorMessage: 'Option lockRetries must be either undefined, a positive number, or 0.'
69+
errorMessage: 'Option lockRetries must be either undefined, a positive number, or 0.',
70+
definedType: 'number'
6371
},
6472
lockRetryWait: {
6573
check: (opt: any) => opt === undefined || typeof opt === 'number' && opt >= 0,
66-
errorMessage: 'Option lockRetryWait must be either undefined, a positive number, or 0.'
74+
errorMessage: 'Option lockRetryWait must be either undefined, a positive number, or 0.',
75+
definedType: 'number'
6776
},
6877
username: {
6978
check: (opt: any) => opt === undefined || typeof opt === 'string' && opt.length <= 32,
70-
errorMessage: 'Option username must be either undefined or a string that is not longer than 32 characters.'
79+
errorMessage: 'Option username must be either undefined or a string that is not longer than 32 characters.',
80+
definedType: 'string'
7181
},
7282
ignoreUnsupportedSystemVersion: {
7383
check: (opt: any) => opt === undefined || typeof opt === 'boolean',
74-
errorMessage: 'Option ignoreUnsupportedSystemVersion must be either undefined or a boolean.'
84+
errorMessage: 'Option ignoreUnsupportedSystemVersion must be either undefined or a boolean.',
85+
definedType: 'boolean'
7586
},
7687
port: {
7788
check: (opt: any) => opt === undefined || typeof opt === 'number' && opt >= 0 && opt <= 65535,
78-
errorMessage: 'Option port must be either undefined or a number that is between 0 and 65535 inclusive.'
89+
errorMessage: 'Option port must be either undefined or a number that is between 0 and 65535 inclusive.',
90+
definedType: 'number'
7991
},
8092
xPort: {
8193
check: (opt: any) => opt === undefined || typeof opt === 'number' && opt >= 0 && opt <= 65535,
82-
errorMessage: 'Option xPort must be either undefined or a number that is between 0 and 65535 inclusive.'
94+
errorMessage: 'Option xPort must be either undefined or a number that is between 0 and 65535 inclusive.',
95+
definedType: 'number'
8396
},
8497
downloadRetries: {
8598
check: (opt: any) => opt === undefined || typeof opt === 'number' && opt >= 0,
86-
errorMessage: 'Option downloadRetries must be either undefined, a positive number, or 0.'
99+
errorMessage: 'Option downloadRetries must be either undefined, a positive number, or 0.',
100+
definedType: 'number'
87101
},
88102
initSQLString: {
89103
check: (opt: any) => opt === undefined || typeof opt === 'string',
90-
errorMessage: 'Option initSQLString must be either undefined or a string.'
104+
errorMessage: 'Option initSQLString must be either undefined or a string.',
105+
definedType: 'string'
91106
},
92107
_DO_NOT_USE_deleteDBAfterStopped: {
93108
check: (opt: any) => opt === undefined || typeof opt === 'boolean',
94-
errorMessage: 'Option _DO_NOT_USE_deleteDBAfterStopped must be either undefined or a boolean.'
109+
errorMessage: 'Option _DO_NOT_USE_deleteDBAfterStopped must be either undefined or a boolean.',
110+
definedType: 'boolean'
95111
},
96112
_DO_NOT_USE_dbPath: {
97113
check: (opt: any) => opt === undefined || typeof opt === 'string',
98-
errorMessage: 'Option _DO_NOT_USE_dbPath must be either undefined or a string.'
114+
errorMessage: 'Option _DO_NOT_USE_dbPath must be either undefined or a string.',
115+
definedType: 'string'
99116
},
100117
_DO_NOT_USE_binaryDirectoryPath: {
101118
check: (opt: any) => opt === undefined || typeof opt === 'string',
102-
errorMessage: 'Option _DO_NOT_USE_binaryDirectoryPath must be either undefined or a string.'
119+
errorMessage: 'Option _DO_NOT_USE_binaryDirectoryPath must be either undefined or a string.',
120+
definedType: 'string'
121+
},
122+
_DO_NOT_USE_beforeSignalCleanupMessage: {
123+
check: (opt: any) => opt === undefined || typeof opt === 'string',
124+
errorMessage: 'Option _DO_NOT_USE_beforeSignalCleanup must be either undefined or a string.',
125+
definedType: 'string'
126+
},
127+
_DO_NOT_USE_afterSignalCleanupMessage: {
128+
check: (opt: any) => opt === undefined || typeof opt === 'string',
129+
errorMessage: 'Option _DO_NOT_USE_afterSignalCleanup must be either undefined or a string.',
130+
definedType: 'string'
103131
}
104132
} as const;

src/libraries/AbortSignal.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)