@noctcore/eslint-plugin-async-safety 0.1.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/README.md +61 -0
- package/dist/index.cjs +847 -0
- package/dist/index.d.cts +59 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +819 -0
- package/docs/rules/forward-abort-signal.md +46 -0
- package/docs/rules/no-concurrent-shared-mutation.md +48 -0
- package/docs/rules/no-shared-mutable-module-state.md +62 -0
- package/docs/rules/prefer-parallel-awaits.md +45 -0
- package/docs/rules/require-fetch-timeout.md +55 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @noctcore/eslint-plugin-async-safety
|
|
2
|
+
|
|
3
|
+
Async-correctness rules TypeScript can't catch: unbounded `fetch`, dropped `AbortSignal`s, and
|
|
4
|
+
shared-state / concurrency races. Flat-config only, ESLint 9+.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
bun add -D @noctcore/eslint-plugin-async-safety # or npm i -D / pnpm add -D
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Use
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
// eslint.config.js
|
|
16
|
+
import asyncSafety from '@noctcore/eslint-plugin-async-safety';
|
|
17
|
+
|
|
18
|
+
export default [
|
|
19
|
+
asyncSafety.configs.recommended,
|
|
20
|
+
];
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or wire rules individually:
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
import asyncSafety from '@noctcore/eslint-plugin-async-safety';
|
|
27
|
+
|
|
28
|
+
export default [
|
|
29
|
+
{
|
|
30
|
+
plugins: { 'noctcore-async-safety': asyncSafety },
|
|
31
|
+
rules: {
|
|
32
|
+
'noctcore-async-safety/require-fetch-timeout': ['error', { callees: ['undici.request'] }],
|
|
33
|
+
// Off until you point it at server files:
|
|
34
|
+
'noctcore-async-safety/no-shared-mutable-module-state': ['error', { include: ['**/server/**'] }],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Rules
|
|
41
|
+
|
|
42
|
+
| Rule | Description | 💡 |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| [`require-fetch-timeout`](./docs/rules/require-fetch-timeout.md) | A `fetch` (or configured wrapper) call must carry a `signal`/`timeout` — an unbounded request can hang forever. | 💡 |
|
|
45
|
+
| [`forward-abort-signal`](./docs/rules/forward-abort-signal.md) | A function that accepts an `AbortSignal` but awaits a call without forwarding it leaves that work uncancellable. | |
|
|
46
|
+
| [`no-shared-mutable-module-state`](./docs/rules/no-shared-mutable-module-state.md) | A module-scoped mutable binding written from an exported async/handler function is shared across concurrent requests (opt in via `include`). | |
|
|
47
|
+
| [`prefer-parallel-awaits`](./docs/rules/prefer-parallel-awaits.md) | Consecutive independent awaits can run concurrently with `Promise.all`. | 💡 |
|
|
48
|
+
| [`no-concurrent-shared-mutation`](./docs/rules/no-concurrent-shared-mutation.md) | A read-modify-write of an outer binding inside a concurrent `Promise.all(map(async …))` callback can lose updates. | |
|
|
49
|
+
|
|
50
|
+
## `recommended` preset
|
|
51
|
+
|
|
52
|
+
| Rule | Severity | Notes |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| `require-fetch-timeout` | `error` | Precise and syntactic. |
|
|
55
|
+
| `no-shared-mutable-module-state` | `error` | Inert until you set `include` globs, so it ships enabled but off by default. |
|
|
56
|
+
| `forward-abort-signal` | `warn` | Heuristic — advisory. |
|
|
57
|
+
| `prefer-parallel-awaits` | `warn` | Heuristic — advisory suggestion. |
|
|
58
|
+
| `no-concurrent-shared-mutation` | `warn` | Heuristic — advisory. |
|
|
59
|
+
|
|
60
|
+
The 💡 rules provide editor suggestions (not autofixes) — parallelizing awaits and adding a timeout both change
|
|
61
|
+
runtime behavior, so they are never applied automatically.
|