random-words-local 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # Random Words CLI
2
+
3
+ A simple and secure CLI tool for generating random word passphrases.
4
+
5
+ This tool uses cryptographically secure random numbers (`crypto.randomInt`) to pick words from a dictionary of over 275,000 English words, making it ideal for creating strong, memorable passphrases.
6
+
7
+ ## Installation
8
+
9
+ You can run it directly using `npx`, or any of the laternatives such as `bunx`:
10
+
11
+ ```bash
12
+ npx random-words-cli
13
+ ```
14
+
15
+ Or install it globally:
16
+
17
+ ```bash
18
+ npm install -g random-words-cli
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```bash
24
+ random-words [options]
25
+ ```
26
+
27
+ ### Options
28
+
29
+ | Option | Description | Default |
30
+ | :----------------------- | :--------------------------------------------- | :------- |
31
+ | `-n, --count <number>` | Number of words to generate | `4` |
32
+ | `-s, --separator <char>` | Separator between words | `-` |
33
+ | `-f, --format <type>` | Output format: `joined`, `array`, or `newline` | `joined` |
34
+ | `-h, --help` | Show help message | |
35
+
36
+ ## Examples
37
+
38
+ **Default passphrase (4 words):**
39
+
40
+ ```bash
41
+ random-words
42
+ # output: correct-horse-battery-staple
43
+ ```
44
+
45
+ **Generate 6 words with a space separator:**
46
+
47
+ ```bash
48
+ random-words -n 6 -s " "
49
+ ```
50
+
51
+ **Output as a JSON array:**
52
+
53
+ ```bash
54
+ random-words -n 3 -f array
55
+ ```
56
+
57
+ **One word per line:**
58
+
59
+ ```bash
60
+ random-words -n 5 -f newline
61
+ ```
62
+
63
+ ## Security
64
+
65
+ Passphrases generated by this tool are more secure than simple passwords because they are harder to brute-force but easier for humans to remember. By default, with 4 words chosen from a ~275,000 word dictionary, the entropy is approximately 72 bits ($4 \times \log_2(275,000)$).
66
+
67
+ ## License
68
+
69
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import { randomWords } from './words.js';
3
+ const args = process.argv.slice(2);
4
+ let count = 4;
5
+ let separator = '-';
6
+ let format = 'joined';
7
+ for (let i = 0; i < args.length; i++) {
8
+ const arg = args[i];
9
+ if (arg === '-n' || arg === '--count') {
10
+ const nextArg = args[++i];
11
+ if (!nextArg || isNaN(parseInt(nextArg))) {
12
+ console.error('Error: --count requires a valid number');
13
+ process.exit(1);
14
+ }
15
+ count = parseInt(nextArg);
16
+ if (count < 1 || count > 1000) {
17
+ console.error('Error: count must be between 1 and 1000');
18
+ process.exit(1);
19
+ }
20
+ }
21
+ else if (arg === '-s' || arg === '--separator') {
22
+ separator = args[++i];
23
+ if (separator === undefined) {
24
+ console.error('Error: --separator requires a value');
25
+ process.exit(1);
26
+ }
27
+ }
28
+ else if (arg === '-f' || arg === '--format') {
29
+ format = args[++i] || 'joined';
30
+ if (!['joined', 'array', 'newline'].includes(format)) {
31
+ console.error('Error: format must be one of: joined, array, newline');
32
+ process.exit(1);
33
+ }
34
+ }
35
+ else if (arg === '-h' || arg === '--help') {
36
+ console.log(`Usage: random-words [options]
37
+
38
+ Options:
39
+ -n, --count <number> Number of words to generate (default: 4)
40
+ -s, --separator <char> Separator between words (default: -)
41
+ -f, --format <type> Output format: joined, array, or newline (default: joined)
42
+ -h, --help Show this help message
43
+
44
+ Examples:
45
+ random-words
46
+ random-words -n 6
47
+ random-words -n 3 -s _
48
+ random-words -n 2 -f array`);
49
+ process.exit(0);
50
+ }
51
+ else if (!arg.startsWith('-')) {
52
+ const val = parseInt(arg);
53
+ if (!isNaN(val)) {
54
+ if (val < 1 || val > 1000) {
55
+ console.error('Error: count must be between 1 and 1000');
56
+ process.exit(1);
57
+ }
58
+ count = val;
59
+ }
60
+ }
61
+ }
62
+ const words = randomWords(count);
63
+ if (format === 'array') {
64
+ console.log(JSON.stringify(words));
65
+ }
66
+ else if (format === 'newline') {
67
+ console.log(words.join('\n'));
68
+ }
69
+ else {
70
+ console.log(words.join(separator));
71
+ }
72
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import words from 'an-array-of-english-words';
2
+ /**
3
+ * Returns a list of random words.
4
+ * This is now more efficient for large lists by picking individual random indices
5
+ * instead of shuffling the entire array.
6
+ *
7
+ * @param count Number of words to return
8
+ * @returns Array of random words
9
+ */
10
+ export declare function randomWords(count?: number): string[];
11
+ export { words };
package/dist/words.js ADDED
@@ -0,0 +1,31 @@
1
+ import { randomInt } from 'node:crypto';
2
+ // @ts-ignore - The package doesn't have types but we know it's an array of strings
3
+ import words from 'an-array-of-english-words';
4
+ /**
5
+ * Returns a list of random words.
6
+ * This is now more efficient for large lists by picking individual random indices
7
+ * instead of shuffling the entire array.
8
+ *
9
+ * @param count Number of words to return
10
+ * @returns Array of random words
11
+ */
12
+ export function randomWords(count = 4) {
13
+ const n = words.length;
14
+ const actualCount = Math.max(0, Math.min(count, n));
15
+ if (actualCount === 0)
16
+ return [];
17
+ const result = [];
18
+ const seenIndices = new Set();
19
+ // For small counts, we use a Set to pick unique indices.
20
+ // This is O(count) instead of O(N) where N is the total number of words (275,000+).
21
+ while (result.length < actualCount) {
22
+ const j = randomInt(0, n);
23
+ if (!seenIndices.has(j)) {
24
+ seenIndices.add(j);
25
+ result.push(words[j]);
26
+ }
27
+ }
28
+ return result;
29
+ }
30
+ export { words };
31
+ //# sourceMappingURL=words.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"words.js","sourceRoot":"","sources":["../src/words.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,mFAAmF;AACnF,OAAO,KAAK,MAAM,2BAA2B,CAAC;AAE9C;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB,CAAC;IAC3C,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IACvB,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAEpD,IAAI,WAAW,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IAEtC,yDAAyD;IACzD,oFAAoF;IACpF,OAAO,MAAM,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACxB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,OAAO,EAAE,KAAK,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "random-words-local",
3
+ "version": "1.0.0",
4
+ "description": "Generate random words from English dictionary",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "random-words": "dist/index.js"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "prepublishOnly": "npm run build",
13
+ "start": "node dist/index.js",
14
+ "dev": "tsx src/index.ts"
15
+ },
16
+ "keywords": [
17
+ "random",
18
+ "words",
19
+ "cli",
20
+ "generator"
21
+ ],
22
+ "author": "",
23
+ "license": "MIT",
24
+ "devDependencies": {
25
+ "@types/node": "^25.3.0",
26
+ "tsx": "^4.21.0",
27
+ "typescript": "^5.9.3"
28
+ },
29
+ "dependencies": {
30
+ "an-array-of-english-words": "^2.0.0"
31
+ }
32
+ }
package/src/index.ts ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { randomWords } from './words.js';
4
+
5
+ const args: string[] = process.argv.slice(2);
6
+ let count: number = 4;
7
+ let separator: string = '-';
8
+ let format: string = 'joined';
9
+
10
+ for (let i = 0; i < args.length; i++) {
11
+ const arg = args[i];
12
+ if (arg === '-n' || arg === '--count') {
13
+ const nextArg = args[++i];
14
+ if (!nextArg || isNaN(parseInt(nextArg))) {
15
+ console.error('Error: --count requires a valid number');
16
+ process.exit(1);
17
+ }
18
+ count = parseInt(nextArg);
19
+ if (count < 1 || count > 1000) {
20
+ console.error('Error: count must be between 1 and 1000');
21
+ process.exit(1);
22
+ }
23
+ } else if (arg === '-s' || arg === '--separator') {
24
+ separator = args[++i];
25
+ if (separator === undefined) {
26
+ console.error('Error: --separator requires a value');
27
+ process.exit(1);
28
+ }
29
+ } else if (arg === '-f' || arg === '--format') {
30
+ format = args[++i] || 'joined';
31
+ if (!['joined', 'array', 'newline'].includes(format)) {
32
+ console.error('Error: format must be one of: joined, array, newline');
33
+ process.exit(1);
34
+ }
35
+ } else if (arg === '-h' || arg === '--help') {
36
+ console.log(`Usage: random-words [options]
37
+
38
+ Options:
39
+ -n, --count <number> Number of words to generate (default: 4)
40
+ -s, --separator <char> Separator between words (default: -)
41
+ -f, --format <type> Output format: joined, array, or newline (default: joined)
42
+ -h, --help Show this help message
43
+
44
+ Examples:
45
+ random-words
46
+ random-words -n 6
47
+ random-words -n 3 -s _
48
+ random-words -n 2 -f array`);
49
+ process.exit(0);
50
+ } else if (!arg.startsWith('-')) {
51
+ const val = parseInt(arg);
52
+ if (!isNaN(val)) {
53
+ if (val < 1 || val > 1000) {
54
+ console.error('Error: count must be between 1 and 1000');
55
+ process.exit(1);
56
+ }
57
+ count = val;
58
+ }
59
+ }
60
+ }
61
+
62
+ const words: string[] = randomWords(count);
63
+
64
+ if (format === 'array') {
65
+ console.log(JSON.stringify(words));
66
+ } else if (format === 'newline') {
67
+ console.log(words.join('\n'));
68
+ } else {
69
+ console.log(words.join(separator));
70
+ }
package/src/words.ts ADDED
@@ -0,0 +1,35 @@
1
+ import { randomInt } from 'node:crypto';
2
+ // @ts-ignore - The package doesn't have types but we know it's an array of strings
3
+ import words from 'an-array-of-english-words';
4
+
5
+ /**
6
+ * Returns a list of random words.
7
+ * This is now more efficient for large lists by picking individual random indices
8
+ * instead of shuffling the entire array.
9
+ *
10
+ * @param count Number of words to return
11
+ * @returns Array of random words
12
+ */
13
+ export function randomWords(count: number = 4): string[] {
14
+ const n = words.length;
15
+ const actualCount = Math.max(0, Math.min(count, n));
16
+
17
+ if (actualCount === 0) return [];
18
+
19
+ const result: string[] = [];
20
+ const seenIndices = new Set<number>();
21
+
22
+ // For small counts, we use a Set to pick unique indices.
23
+ // This is O(count) instead of O(N) where N is the total number of words (275,000+).
24
+ while (result.length < actualCount) {
25
+ const j = randomInt(0, n);
26
+ if (!seenIndices.has(j)) {
27
+ seenIndices.add(j);
28
+ result.push(words[j]);
29
+ }
30
+ }
31
+
32
+ return result;
33
+ }
34
+
35
+ export { words };
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "dist",
10
+ "rootDir": "src",
11
+ "declaration": true,
12
+ "sourceMap": true,
13
+ "resolveJsonModule": true,
14
+ "forceConsistentCasingInFileNames": true
15
+ },
16
+ "include": ["src"]
17
+ }