# `entropy` _(no submodules)_ ## Purpose `estimateEntropy(password, opts?)` returns the upper-bound entropy of a candidate password as `log2(charsetSize) * password.length`, where `charsetSize` is the cardinality of the union of every detected built-in class (lowercase / uppercase / digits / symbols) plus any caller-supplied `opts.extraClasses` whose characters appear in the password. `estimatePassphraseEntropy(words, wordlistSize)` returns `log2(wordlistSize) * words.length` for Diceware-style passphrases. `assertStrength(password, minBits, opts?)` consumes `estimateEntropy` and throws `PWGEN.STRENGTH_INSUFFICIENT` when the computed bits are below the floor. The entropy formulas assume uniform sampling and ignore patterns — `"AAAAAAAA"` is reported as the same entropy as `"hG83mP9k"` if both contain the same charset classes. Pattern-aware scoring (zxcvbn-style) is intentionally out of scope and lives in the separate `@suaveplan/password-strength` package. ## Features - Detects class membership in a single pass over the password. - `extraClasses` opt-in lets callers report custom-charset passwords (emoji unions, hex-only PINs). - `assertStrength` throws on insufficient entropy with a structured error code. - Zod-validated `EntropyOptions`; mismatch → `PWGEN.INVALID_OPTIONS`. ## Usage: Basic Example ```ts import { estimateEntropy } from "@suaveplan/password-generator"; const { bits, charsetSize } = estimateEntropy("Abc123Xy"); // charsetSize === 62, bits === log2(62) * 8 ≈ 47.6 ``` ## Usage: Advanced Example ```ts import { assertStrength, estimateEntropy, estimatePassphraseEntropy, } from "@suaveplan/password-generator"; // Server-side guard before persisting a vault item function persistVaultItem(pw: string): void { assertStrength(pw, 80); // throws PWGEN.STRENGTH_INSUFFICIENT if < 80 bits // ... persist } // Custom emoji-augmented class const e = estimateEntropy("emoji👍2025", { extraClasses: ["👍🎉🚀"] }); // Diceware reporting const words = ["correct", "horse", "battery", "staple", "alpha", "beta"]; const passEntropy = estimatePassphraseEntropy(words, 7776); // ≈ 77.55 bits ``` ## API Reference ### `estimateEntropy(password: string, opts?: EntropyOptions): EntropyEstimate` Returns `{ bits, charsetSize }`. `bits` is `0` when the password is empty. ### `estimatePassphraseEntropy(words: readonly string[], wordlistSize: number): number` Returns `log2(wordlistSize) * words.length`. Throws `PWGEN.INVALID_OPTIONS` for non-positive integer `wordlistSize`. ### `assertStrength(password: string, minBits: number, opts?: EntropyOptions): void` Returns `void` on success. Throws `PwgenError(PWGEN.STRENGTH_INSUFFICIENT)` when the computed bits are strictly less than `minBits`. ### `EntropyOptions` - `extraClasses?: readonly string[]` — additional character classes the password may draw from. ## Implementation Notes Class detection is a single forward pass over the password, tracking four boolean flags for the built-in classes plus a `Set` of seen `extraClasses` indices. The `charsetSize` accumulator only adds a class's cardinality when the first matching character is seen, which keeps the cost `O(password.length * extraClasses.length)` worst-case. The formula deliberately reports an _upper bound_. A consumer comparing this number to a cracking-time chart should treat it as a ceiling: real-world passwords contain dictionary patterns, keyboard walks, and date stamps that reduce the effective entropy substantially. Use `@suaveplan/password-strength` for adversarial-aware estimates. `assertStrength` is a deliberately blunt instrument — it answers "does this password meet the floor on the most generous estimator?" — and is intended as a server-side guardrail against drastically weak inputs, not a substitute for proper strength scoring.