@starci/eslint-canon-be 1.2.0 → 1.2.2
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/CHANGELOG.md +7 -0
- package/README.md +70 -69
- package/authorization.mjs +106 -106
- package/cdc.mjs +150 -150
- package/comments.mjs +388 -389
- package/cqrs.mjs +302 -303
- package/data-access.mjs +306 -307
- package/e2e-flow.mjs +222 -222
- package/event-delivery.mjs +93 -93
- package/exception-identity.mjs +200 -200
- package/exceptions.mjs +262 -265
- package/index.mjs +107 -107
- package/lib/path.mjs +2 -0
- package/module-layering.mjs +426 -432
- package/naming.mjs +219 -220
- package/observability.mjs +275 -275
- package/package.json +4 -2
- package/testing.mjs +500 -454
- package/transport.mjs +168 -168
- package/type-safety.mjs +510 -380
package/exceptions.mjs
CHANGED
|
@@ -1,265 +1,262 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The rules that hold `exceptions.md`.
|
|
3
|
-
*
|
|
4
|
-
* FOUR RULES, AND THE PAIRING IS THE POINT. Two watch the throw site and two watch the declaration,
|
|
5
|
-
* because either half alone has a hole the other closes: a class that extends a framework base is
|
|
6
|
-
* thrown by its own house-shaped name, so a throw-site rule passes it, and one such class stayed
|
|
7
|
-
* live and thrown from four call sites while the gate stayed green.
|
|
8
|
-
*
|
|
9
|
-
* `throw-abstract-exception` is a heuristic and says so: a rule reading one file at a time cannot
|
|
10
|
-
* verify that a thrown class extends anything, so it matches `Error` and a named list of framework
|
|
11
|
-
* exceptions. `exception-extends-abstract` is what makes that heuristic sound - it guarantees the
|
|
12
|
-
* only `*Exception` classes in the tree are house ones.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (name
|
|
121
|
-
context.report({ node: thrown, messageId: "
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (thrown.arguments.
|
|
173
|
-
context.report({ node: thrown, messageId: "
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (!node.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
"
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
"starci-be/
|
|
262
|
-
|
|
263
|
-
"starci-be/exception-extends-abstract": "error",
|
|
264
|
-
"starci-be/exception-in-errors-folder": "error",
|
|
265
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* The rules that hold `exceptions.md`.
|
|
3
|
+
*
|
|
4
|
+
* FOUR RULES, AND THE PAIRING IS THE POINT. Two watch the throw site and two watch the declaration,
|
|
5
|
+
* because either half alone has a hole the other closes: a class that extends a framework base is
|
|
6
|
+
* thrown by its own house-shaped name, so a throw-site rule passes it, and one such class stayed
|
|
7
|
+
* live and thrown from four call sites while the gate stayed green.
|
|
8
|
+
*
|
|
9
|
+
* `throw-abstract-exception` is a heuristic and says so: a rule reading one file at a time cannot
|
|
10
|
+
* verify that a thrown class extends anything, so it matches `Error` and a named list of framework
|
|
11
|
+
* exceptions. `exception-extends-abstract` is what makes that heuristic sound - it guarantees the
|
|
12
|
+
* only `*Exception` classes in the tree are house ones.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { normalizePath } from "./lib/path.mjs"
|
|
16
|
+
|
|
17
|
+
/** The house base every exception extends. */
|
|
18
|
+
const EXCEPTION_BASE = "AbstractException"
|
|
19
|
+
|
|
20
|
+
/** Anything named like an exception. */
|
|
21
|
+
const EXCEPTION_NAME = /Exception$/
|
|
22
|
+
|
|
23
|
+
/** Framework exceptions that carry an HTTP status and no identity. */
|
|
24
|
+
const FRAMEWORK_EXCEPTIONS = new Set([
|
|
25
|
+
"BadRequestException",
|
|
26
|
+
"NotFoundException",
|
|
27
|
+
"UnauthorizedException",
|
|
28
|
+
"ForbiddenException",
|
|
29
|
+
"InternalServerErrorException",
|
|
30
|
+
"HttpException",
|
|
31
|
+
"ConflictException",
|
|
32
|
+
"NotAcceptableException",
|
|
33
|
+
"RequestTimeoutException",
|
|
34
|
+
"GoneException",
|
|
35
|
+
"PayloadTooLargeException",
|
|
36
|
+
"UnsupportedMediaTypeException",
|
|
37
|
+
"UnprocessableEntityException",
|
|
38
|
+
"NotImplementedException",
|
|
39
|
+
"BadGatewayException",
|
|
40
|
+
"ServiceUnavailableException",
|
|
41
|
+
"GatewayTimeoutException",
|
|
42
|
+
])
|
|
43
|
+
|
|
44
|
+
/** A feature or module capability owns its own errors, including nested error groups. */
|
|
45
|
+
const ERRORS_FOLDER = /\/src\/(?:modules\/(?:domain|platform|integrations)|features)\/[^/]+\/(?:[^/]+\/)*errors\//
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* HFS puts domain failures with their domain capability and provider failures with their integration.
|
|
49
|
+
* A feature can likewise own a transport or orchestration error in its own errors folder. A central
|
|
50
|
+
* platform exceptions capability can still own generic errors.
|
|
51
|
+
*/
|
|
52
|
+
const isInErrorsFolder = (filename) => ERRORS_FOLDER.test(normalizePath(filename))
|
|
53
|
+
|
|
54
|
+
/** The base class's own file, which is the one class allowed to extend something else. */
|
|
55
|
+
const isExceptionBaseFile = (filename) =>
|
|
56
|
+
/\/src\/modules\/(?:domain|platform|integrations)\/[^/]+\/(?:errors\/)?abstract\.ts$/.test(normalizePath(filename))
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The test lanes, where `throw new Error` is a test-runner assertion rather than a domain failure.
|
|
60
|
+
*
|
|
61
|
+
* THE CARVE-OUT WAS ALREADY GRANTED IN PROSE AND MISSING FROM THE RULE, which is the worst of the
|
|
62
|
+
* two places for them to disagree: canon said the lanes may throw, the artifact said they may not,
|
|
63
|
+
* and a repository adopting the rule inherited 69 findings its own canon had already excused.
|
|
64
|
+
*
|
|
65
|
+
* A flow that could not fail its own setup would have to invent a domain exception for "the fixture
|
|
66
|
+
* is missing", which is a failure of the test rather than of the business - and naming it as a
|
|
67
|
+
* domain failure would put it in the same vocabulary the product uses to describe real ones.
|
|
68
|
+
*/
|
|
69
|
+
const isTestLane = (filename) => {
|
|
70
|
+
const path = normalizePath(filename)
|
|
71
|
+
return /\.spec\.ts$/.test(path) || /-spec\.ts$/.test(path) || path.includes("/src/tests/")
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A liveness or readiness probe, where the HTTP STATUS is the whole contract.
|
|
76
|
+
*
|
|
77
|
+
* THE RULE'S OWN REASON IS WHAT GRANTS THIS. A framework exception is refused because it "carries an
|
|
78
|
+
* HTTP status and no identity" - the status is not enough to tell two failures apart. A probe is the
|
|
79
|
+
* one endpoint where that sentence inverts: kubernetes reads the status code and NOTHING else, never
|
|
80
|
+
* the body, so the status is the entire identity and a domain exception carrying a rich code would
|
|
81
|
+
* arrive as a 500 and be read as "down" for the wrong reason.
|
|
82
|
+
*
|
|
83
|
+
* Narrow on purpose. Only a health controller qualifies, so this cannot become the place a service
|
|
84
|
+
* goes to avoid naming its failures.
|
|
85
|
+
*/
|
|
86
|
+
const isHealthProbe = (filename) => /\/health(?:z)?\.controller\.ts$|\/health\//.test(normalizePath(filename))
|
|
87
|
+
|
|
88
|
+
// -- EXCEPTION-1 -----------------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
/** Throw a house exception, never `Error` and never a framework built-in. */
|
|
91
|
+
export const throwAbstractException = {
|
|
92
|
+
meta: {
|
|
93
|
+
type: "problem",
|
|
94
|
+
docs: { description: "Throw an `AbstractException` subclass, not `Error` or a framework exception." },
|
|
95
|
+
schema: [],
|
|
96
|
+
messages: {
|
|
97
|
+
bareError:
|
|
98
|
+
"`throw new Error(...)` carries a sentence and no code, so nothing downstream can group, match or retry on it without parsing English. Throw the exception class that NAMES this failure.",
|
|
99
|
+
framework:
|
|
100
|
+
"`throw new {{name}}(...)` carries an HTTP status and no identity - two unrelated failures arrive at a client indistinguishable, and the only thing telling them apart is the message, which is the part that gets reworded. Throw an `AbstractException` subclass.",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
create(context) {
|
|
104
|
+
const filename = context.filename || context.getFilename()
|
|
105
|
+
// the lanes canon already excuses - see `isTestLane`
|
|
106
|
+
if (isTestLane(filename)) return {}
|
|
107
|
+
const probe = isHealthProbe(filename)
|
|
108
|
+
return {
|
|
109
|
+
ThrowStatement(node) {
|
|
110
|
+
const thrown = node.argument
|
|
111
|
+
if (!thrown || thrown.type !== "NewExpression" || thrown.callee.type !== "Identifier") return
|
|
112
|
+
const name = thrown.callee.name
|
|
113
|
+
if (name === "Error") {
|
|
114
|
+
context.report({ node: thrown, messageId: "bareError" })
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
// a probe answers with a status code and nothing else - see `isHealthProbe`. `Error` above
|
|
118
|
+
// is still refused here: a probe's status is its contract, an unnamed crash is not.
|
|
119
|
+
if (probe) return
|
|
120
|
+
if (FRAMEWORK_EXCEPTIONS.has(name)) {
|
|
121
|
+
context.report({ node: thrown, messageId: "framework", data: { name } })
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// -- EXCEPTION-2 -----------------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
/** The constructor takes one metadata object, `{}` when there is nothing to say. */
|
|
131
|
+
export const requireExceptionObjectArg = {
|
|
132
|
+
meta: {
|
|
133
|
+
type: "problem",
|
|
134
|
+
docs: { description: "`new XException(...)` passes exactly one object-literal argument." },
|
|
135
|
+
schema: [],
|
|
136
|
+
messages: {
|
|
137
|
+
zero:
|
|
138
|
+
"`new {{name}}()` - pass the metadata object even when it is empty: `new {{name}}({})`. One spelling for every throw means a reader never has to check whether THIS exception happens to take arguments.",
|
|
139
|
+
notObject:
|
|
140
|
+
"`new {{name}}(...)` takes a metadata OBJECT, not a positional value. A positional shape cannot grow: the day this failure needs a second field, every throw site is edited and the ones edited wrong still compile.",
|
|
141
|
+
extra:
|
|
142
|
+
"`new {{name}}(...)` takes exactly ONE metadata object, not several arguments.",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
create(context) {
|
|
146
|
+
return {
|
|
147
|
+
ThrowStatement(node) {
|
|
148
|
+
const thrown = node.argument
|
|
149
|
+
if (!thrown || thrown.type !== "NewExpression" || thrown.callee.type !== "Identifier") return
|
|
150
|
+
const name = thrown.callee.name
|
|
151
|
+
if (name === EXCEPTION_BASE || !EXCEPTION_NAME.test(name)) return
|
|
152
|
+
/*
|
|
153
|
+
* A FRAMEWORK EXCEPTION IS NOT A HOUSE ONE, AND ITS SHAPE IS NOT OURS TO DICTATE.
|
|
154
|
+
*
|
|
155
|
+
* This rule says a house exception takes exactly one metadata object. `EXCEPTION_NAME`
|
|
156
|
+
* matches anything ending in `Exception`, so it was also demanding that shape from Nest's
|
|
157
|
+
* own classes - `new ServiceUnavailableException(body)` is the constructor Nest published,
|
|
158
|
+
* and rewriting it to satisfy a house convention would change what the framework sends.
|
|
159
|
+
*
|
|
160
|
+
* Whether a framework exception may be thrown at all is EXCEPTION-1's question, and it
|
|
161
|
+
* answers it. This rule only governs the ones we declare.
|
|
162
|
+
*/
|
|
163
|
+
if (FRAMEWORK_EXCEPTIONS.has(name)) return
|
|
164
|
+
|
|
165
|
+
if (thrown.arguments.length === 0) {
|
|
166
|
+
context.report({ node: thrown, messageId: "zero", data: { name } })
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
if (thrown.arguments.length > 1) {
|
|
170
|
+
context.report({ node: thrown, messageId: "extra", data: { name } })
|
|
171
|
+
}
|
|
172
|
+
if (thrown.arguments[0].type !== "ObjectExpression") {
|
|
173
|
+
context.report({ node: thrown.arguments[0], messageId: "notObject", data: { name } })
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// -- EXCEPTION-3 -----------------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
/** The class extends the house base, so the throw site tells the truth about what is thrown. */
|
|
183
|
+
export const exceptionExtendsAbstract = {
|
|
184
|
+
meta: {
|
|
185
|
+
type: "problem",
|
|
186
|
+
docs: { description: "An `*Exception` class extends `AbstractException`." },
|
|
187
|
+
schema: [],
|
|
188
|
+
messages: {
|
|
189
|
+
base:
|
|
190
|
+
"`{{name}}` extends `{{parent}}`. Every exception extends `{{expected}}` - a framework base slips past the throw-site rule, because the throw then READS as a house exception and nothing looks wrong at any call site.",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
create(context) {
|
|
194
|
+
const baseFile = isExceptionBaseFile(context.filename || context.getFilename())
|
|
195
|
+
return {
|
|
196
|
+
ClassDeclaration(node) {
|
|
197
|
+
if (!node.id || !EXCEPTION_NAME.test(node.id.name)) return
|
|
198
|
+
if (baseFile && node.id.name === EXCEPTION_BASE) return
|
|
199
|
+
const parent = node.superClass
|
|
200
|
+
if (!parent || parent.type !== "Identifier" || parent.name === EXCEPTION_BASE) return
|
|
201
|
+
context.report({
|
|
202
|
+
node: parent,
|
|
203
|
+
messageId: "base",
|
|
204
|
+
data: { name: node.id.name, parent: parent.name, expected: EXCEPTION_BASE },
|
|
205
|
+
})
|
|
206
|
+
},
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// -- EXCEPTION-4 -----------------------------------------------------------------------------------
|
|
212
|
+
|
|
213
|
+
/** Each capability declares its failures in its own errors folder. */
|
|
214
|
+
export const exceptionInErrorsFolder = {
|
|
215
|
+
meta: {
|
|
216
|
+
type: "problem",
|
|
217
|
+
docs: { description: "An `*Exception` class is declared under its owning capability's errors folder." },
|
|
218
|
+
schema: [],
|
|
219
|
+
messages: {
|
|
220
|
+
place:
|
|
221
|
+
"`{{name}}` is declared outside its owning feature or module capability's errors folder. Put the failure under that owner's errors directory.",
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
create(context) {
|
|
225
|
+
const filename = context.filename || context.getFilename()
|
|
226
|
+
if (isInErrorsFolder(filename)) return {}
|
|
227
|
+
const baseFile = isExceptionBaseFile(filename)
|
|
228
|
+
return {
|
|
229
|
+
ClassDeclaration(node) {
|
|
230
|
+
if (!node.id || !EXCEPTION_NAME.test(node.id.name)) return
|
|
231
|
+
if (baseFile && node.id.name === EXCEPTION_BASE) return
|
|
232
|
+
// an undecorated class with no base is a shape, not an exception - the declaration this
|
|
233
|
+
// rule is after always extends something
|
|
234
|
+
if (!node.superClass) return
|
|
235
|
+
context.report({ node: node.id, messageId: "place", data: { name: node.id.name } })
|
|
236
|
+
},
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** The rules this law contributes to the plugin. */
|
|
242
|
+
export const rules = {
|
|
243
|
+
"throw-abstract-exception": throwAbstractException,
|
|
244
|
+
"require-exception-object-arg": requireExceptionObjectArg,
|
|
245
|
+
"exception-extends-abstract": exceptionExtendsAbstract,
|
|
246
|
+
"exception-in-errors-folder": exceptionInErrorsFolder,
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* The level this law asks for, as the plugin's own opinion.
|
|
251
|
+
*
|
|
252
|
+
* All four measured at zero debt in the reference repository. The sanctioned exit is stated where it
|
|
253
|
+
* applies rather than carved into the rule: the spec family and the test tree may `throw new Error`,
|
|
254
|
+
* because there it means "the runner cannot continue" rather than naming a failure the product can
|
|
255
|
+
* produce. A consuming repository turns `throw-abstract-exception` off for those globs.
|
|
256
|
+
*/
|
|
257
|
+
export const recommended = {
|
|
258
|
+
"starci-be/throw-abstract-exception": "error",
|
|
259
|
+
"starci-be/require-exception-object-arg": "error",
|
|
260
|
+
"starci-be/exception-extends-abstract": "error",
|
|
261
|
+
"starci-be/exception-in-errors-folder": "error",
|
|
262
|
+
}
|