kempo-server 1.8.3 → 1.8.4
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 +9 -0
- package/cli-utils-screenshot.png +0 -0
- package/dist/utils/cli.js +1 -1
- package/docs/cli-utils.html +87 -0
- package/docs/fs-utils.html +118 -0
- package/docs/index.html +2 -0
- package/package.json +1 -1
- package/tests/cli.node-test.js +64 -0
- package/utils/cli.js +25 -14
package/README.md
CHANGED
|
@@ -364,6 +364,15 @@ https://github.com/dustinpoissant/kempo-testing-framework
|
|
|
364
364
|
|
|
365
365
|
## Documentation
|
|
366
366
|
|
|
367
|
+
- **[Getting Started](./docs/getting-started.html)** - Installation and basic usage
|
|
368
|
+
- **[Routing](./docs/routing.html)** - File-based routing system
|
|
369
|
+
- **[Request & Response](./docs/request-response.html)** - Working with HTTP objects
|
|
370
|
+
- **[Configuration](./docs/configuration.html)** - Server configuration options
|
|
371
|
+
- **[Middleware](./docs/middleware.html)** - Built-in and custom middleware
|
|
372
|
+
- **[Caching](./docs/caching.html)** - Cache configuration and management
|
|
373
|
+
- **[CLI Utilities](./docs/cli-utils.html)** - Command-line argument parsing
|
|
374
|
+
- **[File System Utilities](./docs/fs-utils.html)** - File and directory operations
|
|
375
|
+
- **[Examples](./docs/examples.html)** - Interactive examples and demos
|
|
367
376
|
- **[CONFIG.md](./CONFIG.md)** - Comprehensive server configuration guide
|
|
368
377
|
- **[UTILS.md](./UTILS.md)** - Utility modules for Node.js projects
|
|
369
378
|
|
|
Binary file
|
package/dist/utils/cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{spawn}from"child_process";import readline from"readline";export const getArgs=(mapping={})=>{const args={};let
|
|
1
|
+
import{spawn}from"child_process";import readline from"readline";export const getArgs=(mapping={},argv=process.argv)=>{const args={};let currentName="",values=[];const save=()=>{if(currentName)if(0===values.length)args[currentName]=!0;else if(1===values.length){const val=values[0];args[currentName]="false"!==val&&("true"===val||val)}else args[currentName]=values};for(let i=2;i<argv.length;i++){const arg=argv[i];if(arg.startsWith("-")){let name;if(save(),name=arg.startsWith("--")?arg.slice(2):arg.slice(1),name.includes("=")){const[key,...valParts]=name.split("=");name=key,values=[valParts.join("=")]}else values=[];mapping[name]&&(name=mapping[name]),currentName=name}else values.push(arg)}return save(),args};export const runChildProcess=command=>new Promise((resolve,reject)=>{const[cmd,...args]=command.split(" "),child=spawn(cmd,args,{stdio:"inherit",shell:!0});child.on("close",code=>{0===code?resolve(`child process exited with code ${code}`):reject(new Error(`child process exited with code ${code}`))}),child.on("error",reject)});export const runChildNodeProcess=(scriptPath,argsObj={})=>{const command=`node ${scriptPath} ${Object.entries(argsObj).flatMap(([key,value])=>!0===value?[`--${key}`]:[`--${key}`,value]).join(" ")}`;return runChildProcess(command)};export const runChildNodeProcessScript=scriptPath=>{const child=spawn("node",[scriptPath],{stdio:"inherit",shell:!0});return new Promise((resolve,reject)=>{child.on("close",code=>{0===code?resolve():reject(new Error(`Process exited with code ${code}`))}),child.on("error",reject)})};export const promptUser=query=>{const rl=readline.createInterface({input:process.stdin,output:process.stdout});return new Promise(resolve=>{rl.question(`${query}: `,answer=>{rl.close(),resolve(answer)})})};export const promptYN=(query,defaultValue="y")=>promptUser(`${query} (${"y"===defaultValue?"Y/n":"y/N"}): `).then(answer=>{const normalizedAnswer=answer.trim().toLowerCase();return""===normalizedAnswer?"y"===defaultValue:"y"===normalizedAnswer});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" theme="auto">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset='utf-8'>
|
|
5
|
+
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
|
6
|
+
<title>CLI Utilities - Kempo Server</title>
|
|
7
|
+
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
|
8
|
+
<link rel="icon" type="image/png" sizes="48x48" href="./media/icon48.png">
|
|
9
|
+
<link rel="manifest" href="./manifest.json">
|
|
10
|
+
<link rel="stylesheet" href="./kempo.min.css" />
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<main>
|
|
14
|
+
<a href="./" class="btn">Home</a>
|
|
15
|
+
<h1>CLI Utilities</h1>
|
|
16
|
+
<p>The CLI utilities provide simple command-line argument parsing functionality for Node.js applications.</p>
|
|
17
|
+
|
|
18
|
+
<h2>Installation</h2>
|
|
19
|
+
<p>Import the utilities from the kempo-server package:</p>
|
|
20
|
+
<pre><code class="hljs javascript">import { getArgs } from 'kempo-server/utils/cli';</code></pre>
|
|
21
|
+
|
|
22
|
+
<h2>getArgs(mapping, argv)</h2>
|
|
23
|
+
<p>Parses command-line arguments into a key-value object.</p>
|
|
24
|
+
|
|
25
|
+
<h3>Parameters</h3>
|
|
26
|
+
<ul>
|
|
27
|
+
<li><code>mapping</code> (object, optional) - Maps short flag names to full names</li>
|
|
28
|
+
<li><code>argv</code> (array, optional) - Array of arguments to parse (defaults to <code>process.argv</code>)</li>
|
|
29
|
+
</ul>
|
|
30
|
+
|
|
31
|
+
<h3>Returns</h3>
|
|
32
|
+
<p>An object where keys are argument names and values are parsed argument values.</p>
|
|
33
|
+
|
|
34
|
+
<h3>Features</h3>
|
|
35
|
+
<ul>
|
|
36
|
+
<li>Supports both space-separated (<code>--flag value</code>) and equals-separated (<code>--flag=value</code>) formats</li>
|
|
37
|
+
<li>Automatically converts <code>"true"</code> and <code>"false"</code> strings to boolean values</li>
|
|
38
|
+
<li>Supports short flags with mapping (e.g., <code>-p</code> maps to <code>port</code>)</li>
|
|
39
|
+
<li>Handles multiple values for the same flag</li>
|
|
40
|
+
<li>Boolean flags without values are set to <code>true</code></li>
|
|
41
|
+
</ul>
|
|
42
|
+
|
|
43
|
+
<h3>Examples</h3>
|
|
44
|
+
|
|
45
|
+
<h4>Basic Usage</h4>
|
|
46
|
+
<pre><code class="hljs bash">node app.js --port 8080 --host localhost --verbose</code></pre>
|
|
47
|
+
<pre><code class="hljs javascript">const args = getArgs();
|
|
48
|
+
// Result: { port: '8080', host: 'localhost', verbose: true }</code></pre>
|
|
49
|
+
|
|
50
|
+
<h4>With Short Flag Mapping</h4>
|
|
51
|
+
<pre><code class="hljs bash">node app.js -p 8080 -h localhost -v</code></pre>
|
|
52
|
+
<pre><code class="hljs javascript">const args = getArgs({
|
|
53
|
+
p: 'port',
|
|
54
|
+
h: 'host',
|
|
55
|
+
v: 'verbose'
|
|
56
|
+
});
|
|
57
|
+
// Result: { port: '8080', host: 'localhost', verbose: true }</code></pre>
|
|
58
|
+
|
|
59
|
+
<h4>Equals-Separated Values</h4>
|
|
60
|
+
<pre><code class="hljs bash">node app.js --port=8080 --enabled=true --disabled=false --name=John</code></pre>
|
|
61
|
+
<pre><code class="hljs javascript">const args = getArgs();
|
|
62
|
+
// Result: { port: '8080', enabled: true, disabled: false, name: 'John' }</code></pre>
|
|
63
|
+
|
|
64
|
+
<h4>Mixed Formats</h4>
|
|
65
|
+
<pre><code class="hljs bash">node app.js --port=8080 -h localhost --verbose --config=prod.json</code></pre>
|
|
66
|
+
<pre><code class="hljs javascript">const args = getArgs({ h: 'host' });
|
|
67
|
+
// Result: { port: '8080', host: 'localhost', verbose: true, config: 'prod.json' }</code></pre>
|
|
68
|
+
|
|
69
|
+
<h4>Values with Equals Signs</h4>
|
|
70
|
+
<pre><code class="hljs bash">node app.js --url=https://example.com?param=value</code></pre>
|
|
71
|
+
<pre><code class="hljs javascript">const args = getArgs();
|
|
72
|
+
// Result: { url: 'https://example.com?param=value' }</code></pre>
|
|
73
|
+
|
|
74
|
+
<h2>Other Functions</h2>
|
|
75
|
+
<p>The CLI utilities module also exports other functions for running child processes and prompting users:</p>
|
|
76
|
+
<ul>
|
|
77
|
+
<li><code>runChildProcess(command)</code> - Run a shell command</li>
|
|
78
|
+
<li><code>runChildNodeProcess(scriptPath, argsObj)</code> - Run a Node.js script with arguments</li>
|
|
79
|
+
<li><code>runChildNodeProcessScript(scriptPath)</code> - Run a Node.js script</li>
|
|
80
|
+
<li><code>promptUser(query)</code> - Prompt user for input</li>
|
|
81
|
+
<li><code>promptYN(query, defaultValue)</code> - Prompt for yes/no with default</li>
|
|
82
|
+
</ul>
|
|
83
|
+
</main>
|
|
84
|
+
<div style="height:25vh"></div>
|
|
85
|
+
</body>
|
|
86
|
+
</html></content>
|
|
87
|
+
<parameter name="filePath">c:\Users\dusti\dev\kempo-server\docs\cli-utils.html
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" theme="auto">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset='utf-8'>
|
|
5
|
+
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
|
6
|
+
<title>File System Utilities - Kempo Server</title>
|
|
7
|
+
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
|
8
|
+
<link rel="icon" type="image/png" sizes="48x48" href="./media/icon48.png">
|
|
9
|
+
<link rel="manifest" href="./manifest.json">
|
|
10
|
+
<link rel="stylesheet" href="./kempo.min.css" />
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<main>
|
|
14
|
+
<a href="./" class="btn">Home</a>
|
|
15
|
+
<h1>File System Utilities</h1>
|
|
16
|
+
<p>The file system utilities provide common file and directory operations with Promise-based APIs for Node.js applications.</p>
|
|
17
|
+
|
|
18
|
+
<h2>Installation</h2>
|
|
19
|
+
<p>Import the utilities from the kempo-server package:</p>
|
|
20
|
+
<pre><code class="hljs javascript">import { ensureDir, copyDir, emptyDir } from 'kempo-server/utils/fs-utils';</code></pre>
|
|
21
|
+
|
|
22
|
+
<h2>ensureDir(dirPath)</h2>
|
|
23
|
+
<p>Ensures that a directory exists, creating it and any necessary parent directories if they don't exist. Similar to <code>mkdir -p</code>.</p>
|
|
24
|
+
|
|
25
|
+
<h3>Parameters</h3>
|
|
26
|
+
<ul>
|
|
27
|
+
<li><code>dirPath</code> (string) - The directory path to ensure exists</li>
|
|
28
|
+
</ul>
|
|
29
|
+
|
|
30
|
+
<h3>Returns</h3>
|
|
31
|
+
<p>Promise that resolves when the directory is confirmed to exist.</p>
|
|
32
|
+
|
|
33
|
+
<h3>Example</h3>
|
|
34
|
+
<pre><code class="hljs javascript">import { ensureDir } from 'kempo-server/utils/fs-utils';
|
|
35
|
+
|
|
36
|
+
await ensureDir('./dist/assets');
|
|
37
|
+
await ensureDir('./logs/app');
|
|
38
|
+
// Creates ./dist/assets and ./logs/app directories if they don't exist</code></pre>
|
|
39
|
+
|
|
40
|
+
<h2>copyDir(srcPath, destPath)</h2>
|
|
41
|
+
<p>Recursively copies an entire directory structure from source to destination.</p>
|
|
42
|
+
|
|
43
|
+
<h3>Parameters</h3>
|
|
44
|
+
<ul>
|
|
45
|
+
<li><code>srcPath</code> (string) - The source directory to copy from</li>
|
|
46
|
+
<li><code>destPath</code> (string) - The destination directory to copy to</li>
|
|
47
|
+
</ul>
|
|
48
|
+
|
|
49
|
+
<h3>Returns</h3>
|
|
50
|
+
<p>Promise that resolves when the copy operation is complete.</p>
|
|
51
|
+
|
|
52
|
+
<h3>Example</h3>
|
|
53
|
+
<pre><code class="hljs javascript">import { copyDir } from 'kempo-server/utils/fs-utils';
|
|
54
|
+
|
|
55
|
+
await copyDir('./src/assets', './dist/assets');
|
|
56
|
+
await copyDir('./public', './dist');
|
|
57
|
+
// Copies all files and subdirectories from src to dest</code></pre>
|
|
58
|
+
|
|
59
|
+
<h2>emptyDir(dirPath)</h2>
|
|
60
|
+
<p>Removes all contents of a directory without deleting the directory itself.</p>
|
|
61
|
+
|
|
62
|
+
<h3>Parameters</h3>
|
|
63
|
+
<ul>
|
|
64
|
+
<li><code>dirPath</code> (string) - The directory path to empty</li>
|
|
65
|
+
</ul>
|
|
66
|
+
|
|
67
|
+
<h3>Returns</h3>
|
|
68
|
+
<p>Promise that resolves when the directory has been emptied.</p>
|
|
69
|
+
|
|
70
|
+
<h3>Example</h3>
|
|
71
|
+
<pre><code class="hljs javascript">import { emptyDir } from 'kempo-server/utils/fs-utils';
|
|
72
|
+
|
|
73
|
+
await emptyDir('./dist');
|
|
74
|
+
await emptyDir('./temp');
|
|
75
|
+
// Removes all files and subdirectories inside ./dist and ./temp</code></pre>
|
|
76
|
+
|
|
77
|
+
<h2>Common Use Cases</h2>
|
|
78
|
+
|
|
79
|
+
<h3>Build Script</h3>
|
|
80
|
+
<pre><code class="hljs javascript">import { ensureDir, copyDir, emptyDir } from 'kempo-server/utils/fs-utils';
|
|
81
|
+
|
|
82
|
+
async function buildProject() {
|
|
83
|
+
// Clean previous build
|
|
84
|
+
await emptyDir('./dist');
|
|
85
|
+
|
|
86
|
+
// Ensure build directories exist
|
|
87
|
+
await ensureDir('./dist/assets');
|
|
88
|
+
await ensureDir('./dist/components');
|
|
89
|
+
|
|
90
|
+
// Copy static assets
|
|
91
|
+
await copyDir('./src/assets', './dist/assets');
|
|
92
|
+
await copyDir('./src/public', './dist');
|
|
93
|
+
}</code></pre>
|
|
94
|
+
|
|
95
|
+
<h3>Backup Script</h3>
|
|
96
|
+
<pre><code class="hljs javascript">import { ensureDir, copyDir } from 'kempo-server/utils/fs-utils';
|
|
97
|
+
|
|
98
|
+
async function backupProject() {
|
|
99
|
+
const timestamp = new Date().toISOString().slice(0, 10);
|
|
100
|
+
const backupPath = `./backups/${timestamp}`;
|
|
101
|
+
|
|
102
|
+
await ensureDir(backupPath);
|
|
103
|
+
await copyDir('./src', `${backupPath}/src`);
|
|
104
|
+
await copyDir('./config', `${backupPath}/config`);
|
|
105
|
+
}</code></pre>
|
|
106
|
+
|
|
107
|
+
<h2>Notes</h2>
|
|
108
|
+
<ul>
|
|
109
|
+
<li>All functions use Node.js's Promise-based fs APIs</li>
|
|
110
|
+
<li>Operations are asynchronous and should be awaited</li>
|
|
111
|
+
<li>Functions handle errors appropriately (e.g., ensureDir ignores EEXIST)</li>
|
|
112
|
+
<li>copyDir preserves directory structure recursively</li>
|
|
113
|
+
</ul>
|
|
114
|
+
</main>
|
|
115
|
+
<div style="height:25vh"></div>
|
|
116
|
+
</body>
|
|
117
|
+
</html></content>
|
|
118
|
+
<parameter name="filePath">c:\Users\dusti\dev\kempo-server\docs\fs-utils.html
|
package/docs/index.html
CHANGED
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
<li><a href="configuration.html">Configuration</a></li>
|
|
36
36
|
<li><a href="middleware.html">Middleware</a></li>
|
|
37
37
|
<li><a href="caching.html">Module Caching</a></li>
|
|
38
|
+
<li><a href="cli-utils.html">CLI Utilities</a></li>
|
|
39
|
+
<li><a href="fs-utils.html">File System Utilities</a></li>
|
|
38
40
|
<li><a href="examples.html">Examples & Demos</a></li>
|
|
39
41
|
</ul>
|
|
40
42
|
</div>
|
package/package.json
CHANGED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { getArgs } from '../utils/cli.js';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
'parses long flags with space-separated values': async ({pass, fail}) => {
|
|
5
|
+
const argv = ['node', 'script.js', '--name', 'John', '--age', '30'];
|
|
6
|
+
const args = getArgs({}, argv);
|
|
7
|
+
|
|
8
|
+
if (args.name !== 'John') return fail('name not parsed');
|
|
9
|
+
if (args.age !== '30') return fail('age not parsed');
|
|
10
|
+
|
|
11
|
+
pass('space-separated values work');
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
'parses long flags with equals-separated values': async ({pass, fail}) => {
|
|
15
|
+
const argv = ['node', 'script.js', '--name=John', '--age=30'];
|
|
16
|
+
const args = getArgs({}, argv);
|
|
17
|
+
|
|
18
|
+
if (args.name !== 'John') return fail('name not parsed with equals');
|
|
19
|
+
if (args.age !== '30') return fail('age not parsed with equals');
|
|
20
|
+
|
|
21
|
+
pass('equals-separated values work');
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
'parses short flags with equals-separated values and mapping': async ({pass, fail}) => {
|
|
25
|
+
const argv = ['node', 'script.js', '-n=John', '-a=30'];
|
|
26
|
+
const args = getArgs({ n: 'name', a: 'age' }, argv);
|
|
27
|
+
|
|
28
|
+
if (args.name !== 'John') return fail('short name not parsed with equals');
|
|
29
|
+
if (args.age !== '30') return fail('short age not parsed with equals');
|
|
30
|
+
|
|
31
|
+
pass('short flags with equals and mapping work');
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
'handles mixed formats': async ({pass, fail}) => {
|
|
35
|
+
const argv = ['node', 'script.js', '--name=John', '-a', '30', '--verbose'];
|
|
36
|
+
const args = getArgs({ a: 'age' }, argv);
|
|
37
|
+
|
|
38
|
+
if (args.name !== 'John') return fail('equals format failed');
|
|
39
|
+
if (args.age !== '30') return fail('space format failed');
|
|
40
|
+
if (args.verbose !== true) return fail('boolean flag failed');
|
|
41
|
+
|
|
42
|
+
pass('mixed formats work');
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
'handles values with equals signs': async ({pass, fail}) => {
|
|
46
|
+
const argv = ['node', 'script.js', '--url=https://example.com?param=value'];
|
|
47
|
+
const args = getArgs({}, argv);
|
|
48
|
+
|
|
49
|
+
if (args.url !== 'https://example.com?param=value') return fail('value with equals not handled');
|
|
50
|
+
|
|
51
|
+
pass('values with equals work');
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
'converts string true and false to booleans': async ({pass, fail}) => {
|
|
55
|
+
const argv = ['node', 'script.js', '--enabled=true', '--disabled=false', '--name=John'];
|
|
56
|
+
const args = getArgs({}, argv);
|
|
57
|
+
|
|
58
|
+
if (args.enabled !== true) return fail('true not converted to boolean');
|
|
59
|
+
if (args.disabled !== false) return fail('false not converted to boolean');
|
|
60
|
+
if (args.name !== 'John') return fail('string value not handled');
|
|
61
|
+
|
|
62
|
+
pass('string booleans converted correctly');
|
|
63
|
+
}
|
|
64
|
+
};
|
package/utils/cli.js
CHANGED
|
@@ -4,40 +4,51 @@ import readline from 'readline';
|
|
|
4
4
|
/*
|
|
5
5
|
Argument Parsing
|
|
6
6
|
*/
|
|
7
|
-
export const getArgs = (mapping = {}) => {
|
|
7
|
+
export const getArgs = (mapping = {}, argv = process.argv) => {
|
|
8
8
|
const args = {};
|
|
9
|
-
let
|
|
9
|
+
let currentName = '';
|
|
10
10
|
let values = [];
|
|
11
11
|
|
|
12
12
|
const save = () => {
|
|
13
|
-
if(
|
|
13
|
+
if(currentName){
|
|
14
14
|
if(values.length === 0){
|
|
15
|
-
args[
|
|
15
|
+
args[currentName] = true;
|
|
16
16
|
} else if(values.length === 1){
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const val = values[0];
|
|
18
|
+
if(val === 'false'){
|
|
19
|
+
args[currentName] = false;
|
|
20
|
+
} else if(val === 'true'){
|
|
21
|
+
args[currentName] = true;
|
|
19
22
|
} else {
|
|
20
|
-
args[
|
|
23
|
+
args[currentName] = val;
|
|
21
24
|
}
|
|
22
25
|
} else {
|
|
23
|
-
args[
|
|
26
|
+
args[currentName] = values;
|
|
24
27
|
}
|
|
25
28
|
}
|
|
26
29
|
};
|
|
27
30
|
|
|
28
|
-
for(let i = 2; i <
|
|
29
|
-
const arg =
|
|
31
|
+
for(let i = 2; i < argv.length; i++){
|
|
32
|
+
const arg = argv[i];
|
|
30
33
|
if(arg.startsWith('-')){
|
|
31
34
|
save();
|
|
35
|
+
let name;
|
|
32
36
|
if(arg.startsWith('--')){
|
|
33
37
|
name = arg.slice(2);
|
|
34
38
|
} else {
|
|
35
39
|
name = arg.slice(1);
|
|
36
|
-
if(mapping[name]){
|
|
37
|
-
name = mapping[name];
|
|
38
|
-
}
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
+
if(name.includes('=')){
|
|
42
|
+
const [key, ...valParts] = name.split('=');
|
|
43
|
+
name = key;
|
|
44
|
+
values = [valParts.join('=')];
|
|
45
|
+
} else {
|
|
46
|
+
values = [];
|
|
47
|
+
}
|
|
48
|
+
if(mapping[name]){
|
|
49
|
+
name = mapping[name];
|
|
50
|
+
}
|
|
51
|
+
currentName = name;
|
|
41
52
|
} else {
|
|
42
53
|
values.push(arg);
|
|
43
54
|
}
|