mat-password-strength 1.0.8
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 +21 -0
- package/README.md +266 -0
- package/fesm2022/mat-password-strength.mjs +153 -0
- package/fesm2022/mat-password-strength.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/password-strength.component.d.ts +58 -0
- package/package.json +47 -0
- package/public-api.d.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Peter Adison
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# mat-password-strength
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/mat-password-strength)
|
|
4
|
+
[](https://github.com/cpeteradison/mat-password-strength/stargazers)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/cpeteradison/mat-password-strength/blob/main/CONTRIBUTING.md)
|
|
7
|
+
[](https://github.com/sponsors/cpeteradison)
|
|
8
|
+
|
|
9
|
+
Modern Angular Material password strength indicator with simple rule-based or advanced [zxcvbn](https://github.com/dropbox/zxcvbn) analysis.
|
|
10
|
+
|
|
11
|
+
## Preview
|
|
12
|
+
|
|
13
|
+
| Simple mode | Advanced mode |
|
|
14
|
+
|:-----------:|:-------------:|
|
|
15
|
+
|  |  |
|
|
16
|
+
|
|
17
|
+
**[Live Demo →](https://cpeteradison.github.io/mat-password-strength/)**
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- Standalone component, no `NgModule` required
|
|
22
|
+
- Signals-based API (`input()` / `computed()`) with `OnPush` change detection
|
|
23
|
+
- Color feedback: red (weak) → yellow (moderate) → green (strong)
|
|
24
|
+
|
|
25
|
+
### Simple mode
|
|
26
|
+
- Fast, regex-based rule checks, no extra dependencies
|
|
27
|
+
- Configurable rules: minimum length, lowercase, uppercase, numbers, special characters
|
|
28
|
+
- Per-rule checklist so users know exactly what to fix
|
|
29
|
+
- Independently show or hide the strength label and checklist
|
|
30
|
+
|
|
31
|
+
### Advanced mode
|
|
32
|
+
- Powered by [zxcvbn](https://github.com/dropbox/zxcvbn) for realistic dictionary and pattern analysis
|
|
33
|
+
- Lazy-loaded on first use, deferred until `mode="advanced"` is first rendered, reducing initial parse time
|
|
34
|
+
- Displays tailored warnings and improvement suggestions
|
|
35
|
+
- Independently show or hide the strength label and feedback
|
|
36
|
+
- Exposes `crack_times_display` via `zxcvbnResult()` signal for custom UI
|
|
37
|
+
|
|
38
|
+
## Version compatibility & requirements
|
|
39
|
+
|
|
40
|
+
| Library version | Angular | `@angular/core` | `@angular/common` | `@angular/material` | `zxcvbn` |
|
|
41
|
+
|---|---|---|---|---|---|
|
|
42
|
+
| `1.x` | 19 | `^19.0.0` | `^19.0.0` | `^19.0.0` | `^4.4.2` |
|
|
43
|
+
|
|
44
|
+
> **Note:** `zxcvbn` must be installed in your application even if you only use `mode="simple"`. Bundlers resolve dynamic imports at build time, so the package must be present in `node_modules`. It is only **loaded at runtime** when `mode="advanced"` is first rendered, so it has no bundle cost if you never use advanced mode.
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install mat-password-strength zxcvbn
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Make sure Angular Material and its required animations are set up in your application:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// main.ts
|
|
56
|
+
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
|
57
|
+
|
|
58
|
+
bootstrapApplication(AppComponent, {
|
|
59
|
+
providers: [provideAnimationsAsync()]
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
You also need a Material theme in your global stylesheet, e.g.:
|
|
64
|
+
|
|
65
|
+
```scss
|
|
66
|
+
// styles.scss
|
|
67
|
+
@import '@angular/material/prebuilt-themes/indigo-pink.css';
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Usage
|
|
71
|
+
|
|
72
|
+
Import `PasswordStrengthComponent` in your standalone component (or `NgModule`):
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { FormsModule } from '@angular/forms';
|
|
76
|
+
import { PasswordStrengthComponent } from 'mat-password-strength';
|
|
77
|
+
|
|
78
|
+
@Component({
|
|
79
|
+
// standalone: true is the default in Angular 19+
|
|
80
|
+
imports: [FormsModule, PasswordStrengthComponent],
|
|
81
|
+
template: `
|
|
82
|
+
<input [(ngModel)]="password" />
|
|
83
|
+
<mat-password-strength [password]="password" />
|
|
84
|
+
`
|
|
85
|
+
})
|
|
86
|
+
export class MyComponent {
|
|
87
|
+
password = '';
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Simple mode (default)
|
|
92
|
+
|
|
93
|
+
Scores the password by checking configurable regex rules. Ideal when you want
|
|
94
|
+
predictable, rule-based feedback.
|
|
95
|
+
|
|
96
|
+
```html
|
|
97
|
+
<!-- bar only (default) -->
|
|
98
|
+
<mat-password-strength [password]="password" />
|
|
99
|
+
|
|
100
|
+
<!-- bar + label ("Fair", "Strong", …) -->
|
|
101
|
+
<mat-password-strength [password]="password" [hideStrength]="false" />
|
|
102
|
+
|
|
103
|
+
<!-- bar + label + per-rule checklist -->
|
|
104
|
+
<mat-password-strength [password]="password" [hideStrength]="false" [hideFeedback]="false" />
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Advanced mode
|
|
108
|
+
|
|
109
|
+
Delegates scoring to [zxcvbn](https://github.com/dropbox/zxcvbn) for realistic
|
|
110
|
+
dictionary and pattern analysis. Displays zxcvbn's warning and improvement suggestions.
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<mat-password-strength
|
|
114
|
+
[password]="password"
|
|
115
|
+
mode="advanced"
|
|
116
|
+
[hideStrength]="false"
|
|
117
|
+
[hideFeedback]="false"
|
|
118
|
+
/>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Pass user-specific context so zxcvbn can penalize passwords containing personal information:
|
|
122
|
+
|
|
123
|
+
```html
|
|
124
|
+
<mat-password-strength
|
|
125
|
+
[password]="password"
|
|
126
|
+
mode="advanced"
|
|
127
|
+
[userInputs]="[user.name, user.email]"
|
|
128
|
+
[hideStrength]="false"
|
|
129
|
+
[hideFeedback]="false"
|
|
130
|
+
/>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Custom rules (simple mode)
|
|
134
|
+
|
|
135
|
+
```html
|
|
136
|
+
<mat-password-strength
|
|
137
|
+
[password]="password"
|
|
138
|
+
[options]="{ min: 12, lowercase: true, uppercase: true, number: true, specialChar: false }"
|
|
139
|
+
/>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Reading signals from a parent
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { ViewChild } from '@angular/core';
|
|
146
|
+
import { PasswordStrengthComponent } from 'mat-password-strength';
|
|
147
|
+
|
|
148
|
+
@ViewChild(PasswordStrengthComponent) strengthComp!: PasswordStrengthComponent;
|
|
149
|
+
|
|
150
|
+
// label: 'Very Weak' | 'Weak' | 'Fair' | 'Good' | 'Strong' | 'Very Strong'
|
|
151
|
+
get label() { return this.strengthComp.strengthLabel(); }
|
|
152
|
+
|
|
153
|
+
// per-rule pass/fail list (simple mode)
|
|
154
|
+
get rules() { return this.strengthComp.ruleChecks(); }
|
|
155
|
+
|
|
156
|
+
// warning and suggestions from zxcvbn (advanced mode)
|
|
157
|
+
get feedback() { return this.strengthComp.zxcvbnResult()?.feedback; }
|
|
158
|
+
|
|
159
|
+
// human-readable crack-time estimate (advanced mode)
|
|
160
|
+
get crackTime() {
|
|
161
|
+
return this.strengthComp.zxcvbnResult()
|
|
162
|
+
?.crack_times_display.offline_slow_hashing_1e4_per_second;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## API
|
|
167
|
+
|
|
168
|
+
### Inputs
|
|
169
|
+
|
|
170
|
+
| Input | Type | Default | Description |
|
|
171
|
+
|----------------|---------------------------|----------------------------------------------------------------------------------|-------------|
|
|
172
|
+
| `password` | `string` | `''` | Password string to evaluate |
|
|
173
|
+
| `mode` | `'simple' \| 'advanced'` | `'simple'` | Scoring engine to use |
|
|
174
|
+
| `options` | `PasswordStrengthOptions` | see `PasswordStrengthOptions` defaults below | Rules for simple mode |
|
|
175
|
+
| `hideStrength` | `boolean` | `true` | When `false`, shows the strength label below the bar |
|
|
176
|
+
| `hideFeedback` | `boolean` | `true` | When `false`, shows the rule checklist (simple) or zxcvbn warning/suggestions (advanced) |
|
|
177
|
+
| `userInputs` | `string[]` | `[]` | User-specific strings passed to zxcvbn (advanced mode only) |
|
|
178
|
+
|
|
179
|
+
### Computed Signals
|
|
180
|
+
|
|
181
|
+
| Signal | Type | Description |
|
|
182
|
+
|-----------------|-------------------------|-------------|
|
|
183
|
+
| `strength()` | `number` | Current strength as a 0–100 value |
|
|
184
|
+
| `strengthLabel()` | `string` | Human-readable label for the current score |
|
|
185
|
+
| `ruleChecks()` | `PasswordRuleCheck[]` | Per-rule pass/fail list (populated in simple mode only) |
|
|
186
|
+
| `zxcvbnResult()` | `ZXCVBNResult \| null` | Raw zxcvbn result with `score`, `feedback`, and `crack_times_display` (populated in advanced mode only) |
|
|
187
|
+
|
|
188
|
+
### `PasswordStrengthOptions` _(simple mode only)_
|
|
189
|
+
|
|
190
|
+
| Property | Type | Default | Description |
|
|
191
|
+
|---------------|-----------|---------|-------------|
|
|
192
|
+
| `min` | `number` | `8` | Minimum password length |
|
|
193
|
+
| `lowercase` | `boolean` | `true` | Require at least one lowercase letter |
|
|
194
|
+
| `uppercase` | `boolean` | `true` | Require at least one uppercase letter |
|
|
195
|
+
| `number` | `boolean` | `true` | Require at least one digit |
|
|
196
|
+
| `specialChar` | `boolean` | `true` | Require at least one special character |
|
|
197
|
+
|
|
198
|
+
### `PasswordRuleCheck` _(simple mode only)_
|
|
199
|
+
|
|
200
|
+
| Property | Type | Description |
|
|
201
|
+
|-----------|-----------|-------------|
|
|
202
|
+
| `label` | `string` | Human-readable rule description (e.g. `"At least 1 uppercase letter"`) |
|
|
203
|
+
| `passed` | `boolean` | Whether the password currently satisfies this rule |
|
|
204
|
+
|
|
205
|
+
### `ZXCVBNResult` _(advanced mode only)_
|
|
206
|
+
|
|
207
|
+
| Property | Type | Description |
|
|
208
|
+
|-----------------------|-----------------------------|-------------|
|
|
209
|
+
| `score` | `0 \| 1 \| 2 \| 3 \| 4` | Strength score from very weak (0) to very strong (4) |
|
|
210
|
+
| `feedback.warning` | `string` | Short explanation of the main weakness |
|
|
211
|
+
| `feedback.suggestions`| `string[]` | Actionable improvement tips |
|
|
212
|
+
| `crack_times_display.online_throttling_100_per_hour` | `string` | Rate-limited login (100 attempts/hr) |
|
|
213
|
+
| `crack_times_display.online_no_throttling_10_per_second` | `string` | Unthrottled online attack |
|
|
214
|
+
| `crack_times_display.offline_slow_hashing_1e4_per_second` | `string` | bcrypt/scrypt offline breach — most realistic for production use |
|
|
215
|
+
| `crack_times_display.offline_fast_hashing_1e10_per_second` | `string` | MD5/SHA1 offline breach — worst case |
|
|
216
|
+
|
|
217
|
+
### Strength labels
|
|
218
|
+
|
|
219
|
+
| Strength value | Label |
|
|
220
|
+
|----------------|--------------|
|
|
221
|
+
| 0 | Very Weak |
|
|
222
|
+
| 1 – 25 | Weak |
|
|
223
|
+
| 26 – 50 | Fair |
|
|
224
|
+
| 51 – 75 | Good |
|
|
225
|
+
| 76 – 99 | Strong |
|
|
226
|
+
| 100 | Very Strong |
|
|
227
|
+
|
|
228
|
+
### Color thresholds
|
|
229
|
+
|
|
230
|
+
| Strength value | Material color | Meaning |
|
|
231
|
+
|----------------|-------------------|----------|
|
|
232
|
+
| < 21 | `warn` (red) | Weak |
|
|
233
|
+
| 21 – 80 | `accent` (yellow) | Moderate |
|
|
234
|
+
| ≥ 81 | `primary` (green) | Strong |
|
|
235
|
+
|
|
236
|
+
## Theming
|
|
237
|
+
|
|
238
|
+
The bar and rule colours can be overridden with CSS custom properties:
|
|
239
|
+
|
|
240
|
+
```css
|
|
241
|
+
mat-password-strength {
|
|
242
|
+
--psc-weak-color: #e53935; /* rule fail + weak bar */
|
|
243
|
+
--psc-medium-color: #fdd835; /* medium bar */
|
|
244
|
+
--psc-strong-color: #43a047; /* rule pass + strong bar */
|
|
245
|
+
--psc-buffer-color: rgba(0, 0, 0, 0.12); /* unfilled bar track */
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Acknowledgements
|
|
250
|
+
|
|
251
|
+
This library was inspired by
|
|
252
|
+
[`angular-material-extensions/password-strength`](https://github.com/angular-material-extensions/password-strength).
|
|
253
|
+
Thank you to its authors for the original concept. This package was created to continue
|
|
254
|
+
support for Angular 19 and beyond.
|
|
255
|
+
|
|
256
|
+
## Contributing
|
|
257
|
+
|
|
258
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for local development setup, running the demo, and submitting pull requests.
|
|
259
|
+
|
|
260
|
+
## Changelog
|
|
261
|
+
|
|
262
|
+
See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, signal, effect, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { MatProgressBar } from '@angular/material/progress-bar';
|
|
4
|
+
|
|
5
|
+
/** Mapped from zxcvbn score (0–4) to a 0–100 progress value. */
|
|
6
|
+
const ZXCVBN_SCORE_MAP = { 0: 0, 1: 25, 2: 50, 3: 75, 4: 100 };
|
|
7
|
+
const LOWERCASE_RE = /[a-z]/;
|
|
8
|
+
const UPPERCASE_RE = /[A-Z]/;
|
|
9
|
+
const NUMBER_RE = /\d/;
|
|
10
|
+
const SPECIAL_CHAR_RE = /[!"#$%&'()*+,\-./:;<=>?@[\]^_`{|}~]/;
|
|
11
|
+
// With 5 rules the possible scores are 0/20/40/60/80/100;
|
|
12
|
+
// 21 and 81 ensure 0–20 → warn, 40–80 → accent, 100 → primary
|
|
13
|
+
const WARN_THRESHOLD = 21;
|
|
14
|
+
const ACCENT_THRESHOLD = 81;
|
|
15
|
+
class PasswordStrengthComponent {
|
|
16
|
+
password = input('');
|
|
17
|
+
options = input({
|
|
18
|
+
min: 8,
|
|
19
|
+
lowercase: true,
|
|
20
|
+
uppercase: true,
|
|
21
|
+
number: true,
|
|
22
|
+
specialChar: true,
|
|
23
|
+
});
|
|
24
|
+
hideStrength = input(true);
|
|
25
|
+
/** When true, hides the per-rule checklist (simple) or zxcvbn warning/suggestions (advanced). */
|
|
26
|
+
hideFeedback = input(true);
|
|
27
|
+
/**
|
|
28
|
+
* Scoring mode:
|
|
29
|
+
* - `'simple'` — fast regex-based rule checks (default)
|
|
30
|
+
* - `'advanced'` — zxcvbn dictionary/pattern analysis with crack-time feedback
|
|
31
|
+
*/
|
|
32
|
+
mode = input('simple');
|
|
33
|
+
/**
|
|
34
|
+
* Optional list of user-supplied inputs (name, email, username, etc.) passed
|
|
35
|
+
* to zxcvbn so it can penalise passwords that contain personal information.
|
|
36
|
+
* Only used when mode is 'advanced'.
|
|
37
|
+
*/
|
|
38
|
+
userInputs = input([]);
|
|
39
|
+
_zxcvbnFn = signal(null);
|
|
40
|
+
constructor() {
|
|
41
|
+
effect(() => {
|
|
42
|
+
if (this.mode() === 'advanced' && !this._zxcvbnFn()) {
|
|
43
|
+
import('zxcvbn').then(m => {
|
|
44
|
+
const mod = m;
|
|
45
|
+
this._zxcvbnFn.set((typeof mod === 'function' ? mod : mod.default));
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** Raw zxcvbn result — only populated when mode() is 'advanced' and zxcvbn has loaded. */
|
|
51
|
+
zxcvbnResult = computed(() => {
|
|
52
|
+
if (this.mode() !== 'advanced' || !this.password())
|
|
53
|
+
return null;
|
|
54
|
+
const fn = this._zxcvbnFn();
|
|
55
|
+
if (!fn)
|
|
56
|
+
return null;
|
|
57
|
+
return fn(this.password(), this.userInputs());
|
|
58
|
+
});
|
|
59
|
+
strength = computed(() => {
|
|
60
|
+
if (this.mode() === 'advanced') {
|
|
61
|
+
const result = this.zxcvbnResult();
|
|
62
|
+
return result ? ZXCVBN_SCORE_MAP[result.score] : 0;
|
|
63
|
+
}
|
|
64
|
+
const password = this.password();
|
|
65
|
+
const opts = this.options();
|
|
66
|
+
let score = 0;
|
|
67
|
+
let total = 0;
|
|
68
|
+
if (opts.lowercase) {
|
|
69
|
+
total++;
|
|
70
|
+
if (LOWERCASE_RE.test(password))
|
|
71
|
+
score++;
|
|
72
|
+
}
|
|
73
|
+
if (opts.uppercase) {
|
|
74
|
+
total++;
|
|
75
|
+
if (UPPERCASE_RE.test(password))
|
|
76
|
+
score++;
|
|
77
|
+
}
|
|
78
|
+
if (opts.number) {
|
|
79
|
+
total++;
|
|
80
|
+
if (NUMBER_RE.test(password))
|
|
81
|
+
score++;
|
|
82
|
+
}
|
|
83
|
+
if (opts.specialChar) {
|
|
84
|
+
total++;
|
|
85
|
+
if (SPECIAL_CHAR_RE.test(password))
|
|
86
|
+
score++;
|
|
87
|
+
}
|
|
88
|
+
if (opts.min) {
|
|
89
|
+
total++;
|
|
90
|
+
if (password.length >= opts.min)
|
|
91
|
+
score++;
|
|
92
|
+
}
|
|
93
|
+
return total === 0 ? 0 : Math.floor((score / total) * 100);
|
|
94
|
+
});
|
|
95
|
+
color = computed(() => {
|
|
96
|
+
const value = this.strength();
|
|
97
|
+
if (value < WARN_THRESHOLD)
|
|
98
|
+
return 'warn';
|
|
99
|
+
if (value < ACCENT_THRESHOLD)
|
|
100
|
+
return 'accent';
|
|
101
|
+
return 'primary';
|
|
102
|
+
});
|
|
103
|
+
strengthLabel = computed(() => {
|
|
104
|
+
const value = this.strength();
|
|
105
|
+
if (value === 0)
|
|
106
|
+
return 'Very Weak';
|
|
107
|
+
if (value <= 25)
|
|
108
|
+
return 'Weak';
|
|
109
|
+
if (value <= 50)
|
|
110
|
+
return 'Fair';
|
|
111
|
+
if (value <= 75)
|
|
112
|
+
return 'Good';
|
|
113
|
+
if (value < 100)
|
|
114
|
+
return 'Strong';
|
|
115
|
+
return 'Very Strong';
|
|
116
|
+
});
|
|
117
|
+
/** Per-rule pass/fail list — only populated in simple mode. */
|
|
118
|
+
ruleChecks = computed(() => {
|
|
119
|
+
if (this.mode() !== 'simple')
|
|
120
|
+
return [];
|
|
121
|
+
const password = this.password();
|
|
122
|
+
const opts = this.options();
|
|
123
|
+
const checks = [];
|
|
124
|
+
if (opts.lowercase)
|
|
125
|
+
checks.push({ label: 'At least 1 lowercase letter', passed: LOWERCASE_RE.test(password) });
|
|
126
|
+
if (opts.uppercase)
|
|
127
|
+
checks.push({ label: 'At least 1 uppercase letter', passed: UPPERCASE_RE.test(password) });
|
|
128
|
+
if (opts.number)
|
|
129
|
+
checks.push({ label: 'At least 1 number', passed: NUMBER_RE.test(password) });
|
|
130
|
+
if (opts.specialChar)
|
|
131
|
+
checks.push({ label: 'At least 1 special character', passed: SPECIAL_CHAR_RE.test(password) });
|
|
132
|
+
if (opts.min)
|
|
133
|
+
checks.push({ label: `At least ${opts.min} characters`, passed: password.length >= opts.min });
|
|
134
|
+
return checks;
|
|
135
|
+
});
|
|
136
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: PasswordStrengthComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
137
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.19", type: PasswordStrengthComponent, isStandalone: true, selector: "mat-password-strength", inputs: { password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, hideStrength: { classPropertyName: "hideStrength", publicName: "hideStrength", isSignal: true, isRequired: false, transformFunction: null }, hideFeedback: { classPropertyName: "hideFeedback", publicName: "hideFeedback", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, userInputs: { classPropertyName: "userInputs", publicName: "userInputs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"password-strength-container\">\r\n <mat-progress-bar [value]=\"strength()\" [color]=\"color()\" mode=\"determinate\"></mat-progress-bar>\r\n @if (!hideStrength()) {\r\n <p class=\"password-strength-label\">{{ strengthLabel() }}</p>\r\n }\r\n @if (!hideFeedback()) {\r\n @if (mode() === 'simple') {\r\n <ul class=\"password-strength-rules\">\r\n @for (rule of ruleChecks(); track rule.label) {\r\n <li [class.passed]=\"rule.passed\" [class.failed]=\"!rule.passed\">\r\n {{ rule.passed ? '\u2713' : '\u2717' }} {{ rule.label }}\r\n </li>\r\n }\r\n </ul>\r\n }\r\n @if (mode() === 'advanced' && zxcvbnResult(); as result) {\r\n @if (result.feedback.warning) {\r\n <p class=\"password-strength-warning\">{{ result.feedback.warning }}</p>\r\n }\r\n @if (result.feedback.suggestions.length) {\r\n <ul class=\"password-strength-suggestions\">\r\n @for (suggestion of result.feedback.suggestions; track suggestion) {\r\n <li>{{ suggestion }}</li>\r\n }\r\n </ul>\r\n }\r\n }\r\n }\r\n</div>\r\n", styles: [".password-strength-container .password-strength-label{margin-top:10px}.password-strength-container .mdc-linear-progress__buffer-bar{background-color:var(--psc-buffer-color, #888888)}.password-strength-container .mat-mdc-progress-bar.mat-warn .mdc-linear-progress__bar-inner{background-color:var(--psc-weak-color, #ed1c24);border-color:var(--psc-weak-color, #ed1c24)}.password-strength-container .mat-mdc-progress-bar.mat-accent .mdc-linear-progress__bar-inner{background-color:var(--psc-medium-color, gold);border-color:var(--psc-medium-color, gold)}.password-strength-container .mat-mdc-progress-bar.mat-primary .mdc-linear-progress__bar-inner{background-color:var(--psc-strong-color, #258341);border-color:var(--psc-strong-color, #258341)}.password-strength-container .password-strength-rules{list-style:none;margin:6px 0 0;padding:0;font-size:.8rem}.password-strength-container .password-strength-rules li{margin:2px 0}.password-strength-container .password-strength-rules li.passed{color:var(--psc-strong-color, #258341)}.password-strength-container .password-strength-rules li.failed{color:var(--psc-weak-color, #ed1c24)}\n"], dependencies: [{ kind: "component", type: MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
138
|
+
}
|
|
139
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: PasswordStrengthComponent, decorators: [{
|
|
140
|
+
type: Component,
|
|
141
|
+
args: [{ selector: 'mat-password-strength', standalone: true, imports: [MatProgressBar], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"password-strength-container\">\r\n <mat-progress-bar [value]=\"strength()\" [color]=\"color()\" mode=\"determinate\"></mat-progress-bar>\r\n @if (!hideStrength()) {\r\n <p class=\"password-strength-label\">{{ strengthLabel() }}</p>\r\n }\r\n @if (!hideFeedback()) {\r\n @if (mode() === 'simple') {\r\n <ul class=\"password-strength-rules\">\r\n @for (rule of ruleChecks(); track rule.label) {\r\n <li [class.passed]=\"rule.passed\" [class.failed]=\"!rule.passed\">\r\n {{ rule.passed ? '\u2713' : '\u2717' }} {{ rule.label }}\r\n </li>\r\n }\r\n </ul>\r\n }\r\n @if (mode() === 'advanced' && zxcvbnResult(); as result) {\r\n @if (result.feedback.warning) {\r\n <p class=\"password-strength-warning\">{{ result.feedback.warning }}</p>\r\n }\r\n @if (result.feedback.suggestions.length) {\r\n <ul class=\"password-strength-suggestions\">\r\n @for (suggestion of result.feedback.suggestions; track suggestion) {\r\n <li>{{ suggestion }}</li>\r\n }\r\n </ul>\r\n }\r\n }\r\n }\r\n</div>\r\n", styles: [".password-strength-container .password-strength-label{margin-top:10px}.password-strength-container .mdc-linear-progress__buffer-bar{background-color:var(--psc-buffer-color, #888888)}.password-strength-container .mat-mdc-progress-bar.mat-warn .mdc-linear-progress__bar-inner{background-color:var(--psc-weak-color, #ed1c24);border-color:var(--psc-weak-color, #ed1c24)}.password-strength-container .mat-mdc-progress-bar.mat-accent .mdc-linear-progress__bar-inner{background-color:var(--psc-medium-color, gold);border-color:var(--psc-medium-color, gold)}.password-strength-container .mat-mdc-progress-bar.mat-primary .mdc-linear-progress__bar-inner{background-color:var(--psc-strong-color, #258341);border-color:var(--psc-strong-color, #258341)}.password-strength-container .password-strength-rules{list-style:none;margin:6px 0 0;padding:0;font-size:.8rem}.password-strength-container .password-strength-rules li{margin:2px 0}.password-strength-container .password-strength-rules li.passed{color:var(--psc-strong-color, #258341)}.password-strength-container .password-strength-rules li.failed{color:var(--psc-weak-color, #ed1c24)}\n"] }]
|
|
142
|
+
}], ctorParameters: () => [] });
|
|
143
|
+
|
|
144
|
+
/*
|
|
145
|
+
* Public API Surface of mat-password-strength
|
|
146
|
+
*/
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Generated bundle index. Do not edit.
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
export { PasswordStrengthComponent };
|
|
153
|
+
//# sourceMappingURL=mat-password-strength.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mat-password-strength.mjs","sources":["../../src/lib/password-strength.component.ts","../../src/lib/password-strength.component.html","../../src/public-api.ts","../../src/mat-password-strength.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, ViewEncapsulation, computed, effect, input, signal } from '@angular/core';\r\nimport { MatProgressBar } from '@angular/material/progress-bar';\r\n\r\ntype ZxcvbnFn = (password: string, userInputs?: string[]) => ZXCVBNResult;\r\n\r\n/** Subset of the zxcvbn result exposed in the public API. */\r\nexport interface ZXCVBNResult {\r\n /** 0 = very weak … 4 = very strong */\r\n score: 0 | 1 | 2 | 3 | 4;\r\n feedback: {\r\n warning: string;\r\n suggestions: string[];\r\n };\r\n /** Human-readable crack-time estimates for common attack scenarios. */\r\n crack_times_display: {\r\n online_throttling_100_per_hour: string;\r\n online_no_throttling_10_per_second: string;\r\n offline_slow_hashing_1e4_per_second: string;\r\n offline_fast_hashing_1e10_per_second: string;\r\n };\r\n}\r\n\r\nexport interface PasswordStrengthOptions {\r\n min?: number;\r\n lowercase?: boolean;\r\n uppercase?: boolean;\r\n number?: boolean;\r\n specialChar?: boolean;\r\n}\r\n\r\nexport interface PasswordRuleCheck {\r\n label: string;\r\n passed: boolean;\r\n}\r\n\r\n/** Mapped from zxcvbn score (0–4) to a 0–100 progress value. */\r\nconst ZXCVBN_SCORE_MAP: Record<number, number> = { 0: 0, 1: 25, 2: 50, 3: 75, 4: 100 };\r\n\r\nconst LOWERCASE_RE = /[a-z]/;\r\nconst UPPERCASE_RE = /[A-Z]/;\r\nconst NUMBER_RE = /\\d/;\r\nconst SPECIAL_CHAR_RE = /[!\"#$%&'()*+,\\-./:;<=>?@[\\]^_`{|}~]/;\r\n\r\n// With 5 rules the possible scores are 0/20/40/60/80/100;\r\n// 21 and 81 ensure 0–20 → warn, 40–80 → accent, 100 → primary\r\nconst WARN_THRESHOLD = 21;\r\nconst ACCENT_THRESHOLD = 81;\r\n\r\n@Component({\r\n selector: 'mat-password-strength',\r\n standalone: true,\r\n imports: [MatProgressBar],\r\n templateUrl: './password-strength.component.html',\r\n styleUrls: ['./password-strength.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n})\r\nexport class PasswordStrengthComponent {\r\n password = input<string>('');\r\n options = input<PasswordStrengthOptions>({\r\n min: 8,\r\n lowercase: true,\r\n uppercase: true,\r\n number: true,\r\n specialChar: true,\r\n });\r\n hideStrength = input<boolean>(true);\r\n /** When true, hides the per-rule checklist (simple) or zxcvbn warning/suggestions (advanced). */\r\n hideFeedback = input<boolean>(true);\r\n /**\r\n * Scoring mode:\r\n * - `'simple'` — fast regex-based rule checks (default)\r\n * - `'advanced'` — zxcvbn dictionary/pattern analysis with crack-time feedback\r\n */\r\n mode = input<'simple' | 'advanced'>('simple');\r\n /**\r\n * Optional list of user-supplied inputs (name, email, username, etc.) passed\r\n * to zxcvbn so it can penalise passwords that contain personal information.\r\n * Only used when mode is 'advanced'.\r\n */\r\n userInputs = input<string[]>([]);\r\n\r\n private readonly _zxcvbnFn = signal<ZxcvbnFn | null>(null);\r\n\r\n constructor() {\r\n effect(() => {\r\n if (this.mode() === 'advanced' && !this._zxcvbnFn()) {\r\n import('zxcvbn').then(m => {\r\n const mod = m as unknown as { default?: ZxcvbnFn } | ZxcvbnFn;\r\n this._zxcvbnFn.set((typeof mod === 'function' ? mod : (mod as { default: ZxcvbnFn }).default) as ZxcvbnFn);\r\n });\r\n }\r\n });\r\n }\r\n\r\n /** Raw zxcvbn result — only populated when mode() is 'advanced' and zxcvbn has loaded. */\r\n zxcvbnResult = computed((): ZXCVBNResult | null => {\r\n if (this.mode() !== 'advanced' || !this.password()) return null;\r\n const fn = this._zxcvbnFn();\r\n if (!fn) return null;\r\n return fn(this.password(), this.userInputs());\r\n });\r\n\r\n strength = computed((): number => {\r\n if (this.mode() === 'advanced') {\r\n const result = this.zxcvbnResult();\r\n return result ? ZXCVBN_SCORE_MAP[result.score] : 0;\r\n }\r\n\r\n const password = this.password();\r\n const opts = this.options();\r\n let score = 0;\r\n let total = 0;\r\n\r\n if (opts.lowercase) {\r\n total++;\r\n if (LOWERCASE_RE.test(password)) score++;\r\n }\r\n\r\n if (opts.uppercase) {\r\n total++;\r\n if (UPPERCASE_RE.test(password)) score++;\r\n }\r\n\r\n if (opts.number) {\r\n total++;\r\n if (NUMBER_RE.test(password)) score++;\r\n }\r\n\r\n if (opts.specialChar) {\r\n total++;\r\n if (SPECIAL_CHAR_RE.test(password)) score++;\r\n }\r\n\r\n if (opts.min) {\r\n total++;\r\n if (password.length >= opts.min) score++;\r\n }\r\n\r\n return total === 0 ? 0 : Math.floor((score / total) * 100);\r\n });\r\n\r\n color = computed((): 'warn' | 'accent' | 'primary' => {\r\n const value = this.strength();\r\n if (value < WARN_THRESHOLD) return 'warn';\r\n if (value < ACCENT_THRESHOLD) return 'accent';\r\n return 'primary';\r\n });\r\n\r\n strengthLabel = computed((): string => {\r\n const value = this.strength();\r\n if (value === 0) return 'Very Weak';\r\n if (value <= 25) return 'Weak';\r\n if (value <= 50) return 'Fair';\r\n if (value <= 75) return 'Good';\r\n if (value < 100) return 'Strong';\r\n return 'Very Strong';\r\n });\r\n\r\n /** Per-rule pass/fail list — only populated in simple mode. */\r\n ruleChecks = computed((): PasswordRuleCheck[] => {\r\n if (this.mode() !== 'simple') return [];\r\n const password = this.password();\r\n const opts = this.options();\r\n const checks: PasswordRuleCheck[] = [];\r\n\r\n if (opts.lowercase) checks.push({ label: 'At least 1 lowercase letter', passed: LOWERCASE_RE.test(password) });\r\n if (opts.uppercase) checks.push({ label: 'At least 1 uppercase letter', passed: UPPERCASE_RE.test(password) });\r\n if (opts.number) checks.push({ label: 'At least 1 number', passed: NUMBER_RE.test(password) });\r\n if (opts.specialChar)checks.push({ label: 'At least 1 special character', passed: SPECIAL_CHAR_RE.test(password) });\r\n if (opts.min) checks.push({ label: `At least ${opts.min} characters`, passed: password.length >= opts.min });\r\n\r\n return checks;\r\n });\r\n}\r\n","<div class=\"password-strength-container\">\r\n <mat-progress-bar [value]=\"strength()\" [color]=\"color()\" mode=\"determinate\"></mat-progress-bar>\r\n @if (!hideStrength()) {\r\n <p class=\"password-strength-label\">{{ strengthLabel() }}</p>\r\n }\r\n @if (!hideFeedback()) {\r\n @if (mode() === 'simple') {\r\n <ul class=\"password-strength-rules\">\r\n @for (rule of ruleChecks(); track rule.label) {\r\n <li [class.passed]=\"rule.passed\" [class.failed]=\"!rule.passed\">\r\n {{ rule.passed ? '✓' : '✗' }} {{ rule.label }}\r\n </li>\r\n }\r\n </ul>\r\n }\r\n @if (mode() === 'advanced' && zxcvbnResult(); as result) {\r\n @if (result.feedback.warning) {\r\n <p class=\"password-strength-warning\">{{ result.feedback.warning }}</p>\r\n }\r\n @if (result.feedback.suggestions.length) {\r\n <ul class=\"password-strength-suggestions\">\r\n @for (suggestion of result.feedback.suggestions; track suggestion) {\r\n <li>{{ suggestion }}</li>\r\n }\r\n </ul>\r\n }\r\n }\r\n }\r\n</div>\r\n","/*\r\n * Public API Surface of mat-password-strength\r\n */\r\nexport * from './lib/password-strength.component';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;AAmCA;AACA,MAAM,gBAAgB,GAA2B,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE;AAEtF,MAAM,YAAY,GAAG,OAAO;AAC5B,MAAM,YAAY,GAAG,OAAO;AAC5B,MAAM,SAAS,GAAG,IAAI;AACtB,MAAM,eAAe,GAAG,qCAAqC;AAE7D;AACA;AACA,MAAM,cAAc,GAAG,EAAE;AACzB,MAAM,gBAAgB,GAAG,EAAE;MAWd,yBAAyB,CAAA;AACpC,IAAA,QAAQ,GAAG,KAAK,CAAS,EAAE,CAAC;IAC5B,OAAO,GAAG,KAAK,CAA0B;AACvC,QAAA,GAAG,EAAE,CAAC;AACN,QAAA,SAAS,EAAE,IAAI;AACf,QAAA,SAAS,EAAE,IAAI;AACf,QAAA,MAAM,EAAE,IAAI;AACZ,QAAA,WAAW,EAAE,IAAI;AAClB,KAAA,CAAC;AACF,IAAA,YAAY,GAAG,KAAK,CAAU,IAAI,CAAC;;AAEnC,IAAA,YAAY,GAAG,KAAK,CAAU,IAAI,CAAC;AACnC;;;;AAIG;AACH,IAAA,IAAI,GAAG,KAAK,CAAwB,QAAQ,CAAC;AAC7C;;;;AAIG;AACH,IAAA,UAAU,GAAG,KAAK,CAAW,EAAE,CAAC;AAEf,IAAA,SAAS,GAAG,MAAM,CAAkB,IAAI,CAAC;AAE1D,IAAA,WAAA,GAAA;QACE,MAAM,CAAC,MAAK;AACV,YAAA,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE;gBACnD,OAAO,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,IAAG;oBACxB,MAAM,GAAG,GAAG,CAAiD;oBAC7D,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,KAAK,UAAU,GAAG,GAAG,GAAI,GAA6B,CAAC,OAAO,EAAc;AAC5G,gBAAA,CAAC,CAAC;YACJ;AACF,QAAA,CAAC,CAAC;IACJ;;AAGA,IAAA,YAAY,GAAG,QAAQ,CAAC,MAA0B;QAChD,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;AAAE,YAAA,OAAO,IAAI;AAC/D,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE;AAC3B,QAAA,IAAI,CAAC,EAAE;AAAE,YAAA,OAAO,IAAI;AACpB,QAAA,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;AAC/C,IAAA,CAAC,CAAC;AAEF,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAa;AAC/B,QAAA,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,UAAU,EAAE;AAC9B,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE;AAClC,YAAA,OAAO,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;QACpD;AAEA,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE;AAChC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE;QAC3B,IAAI,KAAK,GAAG,CAAC;QACb,IAAI,KAAK,GAAG,CAAC;AAEb,QAAA,IAAI,IAAI,CAAC,SAAS,EAAE;AAClB,YAAA,KAAK,EAAE;AACP,YAAA,IAAI,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;AAAE,gBAAA,KAAK,EAAE;QAC1C;AAEA,QAAA,IAAI,IAAI,CAAC,SAAS,EAAE;AAClB,YAAA,KAAK,EAAE;AACP,YAAA,IAAI,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;AAAE,gBAAA,KAAK,EAAE;QAC1C;AAEA,QAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AACf,YAAA,KAAK,EAAE;AACP,YAAA,IAAI,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC;AAAE,gBAAA,KAAK,EAAE;QACvC;AAEA,QAAA,IAAI,IAAI,CAAC,WAAW,EAAE;AACpB,YAAA,KAAK,EAAE;AACP,YAAA,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC;AAAE,gBAAA,KAAK,EAAE;QAC7C;AAEA,QAAA,IAAI,IAAI,CAAC,GAAG,EAAE;AACZ,YAAA,KAAK,EAAE;AACP,YAAA,IAAI,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG;AAAE,gBAAA,KAAK,EAAE;QAC1C;QAEA,OAAO,KAAK,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,IAAI,GAAG,CAAC;AAC5D,IAAA,CAAC,CAAC;AAEF,IAAA,KAAK,GAAG,QAAQ,CAAC,MAAoC;AACnD,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE;QAC7B,IAAI,KAAK,GAAG,cAAc;AAAE,YAAA,OAAO,MAAM;QACzC,IAAI,KAAK,GAAG,gBAAgB;AAAE,YAAA,OAAO,QAAQ;AAC7C,QAAA,OAAO,SAAS;AAClB,IAAA,CAAC,CAAC;AAEF,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAa;AACpC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE;QAC7B,IAAI,KAAK,KAAK,CAAC;AAAI,YAAA,OAAO,WAAW;QACrC,IAAI,KAAK,IAAI,EAAE;AAAI,YAAA,OAAO,MAAM;QAChC,IAAI,KAAK,IAAI,EAAE;AAAI,YAAA,OAAO,MAAM;QAChC,IAAI,KAAK,IAAI,EAAE;AAAI,YAAA,OAAO,MAAM;QAChC,IAAI,KAAK,GAAG,GAAG;AAAI,YAAA,OAAO,QAAQ;AAClC,QAAA,OAAO,aAAa;AACtB,IAAA,CAAC,CAAC;;AAGF,IAAA,UAAU,GAAG,QAAQ,CAAC,MAA0B;AAC9C,QAAA,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,QAAQ;AAAE,YAAA,OAAO,EAAE;AACvC,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE;AAChC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE;QAC3B,MAAM,MAAM,GAAwB,EAAE;QAEtC,IAAI,IAAI,CAAC,SAAS;AAAG,YAAA,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,6BAA6B,EAAK,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClH,IAAI,IAAI,CAAC,SAAS;AAAG,YAAA,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,6BAA6B,EAAK,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClH,IAAI,IAAI,CAAC,MAAM;AAAM,YAAA,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAe,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/G,IAAI,IAAI,CAAC,WAAW;AAAC,YAAA,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,8BAA8B,EAAG,MAAM,EAAE,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpH,IAAI,IAAI,CAAC,GAAG;YAAS,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAA,SAAA,EAAY,IAAI,CAAC,GAAG,CAAA,WAAA,CAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;AAEnH,QAAA,OAAO,MAAM;AACf,IAAA,CAAC,CAAC;wGApHS,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;4FAAzB,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECzDtC,wnCA6BA,EAAA,MAAA,EAAA,CAAA,0mCAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDsBY,cAAc,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,OAAA,EAAA,aAAA,EAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAA,cAAA,CAAA,EAAA,QAAA,EAAA,CAAA,gBAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;4FAMb,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBATrC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,uBAAuB,EAAA,UAAA,EACrB,IAAI,EAAA,OAAA,EACP,CAAC,cAAc,CAAC,EAAA,eAAA,EAGR,uBAAuB,CAAC,MAAM,EAAA,aAAA,EAChC,iBAAiB,CAAC,IAAI,EAAA,QAAA,EAAA,wnCAAA,EAAA,MAAA,EAAA,CAAA,0mCAAA,CAAA,EAAA;;;AEvDvC;;AAEG;;ACFH;;AAEG;;;;"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
/** Subset of the zxcvbn result exposed in the public API. */
|
|
3
|
+
export interface ZXCVBNResult {
|
|
4
|
+
/** 0 = very weak … 4 = very strong */
|
|
5
|
+
score: 0 | 1 | 2 | 3 | 4;
|
|
6
|
+
feedback: {
|
|
7
|
+
warning: string;
|
|
8
|
+
suggestions: string[];
|
|
9
|
+
};
|
|
10
|
+
/** Human-readable crack-time estimates for common attack scenarios. */
|
|
11
|
+
crack_times_display: {
|
|
12
|
+
online_throttling_100_per_hour: string;
|
|
13
|
+
online_no_throttling_10_per_second: string;
|
|
14
|
+
offline_slow_hashing_1e4_per_second: string;
|
|
15
|
+
offline_fast_hashing_1e10_per_second: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface PasswordStrengthOptions {
|
|
19
|
+
min?: number;
|
|
20
|
+
lowercase?: boolean;
|
|
21
|
+
uppercase?: boolean;
|
|
22
|
+
number?: boolean;
|
|
23
|
+
specialChar?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface PasswordRuleCheck {
|
|
26
|
+
label: string;
|
|
27
|
+
passed: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare class PasswordStrengthComponent {
|
|
30
|
+
password: import("@angular/core").InputSignal<string>;
|
|
31
|
+
options: import("@angular/core").InputSignal<PasswordStrengthOptions>;
|
|
32
|
+
hideStrength: import("@angular/core").InputSignal<boolean>;
|
|
33
|
+
/** When true, hides the per-rule checklist (simple) or zxcvbn warning/suggestions (advanced). */
|
|
34
|
+
hideFeedback: import("@angular/core").InputSignal<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* Scoring mode:
|
|
37
|
+
* - `'simple'` — fast regex-based rule checks (default)
|
|
38
|
+
* - `'advanced'` — zxcvbn dictionary/pattern analysis with crack-time feedback
|
|
39
|
+
*/
|
|
40
|
+
mode: import("@angular/core").InputSignal<"simple" | "advanced">;
|
|
41
|
+
/**
|
|
42
|
+
* Optional list of user-supplied inputs (name, email, username, etc.) passed
|
|
43
|
+
* to zxcvbn so it can penalise passwords that contain personal information.
|
|
44
|
+
* Only used when mode is 'advanced'.
|
|
45
|
+
*/
|
|
46
|
+
userInputs: import("@angular/core").InputSignal<string[]>;
|
|
47
|
+
private readonly _zxcvbnFn;
|
|
48
|
+
constructor();
|
|
49
|
+
/** Raw zxcvbn result — only populated when mode() is 'advanced' and zxcvbn has loaded. */
|
|
50
|
+
zxcvbnResult: import("@angular/core").Signal<ZXCVBNResult>;
|
|
51
|
+
strength: import("@angular/core").Signal<number>;
|
|
52
|
+
color: import("@angular/core").Signal<"warn" | "accent" | "primary">;
|
|
53
|
+
strengthLabel: import("@angular/core").Signal<string>;
|
|
54
|
+
/** Per-rule pass/fail list — only populated in simple mode. */
|
|
55
|
+
ruleChecks: import("@angular/core").Signal<PasswordRuleCheck[]>;
|
|
56
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PasswordStrengthComponent, never>;
|
|
57
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PasswordStrengthComponent, "mat-password-strength", never, { "password": { "alias": "password"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "hideStrength": { "alias": "hideStrength"; "required": false; "isSignal": true; }; "hideFeedback": { "alias": "hideFeedback"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "userInputs": { "alias": "userInputs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
58
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mat-password-strength",
|
|
3
|
+
"version": "1.0.8",
|
|
4
|
+
"description": "An Angular Material password strength indicator component",
|
|
5
|
+
"author": "Peter Adison",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/cpeteradison/mat-password-strength.git"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"angular",
|
|
12
|
+
"angular material",
|
|
13
|
+
"password",
|
|
14
|
+
"password strength",
|
|
15
|
+
"component"
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@angular/common": "^19.0.0",
|
|
20
|
+
"@angular/core": "^19.0.0",
|
|
21
|
+
"@angular/material": "^19.0.0",
|
|
22
|
+
"zxcvbn": "^4.4.2"
|
|
23
|
+
},
|
|
24
|
+
"peerDependenciesMeta": {
|
|
25
|
+
"zxcvbn": {
|
|
26
|
+
"optional": true
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"overrides": {
|
|
30
|
+
"@tootallnate/once": "3.0.1"
|
|
31
|
+
},
|
|
32
|
+
"sideEffects": false,
|
|
33
|
+
"module": "fesm2022/mat-password-strength.mjs",
|
|
34
|
+
"typings": "index.d.ts",
|
|
35
|
+
"exports": {
|
|
36
|
+
"./package.json": {
|
|
37
|
+
"default": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./index.d.ts",
|
|
41
|
+
"default": "./fesm2022/mat-password-strength.mjs"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"tslib": "^2.3.0"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/public-api.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './lib/password-strength.component';
|