@codeyam/codeyam-cli 0.1.0-staging.596f0eb → 0.1.0-staging.6e699e5

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 (696) hide show
  1. package/analyzer-template/.build-info.json +8 -8
  2. package/analyzer-template/common/execAsync.ts +1 -1
  3. package/analyzer-template/log.txt +3 -3
  4. package/analyzer-template/package.json +10 -6
  5. package/analyzer-template/packages/ai/index.ts +10 -3
  6. package/analyzer-template/packages/ai/package.json +1 -1
  7. package/analyzer-template/packages/ai/src/lib/__mocks__/completionCall.ts +122 -0
  8. package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +128 -6
  9. package/analyzer-template/packages/ai/src/lib/astScopes/arrayDerivationDetector.ts +199 -0
  10. package/analyzer-template/packages/ai/src/lib/astScopes/astScopeAnalyzer.ts +138 -1
  11. package/analyzer-template/packages/ai/src/lib/astScopes/conditionalEffectsExtractor.ts +644 -0
  12. package/analyzer-template/packages/ai/src/lib/astScopes/methodSemantics.ts +140 -6
  13. package/analyzer-template/packages/ai/src/lib/astScopes/patterns/ifStatementHandler.ts +18 -0
  14. package/analyzer-template/packages/ai/src/lib/astScopes/patterns/switchStatementHandler.ts +38 -1
  15. package/analyzer-template/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.ts +181 -1
  16. package/analyzer-template/packages/ai/src/lib/astScopes/processExpression.ts +1239 -104
  17. package/analyzer-template/packages/ai/src/lib/astScopes/types.ts +304 -0
  18. package/analyzer-template/packages/ai/src/lib/checkAllAttributes.ts +29 -10
  19. package/analyzer-template/packages/ai/src/lib/completionCall.ts +216 -36
  20. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +1501 -138
  21. package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/JavascriptFrameworkManager.ts +2 -1
  22. package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/MuiManager.ts +976 -0
  23. package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/ReactFrameworkManager.ts +243 -77
  24. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +19 -1
  25. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +103 -6
  26. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.ts +23 -0
  27. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertNullToUndefinedBySchema.ts +98 -0
  28. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts +42 -2
  29. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.ts +129 -0
  30. package/analyzer-template/packages/ai/src/lib/dataStructureChunking.ts +156 -0
  31. package/analyzer-template/packages/ai/src/lib/deepEqual.ts +30 -0
  32. package/analyzer-template/packages/ai/src/lib/e2eDataTracking.ts +334 -0
  33. package/analyzer-template/packages/ai/src/lib/extractCriticalDataKeys.ts +120 -0
  34. package/analyzer-template/packages/ai/src/lib/generateChangesEntityScenarioData.ts +74 -7
  35. package/analyzer-template/packages/ai/src/lib/generateChangesEntityScenarios.ts +89 -112
  36. package/analyzer-template/packages/ai/src/lib/generateEntityDataStructure.ts +6 -0
  37. package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +1111 -91
  38. package/analyzer-template/packages/ai/src/lib/generateEntityScenarios.ts +207 -104
  39. package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +570 -0
  40. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.ts +528 -0
  41. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionals.ts +1977 -0
  42. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.ts +239 -0
  43. package/analyzer-template/packages/ai/src/lib/getConditionalUsagesFromCode.ts +143 -31
  44. package/analyzer-template/packages/ai/src/lib/guessScenarioDataFromDescription.ts +8 -2
  45. package/analyzer-template/packages/ai/src/lib/isolateScopes.ts +276 -3
  46. package/analyzer-template/packages/ai/src/lib/mergeStatements.ts +33 -3
  47. package/analyzer-template/packages/ai/src/lib/promptGenerators/gatherAttributesMap.ts +7 -0
  48. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateChangesEntityScenarioDataGenerator.ts +1 -1
  49. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateChangesEntityScenariosGenerator.ts +32 -102
  50. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateChunkPrompt.ts +82 -0
  51. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateCriticalKeysPrompt.ts +103 -0
  52. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.ts +90 -6
  53. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateEntityScenariosGenerator.ts +14 -53
  54. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateMissingKeysPrompt.ts +58 -0
  55. package/analyzer-template/packages/ai/src/lib/promptGenerators/guessNewScenarioDataFromDescriptionGenerator.ts +28 -2
  56. package/analyzer-template/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.ts +391 -0
  57. package/analyzer-template/packages/ai/src/lib/resolvePathToControllable.ts +812 -0
  58. package/analyzer-template/packages/ai/src/lib/splitOutsideParentheses.ts +5 -1
  59. package/analyzer-template/packages/ai/src/lib/validateExecutionFlowPaths.ts +531 -0
  60. package/analyzer-template/packages/ai/src/lib/worker/SerializableDataStructure.ts +123 -0
  61. package/analyzer-template/packages/ai/src/lib/worker/analyzeScopeWorker.ts +8 -1
  62. package/analyzer-template/packages/analyze/src/lib/FileAnalyzer.ts +14 -0
  63. package/analyzer-template/packages/analyze/src/lib/analysisContext.ts +44 -4
  64. package/analyzer-template/packages/analyze/src/lib/asts/nodes/index.ts +1 -0
  65. package/analyzer-template/packages/analyze/src/lib/asts/nodes/isAsyncFunction.ts +67 -0
  66. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +455 -267
  67. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +18 -0
  68. package/analyzer-template/packages/analyze/src/lib/files/analyze/findOrCreateEntity.ts +3 -0
  69. package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +33 -7
  70. package/analyzer-template/packages/analyze/src/lib/files/analyzeChange.ts +31 -15
  71. package/analyzer-template/packages/analyze/src/lib/files/analyzeEntity.ts +11 -7
  72. package/analyzer-template/packages/analyze/src/lib/files/analyzeInitial.ts +11 -12
  73. package/analyzer-template/packages/analyze/src/lib/files/enums/steps.ts +1 -1
  74. package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +265 -0
  75. package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichUnknownTypesFromSourceEquivalencies.ts +102 -0
  76. package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +588 -52
  77. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateChangesScenarioData.ts +1 -1
  78. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateChangesScenarios.ts +28 -62
  79. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +336 -133
  80. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +156 -0
  81. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateScenarioData.ts +78 -83
  82. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateScenarios.ts +4 -8
  83. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +461 -94
  84. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.ts +56 -11
  85. package/analyzer-template/packages/aws/codebuild/index.ts +1 -0
  86. package/analyzer-template/packages/aws/dist/src/lib/codebuild/waitForBuild.d.ts +11 -1
  87. package/analyzer-template/packages/aws/dist/src/lib/codebuild/waitForBuild.d.ts.map +1 -1
  88. package/analyzer-template/packages/aws/dist/src/lib/codebuild/waitForBuild.js +29 -18
  89. package/analyzer-template/packages/aws/dist/src/lib/codebuild/waitForBuild.js.map +1 -1
  90. package/analyzer-template/packages/aws/dist/src/lib/ecs/ecsDefineContainer.d.ts +2 -2
  91. package/analyzer-template/packages/aws/dist/src/lib/ecs/ecsDefineContainer.d.ts.map +1 -1
  92. package/analyzer-template/packages/aws/dist/src/lib/ecs/ecsDefineContainer.js +2 -2
  93. package/analyzer-template/packages/aws/dist/src/lib/ecs/ecsDefineContainer.js.map +1 -1
  94. package/analyzer-template/packages/aws/dist/src/lib/ecs/ecsTaskFactory.d.ts +8 -18
  95. package/analyzer-template/packages/aws/dist/src/lib/ecs/ecsTaskFactory.d.ts.map +1 -1
  96. package/analyzer-template/packages/aws/dist/src/lib/ecs/ecsTaskFactory.js +17 -61
  97. package/analyzer-template/packages/aws/dist/src/lib/ecs/ecsTaskFactory.js.map +1 -1
  98. package/analyzer-template/packages/aws/dist/src/lib/s3/checkS3ObjectExists.d.ts +15 -0
  99. package/analyzer-template/packages/aws/dist/src/lib/s3/checkS3ObjectExists.d.ts.map +1 -0
  100. package/analyzer-template/packages/aws/dist/src/lib/s3/checkS3ObjectExists.js +31 -0
  101. package/analyzer-template/packages/aws/dist/src/lib/s3/checkS3ObjectExists.js.map +1 -0
  102. package/analyzer-template/packages/aws/dist/src/lib/s3/uploadFileToS3.d.ts.map +1 -1
  103. package/analyzer-template/packages/aws/dist/src/lib/s3/uploadFileToS3.js +8 -1
  104. package/analyzer-template/packages/aws/dist/src/lib/s3/uploadFileToS3.js.map +1 -1
  105. package/analyzer-template/packages/aws/package.json +3 -3
  106. package/analyzer-template/packages/aws/s3/index.ts +1 -0
  107. package/analyzer-template/packages/aws/src/lib/codebuild/waitForBuild.ts +43 -19
  108. package/analyzer-template/packages/aws/src/lib/ecs/ecsDefineContainer.ts +3 -3
  109. package/analyzer-template/packages/aws/src/lib/ecs/ecsTaskFactory.ts +17 -69
  110. package/analyzer-template/packages/aws/src/lib/s3/checkS3ObjectExists.ts +47 -0
  111. package/analyzer-template/packages/aws/src/lib/s3/uploadFileToS3.ts +8 -1
  112. package/analyzer-template/packages/database/src/lib/kysely/db.ts +4 -4
  113. package/analyzer-template/packages/database/src/lib/kysely/tableRelations.ts +2 -2
  114. package/analyzer-template/packages/database/src/lib/kysely/tables/debugReportsTable.ts +36 -9
  115. package/analyzer-template/packages/database/src/lib/loadReadyToBeCapturedAnalyses.ts +7 -3
  116. package/analyzer-template/packages/generate/index.ts +3 -0
  117. package/analyzer-template/packages/generate/src/lib/componentScenarioPage/componentScenarioPageNext.ts +17 -1
  118. package/analyzer-template/packages/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.ts +193 -0
  119. package/analyzer-template/packages/generate/src/lib/componentScenarioPage/generateScenarioServerComponent.ts +73 -0
  120. package/analyzer-template/packages/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.ts +9 -4
  121. package/analyzer-template/packages/generate/src/lib/deepMerge.ts +26 -1
  122. package/analyzer-template/packages/generate/src/lib/scenarioComponentForServer.ts +114 -0
  123. package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.d.ts +2 -2
  124. package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.js +2 -2
  125. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tableRelations.d.ts +2 -2
  126. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/analysesTable.d.ts +1 -11
  127. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/analysesTable.d.ts.map +1 -1
  128. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/debugReportsTable.d.ts +30 -7
  129. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/debugReportsTable.d.ts.map +1 -1
  130. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/debugReportsTable.js +9 -3
  131. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/debugReportsTable.js.map +1 -1
  132. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/entitiesTable.d.ts +1 -0
  133. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/entitiesTable.d.ts.map +1 -1
  134. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/scenariosTable.d.ts +2 -6
  135. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/scenariosTable.d.ts.map +1 -1
  136. package/analyzer-template/packages/github/dist/database/src/lib/loadReadyToBeCapturedAnalyses.d.ts.map +1 -1
  137. package/analyzer-template/packages/github/dist/database/src/lib/loadReadyToBeCapturedAnalyses.js +7 -4
  138. package/analyzer-template/packages/github/dist/database/src/lib/loadReadyToBeCapturedAnalyses.js.map +1 -1
  139. package/analyzer-template/packages/github/dist/generate/index.d.ts +3 -0
  140. package/analyzer-template/packages/github/dist/generate/index.d.ts.map +1 -1
  141. package/analyzer-template/packages/github/dist/generate/index.js +3 -0
  142. package/analyzer-template/packages/github/dist/generate/index.js.map +1 -1
  143. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/componentScenarioPageNext.d.ts.map +1 -1
  144. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/componentScenarioPageNext.js +16 -1
  145. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/componentScenarioPageNext.js.map +1 -1
  146. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.d.ts +9 -0
  147. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.d.ts.map +1 -0
  148. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.js +189 -0
  149. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.js.map +1 -0
  150. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/generateScenarioServerComponent.d.ts +20 -0
  151. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/generateScenarioServerComponent.d.ts.map +1 -0
  152. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/generateScenarioServerComponent.js +53 -0
  153. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/generateScenarioServerComponent.js.map +1 -0
  154. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.d.ts.map +1 -1
  155. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.js +8 -4
  156. package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.js.map +1 -1
  157. package/analyzer-template/packages/github/dist/generate/src/lib/deepMerge.d.ts.map +1 -1
  158. package/analyzer-template/packages/github/dist/generate/src/lib/deepMerge.js +27 -1
  159. package/analyzer-template/packages/github/dist/generate/src/lib/deepMerge.js.map +1 -1
  160. package/analyzer-template/packages/github/dist/generate/src/lib/scenarioComponentForServer.d.ts +8 -0
  161. package/analyzer-template/packages/github/dist/generate/src/lib/scenarioComponentForServer.d.ts.map +1 -0
  162. package/analyzer-template/packages/github/dist/generate/src/lib/scenarioComponentForServer.js +89 -0
  163. package/analyzer-template/packages/github/dist/generate/src/lib/scenarioComponentForServer.js.map +1 -0
  164. package/analyzer-template/packages/github/dist/github/src/lib/loadOrCreateCommit.d.ts.map +1 -1
  165. package/analyzer-template/packages/github/dist/github/src/lib/loadOrCreateCommit.js +10 -0
  166. package/analyzer-template/packages/github/dist/github/src/lib/loadOrCreateCommit.js.map +1 -1
  167. package/analyzer-template/packages/github/dist/github/src/lib/syncPrimaryBranch.d.ts.map +1 -1
  168. package/analyzer-template/packages/github/dist/github/src/lib/syncPrimaryBranch.js +3 -0
  169. package/analyzer-template/packages/github/dist/github/src/lib/syncPrimaryBranch.js.map +1 -1
  170. package/analyzer-template/packages/github/dist/types/index.d.ts +2 -2
  171. package/analyzer-template/packages/github/dist/types/index.d.ts.map +1 -1
  172. package/analyzer-template/packages/github/dist/types/index.js.map +1 -1
  173. package/analyzer-template/packages/github/dist/types/src/types/Analysis.d.ts +87 -13
  174. package/analyzer-template/packages/github/dist/types/src/types/Analysis.d.ts.map +1 -1
  175. package/analyzer-template/packages/github/dist/types/src/types/Entity.d.ts +2 -0
  176. package/analyzer-template/packages/github/dist/types/src/types/Entity.d.ts.map +1 -1
  177. package/analyzer-template/packages/github/dist/types/src/types/Scenario.d.ts +11 -6
  178. package/analyzer-template/packages/github/dist/types/src/types/Scenario.d.ts.map +1 -1
  179. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts +196 -0
  180. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  181. package/analyzer-template/packages/github/dist/types/src/types/StatementInfo.d.ts +2 -0
  182. package/analyzer-template/packages/github/dist/types/src/types/StatementInfo.d.ts.map +1 -1
  183. package/analyzer-template/packages/github/dist/utils/src/lib/lightweightEntityExtractor.d.ts.map +1 -1
  184. package/analyzer-template/packages/github/dist/utils/src/lib/lightweightEntityExtractor.js +25 -0
  185. package/analyzer-template/packages/github/dist/utils/src/lib/lightweightEntityExtractor.js.map +1 -1
  186. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.d.ts +9 -1
  187. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.d.ts.map +1 -1
  188. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.js +29 -3
  189. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.js.map +1 -1
  190. package/analyzer-template/packages/github/src/lib/loadOrCreateCommit.ts +14 -0
  191. package/analyzer-template/packages/github/src/lib/syncPrimaryBranch.ts +2 -0
  192. package/analyzer-template/packages/process/index.ts +2 -0
  193. package/analyzer-template/packages/process/package.json +12 -0
  194. package/analyzer-template/packages/process/tsconfig.json +8 -0
  195. package/analyzer-template/packages/types/index.ts +5 -0
  196. package/analyzer-template/packages/types/src/types/Analysis.ts +104 -13
  197. package/analyzer-template/packages/types/src/types/Entity.ts +2 -0
  198. package/analyzer-template/packages/types/src/types/Scenario.ts +11 -10
  199. package/analyzer-template/packages/types/src/types/ScenariosDataStructure.ts +224 -0
  200. package/analyzer-template/packages/types/src/types/StatementInfo.ts +2 -0
  201. package/analyzer-template/packages/ui-components/src/components/ScenarioDetailInteractiveView.tsx +23 -7
  202. package/analyzer-template/packages/utils/dist/types/index.d.ts +2 -2
  203. package/analyzer-template/packages/utils/dist/types/index.d.ts.map +1 -1
  204. package/analyzer-template/packages/utils/dist/types/index.js.map +1 -1
  205. package/analyzer-template/packages/utils/dist/types/src/types/Analysis.d.ts +87 -13
  206. package/analyzer-template/packages/utils/dist/types/src/types/Analysis.d.ts.map +1 -1
  207. package/analyzer-template/packages/utils/dist/types/src/types/Entity.d.ts +2 -0
  208. package/analyzer-template/packages/utils/dist/types/src/types/Entity.d.ts.map +1 -1
  209. package/analyzer-template/packages/utils/dist/types/src/types/Scenario.d.ts +11 -6
  210. package/analyzer-template/packages/utils/dist/types/src/types/Scenario.d.ts.map +1 -1
  211. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts +196 -0
  212. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  213. package/analyzer-template/packages/utils/dist/types/src/types/StatementInfo.d.ts +2 -0
  214. package/analyzer-template/packages/utils/dist/types/src/types/StatementInfo.d.ts.map +1 -1
  215. package/analyzer-template/packages/utils/dist/utils/src/lib/lightweightEntityExtractor.d.ts.map +1 -1
  216. package/analyzer-template/packages/utils/dist/utils/src/lib/lightweightEntityExtractor.js +25 -0
  217. package/analyzer-template/packages/utils/dist/utils/src/lib/lightweightEntityExtractor.js.map +1 -1
  218. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.d.ts +9 -1
  219. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.d.ts.map +1 -1
  220. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.js +29 -3
  221. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.js.map +1 -1
  222. package/analyzer-template/packages/utils/src/lib/lightweightEntityExtractor.ts +27 -0
  223. package/analyzer-template/packages/utils/src/lib/safeFileName.ts +48 -3
  224. package/analyzer-template/playwright/capture.ts +37 -18
  225. package/analyzer-template/playwright/getCodeYamInfo.ts +12 -7
  226. package/analyzer-template/playwright/takeElementScreenshot.ts +26 -11
  227. package/analyzer-template/playwright/takeScreenshot.ts +9 -7
  228. package/analyzer-template/playwright/waitForServer.ts +21 -6
  229. package/analyzer-template/project/analyzeBaselineCommit.ts +4 -0
  230. package/analyzer-template/project/analyzeBranchCommit.ts +4 -0
  231. package/analyzer-template/project/analyzeFileEntities.ts +4 -0
  232. package/analyzer-template/project/analyzeRegularCommit.ts +4 -0
  233. package/analyzer-template/project/constructMockCode.ts +1181 -160
  234. package/analyzer-template/project/controller/startController.ts +16 -1
  235. package/analyzer-template/project/executeLibraryFunctionDirect.ts +7 -3
  236. package/analyzer-template/project/mocks/analyzeFileMock.ts +8 -7
  237. package/analyzer-template/project/orchestrateCapture/KyselyAnalysisLoader.ts +3 -6
  238. package/analyzer-template/project/orchestrateCapture/SequentialCaptureTaskRunner.ts +82 -36
  239. package/analyzer-template/project/orchestrateCapture.ts +36 -3
  240. package/analyzer-template/project/reconcileMockDataKeys.ts +245 -2
  241. package/analyzer-template/project/runAnalysis.ts +11 -0
  242. package/analyzer-template/project/runMultiScenarioServer.ts +11 -10
  243. package/analyzer-template/project/serverOnlyModules.ts +194 -21
  244. package/analyzer-template/project/start.ts +26 -4
  245. package/analyzer-template/project/startScenarioCapture.ts +79 -41
  246. package/analyzer-template/project/writeMockDataTsx.ts +232 -57
  247. package/analyzer-template/project/writeScenarioClientWrapper.ts +21 -0
  248. package/analyzer-template/project/writeScenarioComponents.ts +769 -181
  249. package/analyzer-template/project/writeScenarioFiles.ts +26 -0
  250. package/analyzer-template/project/writeSimpleRoot.ts +13 -15
  251. package/analyzer-template/scripts/comboWorkerLoop.cjs +1 -0
  252. package/analyzer-template/scripts/defaultCmd.sh +9 -0
  253. package/analyzer-template/tsconfig.json +2 -1
  254. package/background/src/lib/local/createLocalAnalyzer.js +1 -29
  255. package/background/src/lib/local/createLocalAnalyzer.js.map +1 -1
  256. package/background/src/lib/local/execAsync.js +1 -1
  257. package/background/src/lib/local/execAsync.js.map +1 -1
  258. package/background/src/lib/virtualized/common/execAsync.js +1 -1
  259. package/background/src/lib/virtualized/common/execAsync.js.map +1 -1
  260. package/background/src/lib/virtualized/project/analyzeBaselineCommit.js +2 -1
  261. package/background/src/lib/virtualized/project/analyzeBaselineCommit.js.map +1 -1
  262. package/background/src/lib/virtualized/project/analyzeBranchCommit.js +2 -1
  263. package/background/src/lib/virtualized/project/analyzeBranchCommit.js.map +1 -1
  264. package/background/src/lib/virtualized/project/analyzeFileEntities.js +2 -1
  265. package/background/src/lib/virtualized/project/analyzeFileEntities.js.map +1 -1
  266. package/background/src/lib/virtualized/project/analyzeRegularCommit.js +2 -1
  267. package/background/src/lib/virtualized/project/analyzeRegularCommit.js.map +1 -1
  268. package/background/src/lib/virtualized/project/constructMockCode.js +1053 -124
  269. package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
  270. package/background/src/lib/virtualized/project/controller/startController.js +11 -1
  271. package/background/src/lib/virtualized/project/controller/startController.js.map +1 -1
  272. package/background/src/lib/virtualized/project/executeLibraryFunctionDirect.js +6 -3
  273. package/background/src/lib/virtualized/project/executeLibraryFunctionDirect.js.map +1 -1
  274. package/background/src/lib/virtualized/project/mocks/analyzeFileMock.js +7 -7
  275. package/background/src/lib/virtualized/project/mocks/analyzeFileMock.js.map +1 -1
  276. package/background/src/lib/virtualized/project/orchestrateCapture/KyselyAnalysisLoader.js +3 -2
  277. package/background/src/lib/virtualized/project/orchestrateCapture/KyselyAnalysisLoader.js.map +1 -1
  278. package/background/src/lib/virtualized/project/orchestrateCapture/SequentialCaptureTaskRunner.js +69 -32
  279. package/background/src/lib/virtualized/project/orchestrateCapture/SequentialCaptureTaskRunner.js.map +1 -1
  280. package/background/src/lib/virtualized/project/orchestrateCapture.js +27 -4
  281. package/background/src/lib/virtualized/project/orchestrateCapture.js.map +1 -1
  282. package/background/src/lib/virtualized/project/reconcileMockDataKeys.js +204 -2
  283. package/background/src/lib/virtualized/project/reconcileMockDataKeys.js.map +1 -1
  284. package/background/src/lib/virtualized/project/runAnalysis.js +9 -0
  285. package/background/src/lib/virtualized/project/runAnalysis.js.map +1 -1
  286. package/background/src/lib/virtualized/project/runMultiScenarioServer.js +11 -9
  287. package/background/src/lib/virtualized/project/runMultiScenarioServer.js.map +1 -1
  288. package/background/src/lib/virtualized/project/serverOnlyModules.js +163 -23
  289. package/background/src/lib/virtualized/project/serverOnlyModules.js.map +1 -1
  290. package/background/src/lib/virtualized/project/start.js +21 -4
  291. package/background/src/lib/virtualized/project/start.js.map +1 -1
  292. package/background/src/lib/virtualized/project/startScenarioCapture.js +61 -31
  293. package/background/src/lib/virtualized/project/startScenarioCapture.js.map +1 -1
  294. package/background/src/lib/virtualized/project/writeMockDataTsx.js +199 -50
  295. package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
  296. package/background/src/lib/virtualized/project/writeScenarioClientWrapper.js +15 -0
  297. package/background/src/lib/virtualized/project/writeScenarioClientWrapper.js.map +1 -0
  298. package/background/src/lib/virtualized/project/writeScenarioComponents.js +552 -125
  299. package/background/src/lib/virtualized/project/writeScenarioComponents.js.map +1 -1
  300. package/background/src/lib/virtualized/project/writeScenarioFiles.js +19 -0
  301. package/background/src/lib/virtualized/project/writeScenarioFiles.js.map +1 -1
  302. package/background/src/lib/virtualized/project/writeSimpleRoot.js +13 -13
  303. package/background/src/lib/virtualized/project/writeSimpleRoot.js.map +1 -1
  304. package/codeyam-cli/src/cli.js +7 -1
  305. package/codeyam-cli/src/cli.js.map +1 -1
  306. package/codeyam-cli/src/commands/analyze.js +1 -1
  307. package/codeyam-cli/src/commands/analyze.js.map +1 -1
  308. package/codeyam-cli/src/commands/baseline.js +174 -0
  309. package/codeyam-cli/src/commands/baseline.js.map +1 -0
  310. package/codeyam-cli/src/commands/debug.js +40 -18
  311. package/codeyam-cli/src/commands/debug.js.map +1 -1
  312. package/codeyam-cli/src/commands/default.js +0 -15
  313. package/codeyam-cli/src/commands/default.js.map +1 -1
  314. package/codeyam-cli/src/commands/recapture.js +226 -0
  315. package/codeyam-cli/src/commands/recapture.js.map +1 -0
  316. package/codeyam-cli/src/commands/report.js +72 -24
  317. package/codeyam-cli/src/commands/report.js.map +1 -1
  318. package/codeyam-cli/src/commands/start.js +8 -12
  319. package/codeyam-cli/src/commands/start.js.map +1 -1
  320. package/codeyam-cli/src/commands/status.js +23 -1
  321. package/codeyam-cli/src/commands/status.js.map +1 -1
  322. package/codeyam-cli/src/commands/test-startup.js +1 -1
  323. package/codeyam-cli/src/commands/test-startup.js.map +1 -1
  324. package/codeyam-cli/src/commands/wipe.js +108 -0
  325. package/codeyam-cli/src/commands/wipe.js.map +1 -0
  326. package/codeyam-cli/src/utils/__tests__/serverVersionStaleness.test.js +81 -0
  327. package/codeyam-cli/src/utils/__tests__/serverVersionStaleness.test.js.map +1 -0
  328. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +31 -27
  329. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  330. package/codeyam-cli/src/utils/analysisRunner.js +8 -13
  331. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  332. package/codeyam-cli/src/utils/backgroundServer.js +14 -4
  333. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  334. package/codeyam-cli/src/utils/database.js +91 -5
  335. package/codeyam-cli/src/utils/database.js.map +1 -1
  336. package/codeyam-cli/src/utils/generateReport.js +253 -106
  337. package/codeyam-cli/src/utils/generateReport.js.map +1 -1
  338. package/codeyam-cli/src/utils/git.js +79 -0
  339. package/codeyam-cli/src/utils/git.js.map +1 -0
  340. package/codeyam-cli/src/utils/install-skills.js +31 -17
  341. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  342. package/codeyam-cli/src/utils/queue/__tests__/manager.test.js +38 -0
  343. package/codeyam-cli/src/utils/queue/__tests__/manager.test.js.map +1 -1
  344. package/codeyam-cli/src/utils/queue/job.js +245 -16
  345. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  346. package/codeyam-cli/src/utils/queue/manager.js +25 -7
  347. package/codeyam-cli/src/utils/queue/manager.js.map +1 -1
  348. package/codeyam-cli/src/utils/queue/persistence.js.map +1 -1
  349. package/codeyam-cli/src/utils/serverState.js.map +1 -1
  350. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +7 -5
  351. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  352. package/codeyam-cli/src/utils/versionInfo.js +25 -19
  353. package/codeyam-cli/src/utils/versionInfo.js.map +1 -1
  354. package/codeyam-cli/src/utils/wipe.js +128 -0
  355. package/codeyam-cli/src/utils/wipe.js.map +1 -0
  356. package/codeyam-cli/src/webserver/app/lib/database.js +98 -1
  357. package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
  358. package/codeyam-cli/src/webserver/app/lib/dbNotifier.js.map +1 -1
  359. package/codeyam-cli/src/webserver/backgroundServer.js +5 -10
  360. package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
  361. package/codeyam-cli/src/webserver/bootstrap.js +49 -0
  362. package/codeyam-cli/src/webserver/bootstrap.js.map +1 -0
  363. package/codeyam-cli/src/webserver/build/client/assets/EntityItem-BXhEawa3.js +1 -0
  364. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeBadge-efWKDYMr.js → EntityTypeBadge-DLqD3qNt.js} +1 -1
  365. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeIcon-Ba2JVPzP.js +41 -0
  366. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-C8lyxW9k.js +34 -0
  367. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-aht4aafF.js +25 -0
  368. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-CVtiBnY5.js +3 -0
  369. package/codeyam-cli/src/webserver/build/client/assets/LoadingDots-B0GLXMsr.js +6 -0
  370. package/codeyam-cli/src/webserver/build/client/assets/LogViewer-xgeCVgSM.js +3 -0
  371. package/codeyam-cli/src/webserver/build/client/assets/ReportIssueModal-D4TZhLuw.js +21 -0
  372. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-DuDvi0jm.js +1 -0
  373. package/codeyam-cli/src/webserver/build/client/assets/ScenarioViewer-DEx02QDa.js +10 -0
  374. package/codeyam-cli/src/webserver/build/client/assets/{TruncatedFilePath-COPstp9J.js → TruncatedFilePath-DyFZkK0l.js} +1 -1
  375. package/codeyam-cli/src/webserver/build/client/assets/_index-BwqWJOgH.js +11 -0
  376. package/codeyam-cli/src/webserver/build/client/assets/activity.(_tab)-DoLIqZX2.js +37 -0
  377. package/codeyam-cli/src/webserver/build/client/assets/api.health-l0sNRNKZ.js +1 -0
  378. package/codeyam-cli/src/webserver/build/client/assets/api.restart-server-l0sNRNKZ.js +1 -0
  379. package/codeyam-cli/src/webserver/build/client/assets/api.rules-l0sNRNKZ.js +1 -0
  380. package/codeyam-cli/src/webserver/build/client/assets/chevron-down-Cx24_aWc.js +6 -0
  381. package/codeyam-cli/src/webserver/build/client/assets/chunk-EPOLDU6W-CXRTFQ3F.js +51 -0
  382. package/codeyam-cli/src/webserver/build/client/assets/circle-check-BOARzkeR.js +6 -0
  383. package/codeyam-cli/src/webserver/build/client/assets/createLucideIcon-BdhJEx6B.js +21 -0
  384. package/codeyam-cli/src/webserver/build/client/assets/{cy-logo-cli-C1gnJVOL.svg → cy-logo-cli-CCKUIm0S.svg} +2 -2
  385. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-DcX-ZS3p.js +1 -0
  386. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-BRb-0kQl.js +1 -0
  387. package/codeyam-cli/src/webserver/build/client/assets/entity._sha._-C2N4Op8e.js +23 -0
  388. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-DavjRmOY.js +6 -0
  389. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-D1T4TGjf.js +6 -0
  390. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.edit._scenarioId-CTBG2mmz.js +5 -0
  391. package/codeyam-cli/src/webserver/build/client/assets/entry.client-CS2cb_eZ.js +29 -0
  392. package/codeyam-cli/src/webserver/build/client/assets/executionFlowCoverage-BWhdfn70.js +1 -0
  393. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-DMJ7zii9.js +1 -0
  394. package/codeyam-cli/src/webserver/build/client/assets/files-Cs4MdYtv.js +1 -0
  395. package/codeyam-cli/src/webserver/build/client/assets/git-B4RJRvYB.js +15 -0
  396. package/codeyam-cli/src/webserver/build/client/assets/git-commit-horizontal-CysbcZxi.js +6 -0
  397. package/codeyam-cli/src/webserver/build/client/assets/globals-DMUaGAqV.css +1 -0
  398. package/codeyam-cli/src/webserver/build/client/assets/html2canvas-pro.esm-fmIEn3Bc.js +9 -0
  399. package/codeyam-cli/src/webserver/build/client/assets/index-B1h680n5.js +9 -0
  400. package/codeyam-cli/src/webserver/build/client/assets/index-lzqtyFU8.js +3 -0
  401. package/codeyam-cli/src/webserver/build/client/assets/loader-circle-B7B9V-bu.js +6 -0
  402. package/codeyam-cli/src/webserver/build/client/assets/manifest-f874c610.js +1 -0
  403. package/codeyam-cli/src/webserver/build/client/assets/preload-helper-ckwbz45p.js +1 -0
  404. package/codeyam-cli/src/webserver/build/client/assets/root-Bz5TunQg.js +57 -0
  405. package/codeyam-cli/src/webserver/build/client/assets/rules-hEkvVw2-.js +97 -0
  406. package/codeyam-cli/src/webserver/build/client/assets/scenarioStatus-B_8jpV3e.js +1 -0
  407. package/codeyam-cli/src/webserver/build/client/assets/search-CxXUmBSd.js +6 -0
  408. package/codeyam-cli/src/webserver/build/client/assets/settings-CS5f3WzT.js +1 -0
  409. package/codeyam-cli/src/webserver/build/client/assets/simulations-DwFIBT09.js +1 -0
  410. package/codeyam-cli/src/webserver/build/client/assets/triangle-alert-B6LgvRJg.js +6 -0
  411. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-C1v1PQzo.js +1 -0
  412. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-3pmpUQB-.js → useLastLogLine-aSv48UbS.js} +1 -1
  413. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-DYxHZQuP.js +1 -0
  414. package/codeyam-cli/src/webserver/build/client/assets/{useToast-DEyawJ8r.js → useToast-mBRpZPiu.js} +1 -1
  415. package/codeyam-cli/src/webserver/build/server/assets/index-967OuJoF.js +1 -0
  416. package/codeyam-cli/src/webserver/build/server/assets/server-build-DRTmerg9.js +257 -0
  417. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  418. package/codeyam-cli/src/webserver/build-info.json +5 -5
  419. package/codeyam-cli/src/webserver/devServer.js +1 -3
  420. package/codeyam-cli/src/webserver/devServer.js.map +1 -1
  421. package/codeyam-cli/src/webserver/server.js +35 -25
  422. package/codeyam-cli/src/webserver/server.js.map +1 -1
  423. package/codeyam-cli/templates/codeyam-power-rules-hook.sh +200 -0
  424. package/codeyam-cli/templates/{codeyam-debug-skill.md → codeyam:debug.md} +48 -4
  425. package/codeyam-cli/templates/codeyam:diagnose.md +650 -0
  426. package/codeyam-cli/templates/codeyam:new-rule.md +13 -0
  427. package/codeyam-cli/templates/codeyam:power-rules.md +447 -0
  428. package/codeyam-cli/templates/{codeyam-setup-skill.md → codeyam:setup.md} +139 -4
  429. package/codeyam-cli/templates/{codeyam-sim-skill.md → codeyam:sim.md} +1 -1
  430. package/codeyam-cli/templates/{codeyam-test-skill.md → codeyam:test.md} +1 -1
  431. package/codeyam-cli/templates/{codeyam-verify-skill.md → codeyam:verify.md} +1 -1
  432. package/package.json +17 -16
  433. package/packages/ai/index.js +5 -4
  434. package/packages/ai/index.js.map +1 -1
  435. package/packages/ai/src/lib/analyzeScope.js +99 -0
  436. package/packages/ai/src/lib/analyzeScope.js.map +1 -1
  437. package/packages/ai/src/lib/astScopes/arrayDerivationDetector.js +150 -0
  438. package/packages/ai/src/lib/astScopes/arrayDerivationDetector.js.map +1 -0
  439. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js +100 -1
  440. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js.map +1 -1
  441. package/packages/ai/src/lib/astScopes/conditionalEffectsExtractor.js +435 -0
  442. package/packages/ai/src/lib/astScopes/conditionalEffectsExtractor.js.map +1 -0
  443. package/packages/ai/src/lib/astScopes/methodSemantics.js +97 -6
  444. package/packages/ai/src/lib/astScopes/methodSemantics.js.map +1 -1
  445. package/packages/ai/src/lib/astScopes/patterns/ifStatementHandler.js +8 -0
  446. package/packages/ai/src/lib/astScopes/patterns/ifStatementHandler.js.map +1 -1
  447. package/packages/ai/src/lib/astScopes/patterns/switchStatementHandler.js +23 -0
  448. package/packages/ai/src/lib/astScopes/patterns/switchStatementHandler.js.map +1 -1
  449. package/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.js +138 -1
  450. package/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.js.map +1 -1
  451. package/packages/ai/src/lib/astScopes/processExpression.js +945 -87
  452. package/packages/ai/src/lib/astScopes/processExpression.js.map +1 -1
  453. package/packages/ai/src/lib/checkAllAttributes.js +24 -9
  454. package/packages/ai/src/lib/checkAllAttributes.js.map +1 -1
  455. package/packages/ai/src/lib/completionCall.js +178 -31
  456. package/packages/ai/src/lib/completionCall.js.map +1 -1
  457. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +1198 -82
  458. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  459. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/JavascriptFrameworkManager.js +2 -1
  460. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/JavascriptFrameworkManager.js.map +1 -1
  461. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/MuiManager.js +661 -0
  462. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/MuiManager.js.map +1 -0
  463. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/ReactFrameworkManager.js +180 -56
  464. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/ReactFrameworkManager.js.map +1 -1
  465. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +16 -1
  466. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  467. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +86 -4
  468. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
  469. package/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.js +20 -0
  470. package/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.js.map +1 -1
  471. package/packages/ai/src/lib/dataStructure/helpers/convertNullToUndefinedBySchema.js +86 -0
  472. package/packages/ai/src/lib/dataStructure/helpers/convertNullToUndefinedBySchema.js.map +1 -0
  473. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js +34 -3
  474. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js.map +1 -1
  475. package/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.js +107 -0
  476. package/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.js.map +1 -0
  477. package/packages/ai/src/lib/dataStructureChunking.js +111 -0
  478. package/packages/ai/src/lib/dataStructureChunking.js.map +1 -0
  479. package/packages/ai/src/lib/deepEqual.js +32 -0
  480. package/packages/ai/src/lib/deepEqual.js.map +1 -0
  481. package/packages/ai/src/lib/e2eDataTracking.js +241 -0
  482. package/packages/ai/src/lib/e2eDataTracking.js.map +1 -0
  483. package/packages/ai/src/lib/extractCriticalDataKeys.js +96 -0
  484. package/packages/ai/src/lib/extractCriticalDataKeys.js.map +1 -0
  485. package/packages/ai/src/lib/generateChangesEntityScenarioData.js +62 -5
  486. package/packages/ai/src/lib/generateChangesEntityScenarioData.js.map +1 -1
  487. package/packages/ai/src/lib/generateChangesEntityScenarios.js +81 -90
  488. package/packages/ai/src/lib/generateChangesEntityScenarios.js.map +1 -1
  489. package/packages/ai/src/lib/generateEntityDataStructure.js +5 -0
  490. package/packages/ai/src/lib/generateEntityDataStructure.js.map +1 -1
  491. package/packages/ai/src/lib/generateEntityScenarioData.js +904 -84
  492. package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
  493. package/packages/ai/src/lib/generateEntityScenarios.js +186 -82
  494. package/packages/ai/src/lib/generateEntityScenarios.js.map +1 -1
  495. package/packages/ai/src/lib/generateExecutionFlows.js +392 -0
  496. package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -0
  497. package/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.js +380 -0
  498. package/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.js.map +1 -0
  499. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js +1440 -0
  500. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js.map +1 -0
  501. package/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.js +194 -0
  502. package/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.js.map +1 -0
  503. package/packages/ai/src/lib/getConditionalUsagesFromCode.js +84 -14
  504. package/packages/ai/src/lib/getConditionalUsagesFromCode.js.map +1 -1
  505. package/packages/ai/src/lib/guessScenarioDataFromDescription.js +2 -1
  506. package/packages/ai/src/lib/guessScenarioDataFromDescription.js.map +1 -1
  507. package/packages/ai/src/lib/isolateScopes.js +231 -4
  508. package/packages/ai/src/lib/isolateScopes.js.map +1 -1
  509. package/packages/ai/src/lib/mergeStatements.js +26 -3
  510. package/packages/ai/src/lib/mergeStatements.js.map +1 -1
  511. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js +6 -0
  512. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js.map +1 -1
  513. package/packages/ai/src/lib/promptGenerators/generateChangesEntityScenarioDataGenerator.js +1 -1
  514. package/packages/ai/src/lib/promptGenerators/generateChangesEntityScenarioDataGenerator.js.map +1 -1
  515. package/packages/ai/src/lib/promptGenerators/generateChangesEntityScenariosGenerator.js +21 -64
  516. package/packages/ai/src/lib/promptGenerators/generateChangesEntityScenariosGenerator.js.map +1 -1
  517. package/packages/ai/src/lib/promptGenerators/generateChunkPrompt.js +54 -0
  518. package/packages/ai/src/lib/promptGenerators/generateChunkPrompt.js.map +1 -0
  519. package/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.js +68 -6
  520. package/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.js.map +1 -1
  521. package/packages/ai/src/lib/promptGenerators/generateEntityScenariosGenerator.js +10 -34
  522. package/packages/ai/src/lib/promptGenerators/generateEntityScenariosGenerator.js.map +1 -1
  523. package/packages/ai/src/lib/promptGenerators/generateMissingKeysPrompt.js +45 -0
  524. package/packages/ai/src/lib/promptGenerators/generateMissingKeysPrompt.js.map +1 -0
  525. package/packages/ai/src/lib/promptGenerators/guessNewScenarioDataFromDescriptionGenerator.js +16 -3
  526. package/packages/ai/src/lib/promptGenerators/guessNewScenarioDataFromDescriptionGenerator.js.map +1 -1
  527. package/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.js +335 -0
  528. package/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.js.map +1 -0
  529. package/packages/ai/src/lib/resolvePathToControllable.js +667 -0
  530. package/packages/ai/src/lib/resolvePathToControllable.js.map +1 -0
  531. package/packages/ai/src/lib/splitOutsideParentheses.js +3 -1
  532. package/packages/ai/src/lib/splitOutsideParentheses.js.map +1 -1
  533. package/packages/ai/src/lib/worker/SerializableDataStructure.js +29 -0
  534. package/packages/ai/src/lib/worker/SerializableDataStructure.js.map +1 -1
  535. package/packages/ai/src/lib/worker/analyzeScopeWorker.js +4 -0
  536. package/packages/ai/src/lib/worker/analyzeScopeWorker.js.map +1 -1
  537. package/packages/analyze/src/lib/FileAnalyzer.js +15 -0
  538. package/packages/analyze/src/lib/FileAnalyzer.js.map +1 -1
  539. package/packages/analyze/src/lib/analysisContext.js +30 -5
  540. package/packages/analyze/src/lib/analysisContext.js.map +1 -1
  541. package/packages/analyze/src/lib/asts/nodes/index.js +1 -0
  542. package/packages/analyze/src/lib/asts/nodes/index.js.map +1 -1
  543. package/packages/analyze/src/lib/asts/nodes/isAsyncFunction.js +52 -0
  544. package/packages/analyze/src/lib/asts/nodes/isAsyncFunction.js.map +1 -0
  545. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +218 -50
  546. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  547. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +10 -0
  548. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  549. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js +2 -0
  550. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js.map +1 -1
  551. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +31 -7
  552. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  553. package/packages/analyze/src/lib/files/analyzeChange.js +21 -11
  554. package/packages/analyze/src/lib/files/analyzeChange.js.map +1 -1
  555. package/packages/analyze/src/lib/files/analyzeEntity.js +9 -8
  556. package/packages/analyze/src/lib/files/analyzeEntity.js.map +1 -1
  557. package/packages/analyze/src/lib/files/analyzeInitial.js +9 -10
  558. package/packages/analyze/src/lib/files/analyzeInitial.js.map +1 -1
  559. package/packages/analyze/src/lib/files/enums/steps.js +1 -1
  560. package/packages/analyze/src/lib/files/enums/steps.js.map +1 -1
  561. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +209 -0
  562. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -0
  563. package/packages/analyze/src/lib/files/scenarios/enrichUnknownTypesFromSourceEquivalencies.js +85 -0
  564. package/packages/analyze/src/lib/files/scenarios/enrichUnknownTypesFromSourceEquivalencies.js.map +1 -0
  565. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +458 -48
  566. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
  567. package/packages/analyze/src/lib/files/scenarios/generateChangesScenarioData.js +1 -1
  568. package/packages/analyze/src/lib/files/scenarios/generateChangesScenarioData.js.map +1 -1
  569. package/packages/analyze/src/lib/files/scenarios/generateChangesScenarios.js +29 -34
  570. package/packages/analyze/src/lib/files/scenarios/generateChangesScenarios.js.map +1 -1
  571. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +264 -78
  572. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  573. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +96 -0
  574. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -0
  575. package/packages/analyze/src/lib/files/scenarios/generateScenarioData.js +56 -69
  576. package/packages/analyze/src/lib/files/scenarios/generateScenarioData.js.map +1 -1
  577. package/packages/analyze/src/lib/files/scenarios/generateScenarios.js +4 -8
  578. package/packages/analyze/src/lib/files/scenarios/generateScenarios.js.map +1 -1
  579. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +372 -89
  580. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  581. package/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.js +46 -9
  582. package/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.js.map +1 -1
  583. package/packages/aws/src/lib/ecs/ecsDefineContainer.js +2 -2
  584. package/packages/aws/src/lib/ecs/ecsDefineContainer.js.map +1 -1
  585. package/packages/aws/src/lib/ecs/ecsTaskFactory.js +17 -61
  586. package/packages/aws/src/lib/ecs/ecsTaskFactory.js.map +1 -1
  587. package/packages/database/src/lib/kysely/db.js +2 -2
  588. package/packages/database/src/lib/kysely/tables/debugReportsTable.js +9 -3
  589. package/packages/database/src/lib/kysely/tables/debugReportsTable.js.map +1 -1
  590. package/packages/database/src/lib/loadReadyToBeCapturedAnalyses.js +7 -4
  591. package/packages/database/src/lib/loadReadyToBeCapturedAnalyses.js.map +1 -1
  592. package/packages/generate/index.js +3 -0
  593. package/packages/generate/index.js.map +1 -1
  594. package/packages/generate/src/lib/componentScenarioPage/componentScenarioPageNext.js +16 -1
  595. package/packages/generate/src/lib/componentScenarioPage/componentScenarioPageNext.js.map +1 -1
  596. package/packages/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.js +189 -0
  597. package/packages/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.js.map +1 -0
  598. package/packages/generate/src/lib/componentScenarioPage/generateScenarioServerComponent.js +53 -0
  599. package/packages/generate/src/lib/componentScenarioPage/generateScenarioServerComponent.js.map +1 -0
  600. package/packages/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.js +8 -4
  601. package/packages/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.js.map +1 -1
  602. package/packages/generate/src/lib/deepMerge.js +27 -1
  603. package/packages/generate/src/lib/deepMerge.js.map +1 -1
  604. package/packages/generate/src/lib/scenarioComponentForServer.js +89 -0
  605. package/packages/generate/src/lib/scenarioComponentForServer.js.map +1 -0
  606. package/packages/github/src/lib/loadOrCreateCommit.js +10 -0
  607. package/packages/github/src/lib/loadOrCreateCommit.js.map +1 -1
  608. package/packages/github/src/lib/syncPrimaryBranch.js +3 -0
  609. package/packages/github/src/lib/syncPrimaryBranch.js.map +1 -1
  610. package/packages/process/index.js +3 -0
  611. package/packages/process/index.js.map +1 -0
  612. package/packages/process/src/GlobalProcessManager.js.map +1 -0
  613. package/{background/src/lib/process → packages/process/src}/ProcessManager.js +1 -1
  614. package/packages/process/src/ProcessManager.js.map +1 -0
  615. package/packages/process/src/index.js.map +1 -0
  616. package/packages/process/src/managedExecAsync.js.map +1 -0
  617. package/packages/types/index.js.map +1 -1
  618. package/packages/utils/src/lib/lightweightEntityExtractor.js +25 -0
  619. package/packages/utils/src/lib/lightweightEntityExtractor.js.map +1 -1
  620. package/packages/utils/src/lib/safeFileName.js +29 -3
  621. package/packages/utils/src/lib/safeFileName.js.map +1 -1
  622. package/scripts/finalize-analyzer.cjs +6 -4
  623. package/analyzer-template/packages/ai/src/lib/findMatchingAttribute.ts +0 -102
  624. package/analyzer-template/packages/ai/src/lib/gatherRelevantDependentKeyAttributes.ts +0 -197
  625. package/analyzer-template/packages/ai/src/lib/generateChangesEntityKeyAttributes.ts +0 -271
  626. package/analyzer-template/packages/ai/src/lib/generateEntityKeyAttributes.ts +0 -294
  627. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateEntityKeyAttributesGenerator.ts +0 -67
  628. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateKeyAttributes.ts +0 -115
  629. package/analyzer-template/process/INTEGRATION_COMPLETE.md +0 -333
  630. package/analyzer-template/process/INTEGRATION_EXAMPLE.md +0 -525
  631. package/analyzer-template/process/README.md +0 -507
  632. package/background/src/lib/process/GlobalProcessManager.js.map +0 -1
  633. package/background/src/lib/process/ProcessManager.js.map +0 -1
  634. package/background/src/lib/process/index.js.map +0 -1
  635. package/background/src/lib/process/managedExecAsync.js.map +0 -1
  636. package/codeyam-cli/scripts/fixtures/cal.com/universal-mocks/packages/prisma/index.js +0 -238
  637. package/codeyam-cli/scripts/fixtures/cal.com/universal-mocks/packages/prisma/index.js.map +0 -1
  638. package/codeyam-cli/src/webserver/build/client/assets/EntityItem-CVbSvOjo.js +0 -1
  639. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeIcon-DcwcHyl5.js +0 -1
  640. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-WgwC1GfJ.js +0 -26
  641. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-IEKom9O2.js +0 -3
  642. package/codeyam-cli/src/webserver/build/client/assets/LogViewer-BYnfxbUG.js +0 -3
  643. package/codeyam-cli/src/webserver/build/client/assets/ReportIssueModal-_lBPJCzG.js +0 -1
  644. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-lHVhvsu_.js +0 -1
  645. package/codeyam-cli/src/webserver/build/client/assets/ScenarioViewer-d_TBk4GQ.js +0 -5
  646. package/codeyam-cli/src/webserver/build/client/assets/_index-kGT7VUqj.js +0 -1
  647. package/codeyam-cli/src/webserver/build/client/assets/activity.(_tab)-DDGmhu7P.js +0 -7
  648. package/codeyam-cli/src/webserver/build/client/assets/chevron-down-n_HPRfM_.js +0 -1
  649. package/codeyam-cli/src/webserver/build/client/assets/chunk-WWGJGFF6-CbVoyx1U.js +0 -26
  650. package/codeyam-cli/src/webserver/build/client/assets/circle-check-D1VOYveA.js +0 -1
  651. package/codeyam-cli/src/webserver/build/client/assets/createLucideIcon-YR8jjAlu.js +0 -1
  652. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-B8vP3V_s.js +0 -1
  653. package/codeyam-cli/src/webserver/build/client/assets/entity._sha._-CN6aLCT1.js +0 -16
  654. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-DA5Jeu2P.js +0 -1
  655. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.edit._scenarioId-BTeitalf.js +0 -5
  656. package/codeyam-cli/src/webserver/build/client/assets/entry.client-du6UEYD-.js +0 -13
  657. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-BpjkhMoi.js +0 -1
  658. package/codeyam-cli/src/webserver/build/client/assets/files-BQGvk4lJ.js +0 -1
  659. package/codeyam-cli/src/webserver/build/client/assets/git-DVdYRT-I.js +0 -12
  660. package/codeyam-cli/src/webserver/build/client/assets/globals-CO-U8Bpo.css +0 -1
  661. package/codeyam-cli/src/webserver/build/client/assets/html2canvas-pro.esm-XQCGvadH.js +0 -5
  662. package/codeyam-cli/src/webserver/build/client/assets/index-DCG-vks0.js +0 -1
  663. package/codeyam-cli/src/webserver/build/client/assets/loader-circle-GazdNeLl.js +0 -1
  664. package/codeyam-cli/src/webserver/build/client/assets/manifest-0b694d28.js +0 -1
  665. package/codeyam-cli/src/webserver/build/client/assets/root-D3tQP7hx.js +0 -16
  666. package/codeyam-cli/src/webserver/build/client/assets/search-CIY6XmtE.js +0 -1
  667. package/codeyam-cli/src/webserver/build/client/assets/server-build-CMKNK2uU.css +0 -1
  668. package/codeyam-cli/src/webserver/build/client/assets/settings-CoMDgElu.js +0 -1
  669. package/codeyam-cli/src/webserver/build/client/assets/simulations-agkniXp2.js +0 -1
  670. package/codeyam-cli/src/webserver/build/client/assets/triangle-alert-B2VUcygF.js +0 -1
  671. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-EvdK-zXP.js +0 -1
  672. package/codeyam-cli/src/webserver/build/server/assets/index-DGVHQEXD.js +0 -1
  673. package/codeyam-cli/src/webserver/build/server/assets/server-build-CghkTkIL.js +0 -166
  674. package/codeyam-cli/templates/debug-command.md +0 -303
  675. package/packages/ai/src/lib/findMatchingAttribute.js +0 -77
  676. package/packages/ai/src/lib/findMatchingAttribute.js.map +0 -1
  677. package/packages/ai/src/lib/gatherRelevantDependentKeyAttributes.js +0 -136
  678. package/packages/ai/src/lib/gatherRelevantDependentKeyAttributes.js.map +0 -1
  679. package/packages/ai/src/lib/generateChangesEntityKeyAttributes.js +0 -220
  680. package/packages/ai/src/lib/generateChangesEntityKeyAttributes.js.map +0 -1
  681. package/packages/ai/src/lib/generateEntityKeyAttributes.js +0 -241
  682. package/packages/ai/src/lib/generateEntityKeyAttributes.js.map +0 -1
  683. package/packages/ai/src/lib/isFrontend.js +0 -5
  684. package/packages/ai/src/lib/isFrontend.js.map +0 -1
  685. package/packages/ai/src/lib/promptGenerators/generateEntityKeyAttributesGenerator.js +0 -40
  686. package/packages/ai/src/lib/promptGenerators/generateEntityKeyAttributesGenerator.js.map +0 -1
  687. package/packages/analyze/src/lib/files/scenarios/generateKeyAttributes.js +0 -72
  688. package/packages/analyze/src/lib/files/scenarios/generateKeyAttributes.js.map +0 -1
  689. /package/analyzer-template/{process → packages/process/src}/GlobalProcessManager.ts +0 -0
  690. /package/analyzer-template/{process → packages/process/src}/ProcessManager.ts +0 -0
  691. /package/analyzer-template/{process → packages/process/src}/index.ts +0 -0
  692. /package/analyzer-template/{process → packages/process/src}/managedExecAsync.ts +0 -0
  693. /package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-CMKNK2uU.css → styles-CMKNK2uU.css} +0 -0
  694. /package/{background/src/lib/process → packages/process/src}/GlobalProcessManager.js +0 -0
  695. /package/{background/src/lib/process → packages/process/src}/index.js +0 -0
  696. /package/{background/src/lib/process → packages/process/src}/managedExecAsync.js +0 -0
@@ -25,13 +25,102 @@ interface ReturnValuePart {
25
25
  }
26
26
 
27
27
  /**
28
- * Converts a variable qualifier to a safe identifier for use in function/const names.
29
- * Handles indexed format like "data[1]" -> "data_1"
28
+ * Converts a call signature to a valid JavaScript identifier (function name).
29
+ * The original signature is preserved for data access - this only creates the function name.
30
+ *
31
+ * Examples:
32
+ * - "useAuth()" → "useAuth"
33
+ * - "db.select(usersQuery)" → "db_select_usersQuery"
34
+ * - "db.select(postsQuery)" → "db_select_postsQuery"
35
+ * - "useFetcher<User>()" → "useFetcher_User"
36
+ * - "useFetcher<{ data: UserData | null }>()" → "useFetcher_data_UserData_null"
37
+ * - "eq('user_id', value)" → "eq_user_id_value"
38
+ * - "from('workouts')" → "from_workouts"
30
39
  */
31
- function safeVariableQualifier(qualifier: string): string {
32
- // Replace brackets with underscores for valid JS identifier
33
- // "data[1]" -> "data_1"
34
- return qualifier.replace(/\[(\d+)\]/g, '_$1');
40
+ function callSignatureToFunctionName(signature: string): string {
41
+ // Extract components from the signature
42
+ const components: string[] = [];
43
+
44
+ // 1. Extract function path (parts separated by dots outside parens/brackets)
45
+ const pathMatch = signature.match(/^([^<(]+)/);
46
+ if (pathMatch) {
47
+ const path = pathMatch[1];
48
+ // Split on dots but preserve the parts
49
+ components.push(...path.split('.').filter(Boolean));
50
+ }
51
+
52
+ // 2. Extract generic type parameters (content between < and >)
53
+ const genericMatch = signature.match(/<([^>]+)>/);
54
+ if (genericMatch) {
55
+ const genericContent = genericMatch[1];
56
+ // Extract meaningful identifiers from generic type
57
+ // Handle complex types like "{ data: UserData | null }"
58
+ const typeIdentifiers = genericContent
59
+ .replace(/[{}:;,]/g, ' ') // Remove structural chars
60
+ .replace(/\|/g, ' ') // Handle union types
61
+ .split(/\s+/)
62
+ .filter(Boolean)
63
+ .filter((s) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(s)) // Only valid identifiers
64
+ .filter(
65
+ (s) =>
66
+ ![
67
+ 'null',
68
+ 'undefined',
69
+ 'void',
70
+ 'never',
71
+ 'any',
72
+ 'unknown',
73
+ 'data',
74
+ 'typeof',
75
+ ].includes(s),
76
+ ); // Skip common non-meaningful keywords
77
+
78
+ if (typeIdentifiers.length > 0) {
79
+ components.push(...typeIdentifiers.slice(0, 2)); // Limit to first 2 for reasonable length
80
+ }
81
+ }
82
+
83
+ // 3. Extract function arguments (first 2 for disambiguation)
84
+ const argsMatch = signature.match(/\(([^)]*)\)/);
85
+ if (argsMatch && argsMatch[1]) {
86
+ const argsContent = argsMatch[1].trim();
87
+ if (argsContent) {
88
+ const args = argsContent.split(',').map((arg) => arg.trim());
89
+ for (const arg of args.slice(0, 2)) {
90
+ // For quoted strings, extract the content
91
+ const stringMatch = arg.match(/^['"`](.+)['"`]$/);
92
+ if (stringMatch) {
93
+ // Split on dots for string paths like 'users.id'
94
+ const parts = stringMatch[1].split('.').filter(Boolean);
95
+ components.push(...parts);
96
+ } else if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(arg)) {
97
+ // Valid identifier - use as-is
98
+ components.push(arg);
99
+ } else if (/^\d+$/.test(arg)) {
100
+ // Number - use as-is
101
+ components.push(arg);
102
+ }
103
+ // Skip complex expressions
104
+ }
105
+ }
106
+ }
107
+
108
+ // Build the function name from components
109
+ const functionName = components
110
+ .join('_')
111
+ .replace(/[^a-zA-Z0-9_]/g, '_') // Sanitize special chars
112
+ .replace(/_+/g, '_') // Collapse multiple underscores
113
+ .replace(/^_|_$/g, ''); // Trim underscores
114
+
115
+ return functionName || 'mock';
116
+ }
117
+
118
+ /**
119
+ * Check if a mock name is a call signature (e.g., "useFetcher<User>()", "db.select(query)")
120
+ */
121
+ function isCallSignature(mockName: string): boolean {
122
+ // Call signatures contain parentheses (function calls)
123
+ return mockName.includes('(');
35
124
  }
36
125
 
37
126
  /**
@@ -196,30 +285,54 @@ function funcArgs(functionSignature: string): string[] {
196
285
 
197
286
  // isValidKey ensures that the key does not contain any characters that would make it invalid in a JavaScript object.
198
287
  // For example, it should not contain spaces, special characters, or start with a number.
288
+ // Also rejects keys that are pure function calls like "()" or "(args)" - these aren't property names.
199
289
  function isValidKey(key: string) {
200
290
  if (!key || key.length === 0) return false;
201
291
  const keyWithOutArguments = key.split('(')[0];
292
+ // Reject empty keys (happens when key is "()" or "(args)") - these are function calls, not property names
293
+ if (!keyWithOutArguments || keyWithOutArguments.length === 0) return false;
202
294
  return !/\s/.test(keyWithOutArguments);
203
295
  }
204
296
 
297
+ /**
298
+ * Known hooks that return tuples [value, setter] instead of arrays.
299
+ * These should NOT use the .map() pattern even when the schema has generic array access ([]).
300
+ * Instead, they should return [data, () => {}] where data is from scenarios().
301
+ */
302
+ const TUPLE_RETURNING_HOOKS = new Set([
303
+ 'useAtom', // Jotai
304
+ 'useState', // React
305
+ 'useReducer', // React
306
+ 'useRecoilState', // Recoil
307
+ 'useImmerAtom', // Jotai with Immer
308
+ ]);
309
+
205
310
  export default function constructMockCode(
206
311
  mockName: string,
207
312
  dependencySchemas: DeepReadonly<DataStructure['dependencySchemas']>,
208
313
  entityType?: EntityType,
209
- options?: { keepOriginalFunctionName?: boolean },
314
+ _canonicalKey?: string, // DEPRECATED: No longer used, kept for API compatibility
315
+ options?: {
316
+ keepOriginalFunctionName?: boolean;
317
+ uniqueFunctionSuffix?: string;
318
+ skipOriginalSpread?: boolean; // Skip spreading from __cyOriginal when it won't be defined
319
+ },
210
320
  ) {
211
- // Check for variable-qualified mock name (e.g., "entityDiffFetcher <- useFetcher")
212
- // Format: "variableName <- functionName" which reads naturally as "variableName receives from functionName"
213
- // Also handles indexed format: "data[1] <- useFetch" for disambiguating reused variable names
214
- const variableQualifierMatch = mockName.match(
215
- /^([a-zA-Z_][a-zA-Z0-9_]*(?:\[\d+\])?)\s*<-\s*/,
216
- );
217
- let variableQualifier = variableQualifierMatch
218
- ? variableQualifierMatch[1]
321
+ // Check if mockName is a call signature (e.g., "useFetcher<User>()", "db.select(query)")
322
+ const mockNameIsCallSignature = isCallSignature(mockName);
323
+
324
+ // For call signatures, use the original signature for data access but generate
325
+ // a valid JS function name from it
326
+ const derivedFunctionName = mockNameIsCallSignature
327
+ ? callSignatureToFunctionName(mockName)
219
328
  : null;
220
- const baseMockName = variableQualifier
221
- ? mockName.slice(variableQualifierMatch[0].length)
222
- : mockName;
329
+
330
+ // The baseMockName is the function name without type params and args
331
+ // e.g., "useFetcher<User>()" -> "useFetcher", "db.select(query)" -> "db"
332
+ const baseMockName = mockName.split(/[<(]/)[0];
333
+
334
+ // The data key is the mockName (call signature) for data access
335
+ let dataKey: string;
223
336
 
224
337
  const mockNameParts = splitOutsideParenthesesAndArrays(baseMockName);
225
338
 
@@ -231,31 +344,10 @@ export default function constructMockCode(
231
344
 
232
345
  for (const filePath in dependencySchemas) {
233
346
  for (const entityName in dependencySchemas[filePath]) {
234
- // For variable-qualified mocks, look for the full qualified name first
235
- // Format: "variableName <- functionName" (e.g., "entityDiffFetcher <- useFetcher")
236
- const targetEntityName = variableQualifier
237
- ? `${variableQualifier} <- ${baseMockName}`
238
- : mockNameParts[0];
239
-
240
- // Check for direct match
241
- let matches =
242
- entityName === targetEntityName || entityName === mockNameParts[0];
243
-
244
- // If no direct match and no qualifier was provided, check if the entity
245
- // is stored under a variable-qualified key (e.g., "stateBadge <- getStateBadge")
246
- // This handles the case where gatherDataForMocks stored the entity with a variable
247
- // qualifier but writeScenarioComponents called constructMockCode without one.
248
- if (!matches && !variableQualifier) {
249
- const qualifiedKeyMatch = entityName.match(
250
- new RegExp(`^([a-zA-Z_][a-zA-Z0-9_]*)\\s*<-\\s*${mockNameParts[0]}$`),
251
- );
252
- if (qualifiedKeyMatch) {
253
- matches = true;
254
- // Extract the variable qualifier from the entity name so we can use
255
- // it for the data lookup key later
256
- variableQualifier = qualifiedKeyMatch[1];
257
- }
258
- }
347
+ // Match entity by base name (without generics/args)
348
+ const entityBaseName = entityName.split(/[<(]/)[0];
349
+ const matches =
350
+ entityBaseName === baseMockName || entityName === mockNameParts[0];
259
351
 
260
352
  if (!matches) continue;
261
353
 
@@ -299,6 +391,44 @@ export default function constructMockCode(
299
391
  }
300
392
  }
301
393
 
394
+ // Check if the entity is used as a function (called with ()) vs an object/namespace.
395
+ // Look for paths in the schema that start with "baseMockName(" or "baseMockName<" indicating function calls.
396
+ // The "<" handles generic type parameters like useLoaderData<T>().
397
+ // Also check dataStructurePath === 'returnValue' which indicates a function return value.
398
+ const entityIsFunction =
399
+ foundEntityWithSignature ||
400
+ dataStructurePath === 'returnValue' ||
401
+ Object.keys(relevantReturnValueSchema ?? {}).some(
402
+ (key) =>
403
+ key.startsWith(`${baseMockName}(`) ||
404
+ key.startsWith(`${baseMockName}<`),
405
+ );
406
+
407
+ // Calculate the data key - use the call signature (mockName) for data access
408
+ // For simple names without parentheses:
409
+ // - Append () ONLY if the entity is a function/hook (detected above)
410
+ // - Don't append () for object/namespace mocks like "supabase"
411
+ if (mockNameIsCallSignature || mockName.includes('(')) {
412
+ dataKey = mockName;
413
+ } else if (entityIsFunction) {
414
+ // Entity is a function/hook - append () to match call signature format
415
+ dataKey = `${mockName}()`;
416
+ } else {
417
+ // Entity is an object/namespace - use bare name as key
418
+ dataKey = mockName;
419
+ }
420
+
421
+ // Helper to wrap key in appropriate quotes for computed property access
422
+ // Use single quotes when key contains double quotes to avoid syntax errors
423
+ const quotePropertyKey = (key: string): string => {
424
+ const escaped = key.replace(/\n/g, '\\n');
425
+ if (escaped.includes('"')) {
426
+ // Use single quotes, escaping any single quotes in the key
427
+ return `['${escaped.replace(/'/g, "\\'")}']`;
428
+ }
429
+ return `["${escaped}"]`;
430
+ };
431
+
302
432
  // Check if the return value schema only contains function type markers
303
433
  // (e.g., "validateInputs()": "function") without actual return data
304
434
  // (no functionCallReturnValue entries)
@@ -327,6 +457,8 @@ export default function constructMockCode(
327
457
  key.startsWith('signature['),
328
458
  ).length;
329
459
  const args = Array.from({ length: argCount }, (_, i) => `arg${i + 1}`);
460
+ // Always add ...rest to accept extra arguments beyond the signature
461
+ args.push('...rest');
330
462
  const argsString = args.join(', ');
331
463
 
332
464
  // Generate empty mock function
@@ -351,8 +483,38 @@ export default function constructMockCode(
351
483
  key.startsWith('signature['),
352
484
  ).length;
353
485
  const args = Array.from({ length: argCount }, (_, i) => `arg${i + 1}`);
486
+ // Always add ...rest to accept extra arguments beyond the signature
487
+ args.push('...rest');
354
488
  const argsString = args.join(', ');
355
489
 
490
+ // Check for Higher-Order Component (HOC) pattern:
491
+ // - First argument is a function (component) or unknown (couldn't trace type)
492
+ // - Returns a function
493
+ // HOCs like memo, forwardRef, createContext should return their first argument
494
+ //
495
+ // The return value key can be either:
496
+ // - 'memo()' (clean format)
497
+ // - 'memo(({ value, width }: Props) => { ... })' (full component code format)
498
+ const firstArgIsFunctionOrUnknown =
499
+ signatureSchema['signature[0]'] === 'function' ||
500
+ signatureSchema['signature[0]'] === 'unknown';
501
+ const returnsFunction = relevantReturnValueSchema
502
+ ? Object.entries(relevantReturnValueSchema).some(([key, value]) => {
503
+ // Check if key represents a function call that returns a function
504
+ // Key should start with the mock name, contain '(', end with ')', and have value 'function'
505
+ const isFunctionCall =
506
+ key.startsWith(mockName + '(') && key.endsWith(')');
507
+ return isFunctionCall && value === 'function';
508
+ })
509
+ : false;
510
+
511
+ if (firstArgIsFunctionOrUnknown && returnsFunction) {
512
+ // HOC pattern detected - return the first argument
513
+ return `function ${mockName}(${argsString}) {
514
+ return arg1;
515
+ }`;
516
+ }
517
+
356
518
  // Generate empty mock function
357
519
  return `function ${mockName}(${argsString}) {
358
520
  // Empty mock - original function mocked out
@@ -381,6 +543,99 @@ export default function constructMockCode(
381
543
  dataStructureValue === 'array' &&
382
544
  (dataStructurePath === 'returnValue' || pathDepth <= mockNameParts.length);
383
545
 
546
+ // OPTIMIZATION: Early return for tuple-returning hooks (useAtom, useState, etc.)
547
+ // These hooks have simple [value, setter] return patterns that don't need the full
548
+ // 9216-key schema processing. Check if this is a tuple-returning hook and generate
549
+ // the mock code directly without iterating over all schema keys.
550
+ if (TUPLE_RETURNING_HOOKS.has(baseMockName) && isFunction) {
551
+ // Check if schema has generic array pattern (indicates tuple return like [value, setter])
552
+ const schemaKeys = Object.keys(relevantReturnValueSchema ?? {});
553
+ const hasGenericArrayInSchema = schemaKeys.some(
554
+ (k) =>
555
+ k.includes('.functionCallReturnValue[]') ||
556
+ k === `${dataKey}.functionCallReturnValue[]` ||
557
+ k === 'returnValue[]',
558
+ );
559
+
560
+ // Check for differentiated tuple indices (e.g., functionCallReturnValue[2], [3]) which would NOT be a standard tuple
561
+ // We only check indices immediately after functionCallReturnValue, not nested indices like signature[2]
562
+ const tupleHasDifferentiatedIndices = schemaKeys.some((k) => {
563
+ // Look for .functionCallReturnValue[N] where N >= 2
564
+ const match = k.match(/\.functionCallReturnValue\[(\d+)\]/);
565
+ if (!match) return false;
566
+ const idx = parseInt(match[1], 10);
567
+ return idx >= 2;
568
+ });
569
+
570
+ const isTupleReturningHook =
571
+ hasGenericArrayInSchema && !tupleHasDifferentiatedIndices;
572
+
573
+ if (isTupleReturningHook) {
574
+ // Find all call patterns for this hook (e.g., useAtom(quoteFilterAtom), useAtom(supplierAtom))
575
+ const hookCallPatterns = Object.keys(relevantReturnValueSchema ?? {})
576
+ .filter((k) => {
577
+ const regex = new RegExp(`^${baseMockName}\\([^)]+\\)$`);
578
+ return regex.test(k);
579
+ })
580
+ .map((k) => {
581
+ const match = k.match(/\(([^)]+)\)/);
582
+ return { key: k, arg: match?.[1] ?? '' };
583
+ });
584
+
585
+ let tupleReturnCode: string;
586
+ if (hookCallPatterns.length > 1) {
587
+ // Multiple patterns - generate conditional dispatch
588
+ const conditions = hookCallPatterns
589
+ .map(
590
+ ({ key, arg }) =>
591
+ `if (argLabel === '${arg}' || argStr.includes('${arg}')) {\n return [scenarios().data()?.["${key}"]?.[0] ?? [], () => {}];\n }`,
592
+ )
593
+ .join('\n ');
594
+ const fallbackKey = hookCallPatterns[0]?.key ?? dataKey;
595
+ tupleReturnCode = `(() => {
596
+ // Dynamic dispatch for tuple-returning hook with multiple argument patterns
597
+ const argLabel = args[0]?.debugLabel ?? '';
598
+ const argStr = args[0]?.toString?.() ?? String(args[0] ?? '');
599
+ ${conditions}
600
+ return [scenarios().data()?.["${fallbackKey}"]?.[0] ?? [], () => {}];
601
+ })()`;
602
+ } else {
603
+ // Single or no patterns - use dynamic dispatch
604
+ const fallbackKey = hookCallPatterns[0]?.key ?? `${baseMockName}()`;
605
+ tupleReturnCode = `(() => {
606
+ // Dynamic dispatch for tuple-returning hook
607
+ const argLabel = args[0]?.debugLabel ?? '';
608
+ const argStr = args[0]?.toString?.() ?? '';
609
+ const allData = scenarios().data() ?? {};
610
+ if (argLabel) {
611
+ const labelKey = '${baseMockName}(' + argLabel + ')';
612
+ if (allData[labelKey]) {
613
+ return [allData[labelKey]?.[0] ?? [], () => {}];
614
+ }
615
+ }
616
+ const keys = Object.keys(allData).filter(k => k.startsWith('${baseMockName}('));
617
+ for (const key of keys) {
618
+ const keyArg = key.slice(${baseMockName.length + 1}, -1);
619
+ if (argStr.includes(keyArg)) {
620
+ return [allData[key]?.[0] ?? [], () => {}];
621
+ }
622
+ }
623
+ return [allData[keys[0] ?? '${fallbackKey}']?.[0] ?? [], () => {}];
624
+ })()`;
625
+ }
626
+
627
+ const safeFunctionName = options?.uniqueFunctionSuffix
628
+ ? `${baseMockName}_${options.uniqueFunctionSuffix}`
629
+ : options?.keepOriginalFunctionName
630
+ ? baseMockName
631
+ : mockNameIsCallSignature && derivedFunctionName
632
+ ? derivedFunctionName
633
+ : baseMockName;
634
+
635
+ return `function ${safeFunctionName}(...args) {\n return ${tupleReturnCode};\n}`;
636
+ }
637
+ }
638
+
384
639
  const returnValueParts: ReturnValuePart = {
385
640
  name: dataStructureName,
386
641
  isArray: isRootArray,
@@ -413,22 +668,21 @@ export default function constructMockCode(
413
668
  // so "useLoaderData<typeof loader>()" becomes "useLoaderData()"
414
669
  name = cleanOutTypes(name);
415
670
 
416
- // For variable-qualified mocks at root level, use the "variableName <- functionName" format
417
- // This matches the format generated by gatherDataForMocks and is more intuitive for LLMs
418
- // e.g., "useFetcher()" becomes "entityDiffFetcher <- useFetcher"
419
- if (isRootAccess && variableQualifier) {
420
- // Remove trailing () from the function name to match gatherDataForMocks key format
421
- const baseName = name.replace(/\(\)$/, '');
422
- name = `${variableQualifier} <- ${baseName}`;
671
+ // For root data access, use the dataKey (original call signature or canonical key)
672
+ // This preserves the original call signature for LLM clarity
673
+ if (isRootAccess) {
674
+ // For call signature format, use the original mockName as the data key
675
+ // e.g., scenarios().data()?.["useFetcher<User>()"]
676
+ // e.g., scenarios().data()?.["db.select(usersQuery)"]
677
+ return `?.${quotePropertyKey(dataKey)}`;
423
678
  }
424
679
 
425
680
  // Only use unquoted array access syntax for pure array indices like [0], [1]
426
- // NOT for variable-qualified names like "fetcher[1] <- useFetcher" which need quotes
427
- if (name.match(/^\[\d+\]$/) && !name.includes(' <- ')) {
681
+ if (name.match(/^\[\d+\]$/)) {
428
682
  return `?.${name}`;
429
683
  }
430
684
 
431
- return `?.["${name.replace(/\n/g, '\\n')}"]`;
685
+ return `?.${quotePropertyKey(name)}`;
432
686
  };
433
687
 
434
688
  const constructDataPaths = () => {
@@ -496,7 +750,20 @@ export default function constructMockCode(
496
750
  hasNoReturnData,
497
751
  } = returnValue;
498
752
 
499
- const nestedContent: (string | undefined)[] = (nested ?? []).map(
753
+ // When an array has differentiated indices ([0], [1], etc.), filter out any
754
+ // non-index items from nested. These non-index items come from generic [] paths
755
+ // like [].filter or [].sort, which describe element properties, not array elements.
756
+ // Including them would generate invalid syntax like "sort: ..." inside an array literal.
757
+ const hasDifferentiatedIndices =
758
+ isArray &&
759
+ nested &&
760
+ nested.some((n) => n.name.match(/^\[\d+\]$/) && n.name !== '[0]');
761
+ const filteredNested =
762
+ hasDifferentiatedIndices && nested
763
+ ? nested.filter((n) => n.name.match(/^\[\d+\]$/))
764
+ : nested;
765
+
766
+ const nestedContent: (string | undefined)[] = (filteredNested ?? []).map(
500
767
  (nestedItem) => {
501
768
  const nestedContent = constructReturnValueString(
502
769
  nestedItem,
@@ -580,53 +847,114 @@ export default function constructMockCode(
580
847
  ) {
581
848
  levelContentItems.push(...dataPaths.map((path) => `...${path}`));
582
849
  }
583
- levelContentItems.push(...nestedContent);
850
+ // Filter out nested content that would be invalid as object properties
851
+ // (e.g., bare arrow functions like "() => {...}" without a property name)
852
+ // Only apply this filter when building object content, not array content.
853
+ // Bare arrow functions ARE valid as array elements (like [0] = {...}, [1] = () => {...})
854
+ // Check both isArray (item IS an array) and returnsFunctionArray (item returns an array)
855
+ const inArrayContext = isArray || returnsFunctionArray;
856
+ const validNestedContent = nestedContent.filter((content) => {
857
+ if (!content) return false;
858
+ // Only filter bare arrow functions when NOT in array context
859
+ // In arrays, bare arrow functions are valid elements
860
+ if (!inArrayContext && content.match(/^\s*\([^)]*\)\s*=>/)) {
861
+ return false;
862
+ }
863
+ return true;
864
+ });
865
+ levelContentItems.push(...validNestedContent);
584
866
 
585
867
  let levelContents: string = levelContentItems.filter(Boolean).join(',\n');
586
868
  if (returnsFunctionArgs) {
587
- const argsString = returnsFunctionArgs
588
- .map((_, index) => `arg${index + 1}`)
589
- .join(', ');
590
- let funcContents = '';
591
- if (returnsFunctionArray) {
592
- if (hasNoReturnData) {
593
- // Function has no return data (only signatures) - return empty array
594
- funcContents = 'return []';
595
- } else if (levelContents.length === 0 && dataPaths.length === 1) {
596
- // When returning an array with no nested content, return the data path directly
597
- // (the data path points to the array in scenario data)
598
- funcContents = `return ${dataPaths[0]}`;
599
- } else if (levelContents.length === 0) {
600
- funcContents = 'return []';
601
- } else {
602
- funcContents = `return [\n${indent(levelContents)}\n]`;
869
+ // When returnsFunctionArgs is empty [] OR has a single literal string argument,
870
+ // the function returns a callable function (e.g., getTranslate() returns t,
871
+ // where t('key') looks up translations)
872
+ // Generate a dispatch function that looks up keys based on the argument
873
+ //
874
+ // Detect translation-like pattern:
875
+ // - Data path ends with ["('some.literal')"] - a literal string key
876
+ // - This means the mock data has keys like "('common.surveys')": "Surveys"
877
+ // - Exclude ["()"] which is an empty function call (not a translation pattern)
878
+ const dataPath = dataPaths[0];
879
+ // Pattern matches ?.["('...')"] at end of path, but NOT ?.["()"] (empty args)
880
+ const literalKeyPattern = dataPath?.match(/\?\.\["\('.+'\)"\]$/);
881
+
882
+ if (
883
+ !returnsFunctionArray &&
884
+ dataPaths.length === 1 &&
885
+ literalKeyPattern // Only dispatch when there's a literal key pattern
886
+ ) {
887
+ // Function returns a function - generate dispatch function
888
+ // Strip the literal key from the path and use dynamic lookup
889
+ const dataPathBase = literalKeyPattern
890
+ ? dataPath.replace(/\?\.\["\('.+'\)"\]$/, '')
891
+ : dataPath;
892
+ const funcContents = `return ${dataPathBase}?.[\`('\${arg1}')\`]`;
893
+ levelContents = `(arg1) => {\n${indent(funcContents)}\n}`;
894
+
895
+ if (!isArray) {
896
+ return levelContents;
603
897
  }
604
898
  } else {
605
- // Check if function has no actual return data (only signatures)
606
- const hasNestedItems = nested && nested.length > 0;
607
- const hasActualNestedContent =
608
- nestedContent.filter(Boolean).length > 0;
609
-
610
- if (levelContentItems.length === 1 && dataPaths.length === 1) {
611
- if (
612
- hasNoReturnData ||
613
- (hasNestedItems && !hasActualNestedContent)
614
- ) {
899
+ const argsString = returnsFunctionArgs
900
+ .map((_, index) => `arg${index + 1}`)
901
+ .join(', ');
902
+ let funcContents = '';
903
+ if (returnsFunctionArray) {
904
+ if (hasNoReturnData) {
615
905
  // Function has no return data (only signatures) - return empty array
616
906
  funcContents = 'return []';
617
- } else {
618
- // Has return data - return data path
907
+ } else if (levelContents.length === 0 && dataPaths.length === 1) {
908
+ // When returning an array with no nested content, return the data path directly
909
+ // (the data path points to the array in scenario data)
619
910
  funcContents = `return ${dataPaths[0]}`;
911
+ } else if (levelContents.length === 0) {
912
+ funcContents = 'return []';
913
+ } else {
914
+ funcContents = `return [\n${indent(levelContents)}\n]`;
620
915
  }
621
916
  } else {
622
- funcContents = `return {\n${indent(levelContents)}\n}`;
917
+ // Check if function has no actual return data (only signatures)
918
+ const hasNestedItems = nested && nested.length > 0;
919
+ const hasActualNestedContent =
920
+ nestedContent.filter(Boolean).length > 0;
921
+
922
+ if (levelContentItems.length === 1 && dataPaths.length === 1) {
923
+ if (
924
+ hasNoReturnData ||
925
+ (hasNestedItems && !hasActualNestedContent)
926
+ ) {
927
+ // Function has no return data (only signatures) - return empty array
928
+ funcContents = 'return []';
929
+ } else {
930
+ // Has return data - return data path
931
+ funcContents = `return ${dataPaths[0]}`;
932
+ }
933
+ } else {
934
+ funcContents = `return {\n${indent(levelContents)}\n}`;
935
+ }
623
936
  }
624
- }
625
937
 
626
- levelContents = `(${argsString}) => {\n${indent(funcContents)}\n}`;
938
+ levelContents = `(${argsString}) => {\n${indent(funcContents)}\n}`;
939
+
940
+ if (!isArray) {
941
+ return levelContents;
942
+ }
943
+ }
627
944
 
628
- if (!isArray) {
629
- return levelContents;
945
+ // For generic arrays of functions WITH nested properties (e.g., functionCallReturnValue[] = "function"
946
+ // with nested .filter, .sort, etc.), the levelContents would be a bare arrow function "() => {...}"
947
+ // that wraps object content. Using this in a .map(({...})) creates invalid syntax like "({ () => {...} })".
948
+ // When isGenericArray is true AND there are nested properties, we're accessing data from the elements,
949
+ // not calling them - so skip the function wrapping.
950
+ // But if there are NO nested properties, keep the wrapper because callers may want to call the elements.
951
+ const hasNonStructuralNestedItems =
952
+ nested &&
953
+ nested.length > 0 &&
954
+ nested.some((n) => !n.name.match(/^\[\d*\]$/));
955
+ if (isGenericArray && hasNonStructuralNestedItems) {
956
+ // Skip the arrow function wrapper - just use the nested content directly
957
+ levelContents = levelContentItems.filter(Boolean).join(',\n');
630
958
  }
631
959
  }
632
960
 
@@ -642,7 +970,123 @@ export default function constructMockCode(
642
970
  });
643
971
 
644
972
  let returnValueContents = '';
645
- if (
973
+
974
+ // Check if this is a known tuple-returning hook (useAtom, useState, etc.)
975
+ // These should return [value, setter] tuples, not arrays or data paths
976
+ // Check isGenericArray from current context OR from schema for root level calls
977
+ // (at root level, isGenericArray might not be set yet but the schema contains [] pattern)
978
+ const hasGenericArrayInSchema =
979
+ root &&
980
+ TUPLE_RETURNING_HOOKS.has(baseMockName) &&
981
+ Object.keys(relevantReturnValueSchema ?? {}).some((k) =>
982
+ k.includes('.functionCallReturnValue[]'),
983
+ );
984
+ // Check if there are array indices beyond what a standard 2-element tuple would have
985
+ // For tuple-returning hooks, [0] and [1] are expected (value and setter)
986
+ // Only consider it "differentiated" if there are indices >= 2 (e.g., [2], [3])
987
+ const tupleHasDifferentiatedIndices = nested?.some((n) => {
988
+ const indexMatch = n.name.match(/^\[(\d+)\]$/);
989
+ if (!indexMatch) return false;
990
+ const index = parseInt(indexMatch[1], 10);
991
+ return index >= 2;
992
+ });
993
+ const isTupleReturningHook =
994
+ TUPLE_RETURNING_HOOKS.has(baseMockName) &&
995
+ (isGenericArray || hasGenericArrayInSchema) &&
996
+ !tupleHasDifferentiatedIndices;
997
+
998
+ // Debug logging for tuple-returning hooks
999
+ if (TUPLE_RETURNING_HOOKS.has(baseMockName) && root) {
1000
+ const schemaKeys = Object.keys(relevantReturnValueSchema ?? {});
1001
+ const hasArrayPattern = schemaKeys.some((k) =>
1002
+ k.includes('.functionCallReturnValue[]'),
1003
+ );
1004
+ console.log(
1005
+ `CodeYam: Tuple hook check for ${baseMockName} (root):`,
1006
+ `hasGenericArrayInSchema=${hasGenericArrayInSchema}`,
1007
+ `hasArrayPattern=${hasArrayPattern}`,
1008
+ `tupleHasDifferentiatedIndices=${tupleHasDifferentiatedIndices}`,
1009
+ `isTupleReturningHook=${isTupleReturningHook}`,
1010
+ `schemaKeysSample=${schemaKeys.slice(0, 5).join(', ')}`,
1011
+ );
1012
+ }
1013
+
1014
+ if (isTupleReturningHook) {
1015
+ // Tuple-returning hooks should return [value, setter] tuple
1016
+ // The value is the first element from scenarios data, setter is a no-op
1017
+ // Default to [] when data is undefined to prevent errors like ".includes is not a function"
1018
+
1019
+ // Check if there are multiple call patterns for this hook in the schema
1020
+ // (e.g., useAtom(quoteFilterAtom) and useAtom(supplierAtom))
1021
+ const hookCallPatterns = Object.keys(relevantReturnValueSchema ?? {})
1022
+ .filter((k) => {
1023
+ // Match patterns like "useAtom(someArg)" but not nested paths like "useAtom(x).foo"
1024
+ const regex = new RegExp(`^${baseMockName}\\([^)]+\\)$`);
1025
+ return regex.test(k);
1026
+ })
1027
+ .map((k) => {
1028
+ // Extract the argument from the key like "useAtom(quoteFilterAtom)" -> "quoteFilterAtom"
1029
+ const match = k.match(/\(([^)]+)\)/);
1030
+ return { key: k, arg: match?.[1] ?? '' };
1031
+ });
1032
+
1033
+ if (hookCallPatterns.length > 1) {
1034
+ // Multiple patterns - generate conditional dispatch based on first argument
1035
+ // For Jotai atoms, we use debugLabel; for others, we try to match the argument string
1036
+ const conditions = hookCallPatterns
1037
+ .map(
1038
+ ({ key, arg }) =>
1039
+ `if (argLabel === '${arg}' || argStr.includes('${arg}')) {\n return [scenarios().data()?.["${key}"]?.[0] ?? [], () => {}];\n }`,
1040
+ )
1041
+ .join('\n ');
1042
+
1043
+ // Use the first pattern as fallback
1044
+ const fallbackKey = hookCallPatterns[0]?.key ?? dataKey;
1045
+
1046
+ returnValueContents = `(() => {
1047
+ // Dynamic dispatch for tuple-returning hook with multiple argument patterns
1048
+ const argLabel = args[0]?.debugLabel ?? '';
1049
+ const argStr = args[0]?.toString?.() ?? String(args[0] ?? '');
1050
+ ${conditions}
1051
+ // Fallback to first pattern
1052
+ return [scenarios().data()?.["${fallbackKey}"]?.[0] ?? [], () => {}];
1053
+ })()`;
1054
+ } else {
1055
+ // Single pattern or no patterns - use dynamic dispatch to handle case where
1056
+ // the mock is used with different atoms than what was captured in the schema.
1057
+ // Use the first argument to construct the data key dynamically.
1058
+ const fallbackKey = hookCallPatterns[0]?.key ?? `${baseMockName}()`;
1059
+
1060
+ returnValueContents = `(() => {
1061
+ // Dynamic dispatch for tuple-returning hook
1062
+ // Try to construct key from argument's debugLabel (Jotai atoms) or toString
1063
+ const argLabel = args[0]?.debugLabel ?? '';
1064
+ const argStr = args[0]?.toString?.() ?? '';
1065
+ const allData = scenarios().data() ?? {};
1066
+
1067
+ // Try to find a matching key using debugLabel first
1068
+ if (argLabel) {
1069
+ const labelKey = '${baseMockName}(' + argLabel + ')';
1070
+ if (allData[labelKey]) {
1071
+ return [allData[labelKey]?.[0] ?? [], () => {}];
1072
+ }
1073
+ }
1074
+
1075
+ // Try to find any matching key that contains part of the argument string
1076
+ const keys = Object.keys(allData).filter(k => k.startsWith('${baseMockName}('));
1077
+ for (const key of keys) {
1078
+ const keyArg = key.slice(${baseMockName.length + 1}, -1);
1079
+ if (argStr.includes(keyArg)) {
1080
+ return [allData[key]?.[0] ?? [], () => {}];
1081
+ }
1082
+ }
1083
+
1084
+ // Fallback to first matching key or default
1085
+ const fallback = keys[0] ?? '${fallbackKey}';
1086
+ return [allData[fallback]?.[0] ?? [], () => {}];
1087
+ })()`;
1088
+ }
1089
+ } else if (
646
1090
  !returnsFunctionArgs &&
647
1091
  nestedContent.length === 0 &&
648
1092
  dataPaths.length === 1
@@ -663,31 +1107,407 @@ export default function constructMockCode(
663
1107
  // When GENERIC array (using []) has nested content (like functions that need wrapping),
664
1108
  // use .map() to transform ALL elements instead of just creating [0]
665
1109
  // For DIFFERENTIATED arrays (using [0], [1], etc.), keep the static array structure
1110
+ //
1111
+ // IMPORTANT: If the nested content contains differentiated indices like [0], [1],
1112
+ // we MUST use static array pattern, not .map(). The presence of differentiated
1113
+ // indices means the array elements have different types/structures, so .map()
1114
+ // would generate invalid code trying to treat them uniformly.
1115
+ const hasDifferentiatedIndices =
1116
+ nested &&
1117
+ nested.some((n) => n.name.match(/^\[\d+\]$/) && n.name !== '[0]');
666
1118
  if (
667
1119
  isGenericArray &&
668
1120
  nestedContent.length > 0 &&
669
- dataPaths.length > 0
1121
+ dataPaths.length > 0 &&
1122
+ !hasDifferentiatedIndices
670
1123
  ) {
671
1124
  // Get the array base path (without the [0])
672
1125
  const arrayBasePath = dataPaths[0].replace(/\?\.\[0\]$/, '');
673
1126
  // Replace [0] references with [__idx__] in level contents
674
- const mappedContents = levelContents.replace(
1127
+ let mappedContents = levelContents.replace(
675
1128
  /\?\.\[0\]/g,
676
1129
  '?.[__idx__]',
677
1130
  );
678
1131
  // levelContents may already be wrapped in {...} from structural [0] element,
679
1132
  // so check if we need to add the wrapper or not
680
1133
  const needsWrapper = !mappedContents.trim().startsWith('{');
1134
+
1135
+ // Helper to check if a position is inside a string literal
1136
+ // Returns the end position of the string if inside one, -1 otherwise
1137
+ // Defined here so it's accessible in both needsWrapper branches
1138
+ const skipStringLiteral = (
1139
+ content: string,
1140
+ pos: number,
1141
+ ): number => {
1142
+ const char = content[pos];
1143
+ if (char !== '"' && char !== "'" && char !== '`') return -1;
1144
+ // Find the matching closing quote
1145
+ let j = pos + 1;
1146
+ while (j < content.length) {
1147
+ if (content[j] === '\\') {
1148
+ j += 2; // Skip escaped character
1149
+ continue;
1150
+ }
1151
+ if (content[j] === char) {
1152
+ return j + 1; // Return position after closing quote
1153
+ }
1154
+ j++;
1155
+ }
1156
+ return content.length; // Unclosed string, skip to end
1157
+ };
1158
+
1159
+ // Filter out bare arrow functions which are invalid as object properties.
1160
+ // Arrow functions can be multi-line, so we need to match the entire function body, not just the first line.
1161
+ // Pattern: starts with "(args) =>", followed by either:
1162
+ // - A single-line body: "() => expression"
1163
+ // - A multi-line body: "() => { ... }" (with matching braces)
1164
+ // IMPORTANT: Only filter BARE arrow functions (without property names).
1165
+ // "() => {...}" is invalid, but "get: (arg1) => {...}" is valid.
1166
+ // We use a function to properly handle nested braces.
1167
+ // Defined here so it's accessible in both needsWrapper branches
1168
+ const filterOutArrowFunctions = (content: string): string => {
1169
+ const result: string[] = [];
1170
+ let i = 0;
1171
+ while (i < content.length) {
1172
+ // Skip over string literals entirely
1173
+ const stringEnd = skipStringLiteral(content, i);
1174
+ if (stringEnd !== -1) {
1175
+ result.push(content.slice(i, stringEnd));
1176
+ i = stringEnd;
1177
+ continue;
1178
+ }
1179
+
1180
+ // Check if we're at the start of an arrow function (with optional leading whitespace)
1181
+ const arrowMatch = content
1182
+ .slice(i)
1183
+ .match(/^(\s*)\([^)]*\)\s*=>\s*/);
1184
+ if (arrowMatch) {
1185
+ // Check if this is a bare arrow function or a named property with arrow function value
1186
+ // Look back to see if there's a "key:" pattern before this position
1187
+ const before = content.slice(0, i);
1188
+ const beforeTrimmed = before.trim();
1189
+ // Valid patterns where arrow function is NOT bare:
1190
+ // 1. Property value: "key: (arg) => ..." - ends with ':'
1191
+ // 2. Function argument: ".map((arg) => ..." - ends with '('
1192
+ // NOTE: We don't include ',' because "{ prop, () => {} }" is invalid
1193
+ // (can't distinguish function argument from object property context)
1194
+ const isPropertyValue = beforeTrimmed.endsWith(':');
1195
+ const isFunctionArg = beforeTrimmed.endsWith('(');
1196
+ const hasPropertyName = isPropertyValue || isFunctionArg;
1197
+
1198
+ if (!hasPropertyName) {
1199
+ // This is a bare arrow function - filter it out
1200
+ // Found arrow function start, need to find its end
1201
+ const afterArrow = i + arrowMatch[0].length;
1202
+ if (content[afterArrow] === '{') {
1203
+ // Multi-line arrow function - find matching closing brace
1204
+ // Must respect string literals when counting braces
1205
+ let braceCount = 1;
1206
+ let j = afterArrow + 1;
1207
+ while (j < content.length && braceCount > 0) {
1208
+ const strEnd = skipStringLiteral(content, j);
1209
+ if (strEnd !== -1) {
1210
+ j = strEnd;
1211
+ continue;
1212
+ }
1213
+ if (content[j] === '{') braceCount++;
1214
+ if (content[j] === '}') braceCount--;
1215
+ j++;
1216
+ }
1217
+ // Skip past the arrow function
1218
+ i = j;
1219
+ // Only skip trailing comma, keep newlines
1220
+ while (i < content.length && content[i] === ' ') {
1221
+ i++;
1222
+ }
1223
+ if (content[i] === ',') {
1224
+ i++; // Skip the comma after the arrow function
1225
+ }
1226
+ } else {
1227
+ // Single expression arrow function - skip to next comma or newline
1228
+ let j = afterArrow;
1229
+ while (
1230
+ j < content.length &&
1231
+ content[j] !== ',' &&
1232
+ content[j] !== '\n'
1233
+ ) {
1234
+ j++;
1235
+ }
1236
+ i = j;
1237
+ if (content[i] === ',') i++; // Skip the comma
1238
+ }
1239
+ continue;
1240
+ }
1241
+ }
1242
+ // Not a bare arrow function, keep this character
1243
+ result.push(content[i]);
1244
+ i++;
1245
+ }
1246
+ return result.join('');
1247
+ };
1248
+
1249
+ // Filter out bare object blocks (e.g., "{ ...spread, props }," without a property name)
1250
+ // These are invalid in object literal context - you need "key: { ... }" not just "{ ... }"
1251
+ // Defined here so it's accessible in both needsWrapper branches
1252
+ // The skipFirstBrace parameter allows the else branch to preserve the outer object
1253
+ const filterOutBareObjects = (
1254
+ content: string,
1255
+ skipFirstBrace = false,
1256
+ ): string => {
1257
+ const result: string[] = [];
1258
+ let i = 0;
1259
+ let firstBraceSkipped = false;
1260
+ while (i < content.length) {
1261
+ // Skip over string literals entirely - braces inside strings should not be processed
1262
+ const stringEnd = skipStringLiteral(content, i);
1263
+ if (stringEnd !== -1) {
1264
+ result.push(content.slice(i, stringEnd));
1265
+ i = stringEnd;
1266
+ continue;
1267
+ }
1268
+
1269
+ // Check if we're at a bare object start (newline/comma followed by { without : before it)
1270
+ // Look back to see if there's a colon (property assignment) before this brace
1271
+ const isStartOfLine =
1272
+ i === 0 ||
1273
+ content[i - 1] === '\n' ||
1274
+ content.slice(0, i).trim().endsWith(',');
1275
+ if (content[i] === '{' && isStartOfLine) {
1276
+ // Check if this is actually a bare object (not "key: {")
1277
+ const beforeTrimmed = content.slice(0, i).trim();
1278
+ const isBareObject =
1279
+ beforeTrimmed.endsWith(',') ||
1280
+ beforeTrimmed === '' ||
1281
+ beforeTrimmed.endsWith('(');
1282
+
1283
+ if (isBareObject) {
1284
+ // If skipFirstBrace is true and this is the first bare brace at position 0,
1285
+ // don't filter it - it's the intentional outer object wrapper
1286
+ if (skipFirstBrace && !firstBraceSkipped && i === 0) {
1287
+ firstBraceSkipped = true;
1288
+ result.push(content[i]);
1289
+ i++;
1290
+ continue;
1291
+ }
1292
+
1293
+ // Find matching closing brace, respecting string literals
1294
+ let braceCount = 1;
1295
+ let j = i + 1;
1296
+ while (j < content.length && braceCount > 0) {
1297
+ const strEnd = skipStringLiteral(content, j);
1298
+ if (strEnd !== -1) {
1299
+ j = strEnd;
1300
+ continue;
1301
+ }
1302
+ if (content[j] === '{') braceCount++;
1303
+ if (content[j] === '}') braceCount--;
1304
+ j++;
1305
+ }
1306
+ // Skip past the object
1307
+ i = j;
1308
+ // Skip trailing comma
1309
+ while (i < content.length && content[i] === ' ') {
1310
+ i++;
1311
+ }
1312
+ if (content[i] === ',') {
1313
+ i++;
1314
+ }
1315
+ continue;
1316
+ }
1317
+ }
1318
+ result.push(content[i]);
1319
+ i++;
1320
+ }
1321
+ return result.join('');
1322
+ };
1323
+
1324
+ // Helper to clean up formatting issues after filtering
1325
+ const cleanupContent = (content: string): string => {
1326
+ return (
1327
+ content
1328
+ .replace(/,\s*,/g, ',') // Double commas
1329
+ .replace(/,(\s*\n\s*\})/g, '$1') // Trailing comma before closing brace
1330
+ .replace(/\{\s*\n\s*,/g, '{\n') // Leading comma after opening brace
1331
+ // Remove incomplete .map calls where callback was filtered out
1332
+ // Pattern: ".map" followed by newline/whitespace without "(" for args
1333
+ .replace(/\?\.map(?=\s*[\n\r,}\]])/g, '')
1334
+ .replace(/\.map(?=\s*[\n\r,}\]])/g, '')
1335
+ // Clean up orphan })) sequences (from nested filtered map callbacks)
1336
+ .replace(/\s*\}\)\)\s*\n\s*\}/g, '\n}')
1337
+ .replace(/^\s*\n/gm, '') // Empty lines
1338
+ .trim()
1339
+ );
1340
+ };
1341
+
681
1342
  if (needsWrapper) {
682
- returnValueContents = `${arrayBasePath}?.map((__item__, __idx__) => ({\n${indent(mappedContents)}\n}))`;
1343
+ // Apply filters to remove invalid content
1344
+ mappedContents = filterOutArrowFunctions(mappedContents);
1345
+ mappedContents = filterOutBareObjects(mappedContents);
1346
+ mappedContents = cleanupContent(mappedContents);
1347
+
1348
+ // If mappedContents is empty after filtering, don't generate .map() at all
1349
+ // Just use the array path directly with spread or as-is
1350
+ // This prevents orphan )) from empty .map() callbacks
1351
+ const cleanedForEmptyCheck = mappedContents
1352
+ .replace(/\s+/g, '')
1353
+ .replace(/,+/g, '');
1354
+ if (cleanedForEmptyCheck.length === 0) {
1355
+ // Content is empty - just return the array directly
1356
+ returnValueContents = arrayBasePath;
1357
+ } else {
1358
+ // Check if mappedContents is just a bare expression (no property names)
1359
+ // A bare expression like "scenarios().data()?.["key"]?.[__idx__]," cannot be
1360
+ // wrapped in ({ }) because it's not a valid object property.
1361
+ // Pattern: content has no ":" that's not inside brackets/parens/strings
1362
+ const hasBareExpression = (() => {
1363
+ const trimmed = mappedContents.trim().replace(/,\s*$/, ''); // Remove trailing comma
1364
+ let depth = 0;
1365
+ let inString = false;
1366
+ let stringChar = '';
1367
+ for (let i = 0; i < trimmed.length; i++) {
1368
+ const char = trimmed[i];
1369
+ if (inString) {
1370
+ if (char === '\\') {
1371
+ i++; // Skip escaped char
1372
+ continue;
1373
+ }
1374
+ if (char === stringChar) {
1375
+ inString = false;
1376
+ }
1377
+ continue;
1378
+ }
1379
+ if (char === '"' || char === "'" || char === '`') {
1380
+ inString = true;
1381
+ stringChar = char;
1382
+ continue;
1383
+ }
1384
+ if (char === '(' || char === '[' || char === '{') {
1385
+ depth++;
1386
+ continue;
1387
+ }
1388
+ if (char === ')' || char === ']' || char === '}') {
1389
+ depth--;
1390
+ continue;
1391
+ }
1392
+ // Found a colon at depth 0 = has property name
1393
+ if (char === ':' && depth === 0) {
1394
+ return false;
1395
+ }
1396
+ }
1397
+ return true;
1398
+ })();
1399
+
1400
+ if (hasBareExpression) {
1401
+ // Content is just an expression - return it directly without object wrapper
1402
+ const trimmedContent = mappedContents
1403
+ .trim()
1404
+ .replace(/,\s*$/, '');
1405
+ returnValueContents = `${arrayBasePath}?.map((__item__, __idx__) => (\n${indent(trimmedContent)}\n))`;
1406
+ } else {
1407
+ returnValueContents = `${arrayBasePath}?.map((__item__, __idx__) => ({\n${indent(mappedContents)}\n}))`;
1408
+ }
1409
+ } // Close the empty content check else block
683
1410
  } else {
1411
+ // Content already starts with '{'. Check if there are additional properties after the inner object.
1412
+ // If so, we need to merge them INTO the object, not leave them outside.
1413
+ // Pattern: "{ ...spread, props },\nfilter: ...,\nsort: ..."
1414
+ // Should become: "{ ...spread, props, filter: ..., sort: ... }"
1415
+ const trimmed = mappedContents.trim();
1416
+
1417
+ // Find first }, at depth 0 that is NOT inside a string literal
1418
+ // This prevents splitting keys like ?.["useQuery({ id }, { enabled })"]
1419
+ // and also prevents finding }, inside nested arrow functions
1420
+ const findBraceCommaOutsideStrings = (
1421
+ content: string,
1422
+ ): number => {
1423
+ let i = 0;
1424
+ let depth = 0; // Track brace depth to find the outer object's },
1425
+ while (i < content.length - 1) {
1426
+ // Skip over string literals
1427
+ const strEnd = skipStringLiteral(content, i);
1428
+ if (strEnd !== -1) {
1429
+ i = strEnd;
1430
+ continue;
1431
+ }
1432
+ // Track brace depth
1433
+ if (content[i] === '{') {
1434
+ depth++;
1435
+ i++;
1436
+ continue;
1437
+ }
1438
+ // Check for }, pattern at depth 1 (the outer object level)
1439
+ // We're looking for the outer object's closing brace, which is at depth 1
1440
+ // (we started at depth 0, opened { at depth 0 -> 1)
1441
+ if (content[i] === '}') {
1442
+ depth--;
1443
+ if (
1444
+ depth === 0 &&
1445
+ i + 1 < content.length &&
1446
+ content[i + 1] === ','
1447
+ ) {
1448
+ return i;
1449
+ }
1450
+ }
1451
+ i++;
1452
+ }
1453
+ return -1;
1454
+ };
1455
+
1456
+ const firstBraceEnd = findBraceCommaOutsideStrings(trimmed);
1457
+ if (firstBraceEnd !== -1) {
1458
+ // Found pattern "{ ... }," followed by more content
1459
+ // Extract the inner object and the trailing properties
1460
+ const innerObject = trimmed.slice(0, firstBraceEnd);
1461
+ const trailingContent = trimmed.slice(firstBraceEnd + 2).trim();
1462
+ if (trailingContent) {
1463
+ // Merge trailing properties into the inner object
1464
+ mappedContents = `${innerObject},\n${trailingContent}\n}`;
1465
+ }
1466
+ }
1467
+ // Even when content starts with {, we need to filter out invalid properties inside
1468
+ // (arrow functions and bare objects that were generated from the schema)
1469
+ // Pass skipFirstBrace=true because the content's outer { is the intentional wrapper
1470
+ mappedContents = filterOutArrowFunctions(mappedContents);
1471
+ mappedContents = filterOutBareObjects(mappedContents, true);
1472
+ mappedContents = cleanupContent(mappedContents);
684
1473
  returnValueContents = `${arrayBasePath}?.map((__item__, __idx__) => (\n${indent(mappedContents)}\n))`;
685
1474
  }
686
1475
  } else {
687
1476
  returnValueContents = `[\n${indent(levelContents)}\n]`;
688
1477
  }
689
1478
  } else {
690
- returnValueContents = `{\n${indent(levelContents)}\n}`;
1479
+ // When we have a single data path and nested content that creates an object structure,
1480
+ // and we're NOT at the root level, we need to handle the case where the parent data
1481
+ // value is null or undefined. Without this check, `{ ...null, prop: null?.["prop"] }`
1482
+ // creates `{ prop: undefined }` instead of `null`, causing errors like
1483
+ // "Cannot read properties of undefined (reading 'some')" when code does
1484
+ // data?.prop.some(...) because data is an object with prop: undefined, not null.
1485
+ // We only apply this to non-root cases because root-level mocks are expected to exist.
1486
+ // We also skip structural elements (like [0] inside arrays) because the null check
1487
+ // syntax doesn't work inside .map() callbacks where structural elements are used.
1488
+ // We also skip array index elements ([0], [1], etc.) because they represent tuple/array
1489
+ // elements, not properties that could be null.
1490
+ // We also only apply this when we're inside a function return value context - i.e.,
1491
+ // when the data path contains a function call pattern like ?.["someFunction(...)"].
1492
+ // This prevents adding null checks to intermediate objects in chains like supabase.auth.
1493
+ const hasNestedContent = nestedContent.filter(Boolean).length > 0;
1494
+ const isArrayIndexElement = name.match(/^\[\d*\]$/);
1495
+ // Check if data path contains a function call pattern, indicating we're inside a function return value
1496
+ const isInsideFunctionReturnValue =
1497
+ dataPaths.length === 1 &&
1498
+ dataPaths[0].match(/\?\.\["\w+\([^"]*\)"\]/);
1499
+ if (
1500
+ !root &&
1501
+ !returnValue.isStructural &&
1502
+ !isArrayIndexElement &&
1503
+ isInsideFunctionReturnValue &&
1504
+ hasNestedContent
1505
+ ) {
1506
+ // Wrap with null check: if parent is null/undefined, return it directly; otherwise create object
1507
+ returnValueContents = `${dataPaths[0]} == null ? ${dataPaths[0]} : {\n${indent(levelContents)}\n}`;
1508
+ } else {
1509
+ returnValueContents = `{\n${indent(levelContents)}\n}`;
1510
+ }
691
1511
  }
692
1512
  }
693
1513
 
@@ -699,6 +1519,13 @@ export default function constructMockCode(
699
1519
  if (args && args.length > 0) {
700
1520
  if (!isValidKey(name)) return;
701
1521
 
1522
+ // Skip array index patterns like [], [0], [1] when they have args
1523
+ // These represent function calls on array elements, not property keys
1524
+ // e.g., customSizes[].(args) means each array element is callable, not a property named "[]"
1525
+ if (name.match(/^\[\d*\]$/)) {
1526
+ return;
1527
+ }
1528
+
702
1529
  const mostArgs = args.sort(
703
1530
  (a: string[], b: string[]) => b.length - a.length,
704
1531
  )[0];
@@ -758,8 +1585,18 @@ export default function constructMockCode(
758
1585
  // Use all paths for fallback (existing behavior)
759
1586
  fallbackContent = `return ${returnValueContents}`;
760
1587
  } else {
761
- // Use all paths as fallback for unknown arguments
762
- fallbackContent = `return ${returnValueContents}`;
1588
+ // No explicit fallback paths - return the first literal's value as default
1589
+ // Returning spread of all values is dangerous because if values are primitives (strings),
1590
+ // spreading them creates objects with numeric keys like {0:'a', 1:'b', ...}
1591
+ // which causes "Objects are not valid as React child" errors
1592
+ const firstLiteralValue = literalKeys[0];
1593
+ const firstGroupPaths = argGroups.get(firstLiteralValue);
1594
+ if (firstGroupPaths && firstGroupPaths.length === 1) {
1595
+ fallbackContent = `return ${firstGroupPaths[0]}`;
1596
+ } else {
1597
+ // Multiple paths for first literal - return undefined as safe fallback
1598
+ fallbackContent = `return undefined`;
1599
+ }
763
1600
  }
764
1601
 
765
1602
  const funcContents =
@@ -769,14 +1606,29 @@ export default function constructMockCode(
769
1606
  content = `${cleanOutTypes(name)}: ${isAsyncFunction ? 'async ' : ''}(${argsString}) => {\n${indent(funcContents)}\n}`;
770
1607
  } else {
771
1608
  // No argument variants - use existing behavior
772
- const funcContents = `return ${returnValueContents}`;
1609
+ // But if there's nested content, we need to include it in the return object
1610
+ // (similar to how argument variant branches handle this at line 1070-1072)
1611
+ const hasNestedContent = validNestedContent.length > 0;
1612
+ let funcReturnContents: string;
1613
+ if (hasNestedContent && levelContentItems.length > 1) {
1614
+ // Include both spread and nested content in the return
1615
+ funcReturnContents = `{\n${indent(levelContents)}\n}`;
1616
+ } else {
1617
+ funcReturnContents = returnValueContents;
1618
+ }
1619
+ const funcContents = `return ${funcReturnContents}`;
773
1620
  content = `${cleanOutTypes(name)}: ${isAsyncFunction ? 'async ' : ''}(${argsString}) => {\n${indent(funcContents)}\n}`;
774
1621
  }
775
1622
  } else {
776
1623
  if (!isValidKey(name)) {
777
1624
  return;
778
1625
  } else if (name.match(/\[\d*\]/)) {
1626
+ // Numeric array index like [0], [1] - can be used as computed property
779
1627
  content = returnValueContents;
1628
+ } else if (name.match(/^\[[a-zA-Z_]\w*\]$/)) {
1629
+ // Variable-based index like [currentItemIndex] - must be quoted string key
1630
+ // Otherwise JavaScript would try to evaluate the variable name
1631
+ content = `"${safeString(name)}": ${returnValueContents}`;
780
1632
  } else {
781
1633
  content = `${safeString(name)}: ${returnValueContents}`;
782
1634
  }
@@ -791,34 +1643,55 @@ export default function constructMockCode(
791
1643
  };
792
1644
 
793
1645
  // Create the return value structure
794
- const sortedKeys = Object.keys(relevantReturnValueSchema ?? {}).sort(
795
- (a: string, b: string) => {
796
- const aParts = splitOutsideParenthesesAndArrays(a);
797
- const bParts = splitOutsideParenthesesAndArrays(b);
798
-
799
- const maxLength = Math.max(aParts.length, bParts.length);
800
- for (let i = 0; i < maxLength; ++i) {
801
- const aPart = aParts[i];
802
- const bPart = bParts[i];
803
-
804
- if (!aPart) return -1;
805
- if (!bPart) return 1;
806
-
807
- if (aPart === bPart) continue;
808
-
809
- const aName = aPart.split('(')[0];
810
- const bName = bPart.split('(')[0];
811
-
812
- if (aName !== bName) {
813
- return aName.localeCompare(bName);
814
- } else {
815
- return aPart.localeCompare(bPart);
816
- }
1646
+ // OPTIMIZATION: Filter keys to only those starting with baseMockName before sorting.
1647
+ // This dramatically reduces processing time for large schemas (e.g., 9216 keys -> ~100 relevant keys).
1648
+ // Without this filter, the loop would call splitOutsideParenthesesAndArrays on every key
1649
+ // even though most are filtered out later by the baseMockName check.
1650
+ const allSchemaKeys = Object.keys(relevantReturnValueSchema ?? {});
1651
+ const relevantKeys = allSchemaKeys.filter((key) => {
1652
+ // Fast prefix check - key must start with baseMockName followed by ( or < or .
1653
+ // This matches: "useAtom()", "useAtom<T>()", "useAtom.something", but not "useAtomValue()"
1654
+ if (key === baseMockName) return true;
1655
+ if (key.startsWith(baseMockName + '(')) return true;
1656
+ if (key.startsWith(baseMockName + '<')) return true;
1657
+ if (key.startsWith(baseMockName + '.')) return true;
1658
+ // Also include 'returnValue' paths which are normalized later
1659
+ if (
1660
+ key === 'returnValue' ||
1661
+ key.startsWith('returnValue.') ||
1662
+ key.startsWith('returnValue[')
1663
+ )
1664
+ return true;
1665
+ return false;
1666
+ });
1667
+
1668
+ const schemaKeyCount = relevantKeys.length;
1669
+ const sortedKeys = relevantKeys.sort((a: string, b: string) => {
1670
+ const aParts = splitOutsideParenthesesAndArrays(a);
1671
+ const bParts = splitOutsideParenthesesAndArrays(b);
1672
+
1673
+ const maxLength = Math.max(aParts.length, bParts.length);
1674
+ for (let i = 0; i < maxLength; ++i) {
1675
+ const aPart = aParts[i];
1676
+ const bPart = bParts[i];
1677
+
1678
+ if (!aPart) return -1;
1679
+ if (!bPart) return 1;
1680
+
1681
+ if (aPart === bPart) continue;
1682
+
1683
+ const aName = aPart.split('(')[0];
1684
+ const bName = bPart.split('(')[0];
1685
+
1686
+ if (aName !== bName) {
1687
+ return aName.localeCompare(bName);
1688
+ } else {
1689
+ return aPart.localeCompare(bPart);
817
1690
  }
1691
+ }
818
1692
 
819
- return 0;
820
- },
821
- );
1693
+ return 0;
1694
+ });
822
1695
 
823
1696
  for (const key of sortedKeys) {
824
1697
  const value = relevantReturnValueSchema[key];
@@ -853,8 +1726,8 @@ export default function constructMockCode(
853
1726
  }
854
1727
  }
855
1728
 
856
- // For variable-qualified mocks like "revalidator <- useRevalidator",
857
- // compare against baseMockName ("useRevalidator"), not the full mockName
1729
+ // Compare against baseMockName (without generics/args), not the full mockName
1730
+ // e.g., for "useFetcher<User>()", baseMockName is "useFetcher"
858
1731
  if (parts[0].split('(')[0] !== baseMockName) continue;
859
1732
 
860
1733
  // Include paths with functionCallReturnValue OR function-typed paths that need mocking
@@ -970,6 +1843,17 @@ export default function constructMockCode(
970
1843
  const nextIsArray = !!nextPart?.match(/^\[\d*\]/);
971
1844
  const isDifferentiatedArray = !!part?.match(/^\[\d+\]/);
972
1845
  const nextIsDifferentiatedArray = !!nextPart?.match(/^\[\d+\]/);
1846
+
1847
+ // Variable index patterns like [currentItemIndex] or [targetIndex] indicate array access
1848
+ // but don't represent actual data structure - they're markers from variable-based index tracking.
1849
+ // Skip them AND all remaining parts to avoid creating spurious nested structure that breaks array iteration.
1850
+ // The remaining parts (e.g., .missing_attributes) describe properties of array elements, which are
1851
+ // already handled by the generic [] accessor path.
1852
+ const isVariableIndex = !!part?.match(/^\[[a-zA-Z_]\w*\]$/);
1853
+ if (isVariableIndex) {
1854
+ // Break out of the loop entirely - don't process any remaining parts
1855
+ break;
1856
+ }
973
1857
  // Find the correct value for the current part being processed
974
1858
  let partValue = value; // default to the final value
975
1859
  if (isFunctionCallReturnValue(part) && nextIsArray) {
@@ -1077,7 +1961,52 @@ export default function constructMockCode(
1077
1961
  }
1078
1962
  }
1079
1963
  } else {
1080
- returnValueSection.returnsFunctionArgs = [];
1964
+ // Before setting returnsFunctionArgs on the parent (for generic [] = function),
1965
+ // check if there are specific array indices (like [0], [1]) that are NOT functions.
1966
+ // If so, don't set returnsFunctionArgs because those specific indices take precedence.
1967
+ // This prevents adding ["()"] to paths like [0] when [0] is 'unknown' but [] is 'function'.
1968
+ //
1969
+ // Use parts.slice(0, i + 1) to get the current path INCLUDING functionCallReturnValue.
1970
+ // For example, if parts = ['useAtom()','functionCallReturnValue','[]']
1971
+ // and i = 1, we want to check 'useAtom().functionCallReturnValue[0]' etc.
1972
+ const arrayContainerPath = joinParenthesesAndArrays(
1973
+ parts.slice(0, i + 1),
1974
+ );
1975
+
1976
+ const hasNonFunctionSpecificIndices = Object.entries(
1977
+ relevantReturnValueSchema,
1978
+ ).some(([k, v]) => {
1979
+ // Look for paths like "arrayContainerPath[0]", "arrayContainerPath[1]" etc.
1980
+ const indexMatch = k.match(
1981
+ new RegExp(
1982
+ `^${arrayContainerPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\[(\\d+)\\]$`,
1983
+ ),
1984
+ );
1985
+ // If found and it's NOT a function type, we have a conflict
1986
+ return (
1987
+ indexMatch &&
1988
+ !['function', 'async-function'].includes(v as string)
1989
+ );
1990
+ });
1991
+
1992
+ // Also check if [] has nested object properties (like [].filter, [].name)
1993
+ // If so, [] items are objects with properties, not pure functions to be called
1994
+ // This handles cases where the schema shows [].filter = object but doesn't
1995
+ // have explicit [0] entries
1996
+ const genericArrayPath = `${arrayContainerPath}[]`;
1997
+ const hasNestedProperties = Object.keys(
1998
+ relevantReturnValueSchema,
1999
+ ).some((k) => {
2000
+ // Check for paths like "arrayContainerPath[].propertyName" (not [].())
2001
+ return (
2002
+ k.startsWith(genericArrayPath + '.') &&
2003
+ !k.startsWith(genericArrayPath + '.(')
2004
+ );
2005
+ });
2006
+
2007
+ if (!hasNonFunctionSpecificIndices && !hasNestedProperties) {
2008
+ returnValueSection.returnsFunctionArgs = [];
2009
+ }
1081
2010
  }
1082
2011
  }
1083
2012
  }
@@ -1102,7 +2031,8 @@ export default function constructMockCode(
1102
2031
  }
1103
2032
  // If the next part is an object with nested content, continue processing
1104
2033
  // This handles paths like functionCallReturnValue.selectedOptions.elementOptions[]
1105
- if (nextValue === 'object' || nextValue === 'array') {
2034
+ // Also handles union types like 'array | undefined' or 'object | undefined'
2035
+ if (nextValue?.includes('object') || nextValue?.includes('array')) {
1106
2036
  continue;
1107
2037
  }
1108
2038
  }
@@ -1256,7 +2186,14 @@ export default function constructMockCode(
1256
2186
  relevantPart.isGenericArray = true;
1257
2187
  }
1258
2188
 
1259
- if (!hasNestedFunction) {
2189
+ // Check if there are remaining parts after functionCallReturnValue that need processing
2190
+ // (e.g., data properties like useQuery().functionCallReturnValue.data)
2191
+ const hasRemainingPartsAfterReturnValue =
2192
+ nextPart &&
2193
+ (isFunctionCallReturnValue(nextPart) ||
2194
+ (isFunctionCallReturnValue(parts[i]) && i < parts.length - 1));
2195
+
2196
+ if (!hasNestedFunction && !hasRemainingPartsAfterReturnValue) {
1260
2197
  // Before breaking, check if this function returns an array
1261
2198
  // by looking for a functionCallReturnValue: 'array' entry in the schema
1262
2199
  if (relevantPart && part.endsWith(')')) {
@@ -1288,6 +2225,7 @@ export default function constructMockCode(
1288
2225
 
1289
2226
  if (mockNameParts.length > 1) {
1290
2227
  const originalLib = `${mockNameParts[0]}__cyOriginal`;
2228
+ const skipOriginalSpread = options?.skipOriginalSpread;
1291
2229
 
1292
2230
  const subPart = (
1293
2231
  parts: string[],
@@ -1299,7 +2237,9 @@ export default function constructMockCode(
1299
2237
 
1300
2238
  const partContents = isLast
1301
2239
  ? contents
1302
- : `...${originalLib}.${part},\n${subPart(parts, originalLib)}`;
2240
+ : skipOriginalSpread
2241
+ ? subPart(parts, originalLib)
2242
+ : `...${originalLib}.${part},\n${subPart(parts, originalLib)}`;
1303
2243
 
1304
2244
  let code = `${part}: {\n${indent(partContents)}\n}`;
1305
2245
 
@@ -1313,27 +2253,31 @@ export default function constructMockCode(
1313
2253
  return code;
1314
2254
  };
1315
2255
 
1316
- const returnParts = [
1317
- `...${mockNameParts[0]}__cyOriginal`,
1318
- subPart(mockNameParts.slice(1), originalLib),
1319
- ].filter(Boolean);
2256
+ const returnParts = skipOriginalSpread
2257
+ ? [subPart(mockNameParts.slice(1), originalLib)]
2258
+ : [
2259
+ `...${mockNameParts[0]}__cyOriginal`,
2260
+ subPart(mockNameParts.slice(1), originalLib),
2261
+ ];
1320
2262
 
1321
- return `const ${mockNameParts[0]} = {\n${indent(returnParts.join(',\n'))}\n};`;
2263
+ return `const ${mockNameParts[0]} = {\n${indent(returnParts.filter(Boolean).join(',\n'))}\n};`;
1322
2264
  } else if (isFunction) {
1323
2265
  // For headers() and cookies() from next/headers, add common iterator methods
1324
2266
  // These are needed when the mock is passed to functions that use .entries(), .keys(), etc.
1325
2267
  // (e.g., Object.fromEntries(headers.entries()) in buildLegacyHeaders)
1326
2268
  const needsIteratorMethods =
1327
- mockName === 'headers' || mockName === 'cookies';
2269
+ baseMockName === 'headers' || baseMockName === 'cookies';
1328
2270
  let enhancedContents = contents;
1329
2271
  if (needsIteratorMethods && contents.trim().startsWith('{')) {
1330
2272
  // Add iterator methods that operate on the scenario data
2273
+ // Use the dataKey (original call signature or canonical key)
2274
+ const quotedDataKey = quotePropertyKey(dataKey);
1331
2275
  const iteratorMethods = `,
1332
- entries: () => Object.entries(scenarios().data()?.["${mockName}()"] || {}),
1333
- keys: () => Object.keys(scenarios().data()?.["${mockName}()"] || {}),
1334
- values: () => Object.values(scenarios().data()?.["${mockName}()"] || {}),
1335
- forEach: (fn) => Object.entries(scenarios().data()?.["${mockName}()"] || {}).forEach(([k, v]) => fn(v, k)),
1336
- has: (key) => Object.prototype.hasOwnProperty.call(scenarios().data()?.["${mockName}()"] || {}, key)`;
2276
+ entries: () => Object.entries(scenarios().data()?.${quotedDataKey} || {}),
2277
+ keys: () => Object.keys(scenarios().data()?.${quotedDataKey} || {}),
2278
+ values: () => Object.values(scenarios().data()?.${quotedDataKey} || {}),
2279
+ forEach: (fn) => Object.entries(scenarios().data()?.${quotedDataKey} || {}).forEach(([k, v]) => fn(v, k)),
2280
+ has: (key) => Object.prototype.hasOwnProperty.call(scenarios().data()?.${quotedDataKey} || {}, key)`;
1337
2281
  // Insert before the closing brace (handle trailing whitespace)
1338
2282
  enhancedContents = contents.replace(/\}\s*$/, iteratorMethods + '\n}');
1339
2283
  }
@@ -1344,40 +2288,117 @@ export default function constructMockCode(
1344
2288
  // `new ClassName("arg")` wouldn't create the expected instance.
1345
2289
  // For Error subclasses (detected by name ending in "Error"), extend Error for proper error handling.
1346
2290
  if (entityType === 'class') {
1347
- const isErrorSubclass = mockName.endsWith('Error');
1348
- const baseClass = isErrorSubclass ? 'Error' : 'Object';
2291
+ const isErrorSubclass = baseMockName.endsWith('Error');
1349
2292
  const superCall = isErrorSubclass ? 'super(message);' : '';
1350
2293
  const nameAssignment = isErrorSubclass
1351
- ? `this.name = '${mockName}';`
2294
+ ? `this.name = '${baseMockName}';`
1352
2295
  : '';
2296
+ // Use the safe function name for the class definition
2297
+ const className = mockNameIsCallSignature
2298
+ ? derivedFunctionName
2299
+ : baseMockName;
1353
2300
 
1354
- return `class ${mockName}${isErrorSubclass ? ' extends Error' : ''} {
2301
+ return `class ${className}${isErrorSubclass ? ' extends Error' : ''} {
1355
2302
  constructor(message) {
1356
2303
  ${superCall}
1357
2304
  ${nameAssignment}
1358
- Object.assign(this, scenarios().data()?.["${mockName}()"] || {});
2305
+ Object.assign(this, scenarios().data()?.${quotePropertyKey(dataKey)} || {});
1359
2306
  }
1360
2307
  }`;
1361
2308
  }
1362
2309
 
1363
- // For variable-qualified mocks, use underscore separator in the function name
1364
- // e.g., "entityDiffFetcher <- useFetcher" becomes "useFetcher_entityDiffFetcher"
1365
- // For indexed qualifiers like "data[1]", convert to safe identifier: "useFetcher_data_1"
1366
- // EXCEPTION: When keepOriginalFunctionName is true (for single-call cases), use the base name
1367
- const safeFunctionName =
1368
- variableQualifier && !options?.keepOriginalFunctionName
1369
- ? `${baseMockName}_${safeVariableQualifier(variableQualifier)}`
1370
- : baseMockName;
2310
+ // Generate safe function name:
2311
+ // 1. For call signatures: use derivedFunctionName
2312
+ // e.g., "useFetcher<User>()" becomes "useFetcher_User"
2313
+ // e.g., "db.select(usersQuery)" becomes "db_select_usersQuery"
2314
+ // 2. With uniqueFunctionSuffix option: append suffix for unique naming
2315
+ // e.g., baseMockName = "useFetcher", suffix = "entityDiffFetcher" -> "useFetcher_entityDiffFetcher"
2316
+ // 3. EXCEPTION: When keepOriginalFunctionName is true (for single-call cases), use the base name
2317
+ let safeFunctionName: string;
2318
+ if (options?.keepOriginalFunctionName) {
2319
+ safeFunctionName = baseMockName;
2320
+ } else if (options?.uniqueFunctionSuffix) {
2321
+ safeFunctionName = `${baseMockName}_${options.uniqueFunctionSuffix}`;
2322
+ } else if (mockNameIsCallSignature && derivedFunctionName) {
2323
+ safeFunctionName = derivedFunctionName;
2324
+ } else {
2325
+ safeFunctionName = baseMockName;
2326
+ }
2327
+
2328
+ // Check if this function returns a function (detected by double-call pattern: mockName(args)())
2329
+ // This happens when the schema has keys like "wrapThrows(() => JSON.parse(savedFilters))()"
2330
+ // where the function call is immediately followed by another call.
2331
+ // Example usage: const result = wrapThrows(() => JSON.parse(x))(); // double call
2332
+ const isHigherOrderFunction = Object.keys(
2333
+ relevantReturnValueSchema ?? {},
2334
+ ).some((key) => {
2335
+ if (!key.startsWith(baseMockName)) return false;
2336
+
2337
+ // Find the first ( after baseMockName (the start of the function call)
2338
+ const firstOpenParen = key.indexOf('(', baseMockName.length);
2339
+ if (firstOpenParen === -1) return false;
2340
+
2341
+ // Skip if the ( is not immediately after the mock name
2342
+ // (there might be type params like func<T>() - handle by checking for < or ()
2343
+ const between = key.slice(baseMockName.length, firstOpenParen);
2344
+ if (between.length > 0 && !between.startsWith('<')) return false;
2345
+
2346
+ // Find the matching ) for the first ( using depth counting
2347
+ let depth = 1;
2348
+ let i = firstOpenParen + 1;
2349
+ while (i < key.length && depth > 0) {
2350
+ if (key[i] === '(') depth++;
2351
+ if (key[i] === ')') depth--;
2352
+ i++;
2353
+ }
1371
2354
 
1372
- return `const ${safeFunctionName}ReturnValue = ${enhancedContents};\n\n${isRootAsyncFunction ? 'async ' : ''}function ${safeFunctionName}() {\n${indent(`return ${safeFunctionName}ReturnValue;`)}\n}`;
2355
+ if (depth !== 0) return false; // Unbalanced parentheses
2356
+
2357
+ // Now i points just after the matching )
2358
+ // Check if there's another ( immediately (indicating double call)
2359
+ const remaining = key.slice(i);
2360
+ if (remaining.startsWith('(')) return true;
2361
+
2362
+ return false;
2363
+ });
2364
+
2365
+ // Use ...args to accept any number of arguments - prevents TypeScript errors
2366
+ // like "Expected 0 arguments, but got X" when caller passes arguments
2367
+ // For higher-order functions, wrap the return in an arrow function
2368
+ // so that mockFunc(arg)() works correctly (outer call returns a function, inner call gets the data)
2369
+ const returnValue = isHigherOrderFunction
2370
+ ? `() => (${enhancedContents})`
2371
+ : enhancedContents;
2372
+
2373
+ // Inline the return value directly in the function to avoid module-level const
2374
+ // that would be evaluated before scenario context is ready
2375
+ // Add fallback for simple data path returns to prevent undefined errors (e.g., createTheme)
2376
+ // Only add fallback if returnValue is a simple data accessor (starts with scenarios().data())
2377
+ // and doesn't already have nested structure (object literal, array, or method chains like .map())
2378
+ const isSimpleDataPath =
2379
+ returnValue.startsWith('scenarios().data()') &&
2380
+ !returnValue.trim().startsWith('{') &&
2381
+ !returnValue.trim().startsWith('[') &&
2382
+ !returnValue.includes('.map('); // Exclude method chains
2383
+ const safeReturnValue = isSimpleDataPath
2384
+ ? `${returnValue} ?? {}`
2385
+ : returnValue;
2386
+ return `${isRootAsyncFunction ? 'async ' : ''}function ${safeFunctionName}(...args) {\n${indent(`return ${safeReturnValue};`)}\n}`;
1373
2387
  } else {
1374
- // For variable-qualified mocks, use underscore separator in the const name
1375
- // For indexed qualifiers like "data[1]", convert to safe identifier: "useFetcher_data_1"
1376
- // EXCEPTION: When keepOriginalFunctionName is true (for single-call cases), use the base name
1377
- const safeName =
1378
- variableQualifier && !options?.keepOriginalFunctionName
1379
- ? `${baseMockName}_${safeVariableQualifier(variableQualifier)}`
1380
- : baseMockName;
2388
+ // Generate safe const name:
2389
+ // 1. For call signatures: use derivedFunctionName
2390
+ // 2. With uniqueFunctionSuffix option: append suffix for unique naming
2391
+ // 3. EXCEPTION: When keepOriginalFunctionName is true (for single-call cases), use the base name
2392
+ let safeName: string;
2393
+ if (options?.keepOriginalFunctionName) {
2394
+ safeName = baseMockName;
2395
+ } else if (options?.uniqueFunctionSuffix) {
2396
+ safeName = `${baseMockName}_${options.uniqueFunctionSuffix}`;
2397
+ } else if (mockNameIsCallSignature && derivedFunctionName) {
2398
+ safeName = derivedFunctionName;
2399
+ } else {
2400
+ safeName = baseMockName;
2401
+ }
1381
2402
 
1382
2403
  // Get any jsx-component properties that need to be preserved from the original
1383
2404
  const jsxProperties = getJsxComponentProperties(