@systemix/password 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,17 @@
1
1
  # @systemix/password
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@systemix/password.svg)](https://www.npmjs.com/package/@systemix/password)
4
+
3
5
  `@systemix/password` is a customizable, lightweight, and cryptographically secure password generator for JavaScript and TypeScript. Built for maximum security, it relies entirely on Node.js built-in `crypto` modules, ensuring zero external dependencies and a low supply-chain risk profile.
4
6
 
5
7
  ## Features
6
8
 
7
- - **Secure**: Uses `crypto.getRandomValues()` for cryptographically strong random data.
9
+ - **Secure**: Uses `crypto.getRandomValues()` with bias-free distribution for maximum security.
10
+ - **Character Guarantees**: Ensure a minimum number of uppercase, lowercase, numbers, or symbols.
11
+ - **Custom Charsets**: Pass your own character sets for generation.
12
+ - **Entropy Calculation**: Built-in utility to calculate technical password entropy.
8
13
  - **Zero Dependencies**: Purely built with Node.js built-ins.
9
14
  - **TypeScript Ready**: Full type definitions included.
10
- - **Customizable**: Control length, character sets (numbers, symbols, uppercase, lowercase), and exclude similar characters.
11
- - **Validation**: Built-in strict validation for generation options.
12
15
 
13
16
  ## Installation
14
17
 
@@ -22,6 +25,14 @@ or
22
25
  npm install @systemix/password
23
26
  ```
24
27
 
28
+ From GitHub Packages (add to `.npmrc`: `@shahadathhs:registry=https://npm.pkg.github.com`):
29
+
30
+ ```bash
31
+ pnpm add @shahadathhs/password
32
+ ```
33
+
34
+ Use `@shahadathhs/password` in imports.
35
+
25
36
  ## Usage
26
37
 
27
38
  ### Simple Example
@@ -33,37 +44,58 @@ const password = generatePassword({
33
44
  length: 16,
34
45
  useNumbers: true,
35
46
  useSymbols: true,
36
- useUppercase: true,
37
- useLowercase: true,
38
47
  });
39
48
 
40
49
  console.log(password); // Example: "Z#kM@4p*J!h2X&b7"
41
50
  ```
42
51
 
43
- ### Advanced Configuration
52
+ ### Guarantees and Character Sets
44
53
 
45
54
  ```typescript
46
- const password = generatePassword({
55
+ const securePass = generatePassword({
47
56
  length: 12,
48
- useNumbers: true,
49
- excludeSimilarCharacters: true, // Excludes: i, l, 1, L, o, 0, O
50
- exclude: 'abc', // Manually exclude specific characters
57
+ minNumbers: 2,
58
+ minSymbols: 2,
59
+ useUppercase: true,
60
+ customSymbols: '!@#$%^&*', // Use specific symbols only
51
61
  });
52
62
  ```
53
63
 
64
+ ### Entropy Calculation
65
+
66
+ ```typescript
67
+ import { calculatePasswordEntropy } from '@systemix/password';
68
+
69
+ const entropy = calculatePasswordEntropy(12, 62); // length, charset size
70
+ console.log(`Entropy: ${entropy} bits`);
71
+ ```
72
+
54
73
  ## API Reference
55
74
 
56
- ### `generatePassword(props?: PasswordProps): string`
57
-
58
- | Property | Type | Default | Description |
59
- | :------------------------- | :-------- | :------ | :----------------------------------- |
60
- | `length` | `number` | `8` | Password length (1 to 50). |
61
- | `useNumbers` | `boolean` | `true` | Include 0-9. |
62
- | `useUppercase` | `boolean` | `true` | Include A-Z. |
63
- | `useLowercase` | `boolean` | `true` | Include a-z. |
64
- | `useSymbols` | `boolean` | `false` | Include special characters. |
65
- | `excludeSimilarCharacters` | `boolean` | `false` | Exclude visually similar characters. |
66
- | `exclude` | `string` | `""` | Specific characters to exclude. |
75
+ ### `generatePassword(props?: PasswordProps): string | string[]`
76
+
77
+ | Property | Type | Default | Description |
78
+ | :------------------------- | :-------- | :-------- | :------------------------------------ |
79
+ | `length` | `number` | `12` | Password length (1 to 100). |
80
+ | `useNumbers` | `boolean` | `true` | Include 0-9. |
81
+ | `useUppercase` | `boolean` | `true` | Include A-Z. |
82
+ | `useLowercase` | `boolean` | `true` | Include a-z. |
83
+ | `useSymbols` | `boolean` | `false` | Include special characters. |
84
+ | `minNumbers` | `number` | `0` | Minimum numbers to include. |
85
+ | `minUppercase` | `number` | `0` | Minimum uppercase letters to include. |
86
+ | `minLowercase` | `number` | `0` | Minimum lowercase letters to include. |
87
+ | `minSymbols` | `number` | `0` | Minimum symbols to include. |
88
+ | `customNumbers` | `string` | _0-9_ | Custom number set. |
89
+ | `customUppercase` | `string` | _A-Z_ | Custom uppercase set. |
90
+ | `customLowercase` | `string` | _a-z_ | Custom lowercase set. |
91
+ | `customSymbols` | `string` | _Special_ | Custom symbol set. |
92
+ | `excludeSimilarCharacters` | `boolean` | `false` | Exclude visually similar characters. |
93
+ | `exclude` | `string` | `""` | Specific characters to exclude. |
94
+ | `count` | `number` | `1` | Number of passwords to generate. |
95
+
96
+ ### `calculatePasswordEntropy(length: number, charsetSize: number): number`
97
+
98
+ Calculates the theoretical entropy of a password (H = L \* log2(N)).
67
99
 
68
100
  ## License
69
101
 
package/dist/index.cjs CHANGED
@@ -22,6 +22,7 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  GeneratePasswordPropsEnum: () => GeneratePasswordPropsEnum,
24
24
  allOptionsFalseError: () => allOptionsFalseError,
25
+ calculatePasswordEntropy: () => calculatePasswordEntropy,
25
26
  countError: () => countError,
26
27
  excludeError: () => excludeError,
27
28
  excludeSimilarCharactersError: () => excludeSimilarCharactersError,
@@ -45,6 +46,14 @@ var GeneratePasswordPropsEnum = /* @__PURE__ */ ((GeneratePasswordPropsEnum2) =>
45
46
  GeneratePasswordPropsEnum2["EXCLUDE_SIMILAR_CHARACTERS"] = "excludeSimilarCharacters";
46
47
  GeneratePasswordPropsEnum2["EXCLUDE"] = "exclude";
47
48
  GeneratePasswordPropsEnum2["COUNT"] = "count";
49
+ GeneratePasswordPropsEnum2["MIN_NUMBERS"] = "minNumbers";
50
+ GeneratePasswordPropsEnum2["MIN_UPPERCASE"] = "minUppercase";
51
+ GeneratePasswordPropsEnum2["MIN_LOWERCASE"] = "minLowercase";
52
+ GeneratePasswordPropsEnum2["MIN_SYMBOLS"] = "minSymbols";
53
+ GeneratePasswordPropsEnum2["CUSTOM_LOWERCASE"] = "customLowercase";
54
+ GeneratePasswordPropsEnum2["CUSTOM_UPPERCASE"] = "customUppercase";
55
+ GeneratePasswordPropsEnum2["CUSTOM_NUMBERS"] = "customNumbers";
56
+ GeneratePasswordPropsEnum2["CUSTOM_SYMBOLS"] = "customSymbols";
48
57
  return GeneratePasswordPropsEnum2;
49
58
  })(GeneratePasswordPropsEnum || {});
50
59
 
@@ -89,14 +98,46 @@ function generatePasswordPropValidation(props) {
89
98
  useSymbols,
90
99
  excludeSimilarCharacters,
91
100
  exclude,
92
- count
101
+ count,
102
+ minNumbers,
103
+ minUppercase,
104
+ minLowercase,
105
+ minSymbols,
106
+ customNumbers,
107
+ customUppercase,
108
+ customLowercase,
109
+ customSymbols
93
110
  } = props;
94
111
  if (typeof length !== "number" || length < 1) {
95
112
  throw new Error(lengthError.invalidLength);
96
113
  }
97
- if (length > 50) {
114
+ if (length > 100) {
98
115
  throw new Error(lengthError.lengthTooLarge);
99
116
  }
117
+ const checkMin = (val, name) => {
118
+ if (val !== void 0 && (typeof val !== "number" || val < 0)) {
119
+ throw new Error(`${name} must be a non-negative number.`);
120
+ }
121
+ };
122
+ checkMin(minNumbers, "minNumbers");
123
+ checkMin(minUppercase, "minUppercase");
124
+ checkMin(minLowercase, "minLowercase");
125
+ checkMin(minSymbols, "minSymbols");
126
+ const totalMin = (minNumbers ?? 0) + (minUppercase ?? 0) + (minLowercase ?? 0) + (minSymbols ?? 0);
127
+ if (totalMin > length) {
128
+ throw new Error(
129
+ "Total minimum character requirements exceed requested password length."
130
+ );
131
+ }
132
+ const checkString = (val, name) => {
133
+ if (val !== void 0 && typeof val !== "string") {
134
+ throw new Error(`${name} must be a string.`);
135
+ }
136
+ };
137
+ checkString(customNumbers, "customNumbers");
138
+ checkString(customUppercase, "customUppercase");
139
+ checkString(customLowercase, "customLowercase");
140
+ checkString(customSymbols, "customSymbols");
100
141
  if (typeof useNumbers !== "boolean") {
101
142
  throw new Error(useNumbersError);
102
143
  }
@@ -127,9 +168,32 @@ function generatePasswordPropValidation(props) {
127
168
  }
128
169
 
129
170
  // src/functions/generate.function.ts
171
+ function calculatePasswordEntropy(length, charsetSize) {
172
+ if (length <= 0 || charsetSize <= 0) return 0;
173
+ return length * Math.log2(charsetSize);
174
+ }
175
+ function getRandomInt(max) {
176
+ if (max <= 1) return 0;
177
+ const array = new Uint32Array(1);
178
+ const range = Math.floor(4294967295 / max) * max;
179
+ let randomValue;
180
+ do {
181
+ import_node_crypto.webcrypto.getRandomValues(array);
182
+ randomValue = array[0];
183
+ } while (randomValue >= range);
184
+ return randomValue % max;
185
+ }
186
+ function shuffleString(str) {
187
+ const arr = str.split("");
188
+ for (let i = arr.length - 1; i > 0; i--) {
189
+ const j = getRandomInt(i + 1);
190
+ [arr[i], arr[j]] = [arr[j], arr[i]];
191
+ }
192
+ return arr.join("");
193
+ }
130
194
  function generatePassword(props = {}) {
131
195
  const {
132
- length = 8,
196
+ length = 12,
133
197
  useNumbers = true,
134
198
  useUppercase = true,
135
199
  useLowercase = true,
@@ -137,57 +201,76 @@ function generatePassword(props = {}) {
137
201
  excludeSimilarCharacters = false,
138
202
  exclude = "",
139
203
  count = 1,
140
- ...extraProps
204
+ minUppercase = 0,
205
+ minLowercase = 0,
206
+ minNumbers = 0,
207
+ minSymbols = 0,
208
+ customUppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
209
+ customLowercase = "abcdefghijklmnopqrstuvwxyz",
210
+ customNumbers = "0123456789",
211
+ customSymbols = "@#$%^&*()_+=<>?/|"
141
212
  } = props;
142
- const allProps = {
143
- length,
144
- useNumbers,
145
- useUppercase,
146
- useLowercase,
147
- useSymbols,
148
- excludeSimilarCharacters,
149
- exclude,
150
- count,
151
- ...extraProps
152
- };
153
- generatePasswordPropValidation(allProps);
154
- const lowercaseChars = "abcdefghijklmnopqrstuvwxyz";
155
- const uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
156
- const numberChars = "0123456789";
157
- const symbolChars = "@#$%^&*()_+=<>?/|";
213
+ generatePasswordPropValidation(props);
158
214
  const similarChars = "il1Lo0O";
159
- let charset = "";
160
- const charsetOptions = [
161
- { condition: useLowercase, value: lowercaseChars },
162
- { condition: useUppercase, value: uppercaseChars },
163
- { condition: useNumbers, value: numberChars },
164
- { condition: useSymbols, value: symbolChars }
165
- ];
166
- charset = charsetOptions.reduce((acc, { condition, value }) => {
167
- return condition ? acc + value : acc;
168
- }, "");
169
- if (excludeSimilarCharacters) {
170
- charset = charset.split("").filter((char) => !similarChars.includes(char)).join("");
171
- }
172
- if (exclude) {
173
- charset = charset.split("").filter((char) => !exclude.includes(char)).join("");
174
- }
175
- if (charset.length === 0) {
215
+ const filterCharset = (base) => {
216
+ let result = base;
217
+ if (excludeSimilarCharacters) {
218
+ result = result.split("").filter((char) => !similarChars.includes(char)).join("");
219
+ }
220
+ if (exclude) {
221
+ result = result.split("").filter((char) => !exclude.includes(char)).join("");
222
+ }
223
+ return result;
224
+ };
225
+ const charsets = {
226
+ uppercase: filterCharset(customUppercase),
227
+ lowercase: filterCharset(customLowercase),
228
+ numbers: filterCharset(customNumbers),
229
+ symbols: filterCharset(customSymbols)
230
+ };
231
+ const activeCharsets = [];
232
+ if (useUppercase && charsets.uppercase)
233
+ activeCharsets.push(charsets.uppercase);
234
+ if (useLowercase && charsets.lowercase)
235
+ activeCharsets.push(charsets.lowercase);
236
+ if (useNumbers && charsets.numbers) activeCharsets.push(charsets.numbers);
237
+ if (useSymbols && charsets.symbols) activeCharsets.push(charsets.symbols);
238
+ const fullCharset = activeCharsets.join("");
239
+ if (fullCharset.length === 0) {
176
240
  throw new Error("No valid characters to generate password.");
177
241
  }
178
242
  const generateSinglePassword = () => {
179
- const randomIndices = new Uint32Array(length);
180
- import_node_crypto.webcrypto.getRandomValues(randomIndices);
181
- const password = Array.from(randomIndices).map((index) => charset[index % charset.length]).join("");
182
- return password;
243
+ const passwordChars = [];
244
+ const addGuaranteed = (count2, set) => {
245
+ if (count2 > 0 && set.length > 0) {
246
+ for (let i = 0; i < count2; i++) {
247
+ passwordChars.push(set[getRandomInt(set.length)]);
248
+ }
249
+ }
250
+ };
251
+ if (useUppercase) addGuaranteed(minUppercase, charsets.uppercase);
252
+ if (useLowercase) addGuaranteed(minLowercase, charsets.lowercase);
253
+ if (useNumbers) addGuaranteed(minNumbers, charsets.numbers);
254
+ if (useSymbols) addGuaranteed(minSymbols, charsets.symbols);
255
+ if (passwordChars.length > length) {
256
+ throw new Error(
257
+ "Minimum character requirements exceed requested password length."
258
+ );
259
+ }
260
+ const remainingLength = length - passwordChars.length;
261
+ for (let i = 0; i < remainingLength; i++) {
262
+ passwordChars.push(fullCharset[getRandomInt(fullCharset.length)]);
263
+ }
264
+ return shuffleString(passwordChars.join(""));
183
265
  };
184
266
  if (count === 1) return generateSinglePassword();
185
- return Array.from({ length: count }, generateSinglePassword);
267
+ return Array.from({ length: count }, () => generateSinglePassword());
186
268
  }
187
269
  // Annotate the CommonJS export names for ESM import in node:
188
270
  0 && (module.exports = {
189
271
  GeneratePasswordPropsEnum,
190
272
  allOptionsFalseError,
273
+ calculatePasswordEntropy,
191
274
  countError,
192
275
  excludeError,
193
276
  excludeSimilarCharactersError,
package/dist/index.d.cts CHANGED
@@ -6,14 +6,33 @@ declare enum GeneratePasswordPropsEnum {
6
6
  USE_SYMBOLS = "useSymbols",
7
7
  EXCLUDE_SIMILAR_CHARACTERS = "excludeSimilarCharacters",
8
8
  EXCLUDE = "exclude",
9
- COUNT = "count"
9
+ COUNT = "count",
10
+ MIN_NUMBERS = "minNumbers",
11
+ MIN_UPPERCASE = "minUppercase",
12
+ MIN_LOWERCASE = "minLowercase",
13
+ MIN_SYMBOLS = "minSymbols",
14
+ CUSTOM_LOWERCASE = "customLowercase",
15
+ CUSTOM_UPPERCASE = "customUppercase",
16
+ CUSTOM_NUMBERS = "customNumbers",
17
+ CUSTOM_SYMBOLS = "customSymbols"
10
18
  }
11
19
 
12
20
  type GeneratePasswordFunctionProps = {
13
- [key in GeneratePasswordPropsEnum]?: key extends GeneratePasswordPropsEnum.LENGTH | GeneratePasswordPropsEnum.COUNT ? number : key extends GeneratePasswordPropsEnum.EXCLUDE ? string : key extends GeneratePasswordPropsEnum.USE_NUMBERS | GeneratePasswordPropsEnum.USE_UPPERCASE | GeneratePasswordPropsEnum.USE_LOWERCASE | GeneratePasswordPropsEnum.USE_SYMBOLS | GeneratePasswordPropsEnum.EXCLUDE_SIMILAR_CHARACTERS ? boolean : never;
21
+ [key in GeneratePasswordPropsEnum]?: key extends GeneratePasswordPropsEnum.LENGTH | GeneratePasswordPropsEnum.COUNT | GeneratePasswordPropsEnum.MIN_NUMBERS | GeneratePasswordPropsEnum.MIN_UPPERCASE | GeneratePasswordPropsEnum.MIN_LOWERCASE | GeneratePasswordPropsEnum.MIN_SYMBOLS ? number : key extends GeneratePasswordPropsEnum.EXCLUDE | GeneratePasswordPropsEnum.CUSTOM_LOWERCASE | GeneratePasswordPropsEnum.CUSTOM_UPPERCASE | GeneratePasswordPropsEnum.CUSTOM_NUMBERS | GeneratePasswordPropsEnum.CUSTOM_SYMBOLS ? string : key extends GeneratePasswordPropsEnum.USE_NUMBERS | GeneratePasswordPropsEnum.USE_UPPERCASE | GeneratePasswordPropsEnum.USE_LOWERCASE | GeneratePasswordPropsEnum.USE_SYMBOLS | GeneratePasswordPropsEnum.EXCLUDE_SIMILAR_CHARACTERS ? boolean : never;
14
22
  };
15
23
 
16
- declare function generatePassword(props?: GeneratePasswordFunctionProps): string | string[];
24
+ interface ExtendedGeneratePasswordFunctionProps extends GeneratePasswordFunctionProps {
25
+ minUppercase?: number;
26
+ minLowercase?: number;
27
+ minNumbers?: number;
28
+ minSymbols?: number;
29
+ customUppercase?: string;
30
+ customLowercase?: string;
31
+ customNumbers?: string;
32
+ customSymbols?: string;
33
+ }
34
+ declare function calculatePasswordEntropy(length: number, charsetSize: number): number;
35
+ declare function generatePassword(props?: ExtendedGeneratePasswordFunctionProps): string | string[];
17
36
 
18
37
  declare const lengthError: {
19
38
  invalidLength: string;
@@ -32,4 +51,4 @@ declare const countError: {
32
51
  declare const unwantedPropsError: (unwantedProps: string[]) => string;
33
52
  declare const allOptionsFalseError = "At least one of the options must be true.";
34
53
 
35
- export { type GeneratePasswordFunctionProps, GeneratePasswordPropsEnum, allOptionsFalseError, countError, excludeError, excludeSimilarCharactersError, generatePassword, lengthError, unwantedPropsError, useLowercaseError, useNumbersError, useSymbolsError, useUppercaseError };
54
+ export { type ExtendedGeneratePasswordFunctionProps, type GeneratePasswordFunctionProps, GeneratePasswordPropsEnum, allOptionsFalseError, calculatePasswordEntropy, countError, excludeError, excludeSimilarCharactersError, generatePassword, lengthError, unwantedPropsError, useLowercaseError, useNumbersError, useSymbolsError, useUppercaseError };
package/dist/index.d.ts CHANGED
@@ -6,14 +6,33 @@ declare enum GeneratePasswordPropsEnum {
6
6
  USE_SYMBOLS = "useSymbols",
7
7
  EXCLUDE_SIMILAR_CHARACTERS = "excludeSimilarCharacters",
8
8
  EXCLUDE = "exclude",
9
- COUNT = "count"
9
+ COUNT = "count",
10
+ MIN_NUMBERS = "minNumbers",
11
+ MIN_UPPERCASE = "minUppercase",
12
+ MIN_LOWERCASE = "minLowercase",
13
+ MIN_SYMBOLS = "minSymbols",
14
+ CUSTOM_LOWERCASE = "customLowercase",
15
+ CUSTOM_UPPERCASE = "customUppercase",
16
+ CUSTOM_NUMBERS = "customNumbers",
17
+ CUSTOM_SYMBOLS = "customSymbols"
10
18
  }
11
19
 
12
20
  type GeneratePasswordFunctionProps = {
13
- [key in GeneratePasswordPropsEnum]?: key extends GeneratePasswordPropsEnum.LENGTH | GeneratePasswordPropsEnum.COUNT ? number : key extends GeneratePasswordPropsEnum.EXCLUDE ? string : key extends GeneratePasswordPropsEnum.USE_NUMBERS | GeneratePasswordPropsEnum.USE_UPPERCASE | GeneratePasswordPropsEnum.USE_LOWERCASE | GeneratePasswordPropsEnum.USE_SYMBOLS | GeneratePasswordPropsEnum.EXCLUDE_SIMILAR_CHARACTERS ? boolean : never;
21
+ [key in GeneratePasswordPropsEnum]?: key extends GeneratePasswordPropsEnum.LENGTH | GeneratePasswordPropsEnum.COUNT | GeneratePasswordPropsEnum.MIN_NUMBERS | GeneratePasswordPropsEnum.MIN_UPPERCASE | GeneratePasswordPropsEnum.MIN_LOWERCASE | GeneratePasswordPropsEnum.MIN_SYMBOLS ? number : key extends GeneratePasswordPropsEnum.EXCLUDE | GeneratePasswordPropsEnum.CUSTOM_LOWERCASE | GeneratePasswordPropsEnum.CUSTOM_UPPERCASE | GeneratePasswordPropsEnum.CUSTOM_NUMBERS | GeneratePasswordPropsEnum.CUSTOM_SYMBOLS ? string : key extends GeneratePasswordPropsEnum.USE_NUMBERS | GeneratePasswordPropsEnum.USE_UPPERCASE | GeneratePasswordPropsEnum.USE_LOWERCASE | GeneratePasswordPropsEnum.USE_SYMBOLS | GeneratePasswordPropsEnum.EXCLUDE_SIMILAR_CHARACTERS ? boolean : never;
14
22
  };
15
23
 
16
- declare function generatePassword(props?: GeneratePasswordFunctionProps): string | string[];
24
+ interface ExtendedGeneratePasswordFunctionProps extends GeneratePasswordFunctionProps {
25
+ minUppercase?: number;
26
+ minLowercase?: number;
27
+ minNumbers?: number;
28
+ minSymbols?: number;
29
+ customUppercase?: string;
30
+ customLowercase?: string;
31
+ customNumbers?: string;
32
+ customSymbols?: string;
33
+ }
34
+ declare function calculatePasswordEntropy(length: number, charsetSize: number): number;
35
+ declare function generatePassword(props?: ExtendedGeneratePasswordFunctionProps): string | string[];
17
36
 
18
37
  declare const lengthError: {
19
38
  invalidLength: string;
@@ -32,4 +51,4 @@ declare const countError: {
32
51
  declare const unwantedPropsError: (unwantedProps: string[]) => string;
33
52
  declare const allOptionsFalseError = "At least one of the options must be true.";
34
53
 
35
- export { type GeneratePasswordFunctionProps, GeneratePasswordPropsEnum, allOptionsFalseError, countError, excludeError, excludeSimilarCharactersError, generatePassword, lengthError, unwantedPropsError, useLowercaseError, useNumbersError, useSymbolsError, useUppercaseError };
54
+ export { type ExtendedGeneratePasswordFunctionProps, type GeneratePasswordFunctionProps, GeneratePasswordPropsEnum, allOptionsFalseError, calculatePasswordEntropy, countError, excludeError, excludeSimilarCharactersError, generatePassword, lengthError, unwantedPropsError, useLowercaseError, useNumbersError, useSymbolsError, useUppercaseError };
package/dist/index.js CHANGED
@@ -8,6 +8,14 @@ var GeneratePasswordPropsEnum = /* @__PURE__ */ ((GeneratePasswordPropsEnum2) =>
8
8
  GeneratePasswordPropsEnum2["EXCLUDE_SIMILAR_CHARACTERS"] = "excludeSimilarCharacters";
9
9
  GeneratePasswordPropsEnum2["EXCLUDE"] = "exclude";
10
10
  GeneratePasswordPropsEnum2["COUNT"] = "count";
11
+ GeneratePasswordPropsEnum2["MIN_NUMBERS"] = "minNumbers";
12
+ GeneratePasswordPropsEnum2["MIN_UPPERCASE"] = "minUppercase";
13
+ GeneratePasswordPropsEnum2["MIN_LOWERCASE"] = "minLowercase";
14
+ GeneratePasswordPropsEnum2["MIN_SYMBOLS"] = "minSymbols";
15
+ GeneratePasswordPropsEnum2["CUSTOM_LOWERCASE"] = "customLowercase";
16
+ GeneratePasswordPropsEnum2["CUSTOM_UPPERCASE"] = "customUppercase";
17
+ GeneratePasswordPropsEnum2["CUSTOM_NUMBERS"] = "customNumbers";
18
+ GeneratePasswordPropsEnum2["CUSTOM_SYMBOLS"] = "customSymbols";
11
19
  return GeneratePasswordPropsEnum2;
12
20
  })(GeneratePasswordPropsEnum || {});
13
21
 
@@ -52,14 +60,46 @@ function generatePasswordPropValidation(props) {
52
60
  useSymbols,
53
61
  excludeSimilarCharacters,
54
62
  exclude,
55
- count
63
+ count,
64
+ minNumbers,
65
+ minUppercase,
66
+ minLowercase,
67
+ minSymbols,
68
+ customNumbers,
69
+ customUppercase,
70
+ customLowercase,
71
+ customSymbols
56
72
  } = props;
57
73
  if (typeof length !== "number" || length < 1) {
58
74
  throw new Error(lengthError.invalidLength);
59
75
  }
60
- if (length > 50) {
76
+ if (length > 100) {
61
77
  throw new Error(lengthError.lengthTooLarge);
62
78
  }
79
+ const checkMin = (val, name) => {
80
+ if (val !== void 0 && (typeof val !== "number" || val < 0)) {
81
+ throw new Error(`${name} must be a non-negative number.`);
82
+ }
83
+ };
84
+ checkMin(minNumbers, "minNumbers");
85
+ checkMin(minUppercase, "minUppercase");
86
+ checkMin(minLowercase, "minLowercase");
87
+ checkMin(minSymbols, "minSymbols");
88
+ const totalMin = (minNumbers ?? 0) + (minUppercase ?? 0) + (minLowercase ?? 0) + (minSymbols ?? 0);
89
+ if (totalMin > length) {
90
+ throw new Error(
91
+ "Total minimum character requirements exceed requested password length."
92
+ );
93
+ }
94
+ const checkString = (val, name) => {
95
+ if (val !== void 0 && typeof val !== "string") {
96
+ throw new Error(`${name} must be a string.`);
97
+ }
98
+ };
99
+ checkString(customNumbers, "customNumbers");
100
+ checkString(customUppercase, "customUppercase");
101
+ checkString(customLowercase, "customLowercase");
102
+ checkString(customSymbols, "customSymbols");
63
103
  if (typeof useNumbers !== "boolean") {
64
104
  throw new Error(useNumbersError);
65
105
  }
@@ -90,9 +130,32 @@ function generatePasswordPropValidation(props) {
90
130
  }
91
131
 
92
132
  // src/functions/generate.function.ts
133
+ function calculatePasswordEntropy(length, charsetSize) {
134
+ if (length <= 0 || charsetSize <= 0) return 0;
135
+ return length * Math.log2(charsetSize);
136
+ }
137
+ function getRandomInt(max) {
138
+ if (max <= 1) return 0;
139
+ const array = new Uint32Array(1);
140
+ const range = Math.floor(4294967295 / max) * max;
141
+ let randomValue;
142
+ do {
143
+ crypto.getRandomValues(array);
144
+ randomValue = array[0];
145
+ } while (randomValue >= range);
146
+ return randomValue % max;
147
+ }
148
+ function shuffleString(str) {
149
+ const arr = str.split("");
150
+ for (let i = arr.length - 1; i > 0; i--) {
151
+ const j = getRandomInt(i + 1);
152
+ [arr[i], arr[j]] = [arr[j], arr[i]];
153
+ }
154
+ return arr.join("");
155
+ }
93
156
  function generatePassword(props = {}) {
94
157
  const {
95
- length = 8,
158
+ length = 12,
96
159
  useNumbers = true,
97
160
  useUppercase = true,
98
161
  useLowercase = true,
@@ -100,56 +163,75 @@ function generatePassword(props = {}) {
100
163
  excludeSimilarCharacters = false,
101
164
  exclude = "",
102
165
  count = 1,
103
- ...extraProps
166
+ minUppercase = 0,
167
+ minLowercase = 0,
168
+ minNumbers = 0,
169
+ minSymbols = 0,
170
+ customUppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
171
+ customLowercase = "abcdefghijklmnopqrstuvwxyz",
172
+ customNumbers = "0123456789",
173
+ customSymbols = "@#$%^&*()_+=<>?/|"
104
174
  } = props;
105
- const allProps = {
106
- length,
107
- useNumbers,
108
- useUppercase,
109
- useLowercase,
110
- useSymbols,
111
- excludeSimilarCharacters,
112
- exclude,
113
- count,
114
- ...extraProps
115
- };
116
- generatePasswordPropValidation(allProps);
117
- const lowercaseChars = "abcdefghijklmnopqrstuvwxyz";
118
- const uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
119
- const numberChars = "0123456789";
120
- const symbolChars = "@#$%^&*()_+=<>?/|";
175
+ generatePasswordPropValidation(props);
121
176
  const similarChars = "il1Lo0O";
122
- let charset = "";
123
- const charsetOptions = [
124
- { condition: useLowercase, value: lowercaseChars },
125
- { condition: useUppercase, value: uppercaseChars },
126
- { condition: useNumbers, value: numberChars },
127
- { condition: useSymbols, value: symbolChars }
128
- ];
129
- charset = charsetOptions.reduce((acc, { condition, value }) => {
130
- return condition ? acc + value : acc;
131
- }, "");
132
- if (excludeSimilarCharacters) {
133
- charset = charset.split("").filter((char) => !similarChars.includes(char)).join("");
134
- }
135
- if (exclude) {
136
- charset = charset.split("").filter((char) => !exclude.includes(char)).join("");
137
- }
138
- if (charset.length === 0) {
177
+ const filterCharset = (base) => {
178
+ let result = base;
179
+ if (excludeSimilarCharacters) {
180
+ result = result.split("").filter((char) => !similarChars.includes(char)).join("");
181
+ }
182
+ if (exclude) {
183
+ result = result.split("").filter((char) => !exclude.includes(char)).join("");
184
+ }
185
+ return result;
186
+ };
187
+ const charsets = {
188
+ uppercase: filterCharset(customUppercase),
189
+ lowercase: filterCharset(customLowercase),
190
+ numbers: filterCharset(customNumbers),
191
+ symbols: filterCharset(customSymbols)
192
+ };
193
+ const activeCharsets = [];
194
+ if (useUppercase && charsets.uppercase)
195
+ activeCharsets.push(charsets.uppercase);
196
+ if (useLowercase && charsets.lowercase)
197
+ activeCharsets.push(charsets.lowercase);
198
+ if (useNumbers && charsets.numbers) activeCharsets.push(charsets.numbers);
199
+ if (useSymbols && charsets.symbols) activeCharsets.push(charsets.symbols);
200
+ const fullCharset = activeCharsets.join("");
201
+ if (fullCharset.length === 0) {
139
202
  throw new Error("No valid characters to generate password.");
140
203
  }
141
204
  const generateSinglePassword = () => {
142
- const randomIndices = new Uint32Array(length);
143
- crypto.getRandomValues(randomIndices);
144
- const password = Array.from(randomIndices).map((index) => charset[index % charset.length]).join("");
145
- return password;
205
+ const passwordChars = [];
206
+ const addGuaranteed = (count2, set) => {
207
+ if (count2 > 0 && set.length > 0) {
208
+ for (let i = 0; i < count2; i++) {
209
+ passwordChars.push(set[getRandomInt(set.length)]);
210
+ }
211
+ }
212
+ };
213
+ if (useUppercase) addGuaranteed(minUppercase, charsets.uppercase);
214
+ if (useLowercase) addGuaranteed(minLowercase, charsets.lowercase);
215
+ if (useNumbers) addGuaranteed(minNumbers, charsets.numbers);
216
+ if (useSymbols) addGuaranteed(minSymbols, charsets.symbols);
217
+ if (passwordChars.length > length) {
218
+ throw new Error(
219
+ "Minimum character requirements exceed requested password length."
220
+ );
221
+ }
222
+ const remainingLength = length - passwordChars.length;
223
+ for (let i = 0; i < remainingLength; i++) {
224
+ passwordChars.push(fullCharset[getRandomInt(fullCharset.length)]);
225
+ }
226
+ return shuffleString(passwordChars.join(""));
146
227
  };
147
228
  if (count === 1) return generateSinglePassword();
148
- return Array.from({ length: count }, generateSinglePassword);
229
+ return Array.from({ length: count }, () => generateSinglePassword());
149
230
  }
150
231
  export {
151
232
  GeneratePasswordPropsEnum,
152
233
  allOptionsFalseError,
234
+ calculatePasswordEntropy,
153
235
  countError,
154
236
  excludeError,
155
237
  excludeSimilarCharactersError,
package/package.json CHANGED
@@ -1,31 +1,27 @@
1
1
  {
2
2
  "name": "@systemix/password",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
7
  "private": false,
8
8
  "type": "module",
9
- "main": "./dist/index.js",
10
- "module": "./dist/index.mjs",
9
+ "main": "./dist/index.cjs",
10
+ "module": "./dist/index.js",
11
11
  "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.cjs"
17
+ }
18
+ },
12
19
  "repository": {
13
20
  "type": "git",
14
21
  "url": "git+https://github.com/shahadathhs/systemix.git",
15
22
  "directory": "packages/password"
16
23
  },
17
24
  "homepage": "https://github.com/shahadathhs/systemix/tree/main/packages/password#readme",
18
- "scripts": {
19
- "build": "tsup src/index.ts --format cjs,esm --dts",
20
- "typecheck": "tsc --noEmit",
21
- "lint": "eslint .",
22
- "lint:fix": "eslint . --fix",
23
- "format": "prettier --check . --ignore-path .prettierignore",
24
- "format:fix": "prettier --write . --ignore-path .prettierignore",
25
- "clean": "rimraf dist node_modules",
26
- "clean:turbo": "rimraf .turbo dist node_modules",
27
- "prepublishOnly": "pnpm build"
28
- },
29
25
  "keywords": [
30
26
  "systemix",
31
27
  "password",
@@ -38,16 +34,16 @@
38
34
  "node",
39
35
  "utility"
40
36
  ],
41
- "author": "Shahadath Hossain <shahadath.dev@gmail.com> (https://github.com/shahadathhs)",
37
+ "author": "Shahadath Hossen Sajib <shahadathhossensajib732@gmail.com> (https://github.com/shahadathhs)",
42
38
  "license": "MIT",
43
39
  "description": "A cryptographically secure password generator with customizable complexity. Part of the Systemix toolkit.",
44
40
  "devDependencies": {
45
- "@systemix/eslint": "workspace:*",
46
- "@systemix/typescript": "workspace:*",
47
41
  "@types/node": "^25.2.3",
48
42
  "eslint": "^10.0.0",
49
43
  "tsup": "^8.5.1",
50
- "typescript": "^5.9.3"
44
+ "typescript": "^5.9.3",
45
+ "@systemix/eslint": "0.2.1",
46
+ "@systemix/typescript": "0.2.1"
51
47
  },
52
48
  "directories": {
53
49
  "test": "tests"
@@ -63,5 +59,15 @@
63
59
  "dist",
64
60
  "README.md",
65
61
  "package.json"
66
- ]
67
- }
62
+ ],
63
+ "scripts": {
64
+ "build": "tsup src/index.ts --format cjs,esm --dts",
65
+ "typecheck": "tsc --noEmit",
66
+ "lint": "eslint .",
67
+ "lint:fix": "eslint . --fix",
68
+ "format": "prettier --check . --ignore-path .prettierignore",
69
+ "format:fix": "prettier --write . --ignore-path .prettierignore",
70
+ "clean": "rimraf dist node_modules",
71
+ "clean:turbo": "rimraf .turbo dist node_modules"
72
+ }
73
+ }
package/dist/index.d.mts DELETED
@@ -1,35 +0,0 @@
1
- declare enum GeneratePasswordPropsEnum {
2
- LENGTH = "length",
3
- USE_NUMBERS = "useNumbers",
4
- USE_UPPERCASE = "useUppercase",
5
- USE_LOWERCASE = "useLowercase",
6
- USE_SYMBOLS = "useSymbols",
7
- EXCLUDE_SIMILAR_CHARACTERS = "excludeSimilarCharacters",
8
- EXCLUDE = "exclude",
9
- COUNT = "count"
10
- }
11
-
12
- type GeneratePasswordFunctionProps = {
13
- [key in GeneratePasswordPropsEnum]?: key extends GeneratePasswordPropsEnum.LENGTH | GeneratePasswordPropsEnum.COUNT ? number : key extends GeneratePasswordPropsEnum.EXCLUDE ? string : key extends GeneratePasswordPropsEnum.USE_NUMBERS | GeneratePasswordPropsEnum.USE_UPPERCASE | GeneratePasswordPropsEnum.USE_LOWERCASE | GeneratePasswordPropsEnum.USE_SYMBOLS | GeneratePasswordPropsEnum.EXCLUDE_SIMILAR_CHARACTERS ? boolean : never;
14
- };
15
-
16
- declare function generatePassword(props?: GeneratePasswordFunctionProps): string | string[];
17
-
18
- declare const lengthError: {
19
- invalidLength: string;
20
- lengthTooLarge: string;
21
- };
22
- declare const useNumbersError = "Invalid useNumbers. It must be a boolean value.";
23
- declare const useUppercaseError = "Invalid useUppercase. It must be a boolean value.";
24
- declare const useLowercaseError = "Invalid useLowercase. It must be a boolean value.";
25
- declare const useSymbolsError = "Invalid useSymbols. It must be a boolean value.";
26
- declare const excludeSimilarCharactersError = "Invalid excludeSimilarCharacters. It must be a boolean value.";
27
- declare const excludeError = "Invalid exclude. It must be a string.";
28
- declare const countError: {
29
- invalidCount: string;
30
- countTooLarge: string;
31
- };
32
- declare const unwantedPropsError: (unwantedProps: string[]) => string;
33
- declare const allOptionsFalseError = "At least one of the options must be true.";
34
-
35
- export { type GeneratePasswordFunctionProps, GeneratePasswordPropsEnum, allOptionsFalseError, countError, excludeError, excludeSimilarCharactersError, generatePassword, lengthError, unwantedPropsError, useLowercaseError, useNumbersError, useSymbolsError, useUppercaseError };
package/dist/index.mjs DELETED
@@ -1,158 +0,0 @@
1
- // src/enum/props.enum.ts
2
- var GeneratePasswordPropsEnum = /* @__PURE__ */ ((GeneratePasswordPropsEnum2) => {
3
- GeneratePasswordPropsEnum2["LENGTH"] = "length";
4
- GeneratePasswordPropsEnum2["USE_NUMBERS"] = "useNumbers";
5
- GeneratePasswordPropsEnum2["USE_UPPERCASE"] = "useUppercase";
6
- GeneratePasswordPropsEnum2["USE_LOWERCASE"] = "useLowercase";
7
- GeneratePasswordPropsEnum2["USE_SYMBOLS"] = "useSymbols";
8
- GeneratePasswordPropsEnum2["EXCLUDE_SIMILAR_CHARACTERS"] = "excludeSimilarCharacters";
9
- GeneratePasswordPropsEnum2["EXCLUDE"] = "exclude";
10
- GeneratePasswordPropsEnum2["COUNT"] = "count";
11
- return GeneratePasswordPropsEnum2;
12
- })(GeneratePasswordPropsEnum || {});
13
-
14
- // src/utils/error.message.ts
15
- var lengthError = {
16
- invalidLength: "Invalid length. Length must be a positive number.",
17
- lengthTooLarge: "Invalid length. Length must be less than or equal to 50."
18
- };
19
- var useNumbersError = "Invalid useNumbers. It must be a boolean value.";
20
- var useUppercaseError = "Invalid useUppercase. It must be a boolean value.";
21
- var useLowercaseError = "Invalid useLowercase. It must be a boolean value.";
22
- var useSymbolsError = "Invalid useSymbols. It must be a boolean value.";
23
- var excludeSimilarCharactersError = "Invalid excludeSimilarCharacters. It must be a boolean value.";
24
- var excludeError = "Invalid exclude. It must be a string.";
25
- var countError = {
26
- invalidCount: "Invalid count. Count must be a positive number.",
27
- countTooLarge: "Invalid count. Count must be less than or equal to 10."
28
- };
29
- var unwantedPropsError = (unwantedProps) => {
30
- return `Invalid prop(s): ${unwantedProps.join(", ")}. Only the following options are allowed: length, useNumbers, useUppercase, useLowercase, useSymbols, excludeSimilarCharacters, exclude, count.`;
31
- };
32
- var allOptionsFalseError = "At least one of the options must be true.";
33
-
34
- // src/functions/validation.function.ts
35
- var allValidProps = Object.values(GeneratePasswordPropsEnum);
36
- function generatePasswordPropValidation(props) {
37
- const propsValue = Object.keys(props);
38
- const unwantedProps = propsValue.filter((prop) => !allValidProps.includes(prop));
39
- if (unwantedProps.length > 0) {
40
- throw new Error(unwantedPropsError(unwantedProps));
41
- }
42
- const {
43
- length,
44
- useNumbers,
45
- useUppercase,
46
- useLowercase,
47
- useSymbols,
48
- excludeSimilarCharacters,
49
- exclude,
50
- count
51
- } = props;
52
- if (typeof length !== "number" || length < 1) {
53
- throw new Error(lengthError.invalidLength);
54
- }
55
- if (length > 50) {
56
- throw new Error(lengthError.lengthTooLarge);
57
- }
58
- if (typeof useNumbers !== "boolean") {
59
- throw new Error(useNumbersError);
60
- }
61
- if (typeof useUppercase !== "boolean") {
62
- throw new Error(useUppercaseError);
63
- }
64
- if (typeof useLowercase !== "boolean") {
65
- throw new Error(useLowercaseError);
66
- }
67
- if (typeof useSymbols !== "boolean") {
68
- throw new Error(useSymbolsError);
69
- }
70
- if (typeof excludeSimilarCharacters !== "boolean") {
71
- throw new Error(excludeSimilarCharactersError);
72
- }
73
- if (typeof exclude !== "string") {
74
- throw new Error(excludeError);
75
- }
76
- if (typeof count !== "number" || count < 1) {
77
- throw new Error(countError.invalidCount);
78
- }
79
- if (count > 10) {
80
- throw new Error(countError.countTooLarge);
81
- }
82
- if (!useNumbers && !useUppercase && !useLowercase && !useSymbols) {
83
- throw new Error(allOptionsFalseError);
84
- }
85
- }
86
-
87
- // src/functions/generate.function.ts
88
- function generatePassword(props = {}) {
89
- const {
90
- length = 8,
91
- useNumbers = true,
92
- useUppercase = true,
93
- useLowercase = true,
94
- useSymbols = false,
95
- excludeSimilarCharacters = false,
96
- exclude = "",
97
- count = 1,
98
- ...extraProps
99
- } = props;
100
- const allProps = {
101
- length,
102
- useNumbers,
103
- useUppercase,
104
- useLowercase,
105
- useSymbols,
106
- excludeSimilarCharacters,
107
- exclude,
108
- count,
109
- ...extraProps
110
- };
111
- generatePasswordPropValidation(allProps);
112
- const lowercaseChars = "abcdefghijklmnopqrstuvwxyz";
113
- const uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
114
- const numberChars = "0123456789";
115
- const symbolChars = "@#$%^&*()_+=<>?/|";
116
- const similarChars = "il1Lo0O";
117
- let charset = "";
118
- const charsetOptions = [
119
- { condition: useLowercase, value: lowercaseChars },
120
- { condition: useUppercase, value: uppercaseChars },
121
- { condition: useNumbers, value: numberChars },
122
- { condition: useSymbols, value: symbolChars }
123
- ];
124
- charset = charsetOptions.reduce((acc, { condition, value }) => {
125
- return condition ? acc + value : acc;
126
- }, "");
127
- if (excludeSimilarCharacters) {
128
- charset = charset.split("").filter((char) => !similarChars.includes(char)).join("");
129
- }
130
- if (exclude) {
131
- charset = charset.split("").filter((char) => !exclude.includes(char)).join("");
132
- }
133
- if (charset.length === 0) {
134
- throw new Error("No valid characters to generate password.");
135
- }
136
- const generateSinglePassword = () => {
137
- const randomIndices = new Uint32Array(length);
138
- crypto.getRandomValues(randomIndices);
139
- const password = Array.from(randomIndices).map((index) => charset[index % charset.length]).join("");
140
- return password;
141
- };
142
- if (count === 1) return generateSinglePassword();
143
- return Array.from({ length: count }, generateSinglePassword);
144
- }
145
- export {
146
- GeneratePasswordPropsEnum,
147
- allOptionsFalseError,
148
- countError,
149
- excludeError,
150
- excludeSimilarCharactersError,
151
- generatePassword,
152
- lengthError,
153
- unwantedPropsError,
154
- useLowercaseError,
155
- useNumbersError,
156
- useSymbolsError,
157
- useUppercaseError
158
- };