generate-pw 1.5.16 → 2.0.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
@@ -25,12 +25,12 @@
25
25
  <img height=31 src="https://img.shields.io/npm/dm/generate-pw?logo=npm&color=af68ff&logoColor=white&labelColor=464646&style=for-the-badge"></a>
26
26
  <a href="#%EF%B8%8F-mit-license">
27
27
  <img height=31 src="https://img.shields.io/badge/License-MIT-orange.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
28
- <a href="https://github.com/adamlui/js-utils/releases/tag/generate-pw-1.5.16">
29
- <img height=31 src="https://img.shields.io/badge/Latest_Build-1.5.16-44cc11.svg?logo=icinga&logoColor=white&labelColor=464646&style=for-the-badge"></a>
28
+ <a href="https://github.com/adamlui/js-utils/releases/tag/generate-pw-2.0.0">
29
+ <img height=31 src="https://img.shields.io/badge/Latest_Build-2.0.0-44cc11.svg?logo=icinga&logoColor=white&labelColor=464646&style=for-the-badge"></a>
30
30
  <a href="https://www.npmjs.com/package/generate-pw?activeTab=code">
31
31
  <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>
32
- <a href="https://github.com/adamlui/js-utils/blob/generate-pw-1.5.16/generate-pw/dist/generate-pw.min.js">
33
- <img height=31 src="https://img.shields.io/github/size/adamlui/js-utils/generate-pw/dist/generate-pw.min.js?branch=generate-pw-1.5.16&label=Minified%20Size&logo=databricks&logoColor=white&labelColor=464646&color=ff69b4&style=for-the-badge"></a>
32
+ <a href="https://github.com/adamlui/js-utils/blob/generate-pw-2.0.0/generate-pw/dist/generate-pw.min.js">
33
+ <img height=31 src="https://img.shields.io/github/size/adamlui/js-utils/generate-pw/dist/generate-pw.min.js?branch=generate-pw-2.0.0&label=Minified%20Size&logo=databricks&logoColor=white&labelColor=464646&color=ff69b4&style=for-the-badge"></a>
34
34
  <a href="https://sonarcloud.io/component_measures?metric=new_vulnerabilities&id=adamlui_js-utils:generate-pw/src/generate-pw.js">
35
35
  <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%2Fsrc%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>
36
36
  <a href="https://github.com/toolleeo/cli-apps#password-managers">
@@ -78,7 +78,7 @@ $ npm install generate-pw
78
78
  #### ECMAScript*:
79
79
 
80
80
  ```js
81
- import pw from 'generate-pw';
81
+ import pw from 'generate-pw'
82
82
  ```
83
83
 
84
84
  #### CommonJS:
@@ -94,14 +94,14 @@ const pw = require('generate-pw')
94
94
  #### <> HTML script tag:
95
95
 
96
96
  ```html
97
- <script src="https://cdn.jsdelivr.net/npm/generate-pw@1.5.16/dist/generate-pw.min.js"></script>
97
+ <script src="https://cdn.jsdelivr.net/npm/generate-pw@2.0.0/dist/generate-pw.min.js"></script>
98
98
  ```
99
99
 
100
100
  #### ES6:
101
101
 
102
102
  ```js
103
103
  (async () => {
104
- await import('https://cdn.jsdelivr.net/npm/generate-pw@1.5.16/dist/generate-pw.min.js')
104
+ await import('https://cdn.jsdelivr.net/npm/generate-pw@2.0.0/dist/generate-pw.min.js')
105
105
  // Your code here...
106
106
  })()
107
107
  ```
@@ -110,7 +110,7 @@ const pw = require('generate-pw')
110
110
 
111
111
  ```js
112
112
  ...
113
- // @require https://cdn.jsdelivr.net/npm/generate-pw@1.5.16/dist/generate-pw.min.js
113
+ // @require https://cdn.jsdelivr.net/npm/generate-pw@2.0.0/dist/generate-pw.min.js
114
114
  // ==/UserScript==
115
115
 
116
116
  // Your code here...
@@ -118,7 +118,7 @@ const pw = require('generate-pw')
118
118
 
119
119
  <br>
120
120
 
121
- **💡 Note:** To always import the latest version (not recommended in production!) remove the `@1.5.16` version tag from the jsDelivr URL: `https://cdn.jsdelivr.net/npm/generate-pw/dist/generate-pw.min.js`
121
+ **💡 Note:** To always import the latest version (not recommended in production!) remove the `@2.0.0` version tag from the jsDelivr URL: `https://cdn.jsdelivr.net/npm/generate-pw/dist/generate-pw.min.js`
122
122
 
123
123
  <br>
124
124
 
@@ -190,7 +190,7 @@ console.log(strictPW) // sample output: 'a!c2ef'
190
190
 
191
191
  **💡 Note:** If no `requiredCharTypes` array is passed, all available types will be required.
192
192
 
193
- Available `requiredCharTypes` are: `['number', 'symbol', 'lower', 'upper']`
193
+ Available `requiredCharTypes` are: `['numbers', 'symbols', 'lower', 'upper']`
194
194
 
195
195
  Available options (passed as object properties):
196
196
 
@@ -245,15 +245,17 @@ Name | Type | Description
245
245
  ----------------------|---------|--------------------------------------------------------------------------------|---------------
246
246
  `verbose` | Boolean | Show logging in console/terminal. | `true`
247
247
  `length` | Integer | Length of password(s). | `8`
248
- `qty`* | Integer | Number of passwords to generate. | `1`
248
+ `qty` | Integer | Number of passwords to generate. | `1`
249
+ `strength` | String | `<'weak'\|'basic'\|'strong'>` Apply strength preset. | `''`
249
250
  `charset` | String | Characters to include in password(s). | `''`
250
251
  `exclude` | String | Characters to exclude from password(s). | `''`
251
252
  `numbers` | Boolean | Allow numbers in password(s). | `false`
252
253
  `symbols` | Boolean | Allow symbols in password(s). | `false`
253
254
  `lowercase` | Boolean | Allow lowercase letters in password(s). | `true`
254
255
  `uppercase` | Boolean | Allow uppercase letters in password(s). | `true`
255
- `excludeSimilarChars` | Boolean | Exclude similar characters (e.g. o,0,O,i,l,1,\|) in password(s). | `false`
256
- `strict` | Boolean | Require at least one character from each allowed character set in password(s). | `false`
256
+ `similarChars` | Boolean | Include similar characters (e.g. o,0,O,i,l,1,\|) in password(s). | `false`
257
+ `strict` | Boolean | Require at least one character from each allowed character set in password(s). | `true`
258
+ `entropy` | Boolean | Calculate/log estimated entropy. | `false`
257
259
 
258
260
  ##### _*Only available in [`generatePassword([options])`](#generatepasswordoptions) since [`generatePasswords(qty[, options])`](#generatepasswordsqty-options) takes a `qty` argument_
259
261
 
@@ -281,22 +283,56 @@ Parameter options:
281
283
  --qty=n Generate n password(s).
282
284
  --charset=chars Only include chars in password(s).
283
285
  --exclude=chars Exclude chars from password(s).
286
+ --config="path/to/file" Load custom config file.
284
287
 
285
288
  Boolean options:
286
- -n, --include-numbers Allow numbers in password(s).
287
- -y, --include-symbols Allow symbols in password(s).
289
+ -w, --weak Generate weak password(s).
290
+ -b, --basic Generate basic strength password(s).
291
+ -t, --strong Generate strong password(s).
292
+ -N, --no-numbers Disallow numbers in password(s).
293
+ -Y, --no-symbols Disallow symbols in password(s).
288
294
  -L, --no-lowercase Disallow lowercase letters in password(s).
289
295
  -U, --no-uppercase Disallow uppercase letters in password(s).
290
- -S, --no-similar Exclude similar characters in password(s).
291
- -s, --strict Require at least one character from each
292
- allowed character set in password(s).
296
+ -S, --similar-chars Include similar characters in password(s).
297
+ -S, --unstrict Don't require at least one character from
298
+ each allowed character set in password(s).
299
+ -e, --entropy Calculate/log estimated entropy.
293
300
  -q, --quiet Suppress all logging except errors.
294
301
 
295
- Info commands:
302
+ Commands:
303
+ -i, --init Create config file (in project root).
296
304
  -h, --help Display help screen.
297
305
  -v, --version Show version number.
298
306
  ```
299
307
 
308
+ #
309
+
310
+ ### Configuration file
311
+
312
+ **generate-pw** can be customized using a `generate-pw.config.mjs` or `generate-pw.config.js` placed in your project root.
313
+
314
+ Example defaults:
315
+
316
+ ```js
317
+ export default {
318
+ length: 12, // length of passwords to generate
319
+ qty: 1, // # of passwords to generate
320
+ strength: '', // <'weak'|'basic'|'strong'> apply strength preset
321
+ charset: '', // only include chars in password(s)
322
+ exclude: '', // exclude chars from password(s)
323
+ excludeNums: false, // disallow numbers in password(s)
324
+ excludeSymbols: false, // disallow symbols in password(s)
325
+ excludeLowerChars: false, // disallow lowercase letters in password(s)
326
+ excludeUpperChars: false, // disallow uppercase letters in password(s)
327
+ similarChars: false, // include similar chars in password(s)
328
+ unstrict: false, // don't require 1+ char from each allowed charset in password(s)
329
+ entropy: false, // calculate/log estimated entropy
330
+ quietMode: false // suppress all logging except errors
331
+ }
332
+ ```
333
+
334
+ 💡 Run `generate-pw init` to generate a template `generate-pw.config.mjs` in your project root.
335
+
300
336
  <br>
301
337
 
302
338
  <img height=6px width="100%" src="https://assets.js-utils.org/images/separators/aqua-gradient.png?v=0d36e26">
@@ -1,21 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * © 2024–2026 Adam Lui & contributors under the MIT license.
4
- * Source: https://code.generatepw.org
5
- * Documentation: https://docs.generatepw.org
3
+ * © 2024–2026 Adam Lui & contributors under the MIT license.
4
+ * Source: https://github.com/adamlui/js-utils/tree/main/generate-pw/src
5
+ * Documentation: https://github.com/adamlui/js-utils/tree/main/generate-pw/docs
6
6
  */
7
- (async()=>{globalThis.env={devMode:__dirname.match(/[\\/]src/)};var e,s=require("node-clipboardy"),p=require("child_process").execSync,a=require("fs"),o=require(`../generate-pw${env.devMode?"":".min"}.js`).generatePassword,{getMsgs:r,getSysLang:n}=require(`./lib/language${env.devMode?"":".min"}.js`),i=require("path");globalThis.app=require(`../${env.devMode?"../":"./data/"}app.json`),app.urls.docs+="/#-command-line-usage",app.msgs=await r(n()),app.regex={paramOptions:{length:/^--?length(?:=.*|$)/,qty:/^--?qu?a?n?ti?t?y(?:=.*|$)/,charset:/^--?charse?t?(?:=.*|$)/,excludeChars:/^--?exclude(?:=.*|$)/},flags:{includeNums:/^--?(?:n|(?:include-?)?num(?:ber)?s?=?(?:true|1)?)$/,includeSymbols:/^--?(?:y|(?: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))$/,excludeSimilarChars:/^--?(?:S|(?:exclude|disable|no)-?similar-?(?:char(?:acter)?s?)?|similar-?(?:char(?:acter)?s?)?=(?:false|0))$/,strictMode:/^--?s(?:trict)?(?:-?mode)?$/,quietMode:/^--?q(?:uiet)?(?:-?mode)?$/},infoCmds:{help:/^--?h(?:elp)?$/,version:/^--?ve?r?s?i?o?n?$/},version:/^[~^>=]?\d+\.\d+\.\d+$/};let c={nc:"",br:"",by:"",bg:"",bw:"",blk:"",btBG:""};app.config={},process.argv.forEach(s=>{var e,p,a;s.startsWith("-")&&(e=Object.keys(app.regex.paramOptions).find(e=>app.regex.paramOptions[e].test(s)),a=Object.keys(app.regex.flags).find(e=>app.regex.flags[e].test(s)),p=Object.keys(app.regex.infoCmds).find(e=>app.regex.infoCmds[e].test(s)),a?app.config[a]=!0:e?(/=.+/.test(s)||(console.error(`
8
- ${c.br}${app.msgs.prefix_error}: Arg [--${s.replace(/-/g,"")}] ${app.msgs.error_noEqual}.`+c.nc),l(),process.exit(1)),a=s.split("=")[1],app.config[e]=parseInt(a)||a):p||(console.error(`
9
- ${c.br}${app.msgs.prefix_error}: Arg [${s}] ${app.msgs.error_notRecognized}.`+c.nc),console.info(`
10
- ${c.by}${app.msgs.info_validArgs}.`+c.nc),g(["paramOptions","flags","infoCmds"]),process.exit(1)))});for(e of["length","qty"])app.config[e]&&(isNaN(app.config[e])||app.config[e]<1)&&(console.error(`
11
- ${c.br}${app.msgs.prefix_error}: [${e}] ${app.msgs.error_nonPositiveNum}.`+c.nc),l(),process.exit(1));if(process.argv.some(e=>app.regex.infoCmds.help.test(e)))g();else if(process.argv.some(e=>app.regex.infoCmds.version.test(e))){r=p(`npm view ${JSON.stringify(app.name)} version`).toString().trim()||"none";let e,s=process.cwd();for(;"/"!=s;){var t=i.join(s,"package.json");if(a.existsSync(t)){t=require(t);e=(t.dependencies?.[app.name]||t.devDependencies?.[app.name])?.match(app.regex.version)?.[1]||"none";break}s=i.dirname(s)}console.info(`
12
- ${app.msgs.prefix_globalVer}: `+r),console.info(app.msgs.prefix_localVer+": "+e)}else{n=o({length:app.config.length||8,qty:app.config.qty||1,charset:app.config.charset,exclude:app.config.excludeChars,numbers:!!app.config.includeNums,symbols:!!app.config.includeSymbols,lowercase:!app.config.excludeLowerChars,uppercase:!app.config.excludeUpperChars,excludeSimilarChars:!!app.config.excludeSimilarChars,strict:!!app.config.strictMode,verbose:!app.config.quietMode});p=`
13
- ${app.msgs.info_copying}...`,app.config.quietMode||console.info(p),s.writeSync([].concat(n).join("\n"))}function l(){console.info(`
14
- ${app.msgs.info_moreHelp}, ${app.msgs.info_type} ${app.name} --help' ${app.msgs.info_or} ${app.msgs.info_visit}
15
- `+c.bw+app.urls.docs+c.nc)}function g(e=["header","usage","paramOptions","flags","infoCmds"]){app.prefix=""+c.btBG+c.blk+` ${app.name} ${c.nc} `;let s={header:[`
16
- ├ ${app.prefix}${app.msgs.appCopyright||`© ${app.copyrightYear} ${app.author} under the ${app.license} license`}.`,""+app.prefix+app.msgs.prefix_source+": "+app.urls.src],usage:[`
17
- ${c.bw}o ${app.msgs.helpSection_usage}:`+c.nc,` ${c.bw}» `+c.bg+app.cmdFormat+c.nc],paramOptions:[`
18
- ${c.bw}o ${app.msgs.helpSection_paramOptions}:`+c.nc,` --length=n ${app.msgs.optionDesc_length}.`,` --qty=n ${app.msgs.optionDesc_qty}.`,` --charset=chars ${app.msgs.optionDesc_charset}.`,` --exclude=chars ${app.msgs.optionDesc_exclude}.`],flags:[`
19
- ${c.bw}o ${app.msgs.helpSection_flags}:`+c.nc,` -n, --include-numbers ${app.msgs.optionDesc_includeNums}.`,` -y, --include-symbols ${app.msgs.optionDesc_includeSymbols}.`,` -L, --no-lowercase ${app.msgs.optionDesc_noLower}.`,` -U, --no-uppercase ${app.msgs.optionDesc_noUpper}.`,` -S, --no-similar ${app.msgs.optionDesc_noSimilar}.`,` -s, --strict ${app.msgs.optionDesc_strict}.`,` -q, --quiet ${app.msgs.optionDesc_quiet}.`],infoCmds:[`
20
- ${c.bw}o ${app.msgs.helpSection_infoCmds}:`+c.nc," -h, --help "+app.msgs.optionDesc_help,` -v, --version ${app.msgs.optionDesc_version}.`]};e.forEach(n=>s[n]?.forEach(s=>{{var r=/header|usage/.test(n)?1:29;let p=process.stdout.columns||80,a=[],e=s.match(/\S+|\s+/g),o="";e.forEach(e=>{var s=p-(a.length?r:0);o.length+"| ".length+e.length>s&&(a.push(a.length?o.trimStart():o),o=""),o+=e}),a.push(a.length?o.trimStart():o),a.forEach((e,s)=>console.info("| "+(0==s?e:" ".repeat(r)+e)))}})),console.info(`
21
- ${app.msgs.info_moreHelp}, ${app.msgs.info_visit}: `+c.bw+app.urls.docs+c.nc)}})();
7
+ (async()=>{var e,n=process.argv.slice(2),i=(globalThis.env={debugMode:n.some(e=>/^--?debug(?:-?mode)?$/.test(e)),devMode:/[\\/]src(?:[\\/]|$)/i.test(__dirname)},require("node-clipboardy")),o=require(`../generate-pw${env.devMode?"":".min"}.js`).generatePassword,{generateRandomLang:s,getMsgs:r,getSysLang:a}=require(`./lib/language${env.devMode?"":".min"}.js`),p=require(`./lib/log${env.devMode?"":".min"}.js`),t=require(`./lib/settings${env.devMode?"":".min"}.js`);Object.assign(globalThis.app??={},require(`../${env.devMode?"../":"./data/"}app.json`)),app.msgs=await r(env.debugMode?s({excludes:["en"]}):a()),app.urls.docs+="/#-command-line-usage";for(e of n){if(t.controls.init.regex.test(e))return t.initConfigFile();if(t.controls.help.regex.test(e))return p.help();if(t.controls.version.regex.test(e))return p.version()}t.load();r={length:app.config.length,qty:app.config.qty,strength:app.config.mode,charset:app.config.charset,exclude:app.config.excludeChars,numbers:!app.config.excludeNums,symbols:!app.config.excludeSymbols,lowercase:!app.config.excludeLowerChars,uppercase:!app.config.excludeUpperChars,similarChars:app.config.similarChars,strict:!app.config.unstrict,entropy:app.config.entropy,verbose:!app.config.quietMode};i.writeSync([].concat(o(r)).join("\n")),p.ifNotQuiet(`
8
+ ${app.msgs.info_copying}...`)})();
@@ -1,6 +1,6 @@
1
1
  /**
2
- * © 2024–2026 Adam Lui & contributors under the MIT license.
3
- * Source: https://code.generatepw.org
4
- * Documentation: https://docs.generatepw.org
2
+ * © 2024–2026 Adam Lui & contributors under the MIT license.
3
+ * Source: https://github.com/adamlui/js-utils/tree/main/generate-pw/src
4
+ * Documentation: https://github.com/adamlui/js-utils/tree/main/generate-pw/docs
5
5
  */
6
- module.exports={fetch(n){return new Promise((t,e)=>{var r=n.match(/^([^:]+):\/\//)[1];/^https?$/.test(r)||e(new Error(app.msgs.error_invalidURL+".")),require(r).get(n,e=>{let r="";e.on("data",e=>r+=e),e.on("end",()=>t({json:()=>JSON.parse(r)}))}).on("error",e)})},flatten(e,{key:r="message"}={}){var t,n={};for(t in e)n[t]="object"==typeof e[t]&&r in e[t]?e[t][r]:e[t];return n}};
6
+ module.exports={atomicWrite(e,r,t="utf8"){var n=require("path"),a=require("fs"),n=n.join(n.dirname(e),`.${n.basename(e)}.tmp`);a.writeFileSync(n,r,t),a.renameSync(n,e)},fetch(n){return"undefined"==typeof fetch?new Promise((t,e)=>{var r=n.match(/^([^:]+):\/\//)[1];/^https?$/.test(r)||e(new Error(app.msgs.error_invalidURL+".")),require(r).get(n,e=>{let r="";e.on("data",e=>r+=e),e.on("end",()=>t({json:()=>JSON.parse(r)}))}).on("error",e)}):fetch(n)},flatten(e,{key:r="message"}={}){var t,n={};for(t in e)n[t]="object"==typeof e[t]&&r in e[t]?e[t][r]:e[t];return n}};
@@ -1,6 +1,8 @@
1
1
  /**
2
- * © 2024–2026 Adam Lui & contributors under the MIT license.
3
- * Source: https://code.generatepw.org
4
- * Documentation: https://docs.generatepw.org
2
+ * © 2024–2026 Adam Lui & contributors under the MIT license.
3
+ * Source: https://github.com/adamlui/js-utils/tree/main/generate-pw/src
4
+ * Documentation: https://github.com/adamlui/js-utils/tree/main/generate-pw/docs
5
5
  */
6
- module.exports={async getMsgs(r="en"){var e=require(`./data${env.devMode?"":".min"}.js`);let t=e.flatten(require(`../../${env.devMode?"../_locales/en/":"data/"}messages.json`),{key:"message"});if(!r.startsWith("en")){var n=`${app.urls.jsdelivr}@${app.commitHashes.locales}/${app.name}/_locales/`;let s=n+`${r?r.replace("-","_"):"en"}/`+"messages.json",a=0;for(;a<3;)try{t=e.flatten(await(await e.fetch(s)).json(),{key:"message"});break}catch(e){if(3==++a)break;s=r.includes("-")&&1==a?s.replace(/([^_]*)_[^/]*(\/.*)/,"$1$2"):n+"en/messages.json"}}return t},getSysLang(){var e;if("win32"!=process.platform)return((e=process.env).LANG||e.LANGUAGE||e.LC_ALL||e.LC_MESSAGES||e.LC_NAME||"en").split(".")[0];try{return require("child_process").execSync("(Get-Culture).TwoLetterISOLanguageName",{shell:"powershell",encoding:"utf-8"}).trim()}catch(e){return console.error("ERROR loading system language:",e.message),"en"}}};
6
+ let data=require(`./data${env.devMode?"":".min"}.js`),log=require(`./log${env.devMode?"":".min"}.js`);module.exports={generateRandomLang({includes:e=[],excludes:a=[]}={}){let t=require("fs"),s=require("path"),r=e.length?e:(()=>{var e=s.join(__dirname,"..",".cache"),a=s.join(e,"locales.json");if(t.existsSync(a))try{return JSON.parse(t.readFileSync(a,"utf8"))}catch(e){}var r=s.resolve(process.cwd(),"_locales");return t.existsSync(r)?(r=t.readdirSync(r,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name).filter(e=>/^[a-z]{2}(?:_[A-Z]{2})?$/.test(e)),t.mkdirSync(e,{recursive:!0}),data.atomicWrite(a,JSON.stringify(r,null,2)),r):["en"]})(),n=new Set(a),l="en";return(r=r.filter(e=>!n.has(e))).length&&(l=r[Math.floor(Math.random()*r.length)]),log.debug(`
7
+ Random language: ${l}
8
+ `),l},async getMsgs(t="en"){let e=data.flatten(require(`../../${env.devMode?"../_locales/en/":"data/"}messages.json`),{key:"message"});if(!t.startsWith("en")){var s=`${app.urls.jsdelivr}@${app.commitHashes.locales}/${app.name}/_locales/`;let a=s+t.replace("-","_")+"/messages.json",r=0;for(;r<3;)try{log.debug(e=data.flatten(await(await data.fetch(a)).json(),{key:"message"}));break}catch(e){if(2<++r)break;a=t.includes("-")&&1==r?a.replace(/([^_]*)_[^/]*(\/.*)/,"$1$2"):s+"en/messages.json"}}return e},getSysLang(){var e;if("win32"!=process.platform)return((e=process.env).LANG||e.LANGUAGE||e.LC_ALL||e.LC_MESSAGES||e.LC_NAME||"en").split(".")[0];try{return require("child_process").execSync("(Get-Culture).TwoLetterISOLanguageName",{shell:"powershell",encoding:"utf-8"}).trim()}catch(e){return log.error("ERROR loading system language:",e.message),"en"}}};
@@ -0,0 +1,22 @@
1
+ /**
2
+ * © 2024–2026 Adam Lui & contributors under the MIT license.
3
+ * Source: https://github.com/adamlui/js-utils/tree/main/generate-pw/src
4
+ * Documentation: https://github.com/adamlui/js-utils/tree/main/generate-pw/docs
5
+ */
6
+ module.exports={colors:{nc:"",br:"",by:"",bo:"",bg:"",bw:"",blk:"",tlBG:""},configURL(){this.info(`
7
+ ${app.msgs.info_exampleValidConfigFile}: `+app.urls.config)},configURLandExit(...s){this.error(...s),this.configURL(),process.exit(1)},data(s){console.log(`
8
+ `+this.colors.bw+s+this.colors.nc)},debug(s){env.debugMode&&console.log(s)},error(...s){console.error(`
9
+ ${this.colors.br}${app.msgs.prefix_error}:`,...s,this.colors.nc)},errorAndExit(...s){this.error(...s),this.helpCmdAndDocURL(),process.exit(1)},ifNotQuiet(s){app.config.quietMode||console.info(s)},info(s){console.info(`
10
+ `+this.colors.by+s+this.colors.nc)},tip(s){console.info(""+this.colors.by+app.msgs.prefix_tip+": "+s+this.colors.nc)},success(s){console.log(`
11
+ `+this.colors.bg+s+this.colors.nc)},warn(...s){console.warn(`
12
+ ${this.colors.bo}${app.msgs.prefix_warning}:`,...s,this.colors.nc)},help(s=["header","usage","params","flags","cmds"]){app.prefix=""+this.colors.tlBG+this.colors.blk+` ${app.name} ${this.colors.nc} `;let o={header:[`
13
+ ├ ${app.prefix}${app.msgs.appCopyright}.`,""+app.prefix+app.msgs.prefix_source+": "+app.urls.src],usage:[`
14
+ ${this.colors.bw}o ${app.msgs.helpSection_usage}:`+this.colors.nc,` ${this.colors.bw}» `+this.colors.bg+app.cmdFormat+this.colors.nc],params:[`
15
+ ${this.colors.bw}o ${app.msgs.helpSection_params}:`+this.colors.nc,` --length=n ${app.msgs.optionDesc_length}.`,` --qty=n ${app.msgs.optionDesc_qty}.`,` --charset=chars ${app.msgs.optionDesc_charset}.`,` --exclude=chars ${app.msgs.optionDesc_exclude}.`,` --config="path/to/file" ${app.msgs.optionDesc_config}.`],flags:[`
16
+ ${this.colors.bw}o ${app.msgs.helpSection_flags}:`+this.colors.nc,` -w, --weak ${app.msgs.optionDesc_weak}.`,` -b, --basic ${app.msgs.optionDesc_basic}.`,` -t, --strong ${app.msgs.optionDesc_strong}.`,` -N, --no-numbers ${app.msgs.optionDesc_excludeNums}.`,` -Y, --no-symbols ${app.msgs.optionDesc_excludeSymbols}.`,` -L, --no-lowercase ${app.msgs.optionDesc_noLower}.`,` -U, --no-uppercase ${app.msgs.optionDesc_noUpper}.`,` -s, --similar-chars ${app.msgs.optionDesc_similarChars}.`,` -S, --unstrict ${app.msgs.optionDesc_unstrict}.`,` -e, --entropy ${app.msgs.optionDesc_entropy}.`,` -q, --quiet ${app.msgs.optionDesc_quiet}.`],cmds:[`
17
+ ${this.colors.bw}o ${app.msgs.helpSection_cmds}:`+this.colors.nc,` -i, --init ${app.msgs.optionDesc_init}.`,` -h, --help ${app.msgs.optionDesc_help}.`,` -v, --version ${app.msgs.optionDesc_version}.`]};s.forEach(c=>o[c]?.forEach(o=>{{var i=/header|usage/.test(c)?1:29;let e=process.stdout.columns||80,s=o.match(/\S+|\s+/g),p=[],r="";s.forEach(s=>{var o=e-(p.length?i:0);r.length+"| ".length+s.length>o&&(p.push(p.length?r.trimStart():r),r=""),r+=s}),p.push(p.length?r.trimStart():r),p.forEach((s,o)=>console.info("| "+(0==o?s:" ".repeat(i)+s)))}})),console.info(`
18
+ ${app.msgs.info_moreHelp}, ${app.msgs.info_visit}: `+this.colors.bw+app.urls.docs+this.colors.nc)},helpCmdAndDocURL(){console.info(`
19
+ ${app.msgs.info_moreHelp}, ${app.msgs.info_type} ${app.name} --help' ${app.msgs.info_or} ${app.msgs.info_visit}
20
+ `+this.colors.bw+app.urls.docs+this.colors.nc)},version(){var s=require("child_process").execSync(`npm view ${JSON.stringify(app.name)} version`).toString().trim()||"none";let o="none";try{var e=require("path").resolve(process.cwd(),"node_modules",app.name,"package.json");require("fs").existsSync(e)&&(o=require(e).version||"none")}catch(s){this.error("Error reading local package version:",s.message)}console.info(`
21
+ ${app.msgs.prefix_globalVer}: ${s}
22
+ ${app.msgs.prefix_localVer}: `+o)}};
@@ -0,0 +1,8 @@
1
+ /**
2
+ * © 2024–2026 Adam Lui & contributors under the MIT license.
3
+ * Source: https://github.com/adamlui/js-utils/tree/main/generate-pw/src
4
+ * Documentation: https://github.com/adamlui/js-utils/tree/main/generate-pw/docs
5
+ */
6
+ let fs=require("fs"),log=require(`./log${env.devMode?"":".min"}.js`),path=require("path");(globalThis.app??={}).config={},module.exports={configFilename:"generate-pw.config.mjs",controls:{length:{type:"param",defaultVal:12,regex:/^--?length(?:=.*|$)/},qty:{type:"param",defaultVal:1,regex:/^--?qu?a?n?ti?t?y(?:=.*|$)/},weak:{type:"flag",mode:!0,regex:/^--?weak$/},basic:{type:"flag",mode:!0,regex:/^--?basic$/},strong:{type:"flag",mode:!0,regex:/^--?strong$/},charset:{type:"param",regex:/^--?charse?t?(?:=.*|$)/},excludeChars:{type:"param",regex:/^--?exclude(?:=.*|$)/},config:{type:"param",regex:/^--?config(?:=.*|$)/},excludeNums:{type:"flag",regex:/^--?(?:N|(?:exclude|disable|no)-?num(?:ber)?s?=?(?:true|1)?)$/},excludeSymbols:{type:"flag",regex:/^--?(?:Y|(?:exclude|disable|no)-?symbols?=?(?:true|1)?)$/},excludeLowerChars:{type:"flag",regex:/^--?(?:L|(?:exclude|disable|no)-?lower-?(?:case)?|lower-?(?:case)?=(?:false|0))$/},excludeUpperChars:{type:"flag",regex:/^--?(?:U|(?:exclude|disable|no)-?upper-?(?:case)?|upper-?(?:case)?=(?:false|0))$/},similarChars:{type:"flag",regex:/^--?(?:s|(?:include-?)?similar-?chars?=?(?:true|1)?)$/},unstrict:{type:"flag",regex:/^--?(?:S|(?:un-?strict)?(?:-?mode)?)$/},entropy:{type:"flag",regex:/^--?e(?:ntropy)?$/},quietMode:{type:"flag",regex:/^--?q(?:uiet)?(?:-?mode)?$/},init:{type:"cmd",regex:/^-{0,2}i(?:nit)?$/},help:{type:"cmd",regex:/^--?h(?:elp)?$/},version:{type:"cmd",regex:/^--?ve?r?s?i?o?n?$/}},async initConfigFile(r=this.configFilename){var e=path.resolve(process.cwd(),r);if(fs.existsSync(e))return log.warn(app.msgs.warn_configFileExists+":",e);var s=path.resolve(__dirname,"../../"+(env.devMode?"../":"./data/")+r);if(fs.existsSync(s))fs.copyFileSync(s,e);else{s=require(`./data${env.devMode?"":".min"}.js`),r=app.urls.jsdelivr+"/generate-pw/"+r;log.data(app.msgs.info_fetchingRemoteConfigFrom+` ${r}...`);try{var t=await s.fetch(r);if(!t.ok)return log.warn(`${app.msgs.warn_remoteConfigNotFound}: ${r} (${t.status})`);s.atomicWrite(e,await t.text())}catch(e){return log.warn(app.msgs.warn_remoteConfigFailed+`: ${r} `+e.message)}}log.success(app.msgs.info_configFileCreated+`: ${e}
7
+ `),log.tip(app.msgs.tip_editToSetDefaults+"."),log.tip(app.msgs.tip_cliArgsPrioritized+".")},load({args:e=process.argv.slice(2),ctrlKeys:a=Object.keys(this.controls)}={}){a.forEach(e=>{var r=this.controls[e];r.mode||"cmd"==r.type||(app.config[e]??=r.defaultVal??("flag"!=r.type&&""))});let r=null;var s=e.find(e=>this.controls.config.regex.test(e));if(s){/=/.test(s)||log.errorAndExit(`[${s}] `+app.msgs.error_mustIncludePath);s=s.split("=")[1];r=path.isAbsolute(s)?s:path.resolve(process.cwd(),s),fs.existsSync(r)||log.configURLandExit(app.msgs.error_configFileNotFound+":",r)}else for(var t of["mjs","cjs","js"]){t=path.resolve(process.cwd(),this.configFilename.replace(/\.[^.]+$/,"."+t));if(fs.existsSync(t)){r=t;break}}if(r)try{var o=require(r),i=o?.default??o;i&&"object"==typeof i||log.configURLandExit(app.msgs.error_invalidConfigFile+"."),Object.assign(app.config,i)}catch(e){log.configURLandExit(app.msgs.error_failedToLoadConfigFile+":",r,`
8
+ `+e.message)}return e.forEach(r=>{if(!/^[^-]|--?(?:config|debug)/.test(r)){var s=a.find(e=>this.controls[e]?.regex?.test(r)),t=(s||log.errorAndExit(`[${r}] ${app.msgs.error_notRecognized}.`),this.controls[s]);if("cmd"!=t.type){let e="param"!=t.type||r.split("=")[1]?.trim();t.mode?app.config.mode=s.replace(/mode$/i,"").toLowerCase():((t=t.parser)&&(e=t(e),isNaN(e)||e<1)&&log.errorAndExit(`[${s}] ${app.msgs.error_nonPositiveNum}.`),app.config[s]=e)}}}),app.config}};
@@ -5,6 +5,7 @@
5
5
  "copyrightYear": "2024–2026",
6
6
  "cmdFormat": "generate-pw [options|commands]",
7
7
  "urls": {
8
+ "config": "https://github.com/adamlui/js-utils/blob/main/generate-pw/generate-pw.config.mjs",
8
9
  "docs": "https://github.com/adamlui/js-utils/tree/main/generate-pw/docs",
9
10
  "github": "https://github.com/adamlui/js-utils",
10
11
  "jsdelivr": "https://cdn.jsdelivr.net/gh/adamlui/js-utils",
@@ -12,6 +13,6 @@
12
13
  "src": "https://github.com/adamlui/js-utils/tree/main/generate-pw/src"
13
14
  },
14
15
  "commitHashes": {
15
- "locales": "0e30af1"
16
+ "locales": "4bade59"
16
17
  }
17
18
  }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * generate-pw.config.mjs
3
+ *
4
+ * Optional config file for the generate-pw CLI.
5
+ * Copy this file to your project root to set default options.
6
+ * CLI arguments always override these values.
7
+ *
8
+ * Docs: https://github.com/adamlui/js-utils/tree/main/generate-pw/#-command-line-usage
9
+ */
10
+
11
+ export default {
12
+
13
+ // Size params
14
+ length: 12, // length of passwords to generate
15
+ qty: 1, // # of passwords to generate
16
+
17
+ // Generator options
18
+ strength: '', // <'weak'|'basic'|'strong'> apply strength preset
19
+ excludeNums: false, // disallow numbers in password(s)
20
+ excludeSymbols: false, // disallow symbols in password(s)
21
+ excludeLowerChars: false, // disallow lowercase letters in password(s)
22
+ excludeUpperChars: false, // disallow uppercase letters in password(s)
23
+ similarChars: false, // include similar chars (e.g. o,0,O,i,l,1,\|) in password(s)
24
+ unstrict: false, // don't require 1+ char from each allowed charset in password(s)
25
+ charset: '', // only include chars in password(s)
26
+ exclude: '', // exclude chars from password(s)
27
+
28
+ // Info options
29
+ entropy: false, // calculate/log estimated entropy
30
+ quietMode: false // suppress all logging except errors
31
+ }
@@ -2,6 +2,8 @@
2
2
  "appName": { "message": "generate-pw" },
3
3
  "appCopyright": { "message": "© 2024–2026 Adam Lui & contributors under the MIT license" },
4
4
  "prefix_error": { "message": "ERROR" },
5
+ "prefix_tip": { "message": "TIP" },
6
+ "prefix_warning": { "message": "WARNING" },
5
7
  "prefix_globalVer": { "message": "Global version" },
6
8
  "prefix_localVer": { "message": "Local version" },
7
9
  "prefix_source": { "message": "Source" },
@@ -9,27 +11,44 @@
9
11
  "error_notRecognized": { "message": "not recognized" },
10
12
  "error_nonPositiveNum": { "message": "argument can only be > 0" },
11
13
  "error_invalidURL": { "message": "Invalid URL" },
12
- "info_validArgs": { "message": "Valid arguments are below" },
14
+ "error_invalidConfigFile": { "message": "Config file must export an object" },
15
+ "error_configFileNotFound": { "message": "Config file not found" },
16
+ "error_failedToLoadConfigFile": { "message": "Failed to load config file" },
17
+ "error_mustIncludePath": { "message": "must include =path" },
18
+ "warn_configFileExists": { "message": "Config file already exists" },
19
+ "info_exampleValidConfigFile": { "message": "Example valid config file" },
13
20
  "info_copying": { "message": "Copying to clipboard" },
14
21
  "info_moreHelp": { "message": "For more help" },
15
22
  "info_type": { "message": "type" },
16
23
  "info_or": { "message": "or" },
17
24
  "info_visit": { "message": "visit" },
25
+ "info_configFileCreated": { "message": "Config file created" },
26
+ "info_fetchingRemoteConfigFrom": { "message": "Fetching remote config file from" },
27
+ "warn_remoteConfigNotFound": { "message": "Remote config file not found" },
28
+ "warn_remoteConfigFailed": { "message": "Remote config file not found" },
29
+ "tip_editToSetDefaults": { "message": "Edit this file to customize defaults" },
30
+ "tip_cliArgsPrioritized": { "message": "CLI arguments always override these values" },
18
31
  "helpSection_usage": { "message": "Usage" },
19
- "helpSection_paramOptions": { "message": "Parameter options" },
32
+ "helpSection_params": { "message": "Parameter options" },
20
33
  "helpSection_flags": { "message": "Boolean options" },
21
- "helpSection_infoCmds": { "message": "Info commands" },
34
+ "helpSection_cmds": { "message": "Commands" },
22
35
  "optionDesc_length": { "message": "Generate password(s) of n length" },
23
36
  "optionDesc_qty": { "message": "Generate n password(s)" },
37
+ "optionDesc_config": { "message": "Load custom config file" },
24
38
  "optionDesc_charset": { "message": "Only include chars in password(s)" },
25
39
  "optionDesc_exclude": { "message": "Exclude chars from password(s)" },
26
- "optionDesc_includeNums": { "message": "Allow numbers in password(s)" },
27
- "optionDesc_includeSymbols": { "message": "Allow symbols in password(s)" },
40
+ "optionDesc_weak": { "message": "Generate weak password(s)" },
41
+ "optionDesc_basic": { "message": "Generate basic strength password(s)" },
42
+ "optionDesc_strong": { "message": "Generate strong password(s)" },
43
+ "optionDesc_excludeNums": { "message": "Disallow numbers in password(s)" },
44
+ "optionDesc_excludeSymbols": { "message": "Disallow symbols in password(s)" },
28
45
  "optionDesc_noLower": { "message": "Disallow lowercase letters in password(s)" },
29
46
  "optionDesc_noUpper": { "message": "Disallow uppercase letters in password(s)" },
30
- "optionDesc_noSimilar": { "message": "Exclude similar characters in password(s)" },
31
- "optionDesc_strict": { "message": "Require at least one character from each allowed character set in password(s)" },
47
+ "optionDesc_similarChars": { "message": "Include similar characters in password(s)" },
48
+ "optionDesc_unstrict": { "message": "Don't require at least one character from each allowed character set in password(s)" },
49
+ "optionDesc_entropy": { "message": "Calculate/log estimated entropy" },
32
50
  "optionDesc_quiet": { "message": "Suppress all logging except errors" },
33
51
  "optionDesc_help": { "message": "Display help screen" },
52
+ "optionDesc_init": { "message": "Create config file (in project root)" },
34
53
  "optionDesc_version": { "message": "Show version number" }
35
54
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
- * © 2024–2026 Adam Lui & contributors under the MIT license.
3
- * Source: https://code.generatepw.org
4
- * Documentation: https://docs.generatepw.org
2
+ * © 2024–2026 Adam Lui & contributors under the MIT license.
3
+ * Source: https://github.com/adamlui/js-utils/tree/main/generate-pw/src
4
+ * Documentation: https://github.com/adamlui/js-utils/tree/main/generate-pw/docs
5
5
  */
6
- let charsets={lower:"abcdefghijklmnopqrstuvwxyz",upper:"ABCDEFGHIJKLMNOPQRSTUVWXYZ",numbers:"0123456789",symbols:"!@#$%^&*()-_=+[]{}/\\|;:'\",.<>?"};function generatePassword(o={}){var e="generatePassword() » ",t={verbose:!0,length:8,qty:1,charset:"",exclude:"",numbers:!1,symbols:!1,lowercase:!0,uppercase:!0,excludeSimilarChars:!1,strict:!1};if(validateOptions(o,t,"https://docs.generatepw.org/#generatepasswordoptions","generatePassword({ verbose: false, numbers: true })")){if(1<(o={...t,...o}).qty){let{qty:e,...r}=o;return generatePasswords(e,r)}{var n,t="generatePasswords"==generatePassword.caller?.name;o.verbose&&!t&&console.info(e+"Initializing character set...");let r=o.charset?.toString()||(o.numbers?charsets.numbers:"")+(o.symbols?charsets.symbols:"")+(o.lowercase?charsets.lower:"")+(o.uppercase?charsets.upper:""),s=(""==r&&(r=charsets.lower+charsets.upper),o.exclude&&(o.verbose&&!t&&console.info(e+"Removing excluded characters..."),r=r.replace(new RegExp(`[${o.exclude}]`,"g"),"")),o.excludeSimilarChars&&(o.verbose&&!t&&console.info(e+"Excluding similar characters..."),r=r.replace(/[o0Oil1|]/g,"")),o.verbose&&!t&&console.info(e+"Generating password..."),"");for(let e=0;e<o.length;e++){var a=randomInt(0,r.length);s+=r[a]}return o.strict&&(o.verbose&&!t&&console.info(e+"Enforcing strict mode..."),n=["number","symbol","lower","upper"].filter(e=>o[e+"s"]||o[e+"case"]),s=strictify(s,n)),o.verbose&&!t&&(console.info(e+"Password generated!"),"undefined"!=typeof window)&&console.info(e+"Check returned string."),s}}}function generatePasswords(r,s={}){var e="https://docs.generatepw.org/#generatepasswordsqty-options",o="generatePasswords() » ",t={verbose:!0,length:8,charset:"",exclude:"",numbers:!1,symbols:!1,lowercase:!0,uppercase:!0,excludeSimilarChars:!1,strict:!1};if(r=parseInt(r,10),isNaN(r)||r<1)console.error(o+"ERROR: 1st arg <qty> can only be an integer > 0."),console.info(o+"For more help, please visit "+e);else if(validateOptions(s,t,e,"generatePasswords(3, { verbose: false, symbols: true })")){(s={...t,...s}).verbose&&console.info(o+`Generating password${1<r?"s":""}...`);var n=[];for(let e=0;e<r;e++)n.push(generatePassword(s));return s.verbose&&(console.info(o+`Password${1<r?"s":""} generated!`),"undefined"!=typeof window)&&console.info(o+"Check returned array."),n}}function strictify(n,a=["number","symbol","lower","upper"],e={}){var r="https://docs.generatepw.org/#strictifypassword-requiredchartypes-options",i={verbose:!0},l="strictify() » ";if("string"!=typeof n)console.error(l+"ERROR: 1st arg <password> must be a string."),console.info(l+"For more help, please visit "+r);else{var s,o=["number","symbol","lower","upper"];for(s of a=Array.isArray(a)?a:[a])if(!o.includes(s))return console.error(l+`ERROR: 2nd arg \`${s}\` is an invalid character type.`),console.info(l+`Valid character types: [ '${o.join("', '")}' ]`),console.info(l+"Pass one as a string or more as an array, or all types will be required."),void console.info(l+"For more help, please visit "+r);if(validateOptions(e,i,r,"strictify('pa55word', ['symbol', 'upper'], { verbose: false })")){e={...i,...e};let r={},s=[];a.forEach(e=>r[e]=!1);for(let e=0;e<n.length;e++)for(var c of a)(charsets[c]||charsets[c+"s"]).includes(n[e])&&(r[c]=!0,s.push(e));e.verbose&&console.info(l+"Strictifying password...");var d,p=Math.min(n.length,a.length);let o=0,t=n;for(d of a)if(o<p&&!r[d]){let e;for(;e=randomInt(0,n.length),s.includes(e););s.push(e);var f=charsets[d]||charsets[d+"s"];t=t.substring(0,e)+f[randomInt(0,f.length)]+t.substring(e+1),o++}return e.verbose&&(0<o?(console.info(l+"Password is now strict!"),console.info(l+"Check returned string.")):(console.info(l+`Password already includes ${a.join(" + ")} characters!`),console.info(l+"No modifications made."))),t}}}function validateStrength(r,s={}){var e="https://docs.generatepw.org/#validatestrengthpassword-options",o={minLength:8,minLower:1,minUpper:1,minNumber:1,minSymbol:1},t={verbose:!0},n="validateStrength() » ";if("string"!=typeof r)console.error(n+"ERROR: 1st arg <password> must be a string."),console.info(n+"For more help, please visit "+e);else if(validateOptions(s,t,e,"validateStrength('pa55word', { verbose: false })")){(s={...t,...s}).verbose&&console.info(n+"Validating password strength...");var a,i={lower:0,upper:0,number:0,symbol:0};for(a of r)for(var l of Object.keys(i))(charsets[l]||charsets[l+"s"]).includes(a)&&i[l]++;var c,d,p=[];r.length<o.minLength&&p.push(`Make it at least ${o.minLength} characters long.`);for(c of Object.keys(i))i[c]<o["min"+c[0].toUpperCase()+c.slice(1)]&&p.push("Include at least one "+c+`${["upper","lower"].includes(c)?"case letter":""}.`);let e=0;e+=r.length>=o.minLength?20:0;for(d of Object.keys(i))e+=i[d]>=o["min"+d[0].toUpperCase()+d.slice(1)]?20:0;return s.verbose&&(console.info(n+"Password strength validated!"),console.info(n+"Check returned object for score/recommendations.")),{strengthScore:e,recommendations:p,isGood:80<=e}}}function randomInt(e,r){var s;return"undefined"==typeof require?(s=(window.crypto||window.msCrypto)?.getRandomValues(new Uint32Array(1))[0]/4294967295||Math.random(),Math.floor(s*(r-e))+e):require("crypto").randomInt(e,r)}function validateOptions(e,r,s,o){let t=JSON.stringify(r,void 0,2).replace(/"([^"]+)":/g,"$1:").replace(/"/g,"'").replace(/\n\s*/g," "),n=Object.keys(r).join(", "),a=Object.keys(r).filter(e=>"boolean"==typeof r[e]),i=Object.keys(r).filter(e=>Number.isInteger(r[e])),l=`${validateOptions.caller?.name||"validateOptions"}() » `;var c,d=o.split(",").findIndex(e=>e.trim().startsWith("{"))+1;function p(){console.info(l+`Valid options: [ ${n} ]`),console.info(l+"If omitted, default settings are: "+t)}function f(){console.info(l+"For more help, please visit "+s)}if(d+=["st","nd","rd"][d-1]||"th","object"!=typeof e)return console.error(l+`ERROR: ${"0th"==d?"[O":d+" arg [o"}ptions] can only be an object of key/values.`),console.info(l+"Example valid call: "+o),p(),f(),!1;for(c in e){if(!Object.prototype.hasOwnProperty.call(r,c))return console.error(l+`ERROR: \`${c}\` is an invalid option.`),p(),f(),!1;if(a.includes(c)&&"boolean"!=typeof e[c])return console.error(l+`ERROR: [${c}] option can only be \`true\` or \`false\`.`),f(),!1;if(i.includes(c)&&(e[c]=parseInt(e[c],10),isNaN(e[c])||e[c]<1))return console.error(l+`ERROR: [${c}] option can only be an integer > 0.`),f(),!1}return!0}let gpwAliases={generatePassword:["generate","generatepassword","generatepw","generatePw","generatePW","Generate","Generatepassword","GeneratePassword","Generatepw","GeneratePw","GeneratePW"],generatePasswords:["generatepasswords","generatepws","generatePws","generatePWs","generatePWS","Generatepasswords","GeneratePasswords","Generatepws","GeneratePws","GeneratePWs","GeneratePWS"],strictify:["Strictify"],validateStrength:["validate","Validate","validatestrength","Validatestrength","ValidateStrength"]},gpwFuncs={generatePassword:generatePassword,generatePasswords:generatePasswords,strictify:strictify,validateStrength:validateStrength};try{module.exports={...gpwFuncs}}catch(e){}try{window.pw={...gpwFuncs}}catch(e){}for(let r in gpwAliases){try{gpwAliases[r].forEach(e=>module.exports[e]=module.exports[r])}catch(e){}try{gpwAliases[r].forEach(e=>window.pw[e]=window.pw[r])}catch(e){}}
6
+ function generatePassword(s={}){var e={verbose:!0,length:12,qty:1,strength:"",charset:"",exclude:"",numbers:!0,symbols:!0,lowercase:!0,uppercase:!0,similarChars:!1,strict:!0,entropy:!1};if(log.prefix="generatePassword()",validateOptions({options:s,defaultOptions:e,helpURL:"https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/#generatepasswordoptions",exampleCall:"generatePassword({ verbose: false, numbers: true })"})){if(1<(s=(s={...e,...s}).strength&&(e=app.strengthPresets[s.strength.toLowerCase()])?{...s,...e}:s).qty){let{qty:e,...r}=s;return generatePasswords(e,r)}{var a,e="generatePasswords"==generatePassword.caller?.name;s.verbose&&!e&&log.info("Initializing character set...");let r=s.charset?.toString()||(s.numbers?app.charsets.numbers:"")+(s.symbols?app.charsets.symbols:"")+(s.lowercase?app.charsets.lower:"")+(s.uppercase?app.charsets.upper:""),t=(""==r&&(r=app.charsets.lower+app.charsets.upper),s.exclude&&(s.verbose&&!e&&log.info("Removing excluded characters..."),r=r.replace(new RegExp(`[${s.exclude}]`,"g"),"")),s.similarChars||(s.verbose&&!e&&log.info("Excluding similar characters..."),r=r.replace(/[o0Oil1|]/g,"")),s.verbose&&!e&&log.info("Generating password..."),"");for(let e=0;e<s.length;e++){var o=randomInt(0,r.length);t+=r[o]}return s.strict&&(s.verbose&&!e&&log.info("Enforcing strict mode..."),a=["numbers","symbols","lower","upper"].filter(e=>s[e]||s[e+"case"]),t=strictify(t,a)),s.verbose&&(e||(log.info("Password generated!"),"undefined"!=typeof window&&log.info("Check returned string.")),s.entropy)&&(e=(a=r.length)<2?0:Math.log2(a**t.length),log.info(`Estimated entropy: ${e.toFixed(2)} bits (charset length: ${a})`)),t}}}function generatePasswords(r,t={}){var e="https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/#generatepasswordsqty-options",s={verbose:!0,length:12,strength:"",charset:"",exclude:"",numbers:!0,symbols:!0,lowercase:!0,uppercase:!0,similarChars:!1,strict:!0,entropy:!1};if(log.prefix="generatePasswords()",r=parseInt(r,10),isNaN(r)||r<1)return log.error("1st arg <qty> can only be an integer > 0."),log.helpURL(e);if(validateOptions({options:t,defaultOptions:s,helpURL:e,exampleCall:"generatePasswords(3, { verbose: false, symbols: true })"})){(t=(t={...s,...t}).strength&&(e=app.strengthPresets[t.strength.toLowerCase()])?{...t,...e}:t).verbose&&log.info(`Generating password${1<r?"s":""}...`);var a=[];for(let e=0;e<r;e++)a.push(generatePassword(t));return t.verbose&&(log.info(`Password${1<r?"s":""} generated!`),"undefined"!=typeof window)&&log.info("Check returned array."),a}}function strictify(o,n=["numbers","symbols","lower","upper"],e={}){var r="https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/#strictifypassword-requiredchartypes-options",i={verbose:!0};if(log.prefix="strictify()","string"!=typeof o)return log.error("1st arg <password> must be a string."),log.helpURL(r);var t,s=["numbers","symbols","lower","upper"];for(t of n=[].concat(n))if(!s.includes(t))return log.error(`2nd arg \`${t}\` is an invalid character type.`),void log.info([`Valid character types: ['${s.join("', '")}']`,"Pass one as a string or more as an array, or all types will be required.","For more help, please visit "+r].join("\n"));if(validateOptions({options:e,defaultOptions:i,helpURL:r,exampleCall:"strictify('pa55word', ['symbol', 'upper'], { verbose: false })"})){e={...i,...e};let r={},t=[];n.forEach(e=>r[e]=!1);for(let e=0;e<o.length;e++)for(var l of n)!r[l]&&app.charsets[l].includes(o[e])&&(r[l]=!0,t.push(e));e.verbose&&log.info("Strictifying password...");var p,g=Math.min(o.length,n.length);let s=0,a=o;for(p of n)if(s<g&&!r[p]){let e;for(;e=randomInt(0,o.length),t.includes(e););t.push(e);var c=app.charsets[p]||app.charsets[p+"s"];a=a.substring(0,e)+c[randomInt(0,c.length)]+a.substring(e+1),s++}return e.verbose&&(0<s?(log.info("Password is now strict!"),log.info("Check returned string.")):(log.info(`Password already includes ${n.join(" + ")} characters!`),log.info("No modifications made."))),a}}function validateStrength(r,t={}){var e="https://github.com/adamlui/js-utils/tree/main/generate-pw/docs/#validatestrengthpassword-options",s={minlength:12,minLower:1,minUpper:1,minNumber:1,minSymbol:1},a={verbose:!0};if(log.prefix="validateStrength()","string"!=typeof r)return log.error("1st arg <password> must be a string."),log.helpURL(e);if(validateOptions({options:t,defaultOptions:a,helpURL:e,exampleCall:"validateStrength('pa55word', { verbose: false })"})){(t={...a,...t}).verbose&&log.info("Validating password strength...");var o,n={lower:0,upper:0,number:0,symbol:0};for(o of r)for(var i of Object.keys(n))(app.charsets[i]||app.charsets[i+"s"]).includes(o)&&n[i]++;var l,p,g=[];r.length<s.minLength&&g.push(`Make it at least ${s.minLength} characters long.`);for(l of Object.keys(n))n[l]<s["min"+l[0].toUpperCase()+l.slice(1)]&&g.push(`Include at least one ${l}${["upper","lower"].includes(l)?"case letter":""}.`);let e=0;e+=r.length>=s.minLength?20:0;for(p of Object.keys(n))e+=n[p]>=s["min"+p[0].toUpperCase()+p.slice(1)]?20:0;return t.verbose&&(log.info("Password strength validated!"),log.info("Check returned object for score/recommendations.")),{strengthScore:e,recommendations:g,isGood:80<=e}}}function randomInt(e,r){var t;return"undefined"==typeof require?(t=(window.crypto||window.msCrypto)?.getRandomValues(new Uint32Array(1))[0]/4294967295||Math.random(),Math.floor(t*(r-e))+e):require("crypto").randomInt(e,r)}function validateOptions({options:e,defaultOptions:r,helpURL:t,exampleCall:s}){var a=Object.keys(r).filter(e=>"boolean"==typeof r[e]),o=Object.keys(r).filter(e=>Number.isInteger(r[e]));if("object"!=typeof e)return i=s.split(",").findIndex(e=>e.trim().startsWith("{"))+1,i+=["st","nd","rd"][i-1]||"th",log.error(`${"0th"==i?"[O":i+" arg [o"}ptions] can only be an object of key/vals.`),log.info("Example valid call:",s),log.validOptions(r),log.helpURL(t),!1;var n,i=["weak","basic","strong"];if("strength"in e&&e.strength&&!i.includes(e.strength.toLowerCase()))return log.error("[strength] must be one of: "+i.join(", ")),log.helpURL(t),!1;for(n in e){if(!Object.prototype.hasOwnProperty.call(r,n))return log.error(`\`${n}\` is an invalid option.`),log.validOptions(r),log.helpURL(t),!1;if(a.includes(n)&&"boolean"!=typeof e[n])return log.error(`[${n}] option can only be \`true\` or \`false\`.`),log.helpURL(t),!1;if(o.includes(n)&&(e[n]=parseInt(e[n],10),isNaN(e[n])||e[n]<1))return log.error(`[${n}] option can only be an integer > 0.`),log.helpURL(t),!1}return!0}Object.assign(globalThis.app??={},{name:"generate-pw",aliases:{generatePassword:["generate","generatepassword","generatepw","generatePw","generatePW","Generate","Generatepassword","GeneratePassword","Generatepw","GeneratePw","GeneratePW"],generatePasswords:["generatepasswords","generatepws","generatePws","generatePWs","generatePWS","Generatepasswords","GeneratePasswords","Generatepws","GeneratePws","GeneratePWs","GeneratePWS"],strictify:["Strictify"],validateStrength:["validate","Validate","validatestrength","Validatestrength","ValidateStrength"]},charsets:{lower:"abcdefghijklmnopqrstuvwxyz",upper:"ABCDEFGHIJKLMNOPQRSTUVWXYZ",numbers:"0123456789",symbols:"!@#$%^&*()-_=+[]{}/\\|;:'\",.<>?"},strengthPresets:{weak:{length:6,lowercase:!0,uppercase:!1,numbers:!1,symbols:!1,similarChars:!0,strict:!1},basic:{length:8,lowercase:!0,uppercase:!0,numbers:!0,symbols:!1,similarChars:!0,strict:!1},strong:{length:12,lowercase:!0,uppercase:!0,numbers:!0,symbols:!0,similarChars:!1,strict:!0}}});let log={prefix:app.name,error(...e){console.error(this.prefix+" » ERROR:",...e)},helpURL(e=app.urls?.docs){this.info("For more help, please visit",e)},info(...e){console.info(this.prefix+" »",...e)},validOptions(e){var r=Object.keys(e).join(", "),e=JSON.stringify(e,void 0,2).replace(/"([^"]+)":/g,"$1:").replace(/"/g,"'").replace(/\n\s*/g," ");this.info(`Valid options: [${r}]`),this.info("If omitted, default settings are: "+e)}};app.exports={generatePassword:generatePassword,generatePasswords:generatePasswords,strictify:strictify,validateStrength:validateStrength};try{module.exports={...app.exports}}catch(e){}try{window.pw={...app.exports}}catch(e){}for(let r in app.aliases){try{app.aliases[r].forEach(e=>module.exports[e]??=module.exports[r])}catch(e){}try{app.aliases[r].forEach(e=>window.pw[e]??=window.pw[r])}catch(e){}}
package/docs/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  <a id="top"></a>
2
2
 
3
- <a id="top"></a>
4
-
5
3
  <div align="right">
6
4
  <h6>
7
5
  <picture>
@@ -27,12 +25,12 @@
27
25
  <img height=31 src="https://img.shields.io/npm/dm/generate-pw?logo=npm&color=af68ff&logoColor=white&labelColor=464646&style=for-the-badge"></a>
28
26
  <a href="#%EF%B8%8F-mit-license">
29
27
  <img height=31 src="https://img.shields.io/badge/License-MIT-orange.svg?logo=internetarchive&logoColor=white&labelColor=464646&style=for-the-badge"></a>
30
- <a href="https://github.com/adamlui/js-utils/releases/tag/generate-pw-1.5.16">
31
- <img height=31 src="https://img.shields.io/badge/Latest_Build-1.5.16-44cc11.svg?logo=icinga&logoColor=white&labelColor=464646&style=for-the-badge"></a>
28
+ <a href="https://github.com/adamlui/js-utils/releases/tag/generate-pw-2.0.0">
29
+ <img height=31 src="https://img.shields.io/badge/Latest_Build-2.0.0-44cc11.svg?logo=icinga&logoColor=white&labelColor=464646&style=for-the-badge"></a>
32
30
  <a href="https://www.npmjs.com/package/generate-pw?activeTab=code">
33
31
  <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>
34
- <a href="https://github.com/adamlui/js-utils/blob/generate-pw-1.5.16/generate-pw/dist/generate-pw.min.js">
35
- <img height=31 src="https://img.shields.io/github/size/adamlui/js-utils/generate-pw/dist/generate-pw.min.js?branch=generate-pw-1.5.16&label=Minified%20Size&logo=databricks&logoColor=white&labelColor=464646&color=ff69b4&style=for-the-badge"></a>
32
+ <a href="https://github.com/adamlui/js-utils/blob/generate-pw-2.0.0/generate-pw/dist/generate-pw.min.js">
33
+ <img height=31 src="https://img.shields.io/github/size/adamlui/js-utils/generate-pw/dist/generate-pw.min.js?branch=generate-pw-2.0.0&label=Minified%20Size&logo=databricks&logoColor=white&labelColor=464646&color=ff69b4&style=for-the-badge"></a>
36
34
  <a href="https://sonarcloud.io/component_measures?metric=new_vulnerabilities&id=adamlui_js-utils:generate-pw/src/generate-pw.js">
37
35
  <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%2Fsrc%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>
38
36
  <a href="https://github.com/toolleeo/cli-apps#password-managers">
@@ -80,7 +78,7 @@ $ npm install generate-pw
80
78
  #### ECMAScript*:
81
79
 
82
80
  ```js
83
- import pw from 'generate-pw';
81
+ import pw from 'generate-pw'
84
82
  ```
85
83
 
86
84
  #### CommonJS:
@@ -96,14 +94,14 @@ const pw = require('generate-pw')
96
94
  #### <> HTML script tag:
97
95
 
98
96
  ```html
99
- <script src="https://cdn.jsdelivr.net/npm/generate-pw@1.5.16/dist/generate-pw.min.js"></script>
97
+ <script src="https://cdn.jsdelivr.net/npm/generate-pw@2.0.0/dist/generate-pw.min.js"></script>
100
98
  ```
101
99
 
102
100
  #### ES6:
103
101
 
104
102
  ```js
105
103
  (async () => {
106
- await import('https://cdn.jsdelivr.net/npm/generate-pw@1.5.16/dist/generate-pw.min.js')
104
+ await import('https://cdn.jsdelivr.net/npm/generate-pw@2.0.0/dist/generate-pw.min.js')
107
105
  // Your code here...
108
106
  })()
109
107
  ```
@@ -112,7 +110,7 @@ const pw = require('generate-pw')
112
110
 
113
111
  ```js
114
112
  ...
115
- // @require https://cdn.jsdelivr.net/npm/generate-pw@1.5.16/dist/generate-pw.min.js
113
+ // @require https://cdn.jsdelivr.net/npm/generate-pw@2.0.0/dist/generate-pw.min.js
116
114
  // ==/UserScript==
117
115
 
118
116
  // Your code here...
@@ -120,7 +118,7 @@ const pw = require('generate-pw')
120
118
 
121
119
  <br>
122
120
 
123
- **💡 Note:** To always import the latest version (not recommended in production!) remove the `@1.5.16` version tag from the jsDelivr URL: `https://cdn.jsdelivr.net/npm/generate-pw/dist/generate-pw.min.js`
121
+ **💡 Note:** To always import the latest version (not recommended in production!) remove the `@2.0.0` version tag from the jsDelivr URL: `https://cdn.jsdelivr.net/npm/generate-pw/dist/generate-pw.min.js`
124
122
 
125
123
  <br>
126
124
 
@@ -192,7 +190,7 @@ console.log(strictPW) // sample output: 'a!c2ef'
192
190
 
193
191
  **💡 Note:** If no `requiredCharTypes` array is passed, all available types will be required.
194
192
 
195
- Available `requiredCharTypes` are: `['number', 'symbol', 'lower', 'upper']`
193
+ Available `requiredCharTypes` are: `['numbers', 'symbols', 'lower', 'upper']`
196
194
 
197
195
  Available options (passed as object properties):
198
196
 
@@ -247,15 +245,17 @@ Name | Type | Description
247
245
  ----------------------|---------|--------------------------------------------------------------------------------|---------------
248
246
  `verbose` | Boolean | Show logging in console/terminal. | `true`
249
247
  `length` | Integer | Length of password(s). | `8`
250
- `qty`* | Integer | Number of passwords to generate. | `1`
248
+ `qty` | Integer | Number of passwords to generate. | `1`
249
+ `strength` | String | `<'weak'\|'basic'\|'strong'>` Apply strength preset. | `''`
251
250
  `charset` | String | Characters to include in password(s). | `''`
252
251
  `exclude` | String | Characters to exclude from password(s). | `''`
253
252
  `numbers` | Boolean | Allow numbers in password(s). | `false`
254
253
  `symbols` | Boolean | Allow symbols in password(s). | `false`
255
254
  `lowercase` | Boolean | Allow lowercase letters in password(s). | `true`
256
255
  `uppercase` | Boolean | Allow uppercase letters in password(s). | `true`
257
- `excludeSimilarChars` | Boolean | Exclude similar characters (e.g. o,0,O,i,l,1,\|) in password(s). | `false`
258
- `strict` | Boolean | Require at least one character from each allowed character set in password(s). | `false`
256
+ `similarChars` | Boolean | Include similar characters (e.g. o,0,O,i,l,1,\|) in password(s). | `false`
257
+ `strict` | Boolean | Require at least one character from each allowed character set in password(s). | `true`
258
+ `entropy` | Boolean | Calculate/log estimated entropy. | `false`
259
259
 
260
260
  ##### _*Only available in [`generatePassword([options])`](#generatepasswordoptions) since [`generatePasswords(qty[, options])`](#generatepasswordsqty-options) takes a `qty` argument_
261
261
 
@@ -283,22 +283,56 @@ Parameter options:
283
283
  --qty=n Generate n password(s).
284
284
  --charset=chars Only include chars in password(s).
285
285
  --exclude=chars Exclude chars from password(s).
286
+ --config="path/to/file" Load custom config file.
286
287
 
287
288
  Boolean options:
288
- -n, --include-numbers Allow numbers in password(s).
289
- -y, --include-symbols Allow symbols in password(s).
289
+ -w, --weak Generate weak password(s).
290
+ -b, --basic Generate basic strength password(s).
291
+ -t, --strong Generate strong password(s).
292
+ -N, --no-numbers Disallow numbers in password(s).
293
+ -Y, --no-symbols Disallow symbols in password(s).
290
294
  -L, --no-lowercase Disallow lowercase letters in password(s).
291
295
  -U, --no-uppercase Disallow uppercase letters in password(s).
292
- -S, --no-similar Exclude similar characters in password(s).
293
- -s, --strict Require at least one character from each
294
- allowed character set in password(s).
296
+ -S, --similar-chars Include similar characters in password(s).
297
+ -S, --unstrict Don't require at least one character from
298
+ each allowed character set in password(s).
299
+ -e, --entropy Calculate/log estimated entropy.
295
300
  -q, --quiet Suppress all logging except errors.
296
301
 
297
- Info commands:
302
+ Commands:
303
+ -i, --init Create config file (in project root).
298
304
  -h, --help Display help screen.
299
305
  -v, --version Show version number.
300
306
  ```
301
307
 
308
+ #
309
+
310
+ ### Configuration file
311
+
312
+ **generate-pw** can be customized using a `generate-pw.config.mjs` or `generate-pw.config.js` placed in your project root.
313
+
314
+ Example defaults:
315
+
316
+ ```js
317
+ export default {
318
+ length: 12, // length of passwords to generate
319
+ qty: 1, // # of passwords to generate
320
+ strength: '', // <'weak'|'basic'|'strong'> apply strength preset
321
+ charset: '', // only include chars in password(s)
322
+ exclude: '', // exclude chars from password(s)
323
+ excludeNums: false, // disallow numbers in password(s)
324
+ excludeSymbols: false, // disallow symbols in password(s)
325
+ excludeLowerChars: false, // disallow lowercase letters in password(s)
326
+ excludeUpperChars: false, // disallow uppercase letters in password(s)
327
+ similarChars: false, // include similar chars in password(s)
328
+ unstrict: false, // don't require 1+ char from each allowed charset in password(s)
329
+ entropy: false, // calculate/log estimated entropy
330
+ quietMode: false // suppress all logging except errors
331
+ }
332
+ ```
333
+
334
+ 💡 Run `generate-pw init` to generate a template `generate-pw.config.mjs` in your project root.
335
+
302
336
  <br>
303
337
 
304
338
  <img height=6px width="100%" src="https://assets.js-utils.org/images/separators/aqua-gradient.png?v=0d36e26">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generate-pw",
3
- "version": "1.5.16",
3
+ "version": "2.0.0",
4
4
  "description": "Randomly generate, strengthen, and validate cryptographically-secure passwords.",
5
5
  "author": {
6
6
  "name": "Adam Lui",
@@ -42,7 +42,12 @@
42
42
  "doc": "./docs"
43
43
  },
44
44
  "scripts": {
45
- "build": "bash utils/build.sh",
45
+ "build": "node utils/build",
46
+ "build:js": "node utils/build --js",
47
+ "build:data": "node utils/build --data",
48
+ "build:json": "node utils/build --json",
49
+ "debug": "node src/cli --debug",
50
+ "translate": "py utils/translate-en-messages.py",
46
51
  "bump:patch": "bash utils/bump.sh patch",
47
52
  "bump:minor": "bash utils/bump.sh minor",
48
53
  "bump:major": "bash utils/bump.sh major"
@@ -62,10 +67,10 @@
62
67
  "bugs": {
63
68
  "url": "https://github.com/adamlui/js-utils/issues"
64
69
  },
65
- "devDependencies": {
66
- "@adamlui/minify.js": "^2.1.3"
67
- },
68
70
  "dependencies": {
69
71
  "node-clipboardy": "^1.0.3"
72
+ },
73
+ "devDependencies": {
74
+ "@adamlui/minify.js": "^2.2.0"
70
75
  }
71
76
  }