fortifypass-validator 0.2.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Botshelo Mere
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.
@@ -0,0 +1,332 @@
1
+ Metadata-Version: 2.4
2
+ Name: fortifypass-validator
3
+ Version: 0.2.0
4
+ Summary: A modern password validation library powered by zxcvbn with policy enforcement and CLI support.
5
+ Author: Botshelo Mere
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/botshelo-mere/password-validator
8
+ Project-URL: Repository, https://github.com/botshelo-mere/password-validator
9
+ Project-URL: Issues, https://github.com/botshelo-mere/password-validator/issues
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Development Status :: 4 - Beta
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE.md
21
+ Requires-Dist: zxcvbn>=4.4.28
22
+ Requires-Dist: colorama>=0.4.6
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=7.0; extra == "dev"
25
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
26
+ Requires-Dist: pytest-benchmark>=4.0; extra == "dev"
27
+ Requires-Dist: jsonschema>=4.0; extra == "dev"
28
+ Dynamic: license-file
29
+
30
+ # password-validator
31
+
32
+ [![Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://python.org)
33
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE.md)
34
+ [![Development Status](https://img.shields.io/badge/status-beta-yellow.svg)](https://github.com/botshelo-mere/password-validator)
35
+ [![zxcvbn powered](https://img.shields.io/badge/strength-zxcvbn%20powered-purple.svg)](https://github.com/dwolfhub/zxcvbn-python)
36
+
37
+ A modern, policy-driven password validation library for Python — powered by
38
+ [zxcvbn](https://github.com/dwolfhub/zxcvbn-python) for realistic strength
39
+ estimation and fully configurable rule enforcement with a built-in CLI.
40
+
41
+ ---
42
+
43
+ ## Features
44
+
45
+ - **Policy validation** — enforce length, character classes, spaces, and banned words
46
+ - **Realistic strength scoring** — zxcvbn rates passwords 0–4 with human-readable labels
47
+ - **Unified `evaluate()` API** — combines validation + strength in one call
48
+ - **CLI interface** — interactive (colored) and non-interactive (JSON pipe) modes
49
+ - **Resilient design** — defensive constructor, graceful zxcvbn exception handling
50
+ - **Thread-safe** — safe to share a single `PasswordValidator` across threads
51
+ - **Typed** — full `typing` annotations throughout
52
+
53
+ ---
54
+
55
+ ## Installation
56
+
57
+ ### Using uv (recommended)
58
+
59
+ ```bash
60
+ git clone https://github.com/botshelo-mere/password-validator.git
61
+ cd password-validator
62
+
63
+ # Create virtual environment and install dependencies
64
+ uv sync
65
+ ```
66
+
67
+ ### Using pip
68
+
69
+ ```bash
70
+ pip install password-validator
71
+ ```
72
+
73
+ ### Dev / Testing
74
+
75
+ ```bash
76
+ # Install with development extras (pytest, pytest-cov, pytest-benchmark, jsonschema)
77
+ uv sync --extra dev
78
+ # or
79
+ pip install "password-validator[dev]"
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Quick Start
85
+
86
+ ### Library Usage
87
+
88
+ ```python
89
+ from password_validator import PasswordValidator
90
+
91
+ # Default policy: 12–64 chars, upper, lower, digit, special required
92
+ validator = PasswordValidator()
93
+
94
+ # --- validate() → (bool, list[str]) ---
95
+ valid, errors = validator.validate("Str0ngP@ssw0rd!")
96
+ print(valid) # True
97
+ print(errors) # []
98
+
99
+ valid, errors = validator.validate("weak")
100
+ print(valid) # False
101
+ print(errors) # ['Password must be at least 12 characters long', ...]
102
+
103
+ # --- estimate_strength() → dict ---
104
+ strength = validator.estimate_strength("Str0ngP@ssw0rd!")
105
+ print(strength["score"]) # 3
106
+ print(strength["label"]) # 'Strong'
107
+ print(strength["feedback"]) # []
108
+
109
+ # --- evaluate() — combines both in one call ---
110
+ result = validator.evaluate("Str0ngP@ssw0rd!")
111
+ # {
112
+ # "valid": True,
113
+ # "errors": [],
114
+ # "score": 3,
115
+ # "label": "Strong",
116
+ # "feedback": []
117
+ # }
118
+ ```
119
+
120
+ ### Custom Policy
121
+
122
+ ```python
123
+ validator = PasswordValidator(
124
+ min_length=8,
125
+ max_length=32,
126
+ require_uppercase=True,
127
+ require_lowercase=True,
128
+ require_digit=True,
129
+ require_special=True,
130
+ special_chars="!@#$%",
131
+ allow_spaces=False,
132
+ banned_words=["password", "admin", "secret"],
133
+ )
134
+
135
+ valid, errors = validator.validate("Admin123!")
136
+ # valid=False → "Contains a banned word"
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Configuration Reference
142
+
143
+ | Parameter | Type | Default | Description |
144
+ |---|---|---|---|
145
+ | `min_length` | `int` | `12` | Minimum password length (must be ≥ 1) |
146
+ | `max_length` | `int` | `64` | Maximum password length (must be ≥ `min_length`) |
147
+ | `require_uppercase` | `bool` | `True` | Require at least one uppercase letter |
148
+ | `require_lowercase` | `bool` | `True` | Require at least one lowercase letter |
149
+ | `require_digit` | `bool` | `True` | Require at least one digit |
150
+ | `require_special` | `bool` | `True` | Require at least one special character |
151
+ | `special_chars` | `str` | `"!@#$%^&*"` | Set of accepted special characters |
152
+ | `allow_spaces` | `bool` | `False` | Whether whitespace is permitted |
153
+ | `banned_words` | `list[str] \| None` | `None` | Case-insensitive list of forbidden words |
154
+
155
+ > `ValueError` is raised on construction if `min_length <= 0` or `max_length < min_length`.
156
+
157
+ ---
158
+
159
+ ## API Reference
160
+
161
+ ### `PasswordValidator.validate(pwd: str) → Tuple[bool, List[str]]`
162
+
163
+ Runs all configured policy rules against the password.
164
+
165
+ - Returns `(True, [])` if all rules pass.
166
+ - Returns `(False, [<error messages>])` if any rule fails.
167
+ - Raises `ValueError` if `pwd` is not a `str`.
168
+
169
+ ### `PasswordValidator.estimate_strength(pwd: str) → Dict[str, Any]`
170
+
171
+ Uses zxcvbn to estimate password strength.
172
+
173
+ - Passwords longer than 72 characters are truncated before scoring (zxcvbn limit).
174
+ - Always returns a dictionary — never raises.
175
+
176
+ | Key | Type | Description |
177
+ |---|---|---|
178
+ | `score` | `int` | 0 (Very Weak) → 4 (Very Strong) |
179
+ | `label` | `str` | Human-readable label |
180
+ | `feedback` | `list[str]` | Warnings and suggestions from zxcvbn |
181
+
182
+ ### `PasswordValidator.evaluate(pwd: str) → Dict[str, Any]`
183
+
184
+ Combines `validate()` and `estimate_strength()` into a single result.
185
+
186
+ | Key | Type | Description |
187
+ |---|---|---|
188
+ | `valid` | `bool` | Whether all policy rules passed |
189
+ | `errors` | `list[str]` | Policy error messages |
190
+ | `score` | `int` | zxcvbn score 0–4 |
191
+ | `label` | `str` | Strength label |
192
+ | `feedback` | `list[str]` | zxcvbn feedback |
193
+
194
+ ---
195
+
196
+ ## CLI Usage
197
+
198
+ ### Interactive Mode
199
+
200
+ ```bash
201
+ uv run password-validator
202
+ ```
203
+
204
+ ```
205
+ Password Validator v0.2.0 (zxcvbn powered)
206
+ Type .exit() to quit
207
+
208
+ Enter password: ········
209
+ ✓ Valid password
210
+
211
+ Strength: Strong
212
+ • Use a longer keyboard pattern with more turns
213
+ ```
214
+
215
+ - Type `.exit()` to quit.
216
+ - Press `Ctrl+C` to interrupt.
217
+ - Password input is hidden (no echo).
218
+
219
+ ### Non-Interactive / Pipe Mode
220
+
221
+ Pipe a password directly — output is JSON, exit code is `0` for score ≥ 3, `1` otherwise:
222
+
223
+ ```bash
224
+ echo "Str0ngP@ssw0rd!" | password-validator
225
+ ```
226
+
227
+ ```json
228
+ {
229
+ "valid": true,
230
+ "errors": [],
231
+ "score": 3,
232
+ "label": "Strong",
233
+ "feedback": []
234
+ }
235
+ ```
236
+
237
+ Ideal for shell scripts and CI pipelines:
238
+
239
+ ```bash
240
+ echo "$PASSWORD" | password-validator && echo "Password accepted" || echo "Password rejected"
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Project Structure
246
+
247
+ ```
248
+ password-validator/
249
+ ├── pyproject.toml # Project configuration
250
+ ├── .gitignore
251
+ ├── README.md
252
+ ├── LICENSE.md
253
+ ├── src/
254
+ │ └── password_validator/
255
+ │ ├── __init__.py # Public API (__version__, __all__)
256
+ │ ├── validator.py # Core validation + zxcvbn strength logic
257
+ │ └── cli.py # CLI interface (colorama)
258
+ └── tests/
259
+ ├── __init__.py
260
+ ├── test_validator.py # Validator unit tests
261
+ ├── test_cli.py # CLI unit tests
262
+ └── test_performance_benchmarks.py # pytest-benchmark performance tests
263
+ ```
264
+
265
+ ---
266
+
267
+ ## Development
268
+
269
+ ### Running Tests
270
+
271
+ ```bash
272
+ # All tests
273
+ uv run pytest
274
+
275
+ # With coverage report
276
+ uv run pytest --cov=password_validator --cov-report=term-missing
277
+
278
+ # Verbose output
279
+ uv run pytest -v
280
+ ```
281
+
282
+ ### Running Benchmarks
283
+
284
+ ```bash
285
+ uv run pytest tests/test_performance_benchmarks.py --benchmark-only
286
+ ```
287
+
288
+ ### Code Coverage
289
+
290
+ ```bash
291
+ uv run pytest --cov=password_validator --cov-branch --cov-report=html
292
+ # Open htmlcov/index.html in your browser
293
+ ```
294
+
295
+ ---
296
+
297
+ ## Strength Score Labels
298
+
299
+ | Score | Label | Meaning |
300
+ |---|---|---|
301
+ | 0 | Very Weak | Trivially crackable |
302
+ | 1 | Weak | Easy to crack |
303
+ | 2 | Moderate | Some resistance |
304
+ | 3 | Strong | Good security |
305
+ | 4 | Very Strong | Excellent security |
306
+
307
+ Scoring is provided by [zxcvbn](https://github.com/dwolfhub/zxcvbn-python), which
308
+ models real-world cracking strategies (dictionary attacks, keyboard patterns, etc.)
309
+ rather than simple character-class rules.
310
+
311
+ ---
312
+
313
+ ## Version History
314
+
315
+ | Version | Changes |
316
+ |---|---|
317
+ | **v0.2.0** | Added zxcvbn strength estimation, `evaluate()` API, banned words, full type annotations, colorama CLI, pipe mode, comprehensive test suite |
318
+ | **v0.1.1** | Infrastructure improvements, packaging fixes |
319
+ | **v0.1.0** | Initial public release |
320
+
321
+ ---
322
+
323
+ ## License
324
+
325
+ This project is licensed under the MIT License — see [LICENSE.md](LICENSE.md) for details.
326
+
327
+ ---
328
+
329
+ ## Author
330
+
331
+ **Botshelo Mere**
332
+ GitHub: [botshelo-mere](https://github.com/botshelo-mere)
@@ -0,0 +1,303 @@
1
+ # password-validator
2
+
3
+ [![Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)](https://python.org)
4
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE.md)
5
+ [![Development Status](https://img.shields.io/badge/status-beta-yellow.svg)](https://github.com/botshelo-mere/password-validator)
6
+ [![zxcvbn powered](https://img.shields.io/badge/strength-zxcvbn%20powered-purple.svg)](https://github.com/dwolfhub/zxcvbn-python)
7
+
8
+ A modern, policy-driven password validation library for Python — powered by
9
+ [zxcvbn](https://github.com/dwolfhub/zxcvbn-python) for realistic strength
10
+ estimation and fully configurable rule enforcement with a built-in CLI.
11
+
12
+ ---
13
+
14
+ ## Features
15
+
16
+ - **Policy validation** — enforce length, character classes, spaces, and banned words
17
+ - **Realistic strength scoring** — zxcvbn rates passwords 0–4 with human-readable labels
18
+ - **Unified `evaluate()` API** — combines validation + strength in one call
19
+ - **CLI interface** — interactive (colored) and non-interactive (JSON pipe) modes
20
+ - **Resilient design** — defensive constructor, graceful zxcvbn exception handling
21
+ - **Thread-safe** — safe to share a single `PasswordValidator` across threads
22
+ - **Typed** — full `typing` annotations throughout
23
+
24
+ ---
25
+
26
+ ## Installation
27
+
28
+ ### Using uv (recommended)
29
+
30
+ ```bash
31
+ git clone https://github.com/botshelo-mere/password-validator.git
32
+ cd password-validator
33
+
34
+ # Create virtual environment and install dependencies
35
+ uv sync
36
+ ```
37
+
38
+ ### Using pip
39
+
40
+ ```bash
41
+ pip install password-validator
42
+ ```
43
+
44
+ ### Dev / Testing
45
+
46
+ ```bash
47
+ # Install with development extras (pytest, pytest-cov, pytest-benchmark, jsonschema)
48
+ uv sync --extra dev
49
+ # or
50
+ pip install "password-validator[dev]"
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Quick Start
56
+
57
+ ### Library Usage
58
+
59
+ ```python
60
+ from password_validator import PasswordValidator
61
+
62
+ # Default policy: 12–64 chars, upper, lower, digit, special required
63
+ validator = PasswordValidator()
64
+
65
+ # --- validate() → (bool, list[str]) ---
66
+ valid, errors = validator.validate("Str0ngP@ssw0rd!")
67
+ print(valid) # True
68
+ print(errors) # []
69
+
70
+ valid, errors = validator.validate("weak")
71
+ print(valid) # False
72
+ print(errors) # ['Password must be at least 12 characters long', ...]
73
+
74
+ # --- estimate_strength() → dict ---
75
+ strength = validator.estimate_strength("Str0ngP@ssw0rd!")
76
+ print(strength["score"]) # 3
77
+ print(strength["label"]) # 'Strong'
78
+ print(strength["feedback"]) # []
79
+
80
+ # --- evaluate() — combines both in one call ---
81
+ result = validator.evaluate("Str0ngP@ssw0rd!")
82
+ # {
83
+ # "valid": True,
84
+ # "errors": [],
85
+ # "score": 3,
86
+ # "label": "Strong",
87
+ # "feedback": []
88
+ # }
89
+ ```
90
+
91
+ ### Custom Policy
92
+
93
+ ```python
94
+ validator = PasswordValidator(
95
+ min_length=8,
96
+ max_length=32,
97
+ require_uppercase=True,
98
+ require_lowercase=True,
99
+ require_digit=True,
100
+ require_special=True,
101
+ special_chars="!@#$%",
102
+ allow_spaces=False,
103
+ banned_words=["password", "admin", "secret"],
104
+ )
105
+
106
+ valid, errors = validator.validate("Admin123!")
107
+ # valid=False → "Contains a banned word"
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Configuration Reference
113
+
114
+ | Parameter | Type | Default | Description |
115
+ |---|---|---|---|
116
+ | `min_length` | `int` | `12` | Minimum password length (must be ≥ 1) |
117
+ | `max_length` | `int` | `64` | Maximum password length (must be ≥ `min_length`) |
118
+ | `require_uppercase` | `bool` | `True` | Require at least one uppercase letter |
119
+ | `require_lowercase` | `bool` | `True` | Require at least one lowercase letter |
120
+ | `require_digit` | `bool` | `True` | Require at least one digit |
121
+ | `require_special` | `bool` | `True` | Require at least one special character |
122
+ | `special_chars` | `str` | `"!@#$%^&*"` | Set of accepted special characters |
123
+ | `allow_spaces` | `bool` | `False` | Whether whitespace is permitted |
124
+ | `banned_words` | `list[str] \| None` | `None` | Case-insensitive list of forbidden words |
125
+
126
+ > `ValueError` is raised on construction if `min_length <= 0` or `max_length < min_length`.
127
+
128
+ ---
129
+
130
+ ## API Reference
131
+
132
+ ### `PasswordValidator.validate(pwd: str) → Tuple[bool, List[str]]`
133
+
134
+ Runs all configured policy rules against the password.
135
+
136
+ - Returns `(True, [])` if all rules pass.
137
+ - Returns `(False, [<error messages>])` if any rule fails.
138
+ - Raises `ValueError` if `pwd` is not a `str`.
139
+
140
+ ### `PasswordValidator.estimate_strength(pwd: str) → Dict[str, Any]`
141
+
142
+ Uses zxcvbn to estimate password strength.
143
+
144
+ - Passwords longer than 72 characters are truncated before scoring (zxcvbn limit).
145
+ - Always returns a dictionary — never raises.
146
+
147
+ | Key | Type | Description |
148
+ |---|---|---|
149
+ | `score` | `int` | 0 (Very Weak) → 4 (Very Strong) |
150
+ | `label` | `str` | Human-readable label |
151
+ | `feedback` | `list[str]` | Warnings and suggestions from zxcvbn |
152
+
153
+ ### `PasswordValidator.evaluate(pwd: str) → Dict[str, Any]`
154
+
155
+ Combines `validate()` and `estimate_strength()` into a single result.
156
+
157
+ | Key | Type | Description |
158
+ |---|---|---|
159
+ | `valid` | `bool` | Whether all policy rules passed |
160
+ | `errors` | `list[str]` | Policy error messages |
161
+ | `score` | `int` | zxcvbn score 0–4 |
162
+ | `label` | `str` | Strength label |
163
+ | `feedback` | `list[str]` | zxcvbn feedback |
164
+
165
+ ---
166
+
167
+ ## CLI Usage
168
+
169
+ ### Interactive Mode
170
+
171
+ ```bash
172
+ uv run password-validator
173
+ ```
174
+
175
+ ```
176
+ Password Validator v0.2.0 (zxcvbn powered)
177
+ Type .exit() to quit
178
+
179
+ Enter password: ········
180
+ ✓ Valid password
181
+
182
+ Strength: Strong
183
+ • Use a longer keyboard pattern with more turns
184
+ ```
185
+
186
+ - Type `.exit()` to quit.
187
+ - Press `Ctrl+C` to interrupt.
188
+ - Password input is hidden (no echo).
189
+
190
+ ### Non-Interactive / Pipe Mode
191
+
192
+ Pipe a password directly — output is JSON, exit code is `0` for score ≥ 3, `1` otherwise:
193
+
194
+ ```bash
195
+ echo "Str0ngP@ssw0rd!" | password-validator
196
+ ```
197
+
198
+ ```json
199
+ {
200
+ "valid": true,
201
+ "errors": [],
202
+ "score": 3,
203
+ "label": "Strong",
204
+ "feedback": []
205
+ }
206
+ ```
207
+
208
+ Ideal for shell scripts and CI pipelines:
209
+
210
+ ```bash
211
+ echo "$PASSWORD" | password-validator && echo "Password accepted" || echo "Password rejected"
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Project Structure
217
+
218
+ ```
219
+ password-validator/
220
+ ├── pyproject.toml # Project configuration
221
+ ├── .gitignore
222
+ ├── README.md
223
+ ├── LICENSE.md
224
+ ├── src/
225
+ │ └── password_validator/
226
+ │ ├── __init__.py # Public API (__version__, __all__)
227
+ │ ├── validator.py # Core validation + zxcvbn strength logic
228
+ │ └── cli.py # CLI interface (colorama)
229
+ └── tests/
230
+ ├── __init__.py
231
+ ├── test_validator.py # Validator unit tests
232
+ ├── test_cli.py # CLI unit tests
233
+ └── test_performance_benchmarks.py # pytest-benchmark performance tests
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Development
239
+
240
+ ### Running Tests
241
+
242
+ ```bash
243
+ # All tests
244
+ uv run pytest
245
+
246
+ # With coverage report
247
+ uv run pytest --cov=password_validator --cov-report=term-missing
248
+
249
+ # Verbose output
250
+ uv run pytest -v
251
+ ```
252
+
253
+ ### Running Benchmarks
254
+
255
+ ```bash
256
+ uv run pytest tests/test_performance_benchmarks.py --benchmark-only
257
+ ```
258
+
259
+ ### Code Coverage
260
+
261
+ ```bash
262
+ uv run pytest --cov=password_validator --cov-branch --cov-report=html
263
+ # Open htmlcov/index.html in your browser
264
+ ```
265
+
266
+ ---
267
+
268
+ ## Strength Score Labels
269
+
270
+ | Score | Label | Meaning |
271
+ |---|---|---|
272
+ | 0 | Very Weak | Trivially crackable |
273
+ | 1 | Weak | Easy to crack |
274
+ | 2 | Moderate | Some resistance |
275
+ | 3 | Strong | Good security |
276
+ | 4 | Very Strong | Excellent security |
277
+
278
+ Scoring is provided by [zxcvbn](https://github.com/dwolfhub/zxcvbn-python), which
279
+ models real-world cracking strategies (dictionary attacks, keyboard patterns, etc.)
280
+ rather than simple character-class rules.
281
+
282
+ ---
283
+
284
+ ## Version History
285
+
286
+ | Version | Changes |
287
+ |---|---|
288
+ | **v0.2.0** | Added zxcvbn strength estimation, `evaluate()` API, banned words, full type annotations, colorama CLI, pipe mode, comprehensive test suite |
289
+ | **v0.1.1** | Infrastructure improvements, packaging fixes |
290
+ | **v0.1.0** | Initial public release |
291
+
292
+ ---
293
+
294
+ ## License
295
+
296
+ This project is licensed under the MIT License — see [LICENSE.md](LICENSE.md) for details.
297
+
298
+ ---
299
+
300
+ ## Author
301
+
302
+ **Botshelo Mere**
303
+ GitHub: [botshelo-mere](https://github.com/botshelo-mere)