poof 2.1.1 → 3.0.0

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Hiroki Osame <hiroki.osame@gmail.com>
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 CHANGED
@@ -1,305 +1,248 @@
1
- # Poof
2
-
3
- > Simple data processing with decorators
4
-
5
- [![Build Status](https://travis-ci.org/fatfisz/poof.svg?branch=master)](https://travis-ci.org/fatfisz/poof)
6
- [![Dependency Status](https://david-dm.org/fatfisz/poof.svg?path=packages/poof)](https://david-dm.org/fatfisz/poof?path=packages/poof)
7
- [![devDependency Status](https://david-dm.org/fatfisz/poof/dev-status.svg?path=packages/poof)](https://david-dm.org/fatfisz/poof?path=packages/poof#info=devDependencies)
8
-
9
- Poof is a tool for creating data processing functions in a declarative way by utilising an upcoming JS feature - decorators.
10
-
11
- It makes use of the awesome [validator](https://www.npmjs.com/package/validator) lib to provide assertion functions. Without it Poof probably wouldn't exist.
12
-
13
- ## Contents
14
-
15
- - [Getting started](#getting-started)
16
- - [Why use Poof?](#why-use-poof)
17
- - [Example](#example)
18
- - [Example explained](#example-explained)
19
- - [The difference between poof and poof-cast](#the-difference-between-poof-and-poof-cast)
20
- - [API](#api)
21
- - [createProcessor(config)](#createprocessorconfig)
22
- - [decorators](#decorators)
23
- - [decorators.assert.method and decorators.assert.not.method](#decoratorsassertmethod-and-decoratorsassertnotmethod)
24
- - [decorators.assert.hasType and decorators.assert.not.hasType](#decoratorsasserthastype-and-decoratorsassertnothastype)
25
- - [decorators.assert.isInstanceOf and decorators.assert.not.isInstanceOf](#decoratorsassertisinstanceof-and-decoratorsassertnotisinstanceof)
26
- - [decorators.assign](#decoratorsassign)
27
- - [decorators.filter(predicate)](#decoratorsfilterpredicate)
28
- - [decorators.from(key)](#decoratorsfromkey)
29
- - [decorators.ignoreIf(predicate)](#decoratorsignoreifpredicate)
30
- - [decorators.ignoreIfUndefined](#decoratorsignoreifundefined)
31
- - [decorators.map(mapper)](#decoratorsmapmapper)
32
- - [decorators.set(value)](#decoratorssetvalue)
33
- - [decorators.transform(transformer)](#decoratorstransformtransformer)
34
- - [Some questions you might have](#some-questions-you-might-have)
35
- - [How can I use decorators in my code?](#how-can-i-use-decorators-in-my-code)
36
- - [Why the explicit assignment decorator?](#why-the-explicit-assignment-decorator)
37
- - [What about nested structures?](#what-about-nested-structures)
38
- - [Why is the field error exception a separate package?](#why-is-the-field-error-exception-a-separate-package)
39
- - [Be careful with decorators!](#be-careful-with-decorators)
40
- - [Contributing](#contributing)
41
- - [License](#license)
42
-
43
- ## Getting started
44
-
45
- Install the `poof` package with this command:
46
- ```shell
47
- npm install poof field-validation-error --save
48
- ```
49
- and/or install the `poof-cast` package with this command:
50
- ```shell
51
- npm install poof-cast field-validation-error -save
52
- ```
53
-
54
- `field-validation-error` is a peer dependency of both `poof` packages.
55
-
56
- ### ES6 Data Structures
1
+ <h2 align="center">
2
+ <img width="240" src=".github/logo.png">
3
+ <br><br>
4
+ <a href="https://npm.im/poof"><img src="https://badgen.net/npm/v/poof"></a> <a href="https://npm.im/poof"><img src="https://badgen.net/npm/dm/poof"></a> <a href="https://packagephobia.now.sh/result?p=poof"><img src="https://packagephobia.now.sh/badge?p=poof"></a>
5
+ </h2>
57
6
 
58
- Poof makes use of ES6 data structures, e.g. `WeakMap`, but doesn't include any polyfill.
59
- Make sure you add a polyfill yourself if you want to support older browsers.
7
+ Ever `rm -rf`'d a large directory and sat there waiting for it to finish? With `poof` you no longer need to wait:
60
8
 
61
- ## Why use Poof?
62
-
63
- - it lets you describe data processing in a straightforward, declarative way
64
- - it is especially useful for isomorphic websites - you can declare data validator once and use it both on the client-side and the on the server-side
65
- - Poof processors are composable - you can pass the result from one processor to another, e.g. when you want to separate validation and transforming
9
+ ```sh
10
+ $ poof ./large-file ./large-directory "**/globs"
11
+ ```
66
12
 
67
- ## Example
13
+ ### What's "poof"?
14
+ `poof` is a fast, non-blocking CLI alternative to `rm -rf`, designed for deleting large files and directories without waiting for cleanup to complete.
68
15
 
69
- ```js
70
- import FieldValidationError from 'field-validation-error';
71
- import { createProcessor, decorators } from 'poof-cast';
16
+ It works by quickly moving files and directories out of the way, then deleting them in the background.
17
+ This means large deletions finish instantly from your perspective, even when there's a lot of data to clean up.
72
18
 
73
- const processIdAndIndex = createProcessor({
74
- @decorators.from('postId')
75
- @decorators.assert.not.isNull('Missing post id')
76
- @decorators.assert.isMongoId('Invalid id')
77
- @decorators.transform(ObjectID)
78
- @decorators.assign
79
- _id() {},
19
+ On a large filesystem (4.7 GB, 190k files), `poof` returns control in ~0.6s while `rm -rf` takes ~28s and `rimraf` ~12s. Full benchmarks below.
80
20
 
81
- @decorators.assert.not.isNull('Missing index')
82
- @decorators.assert.isInt('Invalid index', { min: 0 })
83
- @decorators.transform(Number)
84
- @decorators.assign
85
- index() {},
86
- });
21
+ ### Features
22
+ * ⚡ **Immediate return**: deletion doesn't block your shell
23
+ * 🗂 **Move-then-delete**: fast, atomic rename before cleanup
24
+ * 🛡 **Built-in safeguards**: root protection and directory scoping
25
+ * 🖥 **Cross-platform**: macOS, Linux, and Windows
87
26
 
88
- try {
89
- const result = processIdAndIndex(request.body);
27
+ ## Install
90
28
 
91
- // Do something with the result...
92
- } catch(error) {
93
- if (error instanceof FieldValidationError) {
94
- // Do something with error messages contained in `error.fields`
95
- }
96
- }
29
+ ```sh
30
+ npm install -g poof
97
31
  ```
98
32
 
99
- ## Example explained
100
-
101
- Poof library has two versions: [`poof`, and `poof-cast`](#the-difference-between-poof-and-poof-cast). Both of them have two exports: the [`createProcessor` function](#createprocessorconfig) and the [`decorators` object](#decorators).
33
+ ## CLI Usage
102
34
 
103
- ```js
104
- // First import the FieldValidationError and also tools from Poof. The
105
- // `poof-cast` version additionaly casts data to String for assertions. More
106
- // about it later.
107
- import FieldValidationError from 'field-validation-error';
108
- import { createProcessor, decorators } from 'poof-cast';
35
+ ```sh
36
+ # Delete files or directories
37
+ poof node_modules dist
109
38
 
110
- // The `createProcessor` function returns a processor based on the passed
111
- // config object.
112
- const processIdAndIndex = createProcessor({
39
+ # Use glob patterns
40
+ poof "*.log" "temp-*"
113
41
 
114
- // This decorator tells to pick the data from the `postId` property of
115
- // a passed object.
116
- @decorators.from('postId')
42
+ # Recursive match with ** (searches all subdirectories)
43
+ poof "**/node_modules" "**/dist"
117
44
 
118
- // This decorator checks if the value is empty; if not, the error for this
119
- // field is set to 'Missing post id' and this field's processing stops here.
120
- @decorators.assert.not.isNull('Missing post id')
45
+ # Verbose output
46
+ poof --verbose ./large-directory
47
+ ```
121
48
 
122
- // Similarly, check if the value is a MongoDB id.
123
- @decorators.assert.isMongoId('Invalid id')
49
+ ### Options
124
50
 
125
- // This decorator takes a function and uses it to transforms the value.
126
- // In this case the ObjectID constructor is used.
127
- @decorators.transform(ObjectID)
51
+ | Flag | Alias | Description |
52
+ | ------------- | ----- | ---------------------------------------------- |
53
+ | `--dry` | `-d` | Preview files without deleting |
54
+ | `--verbose` | `-v` | Log each file as it's deleted |
55
+ | `--dangerous` | | Allow deleting paths outside current directory |
56
+ | `--version` | | Show version |
57
+ | `--help` | | Show help |
128
58
 
129
- // If you want to have the result of the processing contained in the result
130
- // object, you have to do it explicitly with the `assign` decorator.
131
- @decorators.assign
59
+ ## File matching
132
60
 
133
- // This defines the output property which will have the processed value.
134
- _id() {},
61
+ `poof` accepts explicit paths or [glob patterns](https://en.wikipedia.org/wiki/Glob_%28programming%29) for flexible file matching.
135
62
 
63
+ > [!TIP]
64
+ > When using glob patterns, start with `--dry` to preview what will match before deleting.
136
65
 
137
- // Assert that the value is not empty.
138
- @decorators.assert.not.isNull('Missing index')
66
+ ### Quick refresher: shell quoting
139
67
 
140
- // Assert that the value is an integer, with a value of at least 0.
141
- @decorators.assert.isInt('Invalid index', { min: 0 })
68
+ How unquoted glob patterns are expanded depends on your shell's settings (`dotglob`, `globstar`, etc.).
69
+ To get consistent behavior, quote the pattern so `poof` handles the matching itself.
142
70
 
143
- // Cast to Number
144
- @decorators.transform(Number)
71
+ ```sh
72
+ # The shell expands the glob before poof runs
73
+ $ poof **/node_modules
145
74
 
146
- // Pass to the output
147
- @decorators.assign
75
+ # Recommended: poof expands the glob
76
+ $ poof "**/node_modules"
77
+ ```
148
78
 
149
- // The result will land in the `index` property.
150
- // Note that here we initially get the value from the `index` property, so
151
- // the `from` decorator is not needed.
152
- index() {},
153
- });
79
+ ### Basic patterns
154
80
 
81
+ ```sh
82
+ # Explicit paths
83
+ $ poof node_modules
155
84
 
156
- try {
157
- // Now the processor can be used with an object.
158
- const result = processIdAndIndex(request.body);
85
+ # Multiple paths
86
+ $ poof dist coverage
159
87
 
160
- // Do something with the result...
161
- } catch(error) {
162
- if (error instanceof FieldValidationError) {
163
- // Do something with error messages contained in `error.fields`
164
- }
165
- }
88
+ # Wildcards in current directory
89
+ $ poof "*.log"
90
+ $ poof "temp-*"
166
91
  ```
167
92
 
168
- So if `request.body` is:
169
- ```js
170
- {
171
- postId: '507f1f77bcf86cd799439011',
172
- index: '12',
173
- }
174
- ```
175
- then `result` would be:
176
- ```js
177
- {
178
- _id: ObjectID('507f1f77bcf86cd799439011'),
179
- index: 12,
180
- }
181
- ```
93
+ ### Recursive patterns
182
94
 
183
- If instead `request.body` is:
184
- ```js
185
- {
186
- index: '-1',
187
- }
188
- ```
189
- then you'd get a `FieldValidationError` with the `fields` property equal to:
190
- ```js
191
- {
192
- _id: 'Missing post id',
193
- index: 'Invalid index',
194
- }
195
- ```
196
-
197
- ## The difference between poof and poof-cast
95
+ Use `**` to match across nested directories:
198
96
 
199
- While `poof` passes processed data for asserts without change, `poof-cast` casts it to string beforehand. The casting works the same as default JS string casting, with this exception: `null`, `undefined`, and `NaN` become `''` (empty string).
97
+ ```sh
98
+ # All node_modules in a monorepo
99
+ $ poof "**/node_modules"
200
100
 
201
- This is quite useful for parsing request bodies which usually consist of strings - missing fields assume the value of `''` when auto-casting is on. Because of this `decorators.assert.isNull` from `poof-cast` can be used to check both for field presence and whether the value is a non-empty string. This of course reduces otherwise necessary boilerplate code.
101
+ # All .log files recursively
102
+ $ poof "**/*.log"
202
103
 
203
- The [validator](https://www.npmjs.com/package/validator) library used to make the casting by itself before v. 5. Now it instead throws when the passed argument is not a string. Those exceptions can be thrown when using `poof` (without casting), so be careful when using that version.
204
-
205
- ## API
104
+ # Multiple patterns
105
+ $ poof "**/dist" "**/coverage" "**/*.tmp"
106
+ ```
206
107
 
207
- Both `poof` and `poof-cast` export:
108
+ ### Dotfiles (hidden files)
208
109
 
209
- ### createProcessor(config)
110
+ By default, glob wildcards (`*`, `**`) don't match dotfiles (files starting with `.`). This helps avoid accidentally deleting `.git`, `.env`, or other hidden files.
210
111
 
211
- Use this function with a config object to get a simple data processor.
112
+ To target dotfiles, explicitly include the `.` in your pattern:
212
113
 
213
- The processor either returns an output object or throws `FieldValidationError` if there was a validation error.
114
+ ```sh
115
+ # Delete all dotfiles in current directory
116
+ $ poof ".*"
214
117
 
215
- ### decorators
118
+ # Delete .cache directories (not inside hidden dirs)
119
+ $ poof "**/.cache"
216
120
 
217
- #### decorators.assert.method and decorators.assert.not.method
121
+ # Delete a specific dotfile
122
+ $ poof .env.local
123
+ ```
218
124
 
219
- Those contain all validation functions from the validator lib: `contains`, `equals`, `isAfter`, `isAlpha`, `isAlphanumeric`, `isAscii`, `isBase64`, `isBefore`, `isBoolean`, `isByteLength`, `isCreditCard`, `isCurrency`, `isDataURI`, `isDate`, `isDecimal`, `isDivisibleBy`, `isEmail`, `isFloat`, `isFQDN`, `isFullWidth`, `isHalfWidth`, `isHexadecimal`, `isHexColor`, `isIn`, `isInt`, `isIP`, `isISBN`, `isISIN`, `isISO8601`, `isJSON`, `isLength`, `isLowercase`, `isMACAddress`, `isMobilePhone`, `isMongoId`, `isMultibyte`, `isNull`, `isNumeric`, `isSurrogatePair`, `isUppercase`, `isURL`, `isUUID`, `isVariableWidth`, `isWhitelisted`, and `matches`.
125
+ #### Searching inside hidden directories
220
126
 
221
- *The validator methods will be updated if necessary, but the new ones should be auto-detected in most cases.*
127
+ `**/.cache` finds `.cache` directories in regular directories, but does **not** scan inside other hidden directories like `.git`. This is intentional—scanning hidden directories is slow and rarely useful.
222
128
 
223
- The first argument is always a message that can be found in the thrown exception in case the validation failed.
129
+ To search inside hidden directories, start your pattern with `.*`:
224
130
 
225
- That is followed by other arguments, which are passed to the validation function as the second, third, and so on arguments. The first argument passed is the currently processed value.
131
+ ```sh
132
+ # Find .cache inside any hidden directory
133
+ $ poof ".*/**/.cache"
134
+ ```
226
135
 
227
- So for example for `@decorators.assert.equals('Is different', 'expected')` the arguments passed to the `equals` validator will be the current value and `'expected'`, and if the currently processed value is different from `'expected'`, the error message for this field will be set to `'Is different'`.
136
+ Or use brace expansion to target specific directories:
228
137
 
229
- `decorators.assert.not` works almost the same, only the validation fails if the function returns `true`.
138
+ ```sh
139
+ # Search inside .config and src
140
+ $ poof "{.config,src}/**/.cache"
141
+ ```
230
142
 
231
- #### decorators.assert.hasType and decorators.assert.not.hasType
143
+ ### Negation patterns
232
144
 
233
- An additional assertion function that performs the `typeof` check on the current value and the passed argument.
145
+ Extglob negations like `!(pattern)` match everything *except* the specified pattern:
234
146
 
235
- #### decorators.assert.isInstanceOf and decorators.assert.not.isInstanceOf
147
+ ```sh
148
+ # Delete everything except .gitkeep
149
+ $ poof "!(.gitkeep)"
150
+ ```
236
151
 
237
- An additional assertion function that performs the `instanceof` check on the current value and the passed argument.
152
+ > [!WARNING]
153
+ > Negations match dotfiles. `!(important.txt)` will match `.env`, `.git`, and other hidden files. Always use `--dry` first.
238
154
 
239
- #### decorators.assign
155
+ ## Safety
240
156
 
241
- Used to assign the processed value to the output object. When using, it's best to put this decorator last, because any further processing results won't be saved.
157
+ `poof` includes guards to help prevent common accidents:
242
158
 
243
- #### decorators.filter(predicate)
159
+ - **Root protection**: refuses to delete the filesystem root
160
+ - **Directory scoping**: won't delete paths outside `cwd` unless `--dangerous` is passed
161
+ - **Typo protection**: explicit paths that don't exist are reported as errors, so `poof ndoe_modoules` won't silently succeed
162
+ - **Script-friendly globs**: glob patterns with no matches exit silently, so `poof "*.log"` won't break your scripts
244
163
 
245
- Filters the current value using `predicate`. The current value is assumed to be an array.
164
+ ## How it works
246
165
 
247
- #### decorators.from(key)
166
+ Traditional `rm -rf` blocks until every file is unlinked.
167
+ For large directories, this means waiting on thousands of filesystem operations.
248
168
 
249
- The initial value for the processed field is taken from the input object using its key. If you want to use a value of a different field instead, use this decorator.
169
+ `poof` uses a different strategy:
250
170
 
251
- #### decorators.ignoreIf(predicate)
171
+ 1. Resolve glob patterns and validate paths
172
+ 2. Spawn a detached cleanup process
173
+ 3. Rename files to a temp directory (constant-time, atomic)
174
+ 4. Stream renamed paths to the cleanup process as renames complete
175
+ 5. Exit process to return your shell while cleanup process continues in the background
252
176
 
253
- The predicate is called with the processed value. If the result is `true`, then the processing is stopped for that field and it is omitted from the output object.
177
+ ### Cross-device fallback
254
178
 
255
- #### decorators.ignoreIfUndefined
179
+ If the target is on a different filesystem (`EXDEV`), `poof` falls back to renaming in place with a hidden prefix (e.g., `.poof-uuid-large-directory`) and streams it directly to the cleaner.
256
180
 
257
- Sometimes you might want to process some data only if it's present. Use this decorator to avoid validation and transforming in case the processed value is `undefined`. It's equivalent to `decorators.ignoreIf((value) => typeof value === 'undefined')`.
181
+ ## Benchmarks
258
182
 
259
- #### decorators.map(mapper)
183
+ Deleting `**/node_modules` directories from a synthetic fixture:
260
184
 
261
- Maps the current value using `mapper`. The current value is assumed to be an array.
185
+ | Tool | Time | vs poof |
186
+ | -------- | ------- | ---------- |
187
+ | `poof` | 0.59s | — |
188
+ | `rimraf` | 12.32s | 21x slower |
189
+ | `rm -rf` | 27.82s | 48x slower |
262
190
 
263
- #### decorators.set(value)
191
+ Fixture: 4.7 GB, 190k files
264
192
 
265
- Simply sets the processed value to the one passed in the argument.
193
+ Environment: macOS 15.2, Apple M2 Max, SSD
266
194
 
267
- #### decorators.transform(transformer)
195
+ > [!NOTE]
196
+ > These benchmarks measure how long it takes to return control of your terminal, not actual deletion time. Background deletion continues after `poof` exits. `rm -rf` and `rimraf` measure actual deletion time.
268
197
 
269
- Takes transformer, applies it to the processed value, and sets it as a new processed value. Useful for type casting or otherwise transforming the validated data.
198
+ ## JS API
270
199
 
271
- ## Some questions you might have
200
+ `poof` can also be used programmatically:
272
201
 
273
- ### How can I use decorators in my code?
202
+ ```ts
203
+ import poof from 'poof'
274
204
 
275
- You can use Babel 5 with an optional "es7.decorators" transformer, or use Babel 6 with [this legacy plugin](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy). There are some differences though, you can read about them [here](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy/blob/master/README.md).
205
+ await poof('./large-directory')
206
+ await poof(['**/dist', 'coverage'])
276
207
 
277
- ### Why the explicit assignment decorator?
208
+ const { deleted, errors } = await poof('./large-directory', { dry: true })
209
+ ```
278
210
 
279
- When I started creating what later became Poof I didn't always want to have all of the fields in the output object, and so I decided for the explicit assignment. Maybe in the future I'll add some options to enable auto-assignment for all fields in the object though.
211
+ ### Types
280
212
 
281
- ### What about nested structures?
213
+ ```ts
214
+ type Options = {
215
+ cwd?: string
216
+ dry?: boolean
217
+ dangerous?: boolean
218
+ }
282
219
 
283
- This was supposed to be a simple lib, and I didn't have a need to support nested structures with it. I might do it someday and I'm open to suggestions/PRs.
220
+ type Failure = {
221
+ path: string
222
+ error: Error
223
+ }
284
224
 
285
- ### Why is the field error exception a separate package?
225
+ type Result = {
226
+ deleted: string[]
227
+ errors: Failure[]
228
+ }
229
+ ```
286
230
 
287
- It makes sense to decouple the exception from the `poof` package because another package that handles this exception shouldn't be tied to `poof`. This problem is not contrived, it's an actual problem I had.
231
+ ## Alternatives
288
232
 
289
- ## Be careful with decorators!
233
+ Some tools provide fast, non-blocking removal by moving files to the system trash:
290
234
 
291
- The decorators feature is still at stage 1, a proposal.
292
- This means that the [current decorator spec](https://github.com/wycats/javascript-decorators) can change dramatically before it advances to the next stage.
235
+ - [trash](https://formulae.brew.sh/formula/trash) (macOS)
236
+ - [trash-cli](https://github.com/sindresorhus/trash-cli) (cross-platform)
293
237
 
294
- With this said, no changes should affect your use of this library much. You will be mostly using the exterior (decorator application) which now has a final form more or less, while the interior (how the decorating works in Poof) will be kept up-to-date with the spec.
238
+ These are useful for recoverable deletes, but large directories can accumulate in the trash and consume disk space.
295
239
 
296
- While some things may be set in stone, there is still a room for improvement. So if you care about decorators, please can take part in discussions at [wycats/javascript-decorators](https://github.com/wycats/javascript-decorators) and [jeffmo/es-class-fields-and-static-properties](https://github.com/jeffmo/es-class-fields-and-static-properties).
240
+ `poof` permanently deletes files, freeing space immediately.
297
241
 
298
- ## Contributing
242
+ ## Requirements
299
243
 
300
- In lieu of a formal style guide, take care to maintain the existing coding style.
301
- Add unit tests for any new or changed functionality. Lint and test your code with `npm test`.
244
+ Node.js >= 20.19.6
302
245
 
303
246
  ## License
304
247
 
305
- Copyright (c) 2016 Rafał Ruciński. Licensed under the MIT license.
248
+ MIT