@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
@@ -12,6 +12,78 @@
12
12
  * - compound conditionals → one flow with all conditions (only if ALL paths controllable)
13
13
  */
14
14
  import resolvePathToControllable from "./resolvePathToControllable.js";
15
+ import cleanPathOfNonTransformingFunctions from "./dataStructure/helpers/cleanPathOfNonTransformingFunctions.js";
16
+ /**
17
+ * Recursively expands a derived variable to its leaf data sources.
18
+ *
19
+ * For OR expressions like `isAnalyzing = a || b || c`:
20
+ * - Returns all source paths [a, b, c] so they can all be set appropriately
21
+ *
22
+ * For nested derivations like `isAnalyzing = isInCurrentRun || isInQueue`:
23
+ * - Where `isInCurrentRun` is derived from `currentRun.entityShas.includes(x)`
24
+ * - Returns the final data sources: [currentRun.entityShas, queueState.jobs]
25
+ *
26
+ * @param path The variable path to expand
27
+ * @param conditionalUsages All conditional usages (to look up derivedFrom info)
28
+ * @param attributesMap Map of controllable paths
29
+ * @param equivalentSignatureVariables Variable-to-path mappings
30
+ * @param fullToShortPathMap Full-to-short path mappings
31
+ * @param visited Set of already-visited paths (prevents infinite recursion)
32
+ * @param derivedVariables Optional map of all derived variables (for intermediate tracing)
33
+ * @returns Array of resolved source paths that are controllable
34
+ */
35
+ function expandDerivedVariableToSources(path, conditionalUsages, attributesMap, equivalentSignatureVariables, fullToShortPathMap, visited = new Set(), derivedVariables) {
36
+ // Prevent infinite recursion
37
+ if (visited.has(path)) {
38
+ return [];
39
+ }
40
+ visited.add(path);
41
+ // First, check if this path is directly controllable
42
+ const directResolution = resolvePathToControllable(path, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
43
+ if (directResolution.isControllable && directResolution.resolvedPath) {
44
+ return [{ path: directResolution.resolvedPath }];
45
+ }
46
+ // Look up derivedFrom info for this path
47
+ // First check conditionalUsages, then fall back to derivedVariables
48
+ const usage = conditionalUsages[path]?.[0];
49
+ let derivedFrom = usage?.derivedFrom;
50
+ // CRITICAL: If not found in conditionalUsages, check derivedVariables
51
+ // This handles intermediate derived variables like `isInCurrentRun` that aren't
52
+ // directly used in conditionals but ARE derived from data sources
53
+ if (!derivedFrom && derivedVariables?.[path]) {
54
+ derivedFrom = derivedVariables[path];
55
+ }
56
+ if (!derivedFrom) {
57
+ return [];
58
+ }
59
+ const { operation, sourcePath, sourcePaths } = derivedFrom;
60
+ // For OR/AND operations, recursively expand all source paths
61
+ if ((operation === 'or' || operation === 'and') && sourcePaths) {
62
+ const allSources = [];
63
+ for (const sp of sourcePaths) {
64
+ const expanded = expandDerivedVariableToSources(sp, conditionalUsages, attributesMap, equivalentSignatureVariables, fullToShortPathMap, visited, derivedVariables);
65
+ // Add all expanded sources
66
+ for (const source of expanded) {
67
+ // Avoid duplicates
68
+ if (!allSources.some((s) => s.path === source.path)) {
69
+ allSources.push(source);
70
+ }
71
+ }
72
+ }
73
+ return allSources;
74
+ }
75
+ // For single-source operations (arrayIncludes, arraySome, notNull, etc.)
76
+ if (sourcePath) {
77
+ // Try to resolve the source path directly
78
+ const sourceResolution = resolvePathToControllable(sourcePath, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
79
+ if (sourceResolution.isControllable && sourceResolution.resolvedPath) {
80
+ return [{ path: sourceResolution.resolvedPath, operation }];
81
+ }
82
+ // If not directly resolvable, recursively expand
83
+ return expandDerivedVariableToSources(sourcePath, conditionalUsages, attributesMap, equivalentSignatureVariables, fullToShortPathMap, visited, derivedVariables);
84
+ }
85
+ return [];
86
+ }
15
87
  /**
16
88
  * Clean up sourceDataPath by removing redundant scope prefixes.
17
89
  *
@@ -59,6 +131,47 @@ function cleanSourceDataPath(sourceDataPath) {
59
131
  }
60
132
  return actualPath;
61
133
  }
134
+ /**
135
+ * Strip .length suffix from a path if present.
136
+ *
137
+ * When we have a path like "items.length", the controllable attribute is "items"
138
+ * (the array), not "items.length". The length is derived from the array contents.
139
+ *
140
+ * This ensures that execution flows reference the actual controllable attribute
141
+ * rather than the derived .length property.
142
+ */
143
+ function stripLengthSuffix(path) {
144
+ if (path.endsWith('.length')) {
145
+ return path.slice(0, -7); // Remove ".length" (7 characters)
146
+ }
147
+ return path;
148
+ }
149
+ /**
150
+ * Extract the controllable base path from a path that may contain method calls.
151
+ *
152
+ * This handles complex expressions like:
153
+ * - `scenarios.filter((s) => s.active).length` → `scenarios`
154
+ * - `users.some((u) => u.role === 'admin')` → `users`
155
+ * - `items.map(x => x.name).join(', ')` → `items`
156
+ *
157
+ * The controllable base is the path that can be mocked - we can control
158
+ * what `scenarios` contains, but we can't control what `.filter()` returns.
159
+ *
160
+ * @param path - The path that may contain method calls
161
+ * @returns The controllable base path with method calls stripped
162
+ */
163
+ function extractControllableBase(path) {
164
+ // First strip .length suffix if present
165
+ const pathWithoutLength = stripLengthSuffix(path);
166
+ // Use cleanPathOfNonTransformingFunctions to strip method calls like .filter(), .some()
167
+ const cleanedPath = cleanPathOfNonTransformingFunctions(pathWithoutLength);
168
+ // If the cleaned path is different, return it
169
+ if (cleanedPath !== pathWithoutLength) {
170
+ return cleanedPath;
171
+ }
172
+ // Otherwise, return the path with just .length stripped
173
+ return pathWithoutLength;
174
+ }
62
175
  /**
63
176
  * Find a path in attributesMap, using fullToShortPathMap to verify the path is controllable.
64
177
  *
@@ -117,6 +230,19 @@ function findInAttributesMapForPath(path, attributesMap, fullToShortPathMap) {
117
230
  }
118
231
  return null;
119
232
  }
233
+ /**
234
+ * Generate a slug from a path for use in flow IDs and exclusive groups.
235
+ */
236
+ function pathToSlug(path) {
237
+ return path
238
+ .replace(/\[\d+\]/g, '')
239
+ .replace(/\[\]/g, '')
240
+ .replace(/\(\)/g, '')
241
+ .replace(/\.functionCallReturnValue/g, '')
242
+ .replace(/[<>]/g, '')
243
+ .replace(/\./g, '-')
244
+ .toLowerCase();
245
+ }
120
246
  /**
121
247
  * Generate a human-readable name from a path.
122
248
  * Extracts the last meaningful part of the path.
@@ -173,6 +299,12 @@ function inferValueType(value) {
173
299
  /**
174
300
  * Generate flows from a single conditional usage.
175
301
  * Sets impact to 'high' if the conditional controls JSX rendering.
302
+ *
303
+ * When the usage has a `constraintExpression`, it represents a complex expression
304
+ * that can't be simply resolved (e.g., `scenarios.filter(x => x.active).length > 1`).
305
+ * In this case:
306
+ * - `attributePath` is set to the controllable base (e.g., `scenarios`)
307
+ * - `constraint` is set to the full expression for LLM reasoning
176
308
  */
177
309
  function generateFlowsFromUsage(usage, resolvedPath) {
178
310
  const flows = [];
@@ -182,6 +314,13 @@ function generateFlowsFromUsage(usage, resolvedPath) {
182
314
  const impact = usage.controlsJsxRendering
183
315
  ? 'high'
184
316
  : 'medium';
317
+ // When there's a constraintExpression, use the controllable base for attributePath
318
+ // and pass through the constraint for LLM reasoning
319
+ const hasConstraint = !!usage.constraintExpression;
320
+ const attributePath = hasConstraint
321
+ ? extractControllableBase(resolvedPath)
322
+ : stripLengthSuffix(resolvedPath);
323
+ const constraint = usage.constraintExpression;
185
324
  if (usage.conditionType === 'truthiness') {
186
325
  // Generate both truthy and falsy flows
187
326
  const isNegated = usage.isNegated ?? false;
@@ -192,10 +331,11 @@ function generateFlowsFromUsage(usage, resolvedPath) {
192
331
  description: `When ${baseName.toLowerCase()} is ${isNegated ? 'falsy' : 'truthy'}`,
193
332
  requiredValues: [
194
333
  {
195
- attributePath: resolvedPath,
334
+ attributePath,
196
335
  value: isNegated ? 'falsy' : 'truthy',
197
336
  comparison: isNegated ? 'falsy' : 'truthy',
198
337
  valueType: 'boolean',
338
+ constraint,
199
339
  },
200
340
  ],
201
341
  impact,
@@ -214,10 +354,11 @@ function generateFlowsFromUsage(usage, resolvedPath) {
214
354
  description: `When ${baseName.toLowerCase()} is ${isNegated ? 'truthy' : 'falsy'}`,
215
355
  requiredValues: [
216
356
  {
217
- attributePath: resolvedPath,
357
+ attributePath,
218
358
  value: isNegated ? 'truthy' : 'falsy',
219
359
  comparison: isNegated ? 'truthy' : 'falsy',
220
360
  valueType: 'boolean',
361
+ constraint,
221
362
  },
222
363
  ],
223
364
  impact,
@@ -241,10 +382,11 @@ function generateFlowsFromUsage(usage, resolvedPath) {
241
382
  description: `When ${baseName.toLowerCase()} equals "${value}"`,
242
383
  requiredValues: [
243
384
  {
244
- attributePath: resolvedPath,
385
+ attributePath,
245
386
  value: value,
246
387
  comparison: 'equals',
247
388
  valueType: inferValueType(value),
389
+ constraint,
248
390
  },
249
391
  ],
250
392
  impact,
@@ -293,7 +435,7 @@ function generateFlowFromCompound(compound, resolvedPaths) {
293
435
  comparison = 'equals';
294
436
  }
295
437
  requiredValues.push({
296
- attributePath: resolvedPath,
438
+ attributePath: stripLengthSuffix(resolvedPath),
297
439
  value,
298
440
  comparison,
299
441
  valueType: inferValueType(value),
@@ -321,6 +463,48 @@ function generateFlowFromCompound(compound, resolvedPaths) {
321
463
  codeSnippet: compound.sourceLocation.codeSnippet,
322
464
  };
323
465
  }
466
+ /**
467
+ * Expand a compound conditional with OR groups into multiple condition sets.
468
+ *
469
+ * For a compound like `A && (B || C)`:
470
+ * - Conditions: [{ path: 'A' }, { path: 'B', orGroupId: 'or_xxx' }, { path: 'C', orGroupId: 'or_xxx' }]
471
+ * - Returns: [[A, B], [A, C]] - two sets of conditions
472
+ *
473
+ * For multiple OR groups like `A && (B || C) && (D || E)`:
474
+ * - Returns: [[A, B, D], [A, B, E], [A, C, D], [A, C, E]]
475
+ */
476
+ function expandOrGroups(conditions) {
477
+ // Separate conditions into mandatory (no orGroupId) and OR groups
478
+ const mandatory = conditions.filter((c) => !c.orGroupId);
479
+ const orGroups = new Map();
480
+ for (const condition of conditions) {
481
+ if (condition.orGroupId) {
482
+ const group = orGroups.get(condition.orGroupId) ?? [];
483
+ group.push(condition);
484
+ orGroups.set(condition.orGroupId, group);
485
+ }
486
+ }
487
+ // If no OR groups, return the original conditions
488
+ if (orGroups.size === 0) {
489
+ return [conditions];
490
+ }
491
+ // Generate all combinations by picking one condition from each OR group
492
+ const groupArrays = Array.from(orGroups.values());
493
+ const combinations = [];
494
+ function generateCombinations(index, current) {
495
+ if (index === groupArrays.length) {
496
+ // We've picked one from each OR group - combine with mandatory conditions
497
+ combinations.push([...mandatory, ...current]);
498
+ return;
499
+ }
500
+ // Pick each option from the current OR group
501
+ for (const option of groupArrays[index]) {
502
+ generateCombinations(index + 1, [...current, option]);
503
+ }
504
+ }
505
+ generateCombinations(0, []);
506
+ return combinations;
507
+ }
324
508
  /**
325
509
  * Generate execution flows from conditional usages using pure static analysis.
326
510
  *
@@ -386,7 +570,11 @@ function translateChildPathToParent(childPath, childEquivalentSignatureVariables
386
570
  }
387
571
  // Look up the child's equivalence for this root variable
388
572
  // e.g., childEquiv[selectedScenario] = "signature[0].selectedScenario"
389
- const childPropPath = childEquivalentSignatureVariables[rootVar];
573
+ // Handle array case (OR expressions) - use first element if array
574
+ const rawChildPropPath = childEquivalentSignatureVariables[rootVar];
575
+ const childPropPath = Array.isArray(rawChildPropPath)
576
+ ? rawChildPropPath[0]
577
+ : rawChildPropPath;
390
578
  if (!childPropPath) {
391
579
  // No mapping found - this might be internal state, not a prop
392
580
  return null;
@@ -396,7 +584,11 @@ function translateChildPathToParent(childPath, childEquivalentSignatureVariables
396
584
  const fullChildPropPath = `${childName}().${childPropPath}`;
397
585
  // Look up parent's equivalence to find what value was passed to this prop
398
586
  // e.g., parentEquiv["ChildName().signature[0].selectedScenario"] = "selectedScenario"
399
- const parentValue = parentEquivalentSignatureVariables[fullChildPropPath];
587
+ // Handle array case (OR expressions) - use first element if array
588
+ const rawParentValue = parentEquivalentSignatureVariables[fullChildPropPath];
589
+ const parentValue = Array.isArray(rawParentValue)
590
+ ? rawParentValue[0]
591
+ : rawParentValue;
400
592
  if (!parentValue) {
401
593
  // No parent mapping found - log ALL parent keys that contain the childName
402
594
  const relevantParentKeys = Object.keys(parentEquivalentSignatureVariables).filter((k) => k.includes(childName));
@@ -408,7 +600,7 @@ function translateChildPathToParent(childPath, childEquivalentSignatureVariables
408
600
  return result;
409
601
  }
410
602
  export default function generateExecutionFlowsFromConditionals(args) {
411
- const { conditionalUsages, compoundConditionals, attributesMap, equivalentSignatureVariables, fullToShortPathMap, childComponentData, } = args;
603
+ const { conditionalUsages, compoundConditionals, attributesMap, equivalentSignatureVariables, fullToShortPathMap, childComponentData, derivedVariables, sourceEquivalencies, } = args;
412
604
  const flows = [];
413
605
  const seenFlowIds = new Set();
414
606
  // Track normalized resolved paths to prevent duplicate flows
@@ -454,7 +646,7 @@ export default function generateExecutionFlowsFromConditionals(args) {
454
646
  // This handles cases like: const hasAnalysis = analysis !== null
455
647
  // where hasAnalysis is not in attributesMap but analysis is
456
648
  if (!resolvedPath && usage.derivedFrom) {
457
- const { sourcePath, sourcePaths } = usage.derivedFrom;
649
+ const { operation, sourcePath, sourcePaths, comparedValue } = usage.derivedFrom;
458
650
  // For single-source derivations (notNull, equals, etc.)
459
651
  if (sourcePath) {
460
652
  const resolution = resolvePathToControllable(sourcePath, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
@@ -462,8 +654,232 @@ export default function generateExecutionFlowsFromConditionals(args) {
462
654
  resolvedPath = resolution.resolvedPath;
463
655
  }
464
656
  }
465
- // For multi-source derivations (or, and), try the first resolvable path
466
- // This is a simplification - ideally we'd generate flows for each source
657
+ // For equals/notEquals derivations with comparedValue, generate comparison flows
658
+ // e.g., canEdit derived from user.role === 'admin'
659
+ // When canEdit is used in truthiness check, we need:
660
+ // - Truthy flow: user.role = 'admin' (comparison: 'equals')
661
+ // - Falsy flow: user.role != 'admin' (comparison: 'notEquals')
662
+ if ((operation === 'equals' || operation === 'notEquals') &&
663
+ comparedValue !== undefined &&
664
+ resolvedPath &&
665
+ usage.conditionType === 'truthiness') {
666
+ const baseName = generateNameFromPath(usage.path);
667
+ const impact = usage.controlsJsxRendering
668
+ ? 'high'
669
+ : 'medium';
670
+ const isNegated = usage.isNegated ?? false;
671
+ // For equals derivation:
672
+ // - Truthy check (!negated): needs value = comparedValue (equals)
673
+ // - Falsy check (negated): needs value != comparedValue (notEquals)
674
+ // For notEquals derivation: inverse of above
675
+ const isEqualsDerivation = operation === 'equals';
676
+ const truthyNeedsEquals = isEqualsDerivation !== isNegated;
677
+ // Generate truthy flow
678
+ const truthyFlow = {
679
+ id: generateFlowId(usage.path, 'truthy'),
680
+ name: `${baseName} True`,
681
+ description: `When ${baseName.toLowerCase()} is truthy (${resolvedPath} ${truthyNeedsEquals ? '=' : '!='} ${comparedValue})`,
682
+ requiredValues: [
683
+ {
684
+ attributePath: stripLengthSuffix(resolvedPath),
685
+ value: comparedValue,
686
+ comparison: truthyNeedsEquals ? 'equals' : 'notEquals',
687
+ valueType: inferValueType(comparedValue),
688
+ },
689
+ ],
690
+ impact,
691
+ sourceLocation: usage.sourceLocation
692
+ ? {
693
+ lineNumber: usage.sourceLocation.lineNumber,
694
+ column: usage.sourceLocation.column,
695
+ }
696
+ : undefined,
697
+ codeSnippet: usage.sourceLocation?.codeSnippet,
698
+ };
699
+ // Generate falsy flow
700
+ const falsyFlow = {
701
+ id: generateFlowId(usage.path, 'falsy'),
702
+ name: `${baseName} False`,
703
+ description: `When ${baseName.toLowerCase()} is falsy (${resolvedPath} ${truthyNeedsEquals ? '!=' : '='} ${comparedValue})`,
704
+ requiredValues: [
705
+ {
706
+ attributePath: stripLengthSuffix(resolvedPath),
707
+ value: comparedValue,
708
+ comparison: truthyNeedsEquals ? 'notEquals' : 'equals',
709
+ valueType: inferValueType(comparedValue),
710
+ },
711
+ ],
712
+ impact,
713
+ sourceLocation: usage.sourceLocation
714
+ ? {
715
+ lineNumber: usage.sourceLocation.lineNumber,
716
+ column: usage.sourceLocation.column,
717
+ }
718
+ : undefined,
719
+ codeSnippet: usage.sourceLocation?.codeSnippet,
720
+ };
721
+ // Add flows and skip normal flow generation
722
+ if (!seenFlowIds.has(truthyFlow.id)) {
723
+ seenFlowIds.add(truthyFlow.id);
724
+ flows.push(truthyFlow);
725
+ }
726
+ if (!seenFlowIds.has(falsyFlow.id)) {
727
+ seenFlowIds.add(falsyFlow.id);
728
+ flows.push(falsyFlow);
729
+ }
730
+ continue;
731
+ }
732
+ // For OR derivations with negation, we need ALL sources to be falsy
733
+ // e.g., !isBusy where isBusy = isRunning || isQueued || isPending
734
+ // For the falsy flow, ALL sources must be falsy
735
+ if (operation === 'or' &&
736
+ usage.conditionType === 'truthiness' &&
737
+ usage.isNegated === true &&
738
+ sourcePaths &&
739
+ sourcePaths.length > 0) {
740
+ // Use expandDerivedVariableToSources to recursively resolve all sources
741
+ const allSources = expandDerivedVariableToSources(usage.path, conditionalUsages, attributesMap, equivalentSignatureVariables, fullToShortPathMap, new Set(), derivedVariables);
742
+ if (allSources.length > 0) {
743
+ // Generate a compound-style flow with all sources set to falsy
744
+ const baseName = generateNameFromPath(usage.path);
745
+ const impact = usage.controlsJsxRendering
746
+ ? 'high'
747
+ : 'medium';
748
+ const requiredValues = allSources.map((source) => ({
749
+ attributePath: source.path,
750
+ value: 'falsy',
751
+ comparison: 'falsy',
752
+ valueType: 'boolean',
753
+ }));
754
+ // Create a single falsy flow with all sources
755
+ const falsyFlow = {
756
+ id: generateFlowId(usage.path, 'falsy'),
757
+ name: `${baseName} False`,
758
+ description: `When ${baseName.toLowerCase()} is falsy (all sources are falsy)`,
759
+ requiredValues,
760
+ impact,
761
+ sourceLocation: usage.sourceLocation
762
+ ? {
763
+ lineNumber: usage.sourceLocation.lineNumber,
764
+ column: usage.sourceLocation.column,
765
+ }
766
+ : undefined,
767
+ codeSnippet: usage.sourceLocation?.codeSnippet,
768
+ };
769
+ // Create a truthy flow - for OR, ANY source being truthy is sufficient
770
+ // We use the first resolvable source for the truthy flow
771
+ const firstSource = allSources[0];
772
+ const truthyFlow = {
773
+ id: generateFlowId(usage.path, 'truthy'),
774
+ name: `${baseName} True`,
775
+ description: `When ${baseName.toLowerCase()} is truthy`,
776
+ requiredValues: [
777
+ {
778
+ attributePath: firstSource.path,
779
+ value: 'truthy',
780
+ comparison: 'truthy',
781
+ valueType: 'boolean',
782
+ },
783
+ ],
784
+ impact,
785
+ sourceLocation: usage.sourceLocation
786
+ ? {
787
+ lineNumber: usage.sourceLocation.lineNumber,
788
+ column: usage.sourceLocation.column,
789
+ }
790
+ : undefined,
791
+ codeSnippet: usage.sourceLocation?.codeSnippet,
792
+ };
793
+ // Add both flows (falsy needs all sources, truthy needs one)
794
+ if (!seenFlowIds.has(falsyFlow.id)) {
795
+ seenFlowIds.add(falsyFlow.id);
796
+ flows.push(falsyFlow);
797
+ }
798
+ if (!seenFlowIds.has(truthyFlow.id)) {
799
+ seenFlowIds.add(truthyFlow.id);
800
+ flows.push(truthyFlow);
801
+ }
802
+ // Skip the normal flow generation for this usage
803
+ continue;
804
+ }
805
+ }
806
+ // For AND derivations without negation, we need ALL sources to be truthy
807
+ // e.g., isReady where isReady = hasData && isLoaded && isValid
808
+ // For the truthy flow, ALL sources must be truthy
809
+ // For negated AND (!isReady), ANY source being falsy is sufficient (fallback behavior)
810
+ if (operation === 'and' &&
811
+ usage.conditionType === 'truthiness' &&
812
+ usage.isNegated !== true &&
813
+ sourcePaths &&
814
+ sourcePaths.length > 0) {
815
+ // Use expandDerivedVariableToSources to recursively resolve all sources
816
+ const allSources = expandDerivedVariableToSources(usage.path, conditionalUsages, attributesMap, equivalentSignatureVariables, fullToShortPathMap, new Set(), derivedVariables);
817
+ if (allSources.length > 0) {
818
+ // Generate a compound-style flow with all sources set to truthy
819
+ const baseName = generateNameFromPath(usage.path);
820
+ const impact = usage.controlsJsxRendering
821
+ ? 'high'
822
+ : 'medium';
823
+ const requiredValues = allSources.map((source) => ({
824
+ attributePath: source.path,
825
+ value: 'truthy',
826
+ comparison: 'truthy',
827
+ valueType: 'boolean',
828
+ }));
829
+ // Create a truthy flow with all sources
830
+ const truthyFlow = {
831
+ id: generateFlowId(usage.path, 'truthy'),
832
+ name: `${baseName} True`,
833
+ description: `When ${baseName.toLowerCase()} is truthy (all sources are truthy)`,
834
+ requiredValues,
835
+ impact,
836
+ sourceLocation: usage.sourceLocation
837
+ ? {
838
+ lineNumber: usage.sourceLocation.lineNumber,
839
+ column: usage.sourceLocation.column,
840
+ }
841
+ : undefined,
842
+ codeSnippet: usage.sourceLocation?.codeSnippet,
843
+ };
844
+ // Create a falsy flow - for AND, ANY source being falsy is sufficient
845
+ // We use the first resolvable source for the falsy flow
846
+ const firstSource = allSources[0];
847
+ const falsyFlow = {
848
+ id: generateFlowId(usage.path, 'falsy'),
849
+ name: `${baseName} False`,
850
+ description: `When ${baseName.toLowerCase()} is falsy`,
851
+ requiredValues: [
852
+ {
853
+ attributePath: firstSource.path,
854
+ value: 'falsy',
855
+ comparison: 'falsy',
856
+ valueType: 'boolean',
857
+ },
858
+ ],
859
+ impact,
860
+ sourceLocation: usage.sourceLocation
861
+ ? {
862
+ lineNumber: usage.sourceLocation.lineNumber,
863
+ column: usage.sourceLocation.column,
864
+ }
865
+ : undefined,
866
+ codeSnippet: usage.sourceLocation?.codeSnippet,
867
+ };
868
+ // Add both flows (truthy needs all sources, falsy needs one)
869
+ if (!seenFlowIds.has(truthyFlow.id)) {
870
+ seenFlowIds.add(truthyFlow.id);
871
+ flows.push(truthyFlow);
872
+ }
873
+ if (!seenFlowIds.has(falsyFlow.id)) {
874
+ seenFlowIds.add(falsyFlow.id);
875
+ flows.push(falsyFlow);
876
+ }
877
+ // Skip the normal flow generation for this usage
878
+ continue;
879
+ }
880
+ }
881
+ // For multi-source derivations (or, and) without special handling,
882
+ // try the first resolvable path as a fallback
467
883
  if (!resolvedPath && sourcePaths && sourcePaths.length > 0) {
468
884
  for (const sp of sourcePaths) {
469
885
  const resolution = resolvePathToControllable(sp, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
@@ -501,23 +917,167 @@ export default function generateExecutionFlowsFromConditionals(args) {
501
917
  }
502
918
  // Process compound conditionals
503
919
  for (const compound of compoundConditionals) {
504
- // First, check if ALL paths in this compound are controllable
505
- const resolvedPaths = new Map();
506
- let allControllable = true;
507
- for (const condition of compound.conditions) {
508
- const resolution = resolvePathToControllable(condition.path, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
509
- if (!resolution.isControllable || !resolution.resolvedPath) {
510
- allControllable = false;
511
- break;
920
+ // Expand OR groups into separate condition sets
921
+ // For example, `A && (B || C)` becomes [[A, B], [A, C]]
922
+ const expandedConditionSets = expandOrGroups(compound.conditions);
923
+ // Process each expanded condition set as a separate potential flow
924
+ for (const conditionSet of expandedConditionSets) {
925
+ // First, check if ALL paths in this condition set are controllable (or can be expanded to controllable sources)
926
+ const resolvedPaths = new Map();
927
+ // Track expanded sources for derived variables (path -> array of expanded sources)
928
+ const expandedSources = new Map();
929
+ let allControllable = true;
930
+ for (const condition of conditionSet) {
931
+ // Check if this condition path has derivation info
932
+ // First check conditionalUsages, then fall back to derivedVariables
933
+ const usagesForPath = conditionalUsages[condition.path];
934
+ let derivedFromInfo = usagesForPath?.find((u) => u.derivedFrom?.operation)?.derivedFrom;
935
+ // CRITICAL: Also check derivedVariables for intermediate derived variables
936
+ if (!derivedFromInfo && derivedVariables?.[condition.path]) {
937
+ derivedFromInfo = derivedVariables[condition.path];
938
+ }
939
+ if (derivedFromInfo) {
940
+ // This is a derived variable - expand to its sources
941
+ const sources = expandDerivedVariableToSources(condition.path, conditionalUsages, attributesMap, equivalentSignatureVariables, fullToShortPathMap, new Set(), derivedVariables);
942
+ if (sources.length > 0) {
943
+ // Store the expanded sources for this condition
944
+ expandedSources.set(condition.path, sources);
945
+ // Use the first source's path for the resolvedPaths map (for ID generation)
946
+ resolvedPaths.set(condition.path, sources[0].path);
947
+ }
948
+ else {
949
+ // Derived variable but no controllable sources found
950
+ allControllable = false;
951
+ break;
952
+ }
953
+ }
954
+ else {
955
+ // Not a derived variable - resolve directly
956
+ const resolution = resolvePathToControllable(condition.path, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
957
+ if (!resolution.isControllable || !resolution.resolvedPath) {
958
+ allControllable = false;
959
+ break;
960
+ }
961
+ resolvedPaths.set(condition.path, resolution.resolvedPath);
962
+ }
512
963
  }
513
- resolvedPaths.set(condition.path, resolution.resolvedPath);
514
- }
515
- // Only create a flow if ALL paths are controllable
516
- if (allControllable && resolvedPaths.size > 0) {
517
- const compoundFlow = generateFlowFromCompound(compound, resolvedPaths);
518
- if (compoundFlow && !seenFlowIds.has(compoundFlow.id)) {
519
- seenFlowIds.add(compoundFlow.id);
520
- flows.push(compoundFlow);
964
+ // Only create a flow if ALL paths are controllable
965
+ if (allControllable && resolvedPaths.size > 0) {
966
+ // If any conditions were expanded from derived variables, we need to build a custom flow
967
+ if (expandedSources.size > 0) {
968
+ const requiredValues = [];
969
+ for (const condition of conditionSet) {
970
+ const sources = expandedSources.get(condition.path);
971
+ if (sources) {
972
+ // This condition was expanded - add all its sources
973
+ // Determine the required value based on condition type and derivation operation
974
+ const usagesForPath = conditionalUsages[condition.path];
975
+ let expandedDerivedFrom = usagesForPath?.find((u) => u.derivedFrom?.operation)?.derivedFrom;
976
+ // Also check derivedVariables for intermediate derived variables
977
+ if (!expandedDerivedFrom && derivedVariables?.[condition.path]) {
978
+ expandedDerivedFrom = derivedVariables[condition.path];
979
+ }
980
+ const operation = expandedDerivedFrom?.operation;
981
+ for (const source of sources) {
982
+ // For OR-derived truthy: ANY source truthy
983
+ // For AND-derived truthy: ALL sources truthy
984
+ // For negated: inverse
985
+ let value;
986
+ let comparison;
987
+ if (condition.conditionType === 'truthiness') {
988
+ const isNegated = condition.isNegated === true;
989
+ // For OR: truthy needs ANY source truthy, falsy needs ALL sources falsy
990
+ // For AND: truthy needs ALL sources truthy, falsy needs ANY source falsy
991
+ // In compound conditionals, we generate the truthy path by default
992
+ // (the compound expression must be truthy)
993
+ if (operation === 'or') {
994
+ // For OR-derived, truthy means we need at least one source truthy
995
+ // We'll use the first source as truthy (simplification)
996
+ value = isNegated ? 'falsy' : 'truthy';
997
+ }
998
+ else if (operation === 'and') {
999
+ // For AND-derived, truthy means ALL sources truthy
1000
+ value = isNegated ? 'falsy' : 'truthy';
1001
+ }
1002
+ else {
1003
+ value = isNegated ? 'falsy' : 'truthy';
1004
+ }
1005
+ comparison = isNegated ? 'falsy' : 'truthy';
1006
+ }
1007
+ else {
1008
+ value = 'truthy';
1009
+ comparison = 'truthy';
1010
+ }
1011
+ requiredValues.push({
1012
+ attributePath: source.path,
1013
+ value,
1014
+ comparison,
1015
+ valueType: 'boolean',
1016
+ });
1017
+ }
1018
+ }
1019
+ else {
1020
+ // This condition was resolved directly
1021
+ const resolvedPath = resolvedPaths.get(condition.path);
1022
+ if (resolvedPath) {
1023
+ let value;
1024
+ let comparison;
1025
+ if (condition.conditionType === 'truthiness') {
1026
+ value = condition.isNegated ? 'falsy' : 'truthy';
1027
+ comparison = condition.isNegated ? 'falsy' : 'truthy';
1028
+ }
1029
+ else {
1030
+ value =
1031
+ condition.requiredValue?.toString() ??
1032
+ condition.comparedValues?.[0] ??
1033
+ 'truthy';
1034
+ comparison = 'equals';
1035
+ }
1036
+ requiredValues.push({
1037
+ attributePath: stripLengthSuffix(resolvedPath),
1038
+ value,
1039
+ comparison,
1040
+ valueType: inferValueType(value),
1041
+ });
1042
+ }
1043
+ }
1044
+ }
1045
+ if (requiredValues.length > 0) {
1046
+ const impact = compound.controlsJsxRendering ? 'high' : 'medium';
1047
+ // Generate a combined ID from all paths
1048
+ const pathParts = requiredValues
1049
+ .map((rv) => {
1050
+ const name = generateNameFromPath(rv.attributePath);
1051
+ return name.toLowerCase().replace(/\s+/g, '-');
1052
+ })
1053
+ .join('-and-');
1054
+ const compoundFlow = {
1055
+ id: `${pathParts}-${requiredValues.map((rv) => rv.value).join('-')}`,
1056
+ name: generateNameFromPath(requiredValues[0].attributePath),
1057
+ description: `When ${requiredValues.map((rv) => `${generateNameFromPath(rv.attributePath).toLowerCase()} is ${rv.value}`).join(' and ')}`,
1058
+ impact,
1059
+ requiredValues,
1060
+ sourceLocation: compound.sourceLocation,
1061
+ };
1062
+ if (!seenFlowIds.has(compoundFlow.id)) {
1063
+ seenFlowIds.add(compoundFlow.id);
1064
+ flows.push(compoundFlow);
1065
+ }
1066
+ }
1067
+ }
1068
+ else {
1069
+ // No derived variables - use the original generateFlowFromCompound
1070
+ // Create a modified compound with just this condition set
1071
+ const modifiedCompound = {
1072
+ ...compound,
1073
+ conditions: conditionSet,
1074
+ };
1075
+ const compoundFlow = generateFlowFromCompound(modifiedCompound, resolvedPaths);
1076
+ if (compoundFlow && !seenFlowIds.has(compoundFlow.id)) {
1077
+ seenFlowIds.add(compoundFlow.id);
1078
+ flows.push(compoundFlow);
1079
+ }
1080
+ }
521
1081
  }
522
1082
  }
523
1083
  }
@@ -541,7 +1101,17 @@ export default function generateExecutionFlowsFromConditionals(args) {
541
1101
  const [, varName, operator, comparedValue] = comparisonMatch;
542
1102
  // Try to resolve the variable name
543
1103
  const varResolution = resolvePathToControllable(varName, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
1104
+ // Only use controllable paths for gating conditions (whitelist approach).
1105
+ // Do NOT fall back to equivalentSignatureVariables because those may contain
1106
+ // uncontrollable paths like useState that cannot be mocked.
1107
+ let resolvedVarPath = null;
544
1108
  if (varResolution.isControllable && varResolution.resolvedPath) {
1109
+ resolvedVarPath = varResolution.resolvedPath;
1110
+ }
1111
+ // Note: We intentionally do NOT fall back to equivalentSignatureVariables here
1112
+ // because that would allow uncontrollable paths (like useState) to be added
1113
+ // as gating conditions.
1114
+ if (resolvedVarPath) {
545
1115
  const isNegated = gatingCondition.isNegated === true;
546
1116
  const isNotEquals = operator === '!=' || operator === '!==';
547
1117
  // Determine the effective value for this gating condition
@@ -551,7 +1121,7 @@ export default function generateExecutionFlowsFromConditionals(args) {
551
1121
  // XOR logic: isNegated XOR isNotEquals
552
1122
  const needsExactValue = isNegated !== isNotEquals;
553
1123
  gatingRequiredValues.push({
554
- attributePath: varResolution.resolvedPath,
1124
+ attributePath: resolvedVarPath,
555
1125
  value: needsExactValue ? 'falsy' : comparedValue,
556
1126
  comparison: needsExactValue ? 'falsy' : 'equals',
557
1127
  });
@@ -604,7 +1174,7 @@ export default function generateExecutionFlowsFromConditionals(args) {
604
1174
  // For non-negated &&: all parts must be truthy
605
1175
  // For negated ||: all parts must be falsy (DeMorgan: !(A || B) = !A && !B)
606
1176
  gatingRequiredValues.push({
607
- attributePath: partResolution.resolvedPath,
1177
+ attributePath: stripLengthSuffix(partResolution.resolvedPath),
608
1178
  value: isNegated ? 'falsy' : 'truthy',
609
1179
  comparison: isNegated ? 'falsy' : 'truthy',
610
1180
  });
@@ -616,14 +1186,24 @@ export default function generateExecutionFlowsFromConditionals(args) {
616
1186
  // Simple gating condition (single path)
617
1187
  // Resolve the gating path in parent context
618
1188
  const gatingResolution = resolvePathToControllable(gatingPath, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
1189
+ // Only use controllable paths for gating conditions (whitelist approach).
1190
+ // Do NOT fall back to equivalentSignatureVariables because those may contain
1191
+ // uncontrollable paths like useState that cannot be mocked.
1192
+ let resolvedGatingPath = null;
619
1193
  if (gatingResolution.isControllable &&
620
1194
  gatingResolution.resolvedPath) {
1195
+ resolvedGatingPath = gatingResolution.resolvedPath;
1196
+ }
1197
+ // Note: We intentionally do NOT fall back to equivalentSignatureVariables here
1198
+ // because that would allow uncontrollable paths (like useState) to be added
1199
+ // as gating conditions.
1200
+ if (resolvedGatingPath) {
621
1201
  // For truthiness conditions on gating, check if the condition is negated
622
1202
  // e.g., ternary else branch: isError ? <ErrorView /> : <SuccessView />
623
1203
  // SuccessView has isNegated: true, meaning it renders when isError is falsy
624
1204
  const isNegated = gatingCondition.isNegated === true;
625
1205
  gatingRequiredValues.push({
626
- attributePath: gatingResolution.resolvedPath,
1206
+ attributePath: resolvedGatingPath,
627
1207
  value: isNegated ? 'falsy' : 'truthy',
628
1208
  comparison: isNegated ? 'falsy' : 'truthy',
629
1209
  });
@@ -636,6 +1216,8 @@ export default function generateExecutionFlowsFromConditionals(args) {
636
1216
  const childCompoundChainIds = new Set(childData.compoundConditionals.map((c) => c.chainId).filter(Boolean));
637
1217
  for (const [_path, usages] of Object.entries(childData.conditionalUsages)) {
638
1218
  for (const usage of usages) {
1219
+ // Debug logging (disabled - set to true for troubleshooting child flow resolution)
1220
+ const shouldDebugChild = false;
639
1221
  // Skip usages that are part of compound conditionals (handled separately)
640
1222
  // Fix 33: Only skip if the chainId is in the child's compound conditionals
641
1223
  if (usage.chainId && childCompoundChainIds.has(usage.chainId)) {
@@ -647,30 +1229,156 @@ export default function generateExecutionFlowsFromConditionals(args) {
647
1229
  if (usage.derivedFrom?.sourcePath) {
648
1230
  childPath = usage.derivedFrom.sourcePath;
649
1231
  }
1232
+ if (shouldDebugChild) {
1233
+ console.log(`[DEBUG CHILD ${childName}] Processing usage path: ${usage.path}`);
1234
+ console.log(`[DEBUG CHILD ${childName}] childPath (after derivedFrom): ${childPath}`);
1235
+ console.log(`[DEBUG CHILD ${childName}] sourceDataPath: ${usage.sourceDataPath}`);
1236
+ }
650
1237
  // Translate the child path to a parent path
651
1238
  let translatedPath = translateChildPathToParent(childPath, childData.equivalentSignatureVariables, equivalentSignatureVariables, childName);
1239
+ if (shouldDebugChild) {
1240
+ console.log(`[DEBUG CHILD ${childName}] translatedPath (from translateChildPathToParent): ${translatedPath}`);
1241
+ }
652
1242
  // If translation failed but we have sourceDataPath, try to extract the prop path from it
653
1243
  // sourceDataPath format: "ChildName.signature[n].propPath.rest" → extract "propPath.rest"
654
1244
  if (!translatedPath && usage.sourceDataPath) {
655
1245
  const signatureMatch = usage.sourceDataPath.match(/\.signature\[\d+\]\.(.+)$/);
656
1246
  if (signatureMatch) {
657
1247
  translatedPath = signatureMatch[1];
1248
+ if (shouldDebugChild) {
1249
+ console.log(`[DEBUG CHILD ${childName}] translatedPath (from sourceDataPath regex): ${translatedPath}`);
1250
+ }
658
1251
  }
659
1252
  }
660
1253
  if (!translatedPath) {
661
1254
  // Could not translate - skip this usage
1255
+ if (shouldDebugChild) {
1256
+ console.log(`[DEBUG CHILD ${childName}] SKIPPED - no translation found`);
1257
+ }
662
1258
  continue;
663
1259
  }
664
1260
  // Now resolve the translated path in the parent context
1261
+ // First, try standard resolution
665
1262
  const resolution = resolvePathToControllable(translatedPath, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
666
- if (!resolution.isControllable || !resolution.resolvedPath) {
667
- // Path is not controllable in parent context
668
- continue;
1263
+ if (shouldDebugChild) {
1264
+ console.log(`[DEBUG CHILD ${childName}] resolvePathToControllable result:`);
1265
+ console.log(`[DEBUG CHILD ${childName}] isControllable: ${resolution.isControllable}`);
1266
+ console.log(`[DEBUG CHILD ${childName}] resolvedPath: ${resolution.resolvedPath}`);
1267
+ console.log(`[DEBUG CHILD ${childName}] chain: ${resolution.resolutionChain.join(' -> ')}`);
1268
+ }
1269
+ // Only create flows for controllable paths (whitelist approach).
1270
+ // If the path doesn't resolve to something in attributesMap, skip it.
1271
+ // This prevents creating flows for useState values which are not
1272
+ // controllable via mock data injection.
1273
+ let resolvedPath = resolution.resolvedPath;
1274
+ if (!resolution.isControllable || !resolvedPath) {
1275
+ // Path is not controllable via standard resolution.
1276
+ // Try fallback: For useState values (cyScope*().functionCallReturnValue),
1277
+ // look for a related URL parameter like "varNameFromUrl" that might
1278
+ // control the initial state.
1279
+ //
1280
+ // Example: viewMode → cyScope20().functionCallReturnValue (useState value)
1281
+ // Fallback: viewModeFromUrl → segments[2] (URL param that initializes the useState)
1282
+ const useStateMatch = translatedPath.match(/^cyScope\d+\(\)\.functionCallReturnValue$/);
1283
+ if (useStateMatch) {
1284
+ // Find what variable this useState value corresponds to by looking
1285
+ // for entries like "varName": "cyScope20()" in equivalentSignatureVariables
1286
+ const useStatePattern = translatedPath.replace(/\.functionCallReturnValue$/, ''); // e.g., "cyScope20()"
1287
+ // Find the variable name that maps to this useState
1288
+ let useStateVarName = null;
1289
+ for (const [varName, varPath] of Object.entries(equivalentSignatureVariables)) {
1290
+ if (varPath === useStatePattern) {
1291
+ useStateVarName = varName;
1292
+ break;
1293
+ }
1294
+ }
1295
+ if (shouldDebugChild) {
1296
+ console.log(`[DEBUG CHILD ${childName}] useState fallback: looking for URL param`);
1297
+ console.log(`[DEBUG CHILD ${childName}] useStatePattern: ${useStatePattern}`);
1298
+ console.log(`[DEBUG CHILD ${childName}] useStateVarName: ${useStateVarName}`);
1299
+ }
1300
+ if (useStateVarName) {
1301
+ // Look for a related URL param like "varNameFromUrl"
1302
+ const urlParamName = `${useStateVarName}FromUrl`;
1303
+ const urlParamPath = equivalentSignatureVariables[urlParamName];
1304
+ if (shouldDebugChild) {
1305
+ console.log(`[DEBUG CHILD ${childName}] urlParamName: ${urlParamName}`);
1306
+ console.log(`[DEBUG CHILD ${childName}] urlParamPath: ${urlParamPath}`);
1307
+ }
1308
+ if (urlParamPath) {
1309
+ // For useState values initialized from URL params, use the
1310
+ // URL param variable name directly (e.g., "viewModeFromUrl")
1311
+ // rather than fully resolving it. This keeps the path meaningful
1312
+ // for scenario generation and avoids overly generic paths like
1313
+ // "useParams().functionCallReturnValue.*".
1314
+ //
1315
+ // The flow will use the URL param name as the attributePath,
1316
+ // which gets properly resolved when generating mock data.
1317
+ resolvedPath = urlParamName;
1318
+ if (shouldDebugChild) {
1319
+ console.log(`[DEBUG CHILD ${childName}] useState fallback SUCCESS: using URL param name ${resolvedPath}`);
1320
+ }
1321
+ }
1322
+ }
1323
+ }
1324
+ // Fallback 2: Try sourceEquivalencies to find the actual data source
1325
+ // This handles the case where props flow through useState but originate
1326
+ // from a mockable data source (e.g., API call, fetcher).
1327
+ //
1328
+ // Example: WorkoutsView receives `workouts` prop which in parent is stored
1329
+ // in useState, but ultimately comes from a Supabase query.
1330
+ // sourceEquivalencies tells us: "WorkoutsView().signature[0].workouts" → "createClient()...data"
1331
+ if (!resolvedPath && sourceEquivalencies) {
1332
+ // Build the child prop path to look up in sourceEquivalencies
1333
+ // Format: "ChildName().signature[0].propName"
1334
+ // First, find what prop this child path maps to
1335
+ let childPropName = null;
1336
+ for (const [varName, varPath] of Object.entries(childData.equivalentSignatureVariables)) {
1337
+ // Check if childPath starts with this variable name
1338
+ // e.g., childPath = "workouts.length", varName = "workouts", varPath = "signature[0].workouts"
1339
+ if (childPath === varName ||
1340
+ childPath.startsWith(`${varName}.`)) {
1341
+ childPropName = varName;
1342
+ break;
1343
+ }
1344
+ }
1345
+ if (childPropName) {
1346
+ // Build the full sourceEquivalencies key
1347
+ const sourceEquivKey = `${childName}().signature[0].${childPropName}`;
1348
+ const sourceEquivEntry = sourceEquivalencies[sourceEquivKey];
1349
+ if (sourceEquivEntry && sourceEquivEntry.length > 0) {
1350
+ const dataSourcePath = sourceEquivEntry[0].schemaPath;
1351
+ // Check if this data source path is controllable
1352
+ const dataSourceResolution = resolvePathToControllable(dataSourcePath, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
1353
+ if (dataSourceResolution.isControllable &&
1354
+ dataSourceResolution.resolvedPath) {
1355
+ // Preserve any suffix from the child path
1356
+ // e.g., childPath = "workouts.length" → suffix = ".length"
1357
+ const suffix = childPath.startsWith(`${childPropName}.`)
1358
+ ? childPath.slice(childPropName.length)
1359
+ : '';
1360
+ resolvedPath = dataSourceResolution.resolvedPath + suffix;
1361
+ if (shouldDebugChild) {
1362
+ console.log(`[DEBUG CHILD ${childName}] sourceEquivalencies fallback SUCCESS: using data source ${resolvedPath}`);
1363
+ }
1364
+ }
1365
+ }
1366
+ }
1367
+ }
1368
+ // If still not resolved after fallback, skip
1369
+ if (!resolvedPath) {
1370
+ if (shouldDebugChild) {
1371
+ console.log(`[DEBUG CHILD ${childName}] SKIPPED - path not controllable`);
1372
+ }
1373
+ continue;
1374
+ }
669
1375
  }
670
- const resolvedPath = resolution.resolvedPath;
671
1376
  // Check for duplicates
672
1377
  const normalizedPath = normalizePathForDeduplication(resolvedPath, fullToShortPathMap);
673
1378
  if (seenNormalizedPaths.has(normalizedPath)) {
1379
+ if (shouldDebugChild) {
1380
+ console.log(`[DEBUG CHILD ${childName}] SKIPPED - duplicate normalized path: ${normalizedPath}`);
1381
+ }
674
1382
  continue;
675
1383
  }
676
1384
  seenNormalizedPaths.add(normalizedPath);
@@ -681,6 +1389,12 @@ export default function generateExecutionFlowsFromConditionals(args) {
681
1389
  path: resolvedPath,
682
1390
  };
683
1391
  const usageFlows = generateFlowsFromUsage(translatedUsage, resolvedPath);
1392
+ if (shouldDebugChild) {
1393
+ console.log(`[DEBUG CHILD ${childName}] GENERATING ${usageFlows.length} flows with resolvedPath: ${resolvedPath}`);
1394
+ for (const f of usageFlows) {
1395
+ console.log(`[DEBUG CHILD ${childName}] - Flow ID: ${f.id}, path: ${f.requiredValues[0]?.attributePath}`);
1396
+ }
1397
+ }
684
1398
  // Add gating conditions to each flow
685
1399
  for (const flow of usageFlows) {
686
1400
  // Add gating required values to the flow
@@ -710,28 +1424,218 @@ export default function generateExecutionFlowsFromConditionals(args) {
710
1424
  // Process child's compound conditionals
711
1425
  for (const compound of childData.compoundConditionals) {
712
1426
  const resolvedPaths = new Map();
713
- let allControllable = true;
1427
+ let allResolvable = true;
714
1428
  for (const condition of compound.conditions) {
715
1429
  // Determine the child path to translate
716
1430
  const childPath = condition.path;
717
1431
  // Translate the child path to a parent path
718
1432
  const translatedPath = translateChildPathToParent(childPath, childData.equivalentSignatureVariables, equivalentSignatureVariables, childName);
719
1433
  if (!translatedPath) {
720
- allControllable = false;
1434
+ allResolvable = false;
721
1435
  break;
722
1436
  }
723
1437
  const resolution = resolvePathToControllable(translatedPath, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
724
- if (!resolution.isControllable || !resolution.resolvedPath) {
725
- allControllable = false;
1438
+ // Only create flows for controllable paths (whitelist approach).
1439
+ // If the path doesn't resolve to something in attributesMap, skip it.
1440
+ // This prevents creating flows for useState values which are not
1441
+ // controllable via mock data injection.
1442
+ let resolvedPath = resolution.resolvedPath;
1443
+ if (!resolution.isControllable || !resolvedPath) {
1444
+ // Path is not controllable via standard resolution.
1445
+ // Try fallback: For useState values (cyScope*().functionCallReturnValue),
1446
+ // look for a related URL parameter like "varNameFromUrl" that might
1447
+ // control the initial state.
1448
+ const useStateMatch = translatedPath.match(/^cyScope\d+\(\)\.functionCallReturnValue$/);
1449
+ if (useStateMatch) {
1450
+ const useStatePattern = translatedPath.replace(/\.functionCallReturnValue$/, '');
1451
+ // Find the variable name that maps to this useState
1452
+ let useStateVarName = null;
1453
+ for (const [varName, varPath] of Object.entries(equivalentSignatureVariables)) {
1454
+ if (varPath === useStatePattern) {
1455
+ useStateVarName = varName;
1456
+ break;
1457
+ }
1458
+ }
1459
+ if (useStateVarName) {
1460
+ const urlParamName = `${useStateVarName}FromUrl`;
1461
+ const urlParamPath = equivalentSignatureVariables[urlParamName];
1462
+ if (urlParamPath) {
1463
+ resolvedPath = urlParamName;
1464
+ }
1465
+ }
1466
+ }
1467
+ }
1468
+ if (!resolvedPath) {
1469
+ allResolvable = false;
726
1470
  break;
727
1471
  }
728
- resolvedPaths.set(condition.path, resolution.resolvedPath);
1472
+ resolvedPaths.set(condition.path, resolvedPath);
729
1473
  }
730
- if (allControllable && resolvedPaths.size > 0) {
1474
+ if (allResolvable && resolvedPaths.size > 0) {
731
1475
  const compoundFlow = generateFlowFromCompound(compound, resolvedPaths);
732
- if (compoundFlow && !seenFlowIds.has(compoundFlow.id)) {
733
- seenFlowIds.add(compoundFlow.id);
734
- flows.push(compoundFlow);
1476
+ if (compoundFlow) {
1477
+ // Add gating conditions to compound flow (same as regular usage flows)
1478
+ if (gatingRequiredValues.length > 0) {
1479
+ // Filter out any gating values that are already in the flow
1480
+ const existingPaths = new Set(compoundFlow.requiredValues.map((rv) => rv.attributePath));
1481
+ const newGatingValues = gatingRequiredValues.filter((gv) => !existingPaths.has(gv.attributePath));
1482
+ compoundFlow.requiredValues = [
1483
+ ...compoundFlow.requiredValues,
1484
+ ...newGatingValues,
1485
+ ];
1486
+ // Update the flow ID to include gating conditions
1487
+ if (newGatingValues.length > 0) {
1488
+ const gatingIdPart = newGatingValues
1489
+ .map((gv) => `${gv.attributePath}-${gv.value}`)
1490
+ .join('-');
1491
+ compoundFlow.id = `${compoundFlow.id}-gated-${gatingIdPart}`;
1492
+ }
1493
+ }
1494
+ if (!seenFlowIds.has(compoundFlow.id)) {
1495
+ seenFlowIds.add(compoundFlow.id);
1496
+ flows.push(compoundFlow);
1497
+ }
1498
+ }
1499
+ }
1500
+ }
1501
+ // Process child's jsxRenderingUsages (array.map flows)
1502
+ // This generates array variation flows (empty, few, many) for arrays rendered in child
1503
+ if (childData.jsxRenderingUsages) {
1504
+ for (const jsxUsage of childData.jsxRenderingUsages) {
1505
+ // Translate the child path to a parent path
1506
+ const translatedPath = translateChildPathToParent(jsxUsage.path, childData.equivalentSignatureVariables, equivalentSignatureVariables, childName);
1507
+ if (!translatedPath) {
1508
+ continue;
1509
+ }
1510
+ // Resolve to controllable path in parent context
1511
+ const resolution = resolvePathToControllable(translatedPath, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
1512
+ let resolvedPath = resolution.resolvedPath;
1513
+ // Try sourceEquivalencies fallback if not controllable
1514
+ if (!resolution.isControllable || !resolvedPath) {
1515
+ if (sourceEquivalencies) {
1516
+ // Build the sourceEquivalencies key
1517
+ // The child path (e.g., "workouts") maps to a prop path (e.g., "signature[0].workouts")
1518
+ let childPropName = null;
1519
+ for (const [varName, varPath] of Object.entries(childData.equivalentSignatureVariables)) {
1520
+ if (jsxUsage.path === varName ||
1521
+ jsxUsage.path.startsWith(`${varName}.`)) {
1522
+ childPropName = varName;
1523
+ break;
1524
+ }
1525
+ }
1526
+ if (childPropName) {
1527
+ const sourceEquivKey = `${childName}().signature[0].${childPropName}`;
1528
+ const sourceEquivEntry = sourceEquivalencies[sourceEquivKey];
1529
+ if (sourceEquivEntry && sourceEquivEntry.length > 0) {
1530
+ const dataSourcePath = sourceEquivEntry[0].schemaPath;
1531
+ const dataSourceResolution = resolvePathToControllable(dataSourcePath, attributesMap, equivalentSignatureVariables, fullToShortPathMap);
1532
+ if (dataSourceResolution.isControllable &&
1533
+ dataSourceResolution.resolvedPath) {
1534
+ resolvedPath = dataSourceResolution.resolvedPath;
1535
+ }
1536
+ }
1537
+ }
1538
+ }
1539
+ }
1540
+ if (!resolvedPath) {
1541
+ continue;
1542
+ }
1543
+ // Check for duplicates
1544
+ const normalizedPath = normalizePathForDeduplication(resolvedPath, fullToShortPathMap);
1545
+ const dedupeKey = `${normalizedPath}:${jsxUsage.renderingType}`;
1546
+ if (seenNormalizedPaths.has(dedupeKey)) {
1547
+ continue;
1548
+ }
1549
+ seenNormalizedPaths.add(dedupeKey);
1550
+ // Generate array variation flows for array-map rendering
1551
+ if (jsxUsage.renderingType === 'array-map') {
1552
+ const baseName = generateNameFromPath(resolvedPath);
1553
+ const pathSlug = pathToSlug(resolvedPath);
1554
+ const exclusiveGroup = `array-length-${pathSlug}`;
1555
+ // Empty array flow
1556
+ const emptyFlow = {
1557
+ id: `${pathSlug}-empty-array`,
1558
+ name: `${baseName} Empty`,
1559
+ description: `When ${baseName.toLowerCase()} array is empty`,
1560
+ requiredValues: [
1561
+ {
1562
+ attributePath: resolvedPath,
1563
+ value: '0',
1564
+ comparison: 'length<',
1565
+ valueType: 'array',
1566
+ },
1567
+ ...gatingRequiredValues,
1568
+ ],
1569
+ impact: 'medium',
1570
+ exclusiveGroup,
1571
+ sourceLocation: jsxUsage.sourceLocation
1572
+ ? {
1573
+ lineNumber: jsxUsage.sourceLocation.lineNumber,
1574
+ column: jsxUsage.sourceLocation.column,
1575
+ }
1576
+ : undefined,
1577
+ codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
1578
+ };
1579
+ if (!seenFlowIds.has(emptyFlow.id)) {
1580
+ seenFlowIds.add(emptyFlow.id);
1581
+ flows.push(emptyFlow);
1582
+ }
1583
+ // Few items flow (1-3)
1584
+ const fewFlow = {
1585
+ id: `${pathSlug}-few-items`,
1586
+ name: `${baseName} Few Items`,
1587
+ description: `When ${baseName.toLowerCase()} array has 1-3 items`,
1588
+ requiredValues: [
1589
+ {
1590
+ attributePath: resolvedPath,
1591
+ value: '3',
1592
+ comparison: 'length<',
1593
+ valueType: 'array',
1594
+ },
1595
+ ...gatingRequiredValues,
1596
+ ],
1597
+ impact: 'low',
1598
+ exclusiveGroup,
1599
+ sourceLocation: jsxUsage.sourceLocation
1600
+ ? {
1601
+ lineNumber: jsxUsage.sourceLocation.lineNumber,
1602
+ column: jsxUsage.sourceLocation.column,
1603
+ }
1604
+ : undefined,
1605
+ codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
1606
+ };
1607
+ if (!seenFlowIds.has(fewFlow.id)) {
1608
+ seenFlowIds.add(fewFlow.id);
1609
+ flows.push(fewFlow);
1610
+ }
1611
+ // Many items flow (10+)
1612
+ const manyFlow = {
1613
+ id: `${pathSlug}-many-items`,
1614
+ name: `${baseName} Many Items`,
1615
+ description: `When ${baseName.toLowerCase()} array has many items`,
1616
+ requiredValues: [
1617
+ {
1618
+ attributePath: resolvedPath,
1619
+ value: '10',
1620
+ comparison: 'length>',
1621
+ valueType: 'array',
1622
+ },
1623
+ ...gatingRequiredValues,
1624
+ ],
1625
+ impact: 'low',
1626
+ exclusiveGroup,
1627
+ sourceLocation: jsxUsage.sourceLocation
1628
+ ? {
1629
+ lineNumber: jsxUsage.sourceLocation.lineNumber,
1630
+ column: jsxUsage.sourceLocation.column,
1631
+ }
1632
+ : undefined,
1633
+ codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
1634
+ };
1635
+ if (!seenFlowIds.has(manyFlow.id)) {
1636
+ seenFlowIds.add(manyFlow.id);
1637
+ flows.push(manyFlow);
1638
+ }
735
1639
  }
736
1640
  }
737
1641
  }