@fr0st/ui-passwordstrength 2.0.8 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -0
- package/README.md +342 -37
- package/dist/frost-ui-passwordstrength.esm.js +403 -0
- package/dist/frost-ui-passwordstrength.esm.js.map +1 -0
- package/dist/frost-ui-passwordstrength.esm.min.js +2 -0
- package/dist/frost-ui-passwordstrength.esm.min.js.map +1 -0
- package/dist/frost-ui-passwordstrength.js +436 -341
- package/dist/frost-ui-passwordstrength.js.map +1 -1
- package/dist/frost-ui-passwordstrength.min.js +2 -1
- package/dist/frost-ui-passwordstrength.min.js.map +1 -1
- package/package.json +57 -25
- package/src/{wrapper.js → browser.js} +0 -0
- package/src/index.js +0 -55
- package/src/password-strength.js +244 -26
- package/src/strength.js +273 -0
- package/src/prototype/events.js +0 -10
- package/src/prototype/helpers.js +0 -33
- package/src/prototype/render.js +0 -29
- package/src/static/helpers.js +0 -162
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -1,37 +1,342 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
1
|
+
# Frost UI PasswordStrength
|
|
2
|
+
|
|
3
|
+
[](https://github.com/frost-js/ui-passwordstrength/actions/workflows/ci.yml)
|
|
4
|
+
[](https://codecov.io/gh/frost-js/ui-passwordstrength)
|
|
5
|
+
[](https://www.npmjs.com/package/@fr0st/ui-passwordstrength)
|
|
6
|
+
[](https://www.npmjs.com/package/@fr0st/ui-passwordstrength)
|
|
7
|
+
[](https://github.com/frost-js/ui-passwordstrength/blob/main/dist/frost-ui-passwordstrength.min.js)
|
|
8
|
+
[](./LICENSE)
|
|
9
|
+
|
|
10
|
+
Password-strength indicator for Frost UI with configurable thresholds, semantic feedback, accessible progress state, and instance or static scoring APIs.
|
|
11
|
+
|
|
12
|
+
## Highlights
|
|
13
|
+
|
|
14
|
+
- Live scoring driven by the password input's native `input` event
|
|
15
|
+
- Automatic refresh after a form reset, unless the reset is canceled
|
|
16
|
+
- Configurable ordered thresholds, labels, and Frost UI semantic color classes
|
|
17
|
+
- Default or explicit progress-container placement
|
|
18
|
+
- Optional UI v4 striped progress treatment
|
|
19
|
+
- Accessible progress markup with reversible `aria-describedby` integration
|
|
20
|
+
- Public instance and static scoring methods with an optional custom scorer
|
|
21
|
+
- Configurable common-password penalties with a small built-in list
|
|
22
|
+
- Native `PasswordStrength` class and `passwordstrength` fQuery plugin
|
|
23
|
+
- Existing-instance reuse with frozen resolved options
|
|
24
|
+
- Prebuilt ESM and UMD bundles with source maps
|
|
25
|
+
- No component-specific CSS or Sass
|
|
26
|
+
- JSDoc-powered IntelliSense
|
|
27
|
+
|
|
28
|
+
Explore [the demo](./demo/index.html) for interactive examples.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
### Browser projects / bundlers
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm i @fr0st/ui-passwordstrength
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Frost UI PasswordStrength's package entry point is ESM-only and requires a browser DOM. Import the default `PasswordStrength` export and the stylesheets in browser projects and bundlers.
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
import '@fr0st/ui/dist/frost-ui.min.css';
|
|
42
|
+
import PasswordStrength from '@fr0st/ui-passwordstrength';
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`@fr0st/ui` and `@fr0st/query` are peer dependencies so the component shares the application's instances.
|
|
46
|
+
|
|
47
|
+
### Browser (ESM)
|
|
48
|
+
|
|
49
|
+
The ESM bundle imports `@fr0st/ui` and `@fr0st/query`. fQuery also imports `@fr0st/core`, so map all three dependencies when loading the bundle directly in a browser:
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<link
|
|
53
|
+
rel="stylesheet"
|
|
54
|
+
href="https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui.min.css">
|
|
55
|
+
<script type="importmap">
|
|
56
|
+
{
|
|
57
|
+
"imports": {
|
|
58
|
+
"@fr0st/core": "https://cdn.jsdelivr.net/npm/@fr0st/core@latest/dist/frost-core.esm.min.js",
|
|
59
|
+
"@fr0st/query": "https://cdn.jsdelivr.net/npm/@fr0st/query@latest/dist/fquery.esm.min.js",
|
|
60
|
+
"@fr0st/ui": "https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui.esm.min.js"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
<script type="module">
|
|
65
|
+
import PasswordStrength from 'https://cdn.jsdelivr.net/npm/@fr0st/ui-passwordstrength@latest/dist/frost-ui-passwordstrength.esm.min.js';
|
|
66
|
+
</script>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Browser (UMD)
|
|
70
|
+
|
|
71
|
+
Load the bundles from your own copy or a CDN:
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<link
|
|
75
|
+
rel="stylesheet"
|
|
76
|
+
href="/path/to/dist/frost-ui.min.css">
|
|
77
|
+
<script src="/path/to/dist/frost-ui-bundle.min.js"></script>
|
|
78
|
+
<script src="/path/to/dist/frost-ui-passwordstrength.min.js"></script>
|
|
79
|
+
<!-- or -->
|
|
80
|
+
<link
|
|
81
|
+
rel="stylesheet"
|
|
82
|
+
href="https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui.min.css">
|
|
83
|
+
<script src="https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui-bundle.min.js"></script>
|
|
84
|
+
<script src="https://cdn.jsdelivr.net/npm/@fr0st/ui-passwordstrength@latest/dist/frost-ui-passwordstrength.min.js"></script>
|
|
85
|
+
<script>
|
|
86
|
+
const { PasswordStrength } = globalThis.UI;
|
|
87
|
+
</script>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The UMD bundle adds `PasswordStrength` to the existing `globalThis.UI` object. Load Frost UI's all-in-one bundle first; it supplies the `UI` and `fQuery` globals.
|
|
91
|
+
|
|
92
|
+
The package root resolves to the prebuilt ESM bundle. Published files under `dist/` and `src/` are also available through matching package subpaths.
|
|
93
|
+
|
|
94
|
+
## Usage
|
|
95
|
+
|
|
96
|
+
Start with a normal password input inside a Frost UI form control. By default, PasswordStrength inserts its progress markup into the closest ancestor that is not `.form-input` or `.input-group`:
|
|
97
|
+
|
|
98
|
+
```html
|
|
99
|
+
<div id="password-field">
|
|
100
|
+
<div class="form-input">
|
|
101
|
+
<label for="password">Password</label>
|
|
102
|
+
<input
|
|
103
|
+
class="input-outline"
|
|
104
|
+
id="password"
|
|
105
|
+
name="password"
|
|
106
|
+
type="password"
|
|
107
|
+
autocomplete="new-password">
|
|
108
|
+
<div class="form-text">Use a long, unique password.</div>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
import PasswordStrength from '@fr0st/ui-passwordstrength';
|
|
115
|
+
|
|
116
|
+
const passwordStrength = PasswordStrength.init(
|
|
117
|
+
document.querySelector('#password'),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
console.log(passwordStrength.getStrength());
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Calling `PasswordStrength.init()` again for the same input returns its existing instance. Dispose the current instance before reinitializing the input with different options.
|
|
124
|
+
|
|
125
|
+
The indicator renders the initial value and refreshes on `input` events. After changing the input's value in JavaScript, dispatch an `input` event to refresh the indicator; `getStrength()` only returns the current score.
|
|
126
|
+
|
|
127
|
+
Form resets refresh the indicator after the browser restores the input's default value. This refresh is deferred until after the reset event finishes and is skipped if the reset is canceled. Inputs associated with a form through the `form` attribute are also supported.
|
|
128
|
+
|
|
129
|
+
## Options
|
|
130
|
+
|
|
131
|
+
Options are resolved in this order:
|
|
132
|
+
|
|
133
|
+
1. Component defaults
|
|
134
|
+
2. The element's `data-ui-*` attributes
|
|
135
|
+
3. Options passed to `PasswordStrength.init()`
|
|
136
|
+
|
|
137
|
+
Resolved `instance.options` are shallow-frozen.
|
|
138
|
+
|
|
139
|
+
Supplied `levels` and `commonPasswords` arrays replace the corresponding defaults completely, including when supplied through data attributes. Arrays passed through JavaScript options take precedence over arrays from data attributes. Supplied arrays and level objects are copied into the resolved configuration.
|
|
140
|
+
|
|
141
|
+
| Option | Type | Default | Description |
|
|
142
|
+
| --- | --- | --- | --- |
|
|
143
|
+
| `commonPasswords` | `string[]` | `PasswordStrength.defaults.commonPasswords` | Passwords the built-in scorer treats as very weak. Matching is case-insensitive and recognizes simple decorations and common leet substitutions. |
|
|
144
|
+
| `container` | `string \| null` | `null` | CSS selector for the element that receives the generated progress markup. `null` uses the closest field container. |
|
|
145
|
+
| `levels` | `PasswordStrengthLevel[]` | See below | Ordered score thresholds with a semantic class and optional label. |
|
|
146
|
+
| `scorer` | `(password, commonPasswords) => number` | Built-in scorer | Calculate a score for the current password. Results are normalized and clamped between `0` and `100`. |
|
|
147
|
+
| `striped` | `boolean` | `false` | Apply UI's `progress-bar-striped` class to the generated progress bar. |
|
|
148
|
+
|
|
149
|
+
The default common-password list is intentionally small to keep the bundle lightweight. Pass `commonPasswords: []` to disable list matching while retaining the other scoring rules. To extend the default list with application-specific terms, include the defaults explicitly:
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
PasswordStrength.init(node, {
|
|
153
|
+
commonPasswords: [
|
|
154
|
+
...PasswordStrength.defaults.commonPasswords,
|
|
155
|
+
'example-company',
|
|
156
|
+
'example-product',
|
|
157
|
+
],
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Provide `scorer` to replace the built-in heuristic without adding a scoring dependency to the base bundle. The callback receives the current password and resolved `commonPasswords` option:
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
PasswordStrength.init(node, {
|
|
165
|
+
scorer: (password, commonPasswords) => {
|
|
166
|
+
if (commonPasswords.includes(password.toLowerCase())) {
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return Math.min(password.length * 5, 100);
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Each level has a numeric `score`, a CSS `class`, and an optional `text` label. An empty or omitted label clears the previous label. Defaults are ordered from lowest to highest threshold:
|
|
176
|
+
|
|
177
|
+
| Score | Class | Text |
|
|
178
|
+
| ---: | --- | --- |
|
|
179
|
+
| `0` | `text-bg-danger` | Very Weak |
|
|
180
|
+
| `20` | `text-bg-danger` | Weak |
|
|
181
|
+
| `40` | `text-bg-warning` | Normal |
|
|
182
|
+
| `60` | `text-bg-success` | Strong |
|
|
183
|
+
| `80` | `text-bg-success` | Very Strong |
|
|
184
|
+
|
|
185
|
+
Provide a complete array when replacing all default levels:
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
const passwordStrength = PasswordStrength.init(node, {
|
|
189
|
+
container: '#password-strength-output',
|
|
190
|
+
levels: [
|
|
191
|
+
{ score: 0, class: 'text-bg-danger', text: 'Risky' },
|
|
192
|
+
{ score: 20, class: 'text-bg-warning', text: 'Improving' },
|
|
193
|
+
{ score: 40, class: 'text-bg-info', text: 'Fair' },
|
|
194
|
+
{ score: 60, class: 'text-bg-primary', text: 'Good' },
|
|
195
|
+
{ score: 80, class: 'text-bg-success', text: 'Excellent' },
|
|
196
|
+
],
|
|
197
|
+
striped: true,
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Provide at least one level, starting at `0`, and sort levels by ascending score. The last level whose threshold is less than or equal to the score is selected. Scores are clamped between `0` and `100`.
|
|
202
|
+
|
|
203
|
+
## Data attributes
|
|
204
|
+
|
|
205
|
+
Use kebab-case `data-ui-*` attributes for serializable options. Arrays and objects use JSON. Supply callbacks and DOM nodes through JavaScript.
|
|
206
|
+
|
|
207
|
+
| Attribute | Example |
|
|
208
|
+
| --- | --- |
|
|
209
|
+
| `data-ui-common-passwords` | `data-ui-common-passwords='["password","example-company"]'` |
|
|
210
|
+
| `data-ui-container` | `data-ui-container="#password-strength-output"` |
|
|
211
|
+
| `data-ui-levels` | `data-ui-levels='[{"score":0,"class":"text-bg-danger","text":"Risky"}]'` |
|
|
212
|
+
| `data-ui-striped` | `data-ui-striped="true"` |
|
|
213
|
+
|
|
214
|
+
```html
|
|
215
|
+
<input
|
|
216
|
+
id="password"
|
|
217
|
+
type="password"
|
|
218
|
+
data-ui-toggle="passwordstrength"
|
|
219
|
+
data-ui-container="#password-strength-output"
|
|
220
|
+
data-ui-striped="true">
|
|
221
|
+
|
|
222
|
+
<div id="password-strength-output"></div>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```js
|
|
226
|
+
import $ from '@fr0st/query';
|
|
227
|
+
import '@fr0st/ui-passwordstrength';
|
|
228
|
+
|
|
229
|
+
$('[data-ui-toggle="passwordstrength"]').passwordstrength();
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Data attributes configure options; they do not initialize PasswordStrength by themselves. Initialize the component through the class or fQuery plugin. Changing an option's data attribute after initialization does not reconfigure the existing instance.
|
|
233
|
+
|
|
234
|
+
## Methods
|
|
235
|
+
|
|
236
|
+
| Method | Returns | Description |
|
|
237
|
+
| --- | --- | --- |
|
|
238
|
+
| `PasswordStrength.init(node, options?)` | `PasswordStrength` | Return the existing instance for an element or create one. |
|
|
239
|
+
| `dispose()` | `void` | Remove generated markup, input and form-reset listeners, and registered component state, then restore the original ARIA state. |
|
|
240
|
+
| `getStrength()` | `number` | Score the instance input's current value from `0` to `100`. |
|
|
241
|
+
|
|
242
|
+
```js
|
|
243
|
+
const passwordStrength = PasswordStrength.init(node, {
|
|
244
|
+
striped: true,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const score = passwordStrength.getStrength();
|
|
248
|
+
|
|
249
|
+
passwordStrength.dispose();
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Static API
|
|
253
|
+
|
|
254
|
+
Use `PasswordStrength.getStrength(password, commonPasswords?)` to run the built-in scorer without creating an instance or rendering progress markup:
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
const score = PasswordStrength.getStrength('CorrectHorseBatteryStaple');
|
|
258
|
+
|
|
259
|
+
console.log(score); // 100
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The static method always uses the built-in scorer and returns a score from `0` to `100`. It uses `PasswordStrength.defaults.commonPasswords` unless a replacement list is supplied as the second argument. An instance's custom `scorer` and options do not change the static method.
|
|
263
|
+
|
|
264
|
+
The built-in scorer treats length as the primary strength factor. It heavily penalizes configured common passwords and simple variants, repeated characters and substrings, ascending and descending sequences, and keyboard-row patterns. Character variety provides only a small adjustment within each length band. Unicode is normalized with NFKC and counted by code point.
|
|
265
|
+
|
|
266
|
+
Common-password matches after case and Unicode normalization score `0`; recognized variants score `5`. Variant matching handles decorations, numeric prefixes or suffixes, and common leet substitutions, including combinations such as:
|
|
267
|
+
|
|
268
|
+
```js
|
|
269
|
+
PasswordStrength.getStrength('administrator', ['administrator']); // 0
|
|
270
|
+
PasswordStrength.getStrength('@dministrator!', ['administrator']); // 5
|
|
271
|
+
PasswordStrength.getStrength('!@dministrator123!', ['administrator']); // 5
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
It is a lightweight UI feedback heuristic, not an entropy estimate, breach check, or substitute for application security policy.
|
|
275
|
+
|
|
276
|
+
## Lifecycle
|
|
277
|
+
|
|
278
|
+
Calling `PasswordStrength.init()` again for the same element returns its existing instance. Dispose the current instance before reinitializing with different options.
|
|
279
|
+
|
|
280
|
+
An instance exposes its original element as `instance.node` and its shallow-frozen resolved configuration as `instance.options`. Both become `null` after disposal.
|
|
281
|
+
|
|
282
|
+
`dispose()` releases resources owned by the component and removes its registered instance. Repeated disposal is safe and does not affect a new instance initialized on the same element. Use a new instance before calling other methods after disposal.
|
|
283
|
+
|
|
284
|
+
If initialization fails, the component releases resources it created and removes its registered instance before rethrowing the error. The element can then be initialized again.
|
|
285
|
+
|
|
286
|
+
## fQuery API
|
|
287
|
+
|
|
288
|
+
Importing PasswordStrength registers `passwordstrength` on `fQuery.QuerySet`:
|
|
289
|
+
|
|
290
|
+
```js
|
|
291
|
+
import $ from '@fr0st/query';
|
|
292
|
+
import '@fr0st/ui-passwordstrength';
|
|
293
|
+
|
|
294
|
+
const passwordStrength = $('#password').passwordstrength({
|
|
295
|
+
striped: true,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const score = $('#password').passwordstrength('getStrength');
|
|
299
|
+
|
|
300
|
+
$('#password').passwordstrength('dispose');
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Pass an options object to initialize every matched element, or pass a public method name followed by its arguments. The first component or method result is returned.
|
|
304
|
+
|
|
305
|
+
## Accessibility
|
|
306
|
+
|
|
307
|
+
- The generated indicator uses `role="progressbar"` with `aria-valuemin="0"`, `aria-valuemax="100"`, and an updated `aria-valuenow` score.
|
|
308
|
+
- The active level's text is rendered inside the progress bar when the level supplies a label.
|
|
309
|
+
- A unique generated progress ID is appended to the input's existing `aria-describedby` value instead of replacing it.
|
|
310
|
+
- Disposal restores the original `aria-describedby` state exactly, including absent and empty values.
|
|
311
|
+
- Disposal also removes the generated progress markup, namespaced input and form-reset listeners, and registered component data.
|
|
312
|
+
- The original password input remains the interactive and submitted form control.
|
|
313
|
+
|
|
314
|
+
Applications remain responsible for meaningful labels, password requirements, validation feedback, and error messages. Treat the displayed score as guidance rather than proof that a password is safe.
|
|
315
|
+
|
|
316
|
+
## Themes and RTL
|
|
317
|
+
|
|
318
|
+
Frost UI follows the user's preferred color scheme by default. Set `data-ui-theme="light"` or `data-ui-theme="dark"` on the document or an ancestor to select a theme explicitly.
|
|
319
|
+
|
|
320
|
+
PasswordStrength uses Frost UI progress and semantic color classes. Labels and progress markup follow the surrounding text direction.
|
|
321
|
+
|
|
322
|
+
## Development
|
|
323
|
+
|
|
324
|
+
Install dependencies with `npm ci`, then install Playwright browsers with `npx playwright install --with-deps`.
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
npm test
|
|
328
|
+
npm run lint
|
|
329
|
+
npm run build
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
`npm test` rebuilds the bundles, then runs the Playwright suite in Chromium, Firefox, and WebKit. `npm run test:browser` runs the suite against the existing bundles, so rebuild after changing source files.
|
|
333
|
+
|
|
334
|
+
After building, `npm run test:coverage` runs Chromium tests and writes coverage reports to `coverage/`.
|
|
335
|
+
|
|
336
|
+
`npm run test:headed` and `npm run test:ui` also use the existing bundles and open headed browsers or the Playwright UI.
|
|
337
|
+
|
|
338
|
+
To view the demo, open `demo/index.html` in your browser after building.
|
|
339
|
+
|
|
340
|
+
## License
|
|
341
|
+
|
|
342
|
+
Frost UI PasswordStrength is released under the [MIT License](./LICENSE).
|