eslint-plugin-reliability 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/README.md ADDED
@@ -0,0 +1,159 @@
1
+ <p align="center">
2
+ <a href="https://eslint.interlace.tools" target="blank"><img src="https://eslint.interlace.tools/eslint-interlace-logo-light.svg" alt="ESLint Interlace Logo" width="120" /></a>
3
+ </p>
4
+
5
+ <p align="center">
6
+ Error handling, null safety, and runtime reliability rules for robust applications.
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/eslint-plugin-reliability" target="_blank"><img src="https://img.shields.io/npm/v/eslint-plugin-reliability.svg" alt="NPM Version" /></a>
11
+ <a href="https://www.npmjs.com/package/eslint-plugin-reliability" target="_blank"><img src="https://img.shields.io/npm/dm/eslint-plugin-reliability.svg" alt="NPM Downloads" /></a>
12
+ <a href="https://opensource.org/licenses/MIT" target="_blank"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="Package License" /></a>
13
+ </p>
14
+
15
+ ## Description
16
+
17
+ This plugin enforces reliable code patterns by detecting unhandled promises, silent error swallowing, missing null checks, and network timeouts. It helps teams build applications that fail gracefully and recover predictably.
18
+
19
+ ## Philosophy
20
+
21
+ **Interlace** fosters **strength through integration**. Reliability isn't optional — it's foundational. These rules catch common runtime failure patterns before they cause incidents in production.
22
+
23
+ ## Getting Started
24
+
25
+ - To check out the [guide](https://eslint.interlace.tools/docs/reliability), visit [eslint.interlace.tools](https://eslint.interlace.tools). 📚
26
+
27
+ ```bash
28
+ npm install eslint-plugin-reliability --save-dev
29
+ ```
30
+
31
+ ## ⚙️ Configuration Presets
32
+
33
+ | Preset | Description |
34
+ | :------------ | :-------------------------------------------- |
35
+ | `recommended` | Balanced reliability checks for most projects |
36
+
37
+ ---
38
+
39
+ ## 🏢 Usage Example
40
+
41
+ ```js
42
+ // eslint.config.js
43
+ import reliability from 'eslint-plugin-reliability';
44
+
45
+ export default [reliability.configs.recommended];
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Rules
51
+
52
+ ### Error Handling
53
+
54
+ | Rule | Description | 💼 | ⚠️ |
55
+ | :------------------------------------------------------------------- | :-------------------------------------------- | :-: | :-: |
56
+ | [no-unhandled-promise](./docs/rules/no-unhandled-promise.md) | Detect unhandled promise rejections | | |
57
+ | [no-silent-errors](./docs/rules/no-silent-errors.md) | Detect empty catch blocks that swallow errors | 💼 | ⚠️ |
58
+ | [no-missing-error-context](./docs/rules/no-missing-error-context.md) | Require error context when re-throwing | | |
59
+ | [error-message](./docs/rules/error-message.md) | Require meaningful error messages | | |
60
+
61
+ ### Runtime Safety
62
+
63
+ | Rule | Description | 💼 | ⚠️ |
64
+ | :------------------------------------------------------------------- | :--------------------------------------------- | :-: | :-: |
65
+ | [no-missing-null-checks](./docs/rules/no-missing-null-checks.md) | Detect potential null/undefined dereferences | 💼 | ⚠️ |
66
+ | [no-unsafe-type-narrowing](./docs/rules/no-unsafe-type-narrowing.md) | Detect unsafe type narrowing patterns | | |
67
+ | [require-network-timeout](./docs/rules/require-network-timeout.md) | Require timeouts on network requests | 💼 | |
68
+ | [no-await-in-loop](./docs/rules/no-await-in-loop.md) | Detect sequential await in loops (performance) | | |
69
+
70
+ **Legend**: 💼 Recommended | ⚠️ Warns (not error)
71
+
72
+ ---
73
+
74
+ ## Why These Rules?
75
+
76
+ ### `no-silent-errors`
77
+
78
+ Empty catch blocks hide errors, making debugging impossible.
79
+
80
+ ```ts
81
+ // ❌ Bad: Silent error swallowing
82
+ try {
83
+ await processPayment(order);
84
+ } catch (e) {
85
+ // Error is silently ignored!
86
+ }
87
+
88
+ // ✅ Good: Handle or log the error
89
+ try {
90
+ await processPayment(order);
91
+ } catch (e) {
92
+ logger.error('Payment processing failed', { orderId: order.id, error: e });
93
+ throw new PaymentError('Payment failed', { cause: e });
94
+ }
95
+ ```
96
+
97
+ ### `no-missing-null-checks`
98
+
99
+ Catches potential `null` or `undefined` dereferences.
100
+
101
+ ```ts
102
+ // ❌ Bad: Potential runtime error
103
+ function greet(user: User | null) {
104
+ return `Hello, ${user.name}`; // TypeError if user is null!
105
+ }
106
+
107
+ // ✅ Good: Null-safe access
108
+ function greet(user: User | null) {
109
+ return user ? `Hello, ${user.name}` : 'Hello, guest';
110
+ }
111
+ ```
112
+
113
+ ### `require-network-timeout`
114
+
115
+ Network requests without timeouts can hang indefinitely.
116
+
117
+ ```ts
118
+ // ❌ Bad: No timeout, can hang forever
119
+ const response = await fetch('/api/data');
120
+
121
+ // ✅ Good: Request with timeout
122
+ const controller = new AbortController();
123
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
124
+
125
+ const response = await fetch('/api/data', {
126
+ signal: controller.signal,
127
+ });
128
+ clearTimeout(timeoutId);
129
+ ```
130
+
131
+ ### `no-await-in-loop`
132
+
133
+ Sequential awaits in loops cause N+1 performance issues.
134
+
135
+ ```ts
136
+ // ❌ Bad: Sequential requests (slow)
137
+ for (const id of userIds) {
138
+ const user = await fetchUser(id); // N sequential requests
139
+ results.push(user);
140
+ }
141
+
142
+ // ✅ Good: Parallel requests (fast)
143
+ const results = await Promise.all(userIds.map((id) => fetchUser(id)));
144
+ ```
145
+
146
+ ---
147
+
148
+ ## 🔗 Related ESLint Plugins
149
+
150
+ Part of the **Interlace ESLint Ecosystem** — AI-native quality plugins with LLM-optimized error messages:
151
+
152
+ | Plugin | Description |
153
+ | :--------------------------------------------------------------------------------------------- | :---------------------------------- |
154
+ | [`eslint-plugin-operability`](https://www.npmjs.com/package/eslint-plugin-operability) | Production readiness & debug code |
155
+ | [`eslint-plugin-maintainability`](https://www.npmjs.com/package/eslint-plugin-maintainability) | Cognitive complexity & code quality |
156
+
157
+ ## 📄 License
158
+
159
+ MIT © [Ofri Peretz](https://github.com/ofri-peretz)
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "eslint-plugin-reliability",
3
+ "version": "3.0.0",
4
+ "description": "ESLint rules for runtime stability, fault tolerance, and type safety.",
5
+ "type": "commonjs",
6
+ "main": "./src/index.js",
7
+ "types": "./src/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.d.ts",
11
+ "default": "./src/index.js"
12
+ }
13
+ },
14
+ "author": "Ofri Peretz <ofriperetzdev@gmail.com>",
15
+ "license": "MIT",
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "files": [
20
+ "src/",
21
+ "dist/",
22
+ "README.md",
23
+ "LICENSE",
24
+ "CHANGELOG.md"
25
+ ],
26
+ "keywords": [
27
+ "eslint",
28
+ "eslint-plugin",
29
+ "interlace-quality",
30
+ "reliability",
31
+ "error-handling",
32
+ "type-safety",
33
+ "llm-optimized"
34
+ ],
35
+ "engines": {
36
+ "node": ">=18.0.0"
37
+ },
38
+ "dependencies": {
39
+ "tslib": "^2.3.0",
40
+ "@interlace/eslint-devkit": "*"
41
+ },
42
+ "devDependencies": {
43
+ "@typescript-eslint/parser": "^8.46.2",
44
+ "@typescript-eslint/rule-tester": "^8.46.2"
45
+ },
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/ofri-peretz/eslint",
49
+ "directory": "packages/eslint-plugin-reliability"
50
+ },
51
+ "homepage": "https://github.com/ofri-peretz/eslint/tree/main/packages/eslint-plugin-reliability#readme",
52
+ "bugs": {
53
+ "url": "https://github.com/ofri-peretz/eslint/issues"
54
+ }
55
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Copyright (c) 2025 Ofri Peretz
3
+ * Licensed under the MIT License. Use of this source code is governed by the
4
+ * MIT license that can be found in the LICENSE file.
5
+ */
6
+ import type { TSESLint } from '@interlace/eslint-devkit';
7
+ export declare const rules: {
8
+ 'no-unhandled-promise': TSESLint.RuleModule<"unhandledPromise" | "addCatch" | "useTryCatch" | "useAwait", [(import("./rules/error-handling/no-unhandled-promise").Options | undefined)?], unknown, TSESLint.RuleListener> & {
9
+ name: string;
10
+ };
11
+ 'no-silent-errors': TSESLint.RuleModule<"silentError" | "addErrorLogging" | "addErrorHandling" | "rethrowError", [(import("./rules/error-handling/no-silent-errors").Options | undefined)?], unknown, TSESLint.RuleListener> & {
12
+ name: string;
13
+ };
14
+ 'no-missing-error-context': TSESLint.RuleModule<"missingErrorContext" | "addErrorMessage" | "addErrorStack" | "useErrorClass", [(import("./rules/error-handling/no-missing-error-context").Options | undefined)?], unknown, TSESLint.RuleListener> & {
15
+ name: string;
16
+ };
17
+ 'error-message': TSESLint.RuleModule<"addErrorMessage" | "missingErrorMessage", [(import("./rules/error-handling/error-message").Options | undefined)?], unknown, TSESLint.RuleListener> & {
18
+ name: string;
19
+ };
20
+ 'no-missing-null-checks': TSESLint.RuleModule<"missingNullCheck" | "useOptionalChaining" | "useNullishCoalescing" | "addExplicitCheck", [(import("./rules/reliability/no-missing-null-checks").Options | undefined)?], unknown, TSESLint.RuleListener> & {
21
+ name: string;
22
+ };
23
+ 'no-unsafe-type-narrowing': TSESLint.RuleModule<"unsafeTypeNarrowing" | "useTypeGuard" | "useProperNarrowing" | "validateBeforeAssert", [(import("./rules/reliability/no-unsafe-type-narrowing").Options | undefined)?], unknown, TSESLint.RuleListener> & {
24
+ name: string;
25
+ };
26
+ 'require-network-timeout': TSESLint.RuleModule<"violationDetected", [(import("./rules/reliability/require-network-timeout").Options | undefined)?], unknown, TSESLint.RuleListener> & {
27
+ name: string;
28
+ };
29
+ 'no-await-in-loop': TSESLint.RuleModule<"awaitInLoop" | "suggestPromiseAll" | "suggestConcurrent" | "considerSequential" | "asyncLoopPattern", [(import("./rules/reliability/no-await-in-loop").Options | undefined)?], unknown, TSESLint.RuleListener> & {
30
+ name: string;
31
+ };
32
+ 'error-handling/no-unhandled-promise': TSESLint.RuleModule<"unhandledPromise" | "addCatch" | "useTryCatch" | "useAwait", [(import("./rules/error-handling/no-unhandled-promise").Options | undefined)?], unknown, TSESLint.RuleListener> & {
33
+ name: string;
34
+ };
35
+ 'error-handling/no-silent-errors': TSESLint.RuleModule<"silentError" | "addErrorLogging" | "addErrorHandling" | "rethrowError", [(import("./rules/error-handling/no-silent-errors").Options | undefined)?], unknown, TSESLint.RuleListener> & {
36
+ name: string;
37
+ };
38
+ 'error-handling/no-missing-error-context': TSESLint.RuleModule<"missingErrorContext" | "addErrorMessage" | "addErrorStack" | "useErrorClass", [(import("./rules/error-handling/no-missing-error-context").Options | undefined)?], unknown, TSESLint.RuleListener> & {
39
+ name: string;
40
+ };
41
+ 'error-handling/error-message': TSESLint.RuleModule<"addErrorMessage" | "missingErrorMessage", [(import("./rules/error-handling/error-message").Options | undefined)?], unknown, TSESLint.RuleListener> & {
42
+ name: string;
43
+ };
44
+ 'reliability/no-missing-null-checks': TSESLint.RuleModule<"missingNullCheck" | "useOptionalChaining" | "useNullishCoalescing" | "addExplicitCheck", [(import("./rules/reliability/no-missing-null-checks").Options | undefined)?], unknown, TSESLint.RuleListener> & {
45
+ name: string;
46
+ };
47
+ 'reliability/no-unsafe-type-narrowing': TSESLint.RuleModule<"unsafeTypeNarrowing" | "useTypeGuard" | "useProperNarrowing" | "validateBeforeAssert", [(import("./rules/reliability/no-unsafe-type-narrowing").Options | undefined)?], unknown, TSESLint.RuleListener> & {
48
+ name: string;
49
+ };
50
+ 'reliability/require-network-timeout': TSESLint.RuleModule<"violationDetected", [(import("./rules/reliability/require-network-timeout").Options | undefined)?], unknown, TSESLint.RuleListener> & {
51
+ name: string;
52
+ };
53
+ 'reliability/no-await-in-loop': TSESLint.RuleModule<"awaitInLoop" | "suggestPromiseAll" | "suggestConcurrent" | "considerSequential" | "asyncLoopPattern", [(import("./rules/reliability/no-await-in-loop").Options | undefined)?], unknown, TSESLint.RuleListener> & {
54
+ name: string;
55
+ };
56
+ };
57
+ export declare const plugin: {
58
+ meta: {
59
+ name: string;
60
+ version: string;
61
+ };
62
+ rules: {
63
+ 'no-unhandled-promise': TSESLint.RuleModule<"unhandledPromise" | "addCatch" | "useTryCatch" | "useAwait", [(import("./rules/error-handling/no-unhandled-promise").Options | undefined)?], unknown, TSESLint.RuleListener> & {
64
+ name: string;
65
+ };
66
+ 'no-silent-errors': TSESLint.RuleModule<"silentError" | "addErrorLogging" | "addErrorHandling" | "rethrowError", [(import("./rules/error-handling/no-silent-errors").Options | undefined)?], unknown, TSESLint.RuleListener> & {
67
+ name: string;
68
+ };
69
+ 'no-missing-error-context': TSESLint.RuleModule<"missingErrorContext" | "addErrorMessage" | "addErrorStack" | "useErrorClass", [(import("./rules/error-handling/no-missing-error-context").Options | undefined)?], unknown, TSESLint.RuleListener> & {
70
+ name: string;
71
+ };
72
+ 'error-message': TSESLint.RuleModule<"addErrorMessage" | "missingErrorMessage", [(import("./rules/error-handling/error-message").Options | undefined)?], unknown, TSESLint.RuleListener> & {
73
+ name: string;
74
+ };
75
+ 'no-missing-null-checks': TSESLint.RuleModule<"missingNullCheck" | "useOptionalChaining" | "useNullishCoalescing" | "addExplicitCheck", [(import("./rules/reliability/no-missing-null-checks").Options | undefined)?], unknown, TSESLint.RuleListener> & {
76
+ name: string;
77
+ };
78
+ 'no-unsafe-type-narrowing': TSESLint.RuleModule<"unsafeTypeNarrowing" | "useTypeGuard" | "useProperNarrowing" | "validateBeforeAssert", [(import("./rules/reliability/no-unsafe-type-narrowing").Options | undefined)?], unknown, TSESLint.RuleListener> & {
79
+ name: string;
80
+ };
81
+ 'require-network-timeout': TSESLint.RuleModule<"violationDetected", [(import("./rules/reliability/require-network-timeout").Options | undefined)?], unknown, TSESLint.RuleListener> & {
82
+ name: string;
83
+ };
84
+ 'no-await-in-loop': TSESLint.RuleModule<"awaitInLoop" | "suggestPromiseAll" | "suggestConcurrent" | "considerSequential" | "asyncLoopPattern", [(import("./rules/reliability/no-await-in-loop").Options | undefined)?], unknown, TSESLint.RuleListener> & {
85
+ name: string;
86
+ };
87
+ 'error-handling/no-unhandled-promise': TSESLint.RuleModule<"unhandledPromise" | "addCatch" | "useTryCatch" | "useAwait", [(import("./rules/error-handling/no-unhandled-promise").Options | undefined)?], unknown, TSESLint.RuleListener> & {
88
+ name: string;
89
+ };
90
+ 'error-handling/no-silent-errors': TSESLint.RuleModule<"silentError" | "addErrorLogging" | "addErrorHandling" | "rethrowError", [(import("./rules/error-handling/no-silent-errors").Options | undefined)?], unknown, TSESLint.RuleListener> & {
91
+ name: string;
92
+ };
93
+ 'error-handling/no-missing-error-context': TSESLint.RuleModule<"missingErrorContext" | "addErrorMessage" | "addErrorStack" | "useErrorClass", [(import("./rules/error-handling/no-missing-error-context").Options | undefined)?], unknown, TSESLint.RuleListener> & {
94
+ name: string;
95
+ };
96
+ 'error-handling/error-message': TSESLint.RuleModule<"addErrorMessage" | "missingErrorMessage", [(import("./rules/error-handling/error-message").Options | undefined)?], unknown, TSESLint.RuleListener> & {
97
+ name: string;
98
+ };
99
+ 'reliability/no-missing-null-checks': TSESLint.RuleModule<"missingNullCheck" | "useOptionalChaining" | "useNullishCoalescing" | "addExplicitCheck", [(import("./rules/reliability/no-missing-null-checks").Options | undefined)?], unknown, TSESLint.RuleListener> & {
100
+ name: string;
101
+ };
102
+ 'reliability/no-unsafe-type-narrowing': TSESLint.RuleModule<"unsafeTypeNarrowing" | "useTypeGuard" | "useProperNarrowing" | "validateBeforeAssert", [(import("./rules/reliability/no-unsafe-type-narrowing").Options | undefined)?], unknown, TSESLint.RuleListener> & {
103
+ name: string;
104
+ };
105
+ 'reliability/require-network-timeout': TSESLint.RuleModule<"violationDetected", [(import("./rules/reliability/require-network-timeout").Options | undefined)?], unknown, TSESLint.RuleListener> & {
106
+ name: string;
107
+ };
108
+ 'reliability/no-await-in-loop': TSESLint.RuleModule<"awaitInLoop" | "suggestPromiseAll" | "suggestConcurrent" | "considerSequential" | "asyncLoopPattern", [(import("./rules/reliability/no-await-in-loop").Options | undefined)?], unknown, TSESLint.RuleListener> & {
109
+ name: string;
110
+ };
111
+ };
112
+ };
113
+ export declare const configs: {
114
+ recommended: {
115
+ plugins: {
116
+ '@interlace/reliability': {
117
+ meta: {
118
+ name: string;
119
+ version: string;
120
+ };
121
+ rules: {
122
+ 'no-unhandled-promise': TSESLint.RuleModule<"unhandledPromise" | "addCatch" | "useTryCatch" | "useAwait", [(import("./rules/error-handling/no-unhandled-promise").Options | undefined)?], unknown, TSESLint.RuleListener> & {
123
+ name: string;
124
+ };
125
+ 'no-silent-errors': TSESLint.RuleModule<"silentError" | "addErrorLogging" | "addErrorHandling" | "rethrowError", [(import("./rules/error-handling/no-silent-errors").Options | undefined)?], unknown, TSESLint.RuleListener> & {
126
+ name: string;
127
+ };
128
+ 'no-missing-error-context': TSESLint.RuleModule<"missingErrorContext" | "addErrorMessage" | "addErrorStack" | "useErrorClass", [(import("./rules/error-handling/no-missing-error-context").Options | undefined)?], unknown, TSESLint.RuleListener> & {
129
+ name: string;
130
+ };
131
+ 'error-message': TSESLint.RuleModule<"addErrorMessage" | "missingErrorMessage", [(import("./rules/error-handling/error-message").Options | undefined)?], unknown, TSESLint.RuleListener> & {
132
+ name: string;
133
+ };
134
+ 'no-missing-null-checks': TSESLint.RuleModule<"missingNullCheck" | "useOptionalChaining" | "useNullishCoalescing" | "addExplicitCheck", [(import("./rules/reliability/no-missing-null-checks").Options | undefined)?], unknown, TSESLint.RuleListener> & {
135
+ name: string;
136
+ };
137
+ 'no-unsafe-type-narrowing': TSESLint.RuleModule<"unsafeTypeNarrowing" | "useTypeGuard" | "useProperNarrowing" | "validateBeforeAssert", [(import("./rules/reliability/no-unsafe-type-narrowing").Options | undefined)?], unknown, TSESLint.RuleListener> & {
138
+ name: string;
139
+ };
140
+ 'require-network-timeout': TSESLint.RuleModule<"violationDetected", [(import("./rules/reliability/require-network-timeout").Options | undefined)?], unknown, TSESLint.RuleListener> & {
141
+ name: string;
142
+ };
143
+ 'no-await-in-loop': TSESLint.RuleModule<"awaitInLoop" | "suggestPromiseAll" | "suggestConcurrent" | "considerSequential" | "asyncLoopPattern", [(import("./rules/reliability/no-await-in-loop").Options | undefined)?], unknown, TSESLint.RuleListener> & {
144
+ name: string;
145
+ };
146
+ 'error-handling/no-unhandled-promise': TSESLint.RuleModule<"unhandledPromise" | "addCatch" | "useTryCatch" | "useAwait", [(import("./rules/error-handling/no-unhandled-promise").Options | undefined)?], unknown, TSESLint.RuleListener> & {
147
+ name: string;
148
+ };
149
+ 'error-handling/no-silent-errors': TSESLint.RuleModule<"silentError" | "addErrorLogging" | "addErrorHandling" | "rethrowError", [(import("./rules/error-handling/no-silent-errors").Options | undefined)?], unknown, TSESLint.RuleListener> & {
150
+ name: string;
151
+ };
152
+ 'error-handling/no-missing-error-context': TSESLint.RuleModule<"missingErrorContext" | "addErrorMessage" | "addErrorStack" | "useErrorClass", [(import("./rules/error-handling/no-missing-error-context").Options | undefined)?], unknown, TSESLint.RuleListener> & {
153
+ name: string;
154
+ };
155
+ 'error-handling/error-message': TSESLint.RuleModule<"addErrorMessage" | "missingErrorMessage", [(import("./rules/error-handling/error-message").Options | undefined)?], unknown, TSESLint.RuleListener> & {
156
+ name: string;
157
+ };
158
+ 'reliability/no-missing-null-checks': TSESLint.RuleModule<"missingNullCheck" | "useOptionalChaining" | "useNullishCoalescing" | "addExplicitCheck", [(import("./rules/reliability/no-missing-null-checks").Options | undefined)?], unknown, TSESLint.RuleListener> & {
159
+ name: string;
160
+ };
161
+ 'reliability/no-unsafe-type-narrowing': TSESLint.RuleModule<"unsafeTypeNarrowing" | "useTypeGuard" | "useProperNarrowing" | "validateBeforeAssert", [(import("./rules/reliability/no-unsafe-type-narrowing").Options | undefined)?], unknown, TSESLint.RuleListener> & {
162
+ name: string;
163
+ };
164
+ 'reliability/require-network-timeout': TSESLint.RuleModule<"violationDetected", [(import("./rules/reliability/require-network-timeout").Options | undefined)?], unknown, TSESLint.RuleListener> & {
165
+ name: string;
166
+ };
167
+ 'reliability/no-await-in-loop': TSESLint.RuleModule<"awaitInLoop" | "suggestPromiseAll" | "suggestConcurrent" | "considerSequential" | "asyncLoopPattern", [(import("./rules/reliability/no-await-in-loop").Options | undefined)?], unknown, TSESLint.RuleListener> & {
168
+ name: string;
169
+ };
170
+ };
171
+ };
172
+ };
173
+ rules: {
174
+ '@interlace/reliability/error-handling/no-silent-errors': "warn";
175
+ '@interlace/reliability/reliability/no-missing-null-checks': "warn";
176
+ '@interlace/reliability/reliability/require-network-timeout': "error";
177
+ };
178
+ };
179
+ };
180
+ export default plugin;
package/src/index.js ADDED
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2025 Ofri Peretz
4
+ * Licensed under the MIT License. Use of this source code is governed by the
5
+ * MIT license that can be found in the LICENSE file.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.configs = exports.plugin = exports.rules = void 0;
9
+ // Error handling rules
10
+ const no_unhandled_promise_1 = require("./rules/error-handling/no-unhandled-promise");
11
+ const no_silent_errors_1 = require("./rules/error-handling/no-silent-errors");
12
+ const no_missing_error_context_1 = require("./rules/error-handling/no-missing-error-context");
13
+ const error_message_1 = require("./rules/error-handling/error-message");
14
+ // Reliability rules
15
+ const no_missing_null_checks_1 = require("./rules/reliability/no-missing-null-checks");
16
+ const no_unsafe_type_narrowing_1 = require("./rules/reliability/no-unsafe-type-narrowing");
17
+ const require_network_timeout_1 = require("./rules/reliability/require-network-timeout");
18
+ const no_await_in_loop_1 = require("./rules/reliability/no-await-in-loop");
19
+ exports.rules = {
20
+ 'no-unhandled-promise': no_unhandled_promise_1.noUnhandledPromise,
21
+ 'no-silent-errors': no_silent_errors_1.noSilentErrors,
22
+ 'no-missing-error-context': no_missing_error_context_1.noMissingErrorContext,
23
+ 'error-message': error_message_1.errorMessage,
24
+ 'no-missing-null-checks': no_missing_null_checks_1.noMissingNullChecks,
25
+ 'no-unsafe-type-narrowing': no_unsafe_type_narrowing_1.noUnsafeTypeNarrowing,
26
+ 'require-network-timeout': require_network_timeout_1.requireNetworkTimeout,
27
+ 'no-await-in-loop': no_await_in_loop_1.noAwaitInLoop,
28
+ // Categorized names
29
+ 'error-handling/no-unhandled-promise': no_unhandled_promise_1.noUnhandledPromise,
30
+ 'error-handling/no-silent-errors': no_silent_errors_1.noSilentErrors,
31
+ 'error-handling/no-missing-error-context': no_missing_error_context_1.noMissingErrorContext,
32
+ 'error-handling/error-message': error_message_1.errorMessage,
33
+ 'reliability/no-missing-null-checks': no_missing_null_checks_1.noMissingNullChecks,
34
+ 'reliability/no-unsafe-type-narrowing': no_unsafe_type_narrowing_1.noUnsafeTypeNarrowing,
35
+ 'reliability/require-network-timeout': require_network_timeout_1.requireNetworkTimeout,
36
+ 'reliability/no-await-in-loop': no_await_in_loop_1.noAwaitInLoop,
37
+ };
38
+ exports.plugin = {
39
+ meta: {
40
+ name: 'eslint-plugin-reliability',
41
+ version: '1.0.0',
42
+ },
43
+ rules: exports.rules,
44
+ };
45
+ exports.configs = {
46
+ recommended: {
47
+ plugins: {
48
+ '@interlace/reliability': exports.plugin,
49
+ },
50
+ rules: {
51
+ '@interlace/reliability/error-handling/no-silent-errors': 'warn',
52
+ '@interlace/reliability/reliability/no-missing-null-checks': 'warn',
53
+ '@interlace/reliability/reliability/require-network-timeout': 'error',
54
+ },
55
+ },
56
+ };
57
+ exports.default = exports.plugin;
@@ -0,0 +1 @@
1
+ export declare function eslintPluginReliability(): string;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.eslintPluginReliability = eslintPluginReliability;
4
+ function eslintPluginReliability() {
5
+ return 'eslint-plugin-reliability';
6
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Copyright (c) 2025 Ofri Peretz
3
+ * Licensed under the MIT License. Use of this source code is governed by the
4
+ * MIT license that can be found in the LICENSE file.
5
+ */
6
+ /**
7
+ * ESLint Rule: error-message
8
+ * Enforces providing a message when creating built-in Error objects
9
+ */
10
+ import type { TSESLint } from '@interlace/eslint-devkit';
11
+ type MessageIds = 'missingErrorMessage' | 'addErrorMessage';
12
+ export interface Options {
13
+ /** Allow Error() without message (not recommended) */
14
+ allowEmptyCatch?: boolean;
15
+ }
16
+ type RuleOptions = [Options?];
17
+ export declare const errorMessage: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
18
+ name: string;
19
+ };
20
+ export {};
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2025 Ofri Peretz
4
+ * Licensed under the MIT License. Use of this source code is governed by the
5
+ * MIT license that can be found in the LICENSE file.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.errorMessage = void 0;
9
+ const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
+ const eslint_devkit_2 = require("@interlace/eslint-devkit");
11
+ exports.errorMessage = (0, eslint_devkit_1.createRule)({
12
+ name: 'error-message',
13
+ meta: {
14
+ type: 'problem',
15
+ docs: {
16
+ description: 'Enforce providing a message when creating built-in Error objects for better debugging',
17
+ },
18
+ hasSuggestions: true,
19
+ messages: {
20
+ missingErrorMessage: (0, eslint_devkit_2.formatLLMMessage)({
21
+ icon: eslint_devkit_2.MessageIcons.INFO,
22
+ issueName: 'Missing Error Message',
23
+ description: 'Error constructor called without message parameter',
24
+ severity: 'HIGH',
25
+ fix: 'Add descriptive error message: new Error("description")',
26
+ documentationLink: 'https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/error-message.md',
27
+ }),
28
+ addErrorMessage: (0, eslint_devkit_2.formatLLMMessage)({
29
+ icon: eslint_devkit_2.MessageIcons.WARNING,
30
+ issueName: 'Add Error Message',
31
+ description: 'Add descriptive error message to constructor',
32
+ severity: 'HIGH',
33
+ fix: 'Add descriptive error message: new Error("description")',
34
+ documentationLink: 'https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/error-message.md',
35
+ }),
36
+ },
37
+ schema: [
38
+ {
39
+ type: 'object',
40
+ properties: {
41
+ allowEmptyCatch: {
42
+ type: 'boolean',
43
+ default: false,
44
+ },
45
+ },
46
+ additionalProperties: false,
47
+ },
48
+ ],
49
+ },
50
+ defaultOptions: [{ allowEmptyCatch: false }],
51
+ create(context) {
52
+ const [options] = context.options;
53
+ const { allowEmptyCatch = false } = options || {};
54
+ // Built-in Error constructors that should have messages
55
+ const errorConstructors = new Set([
56
+ 'Error',
57
+ 'TypeError',
58
+ 'ReferenceError',
59
+ 'SyntaxError',
60
+ 'RangeError',
61
+ 'EvalError',
62
+ 'URIError',
63
+ ]);
64
+ function isErrorConstructor(name) {
65
+ return errorConstructors.has(name);
66
+ }
67
+ function hasMessageArgument(node) {
68
+ // Check if there are arguments
69
+ if (!node.arguments || node.arguments.length === 0) {
70
+ return false;
71
+ }
72
+ // Check if first argument is a non-empty string or expression
73
+ const firstArg = node.arguments[0];
74
+ if (firstArg.type === 'Literal') {
75
+ // Allow non-empty strings
76
+ return (typeof firstArg.value === 'string' && firstArg.value.trim().length > 0);
77
+ }
78
+ // Allow any expression (variable, function call, etc.) as it might be dynamic
79
+ return true;
80
+ }
81
+ function isInCatchClause(node) {
82
+ let current = node;
83
+ while (current) {
84
+ if (current.type === 'CatchClause') {
85
+ return true;
86
+ }
87
+ current = current.parent;
88
+ }
89
+ return false;
90
+ }
91
+ function checkErrorCreation(node) {
92
+ let constructorName = null;
93
+ if (node.type === 'NewExpression') {
94
+ // new Error(...)
95
+ if (node.callee.type === 'Identifier') {
96
+ constructorName = node.callee.name;
97
+ }
98
+ }
99
+ else if (node.type === 'CallExpression') {
100
+ // Error(...) - function call style
101
+ if (node.callee.type === 'Identifier') {
102
+ constructorName = node.callee.name;
103
+ }
104
+ }
105
+ if (!constructorName || !isErrorConstructor(constructorName)) {
106
+ return;
107
+ }
108
+ // Allow empty catch if option is enabled
109
+ if (allowEmptyCatch && isInCatchClause(node)) {
110
+ return;
111
+ }
112
+ // Check if message is provided
113
+ if (!hasMessageArgument(node)) {
114
+ context.report({
115
+ node,
116
+ messageId: 'missingErrorMessage',
117
+ data: {
118
+ constructor: constructorName,
119
+ },
120
+ suggest: [
121
+ {
122
+ messageId: 'addErrorMessage',
123
+ data: { constructor: constructorName },
124
+ fix(fixer) {
125
+ if (node.arguments.length === 0) {
126
+ // No arguments: replace the empty parentheses with ("Error message")
127
+ const parenStart = node.callee.range[1];
128
+ const parenEnd = node.range[1];
129
+ return fixer.replaceTextRange([parenStart, parenEnd], '("Error message")');
130
+ }
131
+ else {
132
+ // Has arguments: replace the first argument with "Error message"
133
+ return fixer.replaceText(node.arguments[0], '"Error message"');
134
+ }
135
+ },
136
+ },
137
+ ],
138
+ });
139
+ }
140
+ }
141
+ return {
142
+ NewExpression: checkErrorCreation,
143
+ CallExpression: checkErrorCreation,
144
+ };
145
+ },
146
+ });