poof 2.2.0 → 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 +21 -0
- package/README.md +165 -227
- package/dist/cli.mjs +842 -0
- package/dist/index.mjs +1854 -0
- package/dist/spawn-rm/worker.mjs +41 -0
- package/package.json +19 -57
- package/dist/index.js +0 -9
- package/lib/index.js +0 -4
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,310 +1,248 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
[](https://david-dm.org/fatfisz/poof?path=packages/poof)
|
|
7
|
-
[](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.assignTo(key)](#decoratorsassigntokey)
|
|
28
|
-
- [decorators.filter(predicate)](#decoratorsfilterpredicate)
|
|
29
|
-
- [decorators.from(key)](#decoratorsfromkey)
|
|
30
|
-
- [decorators.ignoreIf(predicate)](#decoratorsignoreifpredicate)
|
|
31
|
-
- [decorators.ignoreIfUndefined](#decoratorsignoreifundefined)
|
|
32
|
-
- [decorators.map(mapper)](#decoratorsmapmapper)
|
|
33
|
-
- [decorators.set(value)](#decoratorssetvalue)
|
|
34
|
-
- [decorators.transform(transformer)](#decoratorstransformtransformer)
|
|
35
|
-
- [Some questions you might have](#some-questions-you-might-have)
|
|
36
|
-
- [How can I use decorators in my code?](#how-can-i-use-decorators-in-my-code)
|
|
37
|
-
- [Why the explicit assignment decorator?](#why-the-explicit-assignment-decorator)
|
|
38
|
-
- [What about nested structures?](#what-about-nested-structures)
|
|
39
|
-
- [Why is the field error exception a separate package?](#why-is-the-field-error-exception-a-separate-package)
|
|
40
|
-
- [Be careful with decorators!](#be-careful-with-decorators)
|
|
41
|
-
- [Contributing](#contributing)
|
|
42
|
-
- [License](#license)
|
|
43
|
-
|
|
44
|
-
## Getting started
|
|
45
|
-
|
|
46
|
-
Install the `poof` package with this command:
|
|
47
|
-
```shell
|
|
48
|
-
npm install poof field-validation-error --save
|
|
49
|
-
```
|
|
50
|
-
and/or install the `poof-cast` package with this command:
|
|
51
|
-
```shell
|
|
52
|
-
npm install poof-cast field-validation-error -save
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
`field-validation-error` is a peer dependency of both `poof` packages.
|
|
56
|
-
|
|
57
|
-
### ES6 Data Structures
|
|
58
|
-
|
|
59
|
-
Poof makes use of ES6 data structures, e.g. `WeakMap`, but doesn't include any polyfill.
|
|
60
|
-
Make sure you add a polyfill yourself if you want to support older browsers.
|
|
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>
|
|
61
6
|
|
|
62
|
-
|
|
7
|
+
Ever `rm -rf`'d a large directory and sat there waiting for it to finish? With `poof` you no longer need to wait:
|
|
63
8
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
9
|
+
```sh
|
|
10
|
+
$ poof ./large-file ./large-directory "**/globs"
|
|
11
|
+
```
|
|
67
12
|
|
|
68
|
-
|
|
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.
|
|
69
15
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
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.
|
|
73
18
|
|
|
74
|
-
|
|
75
|
-
@decorators.from('postId')
|
|
76
|
-
@decorators.assert.not.isNull('Missing post id')
|
|
77
|
-
@decorators.assert.isMongoId('Invalid id')
|
|
78
|
-
@decorators.transform(ObjectID)
|
|
79
|
-
@decorators.assign
|
|
80
|
-
_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.
|
|
81
20
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
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
|
|
88
26
|
|
|
89
|
-
|
|
90
|
-
const result = processIdAndIndex(request.body);
|
|
27
|
+
## Install
|
|
91
28
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (error instanceof FieldValidationError) {
|
|
95
|
-
// Do something with error messages contained in `error.fields`
|
|
96
|
-
}
|
|
97
|
-
}
|
|
29
|
+
```sh
|
|
30
|
+
npm install -g poof
|
|
98
31
|
```
|
|
99
32
|
|
|
100
|
-
##
|
|
101
|
-
|
|
102
|
-
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).
|
|
103
|
-
|
|
104
|
-
```js
|
|
105
|
-
// First import the FieldValidationError and also tools from Poof. The
|
|
106
|
-
// `poof-cast` version additionaly casts data to String for assertions. More
|
|
107
|
-
// about it later.
|
|
108
|
-
import FieldValidationError from 'field-validation-error';
|
|
109
|
-
import { createProcessor, decorators } from 'poof-cast';
|
|
33
|
+
## CLI Usage
|
|
110
34
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
35
|
+
```sh
|
|
36
|
+
# Delete files or directories
|
|
37
|
+
poof node_modules dist
|
|
114
38
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
@decorators.from('postId')
|
|
39
|
+
# Use glob patterns
|
|
40
|
+
poof "*.log" "temp-*"
|
|
118
41
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
@decorators.assert.not.isNull('Missing post id')
|
|
42
|
+
# Recursive match with ** (searches all subdirectories)
|
|
43
|
+
poof "**/node_modules" "**/dist"
|
|
122
44
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// This decorator takes a function and uses it to transforms the value.
|
|
127
|
-
// In this case the ObjectID constructor is used.
|
|
128
|
-
@decorators.transform(ObjectID)
|
|
129
|
-
|
|
130
|
-
// If you want to have the result of the processing contained in the result
|
|
131
|
-
// object, you have to do it explicitly with the `assign` decorator.
|
|
132
|
-
@decorators.assign
|
|
133
|
-
|
|
134
|
-
// This defines the output property which will have the processed value.
|
|
135
|
-
_id() {},
|
|
45
|
+
# Verbose output
|
|
46
|
+
poof --verbose ./large-directory
|
|
47
|
+
```
|
|
136
48
|
|
|
49
|
+
### Options
|
|
137
50
|
|
|
138
|
-
|
|
139
|
-
|
|
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 |
|
|
140
58
|
|
|
141
|
-
|
|
142
|
-
@decorators.assert.isInt('Invalid index', { min: 0 })
|
|
59
|
+
## File matching
|
|
143
60
|
|
|
144
|
-
|
|
145
|
-
@decorators.transform(Number)
|
|
61
|
+
`poof` accepts explicit paths or [glob patterns](https://en.wikipedia.org/wiki/Glob_%28programming%29) for flexible file matching.
|
|
146
62
|
|
|
147
|
-
|
|
148
|
-
|
|
63
|
+
> [!TIP]
|
|
64
|
+
> When using glob patterns, start with `--dry` to preview what will match before deleting.
|
|
149
65
|
|
|
150
|
-
|
|
151
|
-
// Note that here we initially get the value from the `index` property, so
|
|
152
|
-
// the `from` decorator is not needed.
|
|
153
|
-
index() {},
|
|
154
|
-
});
|
|
66
|
+
### Quick refresher: shell quoting
|
|
155
67
|
|
|
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.
|
|
156
70
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
71
|
+
```sh
|
|
72
|
+
# The shell expands the glob before poof runs
|
|
73
|
+
$ poof **/node_modules
|
|
160
74
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (error instanceof FieldValidationError) {
|
|
164
|
-
// Do something with error messages contained in `error.fields`
|
|
165
|
-
}
|
|
166
|
-
}
|
|
75
|
+
# Recommended: poof expands the glob
|
|
76
|
+
$ poof "**/node_modules"
|
|
167
77
|
```
|
|
168
78
|
|
|
169
|
-
|
|
170
|
-
```js
|
|
171
|
-
{
|
|
172
|
-
postId: '507f1f77bcf86cd799439011',
|
|
173
|
-
index: '12',
|
|
174
|
-
}
|
|
175
|
-
```
|
|
176
|
-
then `result` would be:
|
|
177
|
-
```js
|
|
178
|
-
{
|
|
179
|
-
_id: ObjectID('507f1f77bcf86cd799439011'),
|
|
180
|
-
index: 12,
|
|
181
|
-
}
|
|
182
|
-
```
|
|
79
|
+
### Basic patterns
|
|
183
80
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
index: '-1',
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
then you'd get a `FieldValidationError` with the `fields` property equal to:
|
|
191
|
-
```js
|
|
192
|
-
{
|
|
193
|
-
_id: 'Missing post id',
|
|
194
|
-
index: 'Invalid index',
|
|
195
|
-
}
|
|
196
|
-
```
|
|
81
|
+
```sh
|
|
82
|
+
# Explicit paths
|
|
83
|
+
$ poof node_modules
|
|
197
84
|
|
|
198
|
-
|
|
85
|
+
# Multiple paths
|
|
86
|
+
$ poof dist coverage
|
|
199
87
|
|
|
200
|
-
|
|
88
|
+
# Wildcards in current directory
|
|
89
|
+
$ poof "*.log"
|
|
90
|
+
$ poof "temp-*"
|
|
91
|
+
```
|
|
201
92
|
|
|
202
|
-
|
|
93
|
+
### Recursive patterns
|
|
203
94
|
|
|
204
|
-
|
|
95
|
+
Use `**` to match across nested directories:
|
|
205
96
|
|
|
206
|
-
|
|
97
|
+
```sh
|
|
98
|
+
# All node_modules in a monorepo
|
|
99
|
+
$ poof "**/node_modules"
|
|
207
100
|
|
|
208
|
-
|
|
101
|
+
# All .log files recursively
|
|
102
|
+
$ poof "**/*.log"
|
|
209
103
|
|
|
210
|
-
|
|
104
|
+
# Multiple patterns
|
|
105
|
+
$ poof "**/dist" "**/coverage" "**/*.tmp"
|
|
106
|
+
```
|
|
211
107
|
|
|
212
|
-
|
|
108
|
+
### Dotfiles (hidden files)
|
|
213
109
|
|
|
214
|
-
|
|
110
|
+
By default, glob wildcards (`*`, `**`) don't match dotfiles (files starting with `.`). This helps avoid accidentally deleting `.git`, `.env`, or other hidden files.
|
|
215
111
|
|
|
216
|
-
|
|
112
|
+
To target dotfiles, explicitly include the `.` in your pattern:
|
|
217
113
|
|
|
218
|
-
|
|
114
|
+
```sh
|
|
115
|
+
# Delete all dotfiles in current directory
|
|
116
|
+
$ poof ".*"
|
|
219
117
|
|
|
220
|
-
|
|
118
|
+
# Delete .cache directories (not inside hidden dirs)
|
|
119
|
+
$ poof "**/.cache"
|
|
221
120
|
|
|
222
|
-
|
|
121
|
+
# Delete a specific dotfile
|
|
122
|
+
$ poof .env.local
|
|
123
|
+
```
|
|
223
124
|
|
|
224
|
-
|
|
125
|
+
#### Searching inside hidden directories
|
|
225
126
|
|
|
226
|
-
|
|
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.
|
|
227
128
|
|
|
228
|
-
|
|
129
|
+
To search inside hidden directories, start your pattern with `.*`:
|
|
229
130
|
|
|
230
|
-
|
|
131
|
+
```sh
|
|
132
|
+
# Find .cache inside any hidden directory
|
|
133
|
+
$ poof ".*/**/.cache"
|
|
134
|
+
```
|
|
231
135
|
|
|
232
|
-
|
|
136
|
+
Or use brace expansion to target specific directories:
|
|
233
137
|
|
|
234
|
-
|
|
138
|
+
```sh
|
|
139
|
+
# Search inside .config and src
|
|
140
|
+
$ poof "{.config,src}/**/.cache"
|
|
141
|
+
```
|
|
235
142
|
|
|
236
|
-
|
|
143
|
+
### Negation patterns
|
|
237
144
|
|
|
238
|
-
|
|
145
|
+
Extglob negations like `!(pattern)` match everything *except* the specified pattern:
|
|
239
146
|
|
|
240
|
-
|
|
147
|
+
```sh
|
|
148
|
+
# Delete everything except .gitkeep
|
|
149
|
+
$ poof "!(.gitkeep)"
|
|
150
|
+
```
|
|
241
151
|
|
|
242
|
-
|
|
152
|
+
> [!WARNING]
|
|
153
|
+
> Negations match dotfiles. `!(important.txt)` will match `.env`, `.git`, and other hidden files. Always use `--dry` first.
|
|
243
154
|
|
|
244
|
-
|
|
155
|
+
## Safety
|
|
245
156
|
|
|
246
|
-
|
|
157
|
+
`poof` includes guards to help prevent common accidents:
|
|
247
158
|
|
|
248
|
-
|
|
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
|
|
249
163
|
|
|
250
|
-
|
|
164
|
+
## How it works
|
|
251
165
|
|
|
252
|
-
|
|
166
|
+
Traditional `rm -rf` blocks until every file is unlinked.
|
|
167
|
+
For large directories, this means waiting on thousands of filesystem operations.
|
|
253
168
|
|
|
254
|
-
|
|
169
|
+
`poof` uses a different strategy:
|
|
255
170
|
|
|
256
|
-
|
|
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
|
|
257
176
|
|
|
258
|
-
|
|
177
|
+
### Cross-device fallback
|
|
259
178
|
|
|
260
|
-
|
|
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.
|
|
261
180
|
|
|
262
|
-
|
|
181
|
+
## Benchmarks
|
|
263
182
|
|
|
264
|
-
|
|
183
|
+
Deleting `**/node_modules` directories from a synthetic fixture:
|
|
265
184
|
|
|
266
|
-
|
|
185
|
+
| Tool | Time | vs poof |
|
|
186
|
+
| -------- | ------- | ---------- |
|
|
187
|
+
| `poof` | 0.59s | — |
|
|
188
|
+
| `rimraf` | 12.32s | 21x slower |
|
|
189
|
+
| `rm -rf` | 27.82s | 48x slower |
|
|
267
190
|
|
|
268
|
-
|
|
191
|
+
Fixture: 4.7 GB, 190k files
|
|
269
192
|
|
|
270
|
-
|
|
193
|
+
Environment: macOS 15.2, Apple M2 Max, SSD
|
|
271
194
|
|
|
272
|
-
|
|
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.
|
|
273
197
|
|
|
274
|
-
|
|
198
|
+
## JS API
|
|
275
199
|
|
|
276
|
-
|
|
200
|
+
`poof` can also be used programmatically:
|
|
277
201
|
|
|
278
|
-
|
|
202
|
+
```ts
|
|
203
|
+
import poof from 'poof'
|
|
279
204
|
|
|
280
|
-
|
|
205
|
+
await poof('./large-directory')
|
|
206
|
+
await poof(['**/dist', 'coverage'])
|
|
281
207
|
|
|
282
|
-
|
|
208
|
+
const { deleted, errors } = await poof('./large-directory', { dry: true })
|
|
209
|
+
```
|
|
283
210
|
|
|
284
|
-
|
|
211
|
+
### Types
|
|
285
212
|
|
|
286
|
-
|
|
213
|
+
```ts
|
|
214
|
+
type Options = {
|
|
215
|
+
cwd?: string
|
|
216
|
+
dry?: boolean
|
|
217
|
+
dangerous?: boolean
|
|
218
|
+
}
|
|
287
219
|
|
|
288
|
-
|
|
220
|
+
type Failure = {
|
|
221
|
+
path: string
|
|
222
|
+
error: Error
|
|
223
|
+
}
|
|
289
224
|
|
|
290
|
-
|
|
225
|
+
type Result = {
|
|
226
|
+
deleted: string[]
|
|
227
|
+
errors: Failure[]
|
|
228
|
+
}
|
|
229
|
+
```
|
|
291
230
|
|
|
292
|
-
|
|
231
|
+
## Alternatives
|
|
293
232
|
|
|
294
|
-
|
|
233
|
+
Some tools provide fast, non-blocking removal by moving files to the system trash:
|
|
295
234
|
|
|
296
|
-
|
|
297
|
-
|
|
235
|
+
- [trash](https://formulae.brew.sh/formula/trash) (macOS)
|
|
236
|
+
- [trash-cli](https://github.com/sindresorhus/trash-cli) (cross-platform)
|
|
298
237
|
|
|
299
|
-
|
|
238
|
+
These are useful for recoverable deletes, but large directories can accumulate in the trash and consume disk space.
|
|
300
239
|
|
|
301
|
-
|
|
240
|
+
`poof` permanently deletes files, freeing space immediately.
|
|
302
241
|
|
|
303
|
-
##
|
|
242
|
+
## Requirements
|
|
304
243
|
|
|
305
|
-
|
|
306
|
-
Add unit tests for any new or changed functionality. Lint and test your code with `npm test`.
|
|
244
|
+
Node.js >= 20.19.6
|
|
307
245
|
|
|
308
246
|
## License
|
|
309
247
|
|
|
310
|
-
|
|
248
|
+
MIT
|