smart-passphrase 1.0.2 → 2.0.2

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,44 +1,52 @@
1
+ Human-readable, secure password generator for developers and CLI usage.
1
2
  # smart-passphrase
2
3
 
3
4
  A lightweight, secure, and memorable passphrase generator for Node.js and modern browsers. Built to work smoothly in React, Next.js, Vite, and plain Node projects.
4
5
 
5
6
  ## Features
6
- - Human‑readable passphrases with strong randomness
7
- - Cryptographically secure randomness (via `crypto.getRandomValues`)
8
- - Works in React, Next.js, Vite, and Node 18+
9
- - Fully typed TypeScript API
10
- - Configurable casing, symbols, numbers, and word patterns
11
- - Custom dictionary support
7
+ - **Memorable**: Human‑readable passphrases that are easy to type and remember.
8
+ - **Secure**: Uses cryptographically secure randomness (via `crypto.getRandomValues`).
9
+ - **Versatile**: Works in React, Next.js, Vite, and Node 18+.
10
+ - **Premium CLI**: Beautifully styled terminal output with colors and animations.
11
+ - **Clipboard Support**: Copy passphrases instantly from the CLI.
12
+ - **Zero Dependencies (Core)**: Only adds dependencies for the CLI tool.
13
+ - **Fully Typed**: Written in TypeScript with a complete API.
12
14
 
13
15
  ## Install
14
16
  ```bash
15
17
  npm install smart-passphrase
16
18
  ```
17
19
 
18
- ## Quick Start
19
- ```ts
20
- import { generatePassphrase } from "smart-passphrase";
21
-
22
- const passphrase = generatePassphrase();
23
- console.log(passphrase);
20
+ ## Quick Start (Terminal)
21
+ Generate a passphrase instantly with a premium experience:
22
+ ```bash
23
+ npx smart-passphrase
24
24
  ```
25
25
 
26
- ## Example Output
26
+ **Pro Tip (Copy to clipboard):**
27
+ ```bash
28
+ npx smart-passphrase copy
29
+ # or
30
+ npx smart-passphrase -c
27
31
  ```
28
- SilentGOOSE^mark324
29
- BrAveTiger#run891
30
- quickROCKET=jump472
32
+
33
+ **Custom Strength:**
34
+ ```bash
35
+ npx smart-passphrase --strength ultra
31
36
  ```
32
37
 
33
- ## Usage in React / Next.js / Vite
34
- ```tsx
38
+ ## Quick Start (Code)
39
+ ```ts
35
40
  import { generatePassphrase } from "smart-passphrase";
36
41
 
37
- export default function App() {
38
- return <h2>{generatePassphrase()}</h2>;
39
- }
42
+ const passphrase = generatePassphrase();
43
+ console.log(passphrase);
44
+ // Output: SilentGOOSE^mark324
40
45
  ```
41
46
 
47
+ ## Example Output
48
+ The CLI provides a vibrant, gradient-colored output that stands out!
49
+
42
50
  ## API
43
51
 
44
52
  ### `generatePassphrase(options?)`
@@ -71,102 +79,51 @@ const bits = entropyEstimate({ words: 4, symbols: true, numbers: true });
71
79
  ## Options
72
80
 
73
81
  ### `words`
74
- Number of word tokens in the passphrase.
75
-
76
- ```ts
77
- generatePassphrase({ words: 4 });
78
- ```
82
+ Number of word tokens in the passphrase. Default is `3`.
79
83
 
80
84
  ### `numbers`
81
- - `true` (default) to add digits
82
- - `{ digits: number }` to control length
83
-
84
- ```ts
85
- generatePassphrase({ numbers: { digits: 4 } });
86
- ```
85
+ - `true` (default): Adds digits.
86
+ - `{ digits: number }`: Controls the number of digits.
87
+ - `false`: Removes digits.
87
88
 
88
89
  ### `symbols`
89
- - `true` (default) to include symbols
90
- - `string[]` to provide your own symbol list
91
-
92
- ```ts
93
- generatePassphrase({ symbols: ["$", "-", "_"] });
94
- ```
90
+ - `true` (default): Includes one random symbol.
91
+ - `string[]`: Provide your own symbol list to pick from.
92
+ - `false`: Removes symbols.
95
93
 
96
94
  ### `uppercaseStyle`
97
- Controls casing:
98
-
99
- ```ts
100
- "none" | "random" | "title" | "upper" | "lower"
101
- ```
102
-
103
- Example:
104
- ```ts
105
- generatePassphrase({ uppercaseStyle: "title" });
106
- ```
107
-
108
- ### `separator`
109
- String inserted between all parts.
110
-
111
- ```ts
112
- generatePassphrase({ separator: "-" });
113
- ```
114
-
115
- ### `unique`
116
- Avoid repeating words within the same passphrase.
117
-
118
- ```ts
119
- generatePassphrase({ unique: true });
120
- ```
95
+ Controls casing style for the words:
96
+ `"none" | "random" | "title" | "upper" | "lower"`
121
97
 
122
98
  ### `strength`
123
- Preset security tiers:
124
-
125
- ```ts
126
- "medium" | "strong" | "ultra"
127
- ```
128
-
129
- Each tier increases words, digits, and entropy.
130
-
131
- ```ts
132
- generatePassphrase({ strength: "ultra" });
133
- ```
134
-
135
- ### `pattern`
136
- Custom order of word types.
137
-
138
- ```ts
139
- generatePassphrase({ pattern: ["adj", "noun", "verb"] });
140
- ```
141
-
142
- Available word kinds:
143
- ```ts
144
- "adj" | "noun" | "verb"
145
- ```
146
-
147
- ### `dictionary`
148
- Override the default word lists.
149
-
150
- ```ts
151
- generatePassphrase({
152
- dictionary: {
153
- adjectives: ["silent", "rapid"],
154
- nouns: ["fox", "river"],
155
- verbs: ["glide", "spark"]
156
- }
157
- });
158
- ```
99
+ Preset security tiers that adjust words, symbols, and digits.
100
+
101
+ | Tier | Words | Symbols | Digits | Formatting | Approx. Entropy |
102
+ | :--- | :--- | :--- | :--- | :--- | :--- |
103
+ | `easy` | 3 | No | 2 | Title-Case-99 | ~30-35 bits |
104
+ | `medium` | 3 | Yes | 3 | raNDomSYmb=123 | ~45-50 bits |
105
+ | `strong` | 4 | Yes | 4 | rAndOMWordS#5432 | ~60-70 bits |
106
+ | `ultra` | 5 | Yes | 5 | vErYStrONgWOrDs%12345 | ~80+ bits |
107
+
108
+ ## CLI Reference
109
+ Run `npx smart-passphrase [command] [options]` or install globally.
110
+
111
+ ### Commands
112
+ - `[default]` - Generate a passphrase.
113
+ - `copy` - Generate and copy to clipboard immediately.
114
+
115
+ ### Options
116
+ - `-w, --words <n>` - Set the number of words (default: 3).
117
+ - `-s, --strength <tier>` - Set tier (easy, medium, strong, ultra).
118
+ - `-c, --copy` - Copy generated passphrase to clipboard.
119
+ - `-n, --numbers` - Include numbers.
120
+ - `-N, --no-numbers` - Disable numbers.
121
+ - `-S, --symbols` - Include symbols.
122
+ - `-X, --no-symbols` - Disable symbols.
159
123
 
160
124
  ## Security Notes
161
125
  - Uses `crypto.getRandomValues` for strong randomness.
162
126
  - Requires Node 18+ or a modern browser runtime.
163
- - For even higher entropy, increase `words`, `digits`, or use `strength: "ultra"`.
164
127
 
165
128
  ## License
166
- MIT
167
- ```
168
-
169
- If you want, I can also add:
170
- 1. A `COPY` helper utility example
171
- 2. A CLI usage snippet
172
- 3. A comparison table for `strength` presets
129
+ MIT
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const ora_1 = __importDefault(require("ora"));
10
+ const clipboardy_1 = __importDefault(require("clipboardy"));
11
+ const gradient_string_1 = __importDefault(require("gradient-string"));
12
+ const generator_js_1 = require("./generator.js");
13
+ const program = new commander_1.Command();
14
+ program
15
+ .name("smart-passphrase")
16
+ .description("Generate secure and memorable passphrases with style")
17
+ .version("2.0.0")
18
+ .option("-w, --words <number>", "number of words", (val) => parseInt(val), 3)
19
+ .option("-s, --strength <tier>", "security tier (easy, medium, strong, ultra)", "medium")
20
+ .option("-n, --numbers", "include numbers")
21
+ .option("--no-numbers", "exclude numbers")
22
+ .option("-S, --symbols", "include symbols")
23
+ .option("--no-symbols", "exclude symbols")
24
+ .option("-c, --copy", "copy to clipboard")
25
+ .action(async (options) => {
26
+ console.log("");
27
+ const spinner = (0, ora_1.default)({
28
+ text: chalk_1.default.cyan("Locking the gears..."),
29
+ color: "cyan"
30
+ }).start();
31
+ // Small delay for "fancy" feel
32
+ await new Promise((resolve) => setTimeout(resolve, 600));
33
+ try {
34
+ const password = (0, generator_js_1.generatePassword)({
35
+ words: options.words,
36
+ strength: options.strength,
37
+ numbers: options.numbers,
38
+ symbols: options.symbols,
39
+ });
40
+ spinner.succeed(chalk_1.default.green("Secure passphrase ready!"));
41
+ console.log("");
42
+ const styledPassword = gradient_string_1.default.pastel.multiline(password);
43
+ console.log(" " + chalk_1.default.bold(styledPassword));
44
+ console.log("");
45
+ if (options.copy) {
46
+ clipboardy_1.default.writeSync(password);
47
+ console.log(chalk_1.default.dim(" 📋 Copied to clipboard!"));
48
+ console.log("");
49
+ }
50
+ }
51
+ catch (error) {
52
+ spinner.fail(chalk_1.default.red("Generation failed"));
53
+ console.error(chalk_1.default.red(` Error: ${error.message}`));
54
+ process.exit(1);
55
+ }
56
+ });
57
+ // Handle the "copy" as a shorthand command too
58
+ program
59
+ .command("copy")
60
+ .description("Generate and copy to clipboard immediately")
61
+ .action(async () => {
62
+ const spinner = (0, ora_1.default)(chalk_1.default.cyan("Generating and copying...")).start();
63
+ await new Promise((resolve) => setTimeout(resolve, 400));
64
+ const password = (0, generator_js_1.generatePassword)();
65
+ clipboardy_1.default.writeSync(password);
66
+ spinner.succeed(chalk_1.default.green("Generated and copied to clipboard!"));
67
+ console.log("");
68
+ console.log(" " + gradient_string_1.default.pastel(password));
69
+ console.log("");
70
+ });
71
+ program.parse();
@@ -3,18 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generatePassphrase = generatePassphrase;
4
4
  exports.generatePassword = generatePassword;
5
5
  exports.entropyEstimate = entropyEstimate;
6
- const wordlist_1 = require("./wordlist");
7
- const utils_1 = require("./utils");
6
+ const wordlist_js_1 = require("./wordlist.js");
7
+ const utils_js_1 = require("./utils.js");
8
8
  const strengthDefaults = {
9
- medium: { words: 3, symbols: true, digits: 3 },
10
- strong: { words: 4, symbols: true, digits: 4 },
11
- ultra: { words: 5, symbols: true, digits: 5 }
9
+ easy: { words: 3, symbols: false, numbers: true, digits: 2, uppercaseStyle: "title", separator: "-" },
10
+ medium: { words: 3, symbols: true, numbers: true, digits: 3 },
11
+ strong: { words: 4, symbols: true, numbers: true, digits: 4 },
12
+ ultra: { words: 5, symbols: true, numbers: true, digits: 5 }
12
13
  };
13
- function buildDictionary(overrides) {
14
+ function buildDictionary(overrides, strength) {
15
+ if (strength === "easy") {
16
+ return {
17
+ adjectives: overrides?.adjectives ?? wordlist_js_1.simpleAdjectives,
18
+ nouns: overrides?.nouns ?? wordlist_js_1.simpleNouns,
19
+ verbs: overrides?.verbs ?? wordlist_js_1.simpleVerbs
20
+ };
21
+ }
14
22
  return {
15
- adjectives: overrides?.adjectives ?? wordlist_1.adjectives,
16
- nouns: overrides?.nouns ?? wordlist_1.nouns,
17
- verbs: overrides?.verbs ?? wordlist_1.verbs
23
+ adjectives: overrides?.adjectives ?? wordlist_js_1.adjectives,
24
+ nouns: overrides?.nouns ?? wordlist_js_1.nouns,
25
+ verbs: overrides?.verbs ?? wordlist_js_1.verbs
18
26
  };
19
27
  }
20
28
  function chooseWords(dictionary, kinds, unique) {
@@ -25,11 +33,11 @@ function chooseWords(dictionary, kinds, unique) {
25
33
  if (list.length === 0) {
26
34
  throw new Error(`Dictionary list for ${kind} is empty`);
27
35
  }
28
- let picked = (0, utils_1.randomItem)(list);
36
+ let picked = (0, utils_js_1.randomItem)(list);
29
37
  if (unique) {
30
38
  let attempts = 0;
31
39
  while (used.has(picked) && attempts < 25) {
32
- picked = (0, utils_1.randomItem)(list);
40
+ picked = (0, utils_js_1.randomItem)(list);
33
41
  attempts += 1;
34
42
  }
35
43
  }
@@ -50,27 +58,27 @@ function generatePassphrase(options = {}) {
50
58
  const defaults = options.strength ? strengthDefaults[options.strength] : strengthDefaults.medium;
51
59
  const wordCount = options.words ?? defaults.words;
52
60
  const useSymbols = options.symbols ?? defaults.symbols;
53
- const numbersOption = options.numbers ?? true;
61
+ const numbersOption = options.numbers ?? defaults.numbers;
54
62
  const digits = typeof numbersOption === "object" ? numbersOption.digits ?? defaults.digits : defaults.digits;
55
- const separator = options.separator ?? "";
56
- const uppercaseStyle = options.uppercaseStyle ?? "random";
63
+ const separator = options.separator ?? defaults.separator ?? "";
64
+ const uppercaseStyle = options.uppercaseStyle ?? defaults.uppercaseStyle ?? "random";
57
65
  const unique = options.unique ?? true;
58
- const dictionary = buildDictionary(options.dictionary);
66
+ const dictionary = buildDictionary(options.dictionary, options.strength);
59
67
  const pattern = options.pattern ?? defaultPattern(wordCount);
60
68
  if (pattern.length !== wordCount) {
61
69
  throw new Error("pattern length must match words");
62
70
  }
63
- const words = chooseWords(dictionary, pattern, unique).map((word) => (0, utils_1.applyCase)(word, uppercaseStyle));
71
+ const words = chooseWords(dictionary, pattern, unique).map((word) => (0, utils_js_1.applyCase)(word, uppercaseStyle));
64
72
  const parts = [];
65
73
  for (const word of words) {
66
74
  parts.push(word);
67
75
  }
68
76
  if (useSymbols) {
69
- const symbols = Array.isArray(useSymbols) ? useSymbols : utils_1.defaultSymbols;
70
- parts.push((0, utils_1.randomItem)(symbols));
77
+ const symbols = Array.isArray(useSymbols) ? useSymbols : utils_js_1.defaultSymbols;
78
+ parts.push((0, utils_js_1.randomItem)(symbols));
71
79
  }
72
80
  if (numbersOption) {
73
- parts.push(String((0, utils_1.randomNumberByDigits)(digits)));
81
+ parts.push(String((0, utils_js_1.randomNumberByDigits)(digits)));
74
82
  }
75
83
  if (separator) {
76
84
  return parts.join(separator);
@@ -86,14 +94,14 @@ function entropyEstimate(options = {}) {
86
94
  const useSymbols = options.symbols ?? defaults.symbols;
87
95
  const numbersOption = options.numbers ?? true;
88
96
  const digits = typeof numbersOption === "object" ? numbersOption.digits ?? defaults.digits : defaults.digits;
89
- const dictionary = buildDictionary(options.dictionary);
97
+ const dictionary = buildDictionary(options.dictionary, options.strength);
90
98
  const pattern = options.pattern ?? defaultPattern(wordCount);
91
99
  const sizes = pattern.map((kind) => {
92
100
  const list = kind === "adj" ? dictionary.adjectives : kind === "noun" ? dictionary.nouns : dictionary.verbs;
93
101
  return list.length || 1;
94
102
  });
95
103
  const wordSpace = sizes.reduce((acc, size) => acc * size, 1);
96
- const symbolSpace = useSymbols ? (Array.isArray(useSymbols) ? useSymbols.length : utils_1.defaultSymbols.length) : 1;
104
+ const symbolSpace = useSymbols ? (Array.isArray(useSymbols) ? useSymbols.length : utils_js_1.defaultSymbols.length) : 1;
97
105
  const numberSpace = numbersOption ? Math.pow(10, digits) : 1;
98
106
  const total = wordSpace * symbolSpace * numberSpace;
99
107
  return Math.log2(total);
package/dist/cjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.entropyEstimate = exports.generatePassword = exports.generatePassphrase = void 0;
4
- var generator_1 = require("./generator");
5
- Object.defineProperty(exports, "generatePassphrase", { enumerable: true, get: function () { return generator_1.generatePassphrase; } });
6
- Object.defineProperty(exports, "generatePassword", { enumerable: true, get: function () { return generator_1.generatePassword; } });
7
- Object.defineProperty(exports, "entropyEstimate", { enumerable: true, get: function () { return generator_1.entropyEstimate; } });
4
+ var generator_js_1 = require("./generator.js");
5
+ Object.defineProperty(exports, "generatePassphrase", { enumerable: true, get: function () { return generator_js_1.generatePassphrase; } });
6
+ Object.defineProperty(exports, "generatePassword", { enumerable: true, get: function () { return generator_js_1.generatePassword; } });
7
+ Object.defineProperty(exports, "entropyEstimate", { enumerable: true, get: function () { return generator_js_1.entropyEstimate; } });
@@ -1,10 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.verbs = exports.nouns = exports.adjectives = void 0;
4
- exports.adjectives = [
5
- // Original
3
+ exports.verbs = exports.complexVerbs = exports.simpleVerbs = exports.nouns = exports.complexNouns = exports.simpleNouns = exports.adjectives = exports.complexAdjectives = exports.simpleAdjectives = void 0;
4
+ exports.simpleAdjectives = [
6
5
  "quick", "silent", "brave", "fancy", "eager", "bright",
7
6
  "gentle", "proud", "curious", "steady", "bold", "calm",
7
+ "happy", "tiny", "large", "blue", "red", "green", "fast",
8
+ "slow", "smart", "kind", "soft", "hard"
9
+ ];
10
+ exports.complexAdjectives = [
8
11
  // Advanced originals
9
12
  "resilient", "ephemeral", "luminous", "tenacious", "serene",
10
13
  "voracious", "ethereal", "stoic", "audacious", "fleeting",
@@ -38,10 +41,14 @@ exports.adjectives = [
38
41
  "undying",
39
42
  "everlasting"
40
43
  ];
41
- exports.nouns = [
42
- // Original
44
+ exports.adjectives = [...exports.simpleAdjectives, ...exports.complexAdjectives];
45
+ exports.simpleNouns = [
43
46
  "tiger", "goose", "rocket", "mask", "forest", "ocean",
44
47
  "ember", "comet", "cipher", "garden", "river", "mountain",
48
+ "cat", "dog", "bird", "tree", "star", "moon", "sun",
49
+ "book", "pen", "desk", "home", "road"
50
+ ];
51
+ exports.complexNouns = [
45
52
  // Advanced originals
46
53
  "phantom", "echo", "horizon", "sentinel", "abyss", "beacon",
47
54
  "shroud", "monolith", "specter", "harbinger", "labyrinth",
@@ -75,10 +82,13 @@ exports.nouns = [
75
82
  "archive",
76
83
  "chronicle"
77
84
  ];
78
- exports.verbs = [
79
- // Original
85
+ exports.nouns = [...exports.simpleNouns, ...exports.complexNouns];
86
+ exports.simpleVerbs = [
80
87
  "run", "jump", "build", "mark", "drift", "spark",
81
88
  "forge", "glide", "shift", "trace", "craft", "sprint",
89
+ "walk", "sing", "read", "play", "draw", "smile"
90
+ ];
91
+ exports.complexVerbs = [
82
92
  // Advanced originals
83
93
  "lunge", "flicker", "shatter", "conceal", "summon", "evade",
84
94
  "descend", "ascend", "sunder", "kindle", "lurk", "surge",
@@ -111,3 +121,4 @@ exports.verbs = [
111
121
  "memorialize",
112
122
  "pay homage"
113
123
  ];
124
+ exports.verbs = [...exports.simpleVerbs, ...exports.complexVerbs];
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import chalk from "chalk";
4
+ import ora from "ora";
5
+ import clipboardy from "clipboardy";
6
+ import gradient from "gradient-string";
7
+ import { generatePassword } from "./generator.js";
8
+ const program = new Command();
9
+ program
10
+ .name("smart-passphrase")
11
+ .description("Generate secure and memorable passphrases with style")
12
+ .version("2.0.0")
13
+ .option("-w, --words <number>", "number of words", (val) => parseInt(val), 3)
14
+ .option("-s, --strength <tier>", "security tier (easy, medium, strong, ultra)", "medium")
15
+ .option("-n, --numbers", "include numbers")
16
+ .option("--no-numbers", "exclude numbers")
17
+ .option("-S, --symbols", "include symbols")
18
+ .option("--no-symbols", "exclude symbols")
19
+ .option("-c, --copy", "copy to clipboard")
20
+ .action(async (options) => {
21
+ console.log("");
22
+ const spinner = ora({
23
+ text: chalk.cyan("Locking the gears..."),
24
+ color: "cyan"
25
+ }).start();
26
+ // Small delay for "fancy" feel
27
+ await new Promise((resolve) => setTimeout(resolve, 600));
28
+ try {
29
+ const password = generatePassword({
30
+ words: options.words,
31
+ strength: options.strength,
32
+ numbers: options.numbers,
33
+ symbols: options.symbols,
34
+ });
35
+ spinner.succeed(chalk.green("Secure passphrase ready!"));
36
+ console.log("");
37
+ const styledPassword = gradient.pastel.multiline(password);
38
+ console.log(" " + chalk.bold(styledPassword));
39
+ console.log("");
40
+ if (options.copy) {
41
+ clipboardy.writeSync(password);
42
+ console.log(chalk.dim(" 📋 Copied to clipboard!"));
43
+ console.log("");
44
+ }
45
+ }
46
+ catch (error) {
47
+ spinner.fail(chalk.red("Generation failed"));
48
+ console.error(chalk.red(` Error: ${error.message}`));
49
+ process.exit(1);
50
+ }
51
+ });
52
+ // Handle the "copy" as a shorthand command too
53
+ program
54
+ .command("copy")
55
+ .description("Generate and copy to clipboard immediately")
56
+ .action(async () => {
57
+ const spinner = ora(chalk.cyan("Generating and copying...")).start();
58
+ await new Promise((resolve) => setTimeout(resolve, 400));
59
+ const password = generatePassword();
60
+ clipboardy.writeSync(password);
61
+ spinner.succeed(chalk.green("Generated and copied to clipboard!"));
62
+ console.log("");
63
+ console.log(" " + gradient.pastel(password));
64
+ console.log("");
65
+ });
66
+ program.parse();
@@ -1,4 +1,4 @@
1
- import type { GenerateOptions } from "./types";
1
+ import type { GenerateOptions } from "./types.js";
2
2
  export declare function generatePassphrase(options?: GenerateOptions): string;
3
3
  export declare function generatePassword(options?: GenerateOptions): string;
4
4
  export declare function entropyEstimate(options?: GenerateOptions): number;
@@ -0,0 +1,103 @@
1
+ import { adjectives, nouns, verbs, simpleAdjectives, simpleNouns, simpleVerbs } from "./wordlist.js";
2
+ import { applyCase, defaultSymbols, randomItem, randomNumberByDigits } from "./utils.js";
3
+ const strengthDefaults = {
4
+ easy: { words: 3, symbols: false, numbers: true, digits: 2, uppercaseStyle: "title", separator: "-" },
5
+ medium: { words: 3, symbols: true, numbers: true, digits: 3 },
6
+ strong: { words: 4, symbols: true, numbers: true, digits: 4 },
7
+ ultra: { words: 5, symbols: true, numbers: true, digits: 5 }
8
+ };
9
+ function buildDictionary(overrides, strength) {
10
+ if (strength === "easy") {
11
+ return {
12
+ adjectives: overrides?.adjectives ?? simpleAdjectives,
13
+ nouns: overrides?.nouns ?? simpleNouns,
14
+ verbs: overrides?.verbs ?? simpleVerbs
15
+ };
16
+ }
17
+ return {
18
+ adjectives: overrides?.adjectives ?? adjectives,
19
+ nouns: overrides?.nouns ?? nouns,
20
+ verbs: overrides?.verbs ?? verbs
21
+ };
22
+ }
23
+ function chooseWords(dictionary, kinds, unique) {
24
+ const used = new Set();
25
+ const output = [];
26
+ for (const kind of kinds) {
27
+ const list = kind === "adj" ? dictionary.adjectives : kind === "noun" ? dictionary.nouns : dictionary.verbs;
28
+ if (list.length === 0) {
29
+ throw new Error(`Dictionary list for ${kind} is empty`);
30
+ }
31
+ let picked = randomItem(list);
32
+ if (unique) {
33
+ let attempts = 0;
34
+ while (used.has(picked) && attempts < 25) {
35
+ picked = randomItem(list);
36
+ attempts += 1;
37
+ }
38
+ }
39
+ used.add(picked);
40
+ output.push(picked);
41
+ }
42
+ return output;
43
+ }
44
+ function defaultPattern(words) {
45
+ const pool = ["adj", "noun", "verb"];
46
+ const output = [];
47
+ for (let i = 0; i < words; i += 1) {
48
+ output.push(pool[i % pool.length]);
49
+ }
50
+ return output;
51
+ }
52
+ export function generatePassphrase(options = {}) {
53
+ const defaults = options.strength ? strengthDefaults[options.strength] : strengthDefaults.medium;
54
+ const wordCount = options.words ?? defaults.words;
55
+ const useSymbols = options.symbols ?? defaults.symbols;
56
+ const numbersOption = options.numbers ?? defaults.numbers;
57
+ const digits = typeof numbersOption === "object" ? numbersOption.digits ?? defaults.digits : defaults.digits;
58
+ const separator = options.separator ?? defaults.separator ?? "";
59
+ const uppercaseStyle = options.uppercaseStyle ?? defaults.uppercaseStyle ?? "random";
60
+ const unique = options.unique ?? true;
61
+ const dictionary = buildDictionary(options.dictionary, options.strength);
62
+ const pattern = options.pattern ?? defaultPattern(wordCount);
63
+ if (pattern.length !== wordCount) {
64
+ throw new Error("pattern length must match words");
65
+ }
66
+ const words = chooseWords(dictionary, pattern, unique).map((word) => applyCase(word, uppercaseStyle));
67
+ const parts = [];
68
+ for (const word of words) {
69
+ parts.push(word);
70
+ }
71
+ if (useSymbols) {
72
+ const symbols = Array.isArray(useSymbols) ? useSymbols : defaultSymbols;
73
+ parts.push(randomItem(symbols));
74
+ }
75
+ if (numbersOption) {
76
+ parts.push(String(randomNumberByDigits(digits)));
77
+ }
78
+ if (separator) {
79
+ return parts.join(separator);
80
+ }
81
+ return parts.join("");
82
+ }
83
+ export function generatePassword(options = {}) {
84
+ return generatePassphrase(options);
85
+ }
86
+ export function entropyEstimate(options = {}) {
87
+ const defaults = options.strength ? strengthDefaults[options.strength] : strengthDefaults.medium;
88
+ const wordCount = options.words ?? defaults.words;
89
+ const useSymbols = options.symbols ?? defaults.symbols;
90
+ const numbersOption = options.numbers ?? true;
91
+ const digits = typeof numbersOption === "object" ? numbersOption.digits ?? defaults.digits : defaults.digits;
92
+ const dictionary = buildDictionary(options.dictionary, options.strength);
93
+ const pattern = options.pattern ?? defaultPattern(wordCount);
94
+ const sizes = pattern.map((kind) => {
95
+ const list = kind === "adj" ? dictionary.adjectives : kind === "noun" ? dictionary.nouns : dictionary.verbs;
96
+ return list.length || 1;
97
+ });
98
+ const wordSpace = sizes.reduce((acc, size) => acc * size, 1);
99
+ const symbolSpace = useSymbols ? (Array.isArray(useSymbols) ? useSymbols.length : defaultSymbols.length) : 1;
100
+ const numberSpace = numbersOption ? Math.pow(10, digits) : 1;
101
+ const total = wordSpace * symbolSpace * numberSpace;
102
+ return Math.log2(total);
103
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { generatePassphrase, generatePassword, entropyEstimate } from "./generator";
2
- export type { GenerateOptions, Dictionary, UppercaseStyle, Strength, WordKind } from "./types";
1
+ export { generatePassphrase, generatePassword, entropyEstimate } from "./generator.js";
2
+ export type { GenerateOptions, Dictionary, UppercaseStyle, Strength, WordKind } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { generatePassphrase, generatePassword, entropyEstimate } from "./generator.js";
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type UppercaseStyle = "none" | "random" | "title" | "upper" | "lower";
2
- export type Strength = "medium" | "strong" | "ultra";
2
+ export type Strength = "easy" | "medium" | "strong" | "ultra";
3
3
  export type WordKind = "adj" | "noun" | "verb";
4
4
  export interface Dictionary {
5
5
  adjectives: string[];
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/utils.js ADDED
@@ -0,0 +1,51 @@
1
+ export const defaultSymbols = ["@", "#", "^", "=", ")", "!", "*", "%"];
2
+ export function secureRandomInt(maxExclusive) {
3
+ if (!Number.isInteger(maxExclusive) || maxExclusive <= 0) {
4
+ throw new Error("maxExclusive must be a positive integer");
5
+ }
6
+ const cryptoObj = globalThis.crypto;
7
+ if (!cryptoObj || typeof cryptoObj.getRandomValues !== "function") {
8
+ throw new Error("Secure crypto is not available in this environment");
9
+ }
10
+ const range = 0x100000000;
11
+ const limit = range - (range % maxExclusive);
12
+ const buf = new Uint32Array(1);
13
+ let value = 0;
14
+ do {
15
+ cryptoObj.getRandomValues(buf);
16
+ value = buf[0];
17
+ } while (value >= limit);
18
+ return value % maxExclusive;
19
+ }
20
+ export function randomItem(items) {
21
+ if (items.length === 0) {
22
+ throw new Error("Cannot choose from an empty list");
23
+ }
24
+ return items[secureRandomInt(items.length)];
25
+ }
26
+ export function randomBool() {
27
+ return secureRandomInt(2) === 1;
28
+ }
29
+ export function applyCase(word, style) {
30
+ if (style === "none")
31
+ return word;
32
+ if (style === "upper")
33
+ return word.toUpperCase();
34
+ if (style === "lower")
35
+ return word.toLowerCase();
36
+ if (style === "title")
37
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
38
+ return word
39
+ .split("")
40
+ .map((ch) => (randomBool() ? ch.toUpperCase() : ch.toLowerCase()))
41
+ .join("");
42
+ }
43
+ export function randomNumberByDigits(digits) {
44
+ if (!Number.isInteger(digits) || digits <= 0) {
45
+ throw new Error("digits must be a positive integer");
46
+ }
47
+ const min = Math.pow(10, digits - 1);
48
+ const max = Math.pow(10, digits) - 1;
49
+ const span = max - min + 1;
50
+ return min + secureRandomInt(span);
51
+ }
@@ -1,3 +1,9 @@
1
+ export declare const simpleAdjectives: string[];
2
+ export declare const complexAdjectives: string[];
1
3
  export declare const adjectives: string[];
4
+ export declare const simpleNouns: string[];
5
+ export declare const complexNouns: string[];
2
6
  export declare const nouns: string[];
7
+ export declare const simpleVerbs: string[];
8
+ export declare const complexVerbs: string[];
3
9
  export declare const verbs: string[];
@@ -0,0 +1,121 @@
1
+ export const simpleAdjectives = [
2
+ "quick", "silent", "brave", "fancy", "eager", "bright",
3
+ "gentle", "proud", "curious", "steady", "bold", "calm",
4
+ "happy", "tiny", "large", "blue", "red", "green", "fast",
5
+ "slow", "smart", "kind", "soft", "hard"
6
+ ];
7
+ export const complexAdjectives = [
8
+ // Advanced originals
9
+ "resilient", "ephemeral", "luminous", "tenacious", "serene",
10
+ "voracious", "ethereal", "stoic", "audacious", "fleeting",
11
+ "mellifluous", "incisive", "unyielding", "sublime", "transient",
12
+ "ferocious", "cunning", "reclusive", "radiant", "somber",
13
+ "deft", "keen", "vigilant", "nimble", "profound",
14
+ // New: memorial + solemn / meaningful tone
15
+ "memorial",
16
+ "immortal",
17
+ "reverent",
18
+ "hallowed",
19
+ "eternal",
20
+ "venerable",
21
+ "sacred",
22
+ "mournful",
23
+ "remnant",
24
+ "honored",
25
+ "legendary",
26
+ "fabled",
27
+ "timeless",
28
+ "perpetual",
29
+ "solemn",
30
+ "devoted",
31
+ "faithful",
32
+ "righteous",
33
+ "valiant",
34
+ "gallant",
35
+ "exalted",
36
+ "revered",
37
+ "venerated",
38
+ "undying",
39
+ "everlasting"
40
+ ];
41
+ export const adjectives = [...simpleAdjectives, ...complexAdjectives];
42
+ export const simpleNouns = [
43
+ "tiger", "goose", "rocket", "mask", "forest", "ocean",
44
+ "ember", "comet", "cipher", "garden", "river", "mountain",
45
+ "cat", "dog", "bird", "tree", "star", "moon", "sun",
46
+ "book", "pen", "desk", "home", "road"
47
+ ];
48
+ export const complexNouns = [
49
+ // Advanced originals
50
+ "phantom", "echo", "horizon", "sentinel", "abyss", "beacon",
51
+ "shroud", "monolith", "specter", "harbinger", "labyrinth",
52
+ "oracle", "crucible", "vigil", "rift", "veil", "tempest",
53
+ "solstice", "requiem", "synapse", "nexus", "axiom", "enigma",
54
+ "relic", "citadel",
55
+ // New: memorial + meaningful / legacy words
56
+ "memorial",
57
+ "monument",
58
+ "epitaph",
59
+ "shrine",
60
+ "tomb",
61
+ "grave",
62
+ "remembrance",
63
+ "legacy",
64
+ "heritage",
65
+ "echo",
66
+ "keepsake",
67
+ "memento",
68
+ "relic",
69
+ "vestige",
70
+ "tribute",
71
+ "offering",
72
+ "vow",
73
+ "oath",
74
+ "covenant",
75
+ "pillar",
76
+ "obelisk",
77
+ "chapel",
78
+ "sanctum",
79
+ "archive",
80
+ "chronicle"
81
+ ];
82
+ export const nouns = [...simpleNouns, ...complexNouns];
83
+ export const simpleVerbs = [
84
+ "run", "jump", "build", "mark", "drift", "spark",
85
+ "forge", "glide", "shift", "trace", "craft", "sprint",
86
+ "walk", "sing", "read", "play", "draw", "smile"
87
+ ];
88
+ export const complexVerbs = [
89
+ // Advanced originals
90
+ "lunge", "flicker", "shatter", "conceal", "summon", "evade",
91
+ "descend", "ascend", "sunder", "kindle", "lurk", "surge",
92
+ "fracture", "unravel", "coalesce", "sever", "ignite", "linger",
93
+ "plummet", "scatter", "converge", "diverge", "emanate", "sear", "wither",
94
+ // New: memorial / reverent / legacy actions
95
+ "honor",
96
+ "cherish",
97
+ "enshrine",
98
+ "lament",
99
+ "mourn",
100
+ "remember",
101
+ "immortalize",
102
+ "consecrate",
103
+ "dedicate",
104
+ "sanctify",
105
+ "preserve",
106
+ "bequeath",
107
+ "endure",
108
+ "inscribe",
109
+ "carve",
110
+ "engrave",
111
+ "invoke",
112
+ "recall",
113
+ "revere",
114
+ "venerate",
115
+ "exalt",
116
+ "glorify",
117
+ "commemorate",
118
+ "memorialize",
119
+ "pay homage"
120
+ ];
121
+ export const verbs = [...simpleVerbs, ...complexVerbs];
package/package.json CHANGED
@@ -1,16 +1,14 @@
1
1
  {
2
2
  "name": "smart-passphrase",
3
- "version": "1.0.2",
3
+ "version": "2.0.2",
4
4
  "description": "Memorable, secure passphrase generator for web and Node.",
5
5
  "license": "MIT",
6
6
  "author": "Ayush Solanki <ayushsolanki2901@gmail.com> (https://github.com/ayushsolanki29)",
7
7
  "type": "module",
8
-
9
8
  "repository": {
10
9
  "type": "git",
11
10
  "url": "https://github.com/ayushsolanki29/smart-passphrase"
12
11
  },
13
-
14
12
  "keywords": [
15
13
  "password",
16
14
  "passphrase",
@@ -18,29 +16,38 @@
18
16
  "security",
19
17
  "typescript"
20
18
  ],
21
-
19
+ "bin": {
20
+ "smart-passphrase": "./dist/cli.js"
21
+ },
22
22
  "exports": {
23
23
  ".": {
24
24
  "types": "./dist/index.d.ts",
25
- "import": "./dist/esm/index.js",
25
+ "import": "./dist/index.js",
26
26
  "require": "./dist/cjs/index.cjs"
27
27
  }
28
28
  },
29
-
30
29
  "main": "./dist/cjs/index.cjs",
31
- "module": "./dist/esm/index.js",
30
+ "module": "./dist/index.js",
32
31
  "types": "./dist/index.d.ts",
33
-
34
- "files": ["dist"],
32
+ "files": [
33
+ "dist"
34
+ ],
35
35
  "sideEffects": false,
36
-
37
36
  "scripts": {
38
37
  "build:esm": "tsc -p tsconfig.esm.json",
39
38
  "build:cjs": "tsc -p tsconfig.cjs.json",
40
39
  "build": "npm run build:esm && npm run build:cjs"
41
40
  },
42
-
43
41
  "devDependencies": {
42
+ "@types/gradient-string": "^1.1.6",
43
+ "@types/node": "^25.6.0",
44
44
  "typescript": "^5.5.4"
45
+ },
46
+ "dependencies": {
47
+ "chalk": "^5.6.2",
48
+ "clipboardy": "^5.3.1",
49
+ "commander": "^14.0.3",
50
+ "gradient-string": "^3.0.0",
51
+ "ora": "^9.3.0"
45
52
  }
46
- }
53
+ }