# `qwerty` — QWERTY keyboard adjacency graph _(no submodules)_ ## Purpose Encodes the physical adjacency of keys on a standard US QWERTY layout so the `keyboardMatch` matcher can recognise spatial walks like `qwerty`, `asdf`, and `1qaz2wsx`. The graph drives the keyboard-walk guess estimate in Wheeler 2016 section 4.5. The encoding mirrors the upstream zxcvbn reference implementation's `adjacency_graphs.coffee`. ## Features - Complete coverage of the alphanumeric, symbol, and shifted-symbol rows - Adjacency slots in zxcvbn's canonical order (left, top-left, top-right, right, bottom-right, bottom-left) - Each slot encodes both the unshifted and shifted character at that key - Frozen at module load — safe against accidental mutation ## Usage: Basic Example ```ts import { QWERTY_GRAPH } from "@suaveplan/password-strength/graphs/qwerty"; console.log(QWERTY_GRAPH.q); // [null, "1!", "2@", "wW", "aA", null] ``` ## Usage: Advanced Example ```ts import { keyboardMatch } from "@suaveplan/password-strength/matchers/keyboard"; import { QWERTY_GRAPH } from "@suaveplan/password-strength/graphs/qwerty"; import { DVORAK_GRAPH } from "@suaveplan/password-strength/graphs/dvorak"; const matches = keyboardMatch("qwerty", { graphs: { qwerty: QWERTY_GRAPH, dvorak: DVORAK_GRAPH }, }); console.log(matches); ``` ## API Reference - `QWERTY_GRAPH: KeyboardGraph` — frozen `Record>` mapping each key to its six adjacency slots. ## Implementation Notes The matcher checks both characters of each slot when classifying a key press: matching against the second character flags the press as shift-required (`shiftedCount += 1`). Slots that point off the edge of the keyboard are `null`. The graph deliberately preserves the upstream slot order so callers porting from `zxcvbn` consume the same data layout. ## Additional Implementation Notes The implementation follows the canonical zxcvbn algorithm from Wheeler 2016 with the suaveplan-style additions described in the package proposal: structured error codes via the `defineCodes` registry, Zod boundaries at every public entry point, OTel histograms and counters keyed to low-cardinality attributes, and a strict input-length guard at 200 characters. Consumers integrate with their existing observability stack by installing a meter provider before importing the package, and with their i18n stack via the `MessageResolver` callback type declared in `@suaveplan/types`. The bundled data tables are intentionally small to keep the package bundle compact; production deployments should swap in fuller frequency-ranked tables via `loadDictionaries`. The default English dictionary set is a frozen `DictionarySet` that the matcher never mutates; caller-supplied dictionaries override defaults by name.