@stateward/plugin-errors 0.0.0-stage → 1.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.
Files changed (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -2
  3. package/dist/async-handler-unhandled.d.ts +7 -0
  4. package/dist/async-handler-unhandled.js +342 -0
  5. package/dist/catch-hides-unexpected.d.ts +6 -0
  6. package/dist/catch-hides-unexpected.js +260 -0
  7. package/dist/catch-misses-error.d.ts +6 -0
  8. package/dist/catch-misses-error.js +208 -0
  9. package/dist/catch-without-intent.d.ts +10 -0
  10. package/dist/catch-without-intent.js +590 -0
  11. package/dist/exhaustive-catch.d.ts +6 -0
  12. package/dist/exhaustive-catch.js +190 -0
  13. package/dist/exhaustive-fallback.d.ts +6 -0
  14. package/dist/exhaustive-fallback.js +157 -0
  15. package/dist/finally-can-throw.d.ts +6 -0
  16. package/dist/finally-can-throw.js +184 -0
  17. package/dist/finally-may-throw.d.ts +6 -0
  18. package/dist/finally-may-throw.js +184 -0
  19. package/dist/hover.d.ts +11 -0
  20. package/dist/hover.js +62 -0
  21. package/dist/index.d.ts +7 -0
  22. package/dist/index.js +2 -0
  23. package/dist/inference/builtins.d.ts +52 -0
  24. package/dist/inference/builtins.js +179 -0
  25. package/dist/inference/catches.d.ts +9 -0
  26. package/dist/inference/catches.js +33 -0
  27. package/dist/inference/deps.d.ts +12 -0
  28. package/dist/inference/deps.js +79 -0
  29. package/dist/inference/format.d.ts +25 -0
  30. package/dist/inference/format.js +72 -0
  31. package/dist/inference/packages.d.ts +40 -0
  32. package/dist/inference/packages.js +723 -0
  33. package/dist/inference/project.d.ts +8 -0
  34. package/dist/inference/project.js +22 -0
  35. package/dist/inference/solve.d.ts +128 -0
  36. package/dist/inference/solve.js +575 -0
  37. package/dist/inference/summarize.d.ts +50 -0
  38. package/dist/inference/summarize.js +1324 -0
  39. package/dist/inference/summary.d.ts +191 -0
  40. package/dist/inference/summary.js +237 -0
  41. package/dist/inlay.d.ts +19 -0
  42. package/dist/inlay.js +168 -0
  43. package/dist/must-handle-error.d.ts +55 -0
  44. package/dist/must-handle-error.js +794 -0
  45. package/dist/narrow-try.d.ts +52 -0
  46. package/dist/narrow-try.js +641 -0
  47. package/dist/no-swallow.d.ts +8 -0
  48. package/dist/no-swallow.js +320 -0
  49. package/dist/plugin.d.ts +116 -0
  50. package/dist/plugin.js +39 -0
  51. package/dist/react/boundaries.d.ts +83 -0
  52. package/dist/react/boundaries.js +371 -0
  53. package/dist/react/fallbacks.d.ts +12 -0
  54. package/dist/react/fallbacks.js +83 -0
  55. package/dist/react/render-guards.d.ts +24 -0
  56. package/dist/react/render-guards.js +271 -0
  57. package/dist/react/tree.d.ts +98 -0
  58. package/dist/react/tree.js +863 -0
  59. package/dist/refactors.d.ts +15 -0
  60. package/dist/refactors.js +69 -0
  61. package/dist/render-throw-without-boundary.d.ts +11 -0
  62. package/dist/render-throw-without-boundary.js +223 -0
  63. package/dist/render-throws-without-boundary.d.ts +6 -0
  64. package/dist/render-throws-without-boundary.js +151 -0
  65. package/dist/result-ignored.d.ts +6 -0
  66. package/dist/result-ignored.js +167 -0
  67. package/dist/result-type.d.ts +18 -0
  68. package/dist/result-type.js +55 -0
  69. package/dist/result-unchecked.d.ts +2 -0
  70. package/dist/result-unchecked.js +746 -0
  71. package/dist/rethrow-unknown.d.ts +6 -0
  72. package/dist/rethrow-unknown.js +260 -0
  73. package/dist/rules.meta.d.ts +214 -0
  74. package/dist/rules.meta.js +212 -0
  75. package/dist/syntax.d.ts +41 -0
  76. package/dist/syntax.js +171 -0
  77. package/dist/text.d.ts +22 -0
  78. package/dist/text.js +43 -0
  79. package/dist/throw-in-hook.d.ts +8 -0
  80. package/dist/throw-in-hook.js +510 -0
  81. package/dist/throws-doc-mismatch.d.ts +7 -0
  82. package/dist/throws-doc-mismatch.js +188 -0
  83. package/dist/throws-matches-doc.d.ts +7 -0
  84. package/dist/throws-matches-doc.js +188 -0
  85. package/package.json +34 -3
@@ -0,0 +1,6 @@
1
+ /** The group the rule belongs to in the docs and in `stateward status`. */
2
+ export declare const GROUP = "catch";
3
+ export declare const rethrowUnknown: import("@stateward/core").FileRule<{
4
+ restUsesError: import("@stateward/core").BooleanOption;
5
+ }, "catch">;
6
+ //# sourceMappingURL=rethrow-unknown.d.ts.map
@@ -0,0 +1,260 @@
1
+ import ts from "@stateward/core/typescript";
2
+ import { defineRule, option } from "@stateward/core";
3
+ import { stripParentheses } from "./syntax.js";
4
+ import { HEADLINE_MAX, codeList, fitted, oneLine, withExample } from "./text.js";
5
+ /** The group the rule belongs to in the docs and in `stateward status`. */
6
+ export const GROUP = "catch";
7
+ const EXPLAIN = `# errors/rethrow-unknown
8
+
9
+ A catch that handles some errors with instanceof also rethrows or handles all the others.
10
+
11
+ ## Why
12
+
13
+ A \`catch\` that handles \`NotFoundError\` and \`AuthError\` with \`instanceof\`, and has no last branch, silently drops every other error: a bug, a network failure, a string someone threw. \`save(order)\` fails with a \`TypeError\`, neither branch matches, and the code carries on as if the order was saved.
14
+
15
+ ## When to use it
16
+
17
+ Use it in any code that catches by class. It is on as an error in every preset; keep it on. It needs no knowledge of what the \`try\` throws, so it works even where \`errors/exhaustive-catch\` cannot see.
18
+
19
+ ## What it catches
20
+
21
+ A \`catch (e)\` with an \`if\` / \`else if\` chain or \`switch (true)\` that tests \`e instanceof X\`, and no branch for the other errors. It is fine when:
22
+
23
+ - a last \`else\` or \`default\` does something: rethrows, wraps the error with \`{ cause: e }\`, or handles it;
24
+ - any statement follows the chain in the \`catch\`, since it runs for every error.
25
+
26
+ ### Bad
27
+
28
+ \`\`\`ts
29
+ try {
30
+ await save(order);
31
+ } catch (e) {
32
+ if (e instanceof NotFoundError) {
33
+ showMissing();
34
+ } else if (e instanceof AuthError) {
35
+ redirectToLogin();
36
+ }
37
+ }
38
+ \`\`\`
39
+
40
+ ### Good
41
+
42
+ \`\`\`ts
43
+ try {
44
+ await save(order);
45
+ } catch (e) {
46
+ if (e instanceof NotFoundError) {
47
+ showMissing();
48
+ } else if (e instanceof AuthError) {
49
+ redirectToLogin();
50
+ } else {
51
+ throw e;
52
+ }
53
+ }
54
+ \`\`\`
55
+
56
+ ### Fix
57
+
58
+ A quick fix adds \`else { throw e; }\` to an \`if\` chain. For a \`switch (true)\`, add \`default: throw e;\` yourself; the message shows it.
59
+
60
+ ## What it does not catch
61
+
62
+ - A chain that tests another value, or tests other than \`instanceof\`.
63
+ - A \`catch\` with no parameter, or a destructured one.
64
+ - A last \`else\` that does something but still ignores the error (\`else { showGenericError(); }\`): it counts as handling the rest, unless you set \`restUsesError\` (below). \`errors/exhaustive-catch\` looks at what that \`else\` does with the error.
65
+ - A \`catch\` with no \`instanceof\` chain that ignores the error: that is \`errors/no-swallow\`.
66
+
67
+ ## Turn it on or off
68
+
69
+ In \`stateward.config.ts\`:
70
+
71
+ \`\`\`ts
72
+ rules: { "errors/rethrow-unknown": "off" } // or "warning"
73
+ groups: { "errors/catch": "off" } // the whole group
74
+ rules: { "errors/rethrow-unknown": ["error", { restUsesError: true }] } // the last branch must use the error
75
+ \`\`\`
76
+ `;
77
+ /** The statements of a branch or block, without empty statements; a comment is not a statement. */
78
+ const OPTIONS = {
79
+ restUsesError: option.boolean({
80
+ default: false,
81
+ description: "The branch for the other errors must use the error (rethrow it, wrap it, report it); a generic message alone does not count.",
82
+ }),
83
+ };
84
+ function realStatements(node) {
85
+ if (node === undefined)
86
+ return [];
87
+ const statements = ts.isBlock(node) ? node.statements : [node];
88
+ return statements.filter((statement) => !ts.isEmptyStatement(statement));
89
+ }
90
+ /** The classes of the `instanceof` tests on `variable` in a condition, in source order. */
91
+ function instanceOfClasses(condition, variable, sourceFile) {
92
+ const node = stripParentheses(condition);
93
+ if (!ts.isBinaryExpression(node))
94
+ return [];
95
+ const operator = node.operatorToken.kind;
96
+ if (operator === ts.SyntaxKind.InstanceOfKeyword) {
97
+ const subject = stripParentheses(node.left);
98
+ return ts.isIdentifier(subject) && subject.text === variable
99
+ ? [oneLine(stripParentheses(node.right).getText(sourceFile))]
100
+ : [];
101
+ }
102
+ if (operator === ts.SyntaxKind.BarBarToken ||
103
+ operator === ts.SyntaxKind.AmpersandAmpersandToken) {
104
+ return [
105
+ ...instanceOfClasses(node.left, variable, sourceFile),
106
+ ...instanceOfClasses(node.right, variable, sourceFile),
107
+ ];
108
+ }
109
+ return [];
110
+ }
111
+ /** An `if` / `else if` chain, from its first `if` to its last link. */
112
+ function linksOf(start) {
113
+ const links = [start];
114
+ for (let last = start; last.elseStatement !== undefined && ts.isIfStatement(last.elseStatement);) {
115
+ last = last.elseStatement;
116
+ links.push(last);
117
+ }
118
+ return links;
119
+ }
120
+ function dispatchOf(statement, variable, sourceFile) {
121
+ if (ts.isIfStatement(statement)) {
122
+ const links = linksOf(statement);
123
+ const classes = links.flatMap((link) => instanceOfClasses(link.expression, variable, sourceFile));
124
+ const end = links[links.length - 1];
125
+ if (classes.length === 0 || end === undefined)
126
+ return undefined;
127
+ return {
128
+ start: statement,
129
+ classes,
130
+ rest: realStatements(end.elseStatement),
131
+ openChainEnd: end.elseStatement === undefined ? end : undefined,
132
+ kind: "if",
133
+ };
134
+ }
135
+ if (ts.isSwitchStatement(statement)) {
136
+ if (stripParentheses(statement.expression).kind !== ts.SyntaxKind.TrueKeyword)
137
+ return undefined;
138
+ const classes = statement.caseBlock.clauses.flatMap((clause) => ts.isCaseClause(clause) ? instanceOfClasses(clause.expression, variable, sourceFile) : []);
139
+ if (classes.length === 0)
140
+ return undefined;
141
+ const fallback = statement.caseBlock.clauses.find((clause) => ts.isDefaultClause(clause));
142
+ return {
143
+ start: statement,
144
+ classes,
145
+ rest: (fallback?.statements ?? []).filter((inner) => !ts.isEmptyStatement(inner)),
146
+ openChainEnd: undefined,
147
+ kind: "switch",
148
+ };
149
+ }
150
+ return undefined;
151
+ }
152
+ /** Whether any of `nodes` names `variable` (a nested function's own `e` counts too: rare, and safe). */
153
+ function mentions(nodes, variable) {
154
+ const visit = (node) => (ts.isIdentifier(node) && node.text === variable) || ts.forEachChild(node, visit) === true;
155
+ return nodes.some(visit);
156
+ }
157
+ function ignoringHeadline(classes, variable) {
158
+ return fitted(HEADLINE_MAX, (cut) => `errors other than ${codeList(classes, cut)} are handled here without ever using \`${variable}\``);
159
+ }
160
+ function headline(classes) {
161
+ return fitted(HEADLINE_MAX, (cut) => `errors other than ${codeList(classes, cut)} are silently swallowed here`);
162
+ }
163
+ function hint(dispatch, variable) {
164
+ if (dispatch.kind === "switch") {
165
+ return withExample("end the switch with a case for the rest:", [
166
+ "default:",
167
+ ` throw ${variable};`,
168
+ ]);
169
+ }
170
+ return withExample("end the chain with a branch for the rest:", [
171
+ "} else {",
172
+ ` throw ${variable};`,
173
+ "}",
174
+ ]);
175
+ }
176
+ /** The whitespace at the start of the line that holds `position`. */
177
+ function indentationAt(position, text) {
178
+ const lineStart = text.lastIndexOf("\n", position - 1) + 1;
179
+ return /^[ \t]*/.exec(text.slice(lineStart, position))?.[0] ?? "";
180
+ }
181
+ /** `else { throw e; }` after the last branch, at the indentation of the chain. */
182
+ function rethrowFix(start, end, variable, sourceFile) {
183
+ const indent = indentationAt(start.getStart(sourceFile), sourceFile.text);
184
+ const step = indent.includes("\t") ? "\t" : " ";
185
+ const at = end.thenStatement.getEnd();
186
+ return {
187
+ title: `Rethrow the other errors with \`else { throw ${variable}; }\``,
188
+ edits: [
189
+ {
190
+ location: { file: sourceFile.fileName, start: at, end: at },
191
+ newText: ` else {\n${indent}${step}throw ${variable};\n${indent}}`,
192
+ },
193
+ ],
194
+ };
195
+ }
196
+ function checkCatch(clause, ctx) {
197
+ const declaration = clause.variableDeclaration;
198
+ if (declaration === undefined || !ts.isIdentifier(declaration.name))
199
+ return;
200
+ const variable = declaration.name.text;
201
+ const statements = clause.block.statements;
202
+ let last;
203
+ const classes = [];
204
+ for (const [index, statement] of statements.entries()) {
205
+ const dispatch = dispatchOf(statement, variable, ctx.sourceFile);
206
+ if (dispatch === undefined)
207
+ continue;
208
+ last = { dispatch, index };
209
+ for (const name of dispatch.classes)
210
+ if (!classes.includes(name))
211
+ classes.push(name);
212
+ }
213
+ if (last === undefined)
214
+ return;
215
+ const { dispatch, index } = last;
216
+ // A branch for the rest, or any statement that runs for every error after the chain.
217
+ const after = statements.slice(index + 1).filter((statement) => !ts.isEmptyStatement(statement));
218
+ const rest = [...dispatch.rest, ...after];
219
+ if (rest.length > 0) {
220
+ if (!ctx.options.restUsesError || mentions(rest, variable))
221
+ return;
222
+ ctx.report({
223
+ node: dispatch.start.getFirstToken(ctx.sourceFile) ?? dispatch.start,
224
+ message: ignoringHeadline(classes, variable),
225
+ hint: withExample("rethrow the rest, or pass the error on where you handle it:", [
226
+ `throw ${variable};`,
227
+ ]),
228
+ });
229
+ return;
230
+ }
231
+ const fixes = dispatch.openChainEnd === undefined
232
+ ? []
233
+ : [rethrowFix(dispatch.start, dispatch.openChainEnd, variable, ctx.sourceFile)];
234
+ ctx.report({
235
+ node: dispatch.start.getFirstToken(ctx.sourceFile) ?? dispatch.start,
236
+ message: headline(classes),
237
+ hint: hint(dispatch, variable),
238
+ fixes,
239
+ });
240
+ }
241
+ export const rethrowUnknown = defineRule({
242
+ kind: "file",
243
+ meta: {
244
+ description: "A catch that handles some errors with instanceof also rethrows or handles all the others.",
245
+ defaultSeverity: "error",
246
+ tier: "core",
247
+ group: GROUP,
248
+ options: OPTIONS,
249
+ explain: EXPLAIN,
250
+ },
251
+ check(ctx) {
252
+ const visit = (node) => {
253
+ if (ts.isCatchClause(node))
254
+ checkCatch(node, ctx);
255
+ ts.forEachChild(node, visit);
256
+ };
257
+ visit(ctx.sourceFile);
258
+ },
259
+ });
260
+ //# sourceMappingURL=rethrow-unknown.js.map
@@ -0,0 +1,214 @@
1
+ export declare const RULES: {
2
+ readonly "catch-hides-unexpected": {
3
+ readonly kind: "file";
4
+ readonly meta: {
5
+ readonly description: "A catch that handles some errors with instanceof also rethrows or handles all the others.";
6
+ readonly defaultSeverity: "error";
7
+ readonly tier: "core";
8
+ readonly group: "catch";
9
+ readonly options: {
10
+ readonly restUsesError: {
11
+ readonly kind: "boolean";
12
+ readonly default: false;
13
+ readonly description: "The branch for the other errors must use the error (rethrow it, wrap it, report it); a generic message alone does not count.";
14
+ };
15
+ };
16
+ };
17
+ };
18
+ readonly "catch-without-intent": {
19
+ readonly kind: "file";
20
+ readonly meta: {
21
+ readonly description: "Experimental: on real projects it is mostly wrong; it may change or be removed. A catch that handles every error says how: it narrows, reports, or falls back; one that only logs, or calls something stateward cannot judge, and carries on is reported.";
22
+ readonly defaultSeverity: "warning";
23
+ readonly tier: "addon";
24
+ readonly addon: "catch-intent";
25
+ readonly group: "catch";
26
+ readonly heuristic: true;
27
+ readonly options: {
28
+ readonly loggers: {
29
+ readonly kind: "strings";
30
+ readonly default: readonly ["console", "logger", "log"];
31
+ readonly description: "Objects whose every method only logs (globs like *Logger): a catch that only calls them swallows the error.";
32
+ };
33
+ readonly logMethods: {
34
+ readonly kind: "strings";
35
+ readonly default: readonly ["error", "warn", "info", "debug"];
36
+ readonly description: "Method names that only log, on any object (`Sentry.captureException` is not one).";
37
+ };
38
+ readonly logFunctions: {
39
+ readonly kind: "strings";
40
+ readonly default: readonly [];
41
+ readonly description: "Plain functions of your own that only log, like logError (globs allowed).";
42
+ };
43
+ readonly edgeHelpers: {
44
+ readonly kind: "strings";
45
+ readonly default: readonly [];
46
+ readonly description: "Your functions that report an error where it ends (to a service, to the user): a catch that calls one is handled, like reportAndShow (globs allowed). Sentry, Bugsnag and Datadog are known without it.";
47
+ };
48
+ readonly swallowHelpers: {
49
+ readonly kind: "strings";
50
+ readonly default: readonly [];
51
+ readonly description: "Functions that mark an error as ignored on purpose, like ignoreError (globs allowed): a catch that calls one is not reported.";
52
+ };
53
+ };
54
+ };
55
+ };
56
+ readonly "finally-can-throw": {
57
+ readonly kind: "file";
58
+ readonly meta: {
59
+ readonly description: "A finally block does not call something that throws, so the error that led to it is not lost.";
60
+ readonly defaultSeverity: "error";
61
+ readonly tier: "core";
62
+ readonly group: "catch";
63
+ readonly options: {
64
+ readonly severities: {
65
+ readonly kind: "severities";
66
+ readonly default: {};
67
+ readonly kinds: {
68
+ readonly throw: "a `throw` written in the finally: it throws whenever it runs.";
69
+ readonly builtin: "a built-in such as `JSON.parse` or `new URL`: it throws only on input it cannot handle.";
70
+ readonly "own-function": "a function in the same file with a `throw` in it: it throws on some paths.";
71
+ };
72
+ readonly description: "A severity per kind of call, where you trust one kind less.";
73
+ };
74
+ };
75
+ };
76
+ };
77
+ readonly "catch-misses-error": {
78
+ readonly kind: "project";
79
+ readonly meta: {
80
+ readonly description: "A catch that branches with instanceof handles every error class its try can throw, or rethrows the rest; so does an error boundary's fallback.";
81
+ readonly defaultSeverity: "error";
82
+ readonly tier: "core";
83
+ readonly group: "catch";
84
+ readonly needsJsDoc: true;
85
+ readonly options: {
86
+ readonly ignoreClasses: {
87
+ readonly kind: "strings";
88
+ readonly default: readonly [];
89
+ readonly description: "Error classes a catch never needs a branch for, as its instanceof test names them (SyntaxError, *AbortError).";
90
+ };
91
+ };
92
+ };
93
+ };
94
+ readonly "throws-doc-mismatch": {
95
+ readonly kind: "project";
96
+ readonly meta: {
97
+ readonly description: "A function's @throws tag lists exactly the error classes it can throw, as far as stateward can see.";
98
+ readonly defaultSeverity: "error";
99
+ readonly tier: "core";
100
+ readonly group: "callers";
101
+ readonly needsJsDoc: true;
102
+ readonly options: {
103
+ readonly severities: {
104
+ readonly kind: "severities";
105
+ readonly default: {};
106
+ readonly kinds: {
107
+ readonly missing: "the function can throw a class its `@throws` does not list.";
108
+ readonly extra: "the `@throws` lists a class the function cannot throw (stale, not dangerous).";
109
+ };
110
+ readonly description: "A severity per kind of mismatch.";
111
+ };
112
+ readonly ignoreClasses: {
113
+ readonly kind: "strings";
114
+ readonly default: readonly [];
115
+ readonly description: "Error classes a `@throws` never needs to list (TypeError, *AbortError), as a catch names them.";
116
+ };
117
+ };
118
+ };
119
+ };
120
+ readonly "render-throw-without-boundary": {
121
+ readonly kind: "project";
122
+ readonly meta: {
123
+ readonly description: "A component that can throw a known error while rendering has an error boundary above it.";
124
+ readonly defaultSeverity: "warning";
125
+ readonly tier: "core";
126
+ readonly group: "render";
127
+ readonly needsJsDoc: true;
128
+ readonly options: {
129
+ readonly skipContextGuards: {
130
+ readonly kind: "boolean";
131
+ readonly default: true;
132
+ readonly description: "Leave out the usual check in a context hook: `if (!ctx) throw new Error(\"useAuth must be used within AuthProvider\")`.";
133
+ };
134
+ readonly skipInputGuards: {
135
+ readonly kind: "boolean";
136
+ readonly default: true;
137
+ readonly description: "Leave out a throw when a prop or a parameter whose type rules it out, or a context hook's value, is missing (a mistake of the caller).";
138
+ };
139
+ readonly rootBoundaryCounts: {
140
+ readonly kind: "boolean";
141
+ readonly default: false;
142
+ readonly description: "Count the error boundary around the whole app as handling the error. Off by default: the whole app is then replaced by its fallback, so the rule asks for a boundary nearer the page.";
143
+ };
144
+ readonly boundaries: {
145
+ readonly kind: "record";
146
+ readonly default: {
147
+ readonly "react-error-boundary": readonly ["ErrorBoundary"];
148
+ };
149
+ readonly description: "Error boundary components from packages, by package: add Sentry's with `{ \"@sentry/react\": [\"ErrorBoundary\"] }` (keep react-error-boundary's to keep it).";
150
+ };
151
+ };
152
+ };
153
+ };
154
+ readonly "must-handle-error": {
155
+ readonly kind: "project";
156
+ readonly meta: {
157
+ readonly description: "An error class marked @mustHandle is named in a catch or passed on with @throws by every call that can throw it.";
158
+ readonly defaultSeverity: "error";
159
+ readonly tier: "core";
160
+ readonly group: "callers";
161
+ readonly needsJsDoc: true;
162
+ readonly listUnknowns: true;
163
+ };
164
+ };
165
+ readonly "narrow-try": {
166
+ readonly kind: "project";
167
+ readonly meta: {
168
+ readonly description: "Calls to your own code placed in a `try` after the call it is for: move them after the `try`.";
169
+ readonly defaultSeverity: "warning";
170
+ readonly tier: "addon";
171
+ readonly addon: "narrow-try";
172
+ readonly group: "catch";
173
+ readonly needsJsDoc: true;
174
+ };
175
+ };
176
+ readonly "result-ignored": {
177
+ readonly kind: "file";
178
+ readonly meta: {
179
+ readonly description: "The Result of a call is checked or passed on, not dropped as a statement, after void or after await.";
180
+ readonly defaultSeverity: "error";
181
+ readonly tier: "core";
182
+ readonly group: "callers";
183
+ readonly yieldsTo: readonly ["result-unchecked"];
184
+ readonly options: {
185
+ readonly allowVoid: {
186
+ readonly kind: "boolean";
187
+ readonly default: false;
188
+ readonly description: "Take `void save()` as dropping the Result on purpose, and stay quiet.";
189
+ };
190
+ readonly resultTypes: {
191
+ readonly kind: "strings";
192
+ readonly default: readonly [];
193
+ readonly description: "Your names for a Result type whose shape stateward does not recognise (`Either`, `Outcome*`; globs allowed).";
194
+ };
195
+ readonly ignore: {
196
+ readonly kind: "strings";
197
+ readonly default: readonly [];
198
+ readonly description: "Functions whose Result may be dropped, by the name they are called with (`logEvent`, `*.track`; globs allowed).";
199
+ };
200
+ };
201
+ };
202
+ };
203
+ readonly "result-unchecked": {
204
+ readonly kind: "file";
205
+ readonly meta: {
206
+ readonly description: "A call that reports failure in its value, not by throwing, is checked before the code goes on as if it worked.";
207
+ readonly defaultSeverity: "warning";
208
+ readonly tier: "core";
209
+ readonly group: "callers";
210
+ readonly heuristic: true;
211
+ };
212
+ };
213
+ };
214
+ //# sourceMappingURL=rules.meta.d.ts.map