@tsforge7/ts-password-validator
v0.1.0
Published
Entropy-based password validator with pluggable character sets and pattern collapsers.
Maintainers
Readme
ts-password-validator
Валидатор паролей по энтропии для TypeScript. Подключаемые наборы символов и коллапсеры паттернов, без обязательных требований к типам символов, без словарей, без сетевых запросов. Поставка dual ESM + CJS с тайпингами и runtime-валидацией входных значений через Zod.
Алгоритм — TypeScript-порт
wagslane/go-password-validator:
те же наборы символов, те же штрафы за последовательности и повторы, та же
формула log2(base^length).
Установка
npm install @tsforge7/ts-password-validatorБыстрый старт
import { validatePassword } from '@tsforge7/ts-password-validator';
const result = validatePassword('Ololo_123!');
// {
// valid: true,
// entropy: 55.5,
// base: 72,
// length: 10,
// effectiveLength: 9,
// strength: 'fair',
// minEntropy: 50,
// message: 'Password entropy 55.5 bits meets the required 50 bits (strength: fair).'
// }validatePassword всегда возвращает полный IValidationResult — там и
булево, и энтропия, и метка силы, и готовое сообщение для UI.
Свой порог
import { validatePassword } from '@tsforge7/ts-password-validator';
validatePassword('Ololo_123!', { minEntropy: 70 });
// { valid: false, strength: 'fair', message: '... below the required 70 bits ...' }Переиспользуй валидатор (горячий путь)
import { createPasswordValidator } from '@tsforge7/ts-password-validator';
const validator = createPasswordValidator({ minEntropy: 70 });
validator.check('Ololo_123!'); // → IValidationResult
validator.validate('Ololo_123!'); // → boolean
validator.getEntropy('Ololo_123!'); // → number
validator.details('Ololo_123!'); // → IEntropyDetailsТолько сила пароля
import {
classifyStrength,
getEntropyDetails,
PASSWORD_STRENGTH,
} from '@tsforge7/ts-password-validator';
classifyStrength(42); // 'weak'
getEntropyDetails('xK#9!mLp_2');
// { entropy: 65.5, base: 94, length: 10, effectiveLength: 10 }
// Сравнивай с константой, а не магическими строками
if (classifyStrength(42) === PASSWORD_STRENGTH.weak) {
// ...
}PASSWORD_STRENGTH — as const объект; его значения ровно те же, что и в
IValidationResult.strength, а тип TPasswordStrength выведен из него.
Совместимые низкоуровневые функции
import {
getPasswordEntropy,
validatePasswordEntropy,
} from '@tsforge7/ts-password-validator';
getPasswordEntropy('Ololo_123!'); // 55.5 (бит)
validatePasswordEntropy('Ololo_123!'); // trueКастомизация
createPasswordValidator принимает частичный options-объект — передавай
только то, что хочешь переопределить.
import {
createPasswordValidator,
CharacterSet,
RepeatCollapser,
SequenceCollapser,
PASSWORD_STRENGTH,
} from '@tsforge7/ts-password-validator';
const validator = createPasswordValidator({
minEntropy: 60,
characterSets: [
new CharacterSet('абвгдеёжзийклмнопрстуфхцчшщъыьэюя'),
new CharacterSet('АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ'),
],
sequences: ['абвгдеёжзийклмнопрстуфхцчшщъыьэюя'],
strengthThresholds: [
{ entropy: 80, strength: PASSWORD_STRENGTH.veryStrong },
{ entropy: 60, strength: PASSWORD_STRENGTH.strong },
{ entropy: 40, strength: PASSWORD_STRENGTH.fair },
{ entropy: 20, strength: PASSWORD_STRENGTH.weak },
{ entropy: 0, strength: PASSWORD_STRENGTH.veryWeak },
],
});
validator.check('Прекрасный_Пароль42');| Опция | Что переопределяет | По умолчанию |
| -------------------- | ------------------------------------------------------- | ----------------------------- |
| minEntropy | Порог для valid: true | 50 бит |
| characterSets | Категории символов для расчёта базы | DEFAULT_CHARACTER_SETS |
| sequences | Последовательности для дефолтного пайплайна коллапсеров | DEFAULT_SEQUENCES |
| collapsers | Полный пайплайн коллапсеров (приоритетнее sequences) | DEFAULT_COLLAPSERS |
| strengthThresholds | Соответствие энтропия → метка силы | DEFAULT_STRENGTH_THRESHOLDS |
Runtime-валидация входов
Каждая публичная функция и каждый конструктор класса, который можно
инстанцировать напрямую, валидирует свои аргументы через
Zod и кидает дружелюбный TypeError, если контракт
нарушен. Защита работает и из JavaScript, и при any-типизированных
данных.
validatePassword(123 as any);
// → TypeError: Invalid password: password must be a string (got number).
validatePassword('x', { minEntropy: -1 });
// → TypeError: Invalid options: minEntropy: Number must be greater than or equal to 0
validatePassword('x', { foo: 'bar' } as any);
// → TypeError: Invalid options: Unrecognized key(s) in object: 'foo'
new CharacterSet('');
// → TypeError: Invalid CharacterSet chars: CharacterSet chars must be a non-empty string
new RepeatCollapser(-1);
// → TypeError: Invalid maxStreak: maxStreak must be a positive integervalidatorOptionsSchema помечен .strict(), поэтому опечатка в ключе
options ловится сразу, а не молча игнорируется.
Схемы лежат в src/commands/ как TypeScript-namespace'ы, которые в одном
месте держат Zod-схему и выведенный из неё TS-тип:
import { z } from 'zod';
export namespace PasswordCommand {
export const schema = z.string({ ... });
export type Type = z.infer<typeof schema>;
}Доступные команды: PasswordCommand, CharacterSetCommand,
CollapserCommand, StrengthCommand, ValidatorOptionsCommand. Хелпер
assertSchema(schema, value, label) делает safeParse + оборачивает в
TypeError, если хочется валидировать что-то своё.
Форма результата
interface IValidationResult {
valid: boolean;
entropy: number; // биты
base: number; // размер пула
length: number; // сырых code points
effectiveLength: number; // после схлопывания повторов/последовательностей
strength: TPasswordStrength;
minEntropy: number;
message: string;
}
// PASSWORD_STRENGTH — источник истины, TPasswordStrength выведен из него
const PASSWORD_STRENGTH = {
veryWeak: 'very-weak', // 0 – 34 бит
weak: 'weak', // 35 – 49 бит
fair: 'fair', // 50 – 69 бит
strong: 'strong', // 70 – 99 бит
veryStrong: 'very-strong', // 100+ бит
} as const;
type TPasswordStrength =
(typeof PASSWORD_STRENGTH)[keyof typeof PASSWORD_STRENGTH];Алгоритм
entropy(пароль) = effectiveLength · log2(base)
База (base)
Сумма размеров всех встреченных в пароле категорий + 1 за каждый уникальный символ вне этих категорий (например, unicode-буквы).
| Категория | Дефолтные символы | Размер |
| ------------------ | ------------------------------------------------------------------------------------------------------------- | ------ |
| Replace | !@$&* | 5 |
| Separator | _-., (включая пробел) | 5 |
| Прочие специальные | ", #, %, ', (, ), +, /, :, ;, <, =, >, ?, [, \, ], ^, {, \|, }, ~ | 22 |
| Нижний регистр | a–z | 26 |
| Верхний регистр | A–Z | 26 |
| Цифры | 0–9 | 10 |
| Unknown (каждый) | всё остальное (за каждый уникальный символ) | +1 |
Максимальная база со всеми шестью категориями — 94.
Эффективная длина
Длина уменьшается до подсчёта энтропии, чтобы не переоценивать предсказуемые паттерны:
- Повторы — три и более одинаковых подряд считаются как два.
aaaa→ эффективнаяaa. - Последовательности — три и более подряд идущих символа из известной
последовательности считаются как два; проверка регистронезависимая, в обе
стороны. Дефолтные последовательности:
0123456789,qwertyuiop,asdfghjkl,zxcvbnm,abcdefghijklmnopqrstuvwxyz. Пример:qwerty→qw,9876→98.
Дефолтный порог
DEFAULT_MIN_ENTROPY = 50 бит. Авторы исходной Go-библиотеки рекомендуют
50–70 в зависимости от модели угроз.
Публичный API
Функции
| Экспорт | Возвращает |
| ---------------------------------------- | -------------------- |
| validatePassword(password, options?) | IValidationResult |
| createPasswordValidator(options?) | PasswordValidator |
| getEntropyDetails(password) | IEntropyDetails |
| classifyStrength(entropy, thresholds?) | TPasswordStrength |
| getPasswordEntropy(password) | number (биты) |
| validatePasswordEntropy(password) | boolean |
| assertSchema(schema, value, label) | разобранное значение |
Классы
| Экспорт | Назначение |
| --------------------------- | ---------------------------------------------------- |
| PasswordValidator | validate, check, getEntropy, details |
| PasswordEntropyCalculator | calculate, details |
| BaseCalculator | Размер пула из списка CharacterSet |
| EffectiveLengthCalculator | Сокращает длину через пайплайн IPatternCollapser's |
| CharacterSet | Пул символов с contains / size |
| RepeatCollapser | aaa… → aa |
| SequenceCollapser | Коллапс прямых и обратных последовательностей |
Команды (Zod-схемы + выведенные типы)
| Namespace | Члены |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| PasswordCommand | schema, Type |
| CharacterSetCommand | charsSchema, Chars |
| CollapserCommand | sequenceSchema, maxStreakSchema, patternSchema, Sequence, MaxStreak, Pattern |
| StrengthCommand | passwordStrengthSchema, entropySchema, thresholdSchema, thresholdsSchema, Entropy, Threshold, Thresholds |
| ValidatorOptionsCommand | schema, Type |
Интерфейсы, типы и константы
| Экспорт | Тип |
| ----------------------------- | --------- |
| IValidationResult | интерфейс |
| IEntropyDetails | интерфейс |
| IPatternCollapser | интерфейс |
| TPasswordStrength | тип |
| PASSWORD_STRENGTH | константа |
| DEFAULT_CHARACTER_SETS | константа |
| DEFAULT_SEQUENCES | константа |
| DEFAULT_COLLAPSERS | константа |
| DEFAULT_MIN_ENTROPY | константа |
| DEFAULT_STRENGTH_THRESHOLDS | константа |
| defaultEntropyCalculator | инстанс |
| defaultPasswordValidator | инстанс |
Output-shape интерфейсы (IValidationResult, IEntropyDetails,
IPatternCollapser) лежат в src/interfaces/ и начинаются с I. Input
shapes с runtime-схемой лежат в src/commands/ как namespace'ы.
Type-aliasы верхнего уровня (например, TPasswordStrength) — с префиксом
T.
Структура проекта
src/
├── base-calculator.ts
├── character-set.ts
├── defaults.ts
├── effective-length-calculator.ts
├── factory.ts
├── index.ts # публичный вход
├── password-entropy-calculator.ts
├── password-entropy.ts
├── password-validator.ts
├── strength.ts
├── validate.ts
├── collapsers/
│ ├── index.ts
│ ├── repeat-collapser.ts
│ └── sequence-collapser.ts
├── commands/ # Zod-схемы + выведенные типы
│ ├── index.ts
│ ├── assert.ts
│ ├── character-set.command.ts
│ ├── collapser.command.ts
│ ├── password.command.ts
│ ├── strength.command.ts
│ └── validator-options.command.ts
├── constants/
│ ├── index.ts
│ ├── character-sets.constant.ts
│ ├── collapsers.constant.ts
│ ├── min-entropy.constant.ts
│ ├── password-strength.constant.ts
│ ├── sequences.constant.ts
│ └── strength-thresholds.constant.ts
└── interfaces/
├── index.ts
├── entropy-details.interface.ts
├── pattern-collapser.interface.ts
└── validation-result.interface.tsИмпорты всегда идут через барель папки — from './constants',
from './interfaces', from './collapsers', from './commands' — без
указания конкретного *.constant.ts / *.interface.ts / *.command.ts
файла снаружи его собственной папки.
Скрипты
npm test # vitest (86 тестов: логика + валидация входов)
npm run typecheck # tsc --noEmit
npm run lint # eslint
npm run format # prettier --write
npm run build # tsup → dist/index.js (ESM) + dist/index.cjs + типыСборка (tsup + esbuild + rollup-plugin-dts) выдаёт плоский dist/:
dist/
├── index.js # ESM-бандл
├── index.cjs # CJS-бандл
├── index.d.ts # ESM-типы
├── index.d.cts # CJS-типы
└── *.map # sourcemapsЛицензия
MIT © 2026 tsforge7
