@codeyam/codeyam-cli 0.1.0-staging.1669d45 → 0.1.0-staging.2a88920

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 (437) hide show
  1. package/analyzer-template/.build-info.json +8 -8
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +5 -5
  4. package/analyzer-template/packages/ai/index.ts +15 -2
  5. package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +87 -51
  6. package/analyzer-template/packages/ai/src/lib/astScopes/arrayDerivationDetector.ts +199 -0
  7. package/analyzer-template/packages/ai/src/lib/astScopes/astScopeAnalyzer.ts +98 -9
  8. package/analyzer-template/packages/ai/src/lib/astScopes/methodSemantics.ts +139 -23
  9. package/analyzer-template/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.ts +6 -126
  10. package/analyzer-template/packages/ai/src/lib/astScopes/processExpression.ts +555 -28
  11. package/analyzer-template/packages/ai/src/lib/astScopes/types.ts +88 -7
  12. package/analyzer-template/packages/ai/src/lib/completionCall.ts +198 -34
  13. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +772 -243
  14. package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/MuiManager.ts +205 -0
  15. package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/ReactFrameworkManager.ts +10 -2
  16. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.ts +16 -3
  17. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.ts +6 -4
  18. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +43 -1
  19. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +122 -15
  20. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.ts +160 -0
  21. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.ts +40 -30
  22. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts +319 -88
  23. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.ts +129 -0
  24. package/analyzer-template/packages/ai/src/lib/dataStructureChunking.ts +156 -0
  25. package/analyzer-template/packages/ai/src/lib/e2eDataTracking.ts +334 -0
  26. package/analyzer-template/packages/ai/src/lib/extractCriticalDataKeys.ts +120 -0
  27. package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +642 -7
  28. package/analyzer-template/packages/ai/src/lib/generateEntityScenarios.ts +35 -6
  29. package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +383 -6
  30. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.ts +1 -1
  31. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionals.ts +1299 -51
  32. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.ts +239 -0
  33. package/analyzer-template/packages/ai/src/lib/mergeStatements.ts +90 -96
  34. package/analyzer-template/packages/ai/src/lib/promptGenerators/gatherAttributesMap.ts +10 -7
  35. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateChunkPrompt.ts +82 -0
  36. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateCriticalKeysPrompt.ts +103 -0
  37. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.ts +23 -6
  38. package/analyzer-template/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.ts +391 -0
  39. package/analyzer-template/packages/ai/src/lib/resolvePathToControllable.ts +179 -45
  40. package/analyzer-template/packages/ai/src/lib/worker/SerializableDataStructure.ts +26 -4
  41. package/analyzer-template/packages/ai/src/lib/worker/analyzeScopeWorker.ts +114 -2
  42. package/analyzer-template/packages/analyze/src/lib/FileAnalyzer.ts +65 -59
  43. package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +113 -26
  44. package/analyzer-template/packages/analyze/src/lib/analysisContext.ts +44 -4
  45. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.ts +19 -0
  46. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.ts +19 -0
  47. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllExports.ts +11 -0
  48. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.ts +8 -0
  49. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.ts +49 -1
  50. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.ts +2 -1
  51. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +30 -19
  52. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +14 -4
  53. package/analyzer-template/packages/analyze/src/lib/files/analyze/dependencyResolver.ts +6 -0
  54. package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +4 -2
  55. package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +33 -10
  56. package/analyzer-template/packages/analyze/src/lib/files/analyzeRemixRoute.ts +4 -5
  57. package/analyzer-template/packages/analyze/src/lib/files/getImportedExports.ts +14 -12
  58. package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +189 -76
  59. package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +29 -0
  60. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +77 -9
  61. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +118 -10
  62. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +276 -17
  63. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.ts +56 -11
  64. package/analyzer-template/packages/analyze/src/lib/files/scenarios/propagateArrayItemSchemas.ts +474 -0
  65. package/analyzer-template/packages/analyze/src/lib/files/setImportedExports.ts +2 -1
  66. package/analyzer-template/packages/analyze/src/lib/utils/getFileByPath.ts +19 -0
  67. package/analyzer-template/packages/aws/package.json +2 -2
  68. package/analyzer-template/packages/database/src/lib/kysely/db.ts +8 -1
  69. package/analyzer-template/packages/database/src/lib/kysely/tables/commitsTable.ts +6 -0
  70. package/analyzer-template/packages/database/src/lib/loadAnalyses.ts +58 -1
  71. package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +13 -0
  72. package/analyzer-template/packages/database/src/lib/loadBranch.ts +16 -1
  73. package/analyzer-template/packages/database/src/lib/loadCommit.ts +11 -0
  74. package/analyzer-template/packages/database/src/lib/loadCommits.ts +28 -0
  75. package/analyzer-template/packages/database/src/lib/loadEntities.ts +26 -3
  76. package/analyzer-template/packages/database/src/lib/loadEntityBranches.ts +12 -0
  77. package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +7 -14
  78. package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.d.ts.map +1 -1
  79. package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.js +8 -1
  80. package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.js.map +1 -1
  81. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/analysesTable.d.ts.map +1 -1
  82. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.d.ts +1 -0
  83. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.d.ts.map +1 -1
  84. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.js +3 -0
  85. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.js.map +1 -1
  86. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.d.ts +2 -0
  87. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.d.ts.map +1 -1
  88. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.js +45 -2
  89. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.js.map +1 -1
  90. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
  91. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +8 -0
  92. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
  93. package/analyzer-template/packages/github/dist/database/src/lib/loadBranch.js +11 -1
  94. package/analyzer-template/packages/github/dist/database/src/lib/loadBranch.js.map +1 -1
  95. package/analyzer-template/packages/github/dist/database/src/lib/loadCommit.d.ts.map +1 -1
  96. package/analyzer-template/packages/github/dist/database/src/lib/loadCommit.js +7 -0
  97. package/analyzer-template/packages/github/dist/database/src/lib/loadCommit.js.map +1 -1
  98. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.d.ts +3 -1
  99. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.d.ts.map +1 -1
  100. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.js +22 -1
  101. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.js.map +1 -1
  102. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts +3 -1
  103. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts.map +1 -1
  104. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js +23 -4
  105. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js.map +1 -1
  106. package/analyzer-template/packages/github/dist/database/src/lib/loadEntityBranches.d.ts.map +1 -1
  107. package/analyzer-template/packages/github/dist/database/src/lib/loadEntityBranches.js +9 -0
  108. package/analyzer-template/packages/github/dist/database/src/lib/loadEntityBranches.js.map +1 -1
  109. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts +2 -2
  110. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts.map +1 -1
  111. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js +5 -4
  112. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js.map +1 -1
  113. package/analyzer-template/packages/github/dist/types/index.d.ts +1 -1
  114. package/analyzer-template/packages/github/dist/types/index.d.ts.map +1 -1
  115. package/analyzer-template/packages/github/dist/types/index.js.map +1 -1
  116. package/analyzer-template/packages/github/dist/types/src/types/Analysis.d.ts +25 -1
  117. package/analyzer-template/packages/github/dist/types/src/types/Analysis.d.ts.map +1 -1
  118. package/analyzer-template/packages/github/dist/types/src/types/Commit.d.ts +2 -0
  119. package/analyzer-template/packages/github/dist/types/src/types/Commit.d.ts.map +1 -1
  120. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts +56 -6
  121. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  122. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  123. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  124. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.d.ts +9 -1
  125. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.d.ts.map +1 -1
  126. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.js +29 -3
  127. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.js.map +1 -1
  128. package/analyzer-template/packages/github/package.json +1 -1
  129. package/analyzer-template/packages/types/index.ts +1 -0
  130. package/analyzer-template/packages/types/src/types/Analysis.ts +25 -0
  131. package/analyzer-template/packages/types/src/types/Commit.ts +2 -0
  132. package/analyzer-template/packages/types/src/types/ScenariosDataStructure.ts +70 -6
  133. package/analyzer-template/packages/types/src/types/ScopeAnalysis.ts +6 -1
  134. package/analyzer-template/packages/utils/dist/types/index.d.ts +1 -1
  135. package/analyzer-template/packages/utils/dist/types/index.d.ts.map +1 -1
  136. package/analyzer-template/packages/utils/dist/types/index.js.map +1 -1
  137. package/analyzer-template/packages/utils/dist/types/src/types/Analysis.d.ts +25 -1
  138. package/analyzer-template/packages/utils/dist/types/src/types/Analysis.d.ts.map +1 -1
  139. package/analyzer-template/packages/utils/dist/types/src/types/Commit.d.ts +2 -0
  140. package/analyzer-template/packages/utils/dist/types/src/types/Commit.d.ts.map +1 -1
  141. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts +56 -6
  142. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  143. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  144. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  145. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.d.ts +9 -1
  146. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.d.ts.map +1 -1
  147. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.js +29 -3
  148. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.js.map +1 -1
  149. package/analyzer-template/packages/utils/src/lib/safeFileName.ts +48 -3
  150. package/analyzer-template/playwright/capture.ts +20 -8
  151. package/analyzer-template/playwright/captureStatic.ts +1 -1
  152. package/analyzer-template/project/analyzeBaselineCommit.ts +5 -0
  153. package/analyzer-template/project/analyzeRegularCommit.ts +5 -0
  154. package/analyzer-template/project/captureLibraryFunctionDirect.ts +29 -26
  155. package/analyzer-template/project/constructMockCode.ts +367 -37
  156. package/analyzer-template/project/createEntitiesAndSortFiles.ts +83 -0
  157. package/analyzer-template/project/loadReadyToBeCaptured.ts +65 -41
  158. package/analyzer-template/project/orchestrateCapture/AwsCaptureTaskRunner.ts +12 -4
  159. package/analyzer-template/project/orchestrateCapture/SequentialCaptureTaskRunner.ts +18 -7
  160. package/analyzer-template/project/orchestrateCapture/taskRunner.ts +4 -2
  161. package/analyzer-template/project/orchestrateCapture.ts +71 -6
  162. package/analyzer-template/project/reconcileMockDataKeys.ts +152 -9
  163. package/analyzer-template/project/runAnalysis.ts +4 -0
  164. package/analyzer-template/project/start.ts +35 -11
  165. package/analyzer-template/project/writeMockDataTsx.ts +127 -4
  166. package/analyzer-template/project/writeScenarioComponents.ts +101 -8
  167. package/analyzer-template/scripts/comboWorkerLoop.cjs +98 -50
  168. package/background/src/lib/virtualized/project/analyzeBaselineCommit.js +5 -0
  169. package/background/src/lib/virtualized/project/analyzeBaselineCommit.js.map +1 -1
  170. package/background/src/lib/virtualized/project/analyzeRegularCommit.js +5 -0
  171. package/background/src/lib/virtualized/project/analyzeRegularCommit.js.map +1 -1
  172. package/background/src/lib/virtualized/project/captureLibraryFunctionDirect.js +3 -3
  173. package/background/src/lib/virtualized/project/captureLibraryFunctionDirect.js.map +1 -1
  174. package/background/src/lib/virtualized/project/constructMockCode.js +300 -7
  175. package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
  176. package/background/src/lib/virtualized/project/createEntitiesAndSortFiles.js +73 -1
  177. package/background/src/lib/virtualized/project/createEntitiesAndSortFiles.js.map +1 -1
  178. package/background/src/lib/virtualized/project/loadReadyToBeCaptured.js +19 -8
  179. package/background/src/lib/virtualized/project/loadReadyToBeCaptured.js.map +1 -1
  180. package/background/src/lib/virtualized/project/orchestrateCapture/AwsCaptureTaskRunner.js +2 -2
  181. package/background/src/lib/virtualized/project/orchestrateCapture/AwsCaptureTaskRunner.js.map +1 -1
  182. package/background/src/lib/virtualized/project/orchestrateCapture/SequentialCaptureTaskRunner.js +7 -5
  183. package/background/src/lib/virtualized/project/orchestrateCapture/SequentialCaptureTaskRunner.js.map +1 -1
  184. package/background/src/lib/virtualized/project/orchestrateCapture.js +58 -6
  185. package/background/src/lib/virtualized/project/orchestrateCapture.js.map +1 -1
  186. package/background/src/lib/virtualized/project/reconcileMockDataKeys.js +126 -9
  187. package/background/src/lib/virtualized/project/reconcileMockDataKeys.js.map +1 -1
  188. package/background/src/lib/virtualized/project/runAnalysis.js +3 -0
  189. package/background/src/lib/virtualized/project/runAnalysis.js.map +1 -1
  190. package/background/src/lib/virtualized/project/start.js +32 -11
  191. package/background/src/lib/virtualized/project/start.js.map +1 -1
  192. package/background/src/lib/virtualized/project/writeMockDataTsx.js +101 -4
  193. package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
  194. package/background/src/lib/virtualized/project/writeScenarioComponents.js +57 -8
  195. package/background/src/lib/virtualized/project/writeScenarioComponents.js.map +1 -1
  196. package/codeyam-cli/src/cli.js +2 -0
  197. package/codeyam-cli/src/cli.js.map +1 -1
  198. package/codeyam-cli/src/commands/debug.js +7 -5
  199. package/codeyam-cli/src/commands/debug.js.map +1 -1
  200. package/codeyam-cli/src/commands/memory.js +273 -0
  201. package/codeyam-cli/src/commands/memory.js.map +1 -0
  202. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +4 -0
  203. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  204. package/codeyam-cli/src/utils/analysisRunner.js +21 -2
  205. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  206. package/codeyam-cli/src/utils/install-skills.js +42 -6
  207. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  208. package/codeyam-cli/src/utils/queue/job.js +1 -0
  209. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  210. package/codeyam-cli/src/utils/queue/manager.js +6 -0
  211. package/codeyam-cli/src/utils/queue/manager.js.map +1 -1
  212. package/codeyam-cli/src/utils/rules/index.js +5 -0
  213. package/codeyam-cli/src/utils/rules/index.js.map +1 -0
  214. package/codeyam-cli/src/utils/rules/parser.js +106 -0
  215. package/codeyam-cli/src/utils/rules/parser.js.map +1 -0
  216. package/codeyam-cli/src/utils/rules/pathMatcher.js +18 -0
  217. package/codeyam-cli/src/utils/rules/pathMatcher.js.map +1 -0
  218. package/codeyam-cli/src/utils/rules/staleness.js +132 -0
  219. package/codeyam-cli/src/utils/rules/staleness.js.map +1 -0
  220. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +2 -0
  221. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  222. package/codeyam-cli/src/webserver/app/lib/database.js +7 -3
  223. package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
  224. package/codeyam-cli/src/webserver/bootstrap.js +40 -0
  225. package/codeyam-cli/src/webserver/bootstrap.js.map +1 -1
  226. package/codeyam-cli/src/webserver/build/client/assets/EntityItem-DsN1wKrm.js +11 -0
  227. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeBadge-COi5OvsN.js → EntityTypeBadge-DLqD3qNt.js} +1 -1
  228. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-BwdQv49w.js → EntityTypeIcon-Ba2JVPzP.js} +1 -1
  229. package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-CEleMv_j.js → InlineSpinner-C8lyxW9k.js} +1 -1
  230. package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-D68KarMg.js → InteractivePreview-aht4aafF.js} +2 -2
  231. package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-L75Wvqgw.js → LibraryFunctionPreview-CVtiBnY5.js} +1 -1
  232. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-C53WM8qn.js → LoadingDots-B0GLXMsr.js} +1 -1
  233. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-CrNkmy4i.js → LogViewer-xgeCVgSM.js} +1 -1
  234. package/codeyam-cli/src/webserver/build/client/assets/ReportIssueModal-OApQuNyq.js +16 -0
  235. package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-CQifa1n-.js → SafeScreenshot-DuDvi0jm.js} +1 -1
  236. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-CyaBFX7l.js → ScenarioViewer-DzccYyI8.js} +3 -13
  237. package/codeyam-cli/src/webserver/build/client/assets/{TruncatedFilePath-D36O1rzU.js → TruncatedFilePath-DyFZkK0l.js} +1 -1
  238. package/codeyam-cli/src/webserver/build/client/assets/_index-BwqWJOgH.js +11 -0
  239. package/codeyam-cli/src/webserver/build/client/assets/activity.(_tab)-BwavGCpm.js +32 -0
  240. package/codeyam-cli/src/webserver/build/client/assets/api.health-l0sNRNKZ.js +1 -0
  241. package/codeyam-cli/src/webserver/build/client/assets/api.memory-profile-l0sNRNKZ.js +1 -0
  242. package/codeyam-cli/src/webserver/build/client/assets/api.restart-server-l0sNRNKZ.js +1 -0
  243. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-DgTPh8H-.js → chevron-down-Cx24_aWc.js} +1 -1
  244. package/codeyam-cli/src/webserver/build/client/assets/{chunk-EPOLDU6W-DdQKK6on.js → chunk-EPOLDU6W-CXRTFQ3F.js} +1 -1
  245. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-Dmr2bb1R.js → circle-check-BOARzkeR.js} +1 -1
  246. package/codeyam-cli/src/webserver/build/client/assets/copy-Bb-80kDT.js +6 -0
  247. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-Do4ZLUYa.js → createLucideIcon-BdhJEx6B.js} +1 -1
  248. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-BBnGWYga.js +1 -0
  249. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-CbdFyxZh.js → entity._sha._-BJUiQqZF.js} +12 -12
  250. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-B4iCfs5M.js → entity._sha.scenarios._scenarioId.fullscreen-DavjRmOY.js} +1 -1
  251. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-wDWZZO1W.js → entity._sha_.create-scenario-D1T4TGjf.js} +1 -1
  252. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMbl7MeQ.js → entity._sha_.edit._scenarioId-CTBG2mmz.js} +1 -1
  253. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-5wRKRIH9.js → entry.client-CS2cb_eZ.js} +1 -1
  254. package/codeyam-cli/src/webserver/build/client/assets/file-code-Dhef1kWN.js +6 -0
  255. package/codeyam-cli/src/webserver/build/client/assets/{fileTableUtils-DD3SDH7t.js → fileTableUtils-DMJ7zii9.js} +1 -1
  256. package/codeyam-cli/src/webserver/build/client/assets/files-CJ6lTdTA.js +1 -0
  257. package/codeyam-cli/src/webserver/build/client/assets/{git-zXjT7J0G.js → git-CPTZZ-JZ.js} +8 -8
  258. package/codeyam-cli/src/webserver/build/client/assets/globals-D3yhhV8x.css +1 -0
  259. package/codeyam-cli/src/webserver/build/client/assets/{index-DLbXwndH.js → index-B1h680n5.js} +1 -1
  260. package/codeyam-cli/src/webserver/build/client/assets/{index-gPZ-lad1.js → index-lzqtyFU8.js} +1 -1
  261. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-BsPXJ81F.js → loader-circle-B7B9V-bu.js} +1 -1
  262. package/codeyam-cli/src/webserver/build/client/assets/manifest-a78b90a2.js +1 -0
  263. package/codeyam-cli/src/webserver/build/client/assets/memory--GCbFsBE.js +92 -0
  264. package/codeyam-cli/src/webserver/build/client/assets/root-eVAaavTS.js +62 -0
  265. package/codeyam-cli/src/webserver/build/client/assets/{search-P2FKIUql.js → search-CxXUmBSd.js} +1 -1
  266. package/codeyam-cli/src/webserver/build/client/assets/{settings-B2eDuBj8.js → settings-CS5f3WzT.js} +1 -1
  267. package/codeyam-cli/src/webserver/build/client/assets/{simulations-L18M6-kN.js → simulations-DwFIBT09.js} +1 -1
  268. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BDz7kbVA.js → triangle-alert-B6LgvRJg.js} +1 -1
  269. package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-29dDmbH8.js → useCustomSizes-C1v1PQzo.js} +1 -1
  270. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-BUm0UVJm.js → useLastLogLine-aSv48UbS.js} +1 -1
  271. package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-CkIOKTrZ.js → useReportContext-DYxHZQuP.js} +1 -1
  272. package/codeyam-cli/src/webserver/build/client/assets/{useToast-KKw5kTn-.js → useToast-mBRpZPiu.js} +1 -1
  273. package/codeyam-cli/src/webserver/build/server/assets/index-BM6TDT1Y.js +1 -0
  274. package/codeyam-cli/src/webserver/build/server/assets/server-build-dYC34MHw.js +257 -0
  275. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  276. package/codeyam-cli/src/webserver/build-info.json +5 -5
  277. package/codeyam-cli/templates/codeyam-memory-hook.sh +200 -0
  278. package/codeyam-cli/templates/codeyam:debug.md +47 -3
  279. package/codeyam-cli/templates/codeyam:diagnose.md +203 -25
  280. package/codeyam-cli/templates/codeyam:memory.md +341 -0
  281. package/codeyam-cli/templates/codeyam:new-rule.md +13 -0
  282. package/codeyam-cli/templates/rule-reflection-hook.py +160 -0
  283. package/codeyam-cli/templates/rules-instructions.md +93 -0
  284. package/package.json +8 -5
  285. package/packages/ai/index.js +7 -3
  286. package/packages/ai/index.js.map +1 -1
  287. package/packages/ai/src/lib/analyzeScope.js +70 -29
  288. package/packages/ai/src/lib/analyzeScope.js.map +1 -1
  289. package/packages/ai/src/lib/astScopes/arrayDerivationDetector.js +150 -0
  290. package/packages/ai/src/lib/astScopes/arrayDerivationDetector.js.map +1 -0
  291. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js +78 -8
  292. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js.map +1 -1
  293. package/packages/ai/src/lib/astScopes/methodSemantics.js +109 -23
  294. package/packages/ai/src/lib/astScopes/methodSemantics.js.map +1 -1
  295. package/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.js +1 -102
  296. package/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.js.map +1 -1
  297. package/packages/ai/src/lib/astScopes/processExpression.js +440 -27
  298. package/packages/ai/src/lib/astScopes/processExpression.js.map +1 -1
  299. package/packages/ai/src/lib/completionCall.js +161 -30
  300. package/packages/ai/src/lib/completionCall.js.map +1 -1
  301. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +589 -166
  302. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  303. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/MuiManager.js +179 -0
  304. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/MuiManager.js.map +1 -1
  305. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/ReactFrameworkManager.js +7 -1
  306. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/ReactFrameworkManager.js.map +1 -1
  307. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js +13 -3
  308. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js.map +1 -1
  309. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js +6 -4
  310. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js.map +1 -1
  311. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +41 -1
  312. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  313. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +104 -11
  314. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
  315. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js +159 -0
  316. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js.map +1 -0
  317. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js +37 -20
  318. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js.map +1 -1
  319. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js +265 -79
  320. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js.map +1 -1
  321. package/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.js +107 -0
  322. package/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.js.map +1 -0
  323. package/packages/ai/src/lib/dataStructureChunking.js +111 -0
  324. package/packages/ai/src/lib/dataStructureChunking.js.map +1 -0
  325. package/packages/ai/src/lib/e2eDataTracking.js +241 -0
  326. package/packages/ai/src/lib/e2eDataTracking.js.map +1 -0
  327. package/packages/ai/src/lib/extractCriticalDataKeys.js +96 -0
  328. package/packages/ai/src/lib/extractCriticalDataKeys.js.map +1 -0
  329. package/packages/ai/src/lib/generateEntityScenarioData.js +525 -8
  330. package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
  331. package/packages/ai/src/lib/generateEntityScenarios.js +26 -2
  332. package/packages/ai/src/lib/generateEntityScenarios.js.map +1 -1
  333. package/packages/ai/src/lib/generateExecutionFlows.js +281 -4
  334. package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
  335. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js +946 -42
  336. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js.map +1 -1
  337. package/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.js +194 -0
  338. package/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.js.map +1 -0
  339. package/packages/ai/src/lib/mergeStatements.js +70 -51
  340. package/packages/ai/src/lib/mergeStatements.js.map +1 -1
  341. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js +10 -4
  342. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js.map +1 -1
  343. package/packages/ai/src/lib/promptGenerators/generateChunkPrompt.js +54 -0
  344. package/packages/ai/src/lib/promptGenerators/generateChunkPrompt.js.map +1 -0
  345. package/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.js +15 -7
  346. package/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.js.map +1 -1
  347. package/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.js +335 -0
  348. package/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.js.map +1 -0
  349. package/packages/ai/src/lib/resolvePathToControllable.js +155 -41
  350. package/packages/ai/src/lib/resolvePathToControllable.js.map +1 -1
  351. package/packages/ai/src/lib/worker/SerializableDataStructure.js +7 -0
  352. package/packages/ai/src/lib/worker/SerializableDataStructure.js.map +1 -1
  353. package/packages/ai/src/lib/worker/analyzeScopeWorker.js +94 -1
  354. package/packages/ai/src/lib/worker/analyzeScopeWorker.js.map +1 -1
  355. package/packages/analyze/src/lib/FileAnalyzer.js +60 -36
  356. package/packages/analyze/src/lib/FileAnalyzer.js.map +1 -1
  357. package/packages/analyze/src/lib/ProjectAnalyzer.js +96 -26
  358. package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
  359. package/packages/analyze/src/lib/analysisContext.js +30 -5
  360. package/packages/analyze/src/lib/analysisContext.js.map +1 -1
  361. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js +14 -0
  362. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js.map +1 -1
  363. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js +14 -0
  364. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js.map +1 -1
  365. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js +6 -0
  366. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js.map +1 -1
  367. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js +6 -0
  368. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js.map +1 -1
  369. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js +39 -1
  370. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js.map +1 -1
  371. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js +2 -1
  372. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js.map +1 -1
  373. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +21 -9
  374. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  375. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +14 -4
  376. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  377. package/packages/analyze/src/lib/files/analyze/dependencyResolver.js +5 -0
  378. package/packages/analyze/src/lib/files/analyze/dependencyResolver.js.map +1 -1
  379. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +2 -1
  380. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
  381. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +31 -10
  382. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  383. package/packages/analyze/src/lib/files/analyzeRemixRoute.js +3 -2
  384. package/packages/analyze/src/lib/files/analyzeRemixRoute.js.map +1 -1
  385. package/packages/analyze/src/lib/files/getImportedExports.js +11 -7
  386. package/packages/analyze/src/lib/files/getImportedExports.js.map +1 -1
  387. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +160 -68
  388. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -1
  389. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +25 -8
  390. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
  391. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +71 -9
  392. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  393. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +57 -9
  394. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
  395. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +233 -9
  396. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  397. package/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.js +46 -9
  398. package/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.js.map +1 -1
  399. package/packages/analyze/src/lib/files/setImportedExports.js +2 -1
  400. package/packages/analyze/src/lib/files/setImportedExports.js.map +1 -1
  401. package/packages/analyze/src/lib/utils/getFileByPath.js +12 -0
  402. package/packages/analyze/src/lib/utils/getFileByPath.js.map +1 -0
  403. package/packages/database/src/lib/kysely/db.js +8 -1
  404. package/packages/database/src/lib/kysely/db.js.map +1 -1
  405. package/packages/database/src/lib/kysely/tables/commitsTable.js +3 -0
  406. package/packages/database/src/lib/kysely/tables/commitsTable.js.map +1 -1
  407. package/packages/database/src/lib/loadAnalyses.js +45 -2
  408. package/packages/database/src/lib/loadAnalyses.js.map +1 -1
  409. package/packages/database/src/lib/loadAnalysis.js +8 -0
  410. package/packages/database/src/lib/loadAnalysis.js.map +1 -1
  411. package/packages/database/src/lib/loadBranch.js +11 -1
  412. package/packages/database/src/lib/loadBranch.js.map +1 -1
  413. package/packages/database/src/lib/loadCommit.js +7 -0
  414. package/packages/database/src/lib/loadCommit.js.map +1 -1
  415. package/packages/database/src/lib/loadCommits.js +22 -1
  416. package/packages/database/src/lib/loadCommits.js.map +1 -1
  417. package/packages/database/src/lib/loadEntities.js +23 -4
  418. package/packages/database/src/lib/loadEntities.js.map +1 -1
  419. package/packages/database/src/lib/loadEntityBranches.js +9 -0
  420. package/packages/database/src/lib/loadEntityBranches.js.map +1 -1
  421. package/packages/database/src/lib/updateCommitMetadata.js +5 -4
  422. package/packages/database/src/lib/updateCommitMetadata.js.map +1 -1
  423. package/packages/types/index.js.map +1 -1
  424. package/packages/utils/src/lib/safeFileName.js +29 -3
  425. package/packages/utils/src/lib/safeFileName.js.map +1 -1
  426. package/scripts/finalize-analyzer.cjs +3 -3
  427. package/codeyam-cli/src/webserver/build/client/assets/EntityItem-vauWK972.js +0 -1
  428. package/codeyam-cli/src/webserver/build/client/assets/ReportIssueModal-DzJRkCkr.js +0 -11
  429. package/codeyam-cli/src/webserver/build/client/assets/_index-Be83mo_j.js +0 -11
  430. package/codeyam-cli/src/webserver/build/client/assets/activity.(_tab)-BN6wu6Y-.js +0 -37
  431. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Bn6aCAy_.js +0 -1
  432. package/codeyam-cli/src/webserver/build/client/assets/files-DKyMFI90.js +0 -1
  433. package/codeyam-cli/src/webserver/build/client/assets/globals-DTTQ3gY7.css +0 -1
  434. package/codeyam-cli/src/webserver/build/client/assets/manifest-22590fcf.js +0 -1
  435. package/codeyam-cli/src/webserver/build/client/assets/root-BsAarjAM.js +0 -57
  436. package/codeyam-cli/src/webserver/build/server/assets/index-BND5I5fv.js +0 -1
  437. package/codeyam-cli/src/webserver/build/server/assets/server-build-CFXnd7MG.js +0 -228
@@ -79,6 +79,8 @@
79
79
  * - `helpers/README.md` - Overview of the helper module architecture
80
80
  */
81
81
  import fillInSchemaGapsAndUnknowns from "./helpers/fillInSchemaGapsAndUnknowns.js";
82
+ import { clearCleanKnownObjectFunctionsCache } from "./helpers/cleanKnownObjectFunctions.js";
83
+ import { clearCleanNonObjectFunctionsCache } from "./helpers/cleanNonObjectFunctions.js";
82
84
  /**
83
85
  * Patterns that indicate recursive type structures in schema paths.
84
86
  * Used by hasExcessivePatternRepetition() to detect exponential path blowup.
@@ -120,6 +122,17 @@ export function resetScopeDataStructureMetrics() {
120
122
  followEquivalenciesEarlyExitPhase1Count = 0;
121
123
  followEquivalenciesWithWorkCount = 0;
122
124
  addEquivalencyCallCount = 0;
125
+ // Clear module-level caches to prevent unbounded memory growth across entities
126
+ const knownObjectCache = clearCleanKnownObjectFunctionsCache();
127
+ const nonObjectCache = clearCleanNonObjectFunctionsCache();
128
+ if (knownObjectCache.count > 0 || nonObjectCache.count > 0) {
129
+ const totalBytes = knownObjectCache.estimatedBytes + nonObjectCache.estimatedBytes;
130
+ console.log('CodeYam: Cleared analysis caches', {
131
+ knownObjectCache: `${knownObjectCache.count} entries, ${(knownObjectCache.estimatedBytes / 1024).toFixed(1)}KB`,
132
+ nonObjectCache: `${nonObjectCache.count} entries, ${(nonObjectCache.estimatedBytes / 1024).toFixed(1)}KB`,
133
+ totalKB: `${(totalBytes / 1024).toFixed(1)}KB`,
134
+ });
135
+ }
123
136
  }
124
137
  // Performance: Pre-computed Sets for equivalency reason filtering (O(1) vs O(n))
125
138
  const ALLOWED_EQUIVALENCY_REASONS = new Set([
@@ -203,6 +216,11 @@ export class ScopeDataStructure {
203
216
  * Maps child component name to the conditions that must be true for it to render.
204
217
  */
205
218
  this.rawChildBoundaryGatingConditions = {};
219
+ /**
220
+ * JSX rendering usages collected during AST analysis.
221
+ * Tracks arrays rendered via .map() and strings interpolated in JSX.
222
+ */
223
+ this.rawJsxRenderingUsages = [];
206
224
  this.lastAddToSchemaId = 0;
207
225
  this.lastEquivalencyId = 0;
208
226
  this.lastEquivalencyDatabaseId = 0;
@@ -1070,21 +1088,36 @@ export class ScopeDataStructure {
1070
1088
  }
1071
1089
  setInstantiatedVariables(scopeNode) {
1072
1090
  let instantiatedVariables = scopeNode.analysis?.instantiatedVariables ?? [];
1073
- for (const [path, equivalentPath] of Object.entries(scopeNode.analysis.isolatedEquivalentVariables ?? {})) {
1074
- if (typeof equivalentPath !== 'string') {
1075
- continue;
1076
- }
1077
- if (equivalentPath.startsWith('signature[')) {
1078
- const equivalentPathParts = this.splitPath(equivalentPath);
1079
- instantiatedVariables.push(equivalentPathParts[0]);
1080
- instantiatedVariables.push(path);
1091
+ for (const [path, rawEquivalentPath] of Object.entries(scopeNode.analysis.isolatedEquivalentVariables ?? {})) {
1092
+ // Normalize to array for consistent handling (supports both string and string[])
1093
+ const equivalentPaths = Array.isArray(rawEquivalentPath)
1094
+ ? rawEquivalentPath
1095
+ : rawEquivalentPath
1096
+ ? [rawEquivalentPath]
1097
+ : [];
1098
+ for (const equivalentPath of equivalentPaths) {
1099
+ if (typeof equivalentPath !== 'string') {
1100
+ continue;
1101
+ }
1102
+ if (equivalentPath.startsWith('signature[')) {
1103
+ const equivalentPathParts = this.splitPath(equivalentPath);
1104
+ instantiatedVariables.push(equivalentPathParts[0]);
1105
+ instantiatedVariables.push(path);
1106
+ }
1081
1107
  }
1082
1108
  const duplicateInstantiated = instantiatedVariables.find((v) => path.split('::cyDuplicateKey')[0] === v.split('::cyDuplicateKey')[0]);
1083
1109
  if (duplicateInstantiated) {
1084
1110
  instantiatedVariables.push(path);
1085
1111
  }
1086
1112
  }
1087
- instantiatedVariables = instantiatedVariables.filter((varName, index, self) => self.indexOf(varName) === index);
1113
+ const instantiatedSeen = new Set();
1114
+ instantiatedVariables = instantiatedVariables.filter((varName) => {
1115
+ if (instantiatedSeen.has(varName)) {
1116
+ return false;
1117
+ }
1118
+ instantiatedSeen.add(varName);
1119
+ return true;
1120
+ });
1088
1121
  scopeNode.instantiatedVariables = instantiatedVariables;
1089
1122
  if (!scopeNode.tree || scopeNode.tree.length === 0) {
1090
1123
  return;
@@ -1096,9 +1129,16 @@ export class ScopeDataStructure {
1096
1129
  const parentInstantiatedVariables = [
1097
1130
  ...(parentScopeNode.parentInstantiatedVariables ?? []),
1098
1131
  ...parentScopeNode.instantiatedVariables.filter((v) => !v.startsWith('signature[') && !v.startsWith('returnValue')),
1099
- ].filter((varName, index, self) => !instantiatedVariables.includes(varName) &&
1100
- self.indexOf(varName) === index);
1101
- scopeNode.parentInstantiatedVariables = parentInstantiatedVariables;
1132
+ ].filter((varName) => !instantiatedSeen.has(varName));
1133
+ const parentInstantiatedSeen = new Set();
1134
+ const dedupedParentInstantiatedVariables = parentInstantiatedVariables.filter((varName) => {
1135
+ if (parentInstantiatedSeen.has(varName)) {
1136
+ return false;
1137
+ }
1138
+ parentInstantiatedSeen.add(varName);
1139
+ return true;
1140
+ });
1141
+ scopeNode.parentInstantiatedVariables = dedupedParentInstantiatedVariables;
1102
1142
  }
1103
1143
  trackFunctionCalls(scopeNode) {
1104
1144
  this.captureFunctionCalls(scopeNode);
@@ -1109,116 +1149,136 @@ export class ScopeDataStructure {
1109
1149
  return;
1110
1150
  }
1111
1151
  const { isolatedStructure, isolatedEquivalentVariables } = scopeNode.analysis;
1152
+ // Flatten isolatedEquivalentVariables values for allPaths (handles both string and string[])
1153
+ const flattenedEquivValues = Object.values(isolatedEquivalentVariables || {}).flatMap((v) => (Array.isArray(v) ? v : [v]));
1112
1154
  const allPaths = Array.from(new Set([
1113
1155
  ...Object.keys(isolatedStructure || {}),
1114
1156
  ...Object.keys(isolatedEquivalentVariables || {}),
1115
- ...Object.values(isolatedEquivalentVariables || {}),
1157
+ ...flattenedEquivValues,
1116
1158
  ]));
1117
1159
  for (let path in isolatedEquivalentVariables) {
1118
- let equivalentValue = isolatedEquivalentVariables?.[path];
1119
- if (equivalentValue && this.isValidPath(equivalentValue)) {
1120
- // IMPORTANT: DO NOT strip ::cyDuplicateKey:: markers from equivalencies.
1121
- // These markers are critical for distinguishing variable reassignments.
1122
- // For example, with:
1123
- // let fetcher = useFetcher<ConfigData>();
1124
- // const configData = fetcher.data?.data;
1125
- // fetcher = useFetcher<SettingsData>();
1126
- // const settingsData = fetcher.data?.data;
1127
- //
1128
- // mergeStatements creates:
1129
- // fetcher useFetcher<ConfigData>()...
1130
- // fetcher::cyDuplicateKey1:: useFetcher<SettingsData>()...
1131
- // configData fetcher.data.data
1132
- // settingsData → fetcher::cyDuplicateKey1::.data.data
1133
- //
1134
- // If we strip ::cyDuplicateKey::, settingsData would incorrectly trace
1135
- // to useFetcher<ConfigData>() instead of useFetcher<SettingsData>().
1136
- path = cleanPath(path, allPaths);
1137
- equivalentValue = cleanPath(equivalentValue, allPaths);
1138
- this.addEquivalency(path, equivalentValue, scopeNode.name, scopeNode, 'original equivalency');
1139
- // Propagate equivalencies involving parent-scope variables to those parent scopes.
1140
- // This handles patterns like: collected.push({...entity}) where 'collected' is defined
1141
- // in a parent scope. The equivalency collected[] -> push().signature[0] needs to be
1142
- // visible when tracing from the parent scope.
1143
- const rootVariable = this.extractRootVariable(path);
1144
- const equivalentRootVariable = this.extractRootVariable(equivalentValue);
1145
- // Skip propagation for self-referential reassignment patterns like:
1146
- // x = x.method().functionCallReturnValue
1147
- // where the path IS the variable itself (not a sub-path like x[] or x.prop).
1148
- // These create circular references since both sides reference the same variable.
1149
- //
1150
- // But DO propagate for patterns like collected[] -> collected.push(...).signature[0]
1151
- // where the path has additional segments beyond the root variable.
1152
- const pathIsJustRootVariable = path === rootVariable;
1153
- const isSelfReferentialReassignment = pathIsJustRootVariable && rootVariable === equivalentRootVariable;
1154
- if (rootVariable &&
1155
- !isSelfReferentialReassignment &&
1156
- scopeNode.parentInstantiatedVariables?.includes(rootVariable)) {
1157
- // Find the parent scope where this variable is defined
1158
- for (const parentScopeName of scopeNode.tree || []) {
1159
- const parentScope = this.scopeNodes[parentScopeName];
1160
- if (parentScope?.instantiatedVariables?.includes(rootVariable)) {
1161
- // Add the equivalency to the parent scope as well
1162
- this.addEquivalency(path, equivalentValue, scopeNode.name, // The equivalent path's scope remains the child scope
1163
- parentScope, // But store it in the parent scope's equivalencies
1164
- 'propagated parent-variable equivalency');
1165
- break;
1160
+ const rawEquivalentValue = isolatedEquivalentVariables?.[path];
1161
+ // Normalize to array for consistent handling
1162
+ const equivalentValues = Array.isArray(rawEquivalentValue)
1163
+ ? rawEquivalentValue
1164
+ : [rawEquivalentValue];
1165
+ for (let equivalentValue of equivalentValues) {
1166
+ if (equivalentValue && this.isValidPath(equivalentValue)) {
1167
+ // IMPORTANT: DO NOT strip ::cyDuplicateKey:: markers from equivalencies.
1168
+ // These markers are critical for distinguishing variable reassignments.
1169
+ // For example, with:
1170
+ // let fetcher = useFetcher<ConfigData>();
1171
+ // const configData = fetcher.data?.data;
1172
+ // fetcher = useFetcher<SettingsData>();
1173
+ // const settingsData = fetcher.data?.data;
1174
+ //
1175
+ // mergeStatements creates:
1176
+ // fetcher useFetcher<ConfigData>()...
1177
+ // fetcher::cyDuplicateKey1:: useFetcher<SettingsData>()...
1178
+ // configData fetcher.data.data
1179
+ // settingsData fetcher::cyDuplicateKey1::.data.data
1180
+ //
1181
+ // If we strip ::cyDuplicateKey::, settingsData would incorrectly trace
1182
+ // to useFetcher<ConfigData>() instead of useFetcher<SettingsData>().
1183
+ path = cleanPath(path, allPaths);
1184
+ equivalentValue = cleanPath(equivalentValue, allPaths);
1185
+ this.addEquivalency(path, equivalentValue, scopeNode.name, scopeNode, 'original equivalency');
1186
+ // Propagate equivalencies involving parent-scope variables to those parent scopes.
1187
+ // This handles patterns like: collected.push({...entity}) where 'collected' is defined
1188
+ // in a parent scope. The equivalency collected[] -> push().signature[0] needs to be
1189
+ // visible when tracing from the parent scope.
1190
+ const rootVariable = this.extractRootVariable(path);
1191
+ const equivalentRootVariable = this.extractRootVariable(equivalentValue);
1192
+ // Skip propagation for self-referential reassignment patterns like:
1193
+ // x = x.method().functionCallReturnValue
1194
+ // where the path IS the variable itself (not a sub-path like x[] or x.prop).
1195
+ // These create circular references since both sides reference the same variable.
1196
+ //
1197
+ // But DO propagate for patterns like collected[] -> collected.push(...).signature[0]
1198
+ // where the path has additional segments beyond the root variable.
1199
+ const pathIsJustRootVariable = path === rootVariable;
1200
+ const isSelfReferentialReassignment = pathIsJustRootVariable && rootVariable === equivalentRootVariable;
1201
+ if (rootVariable &&
1202
+ !isSelfReferentialReassignment &&
1203
+ scopeNode.parentInstantiatedVariables?.includes(rootVariable)) {
1204
+ // Find the parent scope where this variable is defined
1205
+ for (const parentScopeName of scopeNode.tree || []) {
1206
+ const parentScope = this.scopeNodes[parentScopeName];
1207
+ if (parentScope?.instantiatedVariables?.includes(rootVariable)) {
1208
+ // Add the equivalency to the parent scope as well
1209
+ this.addEquivalency(path, equivalentValue, scopeNode.name, // The equivalent path's scope remains the child scope
1210
+ parentScope, // But store it in the parent scope's equivalencies
1211
+ 'propagated parent-variable equivalency');
1212
+ break;
1213
+ }
1166
1214
  }
1167
1215
  }
1168
- }
1169
- // Propagate sub-property equivalencies when the equivalentValue is a simple variable
1170
- // that has sub-properties defined in the isolatedEquivalentVariables.
1171
- // This handles cases like: dataItem={{ structure: completeDataStructure }}
1172
- // where completeDataStructure has sub-properties like completeDataStructure['Function Arguments']
1173
- // We need to propagate these to create: dataItem.structure['Function Arguments'] equivalencies
1174
- const isSimpleVariable = !equivalentValue.startsWith('signature[') &&
1175
- !equivalentValue.includes('functionCallReturnValue') &&
1176
- !equivalentValue.includes('.') &&
1177
- !equivalentValue.includes('[');
1178
- if (isSimpleVariable) {
1179
- // Look in current scope and all parent scopes for sub-properties
1180
- const scopesToCheck = [scopeNode.name, ...scopeNode.tree];
1181
- for (const scopeName of scopesToCheck) {
1182
- const checkScope = this.scopeNodes[scopeName];
1183
- if (!checkScope?.analysis?.isolatedEquivalentVariables)
1184
- continue;
1185
- for (const [subPath, subValue] of Object.entries(checkScope.analysis.isolatedEquivalentVariables)) {
1186
- // Check if this is a sub-property of the equivalentValue variable
1187
- // e.g., completeDataStructure['Function Arguments'] or completeDataStructure.foo
1188
- const matchesDot = subPath.startsWith(equivalentValue + '.');
1189
- const matchesBracket = subPath.startsWith(equivalentValue + '[');
1190
- if (matchesDot || matchesBracket) {
1191
- const subPropertyPath = subPath.substring(equivalentValue.length);
1192
- const newPath = cleanPath(path + subPropertyPath, allPaths);
1193
- const newEquivalentValue = cleanPath(subValue.replace(/::cyDuplicateKey\d+::/g, ''), allPaths);
1194
- if (newEquivalentValue &&
1195
- this.isValidPath(newEquivalentValue)) {
1196
- this.addEquivalency(newPath, newEquivalentValue, checkScope.name, // Use the scope where the sub-property was found
1197
- scopeNode, 'propagated sub-property equivalency');
1216
+ // Propagate sub-property equivalencies when the equivalentValue is a simple variable
1217
+ // that has sub-properties defined in the isolatedEquivalentVariables.
1218
+ // This handles cases like: dataItem={{ structure: completeDataStructure }}
1219
+ // where completeDataStructure has sub-properties like completeDataStructure['Function Arguments']
1220
+ // We need to propagate these to create: dataItem.structure['Function Arguments'] equivalencies
1221
+ const isSimpleVariable = !equivalentValue.startsWith('signature[') &&
1222
+ !equivalentValue.includes('functionCallReturnValue') &&
1223
+ !equivalentValue.includes('.') &&
1224
+ !equivalentValue.includes('[');
1225
+ if (isSimpleVariable) {
1226
+ // Look in current scope and all parent scopes for sub-properties
1227
+ const scopesToCheck = [scopeNode.name, ...scopeNode.tree];
1228
+ for (const scopeName of scopesToCheck) {
1229
+ const checkScope = this.scopeNodes[scopeName];
1230
+ if (!checkScope?.analysis?.isolatedEquivalentVariables)
1231
+ continue;
1232
+ for (const [subPath, rawSubValue] of Object.entries(checkScope.analysis.isolatedEquivalentVariables)) {
1233
+ // Normalize to array for consistent handling
1234
+ const subValues = Array.isArray(rawSubValue)
1235
+ ? rawSubValue
1236
+ : rawSubValue
1237
+ ? [rawSubValue]
1238
+ : [];
1239
+ // Check if this is a sub-property of the equivalentValue variable
1240
+ // e.g., completeDataStructure['Function Arguments'] or completeDataStructure.foo
1241
+ const matchesDot = subPath.startsWith(equivalentValue + '.');
1242
+ const matchesBracket = subPath.startsWith(equivalentValue + '[');
1243
+ if (matchesDot || matchesBracket) {
1244
+ const subPropertyPath = subPath.substring(equivalentValue.length);
1245
+ const newPath = cleanPath(path + subPropertyPath, allPaths);
1246
+ for (const subValue of subValues) {
1247
+ if (typeof subValue !== 'string')
1248
+ continue;
1249
+ const newEquivalentValue = cleanPath(subValue.replace(/::cyDuplicateKey\d+::/g, ''), allPaths);
1250
+ if (newEquivalentValue &&
1251
+ this.isValidPath(newEquivalentValue)) {
1252
+ this.addEquivalency(newPath, newEquivalentValue, checkScope.name, // Use the scope where the sub-property was found
1253
+ scopeNode, 'propagated sub-property equivalency');
1254
+ }
1255
+ }
1256
+ }
1257
+ // Also check if equivalentValue itself maps to a functionCallReturnValue
1258
+ // e.g., result = useMemo(...).functionCallReturnValue
1259
+ for (const subValue of subValues) {
1260
+ if (subPath === equivalentValue &&
1261
+ typeof subValue === 'string' &&
1262
+ subValue.endsWith('.functionCallReturnValue')) {
1263
+ this.propagateFunctionCallReturnSubProperties(path, subValue, scopeNode, allPaths);
1264
+ }
1198
1265
  }
1199
- }
1200
- // Also check if equivalentValue itself maps to a functionCallReturnValue
1201
- // e.g., result = useMemo(...).functionCallReturnValue
1202
- if (subPath === equivalentValue &&
1203
- typeof subValue === 'string' &&
1204
- subValue.endsWith('.functionCallReturnValue')) {
1205
- this.propagateFunctionCallReturnSubProperties(path, subValue, scopeNode, allPaths);
1206
1266
  }
1207
1267
  }
1208
1268
  }
1209
- }
1210
- // Handle function call return values by propagating returnValue.* sub-properties
1211
- // from the callback scope to the usage path
1212
- if (equivalentValue.endsWith('.functionCallReturnValue')) {
1213
- this.propagateFunctionCallReturnSubProperties(path, equivalentValue, scopeNode, allPaths);
1214
- // Track which variable receives the return value of each function call
1215
- // This enables generating separate mock data for each call site
1216
- this.trackReceivingVariable(path, equivalentValue);
1217
- }
1218
- // Also track variables that receive destructured properties from function call return values
1219
- // e.g., "userData" -> "db.query('users').functionCallReturnValue.data"
1220
- if (equivalentValue.includes('.functionCallReturnValue.')) {
1221
- this.trackReceivingVariable(path, equivalentValue);
1269
+ // Handle function call return values by propagating returnValue.* sub-properties
1270
+ // from the callback scope to the usage path
1271
+ if (equivalentValue.endsWith('.functionCallReturnValue')) {
1272
+ this.propagateFunctionCallReturnSubProperties(path, equivalentValue, scopeNode, allPaths);
1273
+ // Track which variable receives the return value of each function call
1274
+ // This enables generating separate mock data for each call site
1275
+ this.trackReceivingVariable(path, equivalentValue);
1276
+ }
1277
+ // Also track variables that receive destructured properties from function call return values
1278
+ // e.g., "userData" -> "db.query('users').functionCallReturnValue.data"
1279
+ if (equivalentValue.includes('.functionCallReturnValue.')) {
1280
+ this.trackReceivingVariable(path, equivalentValue);
1281
+ }
1222
1282
  }
1223
1283
  }
1224
1284
  }
@@ -1362,8 +1422,15 @@ export class ScopeDataStructure {
1362
1422
  const checkScope = this.scopeNodes[scopeName];
1363
1423
  if (!checkScope?.analysis?.isolatedEquivalentVariables)
1364
1424
  continue;
1365
- const functionRef = checkScope.analysis.isolatedEquivalentVariables[functionName];
1366
- if (typeof functionRef === 'string' && functionRef.endsWith('F')) {
1425
+ const rawFunctionRef = checkScope.analysis.isolatedEquivalentVariables[functionName];
1426
+ // Normalize to array and find first string ending with 'F'
1427
+ const functionRefs = Array.isArray(rawFunctionRef)
1428
+ ? rawFunctionRef
1429
+ : rawFunctionRef
1430
+ ? [rawFunctionRef]
1431
+ : [];
1432
+ const functionRef = functionRefs.find((r) => typeof r === 'string' && r.endsWith('F'));
1433
+ if (typeof functionRef === 'string') {
1367
1434
  callbackScopeName = functionRef.slice(0, -1);
1368
1435
  break;
1369
1436
  }
@@ -1386,22 +1453,32 @@ export class ScopeDataStructure {
1386
1453
  if (!callbackScope.analysis?.isolatedEquivalentVariables)
1387
1454
  return;
1388
1455
  const isolatedVars = callbackScope.analysis.isolatedEquivalentVariables;
1456
+ // Get the first returnValue equivalency (normalize array to single value for these checks)
1457
+ const rawReturnValue = isolatedVars.returnValue;
1458
+ const firstReturnValue = Array.isArray(rawReturnValue)
1459
+ ? rawReturnValue[0]
1460
+ : rawReturnValue;
1389
1461
  // First, check if returnValue is an alias to another variable (e.g., returnValue = intermediate)
1390
1462
  // If so, we need to look for that variable's sub-properties too
1391
- const returnValueAlias = typeof isolatedVars.returnValue === 'string' &&
1392
- !isolatedVars.returnValue.includes('.')
1393
- ? isolatedVars.returnValue
1463
+ const returnValueAlias = typeof firstReturnValue === 'string' && !firstReturnValue.includes('.')
1464
+ ? firstReturnValue
1394
1465
  : undefined;
1395
1466
  // Pattern 3: Object.keys(X).reduce() - the reduce result has the same sub-properties as X
1396
1467
  // When returnValue = "Object.keys(source).reduce(...).functionCallReturnValue", look for source.* sub-properties
1397
1468
  let reduceSourceVar;
1398
- if (typeof isolatedVars.returnValue === 'string') {
1399
- const reduceMatch = isolatedVars.returnValue.match(/^Object\.keys\((\w+)\)\.reduce\(.*\)\.functionCallReturnValue$/);
1469
+ if (typeof firstReturnValue === 'string') {
1470
+ const reduceMatch = firstReturnValue.match(/^Object\.keys\((\w+)\)\.reduce\(.*\)\.functionCallReturnValue$/);
1400
1471
  if (reduceMatch) {
1401
1472
  reduceSourceVar = reduceMatch[1];
1402
1473
  }
1403
1474
  }
1404
- for (const [subPath, subValue] of Object.entries(isolatedVars)) {
1475
+ for (const [subPath, rawSubValue] of Object.entries(isolatedVars)) {
1476
+ // Normalize to array for consistent handling
1477
+ const subValues = Array.isArray(rawSubValue)
1478
+ ? rawSubValue
1479
+ : rawSubValue
1480
+ ? [rawSubValue]
1481
+ : [];
1405
1482
  // Check for direct returnValue.* sub-properties
1406
1483
  const isReturnValueSub = subPath.startsWith('returnValue.') ||
1407
1484
  subPath.startsWith('returnValue[');
@@ -1413,33 +1490,36 @@ export class ScopeDataStructure {
1413
1490
  const isReduceSourceSub = reduceSourceVar &&
1414
1491
  (subPath.startsWith(reduceSourceVar + '.') ||
1415
1492
  subPath.startsWith(reduceSourceVar + '['));
1416
- if (typeof subValue !== 'string' ||
1417
- (!isReturnValueSub && !isAliasSub && !isReduceSourceSub))
1493
+ if (!isReturnValueSub && !isAliasSub && !isReduceSourceSub)
1418
1494
  continue;
1419
- // Convert alias/reduceSource paths to returnValue paths
1420
- let effectiveSubPath = subPath;
1421
- if (isAliasSub && !isReturnValueSub) {
1422
- // Replace the alias prefix with returnValue
1423
- effectiveSubPath =
1424
- 'returnValue' + subPath.substring(returnValueAlias.length);
1425
- }
1426
- else if (isReduceSourceSub && !isReturnValueSub && !isAliasSub) {
1427
- // Replace the reduce source prefix with returnValue
1428
- effectiveSubPath =
1429
- 'returnValue' + subPath.substring(reduceSourceVar.length);
1430
- }
1431
- const subPropertyPath = effectiveSubPath.substring('returnValue'.length);
1432
- const newPath = cleanPath(path + subPropertyPath, allPaths);
1433
- let newEquivalentValue = cleanPath(subValue.replace(/::cyDuplicateKey\d+::/g, ''), allPaths);
1434
- // Resolve variable references through parent scope equivalencies
1435
- const resolved = this.resolveVariableThroughParentScopes(newEquivalentValue, callbackScope, allPaths);
1436
- newEquivalentValue = resolved.resolvedPath;
1437
- const equivalentScopeName = resolved.scopeName;
1438
- if (!newEquivalentValue || !this.isValidPath(newEquivalentValue))
1439
- continue;
1440
- this.addEquivalency(newPath, newEquivalentValue, equivalentScopeName, scopeNode, 'propagated function call return sub-property equivalency');
1441
- // Ensure the database entry has the usage path
1442
- this.addUsageToEquivalencyDatabaseEntry(newPath, newEquivalentValue, equivalentScopeName, scopeNode.name);
1495
+ for (const subValue of subValues) {
1496
+ if (typeof subValue !== 'string')
1497
+ continue;
1498
+ // Convert alias/reduceSource paths to returnValue paths
1499
+ let effectiveSubPath = subPath;
1500
+ if (isAliasSub && !isReturnValueSub) {
1501
+ // Replace the alias prefix with returnValue
1502
+ effectiveSubPath =
1503
+ 'returnValue' + subPath.substring(returnValueAlias.length);
1504
+ }
1505
+ else if (isReduceSourceSub && !isReturnValueSub && !isAliasSub) {
1506
+ // Replace the reduce source prefix with returnValue
1507
+ effectiveSubPath =
1508
+ 'returnValue' + subPath.substring(reduceSourceVar.length);
1509
+ }
1510
+ const subPropertyPath = effectiveSubPath.substring('returnValue'.length);
1511
+ const newPath = cleanPath(path + subPropertyPath, allPaths);
1512
+ let newEquivalentValue = cleanPath(subValue.replace(/::cyDuplicateKey\d+::/g, ''), allPaths);
1513
+ // Resolve variable references through parent scope equivalencies
1514
+ const resolved = this.resolveVariableThroughParentScopes(newEquivalentValue, callbackScope, allPaths);
1515
+ newEquivalentValue = resolved.resolvedPath;
1516
+ const equivalentScopeName = resolved.scopeName;
1517
+ if (!newEquivalentValue || !this.isValidPath(newEquivalentValue))
1518
+ continue;
1519
+ this.addEquivalency(newPath, newEquivalentValue, equivalentScopeName, scopeNode, 'propagated function call return sub-property equivalency');
1520
+ // Ensure the database entry has the usage path
1521
+ this.addUsageToEquivalencyDatabaseEntry(newPath, newEquivalentValue, equivalentScopeName, scopeNode.name);
1522
+ }
1443
1523
  }
1444
1524
  }
1445
1525
  /**
@@ -1471,7 +1551,14 @@ export class ScopeDataStructure {
1471
1551
  const parentScope = this.scopeNodes[parentScopeName];
1472
1552
  if (!parentScope?.analysis?.isolatedEquivalentVariables)
1473
1553
  continue;
1474
- const rootEquiv = parentScope.analysis.isolatedEquivalentVariables[rootVar];
1554
+ const rawRootEquiv = parentScope.analysis.isolatedEquivalentVariables[rootVar];
1555
+ // Normalize to array and use first string value
1556
+ const rootEquivs = Array.isArray(rawRootEquiv)
1557
+ ? rawRootEquiv
1558
+ : rawRootEquiv
1559
+ ? [rawRootEquiv]
1560
+ : [];
1561
+ const rootEquiv = rootEquivs.find((r) => typeof r === 'string');
1475
1562
  if (typeof rootEquiv === 'string') {
1476
1563
  return {
1477
1564
  resolvedPath: cleanPath(rootEquiv + restOfPath, allPaths),
@@ -1950,9 +2037,70 @@ export class ScopeDataStructure {
1950
2037
  // Update inverted index
1951
2038
  this.intermediatesOrderIndex.set(pathId, databaseEntry);
1952
2039
  if (intermediateIndex === 0) {
1953
- const isValidSourceCandidate = pathInfo.schemaPath.startsWith('signature[') ||
2040
+ let isValidSourceCandidate = pathInfo.schemaPath.startsWith('signature[') ||
1954
2041
  pathInfo.schemaPath.includes('functionCallReturnValue');
1955
- if (isValidSourceCandidate) {
2042
+ // Check if path STARTS with a spread pattern like [...var]
2043
+ // This handles cases like [...files][][0] or [...files].sort(...).functionCallReturnValue[][0]
2044
+ // where the spread source variable needs to be resolved to a signature path.
2045
+ // We do this REGARDLESS of isValidSourceCandidate because even paths containing
2046
+ // functionCallReturnValue may need spread resolution to trace back to the signature.
2047
+ const spreadMatch = pathInfo.schemaPath.match(/^\[\.\.\.(\w+)\]/);
2048
+ if (spreadMatch) {
2049
+ const spreadVar = spreadMatch[1];
2050
+ const spreadPattern = spreadMatch[0]; // The full [...var] match
2051
+ const scopeNode = this.scopeNodes[pathInfo.scopeNodeName];
2052
+ if (scopeNode?.equivalencies) {
2053
+ // Follow the equivalency chain to find a signature path
2054
+ // e.g., files (cyScope1) → files (root) → signature[0].files
2055
+ const resolveToSignature = (varName, currentScopeName, visited) => {
2056
+ const visitKey = `${currentScopeName}::${varName}`;
2057
+ if (visited.has(visitKey))
2058
+ return null;
2059
+ visited.add(visitKey);
2060
+ const currentScope = this.scopeNodes[currentScopeName];
2061
+ if (!currentScope?.equivalencies)
2062
+ return null;
2063
+ const varEquivs = currentScope.equivalencies[varName];
2064
+ if (!varEquivs)
2065
+ return null;
2066
+ // First check if any equivalency directly points to a signature path
2067
+ const signatureEquiv = varEquivs.find((eq) => eq.schemaPath.startsWith('signature['));
2068
+ if (signatureEquiv) {
2069
+ return signatureEquiv;
2070
+ }
2071
+ // Otherwise, follow the chain to other scopes
2072
+ for (const equiv of varEquivs) {
2073
+ // If the equivalency points to the same variable in a different scope,
2074
+ // follow the chain
2075
+ if (equiv.schemaPath === varName &&
2076
+ equiv.scopeNodeName !== currentScopeName) {
2077
+ const result = resolveToSignature(varName, equiv.scopeNodeName, visited);
2078
+ if (result)
2079
+ return result;
2080
+ }
2081
+ }
2082
+ return null;
2083
+ };
2084
+ const signatureEquiv = resolveToSignature(spreadVar, pathInfo.scopeNodeName, new Set());
2085
+ if (signatureEquiv) {
2086
+ // Replace ONLY the [...var] part with the resolved signature path
2087
+ // This preserves any suffix like .sort(...).functionCallReturnValue[][0]
2088
+ const resolvedPath = pathInfo.schemaPath.replace(spreadPattern, signatureEquiv.schemaPath);
2089
+ // Add the resolved path as a source candidate
2090
+ if (!databaseEntry.sourceCandidates.some((sc) => sc.schemaPath === resolvedPath &&
2091
+ sc.scopeNodeName === pathInfo.scopeNodeName)) {
2092
+ databaseEntry.sourceCandidates.push({
2093
+ scopeNodeName: pathInfo.scopeNodeName,
2094
+ schemaPath: resolvedPath,
2095
+ });
2096
+ }
2097
+ isValidSourceCandidate = true;
2098
+ }
2099
+ }
2100
+ }
2101
+ if (isValidSourceCandidate &&
2102
+ !databaseEntry.sourceCandidates.some((sc) => sc.schemaPath === pathInfo.schemaPath &&
2103
+ sc.scopeNodeName === pathInfo.scopeNodeName)) {
1956
2104
  databaseEntry.sourceCandidates.push(pathInfo);
1957
2105
  }
1958
2106
  }
@@ -2362,15 +2510,131 @@ export class ScopeDataStructure {
2362
2510
  if (!scopeNode) {
2363
2511
  return {};
2364
2512
  }
2365
- const entries = this.equivalencyDatabase.filter((entry) => entry.usages.some((usage) => usage.scopeNodeName === scopeNode.name));
2513
+ // Collect all descendant scope names (including the scope itself)
2514
+ // This ensures we include external calls from nested scopes like cyScope2
2515
+ const getAllDescendantScopeNames = (node) => {
2516
+ const names = new Set([node.name]);
2517
+ for (const child of node.children) {
2518
+ for (const name of getAllDescendantScopeNames(child)) {
2519
+ names.add(name);
2520
+ }
2521
+ }
2522
+ return names;
2523
+ };
2524
+ const treeNode = this.scopeTreeManager.findNode(scopeNode.name);
2525
+ const descendantScopeNames = treeNode
2526
+ ? getAllDescendantScopeNames(treeNode)
2527
+ : new Set([scopeNode.name]);
2528
+ // Get all external function calls made from this scope or any descendant scope
2529
+ // This allows us to include prop equivalencies from JSX components
2530
+ // that were rendered in nested scopes (e.g., FileTableRow called from cyScope2)
2531
+ const externalCallsFromScope = this.externalFunctionCalls.filter((efc) => descendantScopeNames.has(efc.callScope));
2532
+ const externalCallNames = new Set(externalCallsFromScope.map((efc) => efc.name));
2533
+ // Helper to check if a usage belongs to this scope (directly, via descendant, or via external call)
2534
+ const usageMatchesScope = (usage) => descendantScopeNames.has(usage.scopeNodeName) ||
2535
+ externalCallNames.has(usage.scopeNodeName);
2536
+ const entries = this.equivalencyDatabase.filter((entry) => entry.usages.some(usageMatchesScope));
2537
+ // Helper to resolve a source candidate through equivalency chains to find signature paths
2538
+ const resolveToSignature = (source, visited) => {
2539
+ const visitKey = `${source.scopeNodeName}::${source.schemaPath}`;
2540
+ if (visited.has(visitKey))
2541
+ return [];
2542
+ visited.add(visitKey);
2543
+ // If already a signature path, return as-is
2544
+ if (source.schemaPath.startsWith('signature[')) {
2545
+ return [source];
2546
+ }
2547
+ const currentScope = this.scopeNodes[source.scopeNodeName];
2548
+ if (!currentScope?.equivalencies)
2549
+ return [source];
2550
+ // Check for direct equivalencies FIRST (full path match)
2551
+ // This ensures paths like "useMemo(...).functionCallReturnValue" follow to "cyScope1::returnValue"
2552
+ // before prefix matching tries "useMemo(...)" which goes to the useMemo scope
2553
+ const directEquivs = currentScope.equivalencies[source.schemaPath];
2554
+ if (directEquivs?.length > 0) {
2555
+ const results = [];
2556
+ for (const equiv of directEquivs) {
2557
+ const resolved = resolveToSignature({
2558
+ scopeNodeName: equiv.scopeNodeName,
2559
+ schemaPath: equiv.schemaPath,
2560
+ }, visited);
2561
+ results.push(...resolved);
2562
+ }
2563
+ if (results.length > 0)
2564
+ return results;
2565
+ }
2566
+ // Handle spread patterns like [...items].sort().functionCallReturnValue
2567
+ // Extract the spread variable and resolve it through the equivalency chain
2568
+ const spreadMatch = source.schemaPath.match(/^\[\.\.\.(\w+)\]/);
2569
+ if (spreadMatch) {
2570
+ const spreadVar = spreadMatch[1];
2571
+ const spreadPattern = spreadMatch[0];
2572
+ const varEquivs = currentScope.equivalencies[spreadVar];
2573
+ if (varEquivs?.length > 0) {
2574
+ const results = [];
2575
+ for (const equiv of varEquivs) {
2576
+ // Follow the variable equivalency and then resolve from there
2577
+ const resolvedVar = resolveToSignature({
2578
+ scopeNodeName: equiv.scopeNodeName,
2579
+ schemaPath: equiv.schemaPath,
2580
+ }, visited);
2581
+ // For each resolved variable path, create the full path with array element suffix
2582
+ for (const rv of resolvedVar) {
2583
+ if (rv.schemaPath.startsWith('signature[')) {
2584
+ // Get the suffix after the spread pattern
2585
+ let suffix = source.schemaPath.slice(spreadPattern.length);
2586
+ // Clean the suffix: strip array method chains like .sort(...).functionCallReturnValue[]
2587
+ // These don't change the data identity, just transform it.
2588
+ // Keep only the final element access parts like [0], [1], etc.
2589
+ // Pattern: strip everything from a method call up through functionCallReturnValue[]
2590
+ suffix = suffix.replace(/\.\w+\([^)]*\)\.functionCallReturnValue\[\]/g, '');
2591
+ // Also handle simpler case without nested parens
2592
+ suffix = suffix.replace(/\.sort\(\w*\(\)\)\.functionCallReturnValue\[\]/g, '');
2593
+ // Add [] to indicate array element access from the spread
2594
+ const resolvedPath = rv.schemaPath + '[]' + suffix;
2595
+ results.push({
2596
+ scopeNodeName: rv.scopeNodeName,
2597
+ schemaPath: resolvedPath,
2598
+ });
2599
+ }
2600
+ }
2601
+ }
2602
+ if (results.length > 0)
2603
+ return results;
2604
+ }
2605
+ }
2606
+ // Try to find prefix equivalencies that can resolve this path
2607
+ // For path like "cyScope3().signature[0][0]", check "cyScope3().signature[0]", etc.
2608
+ const pathParts = this.splitPath(source.schemaPath);
2609
+ for (let i = pathParts.length - 1; i > 0; i--) {
2610
+ const prefix = this.joinPathParts(pathParts.slice(0, i));
2611
+ const suffix = this.joinPathParts(pathParts.slice(i));
2612
+ const prefixEquivs = currentScope.equivalencies[prefix];
2613
+ if (prefixEquivs?.length > 0) {
2614
+ const results = [];
2615
+ for (const equiv of prefixEquivs) {
2616
+ const newPath = this.joinPathParts([equiv.schemaPath, suffix]);
2617
+ const resolved = resolveToSignature({ scopeNodeName: equiv.scopeNodeName, schemaPath: newPath }, visited);
2618
+ results.push(...resolved);
2619
+ }
2620
+ if (results.length > 0)
2621
+ return results;
2622
+ }
2623
+ }
2624
+ return [source];
2625
+ };
2366
2626
  return entries.reduce((acc, entry) => {
2367
2627
  var _a;
2368
2628
  if (entry.sourceCandidates.length === 0)
2369
2629
  return acc;
2370
- const usages = entry.usages.filter((u) => u.scopeNodeName === scopeNode.name);
2630
+ const usages = entry.usages.filter(usageMatchesScope);
2371
2631
  for (const usage of usages) {
2372
2632
  acc[_a = usage.schemaPath] || (acc[_a] = []);
2373
- acc[usage.schemaPath].push(...entry.sourceCandidates);
2633
+ // Resolve each source candidate through the equivalency chain
2634
+ for (const source of entry.sourceCandidates) {
2635
+ const resolvedSources = resolveToSignature(source, new Set());
2636
+ acc[usage.schemaPath].push(...resolvedSources);
2637
+ }
2374
2638
  }
2375
2639
  return acc;
2376
2640
  }, {});
@@ -2659,13 +2923,35 @@ export class ScopeDataStructure {
2659
2923
  getEquivalentSignatureVariables() {
2660
2924
  const scopeNode = this.scopeNodes[this.scopeTreeManager.getRootName()];
2661
2925
  const equivalentSignatureVariables = {};
2926
+ // Helper to add equivalencies - accumulates into array if multiple values for same key
2927
+ // This is critical for OR expressions like `x = a || b` where x should map to both a and b
2928
+ const addEquivalency = (key, value) => {
2929
+ const existing = equivalentSignatureVariables[key];
2930
+ if (existing === undefined) {
2931
+ // First value - store as string
2932
+ equivalentSignatureVariables[key] = value;
2933
+ }
2934
+ else if (typeof existing === 'string') {
2935
+ if (existing !== value) {
2936
+ // Second different value - convert to array
2937
+ equivalentSignatureVariables[key] = [existing, value];
2938
+ }
2939
+ // Same value - no change needed
2940
+ }
2941
+ else {
2942
+ // Already an array - add if not already present
2943
+ if (!existing.includes(value)) {
2944
+ existing.push(value);
2945
+ }
2946
+ }
2947
+ };
2662
2948
  for (const [path, equivalentValues] of Object.entries(scopeNode.equivalencies)) {
2663
2949
  for (const equivalentValue of equivalentValues) {
2664
2950
  // Case 1: Props/signature equivalencies (existing behavior)
2665
2951
  // Maps local variable names to their signature paths
2666
2952
  // e.g., "propValue" -> "signature[0].prop"
2667
2953
  if (path.startsWith('signature[')) {
2668
- equivalentSignatureVariables[equivalentValue.schemaPath] = path;
2954
+ addEquivalency(equivalentValue.schemaPath, path);
2669
2955
  }
2670
2956
  // Case 2: Hook variable equivalencies (new behavior)
2671
2957
  // The equivalencies are stored as: path = variable name, schemaPath = data source
@@ -2675,11 +2961,25 @@ export class ScopeDataStructure {
2675
2961
  // "useFetcher<...>().state" for execution flow validation
2676
2962
  if (equivalentValue.schemaPath.endsWith('.functionCallReturnValue')) {
2677
2963
  // Extract the hook call path (everything before .functionCallReturnValue)
2678
- const hookCallPath = equivalentValue.schemaPath.slice(0, -'.functionCallReturnValue'.length);
2964
+ let hookCallPath = equivalentValue.schemaPath.slice(0, -'.functionCallReturnValue'.length);
2679
2965
  // Only include if it looks like a hook call (contains parentheses)
2680
2966
  // and the variable name (path) is a simple identifier (no dots)
2681
2967
  if (hookCallPath.includes('(') && !path.includes('.')) {
2682
- equivalentSignatureVariables[path] = hookCallPath;
2968
+ // Special case: If hookCallPath is a callback scope (cyScope pattern),
2969
+ // trace through it to find what the callback actually returns.
2970
+ // This handles useState(() => { return prop; }) patterns.
2971
+ const cyScopeMatch = hookCallPath.match(/^(cyScope\d+)\(\)$/);
2972
+ if (cyScopeMatch) {
2973
+ // Use the equivalency database to trace the callback's return value
2974
+ // to its actual source (e.g., viewModeFromUrl -> segments -> params -> useParams)
2975
+ const dbEntry = this.getEquivalenciesDatabaseEntry(scopeNode.name, // Component scope
2976
+ path);
2977
+ if (dbEntry?.sourceCandidates?.length > 0) {
2978
+ // Use the traced source instead of the callback scope
2979
+ hookCallPath = dbEntry.sourceCandidates[0].schemaPath;
2980
+ }
2981
+ }
2982
+ addEquivalency(path, hookCallPath);
2683
2983
  }
2684
2984
  }
2685
2985
  // Case 3: Destructured variables from local variables
@@ -2691,10 +2991,8 @@ export class ScopeDataStructure {
2691
2991
  !equivalentValue.schemaPath.startsWith('signature[') && // not a signature path
2692
2992
  !equivalentValue.schemaPath.endsWith('.functionCallReturnValue') // not already handled above
2693
2993
  ) {
2694
- // Only add if we haven't already captured this variable in Case 1 or 2
2695
- if (!(path in equivalentSignatureVariables)) {
2696
- equivalentSignatureVariables[path] = equivalentValue.schemaPath;
2697
- }
2994
+ // Add equivalency (will accumulate if multiple values for OR expressions)
2995
+ addEquivalency(path, equivalentValue.schemaPath);
2698
2996
  }
2699
2997
  // Case 4: Child component prop mappings (Fix 22)
2700
2998
  // When parent renders <ChildComponent prop={value} />, we get equivalencies like:
@@ -2705,7 +3003,7 @@ export class ScopeDataStructure {
2705
3003
  if (path.includes('().signature[') &&
2706
3004
  !equivalentValue.schemaPath.includes('()') // schemaPath is a simple variable, not a function call
2707
3005
  ) {
2708
- equivalentSignatureVariables[path] = equivalentValue.schemaPath;
3006
+ addEquivalency(path, equivalentValue.schemaPath);
2709
3007
  }
2710
3008
  // Case 5: Destructured function parameters (Fix 25)
2711
3009
  // When a function has destructured props: function Comp({ propA, propB }: Props)
@@ -2718,7 +3016,7 @@ export class ScopeDataStructure {
2718
3016
  if (!path.includes('.') && // path is a simple identifier (destructured prop name)
2719
3017
  equivalentValue.schemaPath.startsWith('signature[') // schemaPath IS a signature path
2720
3018
  ) {
2721
- equivalentSignatureVariables[path] = equivalentValue.schemaPath;
3019
+ addEquivalency(path, equivalentValue.schemaPath);
2722
3020
  }
2723
3021
  // Case 7: Method calls on variables that result in .functionCallReturnValue (Fix 33)
2724
3022
  // When we have patterns like:
@@ -2730,8 +3028,7 @@ export class ScopeDataStructure {
2730
3028
  // segments -> useParams().functionCallReturnValue['*'].split('/').functionCallReturnValue
2731
3029
  if (!path.includes('.') && // path is a simple identifier
2732
3030
  equivalentValue.schemaPath.endsWith('.functionCallReturnValue') && // ends with function return
2733
- equivalentValue.schemaPath.includes('.') && // has property access (method call)
2734
- !(path in equivalentSignatureVariables) // not already captured
3031
+ equivalentValue.schemaPath.includes('.') // has property access (method call)
2735
3032
  ) {
2736
3033
  // Check if this looks like a method call on a variable (not a hook call)
2737
3034
  // Hook calls look like: hookName() or hookName<T>()
@@ -2742,7 +3039,7 @@ export class ScopeDataStructure {
2742
3039
  const parenPos = hookCallPath.indexOf('(');
2743
3040
  if (dotBeforeParen !== -1 && dotBeforeParen < parenPos) {
2744
3041
  // This is a method call like "splat.split('/')", not a hook call
2745
- equivalentSignatureVariables[path] = equivalentValue.schemaPath;
3042
+ addEquivalency(path, equivalentValue.schemaPath);
2746
3043
  }
2747
3044
  }
2748
3045
  }
@@ -2769,8 +3066,9 @@ export class ScopeDataStructure {
2769
3066
  !equivalentValue.schemaPath.includes('()') // schemaPath is a simple variable
2770
3067
  ) {
2771
3068
  // Only add if not already present from the root scope
3069
+ // Root scope values take precedence over child scope values
2772
3070
  if (!(path in equivalentSignatureVariables)) {
2773
- equivalentSignatureVariables[path] = equivalentValue.schemaPath;
3071
+ addEquivalency(path, equivalentValue.schemaPath);
2774
3072
  }
2775
3073
  }
2776
3074
  }
@@ -2780,9 +3078,72 @@ export class ScopeDataStructure {
2780
3078
  // E.g., analysis → currentEntityAnalysis → useLoaderData().functionCallReturnValue.currentEntityAnalysis
2781
3079
  // We need multiple passes because resolutions can depend on each other
2782
3080
  const maxIterations = 5; // Prevent infinite loops
3081
+ // Helper function to resolve a single source path using equivalencies
3082
+ const resolveSourcePath = (sourcePath, equivMap) => {
3083
+ // Extract base variable from the path
3084
+ const dotIndex = sourcePath.indexOf('.');
3085
+ const bracketIndex = sourcePath.indexOf('[');
3086
+ let baseVar;
3087
+ let rest;
3088
+ if (dotIndex === -1 && bracketIndex === -1) {
3089
+ baseVar = sourcePath;
3090
+ rest = '';
3091
+ }
3092
+ else if (dotIndex === -1) {
3093
+ baseVar = sourcePath.slice(0, bracketIndex);
3094
+ rest = sourcePath.slice(bracketIndex);
3095
+ }
3096
+ else if (bracketIndex === -1) {
3097
+ baseVar = sourcePath.slice(0, dotIndex);
3098
+ rest = sourcePath.slice(dotIndex);
3099
+ }
3100
+ else {
3101
+ const firstIndex = Math.min(dotIndex, bracketIndex);
3102
+ baseVar = sourcePath.slice(0, firstIndex);
3103
+ rest = sourcePath.slice(firstIndex);
3104
+ }
3105
+ // Look up the base variable in equivalencies
3106
+ if (baseVar in equivMap && equivMap[baseVar] !== sourcePath) {
3107
+ const baseResolved = equivMap[baseVar];
3108
+ // Skip if baseResolved is an array (handle later)
3109
+ if (Array.isArray(baseResolved))
3110
+ return null;
3111
+ // If it resolves to a signature path, build the full resolved path
3112
+ if (baseResolved.startsWith('signature[') ||
3113
+ baseResolved.includes('()')) {
3114
+ if (baseResolved.endsWith('()')) {
3115
+ return baseResolved + '.functionCallReturnValue' + rest;
3116
+ }
3117
+ return baseResolved + rest;
3118
+ }
3119
+ }
3120
+ return null;
3121
+ };
2783
3122
  for (let iteration = 0; iteration < maxIterations; iteration++) {
2784
3123
  let changed = false;
2785
- for (const [varName, sourcePath] of Object.entries(equivalentSignatureVariables)) {
3124
+ for (const [varName, sourcePathOrArray] of Object.entries(equivalentSignatureVariables)) {
3125
+ // Handle arrays (OR expressions) by resolving each element
3126
+ if (Array.isArray(sourcePathOrArray)) {
3127
+ const resolvedArray = [];
3128
+ let arrayChanged = false;
3129
+ for (const sourcePath of sourcePathOrArray) {
3130
+ // Try to resolve this path using transitive resolution
3131
+ const resolved = resolveSourcePath(sourcePath, equivalentSignatureVariables);
3132
+ if (resolved && resolved !== sourcePath) {
3133
+ resolvedArray.push(resolved);
3134
+ arrayChanged = true;
3135
+ }
3136
+ else {
3137
+ resolvedArray.push(sourcePath);
3138
+ }
3139
+ }
3140
+ if (arrayChanged) {
3141
+ equivalentSignatureVariables[varName] = resolvedArray;
3142
+ changed = true;
3143
+ }
3144
+ continue;
3145
+ }
3146
+ const sourcePath = sourcePathOrArray;
2786
3147
  // Skip if already fully resolved (contains function call syntax)
2787
3148
  // BUT first check for computed value patterns that need resolution (Fix 28)
2788
3149
  // AND method call patterns that need base variable resolution (Fix 33)
@@ -2835,6 +3196,9 @@ export class ScopeDataStructure {
2835
3196
  if (baseVar in equivalentSignatureVariables &&
2836
3197
  baseVar !== varName) {
2837
3198
  const baseResolved = equivalentSignatureVariables[baseVar];
3199
+ // Skip if baseResolved is an array (OR expression)
3200
+ if (Array.isArray(baseResolved))
3201
+ continue;
2838
3202
  // Only resolve if the base resolved to something useful (contains () or .)
2839
3203
  if (baseResolved.includes('()') || baseResolved.includes('.')) {
2840
3204
  const newPath = baseResolved + rest;
@@ -2845,6 +3209,34 @@ export class ScopeDataStructure {
2845
3209
  }
2846
3210
  }
2847
3211
  }
3212
+ // Fix 38: Handle cyScope lazy initializer return values
3213
+ // When we have viewMode -> cyScope20(), trace through to find what cyScope20 returns.
3214
+ // The lazy initializer's return value should be the controllable data source.
3215
+ // Pattern: cyScopeN() where N is a number
3216
+ const cyScopeMatch = sourcePath.match(/^(cyScope\d+)\(\)$/);
3217
+ if (cyScopeMatch) {
3218
+ const cyScopeName = cyScopeMatch[1];
3219
+ const cyScopeNode = this.scopeNodes[cyScopeName];
3220
+ if (cyScopeNode?.equivalencies) {
3221
+ // Look for returnValue equivalency in the cyScope
3222
+ const returnValueEquivs = cyScopeNode.equivalencies['returnValue'];
3223
+ if (returnValueEquivs && returnValueEquivs.length > 0) {
3224
+ // Get the first return value source
3225
+ const returnSource = returnValueEquivs[0].schemaPath;
3226
+ // If the return source is a simple variable (not a complex path),
3227
+ // resolve varName directly to that variable
3228
+ if (returnSource &&
3229
+ !returnSource.includes('(') &&
3230
+ !returnSource.includes('[')) {
3231
+ // Update varName to point to the return source
3232
+ if (equivalentSignatureVariables[varName] !== returnSource) {
3233
+ equivalentSignatureVariables[varName] = returnSource;
3234
+ changed = true;
3235
+ }
3236
+ }
3237
+ }
3238
+ }
3239
+ }
2848
3240
  continue;
2849
3241
  }
2850
3242
  // Check if the source path starts with a variable that's also in the map
@@ -2862,7 +3254,13 @@ export class ScopeDataStructure {
2862
3254
  rest = '';
2863
3255
  }
2864
3256
  if (baseVar in equivalentSignatureVariables && baseVar !== varName) {
2865
- const baseResolved = equivalentSignatureVariables[baseVar];
3257
+ // Handle array case (OR expressions) - use first element
3258
+ const rawBaseResolved = equivalentSignatureVariables[baseVar];
3259
+ const baseResolved = Array.isArray(rawBaseResolved)
3260
+ ? rawBaseResolved[0]
3261
+ : rawBaseResolved;
3262
+ if (!baseResolved)
3263
+ continue;
2866
3264
  // If the base resolves to a hook call, add .functionCallReturnValue
2867
3265
  if (baseResolved.endsWith('()')) {
2868
3266
  const newPath = baseResolved + '.functionCallReturnValue' + rest;
@@ -3169,6 +3567,26 @@ export class ScopeDataStructure {
3169
3567
  }
3170
3568
  return enriched;
3171
3569
  }
3570
+ /**
3571
+ * Add JSX rendering usages from AST analysis.
3572
+ * These track arrays rendered via .map() and strings interpolated in JSX.
3573
+ */
3574
+ addJsxRenderingUsages(usages) {
3575
+ // Add usages, avoiding duplicates based on path and renderingType
3576
+ for (const usage of usages) {
3577
+ const exists = this.rawJsxRenderingUsages.some((existing) => existing.path === usage.path &&
3578
+ existing.renderingType === usage.renderingType);
3579
+ if (!exists) {
3580
+ this.rawJsxRenderingUsages.push(usage);
3581
+ }
3582
+ }
3583
+ }
3584
+ /**
3585
+ * Get JSX rendering usages collected during analysis.
3586
+ */
3587
+ getJsxRenderingUsages() {
3588
+ return this.rawJsxRenderingUsages;
3589
+ }
3172
3590
  toSerializable() {
3173
3591
  // Helper to clean cyScope and cyDuplicateKey from a string for output
3174
3592
  const cleanCyScope = (str) => this.replaceCyScopeInString(str).replace(/::cyDuplicateKey\d+::/g, '');
@@ -3553,6 +3971,10 @@ export class ScopeDataStructure {
3553
3971
  const childBoundaryGatingConditions = Object.keys(enrichedGatingConditions).length > 0
3554
3972
  ? enrichedGatingConditions
3555
3973
  : undefined;
3974
+ // Get JSX rendering usages (arrays via .map(), strings via interpolation)
3975
+ const jsxRenderingUsages = this.rawJsxRenderingUsages.length > 0
3976
+ ? this.rawJsxRenderingUsages
3977
+ : undefined;
3556
3978
  return {
3557
3979
  externalFunctionCalls: deduplicatedExternalFunctionCalls,
3558
3980
  rootFunction,
@@ -3563,6 +3985,7 @@ export class ScopeDataStructure {
3563
3985
  conditionalEffects,
3564
3986
  compoundConditionals,
3565
3987
  childBoundaryGatingConditions,
3988
+ jsxRenderingUsages,
3566
3989
  };
3567
3990
  }
3568
3991
  // ═══════════════════════════════════════════════════════════════════════════