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