lockly 0.1.0 → 0.2.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
@@ -2,13 +2,14 @@
2
2
 
3
3
  Cryptographically secure password generator CLI.
4
4
 
5
- Generates unpredictable random passwords instantly using `node:crypto.getRandomValues()`.
5
+ Generates unpredictable random passwords instantly using the Web Crypto API (`crypto.getRandomValues()`).
6
6
 
7
7
  ## Features
8
8
 
9
- - **Cryptographically secure** — CSPRNG via `node:crypto`
9
+ - **Cryptographically secure** — CSPRNG via Web Crypto API
10
+ - **Browser compatible** — works in Node.js and browsers
10
11
  - **Fast** — under 50ms response time
11
- - **Customizable** — length, character sets, count
12
+ - **Customizable** — length, character sets, count, charset inclusion guarantee (`--ensure`)
12
13
  - **Pipe-friendly** — clean stdout output, no ANSI colors
13
14
  - **Zero install** — run instantly with `npx`
14
15
 
@@ -52,6 +53,16 @@ lockly --no-uppercase --no-lowercase --no-symbols -l 6
52
53
  lockly --no-lowercase --no-symbols
53
54
  ```
54
55
 
56
+ ### Ensure charset inclusion
57
+
58
+ ```bash
59
+ # Guarantee at least one character from each active charset
60
+ lockly --ensure
61
+
62
+ # Short password with guaranteed diversity
63
+ lockly --ensure -l 4
64
+ ```
65
+
55
66
  ### Piping
56
67
 
57
68
  ```bash
@@ -81,12 +92,13 @@ export DB_PASSWORD=$(lockly -l 24 --no-symbols)
81
92
  | `--no-lowercase` | Exclude lowercase (a-z) | included | - |
82
93
  | `--no-numbers` | Exclude digits (0-9) | included | - |
83
94
  | `--no-symbols` | Exclude symbols | included | - |
95
+ | `--ensure` | Guarantee at least one char from each active charset | off | - |
84
96
  | `-V, --version` | Show version | - | - |
85
97
  | `-h, --help` | Show help | - | - |
86
98
 
87
99
  ## Security
88
100
 
89
- - **CSPRNG**: Uses `node:crypto.getRandomValues()` instead of `Math.random()`
101
+ - **CSPRNG**: Uses `crypto.getRandomValues()` (Web Crypto API) instead of `Math.random()`
90
102
  - **Local execution**: No network requests — passwords are generated locally
91
103
  - **Stateless**: Generated passwords are never stored
92
104
  - **No modulo bias**: Rejection sampling ensures uniform distribution
@@ -117,11 +129,17 @@ const customPasswords = generatePassword({
117
129
  numbers: true,
118
130
  symbols: false
119
131
  });
132
+
133
+ // Ensure at least one character from each active charset
134
+ const ensured = generatePassword({
135
+ length: 16,
136
+ ensure: true
137
+ });
120
138
  ```
121
139
 
122
140
  ## Requirements
123
141
 
124
- - Node.js 20+
142
+ - Node.js 20+ or any modern browser with Web Crypto API support
125
143
 
126
144
  ## License
127
145
 
package/dist/cli.js CHANGED
@@ -3,24 +3,76 @@ import { Command } from 'commander';
3
3
  import { readFileSync } from 'fs';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { dirname, join } from 'path';
6
- import { getRandomValues } from 'crypto';
7
6
 
7
+ // src/generator.ts
8
+ var getRandomValues = globalThis.crypto.getRandomValues.bind(
9
+ globalThis.crypto
10
+ );
8
11
  var UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
9
12
  var LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
10
13
  var NUMBERS = "0123456789";
11
14
  var SYMBOLS = "!@#$%^&*()_+-=[]{}|;:,.<>?";
12
15
  function buildPool(options2) {
13
16
  let pool = "";
14
- if (options2.uppercase) pool += UPPERCASE;
15
- if (options2.lowercase) pool += LOWERCASE;
16
- if (options2.numbers) pool += NUMBERS;
17
- if (options2.symbols) pool += SYMBOLS;
18
- return pool;
17
+ const charsets = [];
18
+ if (options2.uppercase) {
19
+ pool += UPPERCASE;
20
+ charsets.push(UPPERCASE);
21
+ }
22
+ if (options2.lowercase) {
23
+ pool += LOWERCASE;
24
+ charsets.push(LOWERCASE);
25
+ }
26
+ if (options2.numbers) {
27
+ pool += NUMBERS;
28
+ charsets.push(NUMBERS);
29
+ }
30
+ if (options2.symbols) {
31
+ pool += SYMBOLS;
32
+ charsets.push(SYMBOLS);
33
+ }
34
+ return { pool, charsets };
35
+ }
36
+ function pickRandomChar(charset) {
37
+ const len = charset.length;
38
+ const limit = 256 - 256 % len;
39
+ for (; ; ) {
40
+ const randomBytes = new Uint8Array(2);
41
+ getRandomValues(randomBytes);
42
+ for (let i = 0; i < randomBytes.length; i++) {
43
+ if (randomBytes[i] < limit) {
44
+ return charset[randomBytes[i] % len];
45
+ }
46
+ }
47
+ }
19
48
  }
20
- function generateSingle(length2, pool) {
49
+ function cryptoShuffle(arr) {
50
+ for (let i = arr.length - 1; i > 0; i--) {
51
+ const range = i + 1;
52
+ const limit = 256 - 256 % range;
53
+ let j;
54
+ for (; ; ) {
55
+ const randomBytes = new Uint8Array(1);
56
+ getRandomValues(randomBytes);
57
+ if (randomBytes[0] < limit) {
58
+ j = randomBytes[0] % range;
59
+ break;
60
+ }
61
+ }
62
+ const tmp = arr[i];
63
+ arr[i] = arr[j];
64
+ arr[j] = tmp;
65
+ }
66
+ }
67
+ function generateSingle(length2, pool, charsets) {
21
68
  const poolLength = pool.length;
22
69
  const limit = 256 - 256 % poolLength;
23
70
  const chars = [];
71
+ if (charsets) {
72
+ for (const charset of charsets) {
73
+ chars.push(pickRandomChar(charset));
74
+ }
75
+ }
24
76
  while (chars.length < length2) {
25
77
  const remaining = length2 - chars.length;
26
78
  const bufferSize = remaining * 2;
@@ -33,6 +85,9 @@ function generateSingle(length2, pool) {
33
85
  }
34
86
  }
35
87
  }
88
+ if (charsets) {
89
+ cryptoShuffle(chars);
90
+ }
36
91
  return chars.join("");
37
92
  }
38
93
  function validateOptions(options2) {
@@ -48,6 +103,14 @@ function validateOptions(options2) {
48
103
  if (!options2.uppercase && !options2.lowercase && !options2.numbers && !options2.symbols) {
49
104
  throw new Error("\uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uBB38\uC790\uC14B\uC744 \uD3EC\uD568\uD574\uC57C \uD569\uB2C8\uB2E4");
50
105
  }
106
+ if (options2.ensure) {
107
+ const activeCount = (options2.uppercase ? 1 : 0) + (options2.lowercase ? 1 : 0) + (options2.numbers ? 1 : 0) + (options2.symbols ? 1 : 0);
108
+ if (options2.length < activeCount) {
109
+ throw new Error(
110
+ `ensure \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uAE38\uC774\uAC00 \uD65C\uC131 \uBB38\uC790\uC14B \uC218(${activeCount}) \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4`
111
+ );
112
+ }
113
+ }
51
114
  }
52
115
  function generatePassword(options2) {
53
116
  const resolved = {
@@ -56,13 +119,20 @@ function generatePassword(options2) {
56
119
  uppercase: options2?.uppercase ?? true,
57
120
  lowercase: options2?.lowercase ?? true,
58
121
  numbers: options2?.numbers ?? true,
59
- symbols: options2?.symbols ?? true
122
+ symbols: options2?.symbols ?? true,
123
+ ensure: options2?.ensure ?? false
60
124
  };
61
125
  validateOptions(resolved);
62
- const pool = buildPool(resolved);
126
+ const { pool, charsets } = buildPool(resolved);
63
127
  const passwords = [];
64
128
  for (let i = 0; i < resolved.count; i++) {
65
- passwords.push(generateSingle(resolved.length, pool));
129
+ passwords.push(
130
+ generateSingle(
131
+ resolved.length,
132
+ pool,
133
+ resolved.ensure ? charsets : void 0
134
+ )
135
+ );
66
136
  }
67
137
  return passwords;
68
138
  }
@@ -78,37 +148,37 @@ program.name("lockly").description("Cryptographically secure random password gen
78
148
  "-l, --length <number>",
79
149
  "\uD328\uC2A4\uC6CC\uB4DC \uAE38\uC774 (\uAE30\uBCF8: 16, \uBC94\uC704: 1-1024)",
80
150
  "16"
81
- ).option("-c, --count <number>", "\uC0DD\uC131 \uAC1C\uC218 (\uAE30\uBCF8: 1)", "1").option("--no-uppercase", "\uB300\uBB38\uC790(A-Z) \uC81C\uC678").option("--no-lowercase", "\uC18C\uBB38\uC790(a-z) \uC81C\uC678").option("--no-numbers", "\uC22B\uC790(0-9) \uC81C\uC678").option("--no-symbols", "\uD2B9\uC218\uBB38\uC790 \uC81C\uC678").helpOption("-h, --help", "\uB3C4\uC6C0\uB9D0");
151
+ ).option("-c, --count <number>", "\uC0DD\uC131 \uAC1C\uC218 (\uAE30\uBCF8: 1)", "1").option("--no-uppercase", "\uB300\uBB38\uC790(A-Z) \uC81C\uC678").option("--no-lowercase", "\uC18C\uBB38\uC790(a-z) \uC81C\uC678").option("--no-numbers", "\uC22B\uC790(0-9) \uC81C\uC678").option("--no-symbols", "\uD2B9\uC218\uBB38\uC790 \uC81C\uC678").option("--ensure", "\uAC01 \uBB38\uC790\uC14B\uC5D0\uC11C \uCD5C\uC18C 1\uC790 \uD3EC\uD568 \uBCF4\uC7A5").helpOption("-h, --help", "\uB3C4\uC6C0\uB9D0");
82
152
  program.parse();
83
153
  var options = program.opts();
84
154
  var length = parseInt(options.length, 10);
85
155
  var count = parseInt(options.count, 10);
86
156
  if (isNaN(length)) {
87
157
  console.error("\uC720\uD6A8\uD55C \uC22B\uC790\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694 (length)");
88
- process.exit(1);
89
- }
90
- if (isNaN(count)) {
158
+ process.exitCode = 1;
159
+ } else if (isNaN(count)) {
91
160
  console.error("\uC720\uD6A8\uD55C \uC22B\uC790\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694 (count)");
92
- process.exit(1);
93
- }
94
- try {
95
- const passwords = generatePassword({
96
- length,
97
- count,
98
- uppercase: options.uppercase,
99
- lowercase: options.lowercase,
100
- numbers: options.numbers,
101
- symbols: options.symbols
102
- });
103
- for (const password of passwords) {
104
- console.log(password);
105
- }
106
- process.exit(0);
107
- } catch (error) {
108
- if (error instanceof Error) {
109
- console.error(error.message);
110
- } else {
111
- console.error("\uC54C \uC218 \uC5C6\uB294 \uC5D0\uB7EC\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4");
112
- }
113
- process.exit(1);
161
+ process.exitCode = 1;
162
+ } else {
163
+ try {
164
+ const passwords = generatePassword({
165
+ length,
166
+ count,
167
+ uppercase: options.uppercase,
168
+ lowercase: options.lowercase,
169
+ numbers: options.numbers,
170
+ symbols: options.symbols,
171
+ ensure: options.ensure
172
+ });
173
+ for (const password of passwords) {
174
+ console.log(password);
175
+ }
176
+ } catch (error) {
177
+ if (error instanceof Error) {
178
+ console.error(error.message);
179
+ } else {
180
+ console.error("\uC54C \uC218 \uC5C6\uB294 \uC5D0\uB7EC\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4");
181
+ }
182
+ process.exitCode = 1;
183
+ }
114
184
  }
package/dist/index.cjs CHANGED
@@ -1,29 +1,79 @@
1
1
  'use strict';
2
2
 
3
- var crypto = require('crypto');
4
-
5
3
  // src/generator.ts
4
+ var getRandomValues = globalThis.crypto.getRandomValues.bind(
5
+ globalThis.crypto
6
+ );
6
7
  var UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
7
8
  var LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
8
9
  var NUMBERS = "0123456789";
9
10
  var SYMBOLS = "!@#$%^&*()_+-=[]{}|;:,.<>?";
10
11
  function buildPool(options) {
11
12
  let pool = "";
12
- if (options.uppercase) pool += UPPERCASE;
13
- if (options.lowercase) pool += LOWERCASE;
14
- if (options.numbers) pool += NUMBERS;
15
- if (options.symbols) pool += SYMBOLS;
16
- return pool;
13
+ const charsets = [];
14
+ if (options.uppercase) {
15
+ pool += UPPERCASE;
16
+ charsets.push(UPPERCASE);
17
+ }
18
+ if (options.lowercase) {
19
+ pool += LOWERCASE;
20
+ charsets.push(LOWERCASE);
21
+ }
22
+ if (options.numbers) {
23
+ pool += NUMBERS;
24
+ charsets.push(NUMBERS);
25
+ }
26
+ if (options.symbols) {
27
+ pool += SYMBOLS;
28
+ charsets.push(SYMBOLS);
29
+ }
30
+ return { pool, charsets };
31
+ }
32
+ function pickRandomChar(charset) {
33
+ const len = charset.length;
34
+ const limit = 256 - 256 % len;
35
+ for (; ; ) {
36
+ const randomBytes = new Uint8Array(2);
37
+ getRandomValues(randomBytes);
38
+ for (let i = 0; i < randomBytes.length; i++) {
39
+ if (randomBytes[i] < limit) {
40
+ return charset[randomBytes[i] % len];
41
+ }
42
+ }
43
+ }
17
44
  }
18
- function generateSingle(length, pool) {
45
+ function cryptoShuffle(arr) {
46
+ for (let i = arr.length - 1; i > 0; i--) {
47
+ const range = i + 1;
48
+ const limit = 256 - 256 % range;
49
+ let j;
50
+ for (; ; ) {
51
+ const randomBytes = new Uint8Array(1);
52
+ getRandomValues(randomBytes);
53
+ if (randomBytes[0] < limit) {
54
+ j = randomBytes[0] % range;
55
+ break;
56
+ }
57
+ }
58
+ const tmp = arr[i];
59
+ arr[i] = arr[j];
60
+ arr[j] = tmp;
61
+ }
62
+ }
63
+ function generateSingle(length, pool, charsets) {
19
64
  const poolLength = pool.length;
20
65
  const limit = 256 - 256 % poolLength;
21
66
  const chars = [];
67
+ if (charsets) {
68
+ for (const charset of charsets) {
69
+ chars.push(pickRandomChar(charset));
70
+ }
71
+ }
22
72
  while (chars.length < length) {
23
73
  const remaining = length - chars.length;
24
74
  const bufferSize = remaining * 2;
25
75
  const randomBytes = new Uint8Array(bufferSize);
26
- crypto.getRandomValues(randomBytes);
76
+ getRandomValues(randomBytes);
27
77
  for (let i = 0; i < randomBytes.length && chars.length < length; i++) {
28
78
  const byte = randomBytes[i];
29
79
  if (byte < limit) {
@@ -31,6 +81,9 @@ function generateSingle(length, pool) {
31
81
  }
32
82
  }
33
83
  }
84
+ if (charsets) {
85
+ cryptoShuffle(chars);
86
+ }
34
87
  return chars.join("");
35
88
  }
36
89
  function validateOptions(options) {
@@ -46,6 +99,14 @@ function validateOptions(options) {
46
99
  if (!options.uppercase && !options.lowercase && !options.numbers && !options.symbols) {
47
100
  throw new Error("\uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uBB38\uC790\uC14B\uC744 \uD3EC\uD568\uD574\uC57C \uD569\uB2C8\uB2E4");
48
101
  }
102
+ if (options.ensure) {
103
+ const activeCount = (options.uppercase ? 1 : 0) + (options.lowercase ? 1 : 0) + (options.numbers ? 1 : 0) + (options.symbols ? 1 : 0);
104
+ if (options.length < activeCount) {
105
+ throw new Error(
106
+ `ensure \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uAE38\uC774\uAC00 \uD65C\uC131 \uBB38\uC790\uC14B \uC218(${activeCount}) \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4`
107
+ );
108
+ }
109
+ }
49
110
  }
50
111
  function generatePassword(options) {
51
112
  const resolved = {
@@ -54,13 +115,20 @@ function generatePassword(options) {
54
115
  uppercase: options?.uppercase ?? true,
55
116
  lowercase: options?.lowercase ?? true,
56
117
  numbers: options?.numbers ?? true,
57
- symbols: options?.symbols ?? true
118
+ symbols: options?.symbols ?? true,
119
+ ensure: options?.ensure ?? false
58
120
  };
59
121
  validateOptions(resolved);
60
- const pool = buildPool(resolved);
122
+ const { pool, charsets } = buildPool(resolved);
61
123
  const passwords = [];
62
124
  for (let i = 0; i < resolved.count; i++) {
63
- passwords.push(generateSingle(resolved.length, pool));
125
+ passwords.push(
126
+ generateSingle(
127
+ resolved.length,
128
+ pool,
129
+ resolved.ensure ? charsets : void 0
130
+ )
131
+ );
64
132
  }
65
133
  return passwords;
66
134
  }
package/dist/index.d.cts CHANGED
@@ -36,6 +36,12 @@ interface GenerateOptions {
36
36
  * @default true
37
37
  */
38
38
  symbols?: boolean;
39
+ /**
40
+ * Ensure at least one character from each enabled charset is included.
41
+ * When enabled, the password length must be >= the number of active charsets.
42
+ * @default false
43
+ */
44
+ ensure?: boolean;
39
45
  }
40
46
  /**
41
47
  * Generate one or more cryptographically secure random passwords.
package/dist/index.d.ts CHANGED
@@ -36,6 +36,12 @@ interface GenerateOptions {
36
36
  * @default true
37
37
  */
38
38
  symbols?: boolean;
39
+ /**
40
+ * Ensure at least one character from each enabled charset is included.
41
+ * When enabled, the password length must be >= the number of active charsets.
42
+ * @default false
43
+ */
44
+ ensure?: boolean;
39
45
  }
40
46
  /**
41
47
  * Generate one or more cryptographically secure random passwords.
package/dist/index.js CHANGED
@@ -1,22 +1,72 @@
1
- import { getRandomValues } from 'crypto';
2
-
3
1
  // src/generator.ts
2
+ var getRandomValues = globalThis.crypto.getRandomValues.bind(
3
+ globalThis.crypto
4
+ );
4
5
  var UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
5
6
  var LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
6
7
  var NUMBERS = "0123456789";
7
8
  var SYMBOLS = "!@#$%^&*()_+-=[]{}|;:,.<>?";
8
9
  function buildPool(options) {
9
10
  let pool = "";
10
- if (options.uppercase) pool += UPPERCASE;
11
- if (options.lowercase) pool += LOWERCASE;
12
- if (options.numbers) pool += NUMBERS;
13
- if (options.symbols) pool += SYMBOLS;
14
- return pool;
11
+ const charsets = [];
12
+ if (options.uppercase) {
13
+ pool += UPPERCASE;
14
+ charsets.push(UPPERCASE);
15
+ }
16
+ if (options.lowercase) {
17
+ pool += LOWERCASE;
18
+ charsets.push(LOWERCASE);
19
+ }
20
+ if (options.numbers) {
21
+ pool += NUMBERS;
22
+ charsets.push(NUMBERS);
23
+ }
24
+ if (options.symbols) {
25
+ pool += SYMBOLS;
26
+ charsets.push(SYMBOLS);
27
+ }
28
+ return { pool, charsets };
29
+ }
30
+ function pickRandomChar(charset) {
31
+ const len = charset.length;
32
+ const limit = 256 - 256 % len;
33
+ for (; ; ) {
34
+ const randomBytes = new Uint8Array(2);
35
+ getRandomValues(randomBytes);
36
+ for (let i = 0; i < randomBytes.length; i++) {
37
+ if (randomBytes[i] < limit) {
38
+ return charset[randomBytes[i] % len];
39
+ }
40
+ }
41
+ }
15
42
  }
16
- function generateSingle(length, pool) {
43
+ function cryptoShuffle(arr) {
44
+ for (let i = arr.length - 1; i > 0; i--) {
45
+ const range = i + 1;
46
+ const limit = 256 - 256 % range;
47
+ let j;
48
+ for (; ; ) {
49
+ const randomBytes = new Uint8Array(1);
50
+ getRandomValues(randomBytes);
51
+ if (randomBytes[0] < limit) {
52
+ j = randomBytes[0] % range;
53
+ break;
54
+ }
55
+ }
56
+ const tmp = arr[i];
57
+ arr[i] = arr[j];
58
+ arr[j] = tmp;
59
+ }
60
+ }
61
+ function generateSingle(length, pool, charsets) {
17
62
  const poolLength = pool.length;
18
63
  const limit = 256 - 256 % poolLength;
19
64
  const chars = [];
65
+ if (charsets) {
66
+ for (const charset of charsets) {
67
+ chars.push(pickRandomChar(charset));
68
+ }
69
+ }
20
70
  while (chars.length < length) {
21
71
  const remaining = length - chars.length;
22
72
  const bufferSize = remaining * 2;
@@ -29,6 +79,9 @@ function generateSingle(length, pool) {
29
79
  }
30
80
  }
31
81
  }
82
+ if (charsets) {
83
+ cryptoShuffle(chars);
84
+ }
32
85
  return chars.join("");
33
86
  }
34
87
  function validateOptions(options) {
@@ -44,6 +97,14 @@ function validateOptions(options) {
44
97
  if (!options.uppercase && !options.lowercase && !options.numbers && !options.symbols) {
45
98
  throw new Error("\uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uBB38\uC790\uC14B\uC744 \uD3EC\uD568\uD574\uC57C \uD569\uB2C8\uB2E4");
46
99
  }
100
+ if (options.ensure) {
101
+ const activeCount = (options.uppercase ? 1 : 0) + (options.lowercase ? 1 : 0) + (options.numbers ? 1 : 0) + (options.symbols ? 1 : 0);
102
+ if (options.length < activeCount) {
103
+ throw new Error(
104
+ `ensure \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uAE38\uC774\uAC00 \uD65C\uC131 \uBB38\uC790\uC14B \uC218(${activeCount}) \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4`
105
+ );
106
+ }
107
+ }
47
108
  }
48
109
  function generatePassword(options) {
49
110
  const resolved = {
@@ -52,13 +113,20 @@ function generatePassword(options) {
52
113
  uppercase: options?.uppercase ?? true,
53
114
  lowercase: options?.lowercase ?? true,
54
115
  numbers: options?.numbers ?? true,
55
- symbols: options?.symbols ?? true
116
+ symbols: options?.symbols ?? true,
117
+ ensure: options?.ensure ?? false
56
118
  };
57
119
  validateOptions(resolved);
58
- const pool = buildPool(resolved);
120
+ const { pool, charsets } = buildPool(resolved);
59
121
  const passwords = [];
60
122
  for (let i = 0; i < resolved.count; i++) {
61
- passwords.push(generateSingle(resolved.length, pool));
123
+ passwords.push(
124
+ generateSingle(
125
+ resolved.length,
126
+ pool,
127
+ resolved.ensure ? charsets : void 0
128
+ )
129
+ );
62
130
  }
63
131
  return passwords;
64
132
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lockly",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Cryptographically secure random password generator CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",