@opensip-cli/checks-typescript 0.1.10 → 0.1.12

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 (252) hide show
  1. package/README.md +4 -2
  2. package/dist/__tests__/all-checks-execute.test.d.ts.map +1 -1
  3. package/dist/__tests__/all-checks-execute.test.js +0 -1
  4. package/dist/__tests__/all-checks-execute.test.js.map +1 -1
  5. package/dist/__tests__/behavior-fixtures-2.test.d.ts.map +1 -1
  6. package/dist/__tests__/behavior-fixtures-2.test.js +0 -1
  7. package/dist/__tests__/behavior-fixtures-2.test.js.map +1 -1
  8. package/dist/__tests__/behavior-fixtures-3.test.d.ts.map +1 -1
  9. package/dist/__tests__/behavior-fixtures-3.test.js +0 -1
  10. package/dist/__tests__/behavior-fixtures-3.test.js.map +1 -1
  11. package/dist/__tests__/behavior-fixtures-4.test.d.ts.map +1 -1
  12. package/dist/__tests__/behavior-fixtures-4.test.js +0 -1
  13. package/dist/__tests__/behavior-fixtures-4.test.js.map +1 -1
  14. package/dist/__tests__/behavior-fixtures-5.test.d.ts.map +1 -1
  15. package/dist/__tests__/behavior-fixtures-5.test.js +0 -1
  16. package/dist/__tests__/behavior-fixtures-5.test.js.map +1 -1
  17. package/dist/__tests__/behavior-fixtures-6.test.js +10 -0
  18. package/dist/__tests__/behavior-fixtures-6.test.js.map +1 -1
  19. package/dist/__tests__/behavior-fixtures.test.d.ts.map +1 -1
  20. package/dist/__tests__/behavior-fixtures.test.js +2 -4
  21. package/dist/__tests__/behavior-fixtures.test.js.map +1 -1
  22. package/dist/__tests__/branch-fixtures-2.test.d.ts.map +1 -1
  23. package/dist/__tests__/branch-fixtures-2.test.js +0 -1
  24. package/dist/__tests__/branch-fixtures-2.test.js.map +1 -1
  25. package/dist/__tests__/branch-fixtures-3.test.d.ts.map +1 -1
  26. package/dist/__tests__/branch-fixtures-3.test.js +0 -1
  27. package/dist/__tests__/branch-fixtures-3.test.js.map +1 -1
  28. package/dist/__tests__/branch-fixtures.test.d.ts.map +1 -1
  29. package/dist/__tests__/branch-fixtures.test.js +0 -1
  30. package/dist/__tests__/branch-fixtures.test.js.map +1 -1
  31. package/dist/checks/architecture/__tests__/live-view-through-cli-live.test.d.ts +2 -0
  32. package/dist/checks/architecture/__tests__/live-view-through-cli-live.test.d.ts.map +1 -0
  33. package/dist/checks/architecture/__tests__/live-view-through-cli-live.test.js +13 -0
  34. package/dist/checks/architecture/__tests__/live-view-through-cli-live.test.js.map +1 -0
  35. package/dist/checks/architecture/contracts-schema-consistency.d.ts.map +1 -1
  36. package/dist/checks/architecture/contracts-schema-consistency.js +0 -3
  37. package/dist/checks/architecture/contracts-schema-consistency.js.map +1 -1
  38. package/dist/checks/architecture/drizzle-orm-migration-guardrails.d.ts.map +1 -1
  39. package/dist/checks/architecture/drizzle-orm-migration-guardrails.js +1 -0
  40. package/dist/checks/architecture/drizzle-orm-migration-guardrails.js.map +1 -1
  41. package/dist/checks/architecture/index.d.ts +1 -0
  42. package/dist/checks/architecture/index.d.ts.map +1 -1
  43. package/dist/checks/architecture/index.js +1 -0
  44. package/dist/checks/architecture/index.js.map +1 -1
  45. package/dist/checks/architecture/live-view-through-cli-live.d.ts +8 -0
  46. package/dist/checks/architecture/live-view-through-cli-live.d.ts.map +1 -0
  47. package/dist/checks/architecture/live-view-through-cli-live.js +43 -0
  48. package/dist/checks/architecture/live-view-through-cli-live.js.map +1 -0
  49. package/dist/checks/architecture/missing-type-exports.d.ts.map +1 -1
  50. package/dist/checks/architecture/missing-type-exports.js +1 -1
  51. package/dist/checks/architecture/missing-type-exports.js.map +1 -1
  52. package/dist/checks/architecture/module-coupling-fan-out.d.ts.map +1 -1
  53. package/dist/checks/architecture/module-coupling-fan-out.js +6 -2
  54. package/dist/checks/architecture/module-coupling-fan-out.js.map +1 -1
  55. package/dist/checks/architecture/no-bootstrap-tool-import.d.ts.map +1 -1
  56. package/dist/checks/architecture/no-bootstrap-tool-import.js +1 -0
  57. package/dist/checks/architecture/no-bootstrap-tool-import.js.map +1 -1
  58. package/dist/checks/architecture/no-run-done-result.d.ts.map +1 -1
  59. package/dist/checks/architecture/no-run-done-result.js +1 -0
  60. package/dist/checks/architecture/no-run-done-result.js.map +1 -1
  61. package/dist/checks/architecture/package-json-exports-field.d.ts.map +1 -1
  62. package/dist/checks/architecture/package-json-exports-field.js +1 -1
  63. package/dist/checks/architecture/package-json-exports-field.js.map +1 -1
  64. package/dist/checks/architecture/phantom-dependency-detection.d.ts.map +1 -1
  65. package/dist/checks/architecture/phantom-dependency-detection.js +0 -4
  66. package/dist/checks/architecture/phantom-dependency-detection.js.map +1 -1
  67. package/dist/checks/architecture/tsconfig-extends-validation.d.ts.map +1 -1
  68. package/dist/checks/architecture/tsconfig-extends-validation.js +0 -2
  69. package/dist/checks/architecture/tsconfig-extends-validation.js.map +1 -1
  70. package/dist/checks/quality/api/fastify-schema-coverage.d.ts.map +1 -1
  71. package/dist/checks/quality/api/fastify-schema-coverage.js +21 -10
  72. package/dist/checks/quality/api/fastify-schema-coverage.js.map +1 -1
  73. package/dist/checks/quality/code-structure/__tests__/duplicate-utility-lang-substrate.test.d.ts +5 -0
  74. package/dist/checks/quality/code-structure/__tests__/duplicate-utility-lang-substrate.test.d.ts.map +1 -0
  75. package/dist/checks/quality/code-structure/__tests__/duplicate-utility-lang-substrate.test.js +17 -0
  76. package/dist/checks/quality/code-structure/__tests__/duplicate-utility-lang-substrate.test.js.map +1 -0
  77. package/dist/checks/quality/code-structure/duplicate-utility-functions-config.d.ts +18 -0
  78. package/dist/checks/quality/code-structure/duplicate-utility-functions-config.d.ts.map +1 -0
  79. package/dist/checks/quality/code-structure/duplicate-utility-functions-config.js +36 -0
  80. package/dist/checks/quality/code-structure/duplicate-utility-functions-config.js.map +1 -0
  81. package/dist/checks/quality/code-structure/duplicate-utility-functions-helpers.d.ts +15 -0
  82. package/dist/checks/quality/code-structure/duplicate-utility-functions-helpers.d.ts.map +1 -0
  83. package/dist/checks/quality/code-structure/duplicate-utility-functions-helpers.js +288 -0
  84. package/dist/checks/quality/code-structure/duplicate-utility-functions-helpers.js.map +1 -0
  85. package/dist/checks/quality/code-structure/duplicate-utility-functions.d.ts +1 -26
  86. package/dist/checks/quality/code-structure/duplicate-utility-functions.d.ts.map +1 -1
  87. package/dist/checks/quality/code-structure/duplicate-utility-functions.js +3 -407
  88. package/dist/checks/quality/code-structure/duplicate-utility-functions.js.map +1 -1
  89. package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.js +39 -2
  90. package/dist/checks/quality/data-integrity/__tests__/null-safety-fp.test.js.map +1 -1
  91. package/dist/checks/quality/data-integrity/array-validation-detectors.d.ts +17 -0
  92. package/dist/checks/quality/data-integrity/array-validation-detectors.d.ts.map +1 -0
  93. package/dist/checks/quality/data-integrity/array-validation-detectors.js +184 -0
  94. package/dist/checks/quality/data-integrity/array-validation-detectors.js.map +1 -0
  95. package/dist/checks/quality/data-integrity/array-validation.d.ts +0 -2
  96. package/dist/checks/quality/data-integrity/array-validation.d.ts.map +1 -1
  97. package/dist/checks/quality/data-integrity/array-validation.js +2 -360
  98. package/dist/checks/quality/data-integrity/array-validation.js.map +1 -1
  99. package/dist/checks/quality/data-integrity/database-schema-validation.d.ts.map +1 -1
  100. package/dist/checks/quality/data-integrity/database-schema-validation.js +0 -1
  101. package/dist/checks/quality/data-integrity/database-schema-validation.js.map +1 -1
  102. package/dist/checks/quality/data-integrity/null-safety-analyze.d.ts +33 -0
  103. package/dist/checks/quality/data-integrity/null-safety-analyze.d.ts.map +1 -0
  104. package/dist/checks/quality/data-integrity/null-safety-analyze.js +164 -0
  105. package/dist/checks/quality/data-integrity/null-safety-analyze.js.map +1 -0
  106. package/dist/checks/quality/data-integrity/null-safety-config.d.ts +50 -0
  107. package/dist/checks/quality/data-integrity/null-safety-config.d.ts.map +1 -0
  108. package/dist/checks/quality/data-integrity/null-safety-config.js +69 -0
  109. package/dist/checks/quality/data-integrity/null-safety-config.js.map +1 -0
  110. package/dist/checks/quality/data-integrity/null-safety-heuristics.d.ts +76 -0
  111. package/dist/checks/quality/data-integrity/null-safety-heuristics.d.ts.map +1 -0
  112. package/dist/checks/quality/data-integrity/null-safety-heuristics.js +276 -0
  113. package/dist/checks/quality/data-integrity/null-safety-heuristics.js.map +1 -0
  114. package/dist/checks/quality/data-integrity/null-safety-prefixes.d.ts +13 -0
  115. package/dist/checks/quality/data-integrity/null-safety-prefixes.d.ts.map +1 -0
  116. package/dist/checks/quality/data-integrity/null-safety-prefixes.js +333 -0
  117. package/dist/checks/quality/data-integrity/null-safety-prefixes.js.map +1 -0
  118. package/dist/checks/quality/data-integrity/null-safety.d.ts +2 -82
  119. package/dist/checks/quality/data-integrity/null-safety.d.ts.map +1 -1
  120. package/dist/checks/quality/data-integrity/null-safety.js +3 -796
  121. package/dist/checks/quality/data-integrity/null-safety.js.map +1 -1
  122. package/dist/checks/quality/frontend/test-only-frontend-modules.d.ts.map +1 -1
  123. package/dist/checks/quality/frontend/test-only-frontend-modules.js +0 -2
  124. package/dist/checks/quality/frontend/test-only-frontend-modules.js.map +1 -1
  125. package/dist/checks/quality/linting/typescript-frontend.d.ts.map +1 -1
  126. package/dist/checks/quality/linting/typescript-frontend.js +1 -0
  127. package/dist/checks/quality/linting/typescript-frontend.js.map +1 -1
  128. package/dist/checks/quality/observability/logger-event-name-format.d.ts.map +1 -1
  129. package/dist/checks/quality/observability/logger-event-name-format.js +0 -1
  130. package/dist/checks/quality/observability/logger-event-name-format.js.map +1 -1
  131. package/dist/checks/quality/observability/no-hardcoded-correlation-id.d.ts.map +1 -1
  132. package/dist/checks/quality/observability/no-hardcoded-correlation-id.js +2 -3
  133. package/dist/checks/quality/observability/no-hardcoded-correlation-id.js.map +1 -1
  134. package/dist/checks/quality/patterns/__tests__/async-waterfall-sequential.test.d.ts +8 -0
  135. package/dist/checks/quality/patterns/__tests__/async-waterfall-sequential.test.d.ts.map +1 -0
  136. package/dist/checks/quality/patterns/__tests__/async-waterfall-sequential.test.js +87 -0
  137. package/dist/checks/quality/patterns/__tests__/async-waterfall-sequential.test.js.map +1 -0
  138. package/dist/checks/quality/patterns/__tests__/error-handling-probes.test.d.ts +2 -0
  139. package/dist/checks/quality/patterns/__tests__/error-handling-probes.test.d.ts.map +1 -0
  140. package/dist/checks/quality/patterns/__tests__/error-handling-probes.test.js +51 -0
  141. package/dist/checks/quality/patterns/__tests__/error-handling-probes.test.js.map +1 -0
  142. package/dist/checks/quality/patterns/__tests__/result-pattern-registration-guards.test.d.ts +2 -0
  143. package/dist/checks/quality/patterns/__tests__/result-pattern-registration-guards.test.d.ts.map +1 -0
  144. package/dist/checks/quality/patterns/__tests__/result-pattern-registration-guards.test.js +89 -0
  145. package/dist/checks/quality/patterns/__tests__/result-pattern-registration-guards.test.js.map +1 -0
  146. package/dist/checks/quality/patterns/__tests__/throws-documentation-analyze.test.d.ts +5 -0
  147. package/dist/checks/quality/patterns/__tests__/throws-documentation-analyze.test.d.ts.map +1 -0
  148. package/dist/checks/quality/patterns/__tests__/throws-documentation-analyze.test.js +78 -0
  149. package/dist/checks/quality/patterns/__tests__/throws-documentation-analyze.test.js.map +1 -0
  150. package/dist/checks/quality/patterns/__tests__/toctou-fp.test.js +44 -0
  151. package/dist/checks/quality/patterns/__tests__/toctou-fp.test.js.map +1 -1
  152. package/dist/checks/quality/patterns/async-waterfall-analysis.d.ts +17 -0
  153. package/dist/checks/quality/patterns/async-waterfall-analysis.d.ts.map +1 -0
  154. package/dist/checks/quality/patterns/async-waterfall-analysis.js +215 -0
  155. package/dist/checks/quality/patterns/async-waterfall-analysis.js.map +1 -0
  156. package/dist/checks/quality/patterns/async-waterfall-branch-keys.d.ts +6 -0
  157. package/dist/checks/quality/patterns/async-waterfall-branch-keys.d.ts.map +1 -0
  158. package/dist/checks/quality/patterns/async-waterfall-branch-keys.js +54 -0
  159. package/dist/checks/quality/patterns/async-waterfall-branch-keys.js.map +1 -0
  160. package/dist/checks/quality/patterns/async-waterfall-detection.d.ts.map +1 -1
  161. package/dist/checks/quality/patterns/async-waterfall-detection.js +3 -352
  162. package/dist/checks/quality/patterns/async-waterfall-detection.js.map +1 -1
  163. package/dist/checks/quality/patterns/containing-function-name.d.ts +3 -0
  164. package/dist/checks/quality/patterns/containing-function-name.d.ts.map +1 -0
  165. package/dist/checks/quality/patterns/containing-function-name.js +21 -0
  166. package/dist/checks/quality/patterns/containing-function-name.js.map +1 -0
  167. package/dist/checks/quality/patterns/error-handling-quality.d.ts +3 -0
  168. package/dist/checks/quality/patterns/error-handling-quality.d.ts.map +1 -1
  169. package/dist/checks/quality/patterns/error-handling-quality.js +150 -30
  170. package/dist/checks/quality/patterns/error-handling-quality.js.map +1 -1
  171. package/dist/checks/quality/patterns/result-pattern-consistency.d.ts +3 -0
  172. package/dist/checks/quality/patterns/result-pattern-consistency.d.ts.map +1 -1
  173. package/dist/checks/quality/patterns/result-pattern-consistency.js +136 -69
  174. package/dist/checks/quality/patterns/result-pattern-consistency.js.map +1 -1
  175. package/dist/checks/quality/patterns/throws-documentation-analyze.d.ts +14 -0
  176. package/dist/checks/quality/patterns/throws-documentation-analyze.d.ts.map +1 -0
  177. package/dist/checks/quality/patterns/throws-documentation-analyze.js +352 -0
  178. package/dist/checks/quality/patterns/throws-documentation-analyze.js.map +1 -0
  179. package/dist/checks/quality/patterns/throws-documentation-constants.d.ts +15 -0
  180. package/dist/checks/quality/patterns/throws-documentation-constants.d.ts.map +1 -0
  181. package/dist/checks/quality/patterns/throws-documentation-constants.js +94 -0
  182. package/dist/checks/quality/patterns/throws-documentation-constants.js.map +1 -0
  183. package/dist/checks/quality/patterns/throws-documentation.d.ts +1 -11
  184. package/dist/checks/quality/patterns/throws-documentation.d.ts.map +1 -1
  185. package/dist/checks/quality/patterns/throws-documentation.js +4 -472
  186. package/dist/checks/quality/patterns/throws-documentation.js.map +1 -1
  187. package/dist/checks/quality/patterns/toctou-race-condition-classify.d.ts +23 -0
  188. package/dist/checks/quality/patterns/toctou-race-condition-classify.d.ts.map +1 -0
  189. package/dist/checks/quality/patterns/toctou-race-condition-classify.js +125 -0
  190. package/dist/checks/quality/patterns/toctou-race-condition-classify.js.map +1 -0
  191. package/dist/checks/quality/patterns/toctou-race-condition-collection.d.ts +24 -0
  192. package/dist/checks/quality/patterns/toctou-race-condition-collection.d.ts.map +1 -0
  193. package/dist/checks/quality/patterns/toctou-race-condition-collection.js +248 -0
  194. package/dist/checks/quality/patterns/toctou-race-condition-collection.js.map +1 -0
  195. package/dist/checks/quality/patterns/toctou-race-condition-constants.d.ts +32 -0
  196. package/dist/checks/quality/patterns/toctou-race-condition-constants.d.ts.map +1 -0
  197. package/dist/checks/quality/patterns/toctou-race-condition-constants.js +116 -0
  198. package/dist/checks/quality/patterns/toctou-race-condition-constants.js.map +1 -0
  199. package/dist/checks/quality/patterns/toctou-race-condition.d.ts +1 -29
  200. package/dist/checks/quality/patterns/toctou-race-condition.d.ts.map +1 -1
  201. package/dist/checks/quality/patterns/toctou-race-condition.js +11 -536
  202. package/dist/checks/quality/patterns/toctou-race-condition.js.map +1 -1
  203. package/dist/checks/quality/unused-config-options.d.ts.map +1 -1
  204. package/dist/checks/quality/unused-config-options.js +1 -4
  205. package/dist/checks/quality/unused-config-options.js.map +1 -1
  206. package/dist/checks/resilience/__tests__/context-mutation.test.js +8 -0
  207. package/dist/checks/resilience/__tests__/context-mutation.test.js.map +1 -1
  208. package/dist/checks/resilience/__tests__/detached-promises-sync-detection.test.d.ts +2 -0
  209. package/dist/checks/resilience/__tests__/detached-promises-sync-detection.test.d.ts.map +1 -0
  210. package/dist/checks/resilience/__tests__/detached-promises-sync-detection.test.js +98 -0
  211. package/dist/checks/resilience/__tests__/detached-promises-sync-detection.test.js.map +1 -0
  212. package/dist/checks/resilience/callback-invocation-safe.d.ts.map +1 -1
  213. package/dist/checks/resilience/callback-invocation-safe.js +0 -1
  214. package/dist/checks/resilience/callback-invocation-safe.js.map +1 -1
  215. package/dist/checks/resilience/context-leakage.d.ts.map +1 -1
  216. package/dist/checks/resilience/context-leakage.js +1 -0
  217. package/dist/checks/resilience/context-leakage.js.map +1 -1
  218. package/dist/checks/resilience/context-mutation.d.ts.map +1 -1
  219. package/dist/checks/resilience/context-mutation.js +30 -4
  220. package/dist/checks/resilience/context-mutation.js.map +1 -1
  221. package/dist/checks/resilience/detached-promises-detection.d.ts +7 -0
  222. package/dist/checks/resilience/detached-promises-detection.d.ts.map +1 -0
  223. package/dist/checks/resilience/detached-promises-detection.js +228 -0
  224. package/dist/checks/resilience/detached-promises-detection.js.map +1 -0
  225. package/dist/checks/resilience/detached-promises-sync-constants.d.ts +36 -0
  226. package/dist/checks/resilience/detached-promises-sync-constants.d.ts.map +1 -0
  227. package/dist/checks/resilience/detached-promises-sync-constants.js +299 -0
  228. package/dist/checks/resilience/detached-promises-sync-constants.js.map +1 -0
  229. package/dist/checks/resilience/detached-promises-sync-detection.d.ts +14 -0
  230. package/dist/checks/resilience/detached-promises-sync-detection.d.ts.map +1 -0
  231. package/dist/checks/resilience/detached-promises-sync-detection.js +69 -0
  232. package/dist/checks/resilience/detached-promises-sync-detection.js.map +1 -0
  233. package/dist/checks/resilience/detached-promises.d.ts +1 -14
  234. package/dist/checks/resilience/detached-promises.d.ts.map +1 -1
  235. package/dist/checks/resilience/detached-promises.js +2 -598
  236. package/dist/checks/resilience/detached-promises.js.map +1 -1
  237. package/dist/checks/resilience/no-raw-fetch.d.ts.map +1 -1
  238. package/dist/checks/resilience/no-raw-fetch.js +1 -0
  239. package/dist/checks/resilience/no-raw-fetch.js.map +1 -1
  240. package/dist/checks/resilience/no-unbounded-concurrency.d.ts.map +1 -1
  241. package/dist/checks/resilience/no-unbounded-concurrency.js +1 -0
  242. package/dist/checks/resilience/no-unbounded-concurrency.js.map +1 -1
  243. package/dist/checks/security/sql-injection.d.ts.map +1 -1
  244. package/dist/checks/security/sql-injection.js +0 -1
  245. package/dist/checks/security/sql-injection.js.map +1 -1
  246. package/dist/display/architecture.d.ts.map +1 -1
  247. package/dist/display/architecture.js +1 -0
  248. package/dist/display/architecture.js.map +1 -1
  249. package/dist/display/types.d.ts.map +1 -1
  250. package/dist/display/types.js +0 -1
  251. package/dist/display/types.js.map +1 -1
  252. package/package.json +5 -5
@@ -0,0 +1,184 @@
1
+ import * as ts from 'typescript';
2
+ export const QUICK_FILTER_KEYWORDS = [
3
+ '[]',
4
+ 'Array',
5
+ 'array',
6
+ '.length',
7
+ '.push',
8
+ '.pop',
9
+ '.map(',
10
+ '.filter(',
11
+ ];
12
+ const RELAXED_VALIDATION_PATHS = [
13
+ /\/internal\//,
14
+ /\/utils\//,
15
+ /\/helpers\//,
16
+ /\/cli\//,
17
+ /\/scripts\//,
18
+ ];
19
+ const COMPLEX_TYPE_PATTERNS = ['Record<', 'Map<', '=> ', ': (', 'Promise<', 'Observable<'];
20
+ export function isTopLevelArrayType(typeNode) {
21
+ if (ts.isArrayTypeNode(typeNode)) {
22
+ return true;
23
+ }
24
+ if (ts.isTypeReferenceNode(typeNode)) {
25
+ const typeName = typeNode.typeName;
26
+ if (ts.isIdentifier(typeName)) {
27
+ const name = typeName.text;
28
+ if (name === 'Array' || name === 'ReadonlyArray') {
29
+ return true;
30
+ }
31
+ }
32
+ }
33
+ if (ts.isUnionTypeNode(typeNode)) {
34
+ return typeNode.types.length > 0 && typeNode.types.every((t) => isTopLevelArrayType(t));
35
+ }
36
+ if (ts.isParenthesizedTypeNode(typeNode)) {
37
+ return isTopLevelArrayType(typeNode.type);
38
+ }
39
+ return false;
40
+ }
41
+ export function isRelaxedValidationPath(filePath) {
42
+ return RELAXED_VALIDATION_PATHS.some((pattern) => pattern.test(filePath));
43
+ }
44
+ export function isComplexNestedType(typeText) {
45
+ return COMPLEX_TYPE_PATTERNS.some((pattern) => typeText.includes(pattern));
46
+ }
47
+ export function isLengthAccess(node, paramName, sourceFile) {
48
+ if (!ts.isPropertyAccessExpression(node))
49
+ return false;
50
+ const objName = node.expression.getText(sourceFile);
51
+ const propName = node.name.getText(sourceFile);
52
+ return objName === paramName && propName === 'length';
53
+ }
54
+ export function isArrayIsArrayCall(node, paramName, sourceFile) {
55
+ if (!ts.isCallExpression(node))
56
+ return false;
57
+ const callText = node.expression.getText(sourceFile);
58
+ if (callText !== 'Array.isArray')
59
+ return false;
60
+ const arg = node.arguments[0]?.getText(sourceFile);
61
+ return arg === paramName;
62
+ }
63
+ export function isZodValidationCall(node, sourceFile) {
64
+ if (!ts.isCallExpression(node))
65
+ return false;
66
+ if (!ts.isPropertyAccessExpression(node.expression))
67
+ return false;
68
+ const methodName = node.expression.name.getText(sourceFile);
69
+ return methodName === 'parse' || methodName === 'safeParse';
70
+ }
71
+ export function isValidationFunctionCall(node) {
72
+ if (!ts.isCallExpression(node))
73
+ return false;
74
+ if (!ts.isIdentifier(node.expression))
75
+ return false;
76
+ const funcName = node.expression.text.toLowerCase();
77
+ return funcName.includes('validate') || funcName.includes('check');
78
+ }
79
+ export function isIterationOverParam(node, paramName, sourceFile) {
80
+ if (ts.isForOfStatement(node)) {
81
+ const iterableText = node.expression.getText(sourceFile);
82
+ if (iterableText === paramName || iterableText.startsWith(`${paramName}.`)) {
83
+ return true;
84
+ }
85
+ }
86
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
87
+ const objText = node.expression.expression.getText(sourceFile);
88
+ const methodName = node.expression.name.getText(sourceFile);
89
+ const iterationMethods = [
90
+ 'forEach',
91
+ 'map',
92
+ 'filter',
93
+ 'some',
94
+ 'every',
95
+ 'find',
96
+ 'findIndex',
97
+ 'findLast',
98
+ 'findLastIndex',
99
+ 'reduce',
100
+ 'reduceRight',
101
+ 'flatMap',
102
+ 'flat',
103
+ 'slice',
104
+ 'includes',
105
+ 'indexOf',
106
+ 'lastIndexOf',
107
+ 'join',
108
+ 'concat',
109
+ 'entries',
110
+ 'values',
111
+ 'keys',
112
+ 'at',
113
+ 'toSorted',
114
+ 'toReversed',
115
+ 'toSpliced',
116
+ 'with',
117
+ ];
118
+ if (objText === paramName && iterationMethods.includes(methodName)) {
119
+ return true;
120
+ }
121
+ }
122
+ return false;
123
+ }
124
+ export function isOutSinkUsage(node, paramName, sourceFile) {
125
+ if (!ts.isCallExpression(node))
126
+ return false;
127
+ if (!ts.isPropertyAccessExpression(node.expression))
128
+ return false;
129
+ const objText = node.expression.expression.getText(sourceFile);
130
+ if (objText !== paramName)
131
+ return false;
132
+ const methodName = node.expression.name.getText(sourceFile);
133
+ return methodName === 'push' || methodName === 'unshift' || methodName === 'splice';
134
+ }
135
+ export function isIndexedAccess(node, paramName, sourceFile) {
136
+ if (!ts.isElementAccessExpression(node))
137
+ return false;
138
+ return node.expression.getText(sourceFile) === paramName;
139
+ }
140
+ export function isSpreadOfParam(node, paramName, sourceFile) {
141
+ if (ts.isSpreadElement(node) || ts.isSpreadAssignment(node)) {
142
+ return node.expression.getText(sourceFile) === paramName;
143
+ }
144
+ return false;
145
+ }
146
+ export function isForwardedToCall(node, paramName, _sourceFile) {
147
+ if (!ts.isCallExpression(node) && !ts.isNewExpression(node))
148
+ return false;
149
+ const args = node.arguments;
150
+ if (!args)
151
+ return false;
152
+ for (const arg of args) {
153
+ if (ts.isIdentifier(arg) && arg.text === paramName) {
154
+ return true;
155
+ }
156
+ if (ts.isAsExpression(arg) || ts.isSatisfiesExpression(arg)) {
157
+ const inner = arg.expression;
158
+ if (ts.isIdentifier(inner) && inner.text === paramName) {
159
+ return true;
160
+ }
161
+ }
162
+ }
163
+ return false;
164
+ }
165
+ export function isShorthandPropertyReference(node, paramName) {
166
+ if (!ts.isShorthandPropertyAssignment(node))
167
+ return false;
168
+ return node.name.text === paramName;
169
+ }
170
+ export function isOptionalHandling(node, paramName, sourceFile) {
171
+ const nodeText = node.getText(sourceFile);
172
+ if (nodeText.includes(`${paramName}?.`)) {
173
+ return true;
174
+ }
175
+ if (ts.isBinaryExpression(node) &&
176
+ node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
177
+ const leftText = node.left.getText(sourceFile);
178
+ if (leftText === paramName) {
179
+ return true;
180
+ }
181
+ }
182
+ return false;
183
+ }
184
+ //# sourceMappingURL=array-validation-detectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"array-validation-detectors.js","sourceRoot":"","sources":["../../../../src/checks/quality/data-integrity/array-validation-detectors.ts"],"names":[],"mappings":"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"}
@@ -3,14 +3,12 @@
3
3
  *
4
4
  * Detects array parameters without proper validation.
5
5
  * Ensures arrays are validated for length, type, and content before processing.
6
- *
7
6
  */
8
7
  /**
9
8
  * Check: quality/array-validation
10
9
  *
11
10
  * Detects array parameters without proper validation to prevent
12
11
  * runtime errors from unvalidated array inputs.
13
- *
14
12
  */
15
13
  export declare const arrayValidation: import("@opensip-cli/fitness").Check;
16
14
  //# sourceMappingURL=array-validation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"array-validation.d.ts","sourceRoot":"","sources":["../../../../src/checks/quality/data-integrity/array-validation.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AAqgBH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,sCA4B1B,CAAC"}
1
+ {"version":3,"file":"array-validation.d.ts","sourceRoot":"","sources":["../../../../src/checks/quality/data-integrity/array-validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA8JH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,sCA2B1B,CAAC"}
@@ -1,340 +1,19 @@
1
- // @fitness-ignore-file file-length-limit -- cohesive single-check module; splitting risks breaking the detector contract
2
1
  /**
3
2
  * @fileoverview Array Parameter Validation Check
4
3
  *
5
4
  * Detects array parameters without proper validation.
6
5
  * Ensures arrays are validated for length, type, and content before processing.
7
- *
8
6
  */
9
7
  import { defineCheck, isTestFile } from '@opensip-cli/fitness';
10
8
  import { getSharedSourceFile } from '@opensip-cli/lang-typescript';
11
9
  import * as ts from 'typescript';
12
- /**
13
- * Quick filter keywords for array validation patterns
14
- */
15
- const QUICK_FILTER_KEYWORDS = [
16
- '[]',
17
- 'Array',
18
- 'array',
19
- '.length',
20
- '.push',
21
- '.pop',
22
- '.map(',
23
- '.filter(',
24
- ];
25
- /**
26
- * Paths where array validation requirements are relaxed
27
- */
28
- const RELAXED_VALIDATION_PATHS = [
29
- /\/internal\//,
30
- /\/utils\//,
31
- /\/helpers\//,
32
- /\/cli\//,
33
- /\/scripts\//,
34
- ];
35
- /**
36
- * Type patterns that indicate complex/nested types where validation is harder to detect
37
- */
38
- const COMPLEX_TYPE_PATTERNS = [
39
- 'Record<',
40
- 'Map<',
41
- '=> ', // Function type
42
- ': (', // Function type with parens
43
- 'Promise<', // Async wrappers
44
- 'Observable<',
45
- ];
46
- /**
47
- * Check whether a parameter's type is a top-level array type (e.g. `string[]`, `Array<T>`)
48
- * as opposed to an object/interface/intersection type that merely _contains_ array-typed
49
- * properties (e.g. `{ items: string[] }`, `Foo & { tags: string[] }`).
50
- *
51
- * This uses the TypeScript AST node kind rather than text matching, eliminating false
52
- * positives for object parameters whose nested properties happen to be arrays.
53
- */
54
- function isTopLevelArrayType(typeNode) {
55
- // Direct array type: `string[]`, `Foo[]`
56
- if (ts.isArrayTypeNode(typeNode)) {
57
- return true;
58
- }
59
- // Generic Array reference: `Array<string>`, `ReadonlyArray<Foo>`
60
- if (ts.isTypeReferenceNode(typeNode)) {
61
- const typeName = typeNode.typeName;
62
- if (ts.isIdentifier(typeName)) {
63
- const name = typeName.text;
64
- if (name === 'Array' || name === 'ReadonlyArray') {
65
- return true;
66
- }
67
- }
68
- }
69
- // Union type: check if ALL branches are arrays (e.g. `string[] | number[]`)
70
- if (ts.isUnionTypeNode(typeNode)) {
71
- return typeNode.types.length > 0 && typeNode.types.every((t) => isTopLevelArrayType(t));
72
- }
73
- // Parenthesized type: unwrap `(string[])`
74
- if (ts.isParenthesizedTypeNode(typeNode)) {
75
- return isTopLevelArrayType(typeNode.type);
76
- }
77
- // Everything else (object literals, intersection types, type references to interfaces,
78
- // mapped types, conditional types, etc.) is NOT a top-level array even if the type
79
- // text contains `[]` in nested positions.
80
- return false;
81
- }
82
- /**
83
- * Check if a file path is in a relaxed validation context
84
- */
85
- function isRelaxedValidationPath(filePath) {
86
- return RELAXED_VALIDATION_PATHS.some((pattern) => pattern.test(filePath));
87
- }
88
- /**
89
- * Check if a type is a complex nested type where validation detection is unreliable
90
- */
91
- function isComplexNestedType(typeText) {
92
- return COMPLEX_TYPE_PATTERNS.some((pattern) => typeText.includes(pattern));
93
- }
94
- /**
95
- * Check if node is a .length access on the parameter
96
- * @param node - The TypeScript AST node to check
97
- * @param paramName - The name of the parameter to check for
98
- * @param sourceFile - The TypeScript source file for text extraction
99
- * @returns True if the node is a .length access on the specified parameter
100
- */
101
- function isLengthAccess(node, paramName, sourceFile) {
102
- if (!ts.isPropertyAccessExpression(node))
103
- return false;
104
- const objName = node.expression.getText(sourceFile);
105
- const propName = node.name.getText(sourceFile);
106
- return objName === paramName && propName === 'length';
107
- }
108
- /**
109
- * Check if node is an Array.isArray() call on the parameter
110
- * @param node - The TypeScript AST node to check
111
- * @param paramName - The name of the parameter to check for
112
- * @param sourceFile - The TypeScript source file for text extraction
113
- * @returns True if the node is an Array.isArray() call on the specified parameter
114
- */
115
- function isArrayIsArrayCall(node, paramName, sourceFile) {
116
- if (!ts.isCallExpression(node))
117
- return false;
118
- const callText = node.expression.getText(sourceFile);
119
- if (callText !== 'Array.isArray')
120
- return false;
121
- const arg = node.arguments[0]?.getText(sourceFile);
122
- return arg === paramName;
123
- }
124
- /**
125
- * Check if node is a Zod schema validation call
126
- * @param node - The TypeScript AST node to check
127
- * @param sourceFile - The TypeScript source file for text extraction
128
- * @returns True if the node is a Zod .parse() or .safeParse() call
129
- */
130
- function isZodValidationCall(node, sourceFile) {
131
- if (!ts.isCallExpression(node))
132
- return false;
133
- if (!ts.isPropertyAccessExpression(node.expression))
134
- return false;
135
- const methodName = node.expression.name.getText(sourceFile);
136
- return methodName === 'parse' || methodName === 'safeParse';
137
- }
138
- /**
139
- * Check if node is a validation function call
140
- * @param node - The TypeScript AST node to check
141
- * @returns True if the node is a call to a function with 'validate' or 'check' in its name
142
- */
143
- function isValidationFunctionCall(node) {
144
- if (!ts.isCallExpression(node))
145
- return false;
146
- if (!ts.isIdentifier(node.expression))
147
- return false;
148
- const funcName = node.expression.text.toLowerCase();
149
- return funcName.includes('validate') || funcName.includes('check');
150
- }
151
- /**
152
- * Check if node is an iteration over the parameter (for...of, forEach, map, filter,
153
- * slice, includes, join, indexOf, concat, flat, etc.)
154
- *
155
- * Iteration implies validation because it handles empty arrays gracefully and
156
- * cannot blow up the runtime — the worst case is no work done.
157
- */
158
- function isIterationOverParam(node, paramName, sourceFile) {
159
- // for...of statement
160
- if (ts.isForOfStatement(node)) {
161
- const iterableText = node.expression.getText(sourceFile);
162
- if (iterableText === paramName || iterableText.startsWith(`${paramName}.`)) {
163
- return true;
164
- }
165
- }
166
- // Array prototype methods that internally iterate (and therefore handle empty
167
- // arrays gracefully) or otherwise produce a bounded view. These are
168
- // validation-equivalent because they don't crash on empty/short input.
169
- if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
170
- const objText = node.expression.expression.getText(sourceFile);
171
- const methodName = node.expression.name.getText(sourceFile);
172
- const iterationMethods = [
173
- 'forEach',
174
- 'map',
175
- 'filter',
176
- 'some',
177
- 'every',
178
- 'find',
179
- 'findIndex',
180
- 'findLast',
181
- 'findLastIndex',
182
- 'reduce',
183
- 'reduceRight',
184
- 'flatMap',
185
- 'flat',
186
- 'slice',
187
- 'includes',
188
- 'indexOf',
189
- 'lastIndexOf',
190
- 'join',
191
- 'concat',
192
- 'entries',
193
- 'values',
194
- 'keys',
195
- 'at',
196
- 'toSorted',
197
- 'toReversed',
198
- 'toSpliced',
199
- 'with',
200
- ];
201
- if (objText === paramName && iterationMethods.includes(methodName)) {
202
- return true;
203
- }
204
- }
205
- return false;
206
- }
207
- /**
208
- * Check if node is a write-only sink usage of the parameter.
209
- *
210
- * Out-array sinks (`bucket.push(...)`, `bucket.unshift(...)`) flag the param
211
- * as a producer target — the function isn't *consuming* the array, it's
212
- * writing into it. Validation belongs at the consumer, not the producer.
213
- */
214
- function isOutSinkUsage(node, paramName, sourceFile) {
215
- if (!ts.isCallExpression(node))
216
- return false;
217
- if (!ts.isPropertyAccessExpression(node.expression))
218
- return false;
219
- const objText = node.expression.expression.getText(sourceFile);
220
- if (objText !== paramName)
221
- return false;
222
- const methodName = node.expression.name.getText(sourceFile);
223
- // Mutating sink methods — caller is producing into the array.
224
- return methodName === 'push' || methodName === 'unshift' || methodName === 'splice';
225
- }
226
- /**
227
- * Check if node is indexed access on the parameter (`param[i]`).
228
- *
229
- * Indexed access on a typed array yields `T | undefined` under
230
- * `noUncheckedIndexedAccess`; under any reasonable consumer it implies
231
- * defensive use and cannot crash on empty/short input the way an
232
- * unconditional `.length`-less iteration would. Practically: if the body
233
- * does `param[i]`, the author has already structured the access around
234
- * length (or expects undefined).
235
- */
236
- function isIndexedAccess(node, paramName, sourceFile) {
237
- if (!ts.isElementAccessExpression(node))
238
- return false;
239
- return node.expression.getText(sourceFile) === paramName;
240
- }
241
- /**
242
- * Check if node is a spread of the parameter (`...param`) inside a call
243
- * argument list, array literal, or object literal.
244
- *
245
- * Spread iterates the array — equivalent to `for...of`.
246
- */
247
- function isSpreadOfParam(node, paramName, sourceFile) {
248
- if (ts.isSpreadElement(node) || ts.isSpreadAssignment(node)) {
249
- return node.expression.getText(sourceFile) === paramName;
250
- }
251
- return false;
252
- }
253
- /**
254
- * Check if node passes the parameter through to another function call.
255
- *
256
- * A pure pass-through forwarder (`return otherFn(..., param, ...)`) defers
257
- * validation to the destination function — flagging the forwarder is a false
258
- * positive, since the wrapper has nothing to validate against. The destination
259
- * function (which actually consumes the array) is the meaningful validation
260
- * site.
261
- *
262
- * Restricted to call-argument position (not the callee position) so we don't
263
- * match the param being treated as a function.
264
- */
265
- function isForwardedToCall(node, paramName, _sourceFile) {
266
- if (!ts.isCallExpression(node) && !ts.isNewExpression(node))
267
- return false;
268
- const args = node.arguments;
269
- /* v8 ignore next -- defensive AST/type guard */
270
- if (!args)
271
- return false;
272
- for (const arg of args) {
273
- if (ts.isIdentifier(arg) && arg.text === paramName) {
274
- return true;
275
- }
276
- // `Array.from(param)`, `[...param]`, etc. handled by isSpreadOfParam.
277
- // Type-cast forwards: `someFn(param as Foo)` or `someFn(param satisfies Foo)`.
278
- if (ts.isAsExpression(arg) || ts.isSatisfiesExpression(arg)) {
279
- const inner = arg.expression;
280
- if (ts.isIdentifier(inner) && inner.text === paramName) {
281
- return true;
282
- }
283
- }
284
- }
285
- return false;
286
- }
287
- /**
288
- * Check if node is a property-shorthand reference to the parameter inside
289
- * an object literal (`return { param }`).
290
- *
291
- * The param value is being copied by reference into a result object — no
292
- * iteration, no boundary crossing, the consumer of the returned object is
293
- * the meaningful validation site (same logic as `isForwardedToCall`).
294
- */
295
- function isShorthandPropertyReference(node, paramName) {
296
- if (!ts.isShorthandPropertyAssignment(node))
297
- return false;
298
- return node.name.text === paramName;
299
- }
300
- /**
301
- * Check if node is an optional chaining or nullish coalescing on the parameter
302
- */
303
- function isOptionalHandling(node, paramName, sourceFile) {
304
- const nodeText = node.getText(sourceFile);
305
- // Optional chaining: param?.length, param?.map
306
- if (nodeText.includes(`${paramName}?.`)) {
307
- return true;
308
- }
309
- /* v8 ignore next -- defensive nullish fallback */
310
- // Nullish coalescing: param ?? []
311
- if (ts.isBinaryExpression(node) &&
312
- node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
313
- const leftText = node.left.getText(sourceFile);
314
- if (leftText === paramName) {
315
- return true;
316
- }
317
- }
318
- return false;
319
- }
320
- /**
321
- * Check if function body contains array validation for the parameter
322
- * @param node - The function declaration, method, or arrow function node
323
- * @param param - The parameter declaration to check for validation
324
- * @param sourceFile - The TypeScript source file for text extraction
325
- * @returns True if the function body contains validation for the array parameter
326
- */
10
+ import { isArrayIsArrayCall, isComplexNestedType, isForwardedToCall, isIndexedAccess, isIterationOverParam, isLengthAccess, isOptionalHandling, isOutSinkUsage, isRelaxedValidationPath, isShorthandPropertyReference, isSpreadOfParam, isTopLevelArrayType, isValidationFunctionCall, isZodValidationCall, QUICK_FILTER_KEYWORDS, } from './array-validation-detectors.js';
327
11
  function checkForArrayValidation(node, param, sourceFile) {
328
- /* v8 ignore next -- defensive guard */
329
12
  if (!node.body)
330
13
  return false;
331
14
  const paramName = ts.isIdentifier(param.name) ? param.name.text : null;
332
- /* v8 ignore next -- defensive AST/type guard */
333
15
  if (!paramName)
334
- return true; // Destructured params are harder to track, assume validated
335
- // Underscore-prefixed params are an established TS convention for
336
- // "intentionally unused" — the body does not consume them, so demanding
337
- // validation is meaningless.
16
+ return true;
338
17
  if (paramName.startsWith('_'))
339
18
  return true;
340
19
  let hasValidation = false;
@@ -351,33 +30,24 @@ function checkForArrayValidation(node, param, sourceFile) {
351
30
  if (isValidationFunctionCall(n)) {
352
31
  hasValidation = true;
353
32
  }
354
- // Iteration patterns imply graceful handling of arrays
355
33
  if (isIterationOverParam(n, paramName, sourceFile)) {
356
34
  hasValidation = true;
357
35
  }
358
- // Optional chaining/nullish coalescing implies null safety
359
36
  if (isOptionalHandling(n, paramName, sourceFile)) {
360
37
  hasValidation = true;
361
38
  }
362
- // Out-array sinks (param.push(...)) — param is a producer target, not a
363
- // consumer input. Validation belongs at the consumer.
364
39
  if (isOutSinkUsage(n, paramName, sourceFile)) {
365
40
  hasValidation = true;
366
41
  }
367
- // Indexed access (param[i]) — defensive, bounded by author intent.
368
42
  if (isIndexedAccess(n, paramName, sourceFile)) {
369
43
  hasValidation = true;
370
44
  }
371
- // Spread (...param) — iterates the array, equivalent to for...of.
372
45
  if (isSpreadOfParam(n, paramName, sourceFile)) {
373
46
  hasValidation = true;
374
47
  }
375
- // Forwarded to another call — destination owns validation.
376
48
  if (isForwardedToCall(n, paramName, sourceFile)) {
377
49
  hasValidation = true;
378
50
  }
379
- // Property-shorthand reference ({ param }) — pass-through into a result
380
- // object; consumer of the result owns validation.
381
51
  if (isShorthandPropertyReference(n, paramName)) {
382
52
  hasValidation = true;
383
53
  }
@@ -388,38 +58,22 @@ function checkForArrayValidation(node, param, sourceFile) {
388
58
  visit(node.body);
389
59
  return hasValidation;
390
60
  }
391
- /**
392
- * Check function parameters for unvalidated arrays
393
- * Note: Ignore directives are handled at the framework level in defineCheck()
394
- * @param {CheckFunctionArrayParamsOptions} options - The check options
395
- * @returns Array of violations
396
- */
397
61
  function checkFunctionArrayParams(options) {
398
62
  const { node, sourceFile, absolutePath } = options;
399
63
  const violations = [];
400
- // Skip files in relaxed validation paths
401
64
  if (isRelaxedValidationPath(absolutePath)) {
402
65
  return violations;
403
66
  }
404
- // Skip abstract methods (can't have validation in body)
405
67
  if (ts.isMethodDeclaration(node) &&
406
68
  node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AbstractKeyword)) {
407
69
  return violations;
408
70
  }
409
- // Filter to array parameters without validation
410
71
  const unvalidatedArrayParams = node.parameters.filter((param) => {
411
72
  if (!param.type)
412
73
  return false;
413
- // Primary gate: use AST to determine if the parameter's type is a top-level
414
- // array type. Object/interface/intersection types whose *properties* are
415
- // arrays will correctly return false here, eliminating the largest category
416
- // of false positives.
417
74
  if (!isTopLevelArrayType(param.type))
418
75
  return false;
419
76
  const typeText = param.type.getText(sourceFile);
420
- // Secondary gate: skip complex/nested types where validation detection is
421
- // unreliable (Map values, Record values, function types, Promises, etc.)
422
- /* v8 ignore next -- defensive AST/type guard */
423
77
  if (isComplexNestedType(typeText))
424
78
  return false;
425
79
  return !checkForArrayValidation(node, param, sourceFile);
@@ -441,25 +95,15 @@ function checkFunctionArrayParams(options) {
441
95
  }
442
96
  return violations;
443
97
  }
444
- /**
445
- * Analyze a file for array validation issues
446
- * @param content - The file content as a string
447
- * @param absolutePath - The absolute path to the file being analyzed
448
- * @returns Array of violations found in the file
449
- */
450
98
  function analyzeFile(content, absolutePath) {
451
99
  const violations = [];
452
- // Quick filter: skip files without array-related patterns
453
100
  if (!QUICK_FILTER_KEYWORDS.some((kw) => content.includes(kw))) {
454
101
  return violations;
455
102
  }
456
- // Note: Ignore directives are handled at the framework level in defineCheck()
457
103
  const sourceFile = getSharedSourceFile(absolutePath, content);
458
- /* v8 ignore next -- defensive guard */
459
104
  if (!sourceFile)
460
105
  return [];
461
106
  const visit = (node) => {
462
- // Check function parameters with array types
463
107
  if (ts.isFunctionDeclaration(node) ||
464
108
  ts.isMethodDeclaration(node) ||
465
109
  ts.isArrowFunction(node)) {
@@ -475,7 +119,6 @@ function analyzeFile(content, absolutePath) {
475
119
  *
476
120
  * Detects array parameters without proper validation to prevent
477
121
  * runtime errors from unvalidated array inputs.
478
- *
479
122
  */
480
123
  export const arrayValidation = defineCheck({
481
124
  id: 'a9e0e70c-a4af-42e6-bbd7-4c87a72cb7d4',
@@ -499,7 +142,6 @@ export const arrayValidation = defineCheck({
499
142
  tags: ['quality', 'validation', 'type-safety', 'arrays'],
500
143
  fileTypes: ['ts'],
501
144
  analyze(content, filePath) {
502
- // Skip test files — array parameter validation in tests is low-risk due to controlled inputs
503
145
  if (isTestFile(filePath))
504
146
  return [];
505
147
  return analyzeFile(content, filePath);