import type { PasswordPolicy } from "../shared/types"; export interface GeneratorState { currentLength: number; userHasAdjusted: boolean; } export interface PolicyResult { length: number; sliderMin: number; sliderMax: number; constraintNote: string; } const DEFAULT_LENGTH = 64; const DEFAULT_SLIDER_MIN = 8; const DEFAULT_SLIDER_MAX = 256; export function initGeneratorState(): GeneratorState { return { currentLength: DEFAULT_LENGTH, userHasAdjusted: false }; } export function handleLengthChange(state: GeneratorState, length: number): GeneratorState { return { ...state, currentLength: length, userHasAdjusted: true }; } export function initFromPolicy( policy: PasswordPolicy | null, state: { currentLength: number; userHasAdjusted: boolean }, ): PolicyResult { const sliderMin = policy?.minLength ? Math.max(1, policy.minLength) : DEFAULT_SLIDER_MIN; const sliderMax = policy?.maxLength ? Math.min(DEFAULT_SLIDER_MAX, policy.maxLength) : DEFAULT_SLIDER_MAX; let length = state.currentLength; if (policy) { if (policy.maxLength !== undefined && policy.minLength !== undefined) { // Both bounds: always clamp to valid range, regardless of userHasAdjusted length = Math.max(policy.minLength, Math.min(policy.maxLength, state.currentLength)); } else if (policy.maxLength !== undefined) { if (!state.userHasAdjusted) { // Fresh open: offer the strongest password the site accepts length = policy.maxLength; } else if (state.currentLength > policy.maxLength) { // User above max: snap down length = policy.maxLength; } } else if (policy.minLength !== undefined && state.currentLength < policy.minLength) { // Below min: snap up length = policy.minLength; } } let constraintNote = ""; if (policy) { if (policy.maxLength !== undefined && length >= policy.maxLength) { constraintNote = `This site allows max ${policy.maxLength} characters`; } else if (policy.minLength !== undefined && length <= policy.minLength) { constraintNote = `This site requires at least ${policy.minLength} characters`; } } return { length, sliderMin, sliderMax, constraintNote }; }