js-generate-password 0.1.9 → 1.1.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/package.json CHANGED
@@ -1,39 +1,59 @@
1
- {
2
- "name": "js-generate-password",
3
- "version": "0.1.9",
4
- "private": false,
5
- "description": "Password Generator for using in javascirpt/typscript based projects.",
6
- "Author": "Ahmad Joya <joya.abn@gmail.com>",
7
- "main": "./dist/index.js",
8
- "bin": "./dist/index.js",
9
- "types": "./src/index.d.ts",
10
- "scripts": {
11
- "test": "echo \"Error: no test specified\" && exit 1"
12
- },
13
- "repository": {
14
- "type": "git",
15
- "url": "git+https://github.com/ahmadjoya/js-generate-password.git"
16
- },
17
- "keywords": [
18
- "generate password",
19
- "js-generate-password",
20
- "password generator",
21
- "password generator",
22
- "random password generator",
23
- "simple password generator",
24
- "easy password generator",
25
- "password",
26
- "generator",
27
- "unique",
28
- "nodejs",
29
- "nextjs",
30
- "react",
31
- "reactjs",
32
- "random"
33
- ],
34
- "license": "ISC",
35
- "bugs": {
36
- "url": "https://github.com/ahmadjoya/js-generate-password/issues"
37
- },
38
- "homepage": "https://github.com/ahmadjoya/js-generate-password#readme"
39
- }
1
+ {
2
+ "name": "js-generate-password",
3
+ "version": "1.1.0",
4
+ "private": false,
5
+ "description": "Tiny, dependency-free, cryptographically secure password generator for JavaScript and TypeScript projects.",
6
+ "author": "Ahmad Joya <joya.abn@gmail.com>",
7
+ "license": "MIT",
8
+ "type": "module",
9
+ "sideEffects": false,
10
+ "main": "./dist/index.cjs",
11
+ "module": "./dist/index.mjs",
12
+ "types": "./dist/index.d.cts",
13
+ "exports": {
14
+ ".": {
15
+ "import": {
16
+ "types": "./dist/index.d.mts",
17
+ "default": "./dist/index.mjs"
18
+ },
19
+ "require": {
20
+ "types": "./dist/index.d.cts",
21
+ "default": "./dist/index.cjs"
22
+ }
23
+ },
24
+ "./package.json": "./package.json"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "src"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/ahmadjoya/generate-password-lite.git"
36
+ },
37
+ "keywords": [
38
+ "password",
39
+ "password-generator",
40
+ "generate-password",
41
+ "generate-password-lite",
42
+ "js-generate-password",
43
+ "random-password",
44
+ "secure-password",
45
+ "crypto",
46
+ "csprng",
47
+ "generator",
48
+ "random",
49
+ "typescript",
50
+ "nodejs",
51
+ "react",
52
+ "nextjs",
53
+ "browser"
54
+ ],
55
+ "bugs": {
56
+ "url": "https://github.com/ahmadjoya/generate-password-lite/issues"
57
+ },
58
+ "homepage": "https://github.com/ahmadjoya/generate-password-lite#readme"
59
+ }
package/src/index.ts CHANGED
@@ -1,39 +1,22 @@
1
- import { generatePswd } from "./lib/password-generator";
2
- import { checkError } from "./lib/check-options-error";
3
- import { processOption } from "./lib/process-options";
4
- import { GenerateOptions } from "./lib/types";
5
-
6
- function GeneratePassword(options?: GenerateOptions) {
7
- let pswd;
8
- if (options) {
9
- options = processOption(options);
10
-
11
- checkError(options);
12
-
13
- pswd = generatePswd(options);
14
- return pswd;
15
- } else {
16
- let opt = {
17
- length: 10,
18
- lowercase: true,
19
- uppercase: true,
20
- numbers: true,
21
- symbols: false,
22
- exclude: "",
23
- minLengthLowercase: 1,
24
- minLengthUppercase: 1,
25
- minLengthNumbers: 1,
26
- minLengthSymbols: 0,
27
- };
28
- options = processOption(opt);
29
-
30
- checkError(options);
31
-
32
- pswd = generatePswd(options);
33
- return pswd;
34
- }
35
- }
36
-
37
- // console.log(GeneratePassword({ minLengthLowercase: 4 }));
38
- export { GeneratePassword };
39
- export default { GeneratePassword };
1
+ import { generatePswd } from './lib/password-generator'
2
+ import { checkError } from './lib/check-options-error'
3
+ import { processOption } from './lib/process-options'
4
+ import { GenerateOptions } from './types'
5
+
6
+ /**
7
+ * Generates a random password using a cryptographically secure random source.
8
+ *
9
+ * @param options - Optional overrides. Every unset option falls back to its
10
+ * documented default.
11
+ * @returns The generated password.
12
+ */
13
+ function GeneratePassword(options: GenerateOptions = {}): string {
14
+ const resolved = processOption(options)
15
+
16
+ checkError(resolved)
17
+
18
+ return generatePswd(resolved)
19
+ }
20
+
21
+ export { GeneratePassword, type GenerateOptions }
22
+ export default GeneratePassword
@@ -1,42 +1,52 @@
1
- import { GenerateOptions } from "./types";
2
-
3
- export const checkError = (options: GenerateOptions) => {
4
- const {
5
- length,
6
- lowercase,
7
- uppercase,
8
- numbers,
9
- symbols,
10
- minLengthLowercase,
11
- minLengthUppercase,
12
- minLengthNumbers,
13
- minLengthSymbols,
14
- } = options;
15
-
16
- //Checking if the sum of minimum characters of Lowercase, uppercase, numbers and special case is greater than the total password length.
17
- if (
18
- minLengthLowercase! +
19
- minLengthUppercase! +
20
- minLengthNumbers! +
21
- minLengthSymbols! >
22
- length!
23
- ) {
24
- throw new Error(
25
- "Sum of Minimum Characters in the passwords greater than the length of the Password to be generated."
26
- );
27
- }
28
-
29
- //Checking if lowercase, uppercase , numbers and special characters have all been set to false.
30
- if (
31
- lowercase == false &&
32
- uppercase == false &&
33
- numbers == false &&
34
- symbols == false
35
- ) {
36
- throw new Error(
37
- "Alphabets, Numbers and Symbols are all set to False. No valid Characters to generate the Password."
38
- );
39
- }
40
- };
41
-
42
- // module.exports = checkError;
1
+ import { ResolvedOptions } from '../types'
2
+
3
+ const assertCount = (value: number, name: string) => {
4
+ if (!Number.isInteger(value) || value < 0) {
5
+ throw new Error(
6
+ `generate-password-lite: \`${name}\` must be a non-negative integer, received ${value}.`
7
+ )
8
+ }
9
+ }
10
+
11
+ export const checkError = (options: ResolvedOptions) => {
12
+ const {
13
+ length,
14
+ lowercase,
15
+ uppercase,
16
+ numbers,
17
+ symbols,
18
+ minLengthLowercase,
19
+ minLengthUppercase,
20
+ minLengthNumbers,
21
+ minLengthSymbols,
22
+ } = options
23
+
24
+ if (!Number.isInteger(length) || length < 1) {
25
+ throw new Error(
26
+ `generate-password-lite: \`length\` must be a positive integer, received ${length}.`
27
+ )
28
+ }
29
+
30
+ assertCount(minLengthLowercase, 'minLengthLowercase')
31
+ assertCount(minLengthUppercase, 'minLengthUppercase')
32
+ assertCount(minLengthNumbers, 'minLengthNumbers')
33
+ assertCount(minLengthSymbols, 'minLengthSymbols')
34
+
35
+ // Lowercase, uppercase, numbers and symbols have all been turned off, so
36
+ // there is no character class left to build a password from.
37
+ if (!lowercase && !uppercase && !numbers && !symbols) {
38
+ throw new Error(
39
+ 'generate-password-lite: `lowercase`, `uppercase`, `numbers` and `symbols` are all false. No valid characters to generate the password from.'
40
+ )
41
+ }
42
+
43
+ // The guaranteed characters alone would already overflow the password.
44
+ const minimumTotal =
45
+ minLengthLowercase + minLengthUppercase + minLengthNumbers + minLengthSymbols
46
+
47
+ if (minimumTotal > length) {
48
+ throw new Error(
49
+ `generate-password-lite: the sum of the minimum character counts (${minimumTotal}) is greater than \`length\` (${length}).`
50
+ )
51
+ }
52
+ }
@@ -1,5 +1,48 @@
1
- export const generateRandomNumbers = (upperLimit: number) => {
2
- return Math.floor(Math.random() * upperLimit);
3
- };
4
-
5
- // module.exports = generateRandomNumbers;
1
+ /**
2
+ * Number of distinct values a Uint32 can hold.
3
+ */
4
+ const UINT32_RANGE = 0x100000000
5
+
6
+ const getWebCrypto = (): Crypto => {
7
+ const webCrypto = globalThis.crypto
8
+
9
+ if (!webCrypto || typeof webCrypto.getRandomValues !== 'function') {
10
+ throw new Error(
11
+ 'generate-password-lite: no Web Crypto implementation found. ' +
12
+ 'A secure random source is required to generate passwords. ' +
13
+ 'Use Node.js 18+, a modern browser, or polyfill `globalThis.crypto`.'
14
+ )
15
+ }
16
+
17
+ return webCrypto
18
+ }
19
+
20
+ /**
21
+ * Returns a cryptographically secure integer in the range [0, upperLimit).
22
+ *
23
+ * Uses rejection sampling so that every value is equally likely. Taking a
24
+ * plain `random % upperLimit` would bias the low end of the range whenever
25
+ * `upperLimit` is not a power of two.
26
+ */
27
+ export const generateRandomNumbers = (upperLimit: number): number => {
28
+ if (!Number.isInteger(upperLimit) || upperLimit <= 0) {
29
+ throw new RangeError(
30
+ `generate-password-lite: upperLimit must be a positive integer, received ${upperLimit}.`
31
+ )
32
+ }
33
+
34
+ const webCrypto = getWebCrypto()
35
+ const buffer = new Uint32Array(1)
36
+
37
+ // Largest multiple of upperLimit that fits in the Uint32 range. Draws at or
38
+ // above this cutoff are discarded rather than folded back into the range.
39
+ const cutoff = Math.floor(UINT32_RANGE / upperLimit) * upperLimit
40
+
41
+ let value: number
42
+ do {
43
+ webCrypto.getRandomValues(buffer)
44
+ value = buffer[0]!
45
+ } while (value >= cutoff)
46
+
47
+ return value % upperLimit
48
+ }
@@ -1,92 +1,92 @@
1
- // const generategenerateRandomNumberss = require("./generate-random-number");
2
- // const shuffleString = require("./shuffle-string");
3
- import { generateRandomNumbers } from "./generate-random-number";
4
- import { shuffleString } from "./shuffle-string";
5
- import { GenerateOptions } from "./types";
6
- export const generatePswd = (options: GenerateOptions) => {
7
- let pswd = "";
8
- let minChar = "";
9
- let {
10
- length,
11
- lowercase,
12
- uppercase,
13
- numbers,
14
- symbols,
15
- minLengthLowercase,
16
- minLengthUppercase,
17
- minLengthNumbers,
18
- minLengthSymbols,
19
- exclude,
20
- } = options;
21
-
22
- let lowercaseChar = "abcdefghijklmnopqrstuvwxyz";
23
- lowercaseChar = removeExceptions(lowercaseChar, exclude!);
24
-
25
- let uppercaseChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
26
- uppercaseChar = removeExceptions(uppercaseChar, exclude!);
27
-
28
- let numberChar = "0123456789";
29
- numberChar = removeExceptions(numberChar, exclude!);
30
-
31
- let specialChar = "!#$%&'()*+,-./:;<=>?@[]^_{|}~";
32
- specialChar = removeExceptions(specialChar, exclude!);
33
-
34
- // Generating a minChar string that holds minimum number of characters on the User Option Selection.
35
- // The minChar variable will store the minimum number of characters for each type - lowercase, uppercase, numbers, and symbols.
36
- // Then, the minChar variable is shuffled to make the password random.
37
-
38
- let upperLimit = lowercaseChar.length;
39
- for (let i = 0; i < minLengthLowercase!; i++) {
40
- minChar += lowercaseChar.charAt(generateRandomNumbers(upperLimit));
41
- }
42
-
43
- upperLimit = uppercaseChar.length;
44
- for (let i = 0; i < minLengthUppercase!; i++) {
45
- minChar += uppercaseChar.charAt(generateRandomNumbers(upperLimit));
46
- }
47
-
48
- upperLimit = numberChar.length;
49
- for (let i = 0; i < minLengthNumbers!; i++) {
50
- minChar += numberChar.charAt(generateRandomNumbers(upperLimit));
51
- }
52
-
53
- upperLimit = specialChar.length;
54
- for (let i = 0; i < minLengthSymbols!; i++) {
55
- minChar += specialChar.charAt(generateRandomNumbers(upperLimit));
56
- }
57
-
58
- //Shuffling the minChar to generate a password that holds minimum criteria.
59
- pswd = pswd + shuffleString(minChar);
60
-
61
- let characterString = "";
62
-
63
- characterString = lowercase
64
- ? characterString + lowercaseChar
65
- : characterString;
66
-
67
- characterString = uppercase
68
- ? characterString + uppercaseChar
69
- : characterString;
70
-
71
- characterString = numbers ? characterString + numberChar : characterString;
72
-
73
- characterString = symbols ? characterString + specialChar : characterString;
74
-
75
- let remainingChar = length! - pswd.length;
76
-
77
- upperLimit = characterString.length;
78
-
79
- for (let i = 0; i < remainingChar; i++) {
80
- pswd += characterString.charAt(generateRandomNumbers(upperLimit));
81
- }
82
- return pswd;
83
- };
84
-
85
- const removeExceptions = (str: string, EXCEPTIONS: string) => {
86
- EXCEPTIONS?.split("").forEach((char) => {
87
- str = str.replace(char, "");
88
- });
89
- return str;
90
- };
91
-
92
- // module.exports = generatePassword;
1
+ import { generateRandomNumbers } from './generate-random-number'
2
+ import { shuffleString } from './shuffle-string'
3
+ import { ResolvedOptions } from '../types'
4
+
5
+ const LOWERCASE_CHARS = 'abcdefghijklmnopqrstuvwxyz'
6
+ const UPPERCASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
7
+ const NUMBER_CHARS = '0123456789'
8
+ const SYMBOL_CHARS = "!#$%&'()*+,-./:;<=>?@[]^_{|}~"
9
+
10
+ /**
11
+ * Drops every excluded character. `String.replace` with a string pattern only
12
+ * removes the first match, so the exclusion is done with a set instead.
13
+ */
14
+ const removeExceptions = (str: string, exclude: string): string => {
15
+ if (!exclude) return str
16
+
17
+ const banned = new Set([...exclude])
18
+ return [...str].filter(char => !banned.has(char)).join('')
19
+ }
20
+
21
+ /**
22
+ * Picks `count` characters from `pool`. Throws when the caller asked for
23
+ * guaranteed characters out of a pool that `exclude` emptied.
24
+ */
25
+ const pickFrom = (pool: string, count: number, name: string): string => {
26
+ if (count === 0) return ''
27
+
28
+ if (pool.length === 0) {
29
+ throw new Error(
30
+ `generate-password-lite: \`exclude\` removed every ${name} character, but \`minLength${name[0]!.toUpperCase()}${name.slice(1)}\` is ${count}.`
31
+ )
32
+ }
33
+
34
+ let picked = ''
35
+ for (let i = 0; i < count; i++) {
36
+ picked += pool.charAt(generateRandomNumbers(pool.length))
37
+ }
38
+ return picked
39
+ }
40
+
41
+ export const generatePswd = (options: ResolvedOptions): string => {
42
+ const {
43
+ length,
44
+ lowercase,
45
+ uppercase,
46
+ numbers,
47
+ symbols,
48
+ minLengthLowercase,
49
+ minLengthUppercase,
50
+ minLengthNumbers,
51
+ minLengthSymbols,
52
+ exclude,
53
+ } = options
54
+
55
+ const lowercaseChar = removeExceptions(LOWERCASE_CHARS, exclude)
56
+ const uppercaseChar = removeExceptions(UPPERCASE_CHARS, exclude)
57
+ const numberChar = removeExceptions(NUMBER_CHARS, exclude)
58
+ const symbolChar = removeExceptions(SYMBOL_CHARS, exclude)
59
+
60
+ // Characters that must appear, so that the password satisfies every
61
+ // `minLength*` the caller asked for.
62
+ const guaranteed =
63
+ pickFrom(lowercaseChar, minLengthLowercase, 'lowercase') +
64
+ pickFrom(uppercaseChar, minLengthUppercase, 'uppercase') +
65
+ pickFrom(numberChar, minLengthNumbers, 'numbers') +
66
+ pickFrom(symbolChar, minLengthSymbols, 'symbols')
67
+
68
+ // Every character the enabled classes still allow after exclusions.
69
+ const characterPool =
70
+ (lowercase ? lowercaseChar : '') +
71
+ (uppercase ? uppercaseChar : '') +
72
+ (numbers ? numberChar : '') +
73
+ (symbols ? symbolChar : '')
74
+
75
+ const remaining = length - guaranteed.length
76
+
77
+ if (remaining > 0 && characterPool.length === 0) {
78
+ throw new Error(
79
+ 'generate-password-lite: `exclude` removed every character of every enabled class. No valid characters to generate the password from.'
80
+ )
81
+ }
82
+
83
+ let filler = ''
84
+ for (let i = 0; i < remaining; i++) {
85
+ filler += characterPool.charAt(generateRandomNumbers(characterPool.length))
86
+ }
87
+
88
+ // Shuffle the whole password, not just the guaranteed part. Shuffling only
89
+ // the guaranteed characters would pin them to the leading positions and make
90
+ // the layout of the password predictable.
91
+ return shuffleString(guaranteed + filler)
92
+ }
@@ -1,61 +1,34 @@
1
- import { GenerateOptions } from "./types";
2
-
3
- export const processOption = (options: GenerateOptions) => {
4
- {
5
- options.length = typeof options.length == "undefined" ? 10 : options.length;
6
-
7
- options.lowercase =
8
- typeof options.lowercase == "undefined" ? true : options.lowercase;
9
-
10
- options.uppercase =
11
- typeof options.uppercase == "undefined" ? true : options.uppercase;
12
-
13
- options.numbers =
14
- typeof options.numbers == "undefined" ? true : options.numbers;
15
-
16
- options.symbols =
17
- typeof options.symbols == "undefined" ? false : options.symbols;
18
-
19
- if (options.lowercase) {
20
- options.minLengthLowercase =
21
- typeof options.minLengthLowercase == "undefined"
22
- ? 1
23
- : options.minLengthLowercase;
24
- } else {
25
- options.minLengthLowercase = 0;
26
- }
27
-
28
- if (options.uppercase) {
29
- options.minLengthUppercase =
30
- typeof options.minLengthUppercase == "undefined"
31
- ? 1
32
- : options.minLengthUppercase;
33
- } else {
34
- options.minLengthUppercase = 0;
35
- }
36
-
37
- if (options.numbers) {
38
- options.minLengthNumbers =
39
- typeof options.minLengthNumbers == "undefined"
40
- ? 1
41
- : options.minLengthNumbers;
42
- } else {
43
- options.minLengthNumbers = 0;
44
- }
45
-
46
- if (options.symbols) {
47
- options.minLengthSymbols =
48
- typeof options.minLengthSymbols == "undefined"
49
- ? 1
50
- : options.minLengthSymbols;
51
- } else {
52
- options.minLengthSymbols = 0;
53
- }
54
-
55
- options.exclude =
56
- typeof options.exclude == "undefined" ? "" : options.exclude;
57
- }
58
- return options;
59
- };
60
-
61
- // module.exports = processOption;
1
+ import { GenerateOptions, ResolvedOptions } from '../types'
2
+
3
+ const withDefault = <T>(value: T | undefined, fallback: T): T =>
4
+ typeof value === 'undefined' ? fallback : value
5
+
6
+ /**
7
+ * Fills in every unset option. Returns a new object so that the caller's
8
+ * options are never mutated.
9
+ */
10
+ export const processOption = (options: GenerateOptions): ResolvedOptions => {
11
+ const lowercase = withDefault(options.lowercase, true)
12
+ const uppercase = withDefault(options.uppercase, true)
13
+ const numbers = withDefault(options.numbers, true)
14
+ const symbols = withDefault(options.symbols, false)
15
+
16
+ // A disabled character class can never contribute a minimum, regardless of
17
+ // what the caller passed in.
18
+ return {
19
+ length: withDefault(options.length, 10),
20
+ lowercase,
21
+ uppercase,
22
+ numbers,
23
+ symbols,
24
+ exclude: withDefault(options.exclude, ''),
25
+ minLengthLowercase: lowercase
26
+ ? withDefault(options.minLengthLowercase, 1)
27
+ : 0,
28
+ minLengthUppercase: uppercase
29
+ ? withDefault(options.minLengthUppercase, 1)
30
+ : 0,
31
+ minLengthNumbers: numbers ? withDefault(options.minLengthNumbers, 1) : 0,
32
+ minLengthSymbols: symbols ? withDefault(options.minLengthSymbols, 1) : 0,
33
+ }
34
+ }
@@ -1,14 +1,21 @@
1
- export const shuffleString = (str: string) => {
2
- let arr = str.split("");
3
- let n = arr.length;
4
-
5
- for (let i = n - 1; i > 0; i--) {
6
- let j = Math.floor(Math.random() * (i + 1));
7
- let tmp = arr[i];
8
- arr[i] = arr[j];
9
- arr[j] = tmp;
10
- }
11
- return arr.join("");
12
- };
13
-
14
- // module.exports = shuffleString;
1
+ import { generateRandomNumbers } from './generate-random-number'
2
+
3
+ /**
4
+ * Fisher-Yates shuffle backed by the same cryptographically secure source
5
+ * used to pick the characters themselves.
6
+ *
7
+ * Spreads across code points rather than UTF-16 units so that a surrogate
8
+ * pair supplied through `exclude` handling is never split in half.
9
+ */
10
+ export const shuffleString = (str: string): string => {
11
+ const chars = [...str]
12
+
13
+ for (let i = chars.length - 1; i > 0; i--) {
14
+ const j = generateRandomNumbers(i + 1)
15
+ const tmp = chars[i]!
16
+ chars[i] = chars[j]!
17
+ chars[j] = tmp
18
+ }
19
+
20
+ return chars.join('')
21
+ }