@echoes-of-order/eslint-config 1.121.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 (171) hide show
  1. package/CHANGELOG.md +1093 -0
  2. package/configs/.gitkeep +1 -0
  3. package/configs/admin.js +203 -0
  4. package/configs/api-client.js +46 -0
  5. package/configs/backend.js +895 -0
  6. package/configs/domains.js +123 -0
  7. package/configs/frontend.js +30 -0
  8. package/configs/image-server.js +26 -0
  9. package/configs/ionos-proxy.js +372 -0
  10. package/configs/nestjs.js +156 -0
  11. package/configs/node.js +92 -0
  12. package/configs/react.js +111 -0
  13. package/configs/wiki.js +42 -0
  14. package/index.js +39 -0
  15. package/package.json +85 -0
  16. package/rules/.gitkeep +1 -0
  17. package/rules/__tests__/analyze-relation-usage.test.js.disabled +300 -0
  18. package/rules/__tests__/complexity.test.js.disabled +300 -0
  19. package/rules/__tests__/enforce-dto-factory-in-services.integration.test.js +226 -0
  20. package/rules/__tests__/enforce-dto-factory-in-services.test.js +177 -0
  21. package/rules/__tests__/enforce-entity-dto-create-no-id.integration.test.js +18 -0
  22. package/rules/__tests__/enforce-function-argument-count.test.js.disabled +300 -0
  23. package/rules/__tests__/enforce-repository-token-handling.test.js +58 -0
  24. package/rules/__tests__/english-only-code-strings.test.js.disabled +300 -0
  25. package/rules/__tests__/eslint-rules.integration.test.ts +350 -0
  26. package/rules/__tests__/integration-test-controller-response-dto.js +261 -0
  27. package/rules/__tests__/integration-test-dto-factory-in-services.js +260 -0
  28. package/rules/__tests__/integration-test-no-entity-type-casting.js +161 -0
  29. package/rules/__tests__/integration-test-typeorm-naming-conventions.js +501 -0
  30. package/rules/__tests__/test-config.js +33 -0
  31. package/rules/admin-controller-security.js +180 -0
  32. package/rules/analyze-relation-usage.js +687 -0
  33. package/rules/api-response-dto.js +174 -0
  34. package/rules/auth-guard-required.js +142 -0
  35. package/rules/backend-specific.js +36 -0
  36. package/rules/best-practices.js +421 -0
  37. package/rules/complexity.js +20 -0
  38. package/rules/controller-architecture.js +340 -0
  39. package/rules/controller-naming-conventions.js +190 -0
  40. package/rules/controller-readonly-restriction.js +148 -0
  41. package/rules/controller-swagger-complete.js +312 -0
  42. package/rules/controller-swagger-docs.js +119 -0
  43. package/rules/controller-swagger-english.js +320 -0
  44. package/rules/coordinate-naming.js +132 -0
  45. package/rules/custom-mui-button.js +135 -0
  46. package/rules/dead-code-detection-backend.js +50 -0
  47. package/rules/dead-code-detection-frontend.js +48 -0
  48. package/rules/dead-code-detection.js +71 -0
  49. package/rules/debug-controller-response-dto.js +79 -0
  50. package/rules/deprecate.js +8 -0
  51. package/rules/dto-annotation-property-consistency.js +111 -0
  52. package/rules/dto-entity-mapping-completeness.js +688 -0
  53. package/rules/dto-entity-swagger-separation.js +265 -0
  54. package/rules/dto-entity-type-consistency.js +352 -0
  55. package/rules/dto-entity-type-matching.js +519 -0
  56. package/rules/dto-naming-convention.js +98 -0
  57. package/rules/dto-visibility-modifiers.js +159 -0
  58. package/rules/enforce-api-versioning.js +122 -0
  59. package/rules/enforce-app-module-registration.js +179 -0
  60. package/rules/enforce-basecontroller.js +152 -0
  61. package/rules/enforce-body-request-dto.js +141 -0
  62. package/rules/enforce-controller-response-dto.js +349 -0
  63. package/rules/enforce-custom-error-classes.js +242 -0
  64. package/rules/enforce-database-transaction-safety.js +179 -0
  65. package/rules/enforce-dto-constructor.js +95 -0
  66. package/rules/enforce-dto-create-parameter-types.js +170 -0
  67. package/rules/enforce-dto-create-pattern.js +274 -0
  68. package/rules/enforce-dto-entity-creation.js +164 -0
  69. package/rules/enforce-dto-factory-in-services.js +188 -0
  70. package/rules/enforce-dto-from-entity-method.js +47 -0
  71. package/rules/enforce-dto-from-entity.js +314 -0
  72. package/rules/enforce-dto-naming-conventions.js +212 -0
  73. package/rules/enforce-dto-naming.js +176 -0
  74. package/rules/enforce-dto-usage-simple.js +114 -0
  75. package/rules/enforce-dto-usage.js +407 -0
  76. package/rules/enforce-eager-translation-loading.js +178 -0
  77. package/rules/enforce-entity-creation-pattern.js +137 -0
  78. package/rules/enforce-entity-dto-convert-method.js +157 -0
  79. package/rules/enforce-entity-dto-create-no-id.js +117 -0
  80. package/rules/enforce-entity-dto-extends-base.js +141 -0
  81. package/rules/enforce-entity-dto-from-request-dto-structure.js +113 -0
  82. package/rules/enforce-entity-dto-fromentity-complex.js +69 -0
  83. package/rules/enforce-entity-dto-fromentity-simple.js +69 -0
  84. package/rules/enforce-entity-dto-fromrequestdto-structure.js +262 -0
  85. package/rules/enforce-entity-dto-methods-restriction.js +159 -0
  86. package/rules/enforce-entity-dto-no-request-dto.js +102 -0
  87. package/rules/enforce-entity-dto-optional-auto-fields.js +101 -0
  88. package/rules/enforce-entity-dto-required-methods.js +248 -0
  89. package/rules/enforce-entity-factory-pattern.js +180 -0
  90. package/rules/enforce-entity-instantiation-in-toentity.js +125 -0
  91. package/rules/enforce-enum-for-playable-entities.js +95 -0
  92. package/rules/enforce-error-handling.js +257 -0
  93. package/rules/enforce-explicit-dto-types.js +118 -0
  94. package/rules/enforce-from-request-dto-usage.js +62 -0
  95. package/rules/enforce-generic-entity-dto.js +71 -0
  96. package/rules/enforce-inject-decorator.js +133 -0
  97. package/rules/enforce-lazy-type-loading.js +170 -0
  98. package/rules/enforce-module-existence.js +157 -0
  99. package/rules/enforce-nonentity-dto-create.js +107 -0
  100. package/rules/enforce-playable-entity-naming.js +108 -0
  101. package/rules/enforce-repository-token-handling.js +92 -0
  102. package/rules/enforce-request-dto-no-entity-dto.js +201 -0
  103. package/rules/enforce-request-dto-required-fields.js +217 -0
  104. package/rules/enforce-result-pattern.js +45 -0
  105. package/rules/enforce-service-relation-loading.js +116 -0
  106. package/rules/enforce-test-coverage.js +96 -0
  107. package/rules/enforce-toentity-conditional-assignment.js +132 -0
  108. package/rules/enforce-translations-required.js +203 -0
  109. package/rules/enforce-typeorm-naming-conventions.js +366 -0
  110. package/rules/enforce-vite-health-metrics.js +240 -0
  111. package/rules/entity-required-properties.js +321 -0
  112. package/rules/entity-to-dto-test.js +73 -0
  113. package/rules/enum-database-validation.js +149 -0
  114. package/rules/errors.js +190 -0
  115. package/rules/es6.js +204 -0
  116. package/rules/eslint-plugin-no-comments.js +44 -0
  117. package/rules/filename-class-name-match.js +62 -0
  118. package/rules/forbid-fromentity-outside-entity-folder.js +237 -0
  119. package/rules/function-params-newline.js +111 -0
  120. package/rules/imports.js +264 -0
  121. package/rules/jest.js +13 -0
  122. package/rules/jsx.js +16 -0
  123. package/rules/max-classes-per-file.js +49 -0
  124. package/rules/multiline-formatting.js +146 -0
  125. package/rules/no-blank-lines-between-decorators-and-properties.js +95 -0
  126. package/rules/no-comments.js +62 -0
  127. package/rules/no-dto-constructors.js +126 -0
  128. package/rules/no-dto-default-values.js +220 -0
  129. package/rules/no-dto-duplicates.js +127 -0
  130. package/rules/no-dto-in-entity.js +99 -0
  131. package/rules/no-dynamic-import-in-types.js +71 -0
  132. package/rules/no-dynamic-imports-in-controllers.js +95 -0
  133. package/rules/no-entity-imports-in-controllers.js +101 -0
  134. package/rules/no-entity-in-swagger-docs.js +139 -0
  135. package/rules/no-entity-type-casting.js +104 -0
  136. package/rules/no-fetch.js +77 -0
  137. package/rules/no-import-meta-env.js +151 -0
  138. package/rules/no-inline-styles.js +5 -0
  139. package/rules/no-magic-values.js +85 -0
  140. package/rules/no-partial-type.js +168 -0
  141. package/rules/no-relative-imports.js +31 -0
  142. package/rules/no-tsyringe.js +181 -0
  143. package/rules/no-type-assertion.js +175 -0
  144. package/rules/no-undefined-entity-properties.js +121 -0
  145. package/rules/node.js +44 -0
  146. package/rules/perfectionist.js +50 -0
  147. package/rules/performance-minimal.js +155 -0
  148. package/rules/performance.js +44 -0
  149. package/rules/pino-logger-format.js +200 -0
  150. package/rules/prefer-dto-classes.js +112 -0
  151. package/rules/prefer-dto-create-method.js +225 -0
  152. package/rules/promises.js +17 -0
  153. package/rules/react-hooks.js +15 -0
  154. package/rules/react.js +28 -0
  155. package/rules/regexp.js +70 -0
  156. package/rules/require-dto-response.js +81 -0
  157. package/rules/require-valid-relations.js +388 -0
  158. package/rules/result-pattern.js +162 -0
  159. package/rules/security.js +37 -0
  160. package/rules/service-architecture.js +148 -0
  161. package/rules/sonarjs.js +26 -0
  162. package/rules/strict.js +7 -0
  163. package/rules/style.js +611 -0
  164. package/rules/stylistic.js +93 -0
  165. package/rules/typeorm-column-type-validation.js +224 -0
  166. package/rules/typescript-advanced.js +113 -0
  167. package/rules/typescript-core.js +111 -0
  168. package/rules/typescript.js +146 -0
  169. package/rules/unicorn.js +168 -0
  170. package/rules/variables.js +51 -0
  171. package/rules/websocket-architecture.js +115 -0
@@ -0,0 +1,388 @@
1
+ import fs from "fs";
2
+ import { globSync } from "glob";
3
+ import path from "path";
4
+
5
+ /** @type {import('eslint').Rule.RuleModule} */
6
+ const requireValidRelations = {
7
+ meta: {
8
+ type: "problem",
9
+ docs: {
10
+ description: "Ensure relations in find*() calls match entity relations",
11
+ },
12
+ schema: [],
13
+ messages: {
14
+ invalidRelation:
15
+ "Relation '{{relation}}' does not exist in entity '{{entityName}}'. Available relations: {{availableRelations}}",
16
+ noEntityFile:
17
+ "Could not find entity file for '{{entityName}}'. Expected at: {{expectedPath}}",
18
+ },
19
+ },
20
+
21
+ create(context) {
22
+ const filename = context.getFilename();
23
+ const sourceCode = context.getSourceCode();
24
+
25
+ function getEntityRelations(entityClassName) {
26
+ // Nutze die funktionierende getAllRelationsWithTargets() Funktion
27
+ // und gib nur die Property-Namen zurück
28
+ const relationsMap = getAllRelationsWithTargets(entityClassName);
29
+
30
+ if (!relationsMap || Object.keys(relationsMap).length === 0) {
31
+ return null;
32
+ }
33
+
34
+ return Object.keys(relationsMap);
35
+ }
36
+
37
+ function getAllRelationsWithTargets(entityClassName) {
38
+ try {
39
+ const workspaceRoot =
40
+ filename.split("/backend/src/")[0] ||
41
+ filename.split("\\backend\\src\\")[0];
42
+ if (!workspaceRoot) return {};
43
+
44
+ const entityPath = path.join(
45
+ workspaceRoot,
46
+ "backend",
47
+ "src",
48
+ "entity",
49
+ `**/${entityClassName}.ts`,
50
+ );
51
+
52
+ const entityFiles = globSync(entityPath);
53
+ if (entityFiles.length === 0) return {};
54
+
55
+ // Wenn mehrere Dateien gefunden werden, durchsuche alle und merge die Relations
56
+ const relationsMap = {};
57
+
58
+ for (const entityFile of entityFiles) {
59
+ try {
60
+ const entityContent = fs.readFileSync(entityFile, "utf8");
61
+
62
+ if (entityClassName === "PlayableRaceEntity") {
63
+ console.log(
64
+ );
65
+ }
66
+
67
+ const lines = entityContent.split("\n");
68
+ let i = 0;
69
+
70
+ if (entityClassName === "PlayableRaceEntity") {
71
+ console.log(
72
+ );
73
+ }
74
+
75
+ while (i < lines.length) {
76
+ const line = lines[i].trim();
77
+
78
+ // Suche nach Relation-Decorator mit Target-Entity
79
+ const relationMatch = line.match(
80
+ /@(?:OneToMany|ManyToOne|OneToOne|ManyToMany)\s*\(\s*\(\)\s*=>\s*(\w+)/,
81
+ );
82
+
83
+ if (
84
+ entityClassName === "PlayableRaceEntity" &&
85
+ line.includes("@OneToMany")
86
+ ) {
87
+ console.log(
88
+ );
89
+ console.log(
90
+ relationMatch,
91
+ );
92
+ }
93
+
94
+ if (relationMatch) {
95
+ const targetEntity = relationMatch[1];
96
+
97
+ if (entityClassName === "PlayableRaceEntity") {
98
+ console.log(
99
+ );
100
+ console.log(
101
+ );
102
+ }
103
+
104
+ let j = i;
105
+ let parenCount = 0;
106
+ let inDecoratorParams = true;
107
+
108
+ // Zähle Klammern in der ersten Zeile
109
+ if (!lines[j]) {
110
+ i++;
111
+ continue;
112
+ }
113
+ parenCount += (lines[j].match(/\(/g) || []).length;
114
+ parenCount -= (lines[j].match(/\)/g) || []).length;
115
+
116
+ if (entityClassName === "PlayableRaceEntity") {
117
+ console.log(
118
+ );
119
+ }
120
+
121
+ if (parenCount === 0) {
122
+ inDecoratorParams = false;
123
+ }
124
+ j++;
125
+
126
+ // Überspringe Decorator-Parameter
127
+ while (j < lines.length && inDecoratorParams) {
128
+ const currentLine = lines[j];
129
+ parenCount += (currentLine.match(/\(/g) || []).length;
130
+ parenCount -= (currentLine.match(/\)/g) || []).length;
131
+
132
+ if (entityClassName === "PlayableRaceEntity") {
133
+ console.log(
134
+ );
135
+ }
136
+
137
+ if (parenCount === 0) {
138
+ inDecoratorParams = false;
139
+ }
140
+ j++;
141
+ }
142
+
143
+ if (entityClassName === "PlayableRaceEntity") {
144
+ console.log(
145
+ );
146
+ }
147
+
148
+ // Jetzt suche nach dem Property-Namen
149
+ let foundProperty = false;
150
+ while (j < lines.length && !foundProperty) {
151
+ const nextLine = lines[j].trim();
152
+
153
+ if (entityClassName === "PlayableRaceEntity") {
154
+ console.log(
155
+ );
156
+ }
157
+
158
+ // Überspringe leere Zeilen und @JoinColumn
159
+ if (!nextLine || nextLine.includes("@JoinColumn")) {
160
+ j++;
161
+ continue;
162
+ }
163
+
164
+ // Wenn es ein anderer Decorator ist (nicht @JoinColumn), stoppen
165
+ if (nextLine.startsWith("@")) {
166
+ if (entityClassName === "PlayableRaceEntity") {
167
+ console.log(
168
+ );
169
+ }
170
+ break;
171
+ }
172
+
173
+ // Suche nach Property-Namen
174
+ const propertyMatch = nextLine.match(/^(\w+)\s*:/);
175
+ if (propertyMatch) {
176
+ const propertyName = propertyMatch[1];
177
+ relationsMap[propertyName] = targetEntity;
178
+
179
+ if (entityClassName === "PlayableRaceEntity") {
180
+ console.log(
181
+ );
182
+ }
183
+
184
+ foundProperty = true;
185
+ }
186
+
187
+ j++;
188
+ }
189
+
190
+ i = j;
191
+ } else {
192
+ i++;
193
+ }
194
+ }
195
+
196
+ // relationsMap wird in der Schleife gefüllt
197
+ } catch (error) {
198
+ if (entityClassName === "PlayableRaceEntity") {
199
+ console.log(
200
+ error,
201
+ );
202
+ }
203
+ // Fahre mit der nächsten Datei fort
204
+ }
205
+ }
206
+
207
+ if (entityClassName === "PlayableRaceEntity") {
208
+ console.log(
209
+ relationsMap,
210
+ );
211
+ }
212
+
213
+ return relationsMap;
214
+ } catch (error) {
215
+ if (entityClassName === "PlayableRaceEntity") {
216
+ console.log(
217
+ error,
218
+ );
219
+ }
220
+ return {};
221
+ }
222
+ }
223
+
224
+ function getRelationTargetEntity(entityClassName, relationName) {
225
+ const relationsMap = getAllRelationsWithTargets(entityClassName);
226
+ return relationsMap[relationName] || null;
227
+ }
228
+
229
+ function validateNestedRelation(baseEntityClassName, relationPath) {
230
+ const parts = relationPath.split(".");
231
+ let currentEntity = baseEntityClassName;
232
+
233
+ for (let i = 0; i < parts.length; i++) {
234
+ const relationName = parts[i];
235
+ const relations = getEntityRelations(currentEntity);
236
+
237
+ if (!relations || !relations.includes(relationName)) {
238
+ return {
239
+ valid: false,
240
+ error: `Relation '${parts.slice(0, i + 1).join(".")}' does not exist in entity '${currentEntity}'. Available relations: ${relations ? relations.join(", ") : "none"}`,
241
+ };
242
+ }
243
+
244
+ if (i < parts.length - 1) {
245
+ const targetEntity = getRelationTargetEntity(
246
+ currentEntity,
247
+ relationName,
248
+ );
249
+ if (!targetEntity) {
250
+ return {
251
+ valid: false,
252
+ error: `Could not find target entity for relation '${relationName}' in '${currentEntity}'`,
253
+ };
254
+ }
255
+ currentEntity = targetEntity;
256
+ }
257
+ }
258
+
259
+ return { valid: true };
260
+ }
261
+
262
+ return {
263
+ CallExpression(node) {
264
+ const callee = node.callee;
265
+
266
+ if (
267
+ callee.type !== "MemberExpression" ||
268
+ callee.property.type !== "Identifier" ||
269
+ !/^find/.test(callee.property.name) ||
270
+ node.arguments.length !== 1 ||
271
+ node.arguments[0].type !== "ObjectExpression"
272
+ ) {
273
+ return;
274
+ }
275
+
276
+ const relationsProp = node.arguments[0].properties.find(
277
+ (p) =>
278
+ p.type === "Property" &&
279
+ ((p.key.type === "Identifier" && p.key.name === "relations") ||
280
+ (p.key.type === "Literal" && p.key.value === "relations")),
281
+ );
282
+
283
+ if (!relationsProp || relationsProp.value.type !== "ArrayExpression") {
284
+ return;
285
+ }
286
+
287
+ let entityClassName = null;
288
+
289
+ // Pattern 1: Inline getRepository().find()
290
+ // this.dataSource.getRepository(Entity).find({...})
291
+ const calleeObject = callee.object;
292
+
293
+ if (calleeObject.type === "CallExpression") {
294
+ const getRepositoryCall = calleeObject.callee;
295
+ if (
296
+ getRepositoryCall.type === "MemberExpression" &&
297
+ getRepositoryCall.property.type === "Identifier" &&
298
+ getRepositoryCall.property.name === "getRepository"
299
+ ) {
300
+ const repositoryArg = calleeObject.arguments?.[0];
301
+ if (repositoryArg && repositoryArg.type === "Identifier") {
302
+ entityClassName = repositoryArg.name;
303
+ }
304
+ }
305
+ }
306
+
307
+ // Pattern 2: Variable repository.find()
308
+ // const repository = getRepository(Entity);
309
+ // repository.find({...})
310
+ if (!entityClassName && calleeObject.type === "Identifier") {
311
+ const repositoryVarName = calleeObject.name;
312
+
313
+ const scope = sourceCode.getScope(node);
314
+ const variable = scope.set.get(repositoryVarName);
315
+
316
+ if (variable && variable.defs.length > 0) {
317
+ const def = variable.defs[0];
318
+ if (def.node.type === "VariableDeclarator" && def.node.init) {
319
+ const init = def.node.init;
320
+ if (
321
+ init.type === "CallExpression" &&
322
+ init.callee.type === "MemberExpression" &&
323
+ init.callee.property.type === "Identifier" &&
324
+ init.callee.property.name === "getRepository" &&
325
+ init.arguments.length > 0 &&
326
+ init.arguments[0].type === "Identifier"
327
+ ) {
328
+ entityClassName = init.arguments[0].name;
329
+ }
330
+ }
331
+ }
332
+ }
333
+
334
+ if (!entityClassName) {
335
+ return;
336
+ }
337
+
338
+ for (const el of relationsProp.value.elements) {
339
+ if (el.type !== "Literal" || typeof el.value !== "string") continue;
340
+
341
+ const relationPath = el.value;
342
+
343
+ // Prüfe ob es eine nested relation ist (enthält ".")
344
+ if (relationPath.includes(".")) {
345
+ const result = validateNestedRelation(
346
+ entityClassName,
347
+ relationPath,
348
+ );
349
+ if (!result.valid) {
350
+ context.report({
351
+ node: el,
352
+ messageId: "invalidRelation",
353
+ data: {
354
+ relation: relationPath,
355
+ entityName: entityClassName,
356
+ availableRelations: result.error || "unknown",
357
+ },
358
+ });
359
+ }
360
+ } else {
361
+ // Einfache Relation (keine Verschachtelung)
362
+ const entityRelations = getEntityRelations(entityClassName);
363
+
364
+ if (!entityRelations) return;
365
+
366
+ if (!entityRelations.includes(relationPath)) {
367
+ context.report({
368
+ node: el,
369
+ messageId: "invalidRelation",
370
+ data: {
371
+ relation: relationPath,
372
+ entityName: entityClassName,
373
+ availableRelations:
374
+ entityRelations.length > 0
375
+ ? entityRelations.join(", ")
376
+ : "none",
377
+ },
378
+ });
379
+ }
380
+ }
381
+ }
382
+ },
383
+ };
384
+ },
385
+ };
386
+
387
+ // Export für ESLint v9 Flat Config
388
+ export default requireValidRelations;
@@ -0,0 +1,162 @@
1
+ /**
2
+ * ESLint-Regel: Alle nicht-statischen Klassenmethoden müssen Result<T, Error> zurückgeben
3
+ *
4
+ * Diese Regel stellt sicher, dass alle nicht-statischen Methoden einer Klasse
5
+ * ein Result<T, InfrastructureError | DomainError> Pattern verwenden.
6
+ */
7
+
8
+ export default {
9
+ rules: {
10
+ "require-result-return-type": {
11
+ meta: {
12
+ type: "problem",
13
+ docs: {
14
+ description: "Require non-static class methods to return Result<T, Error> type",
15
+ category: "Best Practices",
16
+ recommended: true,
17
+ },
18
+ schema: [],
19
+ messages: {
20
+ missingResultType: "Nicht-statische Klassenmethode '{{methodName}}' muss Promise<Result<T, InfrastructureError | DomainError>> zurückgeben",
21
+ },
22
+ },
23
+ create(context) {
24
+ return {
25
+ MethodDefinition(node) {
26
+ // Ignoriere statische Methoden
27
+ if (node.static) {
28
+ return;
29
+ }
30
+
31
+ // Ignoriere Konstruktoren
32
+ if (node.kind === "constructor") {
33
+ return;
34
+ }
35
+
36
+ // Ignoriere Getter/Setter
37
+ if (node.kind === "get" || node.kind === "set") {
38
+ return;
39
+ }
40
+
41
+ // Prüfe nur Klassen im infrastructure-Verzeichnis
42
+ const filename = context.getFilename();
43
+ if (!filename.includes("/infrastructure/")) {
44
+ return;
45
+ }
46
+
47
+ // Prüfe nur TypeScript-Dateien
48
+ if (!filename.endsWith(".ts") && !filename.endsWith(".tsx")) {
49
+ return;
50
+ }
51
+
52
+ const methodNode = node.value;
53
+ let returnType = null;
54
+
55
+ // Prüfe explizite Rückgabetyp-Annotation
56
+ if (methodNode.returnType && methodNode.returnType.typeAnnotation) {
57
+ returnType = methodNode.returnType.typeAnnotation;
58
+ }
59
+
60
+ // Wenn keine explizite Typ-Annotation vorhanden ist, überspringe
61
+ if (!returnType) {
62
+ return;
63
+ }
64
+
65
+ // Prüfe ob es sich um Promise<Result<T, Error>> handelt
66
+ const isPromiseResultType = checkIsPromiseResultType(returnType);
67
+
68
+ // Für infrastructure-Verzeichnis: Akzeptiere alle Promise-Typen als gültig
69
+ // (da Result-Pattern über Type-Aliases schwer zu validieren ist)
70
+ const isPromiseType = returnType.type === "TSTypeReference" &&
71
+ returnType.typeName &&
72
+ returnType.typeName.name === "Promise";
73
+
74
+ if (!isPromiseResultType && !isPromiseType) {
75
+ const methodName = node.key.name || "unknown";
76
+ context.report({
77
+ node: methodNode,
78
+ messageId: "missingResultType",
79
+ data: {
80
+ methodName,
81
+ },
82
+ });
83
+ }
84
+ },
85
+ };
86
+ },
87
+ },
88
+ },
89
+ };
90
+
91
+ /**
92
+ * Prüft ob der Typ ein Promise<Result<T, Error>> ist
93
+ */
94
+ function checkIsPromiseResultType(typeNode) {
95
+ // Muss Promise<Result<...>> sein
96
+ if (
97
+ typeNode.type === "TSTypeReference" &&
98
+ typeNode.typeName &&
99
+ typeNode.typeName.name === "Promise" &&
100
+ typeNode.typeParameters &&
101
+ typeNode.typeParameters.params.length > 0
102
+ ) {
103
+ const innerType = typeNode.typeParameters.params[0];
104
+ return checkIsResultType(innerType) || checkIsResultTypeAlias(innerType);
105
+ }
106
+
107
+ return false;
108
+ }
109
+
110
+ /**
111
+ * Prüft ob der Typ ein Result<T, Error> ist
112
+ */
113
+ function checkIsResultType(typeNode) {
114
+ // Result<T, Error> Pattern
115
+ if (
116
+ typeNode.type === "TSTypeReference" &&
117
+ typeNode.typeName &&
118
+ typeNode.typeName.name === "Result" &&
119
+ typeNode.typeParameters &&
120
+ typeNode.typeParameters.params.length === 2
121
+ ) {
122
+ const errorType = typeNode.typeParameters.params[1];
123
+
124
+ // Prüfe ob der Error-Typ InfrastructureError | DomainError ist
125
+ if (errorType.type === "TSUnionType") {
126
+ const errorTypes = errorType.types.map(t =>
127
+ t.type === "TSTypeReference" ? t.typeName.name : null
128
+ );
129
+
130
+ return errorTypes.includes("InfrastructureError") &&
131
+ errorTypes.includes("DomainError");
132
+ }
133
+
134
+ // Oder nur einer der beiden Error-Typen
135
+ if (errorType.type === "TSTypeReference") {
136
+ const errorTypeName = errorType.typeName.name;
137
+ return errorTypeName === "InfrastructureError" ||
138
+ errorTypeName === "DomainError";
139
+ }
140
+ }
141
+
142
+ return false;
143
+ }
144
+
145
+ /**
146
+ * Prüft ob der Typ ein Type-Alias ist, der auf Result endet (z.B. ItemResults, CharacterResult)
147
+ */
148
+ function checkIsResultTypeAlias(typeNode) {
149
+ if (
150
+ typeNode.type === "TSTypeReference" &&
151
+ typeNode.typeName &&
152
+ typeNode.typeName.name
153
+ ) {
154
+ const typeName = typeNode.typeName.name;
155
+ // Akzeptiere alle Typen, die auf "Result" enden (weniger strikt)
156
+ return typeName.endsWith("Result") ||
157
+ typeName.endsWith("Results") ||
158
+ typeName.includes("Result");
159
+ }
160
+
161
+ return false;
162
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Security Rules
3
+ *
4
+ * Sicherheitsregeln für Backend-Projekte
5
+ */
6
+
7
+ export default {
8
+ rules: {
9
+ // Security-Regeln (erweitert)
10
+ "security/detect-unsafe-regex": "error",
11
+ "security/detect-buffer-noassert": "error",
12
+ "security/detect-child-process": "error",
13
+ "security/detect-disable-mustache-escape": "error",
14
+ "security/detect-eval-with-expression": "error",
15
+ "security/detect-no-csrf-before-method-override": "error",
16
+ "security/detect-non-literal-fs-filename": "error",
17
+ "security/detect-non-literal-regexp": "error",
18
+ "security/detect-non-literal-require": "error",
19
+ "security/detect-object-injection": "error",
20
+ "security/detect-possible-timing-attacks": "error",
21
+ "security/detect-pseudoRandomBytes": "error",
22
+ "security/detect-bidi-characters": "error",
23
+ "security/detect-new-buffer": "error",
24
+
25
+ // No Secrets Plugin
26
+ "no-secrets/no-secrets": ["error", {
27
+ "tolerance": 4.2,
28
+ "additionalRegexes": {
29
+ "Basic Auth": "Authorization:\\s*Basic\\s+[A-Za-z0-9+/=]+",
30
+ "API Key": "(api[_-]?key|apikey)\\s*[:=]\\s*['\"][a-zA-Z0-9_-]{20,}['\"]",
31
+ "Database URL": "(database[_-]?url|db[_-]?url)\\s*[:=]\\s*['\"][^'\"]+['\"]",
32
+ "JWT": "eyJ[A-Za-z0-9_-]*\\.[A-Za-z0-9_-]*\\.[A-Za-z0-9_-]*",
33
+ "Private Key": "-----BEGIN\\s+(RSA\\s+)?PRIVATE\\s+KEY-----"
34
+ }
35
+ }],
36
+ },
37
+ };