@memberjunction/react-linter 0.0.1 → 5.38.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 (330) hide show
  1. package/dist/component-linter.d.ts +77 -0
  2. package/dist/component-linter.d.ts.map +1 -0
  3. package/dist/component-linter.js +1206 -0
  4. package/dist/component-linter.js.map +1 -0
  5. package/dist/control-flow-analyzer.d.ts +184 -0
  6. package/dist/control-flow-analyzer.d.ts.map +1 -0
  7. package/dist/control-flow-analyzer.js +798 -0
  8. package/dist/control-flow-analyzer.js.map +1 -0
  9. package/dist/index.d.ts +6 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +8 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/library-lint-cache.d.ts +50 -0
  14. package/dist/library-lint-cache.d.ts.map +1 -0
  15. package/dist/library-lint-cache.js +173 -0
  16. package/dist/library-lint-cache.js.map +1 -0
  17. package/dist/lint-rule.d.ts +70 -0
  18. package/dist/lint-rule.d.ts.map +1 -0
  19. package/dist/lint-rule.js +30 -0
  20. package/dist/lint-rule.js.map +1 -0
  21. package/dist/lint-utils.d.ts +131 -0
  22. package/dist/lint-utils.d.ts.map +1 -0
  23. package/dist/lint-utils.js +358 -0
  24. package/dist/lint-utils.js.map +1 -0
  25. package/dist/linter-options.d.ts +51 -0
  26. package/dist/linter-options.d.ts.map +1 -0
  27. package/dist/linter-options.js +2 -0
  28. package/dist/linter-options.js.map +1 -0
  29. package/dist/prop-value-extractor.d.ts +147 -0
  30. package/dist/prop-value-extractor.d.ts.map +1 -0
  31. package/dist/prop-value-extractor.js +472 -0
  32. package/dist/prop-value-extractor.js.map +1 -0
  33. package/dist/runtime-rules/ai-tools-availability-check.d.ts +9 -0
  34. package/dist/runtime-rules/ai-tools-availability-check.d.ts.map +1 -0
  35. package/dist/runtime-rules/ai-tools-availability-check.js +223 -0
  36. package/dist/runtime-rules/ai-tools-availability-check.js.map +1 -0
  37. package/dist/runtime-rules/callback-event-validation.d.ts +22 -0
  38. package/dist/runtime-rules/callback-event-validation.d.ts.map +1 -0
  39. package/dist/runtime-rules/callback-event-validation.js +561 -0
  40. package/dist/runtime-rules/callback-event-validation.js.map +1 -0
  41. package/dist/runtime-rules/chart-field-validation.d.ts +10 -0
  42. package/dist/runtime-rules/chart-field-validation.d.ts.map +1 -0
  43. package/dist/runtime-rules/chart-field-validation.js +270 -0
  44. package/dist/runtime-rules/chart-field-validation.js.map +1 -0
  45. package/dist/runtime-rules/child-component-prop-validation.d.ts +11 -0
  46. package/dist/runtime-rules/child-component-prop-validation.d.ts.map +1 -0
  47. package/dist/runtime-rules/child-component-prop-validation.js +443 -0
  48. package/dist/runtime-rules/child-component-prop-validation.js.map +1 -0
  49. package/dist/runtime-rules/component-name-mismatch.d.ts +19 -0
  50. package/dist/runtime-rules/component-name-mismatch.d.ts.map +1 -0
  51. package/dist/runtime-rules/component-name-mismatch.js +82 -0
  52. package/dist/runtime-rules/component-name-mismatch.js.map +1 -0
  53. package/dist/runtime-rules/component-not-in-dependencies.d.ts +20 -0
  54. package/dist/runtime-rules/component-not-in-dependencies.d.ts.map +1 -0
  55. package/dist/runtime-rules/component-not-in-dependencies.js +92 -0
  56. package/dist/runtime-rules/component-not-in-dependencies.js.map +1 -0
  57. package/dist/runtime-rules/component-props-validation.d.ts +25 -0
  58. package/dist/runtime-rules/component-props-validation.d.ts.map +1 -0
  59. package/dist/runtime-rules/component-props-validation.js +228 -0
  60. package/dist/runtime-rules/component-props-validation.js.map +1 -0
  61. package/dist/runtime-rules/component-usage-without-destructuring.d.ts +20 -0
  62. package/dist/runtime-rules/component-usage-without-destructuring.d.ts.map +1 -0
  63. package/dist/runtime-rules/component-usage-without-destructuring.js +124 -0
  64. package/dist/runtime-rules/component-usage-without-destructuring.js.map +1 -0
  65. package/dist/runtime-rules/data-result-validation.d.ts +9 -0
  66. package/dist/runtime-rules/data-result-validation.d.ts.map +1 -0
  67. package/dist/runtime-rules/data-result-validation.js +763 -0
  68. package/dist/runtime-rules/data-result-validation.js.map +1 -0
  69. package/dist/runtime-rules/datagrid-field-validation.d.ts +10 -0
  70. package/dist/runtime-rules/datagrid-field-validation.d.ts.map +1 -0
  71. package/dist/runtime-rules/datagrid-field-validation.js +249 -0
  72. package/dist/runtime-rules/datagrid-field-validation.js.map +1 -0
  73. package/dist/runtime-rules/dependency-shadowing.d.ts +20 -0
  74. package/dist/runtime-rules/dependency-shadowing.d.ts.map +1 -0
  75. package/dist/runtime-rules/dependency-shadowing.js +147 -0
  76. package/dist/runtime-rules/dependency-shadowing.js.map +1 -0
  77. package/dist/runtime-rules/entity-field-access-validation.d.ts +12 -0
  78. package/dist/runtime-rules/entity-field-access-validation.d.ts.map +1 -0
  79. package/dist/runtime-rules/entity-field-access-validation.js +304 -0
  80. package/dist/runtime-rules/entity-field-access-validation.js.map +1 -0
  81. package/dist/runtime-rules/event-parameter-validation.d.ts +22 -0
  82. package/dist/runtime-rules/event-parameter-validation.d.ts.map +1 -0
  83. package/dist/runtime-rules/event-parameter-validation.js +406 -0
  84. package/dist/runtime-rules/event-parameter-validation.js.map +1 -0
  85. package/dist/runtime-rules/index.d.ts +61 -0
  86. package/dist/runtime-rules/index.d.ts.map +1 -0
  87. package/dist/runtime-rules/index.js +62 -0
  88. package/dist/runtime-rules/index.js.map +1 -0
  89. package/dist/runtime-rules/library-variable-names.d.ts +24 -0
  90. package/dist/runtime-rules/library-variable-names.d.ts.map +1 -0
  91. package/dist/runtime-rules/library-variable-names.js +88 -0
  92. package/dist/runtime-rules/library-variable-names.js.map +1 -0
  93. package/dist/runtime-rules/no-child-implementation.d.ts +18 -0
  94. package/dist/runtime-rules/no-child-implementation.d.ts.map +1 -0
  95. package/dist/runtime-rules/no-child-implementation.js +57 -0
  96. package/dist/runtime-rules/no-child-implementation.js.map +1 -0
  97. package/dist/runtime-rules/no-data-prop.d.ts +22 -0
  98. package/dist/runtime-rules/no-data-prop.d.ts.map +1 -0
  99. package/dist/runtime-rules/no-data-prop.js +111 -0
  100. package/dist/runtime-rules/no-data-prop.js.map +1 -0
  101. package/dist/runtime-rules/no-export-statements.d.ts +18 -0
  102. package/dist/runtime-rules/no-export-statements.d.ts.map +1 -0
  103. package/dist/runtime-rules/no-export-statements.js +143 -0
  104. package/dist/runtime-rules/no-export-statements.js.map +1 -0
  105. package/dist/runtime-rules/no-iife-wrapper.d.ts +18 -0
  106. package/dist/runtime-rules/no-iife-wrapper.d.ts.map +1 -0
  107. package/dist/runtime-rules/no-iife-wrapper.js +217 -0
  108. package/dist/runtime-rules/no-iife-wrapper.js.map +1 -0
  109. package/dist/runtime-rules/no-import-statements.d.ts +18 -0
  110. package/dist/runtime-rules/no-import-statements.d.ts.map +1 -0
  111. package/dist/runtime-rules/no-import-statements.js +65 -0
  112. package/dist/runtime-rules/no-import-statements.js.map +1 -0
  113. package/dist/runtime-rules/no-react-destructuring.d.ts +18 -0
  114. package/dist/runtime-rules/no-react-destructuring.d.ts.map +1 -0
  115. package/dist/runtime-rules/no-react-destructuring.js +60 -0
  116. package/dist/runtime-rules/no-react-destructuring.js.map +1 -0
  117. package/dist/runtime-rules/no-require-statements.d.ts +18 -0
  118. package/dist/runtime-rules/no-require-statements.d.ts.map +1 -0
  119. package/dist/runtime-rules/no-require-statements.js +109 -0
  120. package/dist/runtime-rules/no-require-statements.js.map +1 -0
  121. package/dist/runtime-rules/no-return-component.d.ts +18 -0
  122. package/dist/runtime-rules/no-return-component.d.ts.map +1 -0
  123. package/dist/runtime-rules/no-return-component.js +106 -0
  124. package/dist/runtime-rules/no-return-component.js.map +1 -0
  125. package/dist/runtime-rules/no-unwrap-utility-libs.d.ts +20 -0
  126. package/dist/runtime-rules/no-unwrap-utility-libs.d.ts.map +1 -0
  127. package/dist/runtime-rules/no-unwrap-utility-libs.js +75 -0
  128. package/dist/runtime-rules/no-unwrap-utility-libs.js.map +1 -0
  129. package/dist/runtime-rules/no-use-reducer.d.ts +19 -0
  130. package/dist/runtime-rules/no-use-reducer.d.ts.map +1 -0
  131. package/dist/runtime-rules/no-use-reducer.js +78 -0
  132. package/dist/runtime-rules/no-use-reducer.js.map +1 -0
  133. package/dist/runtime-rules/no-window-access.d.ts +23 -0
  134. package/dist/runtime-rules/no-window-access.d.ts.map +1 -0
  135. package/dist/runtime-rules/no-window-access.js +136 -0
  136. package/dist/runtime-rules/no-window-access.js.map +1 -0
  137. package/dist/runtime-rules/noisy-settings-updates.d.ts +18 -0
  138. package/dist/runtime-rules/noisy-settings-updates.d.ts.map +1 -0
  139. package/dist/runtime-rules/noisy-settings-updates.js +110 -0
  140. package/dist/runtime-rules/noisy-settings-updates.js.map +1 -0
  141. package/dist/runtime-rules/overflow-hidden-on-layout-container.d.ts +30 -0
  142. package/dist/runtime-rules/overflow-hidden-on-layout-container.d.ts.map +1 -0
  143. package/dist/runtime-rules/overflow-hidden-on-layout-container.js +220 -0
  144. package/dist/runtime-rules/overflow-hidden-on-layout-container.js.map +1 -0
  145. package/dist/runtime-rules/pass-standard-props.d.ts +19 -0
  146. package/dist/runtime-rules/pass-standard-props.d.ts.map +1 -0
  147. package/dist/runtime-rules/pass-standard-props.js +82 -0
  148. package/dist/runtime-rules/pass-standard-props.js.map +1 -0
  149. package/dist/runtime-rules/prefer-async-await.d.ts +17 -0
  150. package/dist/runtime-rules/prefer-async-await.d.ts.map +1 -0
  151. package/dist/runtime-rules/prefer-async-await.js +52 -0
  152. package/dist/runtime-rules/prefer-async-await.js.map +1 -0
  153. package/dist/runtime-rules/prefer-jsx-syntax.d.ts +17 -0
  154. package/dist/runtime-rules/prefer-jsx-syntax.d.ts.map +1 -0
  155. package/dist/runtime-rules/prefer-jsx-syntax.js +51 -0
  156. package/dist/runtime-rules/prefer-jsx-syntax.js.map +1 -0
  157. package/dist/runtime-rules/prop-state-sync.d.ts +19 -0
  158. package/dist/runtime-rules/prop-state-sync.d.ts.map +1 -0
  159. package/dist/runtime-rules/prop-state-sync.js +76 -0
  160. package/dist/runtime-rules/prop-state-sync.js.map +1 -0
  161. package/dist/runtime-rules/property-name-consistency.d.ts +20 -0
  162. package/dist/runtime-rules/property-name-consistency.d.ts.map +1 -0
  163. package/dist/runtime-rules/property-name-consistency.js +172 -0
  164. package/dist/runtime-rules/property-name-consistency.js.map +1 -0
  165. package/dist/runtime-rules/query-result-field-access-validation.d.ts +10 -0
  166. package/dist/runtime-rules/query-result-field-access-validation.d.ts.map +1 -0
  167. package/dist/runtime-rules/query-result-field-access-validation.js +304 -0
  168. package/dist/runtime-rules/query-result-field-access-validation.js.map +1 -0
  169. package/dist/runtime-rules/react-component-naming.d.ts +19 -0
  170. package/dist/runtime-rules/react-component-naming.d.ts.map +1 -0
  171. package/dist/runtime-rules/react-component-naming.js +72 -0
  172. package/dist/runtime-rules/react-component-naming.js.map +1 -0
  173. package/dist/runtime-rules/react-hooks-rules.d.ts +27 -0
  174. package/dist/runtime-rules/react-hooks-rules.d.ts.map +1 -0
  175. package/dist/runtime-rules/react-hooks-rules.js +223 -0
  176. package/dist/runtime-rules/react-hooks-rules.js.map +1 -0
  177. package/dist/runtime-rules/required-queries-not-called.d.ts +19 -0
  178. package/dist/runtime-rules/required-queries-not-called.d.ts.map +1 -0
  179. package/dist/runtime-rules/required-queries-not-called.js +146 -0
  180. package/dist/runtime-rules/required-queries-not-called.js.map +1 -0
  181. package/dist/runtime-rules/runquery-call-validation.d.ts +11 -0
  182. package/dist/runtime-rules/runquery-call-validation.d.ts.map +1 -0
  183. package/dist/runtime-rules/runquery-call-validation.js +886 -0
  184. package/dist/runtime-rules/runquery-call-validation.js.map +1 -0
  185. package/dist/runtime-rules/runview-call-validation.d.ts +10 -0
  186. package/dist/runtime-rules/runview-call-validation.d.ts.map +1 -0
  187. package/dist/runtime-rules/runview-call-validation.js +336 -0
  188. package/dist/runtime-rules/runview-call-validation.js.map +1 -0
  189. package/dist/runtime-rules/saved-user-settings-pattern.d.ts +19 -0
  190. package/dist/runtime-rules/saved-user-settings-pattern.d.ts.map +1 -0
  191. package/dist/runtime-rules/saved-user-settings-pattern.js +90 -0
  192. package/dist/runtime-rules/saved-user-settings-pattern.js.map +1 -0
  193. package/dist/runtime-rules/search-availability-check.d.ts +9 -0
  194. package/dist/runtime-rules/search-availability-check.d.ts.map +1 -0
  195. package/dist/runtime-rules/search-availability-check.js +220 -0
  196. package/dist/runtime-rules/search-availability-check.js.map +1 -0
  197. package/dist/runtime-rules/search-call-validation.d.ts +9 -0
  198. package/dist/runtime-rules/search-call-validation.d.ts.map +1 -0
  199. package/dist/runtime-rules/search-call-validation.js +336 -0
  200. package/dist/runtime-rules/search-call-validation.js.map +1 -0
  201. package/dist/runtime-rules/server-reload-on-client-operation.d.ts +18 -0
  202. package/dist/runtime-rules/server-reload-on-client-operation.d.ts.map +1 -0
  203. package/dist/runtime-rules/server-reload-on-client-operation.js +107 -0
  204. package/dist/runtime-rules/server-reload-on-client-operation.js.map +1 -0
  205. package/dist/runtime-rules/single-function-only.d.ts +18 -0
  206. package/dist/runtime-rules/single-function-only.d.ts.map +1 -0
  207. package/dist/runtime-rules/single-function-only.js +103 -0
  208. package/dist/runtime-rules/single-function-only.js.map +1 -0
  209. package/dist/runtime-rules/string-replace-all-occurrences.d.ts +19 -0
  210. package/dist/runtime-rules/string-replace-all-occurrences.d.ts.map +1 -0
  211. package/dist/runtime-rules/string-replace-all-occurrences.js +109 -0
  212. package/dist/runtime-rules/string-replace-all-occurrences.js.map +1 -0
  213. package/dist/runtime-rules/string-template-validation.d.ts +22 -0
  214. package/dist/runtime-rules/string-template-validation.d.ts.map +1 -0
  215. package/dist/runtime-rules/string-template-validation.js +163 -0
  216. package/dist/runtime-rules/string-template-validation.js.map +1 -0
  217. package/dist/runtime-rules/styles-validation.d.ts +10 -0
  218. package/dist/runtime-rules/styles-validation.d.ts.map +1 -0
  219. package/dist/runtime-rules/styles-validation.js +153 -0
  220. package/dist/runtime-rules/styles-validation.js.map +1 -0
  221. package/dist/runtime-rules/type-inference-errors.d.ts +23 -0
  222. package/dist/runtime-rules/type-inference-errors.d.ts.map +1 -0
  223. package/dist/runtime-rules/type-inference-errors.js +53 -0
  224. package/dist/runtime-rules/type-inference-errors.js.map +1 -0
  225. package/dist/runtime-rules/type-mismatch-operation.d.ts +23 -0
  226. package/dist/runtime-rules/type-mismatch-operation.d.ts.map +1 -0
  227. package/dist/runtime-rules/type-mismatch-operation.js +145 -0
  228. package/dist/runtime-rules/type-mismatch-operation.js.map +1 -0
  229. package/dist/runtime-rules/undefined-component-usage.d.ts +20 -0
  230. package/dist/runtime-rules/undefined-component-usage.d.ts.map +1 -0
  231. package/dist/runtime-rules/undefined-component-usage.js +138 -0
  232. package/dist/runtime-rules/undefined-component-usage.js.map +1 -0
  233. package/dist/runtime-rules/undefined-jsx-component.d.ts +25 -0
  234. package/dist/runtime-rules/undefined-jsx-component.d.ts.map +1 -0
  235. package/dist/runtime-rules/undefined-jsx-component.js +269 -0
  236. package/dist/runtime-rules/undefined-jsx-component.js.map +1 -0
  237. package/dist/runtime-rules/unsafe-array-operations.d.ts +25 -0
  238. package/dist/runtime-rules/unsafe-array-operations.d.ts.map +1 -0
  239. package/dist/runtime-rules/unsafe-array-operations.js +347 -0
  240. package/dist/runtime-rules/unsafe-array-operations.js.map +1 -0
  241. package/dist/runtime-rules/unsafe-formatting-methods.d.ts +24 -0
  242. package/dist/runtime-rules/unsafe-formatting-methods.d.ts.map +1 -0
  243. package/dist/runtime-rules/unsafe-formatting-methods.js +277 -0
  244. package/dist/runtime-rules/unsafe-formatting-methods.js.map +1 -0
  245. package/dist/runtime-rules/unused-component-dependencies.d.ts +19 -0
  246. package/dist/runtime-rules/unused-component-dependencies.d.ts.map +1 -0
  247. package/dist/runtime-rules/unused-component-dependencies.js +90 -0
  248. package/dist/runtime-rules/unused-component-dependencies.js.map +1 -0
  249. package/dist/runtime-rules/unused-libraries.d.ts +19 -0
  250. package/dist/runtime-rules/unused-libraries.d.ts.map +1 -0
  251. package/dist/runtime-rules/unused-libraries.js +127 -0
  252. package/dist/runtime-rules/unused-libraries.js.map +1 -0
  253. package/dist/runtime-rules/use-function-declaration.d.ts +18 -0
  254. package/dist/runtime-rules/use-function-declaration.d.ts.map +1 -0
  255. package/dist/runtime-rules/use-function-declaration.js +127 -0
  256. package/dist/runtime-rules/use-function-declaration.js.map +1 -0
  257. package/dist/runtime-rules/use-unwrap-components.d.ts +19 -0
  258. package/dist/runtime-rules/use-unwrap-components.d.ts.map +1 -0
  259. package/dist/runtime-rules/use-unwrap-components.js +84 -0
  260. package/dist/runtime-rules/use-unwrap-components.js.map +1 -0
  261. package/dist/runtime-rules/useeffect-unstable-dependencies.d.ts +23 -0
  262. package/dist/runtime-rules/useeffect-unstable-dependencies.d.ts.map +1 -0
  263. package/dist/runtime-rules/useeffect-unstable-dependencies.js +215 -0
  264. package/dist/runtime-rules/useeffect-unstable-dependencies.js.map +1 -0
  265. package/dist/runtime-rules/utilities-api-validation.d.ts +24 -0
  266. package/dist/runtime-rules/utilities-api-validation.d.ts.map +1 -0
  267. package/dist/runtime-rules/utilities-api-validation.js +121 -0
  268. package/dist/runtime-rules/utilities-api-validation.js.map +1 -0
  269. package/dist/runtime-rules/utilities-no-direct-instantiation.d.ts +20 -0
  270. package/dist/runtime-rules/utilities-no-direct-instantiation.d.ts.map +1 -0
  271. package/dist/runtime-rules/utilities-no-direct-instantiation.js +58 -0
  272. package/dist/runtime-rules/utilities-no-direct-instantiation.js.map +1 -0
  273. package/dist/runtime-rules/validate-component-references.d.ts +19 -0
  274. package/dist/runtime-rules/validate-component-references.d.ts.map +1 -0
  275. package/dist/runtime-rules/validate-component-references.js +255 -0
  276. package/dist/runtime-rules/validate-component-references.js.map +1 -0
  277. package/dist/schema-validation/component-prop-rule.d.ts +131 -0
  278. package/dist/schema-validation/component-prop-rule.d.ts.map +1 -0
  279. package/dist/schema-validation/component-prop-rule.js +625 -0
  280. package/dist/schema-validation/component-prop-rule.js.map +1 -0
  281. package/dist/schema-validation/index.d.ts +26 -0
  282. package/dist/schema-validation/index.d.ts.map +1 -0
  283. package/dist/schema-validation/index.js +26 -0
  284. package/dist/schema-validation/index.js.map +1 -0
  285. package/dist/schema-validation/semantic-validators/index.d.ts +23 -0
  286. package/dist/schema-validation/semantic-validators/index.d.ts.map +1 -0
  287. package/dist/schema-validation/semantic-validators/index.js +23 -0
  288. package/dist/schema-validation/semantic-validators/index.js.map +1 -0
  289. package/dist/schema-validation/semantic-validators/required-when-validator.d.ts +43 -0
  290. package/dist/schema-validation/semantic-validators/required-when-validator.d.ts.map +1 -0
  291. package/dist/schema-validation/semantic-validators/required-when-validator.js +94 -0
  292. package/dist/schema-validation/semantic-validators/required-when-validator.js.map +1 -0
  293. package/dist/schema-validation/semantic-validators/semantic-validator-registry.d.ts +122 -0
  294. package/dist/schema-validation/semantic-validators/semantic-validator-registry.d.ts.map +1 -0
  295. package/dist/schema-validation/semantic-validators/semantic-validator-registry.js +166 -0
  296. package/dist/schema-validation/semantic-validators/semantic-validator-registry.js.map +1 -0
  297. package/dist/schema-validation/semantic-validators/semantic-validator.d.ts +260 -0
  298. package/dist/schema-validation/semantic-validators/semantic-validator.d.ts.map +1 -0
  299. package/dist/schema-validation/semantic-validators/semantic-validator.js +301 -0
  300. package/dist/schema-validation/semantic-validators/semantic-validator.js.map +1 -0
  301. package/dist/schema-validation/semantic-validators/sql-where-clause-validator.d.ts +115 -0
  302. package/dist/schema-validation/semantic-validators/sql-where-clause-validator.d.ts.map +1 -0
  303. package/dist/schema-validation/semantic-validators/sql-where-clause-validator.js +381 -0
  304. package/dist/schema-validation/semantic-validators/sql-where-clause-validator.js.map +1 -0
  305. package/dist/schema-validation/semantic-validators/subset-of-entity-fields-validator.d.ts +60 -0
  306. package/dist/schema-validation/semantic-validators/subset-of-entity-fields-validator.d.ts.map +1 -0
  307. package/dist/schema-validation/semantic-validators/subset-of-entity-fields-validator.js +195 -0
  308. package/dist/schema-validation/semantic-validators/subset-of-entity-fields-validator.js.map +1 -0
  309. package/dist/schema-validation/semantic-validators/validation-context.d.ts +335 -0
  310. package/dist/schema-validation/semantic-validators/validation-context.d.ts.map +1 -0
  311. package/dist/schema-validation/semantic-validators/validation-context.js +13 -0
  312. package/dist/schema-validation/semantic-validators/validation-context.js.map +1 -0
  313. package/dist/styles-type-analyzer.d.ts +64 -0
  314. package/dist/styles-type-analyzer.d.ts.map +1 -0
  315. package/dist/styles-type-analyzer.js +242 -0
  316. package/dist/styles-type-analyzer.js.map +1 -0
  317. package/dist/type-context.d.ts +184 -0
  318. package/dist/type-context.d.ts.map +1 -0
  319. package/dist/type-context.js +415 -0
  320. package/dist/type-context.js.map +1 -0
  321. package/dist/type-inference-engine.d.ts +181 -0
  322. package/dist/type-inference-engine.d.ts.map +1 -0
  323. package/dist/type-inference-engine.js +1151 -0
  324. package/dist/type-inference-engine.js.map +1 -0
  325. package/dist/type-rules/type-compatibility-rule.d.ts +85 -0
  326. package/dist/type-rules/type-compatibility-rule.d.ts.map +1 -0
  327. package/dist/type-rules/type-compatibility-rule.js +211 -0
  328. package/dist/type-rules/type-compatibility-rule.js.map +1 -0
  329. package/package.json +35 -7
  330. package/README.md +0 -45
@@ -0,0 +1,798 @@
1
+ /**
2
+ * Control Flow Analyzer
3
+ *
4
+ * Tracks how types and values narrow through JavaScript code based on runtime checks.
5
+ * Similar to TypeScript's control flow analysis for type narrowing.
6
+ *
7
+ * This eliminates false positives in linting rules by understanding patterns like:
8
+ * - if (x != null) { x.method() } // x is non-null here
9
+ * - if (typeof x === 'number') { x + 1 } // x is number here
10
+ * - if (arr.length > 0) { arr[0] } // arr has elements here
11
+ *
12
+ * @example
13
+ * const cfa = new ControlFlowAnalyzer(ast, componentSpec);
14
+ * if (cfa.isDefinitelyNonNull(node, path)) {
15
+ * // Safe to access property - no violation
16
+ * }
17
+ */
18
+ import * as t from '@babel/types';
19
+ import { TypeInferenceEngine } from './type-inference-engine.js';
20
+ export class ControlFlowAnalyzer {
21
+ constructor(ast, componentSpec) {
22
+ this.ast = ast;
23
+ this.componentSpec = componentSpec;
24
+ this.typeEngine = new TypeInferenceEngine(componentSpec);
25
+ }
26
+ /**
27
+ * Check if a node is protected by a ternary/conditional guard
28
+ * Useful for checking expressions inside template literals within ternaries
29
+ *
30
+ * @param node - The node to check (e.g., member expression)
31
+ * @param path - Current path in the AST (should be close to the node's location)
32
+ * @returns true if protected by a ternary guard, false otherwise
33
+ */
34
+ isProtectedByTernary(node, path) {
35
+ const varName = this.extractVariableName(node);
36
+ if (!varName)
37
+ return false;
38
+ // DEBUG: Log what we're looking for
39
+ const debugEnabled = false; // Set to true to enable logging
40
+ if (debugEnabled) {
41
+ console.log('[CFA] isProtectedByTernary checking:', varName);
42
+ console.log('[CFA] Starting from path type:', path.node.type);
43
+ }
44
+ // Walk up from the path to find ConditionalExpression parents
45
+ let currentPath = path;
46
+ let depth = 0;
47
+ while (currentPath) {
48
+ const parentPath = currentPath.parentPath;
49
+ if (debugEnabled) {
50
+ console.log(`[CFA] Depth ${depth}: current=${currentPath.node.type}, parent=${parentPath?.node.type || 'null'}`);
51
+ }
52
+ if (parentPath && t.isConditionalExpression(parentPath.node)) {
53
+ const test = parentPath.node.test;
54
+ if (debugEnabled) {
55
+ console.log('[CFA] Found ConditionalExpression!');
56
+ console.log('[CFA] Test type:', test.type);
57
+ console.log('[CFA] Consequent type:', parentPath.node.consequent.type);
58
+ }
59
+ if (this.detectNullGuard(test, varName) || this.detectTruthinessGuard(test, varName)) {
60
+ if (debugEnabled) {
61
+ console.log('[CFA] Guard detected for', varName);
62
+ }
63
+ // Check if current node is the consequent or is inside it
64
+ // The consequent is the "true" branch of the ternary
65
+ if (currentPath.node === parentPath.node.consequent) {
66
+ if (debugEnabled) {
67
+ console.log('[CFA] Current node IS the consequent - PROTECTED');
68
+ }
69
+ return true;
70
+ }
71
+ // Check if path is inside the consequent by walking up
72
+ let checkPath = path;
73
+ while (checkPath && checkPath !== parentPath) {
74
+ if (checkPath.node === parentPath.node.consequent) {
75
+ if (debugEnabled) {
76
+ console.log('[CFA] Path is inside consequent - PROTECTED');
77
+ }
78
+ return true;
79
+ }
80
+ checkPath = checkPath.parentPath;
81
+ }
82
+ if (debugEnabled) {
83
+ console.log('[CFA] Not in consequent - checking failed');
84
+ }
85
+ }
86
+ else if (debugEnabled) {
87
+ console.log('[CFA] No guard detected for', varName);
88
+ }
89
+ }
90
+ currentPath = parentPath;
91
+ depth++;
92
+ }
93
+ if (debugEnabled) {
94
+ console.log('[CFA] No protection found after', depth, 'levels');
95
+ }
96
+ return false;
97
+ }
98
+ /**
99
+ * Check if a variable/property is definitely non-null at this location
100
+ *
101
+ * Detects patterns like:
102
+ * - if (x != null) { x.method() } // x is non-null here
103
+ * - if (x !== undefined) { x.prop }
104
+ * - if (x) { x.prop } // truthiness check
105
+ * - x && x.prop // short-circuit &&
106
+ * - x ? x.prop : default // ternary check
107
+ *
108
+ * @param node - The node being accessed (identifier or member expression)
109
+ * @param path - Current path in the AST
110
+ * @returns true if guaranteed non-null, false otherwise
111
+ */
112
+ isDefinitelyNonNull(node, path) {
113
+ const varName = this.extractVariableName(node);
114
+ if (!varName)
115
+ return false;
116
+ // For member expressions like products.length, also check if just the object (products) is guarded
117
+ // This handles cases like: !products || ... || products.length
118
+ const objectName = (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) &&
119
+ t.isIdentifier(node.object)
120
+ ? node.object.name
121
+ : null;
122
+ let currentPath = path.parentPath;
123
+ while (currentPath) {
124
+ const node = currentPath.node;
125
+ // Check if we're inside an if statement with a guard
126
+ if (t.isIfStatement(node)) {
127
+ const test = node.test;
128
+ // Check for null guard on full path or object
129
+ if (this.detectNullGuard(test, varName) || (objectName && this.detectNullGuard(test, objectName))) {
130
+ // Make sure we're in the consequent (then block)
131
+ if (this.isInConsequent(path, currentPath)) {
132
+ return true;
133
+ }
134
+ }
135
+ // Check for truthiness guard on full path or object
136
+ if (this.detectTruthinessGuard(test, varName) || (objectName && this.detectTruthinessGuard(test, objectName))) {
137
+ if (this.isInConsequent(path, currentPath)) {
138
+ return true;
139
+ }
140
+ }
141
+ }
142
+ // Check if we're inside a logical && expression
143
+ if (t.isLogicalExpression(node) && node.operator === '&&') {
144
+ // Check if left side is a guard for our variable or its object
145
+ if (this.detectNullGuard(node.left, varName) ||
146
+ this.detectTruthinessGuard(node.left, varName) ||
147
+ (objectName && this.detectNullGuard(node.left, objectName)) ||
148
+ (objectName && this.detectTruthinessGuard(node.left, objectName))) {
149
+ // Make sure we're in the right side
150
+ if (this.isInRightSide(path, currentPath)) {
151
+ return true;
152
+ }
153
+ }
154
+ }
155
+ // Check if we're inside a logical || expression with negated guard
156
+ // Pattern: !x || !y || x.prop - if we evaluate x.prop, !x must be false
157
+ if (t.isLogicalExpression(node) && node.operator === '||') {
158
+ // Check if left side contains a negated guard for our variable or object
159
+ if (this.detectNegatedCheck(node.left, varName) ||
160
+ (objectName && this.detectNegatedCheck(node.left, objectName))) {
161
+ // Make sure we're in the right side
162
+ if (this.isInRightSide(path, currentPath)) {
163
+ return true;
164
+ }
165
+ }
166
+ }
167
+ // Check if we're inside a ternary with guard
168
+ if (t.isConditionalExpression(node)) {
169
+ // Check if we're in the consequent (true branch) with a guard on full path or object
170
+ if (this.detectNullGuard(node.test, varName) ||
171
+ this.detectTruthinessGuard(node.test, varName) ||
172
+ (objectName && this.detectNullGuard(node.test, objectName)) ||
173
+ (objectName && this.detectTruthinessGuard(node.test, objectName))) {
174
+ if (this.isInConsequent(path, currentPath)) {
175
+ return true;
176
+ }
177
+ }
178
+ // Check if we're in the alternate (else branch) with a negated guard
179
+ // Pattern: !x || x.length === 0 ? ... : <here>
180
+ // In the else branch, we know !x is false, so x is truthy
181
+ if (this.isInAlternate(path, currentPath)) {
182
+ if (this.detectNegatedNullGuard(node.test, varName) ||
183
+ (objectName && this.detectNegatedNullGuard(node.test, objectName))) {
184
+ return true;
185
+ }
186
+ }
187
+ }
188
+ currentPath = currentPath.parentPath;
189
+ }
190
+ return false;
191
+ }
192
+ /**
193
+ * Check if a variable is narrowed to a specific type at this location
194
+ *
195
+ * Detects patterns like:
196
+ * - if (typeof x === 'number') { x + 1 } // x is number
197
+ * - if (x instanceof Date) { x.getTime() } // x is Date
198
+ * - if (Array.isArray(x)) { x.push() } // x is array
199
+ *
200
+ * @param node - The node being checked
201
+ * @param path - Current path in the AST
202
+ * @param expectedType - The type to check for ('number', 'string', 'Date', etc.)
203
+ * @returns true if narrowed to that type, false otherwise
204
+ */
205
+ isNarrowedToType(node, path, expectedType) {
206
+ const varName = this.extractVariableName(node);
207
+ if (!varName)
208
+ return false;
209
+ let currentPath = path.parentPath;
210
+ while (currentPath) {
211
+ const node = currentPath.node;
212
+ // Check if we're inside an if statement with a typeof guard
213
+ if (t.isIfStatement(node)) {
214
+ // First check the test directly
215
+ const detectedType = this.detectTypeofGuard(node.test, varName);
216
+ if (detectedType === expectedType) {
217
+ if (this.isInConsequent(path, currentPath)) {
218
+ return true;
219
+ }
220
+ }
221
+ // Also check recursively if the test is a && expression
222
+ if (t.isLogicalExpression(node.test) && node.test.operator === '&&') {
223
+ const recursiveType = this.detectTypeofGuardRecursive(node.test, varName);
224
+ if (recursiveType === expectedType) {
225
+ if (this.isInConsequent(path, currentPath)) {
226
+ return true;
227
+ }
228
+ }
229
+ }
230
+ }
231
+ // Check if we're inside a logical && expression with typeof guard
232
+ if (t.isLogicalExpression(node) && node.operator === '&&') {
233
+ const detectedType = this.detectTypeofGuard(node.left, varName);
234
+ if (detectedType === expectedType) {
235
+ if (this.isInRightSide(path, currentPath)) {
236
+ return true;
237
+ }
238
+ }
239
+ // Also check nested && expressions recursively
240
+ if (t.isLogicalExpression(node.left) && node.left.operator === '&&') {
241
+ const nestedType = this.detectTypeofGuardRecursive(node.left, varName);
242
+ if (nestedType === expectedType) {
243
+ if (this.isInRightSide(path, currentPath)) {
244
+ return true;
245
+ }
246
+ }
247
+ }
248
+ }
249
+ currentPath = currentPath.parentPath;
250
+ }
251
+ return false;
252
+ }
253
+ /**
254
+ * Recursively check for typeof guards in nested && expressions
255
+ */
256
+ detectTypeofGuardRecursive(expr, varName) {
257
+ if (t.isLogicalExpression(expr) && expr.operator === '&&') {
258
+ // Check left side
259
+ const leftType = this.detectTypeofGuard(expr.left, varName);
260
+ if (leftType)
261
+ return leftType;
262
+ // Check right side recursively
263
+ const rightType = this.detectTypeofGuardRecursive(expr.right, varName);
264
+ if (rightType)
265
+ return rightType;
266
+ }
267
+ // Check this expression directly
268
+ return this.detectTypeofGuard(expr, varName);
269
+ }
270
+ /**
271
+ * Detect typeof guard pattern: typeof x === 'type'
272
+ */
273
+ detectTypeofGuard(test, varName) {
274
+ if (!t.isBinaryExpression(test))
275
+ return null;
276
+ // typeof x === 'type'
277
+ if (test.operator === '===' || test.operator === '==') {
278
+ if (t.isUnaryExpression(test.left) &&
279
+ test.left.operator === 'typeof' &&
280
+ t.isIdentifier(test.left.argument) &&
281
+ test.left.argument.name === varName &&
282
+ t.isStringLiteral(test.right)) {
283
+ return test.right.value; // Return the type
284
+ }
285
+ // Reversed: 'type' === typeof x
286
+ if (t.isStringLiteral(test.left) &&
287
+ t.isUnaryExpression(test.right) &&
288
+ test.right.operator === 'typeof' &&
289
+ t.isIdentifier(test.right.argument) &&
290
+ test.right.argument.name === varName) {
291
+ return test.left.value;
292
+ }
293
+ }
294
+ return null;
295
+ }
296
+ /**
297
+ * Detect null/undefined guard pattern: x != null, x !== null, x !== undefined
298
+ * Also detects short-circuit OR patterns: !x || x.prop (x.prop is safe due to short-circuit)
299
+ */
300
+ detectNullGuard(test, varName) {
301
+ // Pattern 1: Short-circuit OR with truthiness check
302
+ // !x || x.prop - if x is null/undefined, !x is true and second operand never evaluates
303
+ if (t.isLogicalExpression(test) && test.operator === '||') {
304
+ // Check immediate left side first
305
+ const left = test.left;
306
+ // Check if left side is !varName (negation/falsiness check)
307
+ if (t.isUnaryExpression(left) && left.operator === '!' &&
308
+ t.isIdentifier(left.argument) && left.argument.name === varName) {
309
+ // The right side is protected by short-circuit evaluation
310
+ // If varName is null/undefined, left is true and right never evaluates
311
+ return true;
312
+ }
313
+ // Also check for: !x.prop || x.prop.method
314
+ // Handles cases like: !results || results.length === 0
315
+ if (t.isUnaryExpression(left) && left.operator === '!' &&
316
+ t.isMemberExpression(left.argument) &&
317
+ t.isIdentifier(left.argument.object) &&
318
+ left.argument.object.name === varName) {
319
+ // This proves varName itself is checked for truthiness
320
+ return true;
321
+ }
322
+ // Handle OR chains: A || B || C || products.length
323
+ // Recursively check the left side of the OR chain
324
+ if (this.detectNullGuard(left, varName)) {
325
+ return true;
326
+ }
327
+ }
328
+ // Pattern 2: Standard binary null checks
329
+ if (!t.isBinaryExpression(test))
330
+ return false;
331
+ // x != null OR x !== null OR x !== undefined
332
+ if (test.operator === '!=' || test.operator === '!==') {
333
+ const left = test.left;
334
+ const right = test.right;
335
+ // Check if left is our variable
336
+ // For member expressions, serialize the full path for comparison
337
+ let leftVarName = null;
338
+ if (t.isIdentifier(left)) {
339
+ leftVarName = left.name;
340
+ }
341
+ else if (t.isMemberExpression(left) || t.isOptionalMemberExpression(left)) {
342
+ leftVarName = this.serializeMemberExpression(left);
343
+ }
344
+ // Check if right is null or undefined
345
+ const isNullish = (t.isNullLiteral(right) ||
346
+ (t.isIdentifier(right) && right.name === 'undefined'));
347
+ return leftVarName === varName && isNullish;
348
+ }
349
+ return false;
350
+ }
351
+ /**
352
+ * Detect truthiness guard pattern: if (x), x && expr, x ? ... : ...
353
+ *
354
+ * Handles:
355
+ * - Simple identifier: if (x)
356
+ * - Member expression: if (obj.prop)
357
+ * - Full path matching: if (item.TotalCost) protects item.TotalCost.toFixed()
358
+ */
359
+ detectTruthinessGuard(test, varName) {
360
+ // Direct identifier: if (x)
361
+ if (t.isIdentifier(test) && test.name === varName) {
362
+ return true;
363
+ }
364
+ // Member expression or optional member expression: if (obj.prop) or if (obj?.prop)
365
+ // Extract the full path and compare with varName
366
+ if (t.isMemberExpression(test) || t.isOptionalMemberExpression(test)) {
367
+ const testPath = this.serializeMemberExpression(test);
368
+ if (testPath === varName) {
369
+ return true;
370
+ }
371
+ }
372
+ // Logical AND expression: if (x && y), both x and y are truthy in consequent
373
+ // Check if varName appears on the left side of the && chain
374
+ if (t.isLogicalExpression(test) && test.operator === '&&') {
375
+ // Check left side first
376
+ if (this.detectTruthinessGuard(test.left, varName)) {
377
+ return true;
378
+ }
379
+ // For varName on the right side, it's only guaranteed truthy if left is also truthy
380
+ // So we recursively check both sides
381
+ if (this.detectTruthinessGuard(test.right, varName)) {
382
+ return true;
383
+ }
384
+ }
385
+ return false;
386
+ }
387
+ /**
388
+ * Check if expression contains a negated check (!x) for the variable
389
+ * Used for detecting guards within OR chains
390
+ *
391
+ * @param expr - Expression to check
392
+ * @param varName - Variable name to look for
393
+ * @returns true if !varName exists in the expression
394
+ */
395
+ detectNegatedCheck(expr, varName) {
396
+ // Direct negation: !x
397
+ if (t.isUnaryExpression(expr) && expr.operator === '!') {
398
+ if (t.isIdentifier(expr.argument) && expr.argument.name === varName) {
399
+ return true;
400
+ }
401
+ // Also check !x.prop pattern (guards x)
402
+ if (t.isMemberExpression(expr.argument) &&
403
+ t.isIdentifier(expr.argument.object) &&
404
+ expr.argument.object.name === varName) {
405
+ return true;
406
+ }
407
+ }
408
+ // Recursively check OR chains: A || B || C
409
+ if (t.isLogicalExpression(expr) && expr.operator === '||') {
410
+ return this.detectNegatedCheck(expr.left, varName) ||
411
+ this.detectNegatedCheck(expr.right, varName);
412
+ }
413
+ return false;
414
+ }
415
+ /**
416
+ * Detect negated null guard pattern for alternate branches
417
+ *
418
+ * In the alternate (else) branch of these patterns, the variable is proven truthy:
419
+ * - !x || ... ? ... : <here> (if !x is false, x is truthy)
420
+ * - !x.prop || ... ? ... : <here> (if !x.prop is false, x is truthy)
421
+ *
422
+ * @param test - The conditional test expression
423
+ * @param varName - Variable name or full member path to check
424
+ * @returns true if the pattern proves varName is truthy in the alternate branch
425
+ */
426
+ detectNegatedNullGuard(test, varName) {
427
+ // Pattern: !x || x.length === 0 ? ... : <else>
428
+ // In else branch, both !x and x.length === 0 are false
429
+ if (t.isLogicalExpression(test) && test.operator === '||') {
430
+ const left = test.left;
431
+ // Check if left side is !varName (negation check)
432
+ if (t.isUnaryExpression(left) && left.operator === '!') {
433
+ // Check if the negated expression is our variable
434
+ if (t.isIdentifier(left.argument) && left.argument.name === varName) {
435
+ // In the alternate branch, !varName is false, so varName is truthy
436
+ return true;
437
+ }
438
+ // Also handle !x.prop pattern
439
+ if (t.isMemberExpression(left.argument) &&
440
+ t.isIdentifier(left.argument.object) &&
441
+ left.argument.object.name === varName) {
442
+ // In the alternate branch, !x.prop is false, so x is truthy
443
+ return true;
444
+ }
445
+ }
446
+ }
447
+ return false;
448
+ }
449
+ /**
450
+ * Extract variable name or full member expression path from node
451
+ *
452
+ * Examples:
453
+ * - `arr` → "arr"
454
+ * - `obj.prop` → "obj.prop"
455
+ * - `result.Results` → "result.Results"
456
+ * - `accountsResult.Results` → "accountsResult.Results"
457
+ *
458
+ * This allows CFA to match guards on nested properties correctly.
459
+ * For example, guard `accountsResult.Results?.length > 0` protects `accountsResult.Results[0]`
460
+ */
461
+ extractVariableName(node) {
462
+ // Simple identifier: arr
463
+ if (t.isIdentifier(node)) {
464
+ return node.name;
465
+ }
466
+ // Member expression: serialize the full path
467
+ if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) {
468
+ return this.serializeMemberExpression(node);
469
+ }
470
+ return null;
471
+ }
472
+ /**
473
+ * Serialize a member expression to its full path string
474
+ * Handles both regular and optional member expressions
475
+ *
476
+ * Examples:
477
+ * - obj.prop → "obj.prop"
478
+ * - obj?.prop → "obj.prop" (normalized, ignores optional chaining syntax)
479
+ * - obj.prop.nested → "obj.prop.nested"
480
+ */
481
+ serializeMemberExpression(node) {
482
+ const parts = [];
483
+ let current = node;
484
+ // Walk up the member expression chain
485
+ while (t.isMemberExpression(current) || t.isOptionalMemberExpression(current)) {
486
+ // Get the property name
487
+ if (t.isIdentifier(current.property)) {
488
+ parts.unshift(current.property.name);
489
+ }
490
+ else {
491
+ // Computed property or private name - can't serialize
492
+ return null;
493
+ }
494
+ // Move to the object
495
+ current = current.object;
496
+ }
497
+ // Base should be an identifier
498
+ if (t.isIdentifier(current)) {
499
+ parts.unshift(current.name);
500
+ return parts.join('.');
501
+ }
502
+ return null;
503
+ }
504
+ /**
505
+ * Check if path is inside the consequent (then block) of an if/ternary
506
+ */
507
+ isInConsequent(targetPath, ifPath) {
508
+ const ifNode = ifPath.node;
509
+ if (t.isIfStatement(ifNode)) {
510
+ // Walk up from target to see if we hit the consequent
511
+ let current = targetPath;
512
+ while (current && current !== ifPath) {
513
+ if (current.node === ifNode.consequent) {
514
+ return true;
515
+ }
516
+ current = current.parentPath;
517
+ }
518
+ }
519
+ if (t.isConditionalExpression(ifNode)) {
520
+ // Check if we're in the consequent branch
521
+ let current = targetPath;
522
+ while (current && current !== ifPath) {
523
+ if (current.node === ifNode.consequent) {
524
+ return true;
525
+ }
526
+ current = current.parentPath;
527
+ }
528
+ }
529
+ return false;
530
+ }
531
+ /**
532
+ * Check if path is in the alternate (else) branch of an if or ternary
533
+ */
534
+ isInAlternate(targetPath, ifPath) {
535
+ const ifNode = ifPath.node;
536
+ if (t.isIfStatement(ifNode)) {
537
+ // Walk up from target to see if we hit the alternate
538
+ let current = targetPath;
539
+ while (current && current !== ifPath) {
540
+ if (current.node === ifNode.alternate) {
541
+ return true;
542
+ }
543
+ current = current.parentPath;
544
+ }
545
+ }
546
+ if (t.isConditionalExpression(ifNode)) {
547
+ // Check if we're in the alternate branch
548
+ let current = targetPath;
549
+ while (current && current !== ifPath) {
550
+ if (current.node === ifNode.alternate) {
551
+ return true;
552
+ }
553
+ current = current.parentPath;
554
+ }
555
+ }
556
+ return false;
557
+ }
558
+ /**
559
+ * Check if path is on the right side of a logical && expression
560
+ */
561
+ isInRightSide(targetPath, logicalPath) {
562
+ const logicalNode = logicalPath.node;
563
+ if (!t.isLogicalExpression(logicalNode)) {
564
+ return false;
565
+ }
566
+ // Walk up from target to see if we hit the right side
567
+ let current = targetPath;
568
+ while (current && current !== logicalPath) {
569
+ if (current.node === logicalNode.right) {
570
+ return true;
571
+ }
572
+ current = current.parentPath;
573
+ }
574
+ return false;
575
+ }
576
+ /**
577
+ * Check if an array access is safe due to bounds checking guards
578
+ *
579
+ * Detects patterns like:
580
+ * - if (arr.length > 0) { arr[0] } // index 0 is safe
581
+ * - if (arr.length > 2) { arr[2] } // index 2 is safe
582
+ * - if (arr.length === 0) return; arr[0] // early return pattern
583
+ * - arr.length > 0 && arr[0] // inline guard
584
+ * - arr ? arr[0] : default // ternary guard
585
+ *
586
+ * @param arrayNode - The array being accessed (identifier or member expression)
587
+ * @param accessIndex - The index being accessed (e.g., 0 for arr[0])
588
+ * @param path - Current path in the AST
589
+ * @returns true if access is guaranteed safe, false otherwise
590
+ */
591
+ isArrayAccessSafe(arrayNode, accessIndex, path) {
592
+ const arrayName = this.extractVariableName(arrayNode);
593
+ if (!arrayName)
594
+ return false;
595
+ // Pattern 1: Ternary with truthiness or length check
596
+ // arr ? arr[0] : default OR arr.length > 0 ? arr[0] : default
597
+ // Also handles nested cases: arr ? `${arr[0]}` : default
598
+ let currentPath = path.parentPath;
599
+ while (currentPath) {
600
+ if (t.isConditionalExpression(currentPath.node)) {
601
+ const test = currentPath.node.test;
602
+ // Check if we're in the consequent (true branch)
603
+ // Walk up from our path to see if we're inside the consequent
604
+ let inConsequent = false;
605
+ let checkPath = path;
606
+ while (checkPath && checkPath !== currentPath) {
607
+ if (checkPath.node === currentPath.node.consequent) {
608
+ inConsequent = true;
609
+ break;
610
+ }
611
+ // Also check if we're inside the consequent
612
+ let parent = checkPath.parentPath;
613
+ while (parent && parent !== currentPath) {
614
+ if (parent.node === currentPath.node.consequent) {
615
+ inConsequent = true;
616
+ break;
617
+ }
618
+ parent = parent.parentPath;
619
+ }
620
+ if (inConsequent)
621
+ break;
622
+ checkPath = checkPath.parentPath;
623
+ }
624
+ if (inConsequent) {
625
+ // Simple truthiness: arr ? arr[0] : default
626
+ if (t.isIdentifier(test) && test.name === arrayName) {
627
+ return true;
628
+ }
629
+ // Length check in test
630
+ const maxSafeIndex = this.getMaxSafeIndexFromLengthCheck(test, arrayName);
631
+ if (maxSafeIndex >= accessIndex) {
632
+ return true;
633
+ }
634
+ }
635
+ }
636
+ currentPath = currentPath.parentPath;
637
+ }
638
+ // Pattern 2: Inline && guard
639
+ // arr && arr[0] OR arr.length > 0 && arr[0]
640
+ // Walk up to find any LogicalExpression ancestor
641
+ currentPath = path.parentPath;
642
+ while (currentPath) {
643
+ if (t.isLogicalExpression(currentPath.node) && currentPath.node.operator === '&&') {
644
+ const left = currentPath.node.left;
645
+ // Check if we're on the right side
646
+ if (this.isInRightSide(path, currentPath)) {
647
+ // Truthiness check
648
+ if (t.isIdentifier(left) && left.name === arrayName) {
649
+ return true;
650
+ }
651
+ // Length check
652
+ const maxSafeIndex = this.getMaxSafeIndexFromLengthCheck(left, arrayName);
653
+ if (maxSafeIndex >= accessIndex) {
654
+ return true;
655
+ }
656
+ }
657
+ }
658
+ currentPath = currentPath.parentPath;
659
+ }
660
+ // Pattern 3: if statement with guard
661
+ currentPath = path.parentPath;
662
+ while (currentPath) {
663
+ if (t.isIfStatement(currentPath.node)) {
664
+ const test = currentPath.node.test;
665
+ const maxSafeIndex = this.getMaxSafeIndexFromLengthCheck(test, arrayName);
666
+ if (maxSafeIndex >= accessIndex) {
667
+ // Check if we're in the consequent block
668
+ if (this.isInConsequent(path, currentPath)) {
669
+ return true;
670
+ }
671
+ // Check for early return pattern
672
+ const consequent = currentPath.node.consequent;
673
+ if (this.hasEarlyReturn(consequent)) {
674
+ // Code after early return is safe
675
+ return true;
676
+ }
677
+ }
678
+ // Also check for truthiness guard with early return
679
+ if (t.isUnaryExpression(test) && test.operator === '!' &&
680
+ t.isIdentifier(test.argument) && test.argument.name === arrayName) {
681
+ // if (!arr) return; pattern makes arr[0] safe after
682
+ if (this.hasEarlyReturn(currentPath.node.consequent)) {
683
+ return true;
684
+ }
685
+ }
686
+ }
687
+ currentPath = currentPath.parentPath;
688
+ }
689
+ return false;
690
+ }
691
+ /**
692
+ * Extract the maximum safe array index from a length check expression
693
+ *
694
+ * Examples:
695
+ * - arr.length > 0 → returns 0 (index 0 is safe)
696
+ * - arr?.length > 0 → returns 0 (optional chaining proves non-null)
697
+ * - arr.length > 2 → returns 2 (indices 0-2 are safe)
698
+ * - arr.length >= 3 → returns 2 (indices 0-2 are safe)
699
+ * - arr.length !== 0 → returns 0 (index 0 is safe)
700
+ *
701
+ * @param test - The test expression to analyze
702
+ * @param arrayName - The array variable name to look for
703
+ * @returns Maximum safe index, or -1 if no length check found
704
+ */
705
+ getMaxSafeIndexFromLengthCheck(test, arrayName) {
706
+ if (t.isBinaryExpression(test)) {
707
+ const { left, right, operator } = test;
708
+ // Check for arr.length > N or arr.length >= N (including optional chaining)
709
+ if (this.isLengthAccess(left, arrayName) && t.isNumericLiteral(right)) {
710
+ const checkValue = right.value;
711
+ // arr.length > N means indices 0 to N are safe
712
+ if (operator === '>') {
713
+ return checkValue;
714
+ }
715
+ // arr.length >= N means indices 0 to N-1 are safe
716
+ if (operator === '>=') {
717
+ return checkValue - 1;
718
+ }
719
+ // arr.length !== 0 or arr.length != 0 means index 0 is safe
720
+ if ((operator === '!==' || operator === '!=') && checkValue === 0) {
721
+ return 0;
722
+ }
723
+ }
724
+ // Check reverse: N < arr.length or N <= arr.length
725
+ if (this.isLengthAccess(right, arrayName) && t.isNumericLiteral(left)) {
726
+ const checkValue = left.value;
727
+ // N < arr.length means indices 0 to N are safe
728
+ if (operator === '<') {
729
+ return checkValue;
730
+ }
731
+ // N <= arr.length means indices 0 to N-1 are safe
732
+ if (operator === '<=') {
733
+ return checkValue - 1;
734
+ }
735
+ }
736
+ }
737
+ // Check logical expressions (recursively)
738
+ if (t.isLogicalExpression(test)) {
739
+ const leftMax = this.getMaxSafeIndexFromLengthCheck(test.left, arrayName);
740
+ const rightMax = this.getMaxSafeIndexFromLengthCheck(test.right, arrayName);
741
+ // For && operator, both sides must be true, so take minimum
742
+ if (test.operator === '&&') {
743
+ if (leftMax >= 0 && rightMax >= 0) {
744
+ return Math.min(leftMax, rightMax);
745
+ }
746
+ return Math.max(leftMax, rightMax);
747
+ }
748
+ // For || operator, either side can be true, so take maximum
749
+ if (test.operator === '||') {
750
+ return Math.max(leftMax, rightMax);
751
+ }
752
+ }
753
+ return -1;
754
+ }
755
+ /**
756
+ * Check if an expression is accessing the length property of an array
757
+ * Handles both regular and optional chaining: arr.length and arr?.length
758
+ * Also handles nested paths: obj.arr.length and obj.arr?.length
759
+ */
760
+ isLengthAccess(expr, arrayName) {
761
+ // Check if it's a member expression accessing 'length'
762
+ if (!t.isMemberExpression(expr) && !t.isOptionalMemberExpression(expr)) {
763
+ return false;
764
+ }
765
+ // Property must be 'length'
766
+ if (!t.isIdentifier(expr.property) || expr.property.name !== 'length') {
767
+ return false;
768
+ }
769
+ // Get the object being accessed (e.g., 'arr' in arr.length or 'obj.arr' in obj.arr.length)
770
+ const objectPath = this.serializeMemberExpression(expr.object);
771
+ // Simple case: arr.length where arrayName is 'arr'
772
+ if (t.isIdentifier(expr.object) && expr.object.name === arrayName) {
773
+ return true;
774
+ }
775
+ // Nested case: obj.arr.length where arrayName is 'obj.arr'
776
+ if (objectPath === arrayName) {
777
+ return true;
778
+ }
779
+ return false;
780
+ }
781
+ /**
782
+ * Check if a statement or block contains an early return
783
+ */
784
+ hasEarlyReturn(statement) {
785
+ if (t.isReturnStatement(statement)) {
786
+ return true;
787
+ }
788
+ if (t.isBlockStatement(statement)) {
789
+ for (const stmt of statement.body) {
790
+ if (t.isReturnStatement(stmt)) {
791
+ return true;
792
+ }
793
+ }
794
+ }
795
+ return false;
796
+ }
797
+ }
798
+ //# sourceMappingURL=control-flow-analyzer.js.map