@depup/crypto-random-string 5.0.0-depup.4 → 6.0.0-depup.1

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
@@ -13,8 +13,8 @@ npm install @depup/crypto-random-string
13
13
 
14
14
  | Field | Value |
15
15
  |-------|-------|
16
- | Original | [crypto-random-string](https://www.npmjs.com/package/crypto-random-string) @ 5.0.0 |
17
- | Processed | 2026-07-21 |
16
+ | Original | [crypto-random-string](https://www.npmjs.com/package/crypto-random-string) @ 6.0.0 |
17
+ | Processed | 2026-08-31 |
18
18
  | Smoke test | passed |
19
19
  | Deps updated | 1 |
20
20
 
@@ -22,7 +22,7 @@ npm install @depup/crypto-random-string
22
22
 
23
23
  | Dependency | From | To |
24
24
  |------------|------|-----|
25
- | type-fest | ^2.12.2 | ^5.8.0 |
25
+ | type-fest | ^5.8.0 | ^5.9.0 |
26
26
 
27
27
  ---
28
28
 
package/changes.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "bumped": {
3
3
  "type-fest": {
4
- "from": "^2.12.2",
5
- "to": "^5.8.0"
4
+ "from": "^5.8.0",
5
+ "to": "^5.9.0"
6
6
  }
7
7
  },
8
- "timestamp": "2026-07-21T17:13:08.396Z",
8
+ "timestamp": "2026-08-31T00:35:27.041Z",
9
9
  "totalUpdated": 1
10
10
  }
package/index.d.ts CHANGED
@@ -1,13 +1,17 @@
1
- import {MergeExclusive} from 'type-fest';
1
+ import {type MergeExclusive} from 'type-fest';
2
2
 
3
- interface BaseOptions {
3
+ type BaseOptions = {
4
4
  /**
5
5
  Length of the returned string.
6
+
7
+ Must be a non-negative integer.
8
+
9
+ This is the number of characters, so a string generated from a `characters` set with characters outside the [Basic Multilingual Plane](https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane), like emoji, has a larger `.length` than this.
6
10
  */
7
11
  length: number;
8
- }
12
+ };
9
13
 
10
- interface TypeOption {
14
+ type TypeOption = {
11
15
  /**
12
16
  Use only characters from a predefined set of allowed characters.
13
17
 
@@ -17,7 +21,7 @@ interface TypeOption {
17
21
 
18
22
  The `distinguishable` set contains only uppercase characters that are not easily confused: `CDEHKMPRTUWXY012458`. It can be useful if you need to print out a short string that you'd like users to read and type back in with minimal errors. For example, reading a code off of a screen that needs to be typed into a phone to connect two devices.
19
23
 
20
- The `ascii-printable` set contains all [printable ASCII characters](https://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters): ``!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~`` Useful for generating passwords where all possible ASCII characters should be used.
24
+ The `ascii-printable` set contains all [printable ASCII characters](https://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters) except the space: ``!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~`` Useful for generating passwords where all possible ASCII characters should be used.
21
25
 
22
26
  The `alphanumeric` set contains uppercase letters, lowercase letters, and digits: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`. Useful for generating [nonce](https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/nonce) values.
23
27
 
@@ -46,14 +50,16 @@ interface TypeOption {
46
50
  ```
47
51
  */
48
52
  type?: 'hex' | 'base64' | 'url-safe' | 'numeric' | 'distinguishable' | 'ascii-printable' | 'alphanumeric';
49
- }
53
+ };
50
54
 
51
- interface CharactersOption {
55
+ type CharactersOption = {
52
56
  /**
53
57
  Use only characters from a custom set of allowed characters.
54
58
 
55
59
  Cannot be set at the same time as the `type` option.
56
60
 
61
+ Each character is picked with equal probability, so repeating a character in the set makes it more likely to be picked. The length limits count Unicode characters, not UTF-16 code units, so characters outside the [Basic Multilingual Plane](https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane), like emoji, are handled correctly.
62
+
57
63
  Minimum length: `1`
58
64
  Maximum length: `65536`
59
65
 
@@ -64,7 +70,7 @@ interface CharactersOption {
64
70
  ```
65
71
  */
66
72
  characters?: string;
67
- }
73
+ };
68
74
 
69
75
  export type Options = BaseOptions & MergeExclusive<TypeOption, CharactersOption>;
70
76
 
@@ -82,24 +88,3 @@ cryptoRandomString({length: 10});
82
88
  ```
83
89
  */
84
90
  export default function cryptoRandomString(options: Options): string;
85
-
86
- /**
87
- Asynchronously generate a [cryptographically strong](https://en.wikipedia.org/wiki/Strong_cryptography) random string.
88
-
89
- For most use-cases, there's really no good reason to use this async version. From the Node.js docs:
90
-
91
- > The `crypto.randomBytes()` method will not complete until there is sufficient entropy available. This should normally never take longer than a few milliseconds. The only time when generating the random bytes may conceivably block for a longer period of time is right after boot, when the whole system is still low on entropy.
92
-
93
- In general, anything async comes with some overhead on it's own.
94
-
95
- @returns A promise which resolves to a randomized string.
96
-
97
- @example
98
- ```
99
- import {cryptoRandomStringAsync} from 'crypto-random-string';
100
-
101
- await cryptoRandomStringAsync({length: 10});
102
- //=> '2cf05d94db'
103
- ```
104
- */
105
- export function cryptoRandomStringAsync(options: Options): Promise<string>;
package/index.js CHANGED
@@ -1,13 +1,104 @@
1
- // TODO: When targeting Node.js 16, remove `cryptoRandomStringAsync` and use `crypto.webcrypto.getRandomValues` to interop with the browser code.
2
- // TODO: Later, when targeting Node.js 18, only use the browser code
3
- import {promisify} from 'node:util';
4
- import {randomBytes} from 'node:crypto';
5
- import {createStringGenerator, createAsyncStringGenerator} from './core.js';
6
-
7
- const randomBytesAsync = promisify(randomBytes);
8
-
9
- export default createStringGenerator((byteLength, type, length) => randomBytes(byteLength).toString(type).slice(0, length), size => new Uint8Array(randomBytes(size)));
10
- export const cryptoRandomStringAsync = createAsyncStringGenerator(async (byteLength, type, length) => {
11
- const buffer = await randomBytesAsync(byteLength);
12
- return buffer.toString(type).slice(0, length);
13
- }, async size => new Uint8Array(await randomBytesAsync(size)));
1
+ import {uint8ArrayToBase64, uint8ArrayToHex} from 'uint8array-extras';
2
+
3
+ // `crypto.getRandomValues` throws an error if too much entropy is requested at once. (https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#exceptions)
4
+ const maxBytesPerRequest = 65_536;
5
+
6
+ // Each character is picked with a single `Uint16` selector value, so a character set cannot be larger than the range of one.
7
+ const maxCharacterSetSize = 0x1_00_00;
8
+
9
+ function fillWithRandomValues(typedArray) {
10
+ const maxElementsPerRequest = maxBytesPerRequest / typedArray.BYTES_PER_ELEMENT;
11
+
12
+ for (let offset = 0; offset < typedArray.length; offset += maxElementsPerRequest) {
13
+ // `TypedArray#subarray` clamps the end index and shares the underlying buffer, so this fills the array in place without any copying.
14
+ crypto.getRandomValues(typedArray.subarray(offset, offset + maxElementsPerRequest));
15
+ }
16
+
17
+ return typedArray;
18
+ }
19
+
20
+ const randomBytes = byteLength => fillWithRandomValues(new Uint8Array(byteLength));
21
+
22
+ const generateForCustomCharacters = (length, characters) => {
23
+ // Generating entropy is faster than complex math operations, so we use the simplest way
24
+ const characterCount = characters.length;
25
+ const validSelectorCount = Math.floor(maxCharacterSetSize / characterCount) * characterCount; // Using values at or above this will ruin distribution when using modular division
26
+ // Generating a bit more than required, adjusted for how many values we expect to discard, so we usually only need one pass
27
+ const entropyLength = Math.ceil(1.1 * length * (maxCharacterSetSize / validSelectorCount));
28
+ let string = '';
29
+ let stringLength = 0;
30
+
31
+ while (stringLength < length) {
32
+ const entropy = fillWithRandomValues(new Uint16Array(entropyLength));
33
+
34
+ for (let index = 0; index < entropyLength; index++) {
35
+ const entropyValue = entropy[index];
36
+
37
+ if (entropyValue < validSelectorCount) { // Skip values which will ruin distribution when using modular division
38
+ string += characters[entropyValue % characterCount];
39
+ stringLength++;
40
+
41
+ if (stringLength === length) {
42
+ return string;
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+ return string;
49
+ };
50
+
51
+ const characterSets = new Map([
52
+ ['url-safe', [...'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~']],
53
+ ['numeric', [...'0123456789']],
54
+ ['distinguishable', [...'CDEHKMPRTUWXY012458']],
55
+ ['ascii-printable', [...'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~']],
56
+ ['alphanumeric', [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789']],
57
+ ]);
58
+
59
+ const allowedTypes = new Set(['hex', 'base64', ...characterSets.keys()]);
60
+
61
+ export default function cryptoRandomString({length, type, characters}) {
62
+ if (!Number.isSafeInteger(length) || length < 0) {
63
+ throw new TypeError('Expected `length` to be a non-negative integer');
64
+ }
65
+
66
+ if (type !== undefined && characters !== undefined) {
67
+ throw new TypeError('Expected either `type` or `characters`');
68
+ }
69
+
70
+ if (characters !== undefined) {
71
+ if (typeof characters !== 'string') {
72
+ throw new TypeError('Expected `characters` to be a string');
73
+ }
74
+
75
+ // Spread to keep characters outside the Basic Multilingual Plane, like emoji, intact.
76
+ const customCharacterSet = [...characters];
77
+
78
+ if (customCharacterSet.length === 0) {
79
+ throw new TypeError('Expected `characters` to contain at least 1 character');
80
+ }
81
+
82
+ if (customCharacterSet.length > maxCharacterSetSize) {
83
+ throw new TypeError(`Expected \`characters\` to contain at most ${maxCharacterSetSize} characters, got ${customCharacterSet.length}`);
84
+ }
85
+
86
+ return generateForCustomCharacters(length, customCharacterSet);
87
+ }
88
+
89
+ if (type !== undefined && !allowedTypes.has(type)) {
90
+ throw new TypeError(`Unknown type: ${type}`);
91
+ }
92
+
93
+ const characterSet = characterSets.get(type);
94
+
95
+ if (characterSet !== undefined) {
96
+ return generateForCustomCharacters(length, characterSet);
97
+ }
98
+
99
+ if (type === 'base64') {
100
+ return uint8ArrayToBase64(randomBytes(Math.ceil(length * 0.75))).slice(0, length); // Needs 0.75 bytes of entropy per character
101
+ }
102
+
103
+ return uint8ArrayToHex(randomBytes(Math.ceil(length * 0.5))).slice(0, length); // Needs 0.5 bytes of entropy per character
104
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@depup/crypto-random-string",
3
- "version": "5.0.0-depup.4",
3
+ "version": "6.0.0-depup.1",
4
4
  "description": "Generate a cryptographically strong random string (with updated dependencies)",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/crypto-random-string",
@@ -13,19 +13,17 @@
13
13
  "type": "module",
14
14
  "exports": {
15
15
  "types": "./index.d.ts",
16
- "node": "./index.js",
17
- "browser": "./browser.js"
16
+ "default": "./index.js"
18
17
  },
18
+ "sideEffects": false,
19
19
  "engines": {
20
- "node": ">=14.16"
20
+ "node": ">=22"
21
21
  },
22
22
  "scripts": {
23
23
  "test": "xo && ava && tsd"
24
24
  },
25
25
  "files": [
26
26
  "index.js",
27
- "browser.js",
28
- "core.js",
29
27
  "index.d.ts",
30
28
  "changes.json",
31
29
  "README.md"
@@ -53,25 +51,25 @@
53
51
  "protect"
54
52
  ],
55
53
  "dependencies": {
56
- "type-fest": "^5.8.0"
54
+ "type-fest": "^5.9.0",
55
+ "uint8array-extras": "^1.5.0"
57
56
  },
58
57
  "devDependencies": {
59
- "ava": "^4.2.0",
60
- "dot-prop": "^7.2.0",
61
- "tsd": "^0.20.0",
62
- "xo": "^0.48.0"
58
+ "ava": "^8.0.1",
59
+ "tsd": "^0.33.0",
60
+ "xo": "^4.0.0"
63
61
  },
64
62
  "depup": {
65
63
  "changes": {
66
64
  "type-fest": {
67
- "from": "^2.12.2",
68
- "to": "^5.8.0"
65
+ "from": "^5.8.0",
66
+ "to": "^5.9.0"
69
67
  }
70
68
  },
71
69
  "depsUpdated": 1,
72
70
  "originalPackage": "crypto-random-string",
73
- "originalVersion": "5.0.0",
74
- "processedAt": "2026-07-21T17:13:16.595Z",
71
+ "originalVersion": "6.0.0",
72
+ "processedAt": "2026-08-31T00:35:43.225Z",
75
73
  "smokeTest": "passed"
76
74
  }
77
75
  }
package/readme.md CHANGED
@@ -48,23 +48,6 @@ cryptoRandomString({length: 10, characters: 'abc'});
48
48
 
49
49
  Returns a randomized string. [Hex](https://en.wikipedia.org/wiki/Hexadecimal) by default.
50
50
 
51
- ### cryptoRandomStringAsync(options)
52
-
53
- Returns a promise which resolves to a randomized string. [Hex](https://en.wikipedia.org/wiki/Hexadecimal) by default.
54
-
55
- For most use-cases, there's really no good reason to use this async version. From the Node.js docs:
56
-
57
- > The `crypto.randomBytes()` method will not complete until there is sufficient entropy available. This should normally never take longer than a few milliseconds. The only time when generating the random bytes may conceivably block for a longer period of time is right after boot, when the whole system is still low on entropy.
58
-
59
- In general, anything async comes with some overhead on it's own.
60
-
61
- ```js
62
- import {cryptoRandomStringAsync} from 'crypto-random-string';
63
-
64
- await cryptoRandomStringAsync({length: 10});
65
- //=> '2cf05d94db'
66
- ```
67
-
68
51
  #### options
69
52
 
70
53
  Type: `object`
@@ -72,10 +55,12 @@ Type: `object`
72
55
  ##### length
73
56
 
74
57
  *Required*\
75
- Type: `number`
58
+ Type: `number` *(non-negative integer)*
76
59
 
77
60
  Length of the returned string.
78
61
 
62
+ This is the number of characters, so a string generated from a `characters` set with characters outside the [Basic Multilingual Plane](https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane), like emoji, has a larger `.length` than this.
63
+
79
64
  ##### type
80
65
 
81
66
  Type: `string`\
@@ -88,7 +73,7 @@ Cannot be set at the same time as the `characters` option.
88
73
 
89
74
  The `distinguishable` set contains only uppercase characters that are not easily confused: `CDEHKMPRTUWXY012458`. It can be useful if you need to print out a short string that you'd like users to read and type back in with minimal errors. For example, reading a code off of a screen that needs to be typed into a phone to connect two devices.
90
75
 
91
- The `ascii-printable` set contains all [printable ASCII characters](https://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters): ``!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~`` Useful for generating passwords where all possible ASCII characters should be used.
76
+ The `ascii-printable` set contains all [printable ASCII characters](https://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters) except the space: ``!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~`` Useful for generating passwords where all possible ASCII characters should be used.
92
77
 
93
78
  The `alphanumeric` set contains uppercase letters, lowercase letters, and digits: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`. Useful for generating [nonce](https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/nonce) values.
94
79
 
@@ -102,24 +87,14 @@ Use only characters from a custom set of allowed characters.
102
87
 
103
88
  Cannot be set at the same time as the `type` option.
104
89
 
90
+ Each character is picked with equal probability, so repeating a character in the set makes it more likely to be picked. The length limits count Unicode characters, not UTF-16 code units, so characters outside the [Basic Multilingual Plane](https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane), like emoji, are handled correctly.
91
+
105
92
  ## Related
106
93
 
107
94
  - [random-int](https://github.com/sindresorhus/random-int) - Generate a random integer
108
95
  - [random-float](https://github.com/sindresorhus/random-float) - Generate a random float
109
96
  - [random-item](https://github.com/sindresorhus/random-item) - Get a random item from an array
110
97
  - [random-boolean](https://github.com/arthurvr/random-boolean) - Get a random boolean
111
- - [random-obj-key](https://github.com/sindresorhus/random-obj-key) - Get a random key from an object
112
- - [random-obj-prop](https://github.com/sindresorhus/random-obj-prop) - Get a random property from an object
98
+ - [random-object-key](https://github.com/sindresorhus/random-object-key) - Get a random key from an object
99
+ - [random-object-value](https://github.com/sindresorhus/random-object-value) - Get a random value from an object
113
100
  - [unique-random](https://github.com/sindresorhus/unique-random) - Generate random numbers that are consecutively unique
114
-
115
- ---
116
-
117
- <div align="center">
118
- <b>
119
- <a href="https://tidelift.com/subscription/pkg/npm-crypto-random-string?utm_source=npm-crypto-random-string&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
120
- </b>
121
- <br>
122
- <sub>
123
- Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
124
- </sub>
125
- </div>
package/browser.js DELETED
@@ -1,31 +0,0 @@
1
- /* eslint-env browser */
2
- import {createStringGenerator, createAsyncStringGenerator} from './core.js';
3
-
4
- const toHex = uInt8Array => [...uInt8Array].map(byte => byte.toString(16).padStart(2, '0')).join('');
5
- const toBase64 = uInt8Array => btoa(String.fromCodePoint(...uInt8Array));
6
-
7
- // `crypto.getRandomValues` throws an error if too much entropy is requested at once. (https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#exceptions)
8
- const maxEntropy = 65_536;
9
-
10
- function getRandomValues(byteLength) {
11
- const generatedBytes = new Uint8Array(byteLength);
12
-
13
- for (let totalGeneratedBytes = 0; totalGeneratedBytes < byteLength; totalGeneratedBytes += maxEntropy) {
14
- generatedBytes.set(
15
- crypto.getRandomValues(new Uint8Array(Math.min(maxEntropy, byteLength - totalGeneratedBytes))),
16
- totalGeneratedBytes,
17
- );
18
- }
19
-
20
- return generatedBytes;
21
- }
22
-
23
- function specialRandomBytes(byteLength, type, length) {
24
- const generatedBytes = getRandomValues(byteLength);
25
- const convert = type === 'hex' ? toHex : toBase64;
26
-
27
- return convert(generatedBytes).slice(0, length);
28
- }
29
-
30
- export default createStringGenerator(specialRandomBytes, getRandomValues);
31
- export const cryptoRandomStringAsync = createAsyncStringGenerator(specialRandomBytes, getRandomValues);
package/core.js DELETED
@@ -1,140 +0,0 @@
1
- const urlSafeCharacters = [...'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~'];
2
- const numericCharacters = [...'0123456789'];
3
- const distinguishableCharacters = [...'CDEHKMPRTUWXY012458'];
4
- const asciiPrintableCharacters = [...'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'];
5
- const alphanumericCharacters = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'];
6
-
7
- const readUInt16LE = (uInt8Array, offset) => uInt8Array[offset] + (uInt8Array[offset + 1] << 8); // eslint-disable-line no-bitwise
8
-
9
- const generateForCustomCharacters = (length, characters, randomBytes) => {
10
- // Generating entropy is faster than complex math operations, so we use the simplest way
11
- const characterCount = characters.length;
12
- const maxValidSelector = (Math.floor(0x1_00_00 / characterCount) * characterCount) - 1; // Using values above this will ruin distribution when using modular division
13
- const entropyLength = 2 * Math.ceil(1.1 * length); // Generating a bit more than required so chances we need more than one pass will be really low
14
- let string = '';
15
- let stringLength = 0;
16
-
17
- while (stringLength < length) { // In case we had many bad values, which may happen for character sets of size above 0x8000 but close to it
18
- const entropy = randomBytes(entropyLength);
19
- let entropyPosition = 0;
20
-
21
- while (entropyPosition < entropyLength && stringLength < length) {
22
- const entropyValue = readUInt16LE(entropy, entropyPosition);
23
- entropyPosition += 2;
24
- if (entropyValue > maxValidSelector) { // Skip values which will ruin distribution when using modular division
25
- continue;
26
- }
27
-
28
- string += characters[entropyValue % characterCount];
29
- stringLength++;
30
- }
31
- }
32
-
33
- return string;
34
- };
35
-
36
- const generateForCustomCharactersAsync = async (length, characters, randomBytesAsync) => {
37
- // Generating entropy is faster than complex math operations, so we use the simplest way
38
- const characterCount = characters.length;
39
- const maxValidSelector = (Math.floor(0x1_00_00 / characterCount) * characterCount) - 1; // Using values above this will ruin distribution when using modular division
40
- const entropyLength = 2 * Math.ceil(1.1 * length); // Generating a bit more than required so chances we need more than one pass will be really low
41
- let string = '';
42
- let stringLength = 0;
43
-
44
- while (stringLength < length) { // In case we had many bad values, which may happen for character sets of size above 0x8000 but close to it
45
- const entropy = await randomBytesAsync(entropyLength); // eslint-disable-line no-await-in-loop
46
- let entropyPosition = 0;
47
-
48
- while (entropyPosition < entropyLength && stringLength < length) {
49
- const entropyValue = readUInt16LE(entropy, entropyPosition);
50
- entropyPosition += 2;
51
- if (entropyValue > maxValidSelector) { // Skip values which will ruin distribution when using modular division
52
- continue;
53
- }
54
-
55
- string += characters[entropyValue % characterCount];
56
- stringLength++;
57
- }
58
- }
59
-
60
- return string;
61
- };
62
-
63
- const allowedTypes = new Set([
64
- undefined,
65
- 'hex',
66
- 'base64',
67
- 'url-safe',
68
- 'numeric',
69
- 'distinguishable',
70
- 'ascii-printable',
71
- 'alphanumeric',
72
- ]);
73
-
74
- const createGenerator = (generateForCustomCharacters, specialRandomBytes, randomBytes) => ({length, type, characters}) => {
75
- if (!(length >= 0 && Number.isFinite(length))) {
76
- throw new TypeError('Expected a `length` to be a non-negative finite number');
77
- }
78
-
79
- if (type !== undefined && characters !== undefined) {
80
- throw new TypeError('Expected either `type` or `characters`');
81
- }
82
-
83
- if (characters !== undefined && typeof characters !== 'string') {
84
- throw new TypeError('Expected `characters` to be string');
85
- }
86
-
87
- if (!allowedTypes.has(type)) {
88
- throw new TypeError(`Unknown type: ${type}`);
89
- }
90
-
91
- if (type === undefined && characters === undefined) {
92
- type = 'hex';
93
- }
94
-
95
- if (type === 'hex' || (type === undefined && characters === undefined)) {
96
- return specialRandomBytes(Math.ceil(length * 0.5), 'hex', length); // Needs 0.5 bytes of entropy per character
97
- }
98
-
99
- if (type === 'base64') {
100
- return specialRandomBytes(Math.ceil(length * 0.75), 'base64', length); // Needs 0.75 bytes of entropy per character
101
- }
102
-
103
- if (type === 'url-safe') {
104
- return generateForCustomCharacters(length, urlSafeCharacters, randomBytes);
105
- }
106
-
107
- if (type === 'numeric') {
108
- return generateForCustomCharacters(length, numericCharacters, randomBytes);
109
- }
110
-
111
- if (type === 'distinguishable') {
112
- return generateForCustomCharacters(length, distinguishableCharacters, randomBytes);
113
- }
114
-
115
- if (type === 'ascii-printable') {
116
- return generateForCustomCharacters(length, asciiPrintableCharacters, randomBytes);
117
- }
118
-
119
- if (type === 'alphanumeric') {
120
- return generateForCustomCharacters(length, alphanumericCharacters, randomBytes);
121
- }
122
-
123
- if (characters.length === 0) {
124
- throw new TypeError('Expected `characters` string length to be greater than or equal to 1');
125
- }
126
-
127
- if (characters.length > 0x1_00_00) {
128
- throw new TypeError('Expected `characters` string length to be less or equal to 65536');
129
- }
130
-
131
- return generateForCustomCharacters(length, characters, randomBytes);
132
- };
133
-
134
- export function createStringGenerator(specialRandomBytes, randomBytes) {
135
- return createGenerator(generateForCustomCharacters, specialRandomBytes, randomBytes);
136
- }
137
-
138
- export function createAsyncStringGenerator(specialRandomBytesAsync, randomBytesAsync) {
139
- return createGenerator(generateForCustomCharactersAsync, specialRandomBytesAsync, randomBytesAsync);
140
- }