# `assess-reactive` — streaming assessment with debounce + cancel ## Purpose Debounce + cancel-on-newer-input wrapper around `assess` for UI strength meters that update as the user types. Each new input cancels the pending assessment for the previous input; only the latest stable value reaches the consumer. The wrapper honours an optional `AbortSignal` so callers can tear down a meter without orphaning pending work. ## Features - Configurable `debounceMs` (default 150 ms, range `[0, 5000]`) - Cancel-on-newer-input — superseded inputs do not yield results - Clean `AbortSignal` completion (no thrown error on abort) - 200-character DoS guard surfaced as `INPUT_TOO_LONG` - Backed by an `AsyncIterable` so consumers integrate via `for await` ## Usage: Basic Example ```ts import { assessReactive } from "@suaveplan/password-strength"; async function* keystrokes() { yield "p"; yield "pa"; yield "pas"; yield "pass"; } for await (const r of assessReactive(keystrokes())) { console.log(r.password, r.score); } ``` ## Usage: Advanced Example ```ts import { assessReactive } from "@suaveplan/password-strength"; const controller = new AbortController(); const stream = assessReactive(keystrokes(), { debounceMs: 100, signal: controller.signal, assessOptions: { userInputs: ["alice@example.com"] }, }); setTimeout(() => controller.abort(), 50); for await (const r of stream) { console.log(r.score); } ``` ## API Reference - `assessReactive(input$: AsyncIterable, opts?: AssessReactiveOptions): AsyncIterable` - Throws `PasswordStrengthError(PWSTRENGTH.INVALID_OPTIONS)` on schema failure; throws `PasswordStrengthError(PWSTRENGTH.INPUT_TOO_LONG)` when an input exceeds 200 characters. - `AssessReactiveOptions`: `{ debounceMs?, signal?, assessOptions? }`. ## Implementation Notes The wrapper schedules each input via `setTimeout(debounceMs)`. A newer input cancels the pending timer and reschedules. Consumers awaiting the next iteration are tracked in an internal resolver queue; an emitted result settles the next resolver or buffers if none is waiting. Abort clears the pending timer and resolves every waiting consumer with `{ done: true }`; the iterable then completes cleanly without throwing. The `ABORTED` error code is registered with the OTel counter on abort so operators can monitor cancellation rates without inspecting the iterator itself.