generate-pw 1.2.1 → 1.3.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.
package/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
  ### Randomly generate cryptographically-secure passwords.
18
18
 
19
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>
20
+ <a href="https://github.com/adamlui/js-utils/releases/tag/generate-pw-1.3.0"><img height=31 src="https://img.shields.io/badge/Latest_Build-1.3.0-44cc11.svg?logo=icinga&logoColor=white&labelColor=464646&style=for-the-badge"></a>
21
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
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
23
 
@@ -101,25 +101,35 @@ See: [Available options](#available-options-for-generate-functions)
101
101
 
102
102
  #
103
103
 
104
- ### `strictify(password[, requiredCharTypes])`
104
+ ### `strictify(password[, requiredCharTypes, options])`
105
105
 
106
106
  Modifies `password` given to use at least one character of each `requiredCharTypes` element passed, returning a string:
107
107
 
108
108
  ```js
109
- const password = 'abcdef',
110
- strictPW = pw.strictify(password, ['numbers', 'symbols']);
111
-
109
+ const strictPW = pw.strictify('abcdef', ['numbers', 'symbols']);
112
110
  console.log(strictPW);
113
- // sample output: 'a!c2ef'
111
+
112
+ /* sample output:
113
+
114
+ strictify() » Strictifying password...
115
+ strictify() » Strictification complete!
116
+ a!c2ef
117
+ */
114
118
  ```
115
119
 
120
+ **💡 Note:** If no `requiredCharTypes` array is passed, all available types will be required.
121
+
116
122
  Available `requiredCharTypes` are: `['number', 'symbol', 'lower', 'upper']`
117
123
 
118
- **💡 Note:** If no `requiredCharTypes` array is passed, all available types will be required.
124
+ Available options (passed as object properties):
125
+
126
+ Name | Type | Description | Default Value
127
+ ----------|---------|-----------------------------------|---------------
128
+ `verbose` | Boolean | Show logging in console/terminal. | `true`
119
129
 
120
130
  #
121
131
 
122
- ### `validateStrength(password)`
132
+ ### `validateStrength(password[, options])`
123
133
 
124
134
  Validates the strength of a password, returning an object containing:
125
135
  - `strengthScore` (0–100)
@@ -135,14 +145,27 @@ const password = 'AawiddsE',
135
145
  console.log(pwStrength);
136
146
 
137
147
  /* outputs:
148
+
149
+ validateStrength() » Validating password strength...
150
+ validateStrength() » Password strength validated!
151
+ validateStrength() » Check returned object for score/recommendations.
138
152
  {
139
153
  strengthScore: 60,
140
- recommendations: [ 'Include at least one number.', 'Include at least one symbol.' ],
154
+ recommendations: [
155
+ 'Make it at least 8 characters long.',
156
+ 'Include at least one number.'
157
+ ],
141
158
  isGood: false
142
159
  }
143
160
  */
144
161
  ```
145
162
 
163
+ Available options (passed as object properties):
164
+
165
+ Name | Type | Description | Default Value
166
+ ----------|---------|-----------------------------------|---------------
167
+ `verbose` | Boolean | Show logging in console/terminal. | `true`
168
+
146
169
  #
147
170
 
148
171
  ### Available options for `generate*()` functions
@@ -175,7 +198,7 @@ When installed [globally](#-installation), **generate-pw** can also be used from
175
198
  $ generate-pw
176
199
  ```
177
200
 
178
- **💡 Note:** To generate multiple results, pass `--qty=n` where `n` is the number of passwords to generate.
201
+ **💡 Note:** For security reasons, generated password(s) are stored in the clipboard.
179
202
 
180
203
  #
181
204
 
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ const m=require("crypto")["randomInt"],r=require("child_process")["execSync"],g={lower:"abcdefghijklmnopqrstuvwxyz",upper:"ABCDEFGHIJKLMNOPQRSTUVWXYZ",numbers:"0123456789",symbols:"!@#$%^&*()-_=+[]{}/\\|;:'\",.<>?"};function a(o={}){var e,r={length:8,qty:1,charset:"",exclude:"",numbers:!1,symbols:!1,lowercase:!0,uppercase:!0,strict:!1};o={...r,...o};for(const s of Object.keys(o))if(!Object.prototype.hasOwnProperty.call(r,s))return console.error(`generatePassword() » ERROR: \`${s}\` is an invalid option.
3
+ `+`generatePassword() » Valid options:
4
+ [ ${Object.keys(r).join(", ")} ]`);for(const n of["length","qty"])if(o[n]=parseInt(o[n],10),isNaN(o[n])||o[n]<1)return console.error(`generatePassword() » ERROR: [${n}] option can only be an integer > 0.`);for(const a of["numbers","symbols","lowercase","uppercase","strict"])if("boolean"!=typeof o[a])return console.error(`generatePassword() » ERROR: [${a}] option can only be \`true\` or \`false\`.`);if(1<o.qty){const{qty:l,...i}=o;return c(l,i)}{let r=o.charset||(o.numbers?g.numbers:"")+(o.symbols?g.symbols:"")+(o.lowercase?g.lower:"")+(o.uppercase?g.upper:""),s=(r=(r=""===r?g.lower+g.upper:r).replace(new RegExp(`[${o.exclude}]`,"g"),""),"");for(let e=0;e<o.length;e++){var t=m(0,r.length);s+=r.charAt(t)}return o.strict&&(e=["number","symbol","lower","upper"].filter(e=>o[e+"s"]||o[e+"case"]),s=p(s,e)),s}}function c(r,s={}){var e={length:8,charset:"",exclude:"",numbers:!1,symbols:!1,lowercase:!0,uppercase:!0,strict:!1};if(s={...e,...s},r=parseInt(r,10),isNaN(r)||r<1)return console.error("generatePasswords() » ERROR: 1st arg [qty] can only be an integer > 0.");for(const t of Object.keys(s))if(!Object.prototype.hasOwnProperty.call(e,t))return console.error(`generatePasswords() » ERROR: \`${t}\` is an invalid option.
5
+ `+`generatePasswords() » Valid options:
6
+ [ ${Object.keys(e).join(", ")} ]`);if(s.length=parseInt(s.length),isNaN(s.length)||s.length<1)return console.error("generatePasswords() » ERROR: [length] option can only be an integer > 0.");for(const n of["numbers","symbols","lowercase","uppercase","strict"])if("boolean"!=typeof s[n])return console.error(`generatePasswords() » ERROR: [${n}] option can only be \`true\` or \`false\`.`);var o=[];for(let e=0;e<r;e++)o.push(a(s));return o}function p(r,s=["number","symbol","lower","upper"],e={}){var o={verbose:!0};if(e={...o,...e},"string"!=typeof r)return console.error("strictify() » ERROR: 1st arg <password> must be a string.");var t=["number","symbol","lower","upper"];for(const p of s)if(!t.includes(p))return console.error(`strictify() » ERROR: \`${p}\` is an invalid character type.
7
+ `+`strictify() » Valid character types: [ ${t.join(", ")} ]`);for(const f of Object.keys(e)){if(!Object.prototype.hasOwnProperty.call(o,f))return console.error(`strictify() » ERROR: \`${f}\` is an invalid option.
8
+ `+`strictify() » Valid options: [ ${Object.keys(o).join(", ")} ]`);if("boolean"!=typeof e[f])return console.error(`strictify() » ERROR: \`${f}\` option can only be set to \`true\` or \`false\`.`)}for(const u of s)global["has"+u.charAt(0).toUpperCase()+u.slice(1)]=!1;for(let e=0;e<r.length;e++)for(const d of s)(g[d]||g[d+"s"]).includes(r.charAt(e))&&(global["has"+d.charAt(0).toUpperCase()+d.slice(1)]=!0);e.verbose&&console.info("strictify() » Strictifying password...");var n=Math.min(r.length,s.length),a=[];let l=0,i=r;for(const h of s)if(l<n&&!global["has"+h.charAt(0).toUpperCase()+h.slice(1)]){let e;for(;e=m(0,r.length),a.includes(e););a.push(e);var c=g[h]||g[h+"s"];i=i.substring(0,e)+c.charAt(m(0,c.length))+i.substring(e+1),l++}return e.verbose&&(0<l?console.info("strictify() » Strictification complete!"):console.info(`strictify() » Password already includes ${s.join(" + ")} characters!
9
+ `+"strictify() » No modifications made.")),i}function e(e,r={}){var s={minLength:8,minLower:1,minUpper:1,minNumber:1,minSymbol:1},o={verbose:!0};if(r={...o,...r},"string"!=typeof e)return console.error("validateStrength() » ERROR: 1st arg <password> must be a string.");for(const l of Object.keys(r)){if(!Object.prototype.hasOwnProperty.call(o,l))return console.error(`validateStrength() » ERROR: \`${l}\` is an invalid option.
10
+ `+`validateStrength() » Valid options: [ ${Object.keys(o).join(", ")} ]`);if("boolean"!=typeof r[l])return console.error(`validateStrength() » ERROR: \`${l}\` option can only be set to \`true\` or \`false\`.`)}r.verbose&&console.info("validateStrength() » Validating password strength...");var t={lower:0,upper:0,number:0,symbol:0};for(const i of e)for(const c of Object.keys(t))(g[c]||g[c+"s"]).includes(i)&&t[c]++;var n=[];e.length<s.minLength&&n.push(`Make it at least ${s.minLength} characters long.`);for(const p of Object.keys(t))t[p]<s["min"+p.charAt(0).toUpperCase()+p.slice(1)]&&n.push("Include at least one "+p+`${["upper","lower"].includes(p)?"case letter":""}.`);let a=0;a+=e.length>=s.minLength?20:0;for(const f of Object.keys(t))a+=t[f]>=s["min"+f.charAt(0).toUpperCase()+f.slice(1)]?20:0;return r.verbose&&console.info("validateStrength() » Password strength validated!\n"+(require.main!==module?"validateStrength() » Check returned object for score/recommendations.":"")),{strengthScore:a,recommendations:n,isGood:80<=a}}if(require.main!==module)module.exports={generatePassword:a,generatePasswords:c,strictify:p,validateStrength:e};else{const n="",l="",i="",f={},u={paramOptions:{length:/^--?length/,qty:/^--?qu?a?n?ti?t?y=.*$/,charset:/^--?chars/,excludeChars:/^--?exclude=/},flags:{includeNums:/^--?(?:n|(?:include-?)?num(?:ber)?s?=?(?:true|1)?)$/,includeSymbols:/^--?(?:s|(?:include-?)?symbols?=?(?:true|1)?)$/,excludeLowerChars:/^--?(?:L|(?:exclude|disable|no)-?lower-?(?:case)?|lower-?(?:case)?=(?:false|0))$/,excludeUpperChars:/^--?(?:U|(?:exclude|disable|no)-?upper-?(?:case)?|upper-?(?:case)?=(?:false|0))$/,strictMode:/^--?s(?:trict)?(?:-?mode)?$/},infoCmds:{help:/^--?h(?:elp)?$/,version:/^--?ve?r?s?i?o?n?$/}};if(process.argv.forEach(r=>{var e,s,o;r.startsWith("-")&&(e=Object.keys(u.paramOptions).find(e=>u.paramOptions[e].test(r)),o=Object.keys(u.flags).find(e=>u.flags[e].test(r)),s=Object.keys(u.infoCmds).find(e=>u.infoCmds[e].test(r)),o?f[o]=!0:e?(o=r.split("=")[1],f[e]=parseInt(o)||o):s||(console.error(`
11
+ ${l}ERROR: Arg [${r}] not recognized.`+n),console.info(`
12
+ ${i}Valid arguments are below.`+n),t(["paramOptions","flags","infoCmds"]),process.exit(1)))}),process.argv.some(e=>u.infoCmds.help.test(e)))t();else if(process.argv.some(e=>u.infoCmds.version.test(e)))console.info("v"+require("./package.json").version);else{for(const h of["length","qty"])f[h]&&(isNaN(f[h])||f[h]<1)&&(console.error(`
13
+ ${l}Error: [${h}] argument can only be > 0.`+n),process.exit(1));const o={length:f.length||8,qty:f.qty||1,charset:f.charset,exclude:f.excludeChars,numbers:!!f.includeNums,symbols:!!f.includeSymbols,lowercase:!f.excludeLowerChars,uppercase:!f.excludeUpperChars,strict:!!f.strictMode},d=a(o);s(Array.isArray(d)?d.join("\n"):d)}function t(e=["cmdFormat","paramOptions","flags","infoCmds"]){const r={cmdFormat:[`
14
+ ${i}generate-pw [options|commands]`+n],paramOptions:["\nParameter options:"," --length=n Generate password(s) of n length."," --qty=n Generate n password(s)."," --charset=chars Only include chars in password(s)."," --charset=chars Only include chars in password(s)."," --exclude=chars Exclude chars from password(s)."],flags:["\nBoolean options:"," -n, --include-numbers Allow numbers in password(s)."," -s, --include-symbols Allow symbols in password(s)."," -L, --no-lowercase Disallow lowercase letters in password(s)."," -U, --no-uppercase Disallow uppercase letters in password(s)."],infoCmds:["\nInfo commands:"," -h, --help Display help screen."," -v, --version Show version number."]};e.forEach(e=>{r[e]?.forEach(e=>{{const o=process.stdout.columns||80,t=[],r=e.match(/\S+|\s+/g);let s="";r.forEach(e=>{var r=o-(0===t.length?0:29);s.length+e.length>r&&(t.push(0===t.length?s:s.trimStart()),s=""),s+=e}),t.push(0===t.length?s:s.trimStart()),t.forEach((e,r)=>console.info(0===r?e:" ".repeat(29)+e))}})})}function s(e){e=e.replace(/\s+$/m,"").replace(/"/g,'""'),"darwin"===process.platform?r(`printf "${e}" | pbcopy`):"linux"===process.platform?r(`printf "${e}" | xclip -selection clipboard`):"win32"===process.platform&&r(`Set-Clipboard -Value "${e}"`,{shell:"powershell"})}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generate-pw",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Randomly generate cryptographically-secure passwords.",
5
5
  "author": {
6
6
  "name": "Adam Lui",
@@ -9,13 +9,19 @@
9
9
  },
10
10
  "homepage": "https://github.com/adamlui/js-utils",
11
11
  "license": "MIT",
12
- "main": "generate-pw.js",
12
+ "main": "dist/generate-pw.min.js",
13
+ "files": [
14
+ "dist",
15
+ "docs/",
16
+ "!docs/*/"
17
+ ],
13
18
  "bin": {
14
- "generatepw": "generate-pw.js",
15
- "generate-pw": "generate-pw.js"
19
+ "generatepw": "dist/generate-pw.min.js",
20
+ "generate-pw": "dist/generate-pw.min.js"
16
21
  },
17
22
  "scripts": {
18
23
  "test": "echo \"Error: no test specified\" && exit 1",
24
+ "build": "minify-js generate-pw.js dist",
19
25
  "bump:patch": "bash utils/bump.sh patch",
20
26
  "bump:minor": "bash utils/bump.sh minor",
21
27
  "bump:major": "bash utils/bump.sh major",
@@ -41,5 +47,8 @@
41
47
  "funding": {
42
48
  "type": "github",
43
49
  "url": "https://github.com/sponsors/adamlui"
50
+ },
51
+ "devDependencies": {
52
+ "@adamlui/minify.js": "^1.4.6"
44
53
  }
45
54
  }
package/generate-pw.js DELETED
@@ -1,269 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // Import crypto.randomInt() for secure RNG
4
- const { randomInt } = require('crypto');
5
-
6
- // Init CHARACTER SETS
7
- const charsets = {
8
- lower: 'abcdefghijklmnopqrstuvwxyz',
9
- upper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
10
- numbers: '0123456789',
11
- symbols: '!@#$%^&*()-_=+[]{}/\\|;:\'",.<>?'
12
- };
13
-
14
- // Define MAIN functions
15
-
16
- function generatePassword(options = {}) {
17
-
18
- // Init options
19
- const defaultOptions = {
20
- length: 8, // length of password
21
- qty: 1, // number of passwords to generate
22
- charset: '', // characters to include
23
- exclude: '', // characters to exclude
24
- numbers: false, // include numberChars
25
- symbols: false, // include symbolChars
26
- lowercase: true, // include lowercase letters
27
- uppercase: true, // include uppercase letters
28
- strict: false // require at least one char from each enabled set
29
- };
30
- options = { ...defaultOptions, ...options };
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
-
38
- if (options.qty > 1) { // return array of [qty] password strings
39
- const { qty, ...otherOptions } = options;
40
- return generatePasswords(qty, otherOptions);
41
-
42
- } else { // return single password string
43
-
44
- // Init password's char set
45
- let pwCharset = options.charset || ( // use passed [charset], or construct from options
46
- (options.numbers ? charsets.numbers : '')
47
- + (options.symbols ? charsets.symbols : '')
48
- + (options.lowercase ? charsets.lower : '')
49
- + (options.uppercase ? charsets.upper : '')
50
- );
51
- if (pwCharset === '') // all flags false + no charset passed
52
- pwCharset = charsets.lower + charsets.upper; // default to upper + lower
53
-
54
- // Exclude passed `exclude` chars
55
- pwCharset = pwCharset.replace(new RegExp(`[${ options.exclude }]`, 'g'), '');
56
-
57
- // Generate unstrict password
58
- let password = '';
59
- for (let i = 0; i < options.length; i++) {
60
- const randomIndex = randomInt(0, pwCharset.length);
61
- password += pwCharset.charAt(randomIndex);
62
- }
63
-
64
- // Enforce strict mode if enabled
65
- if (options.strict) {
66
- const charTypes = ['number', 'symbol', 'lower', 'upper'],
67
- requiredCharTypes = charTypes.filter(charType => options[charType + 's'] || options[charType + 'case']);
68
- password = strictify(password, requiredCharTypes);
69
- }
70
-
71
- return password;
72
- }
73
- }
74
-
75
- function generatePasswords(qty, options) {
76
- qty = parseInt(qty);
77
- if (isNaN(qty)) return console.error(
78
- 'ERROR: First argument [qty] of generatePasswords() must be an integer');
79
- const passwords = [];
80
- for (let i = 0; i < qty; i++) passwords.push(generatePassword(options));
81
- return passwords;
82
- }
83
-
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 };
149
-
150
- else { // run as CLI utility
151
-
152
- // Init UI colors
153
- const nc = '\x1b[0m', // no color
154
- br = '\x1b[1;91m', // bright red
155
- by = '\x1b[1;33m', // bright yellow
156
- bw = '\x1b[1;97m'; // bright white
157
-
158
- // Load settings from ARGS
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
- }
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
- }});
194
-
195
- // Show HELP screen if -h or --help passed
196
- if (process.argv.some(arg => /^--?h(?:elp)?$/.test(arg))) printHelpSections();
197
-
198
- // Show VERSION number if -v or --version passed
199
- else if (process.argv.some(arg => /^--?ve?r?s?i?o?n?$/.test(arg)))
200
- console.info('v' + require('./package.json').version);
201
-
202
- else { // run MAIN routine
203
- for (const numArgType of ['length', 'qty'])
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}`);
206
- const funcOptions = {
207
- length: config.length || 8, qty: config.qty || 1,
208
- charset: config.charset, exclude: config.excludeChars,
209
- numbers: config.includeNums, symbols: config.includeSymbols,
210
- lowercase: !config.excludeLowerChars, uppercase: !config.excludeUpperChars,
211
- strict: config.strictMode
212
- };
213
- const pwResult = generatePassword(funcOptions);
214
- console.log('\n' + bw + ( Array.isArray(pwResult) ? pwResult.join('\n') : pwResult ) + nc);
215
- }
216
-
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
- }
268
- }
269
- }