generate-pw 1.1.0 → 1.2.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
@@ -1,11 +1,29 @@
1
+ <div align="right">
2
+ <h6>
3
+ <picture>
4
+ <source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/white/icon32.svg">
5
+ <img height=14 src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/black/icon32.svg">
6
+ </picture>
7
+ &nbsp;English |
8
+ <a href="https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/zh-cn#readme">简体中文</a> |
9
+ <a href="https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/zh-tw#readme">繁體中文</a> |
10
+ <a href="https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/hi#readme">हिंदी</a> |
11
+ <a href="https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/bn#readme">বাংলা</a>
12
+ </h6>
13
+ </div>
14
+
1
15
  # > generate-pw
2
16
 
3
17
  ### Randomly generate cryptographically-secure passwords.
4
18
 
5
- <a href="#%EF%B8%8F-mit-license"><img height=28 src="https://img.shields.io/badge/License-MIT-orange.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
6
- <a href="https://www.npmjs.com/package/generate-pw?activeTab=versions"><img height=28 src="https://img.shields.io/badge/Latest_Build-1.1.0-44cc11.svg?logo=icinga&logoColor=white&labelColor=464646&style=for-the-badge"></a>
7
- <a href="https://www.npmjs.com/package/generate-pw?activeTab=code"><img height=28 src="https://img.shields.io/npm/unpacked-size/generate-pw?style=for-the-badge&logo=ebox&logoColor=white&labelColor=464646&color=blue"></a>
8
- <a href="https://sonarcloud.io/component_measures?metric=new_vulnerabilities&id=adamlui_js-utils:generate-pw/generate-pw.js"><img height=28 src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fsonarcloud.io%2Fapi%2Fmeasures%2Fcomponent%3Fcomponent%3Dadamlui_js-utils%3Agenerate-pw%2Fgenerate-pw.js%26metricKeys%3Dvulnerabilities&query=%24.component.measures.0.value&style=for-the-badge&logo=sonarcloud&logoColor=white&labelColor=464646&label=Vulnerabilities&color=gold"></a>
19
+ <a href="#%EF%B8%8F-mit-license"><img height=31 src="https://img.shields.io/badge/License-MIT-orange.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
20
+ <a href="https://github.com/adamlui/js-utils/releases/tag/generate-pw-1.2.1"><img height=31 src="https://img.shields.io/badge/Latest_Build-1.2.1-44cc11.svg?logo=icinga&logoColor=white&labelColor=464646&style=for-the-badge"></a>
21
+ <a href="https://www.npmjs.com/package/generate-pw?activeTab=code"><img height=31 src="https://img.shields.io/npm/unpacked-size/generate-pw?style=for-the-badge&logo=ebox&logoColor=white&labelColor=464646&color=blue"></a>
22
+ <a href="https://sonarcloud.io/component_measures?metric=new_vulnerabilities&id=adamlui_js-utils:generate-pw/generate-pw.js"><img height=31 src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fsonarcloud.io%2Fapi%2Fmeasures%2Fcomponent%3Fcomponent%3Dadamlui_js-utils%3Agenerate-pw%2Fgenerate-pw.js%26metricKeys%3Dvulnerabilities&query=%24.component.measures.0.value&style=for-the-badge&logo=sonarcloud&logoColor=white&labelColor=464646&label=Vulnerabilities&color=gold"></a>
23
+
24
+ <br>
25
+
26
+ <img height=6px width="100%" src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/aqua-separator.png">
9
27
 
10
28
  ## ⚡ Installation
11
29
 
@@ -21,6 +39,10 @@ As a **runtime dependency**, from your project root:
21
39
  $ npm install generate-pw
22
40
  ```
23
41
 
42
+ <br>
43
+
44
+ <img height=6px width="100%" src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/aqua-separator.png">
45
+
24
46
  ## 🔌 API usage
25
47
 
26
48
  **generate-pw** can be imported into your app as an ECMAScript module or a CommonJS module.
@@ -37,6 +59,8 @@ import * as pw from 'generate-pw';
37
59
  const pw = require('generate-pw');
38
60
  ```
39
61
 
62
+ #
63
+
40
64
  ### `generatePassword([options])`
41
65
 
42
66
  Generates **one** password if `qty` option is not given, returning a string:
@@ -44,7 +68,7 @@ Generates **one** password if `qty` option is not given, returning a string:
44
68
  ```js
45
69
  const password = pw.generatePassword({ length: 11, numbers: true });
46
70
  console.log(password);
47
- // sample output: 'bAsZmsmqoQn'
71
+ // sample output: 'bAsZm3mq6Qn'
48
72
  ```
49
73
 
50
74
  ...or **multiple** passwords if `qty` option is given, returning an array of strings:
@@ -57,6 +81,10 @@ console.log(passwords);
57
81
 
58
82
  **💡 Note:** If no options are passed, passwords will be 8-chars long, consisting of upper/lower cased letters.
59
83
 
84
+ See: [Available options](#available-options-for-generate-functions)
85
+
86
+ #
87
+
60
88
  ### `generatePasswords(qty[, options])`
61
89
 
62
90
  Generates **multiple** passwords based on `qty` given, returning an array of strings:
@@ -67,16 +95,66 @@ console.log(passwords);
67
95
  // sample output: [ 'yilppxru', 'ckvkyjfp', 'zolcpyfb' ]
68
96
  ```
69
97
 
70
- ### Available options
98
+ **💡 Note:** If no `qty` arg is passed, just one password will be generated, returned as a string.
99
+
100
+ See: [Available options](#available-options-for-generate-functions)
101
+
102
+ #
103
+
104
+ ### `strictify(password[, requiredCharTypes])`
105
+
106
+ Modifies `password` given to use at least one character of each `requiredCharTypes` element passed, returning a string:
107
+
108
+ ```js
109
+ const password = 'abcdef',
110
+ strictPW = pw.strictify(password, ['numbers', 'symbols']);
111
+
112
+ console.log(strictPW);
113
+ // sample output: 'a!c2ef'
114
+ ```
115
+
116
+ Available `requiredCharTypes` are: `['number', 'symbol', 'lower', 'upper']`
117
+
118
+ **💡 Note:** If no `requiredCharTypes` array is passed, all available types will be required.
119
+
120
+ #
121
+
122
+ ### `validateStrength(password)`
123
+
124
+ Validates the strength of a password, returning an object containing:
125
+ - `strengthScore` (0–100)
126
+ - `recommendations` array
127
+ - `isGood` boolean (`true` if `strengthScore` >= 80)
128
+
129
+ Example:
130
+
131
+ ```js
132
+ const password = 'AawiddsE',
133
+ pwStrength = pw.validateStrength(password);
134
+
135
+ console.log(pwStrength);
136
+
137
+ /* outputs:
138
+ {
139
+ strengthScore: 60,
140
+ recommendations: [ 'Include at least one number.', 'Include at least one symbol.' ],
141
+ isGood: false
142
+ }
143
+ */
144
+ ```
145
+
146
+ #
71
147
 
72
- Any of these can be passed into the options object for each function:
148
+ ### Available options for `generate*()` functions
149
+
150
+ Any of these can be passed into the options object for each `generate*()` function:
73
151
 
74
152
  Name | Type | Description | Default Value
75
153
  ------------|---------|--------------------------------------------------------------------------------|---------------
76
154
  `length` | Integer | Length of password(s). | `8`
77
155
  `qty`* | Integer | Number of passwords to generate. | `1`
78
- `charset` | String | Characters to include in password(s). | ''
79
- `exclude` | String | Characters to exclude from password(s). | ''
156
+ `charset` | String | Characters to include in password(s). | `''`
157
+ `exclude` | String | Characters to exclude from password(s). | `''`
80
158
  `numbers` | Boolean | Allow numbers in password(s). | `false`
81
159
  `symbols` | Boolean | Allow symbols in password(s). | `false`
82
160
  `lowercase` | Boolean | Allow lowercase letters in password(s). | `true`
@@ -87,9 +165,11 @@ Name | Type | Description
87
165
 
88
166
  <br>
89
167
 
168
+ <img height=6px width="100%" src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/aqua-separator.png">
169
+
90
170
  ## 💻 Command line usage
91
171
 
92
- **generate-pw** can also be used from the command line. The basic **global command** is:
172
+ When installed [globally](#-installation), **generate-pw** can also be used from the command line. The basic command is:
93
173
 
94
174
  ```
95
175
  $ generate-pw
@@ -97,10 +177,12 @@ $ generate-pw
97
177
 
98
178
  **💡 Note:** To generate multiple results, pass `--qty=n` where `n` is the number of passwords to generate.
99
179
 
180
+ #
181
+
100
182
  ### Command line options
101
183
 
102
184
  ```
103
- Argument options:
185
+ Parameter options:
104
186
  --length=n Generate password(s) of n length.
105
187
  --qty=n Generate n password(s).
106
188
  --charset=chars Only include chars in password(s).
@@ -113,16 +195,13 @@ Boolean options:
113
195
  -U, --no-uppercase Disallow uppercase letters in password(s).
114
196
 
115
197
  Info commands:
116
- -h, --help Display this help screen.
198
+ -h, --help Display help screen.
117
199
  -v, --version Show version number.
118
200
  ```
119
201
 
120
202
  <br>
121
203
 
122
- ## 💖 Support
123
-
124
- Please consider [giving a GitHub ⭐](https://github.com/adamlui/js-utils) or [funding](https://github.com/sponsors/adamlui) this project if it helped you!
125
- <br><br>
204
+ <img height=6px width="100%" src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/aqua-separator.png">
126
205
 
127
206
  ## 🏛️ MIT License
128
207
 
package/docs/LICENSE.md CHANGED
@@ -1,3 +1,17 @@
1
+ <div align="right">
2
+ <h6>
3
+ <picture>
4
+ <source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/white/icon32.svg">
5
+ <img height=14 src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/black/icon32.svg">
6
+ </picture>
7
+ &nbsp;English |
8
+ <a href="zh-cn/LICENSE.md">简体中文</a> |
9
+ <a href="zh-tw/LICENSE.md">繁體中文</a> |
10
+ <a href="hi/LICENSE.md">हिंदी</a> |
11
+ <a href="bn/LICENSE.md">বাংলা</a>
12
+ </h6>
13
+ </div>
14
+
1
15
  # 🏛️ MIT License
2
16
 
3
17
  **Copyright © 2024 [Adam Lui](https://github.com/adamlui)**
package/docs/SECURITY.md CHANGED
@@ -1,3 +1,17 @@
1
+ <div align="right">
2
+ <h6>
3
+ <picture>
4
+ <source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/white/icon32.svg">
5
+ <img height=14 src="https://raw.githubusercontent.com/adamlui/js-utils/main/docs/images/earth-icon/black/icon32.svg">
6
+ </picture>
7
+ &nbsp;English |
8
+ <a href="zh-cn/SECURITY.md">简体中文</a> |
9
+ <a href="zh-tw/SECURITY.md">繁體中文</a> |
10
+ <a href="hi/SECURITY.md">हिंदी</a> |
11
+ <a href="bn/SECURITY.md">বাংলা</a>
12
+ </h6>
13
+ </div>
14
+
1
15
  # 🛡️ Security Policy
2
16
 
3
17
  If you find a vulnerability, please open a [draft security advisory](https://github.com/adamlui/js-utils/security/advisories/new).
package/generate-pw.js CHANGED
@@ -29,6 +29,12 @@ function generatePassword(options = {}) {
29
29
  };
30
30
  options = { ...defaultOptions, ...options };
31
31
 
32
+ // Validate integer args
33
+ for (const numArgType of ['length', 'qty'])
34
+ if (isNaN(options[numArgType]) || options[numArgType] < 1)
35
+ return console.error(
36
+ `ERROR: [${ numArgType }] argument must be 1 or greater.`);
37
+
32
38
  if (options.qty > 1) { // return array of [qty] password strings
33
39
  const { qty, ...otherOptions } = options;
34
40
  return generatePasswords(qty, otherOptions);
@@ -56,34 +62,10 @@ function generatePassword(options = {}) {
56
62
  }
57
63
 
58
64
  // Enforce strict mode if enabled
59
- if (options.strict) {
65
+ if (options.strict) {
60
66
  const charTypes = ['number', 'symbol', 'lower', 'upper'],
61
67
  requiredCharTypes = charTypes.filter(charType => options[charType + 's'] || options[charType + 'case']);
62
-
63
- // Init flags
64
- for (const charType of requiredCharTypes)
65
- global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)] = false;
66
- for (let i = 0; i < password.length; i++)
67
- for (const charType of requiredCharTypes)
68
- if ((charsets[charType] || charsets[charType + 's']).includes(password.charAt(i)))
69
- global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)] = true;
70
-
71
- // Modify password if necessary
72
- const maxReplacements = Math.min(password.length, requiredCharTypes.length),
73
- replacedPositions = []; let replacementCnt = 0;
74
- for (const charType of requiredCharTypes) {
75
- if (replacementCnt < maxReplacements) {
76
- if (!global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)]) {
77
- let replacementPos;
78
- do replacementPos = randomInt(0, password.length); // pick random pos
79
- while (replacedPositions.includes(replacementPos)); // check if pos already replaced
80
- replacedPositions.push(replacementPos); // track new replacement pos
81
- const replacementCharSet = charsets[charType] || charsets[charType + 's'];
82
- password = password.substring(0, replacementPos) // perform actual replacement
83
- + replacementCharSet.charAt(randomInt(0, replacementCharSet.length))
84
- + password.substring(replacementPos + 1);
85
- replacementCnt++;
86
- }}}
68
+ password = strictify(password, requiredCharTypes);
87
69
  }
88
70
 
89
71
  return password;
@@ -93,16 +75,79 @@ function generatePassword(options = {}) {
93
75
  function generatePasswords(qty, options) {
94
76
  qty = parseInt(qty);
95
77
  if (isNaN(qty)) return console.error(
96
- 'ERROR: First argument <qty> of generatePasswords() must be an integer');
78
+ 'ERROR: First argument [qty] of generatePasswords() must be an integer');
97
79
  const passwords = [];
98
80
  for (let i = 0; i < qty; i++) passwords.push(generatePassword(options));
99
81
  return passwords;
100
82
  }
101
83
 
102
- // EXPORT functions if script was required
103
- if (require.main !== module) module.exports = { generatePassword, generatePasswords };
84
+ function strictify(password, requiredCharTypes = ['number', 'symbol', 'lower', 'upper']) {
85
+
86
+ // Init flags
87
+ for (const charType of requiredCharTypes)
88
+ global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)] = false;
89
+ for (let i = 0; i < password.length; i++)
90
+ for (const charType of requiredCharTypes)
91
+ if ((charsets[charType] || charsets[charType + 's']).includes(password.charAt(i)))
92
+ global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)] = true;
93
+
94
+ // Modify password if necessary
95
+ const maxReplacements = Math.min(password.length, requiredCharTypes.length),
96
+ replacedPositions = [];
97
+ let replacementCnt = 0, strictPW = password;
98
+ for (const charType of requiredCharTypes) {
99
+ if (replacementCnt < maxReplacements) {
100
+ if (!global['has' + charType.charAt(0).toUpperCase() + charType.slice(1)]) {
101
+ let replacementPos;
102
+ do replacementPos = randomInt(0, password.length); // pick random pos
103
+ while (replacedPositions.includes(replacementPos)); // check if pos already replaced
104
+ replacedPositions.push(replacementPos); // track new replacement pos
105
+ const replacementCharSet = charsets[charType] || charsets[charType + 's'];
106
+ strictPW = strictPW.substring(0, replacementPos) // perform actual replacement
107
+ + replacementCharSet.charAt(randomInt(0, replacementCharSet.length))
108
+ + strictPW.substring(replacementPos + 1);
109
+ replacementCnt++;
110
+ }}}
111
+
112
+ return strictPW;
113
+ }
114
+
115
+ function validateStrength(password) {
116
+ const strengthCriteria = { minLength: 8, minLower: 1, minUpper: 1, minNumber: 1, minSymbol: 1 };
117
+
118
+ // Count occurrences of each char type
119
+ const charCnts = { 'lower': 0, 'upper': 0, 'number': 0, 'symbol': 0 };
120
+ for (let i = 0; i < password.length; i++) {
121
+ const char = password[i];
122
+ for (const charType of Object.keys(charCnts))
123
+ if ((charsets[charType] || charsets[charType + 's']).includes(char))
124
+ charCnts[charType]++;
125
+ }
126
+
127
+ // Check criteria + add recommendations
128
+ const recommendations = [];
129
+ if (password.length < strengthCriteria.minLength)
130
+ recommendations.push(`Make it at least ${ strengthCriteria.minLength } characters long.`);
131
+ for (const charType of Object.keys(charCnts))
132
+ if (charCnts[charType] < strengthCriteria['min' + charType.charAt(0).toUpperCase() + charType.slice(1)])
133
+ recommendations.push('Include at least one ' + charType
134
+ + `${ ['upper', 'lower'].includes(charType) ? 'case letter' : '' }.`);
135
+
136
+ // Calculate strength score based on counts and criteria
137
+ let strengthScore = 0;
138
+ strengthScore += ( // +20 for satisfying min length
139
+ password.length >= strengthCriteria.minLength) ? 20 : 0;
140
+ for (const charType of Object.keys(charCnts))
141
+ strengthScore += ( // +20 per char type included
142
+ charCnts[charType] >= strengthCriteria['min' + charType.charAt(0).toUpperCase() + charType.slice(1)]) ? 20 : 0;
143
+
144
+ return { strengthScore, recommendations, isGood: strengthScore >= 80 };
145
+ }
146
+
147
+ // EXPORT main functions if script was required
148
+ if (require.main !== module) module.exports = { generatePassword, generatePasswords, strictify, validateStrength };
104
149
 
105
- else { // run as CLI tool
150
+ else { // run as CLI utility
106
151
 
107
152
  // Init UI colors
108
153
  const nc = '\x1b[0m', // no color
@@ -111,47 +156,56 @@ else { // run as CLI tool
111
156
  bw = '\x1b[1;97m'; // bright white
112
157
 
113
158
  // Load settings from ARGS
114
- const config = {
115
- length: parseInt(process.argv.find(arg => /^--?length/.test(arg))?.split('=')[1]) || 8,
116
- qty: parseInt(process.argv.find(arg => /^--?qu?a?n?ti?t?y=\d+$/.test(arg))?.split('=')[1]) || 1,
117
- charset: process.argv.find(arg => /^--?chars/.test(arg))?.split('=')[1],
118
- excludeChars: process.argv.find(arg => /^--?exclude=/.test(arg))?.split('=')[1] || '',
119
- includeNums: process.argv.some(arg => /^--?(?:n|(?:include-?)?num(?:ber)?s?=?(?:true|1)?)$/.test(arg)),
120
- includeSymbols: process.argv.some(arg => /^--?(?:s|(?:include-?)?symbols?=?(?:true|1)?)$/.test(arg)),
121
- excludeLowerChars: process.argv.some(arg =>
122
- /^--?(?:L|(?:exclude|disable|no)-?lower-?(?:case)?|lower-?(?:case)?=(?:false|0))$/.test(arg)),
123
- excludeUpperChars: process.argv.some(arg =>
124
- /^--?(?:U|(?:exclude|disable|no)-?upper-?(?:case)?|upper-?(?:case)?=(?:false|0))$/.test(arg)),
125
- strictMode: process.argv.some(arg => /^--?s(?:trict)?(?:-?mode)?$/.test(arg))
159
+ const config = {};
160
+ const argRegex = {
161
+ paramOptions: {
162
+ 'length': /^--?length/,
163
+ 'qty': /^--?qu?a?n?ti?t?y=\d+$/,
164
+ 'charset': /^--?chars/,
165
+ 'excludeChars': /^--?exclude=/
166
+ },
167
+ flags: {
168
+ 'includeNums': /^--?(?:n|(?:include-?)?num(?:ber)?s?=?(?:true|1)?)$/,
169
+ 'includeSymbols': /^--?(?:s|(?:include-?)?symbols?=?(?:true|1)?)$/,
170
+ 'excludeLowerChars': /^--?(?:L|(?:exclude|disable|no)-?lower-?(?:case)?|lower-?(?:case)?=(?:false|0))$/,
171
+ 'excludeUpperChars': /^--?(?:U|(?:exclude|disable|no)-?upper-?(?:case)?|upper-?(?:case)?=(?:false|0))$/,
172
+ 'strictMode': /^--?s(?:trict)?(?:-?mode)?$/
173
+ },
174
+ cmds: {
175
+ 'help': /^--?h(?:elp)?$/,
176
+ 'version': /^--?ve?r?s?i?o?n?$/
177
+ }
126
178
  };
179
+ process.argv.forEach(arg => {
180
+ if (!arg.startsWith('-')) return;
181
+ const matchedFlag = Object.keys(argRegex.flags).find(flag => argRegex.flags[flag].test(arg)),
182
+ matchedArgOption = Object.keys(argRegex.paramOptions).find(option => argRegex.paramOptions[option].test(arg)),
183
+ matchedCmd = Object.keys(argRegex.cmds).find(cmd => argRegex.cmds[cmd].test(arg));
184
+ if (matchedFlag) config[matchedFlag] = true;
185
+ else if (matchedArgOption) {
186
+ const value = arg.split('=')[1];
187
+ config[matchedArgOption] = parseInt(value) || value;
188
+ } else if (!matchedCmd) {
189
+ console.error(`\n${br}ERROR: Arg [${ arg }] not recognized.${nc}`);
190
+ console.info(`\n${by}Valid arguments are below.${nc}`);
191
+ printHelpSections(['paramOptions', 'booelanOptions', 'infoCmds']);
192
+ process.exit(1);
193
+ }});
127
194
 
128
195
  // Show HELP screen if -h or --help passed
129
- if (process.argv.some(arg => /^--?h(?:elp)?$/.test(arg))) {
130
- printHelp(`\n${by}generate-pw [options]${nc}`);
131
- printHelp('\nArgument options:');
132
- printHelp(' --length=n Generate password(s) of n length.');
133
- printHelp(' --qty=n Generate n password(s).');
134
- printHelp(' --charset=chars Only include chars in password(s).');
135
- printHelp(' --exclude=chars Exclude chars from password(s).');
136
- printHelp('\nBoolean options:');
137
- printHelp(' -n, --include-numbers Allow numbers in password(s).');
138
- printHelp(' -s, --include-symbols Allow symbols in password(s).');
139
- printHelp(' -L, --no-lowercase Disallow lowercase letters in password(s).');
140
- printHelp(' -U, --no-uppercase Disallow uppercase letters in password(s).');
141
- printHelp('\nInfo commands:');
142
- printHelp(' -h, --help Display this help screen.');
143
- printHelp(' -v, --version Show version number.');
196
+ if (process.argv.some(arg => /^--?h(?:elp)?$/.test(arg))) printHelpSections();
144
197
 
145
198
  // Show VERSION number if -v or --version passed
146
- } else if (process.argv.some(arg => /^--?ve?r?s?i?o?n?$/.test(arg))) {
199
+ else if (process.argv.some(arg => /^--?ve?r?s?i?o?n?$/.test(arg)))
147
200
  console.info('v' + require('./package.json').version);
148
201
 
149
- } else { // run MAIN routine
202
+ else { // run MAIN routine
150
203
  for (const numArgType of ['length', 'qty'])
151
- if (config[numArgType] < 1) return console.error(
152
- `\n${br}Error: '${ numArgType }' argument must be 1 or greater.${nc}`);
204
+ if (config[numArgType] && (isNaN(config[numArgType]) || config[numArgType] < 1))
205
+ return console.error(`\n${br}Error: [${ numArgType }] argument must be 1 or greater.${nc}`);
153
206
  const funcOptions = {
154
- length: config.length, qty: config.qty, charset: config.charset, exclude: config.excludeChars,
207
+ length: config.length || 8, qty: config.qty || 1,
208
+ charset: config.charset, exclude: config.excludeChars,
155
209
  numbers: config.includeNums, symbols: config.includeSymbols,
156
210
  lowercase: !config.excludeLowerChars, uppercase: !config.excludeUpperChars,
157
211
  strict: config.strictMode
@@ -160,26 +214,56 @@ else { // run as CLI tool
160
214
  console.log('\n' + bw + ( Array.isArray(pwResult) ? pwResult.join('\n') : pwResult ) + nc);
161
215
  }
162
216
 
163
- function printHelp(msg) { // wrap msg + indent 2nd+ lines (for --help screen)
164
- const terminalWidth = process.stdout.columns || 80,
165
- indentation = 29, lines = [], words = msg.match(/\S+|\s+/g);
166
-
167
- // Split msg into lines of appropriate lengths
168
- let currentLine = '';
169
- words.forEach(word => {
170
- const lineLength = terminalWidth - ( lines.length === 0 ? 0 : indentation );
171
- if (currentLine.length + word.length > lineLength) { // cap/store it
172
- lines.push(lines.length === 0 ? currentLine : currentLine.trimStart());
173
- currentLine = '';
174
- }
175
- currentLine += word;
176
- });
177
- lines.push(lines.length === 0 ? currentLine : currentLine.trimStart());
178
-
179
- // Print formatted msg
180
- lines.forEach((line, index) => console.info(
181
- index === 0 ? line // print 1st line unindented
182
- : ' '.repeat(indentation) + line // print subsequent lines indented
183
- ));
217
+ function printHelpSections(includeSections = ['cmdFormat', 'paramOptions', 'booelanOptions', 'infoCmds']) {
218
+ const helpSections = {
219
+ 'cmdFormat': [
220
+ `\n${by}generate-pw [options|commands]${nc}`
221
+ ],
222
+ 'paramOptions': [
223
+ '\nParameter options:',
224
+ ' --length=n Generate password(s) of n length.',
225
+ ' --qty=n Generate n password(s).',
226
+ ' --charset=chars Only include chars in password(s).',
227
+ ' --charset=chars Only include chars in password(s).',
228
+ ' --exclude=chars Exclude chars from password(s).'
229
+ ],
230
+ 'booelanOptions': [
231
+ '\nBoolean options:',
232
+ ' -n, --include-numbers Allow numbers in password(s).',
233
+ ' -s, --include-symbols Allow symbols in password(s).',
234
+ ' -L, --no-lowercase Disallow lowercase letters in password(s).',
235
+ ' -U, --no-uppercase Disallow uppercase letters in password(s).'
236
+ ],
237
+ 'infoCmds': [
238
+ '\nInfo commands:',
239
+ ' -h, --help Display help screen.',
240
+ ' -v, --version Show version number.'
241
+ ]
242
+ };
243
+ includeSections.forEach(section => { // print valid arg elems
244
+ helpSections[section]?.forEach(line => printHelpMsg(line)); });
245
+
246
+ function printHelpMsg(msg) { // wrap msg + indent 2nd+ lines (for --help screen)
247
+ const terminalWidth = process.stdout.columns || 80,
248
+ indentation = 29, lines = [], words = msg.match(/\S+|\s+/g);
249
+
250
+ // Split msg into lines of appropriate lengths
251
+ let currentLine = '';
252
+ words.forEach(word => {
253
+ const lineLength = terminalWidth - ( lines.length === 0 ? 0 : indentation );
254
+ if (currentLine.length + word.length > lineLength) { // cap/store it
255
+ lines.push(lines.length === 0 ? currentLine : currentLine.trimStart());
256
+ currentLine = '';
257
+ }
258
+ currentLine += word;
259
+ });
260
+ lines.push(lines.length === 0 ? currentLine : currentLine.trimStart());
261
+
262
+ // Print formatted msg
263
+ lines.forEach((line, index) => console.info(
264
+ index === 0 ? line // print 1st line unindented
265
+ : ' '.repeat(indentation) + line // print subsequent lines indented
266
+ ));
267
+ }
184
268
  }
185
269
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generate-pw",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Randomly generate cryptographically-secure passwords.",
5
5
  "author": {
6
6
  "name": "Adam Lui",
@@ -31,7 +31,9 @@
31
31
  "password",
32
32
  "generator",
33
33
  "unique",
34
- "generate"
34
+ "generate",
35
+ "api",
36
+ "cli"
35
37
  ],
36
38
  "bugs": {
37
39
  "url": "https://github.com/adamlui/js-utils/issues"