first commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# `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<StrengthResult>` 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<string>, opts?: AssessReactiveOptions): AsyncIterable<StrengthResult>`
|
||||
- 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.
|
||||
Reference in New Issue
Block a user