@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
|
@@ -13,9 +13,16 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import type { ExecutionFlow } from '~codeyam/types';
|
|
16
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
ConditionalUsage,
|
|
18
|
+
CompoundConditional,
|
|
19
|
+
DerivedVariableOperation,
|
|
20
|
+
DerivedVariableInfo,
|
|
21
|
+
JsxRenderingUsage,
|
|
22
|
+
} from './astScopes/types';
|
|
17
23
|
import type { EnrichedConditionalUsage } from './worker/SerializableDataStructure';
|
|
18
24
|
import resolvePathToControllable from './resolvePathToControllable';
|
|
25
|
+
import cleanPathOfNonTransformingFunctions from './dataStructure/helpers/cleanPathOfNonTransformingFunctions';
|
|
19
26
|
|
|
20
27
|
/** Extended conditional usage type that may include sourceDataPath from enrichment */
|
|
21
28
|
type ExtendedConditionalUsage = ConditionalUsage &
|
|
@@ -26,7 +33,7 @@ export interface ChildComponentConditionalData {
|
|
|
26
33
|
/** Child's conditional usages keyed by variable name (may include sourceDataPath from enrichment) */
|
|
27
34
|
conditionalUsages: Record<string, ExtendedConditionalUsage[]>;
|
|
28
35
|
/** Child's equivalent signature variables (maps internal paths to prop paths) */
|
|
29
|
-
equivalentSignatureVariables: Record<string, string>;
|
|
36
|
+
equivalentSignatureVariables: Record<string, string | string[]>;
|
|
30
37
|
/** Child's compound conditionals */
|
|
31
38
|
compoundConditionals: CompoundConditional[];
|
|
32
39
|
/**
|
|
@@ -35,6 +42,11 @@ export interface ChildComponentConditionalData {
|
|
|
35
42
|
* then `hasAnalysis` is a gating condition for all of ChildComponent's flows.
|
|
36
43
|
*/
|
|
37
44
|
gatingConditions?: ConditionalUsage[];
|
|
45
|
+
/**
|
|
46
|
+
* Child's JSX rendering usages (arrays rendered via .map(), text interpolation).
|
|
47
|
+
* These generate "variation flows" for different array lengths.
|
|
48
|
+
*/
|
|
49
|
+
jsxRenderingUsages?: JsxRenderingUsage[];
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
export interface GenerateFlowsFromConditionalsArgs {
|
|
@@ -45,7 +57,7 @@ export interface GenerateFlowsFromConditionalsArgs {
|
|
|
45
57
|
/** Map of controllable paths to their types */
|
|
46
58
|
attributesMap: Record<string, string>;
|
|
47
59
|
/** Map from local variable names to data sources */
|
|
48
|
-
equivalentSignatureVariables: Record<string, string>;
|
|
60
|
+
equivalentSignatureVariables: Record<string, string | string[]>;
|
|
49
61
|
/** Map from full paths to short paths */
|
|
50
62
|
fullToShortPathMap: Record<string, string>;
|
|
51
63
|
/**
|
|
@@ -54,6 +66,173 @@ export interface GenerateFlowsFromConditionalsArgs {
|
|
|
54
66
|
* Used to merge child execution flows into parent.
|
|
55
67
|
*/
|
|
56
68
|
childComponentData?: Record<string, ChildComponentConditionalData>;
|
|
69
|
+
/**
|
|
70
|
+
* Optional map of derived variables to their derivation info.
|
|
71
|
+
* Used to trace intermediate derived variables that aren't directly
|
|
72
|
+
* used in conditionals but are sources for other derived variables.
|
|
73
|
+
*
|
|
74
|
+
* Example:
|
|
75
|
+
* - `isInCurrentRun = currentRun.entityShas.includes(sha)` → { sourcePath: 'currentRun.entityShas', operation: 'arrayIncludes' }
|
|
76
|
+
* - `isAnalyzing = isInCurrentRun || isInQueue` → { sourcePaths: ['isInCurrentRun', 'isInQueue'], operation: 'or' }
|
|
77
|
+
*
|
|
78
|
+
* Without this map, we can't trace from `isAnalyzing` through `isInCurrentRun`
|
|
79
|
+
* to `currentRun.entityShas` when `isInCurrentRun` isn't in conditionalUsages.
|
|
80
|
+
*/
|
|
81
|
+
derivedVariables?: Record<string, DerivedVariableInfo>;
|
|
82
|
+
/**
|
|
83
|
+
* Optional map of child prop paths to their actual data sources.
|
|
84
|
+
* Used when child props flow through useState but ultimately come from
|
|
85
|
+
* mockable data sources (e.g., API calls, fetchers).
|
|
86
|
+
*
|
|
87
|
+
* Example:
|
|
88
|
+
* - "WorkoutsView().signature[0].workouts" → "createClient()...functionCallReturnValue.data"
|
|
89
|
+
*
|
|
90
|
+
* When a child path translates to a useState value, we check this map
|
|
91
|
+
* to find the real data source that can be mocked.
|
|
92
|
+
*/
|
|
93
|
+
sourceEquivalencies?: Record<
|
|
94
|
+
string,
|
|
95
|
+
Array<{ scopeNodeName: string; schemaPath: string }>
|
|
96
|
+
>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Resolved source info from expanding a derived variable.
|
|
101
|
+
* Contains the path and the operation that led to it.
|
|
102
|
+
*/
|
|
103
|
+
interface ResolvedSourceInfo {
|
|
104
|
+
path: string;
|
|
105
|
+
operation?:
|
|
106
|
+
| 'notNull'
|
|
107
|
+
| 'isNull'
|
|
108
|
+
| 'equals'
|
|
109
|
+
| 'notEquals'
|
|
110
|
+
| 'or'
|
|
111
|
+
| 'and'
|
|
112
|
+
| 'arrayIncludes'
|
|
113
|
+
| 'arraySome'
|
|
114
|
+
| 'arrayEvery'
|
|
115
|
+
| 'arrayLength';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Recursively expands a derived variable to its leaf data sources.
|
|
120
|
+
*
|
|
121
|
+
* For OR expressions like `isAnalyzing = a || b || c`:
|
|
122
|
+
* - Returns all source paths [a, b, c] so they can all be set appropriately
|
|
123
|
+
*
|
|
124
|
+
* For nested derivations like `isAnalyzing = isInCurrentRun || isInQueue`:
|
|
125
|
+
* - Where `isInCurrentRun` is derived from `currentRun.entityShas.includes(x)`
|
|
126
|
+
* - Returns the final data sources: [currentRun.entityShas, queueState.jobs]
|
|
127
|
+
*
|
|
128
|
+
* @param path The variable path to expand
|
|
129
|
+
* @param conditionalUsages All conditional usages (to look up derivedFrom info)
|
|
130
|
+
* @param attributesMap Map of controllable paths
|
|
131
|
+
* @param equivalentSignatureVariables Variable-to-path mappings
|
|
132
|
+
* @param fullToShortPathMap Full-to-short path mappings
|
|
133
|
+
* @param visited Set of already-visited paths (prevents infinite recursion)
|
|
134
|
+
* @param derivedVariables Optional map of all derived variables (for intermediate tracing)
|
|
135
|
+
* @returns Array of resolved source paths that are controllable
|
|
136
|
+
*/
|
|
137
|
+
function expandDerivedVariableToSources(
|
|
138
|
+
path: string,
|
|
139
|
+
conditionalUsages: Record<string, ConditionalUsage[]>,
|
|
140
|
+
attributesMap: Record<string, string>,
|
|
141
|
+
equivalentSignatureVariables: Record<string, string | string[]>,
|
|
142
|
+
fullToShortPathMap: Record<string, string>,
|
|
143
|
+
visited: Set<string> = new Set(),
|
|
144
|
+
derivedVariables?: Record<string, DerivedVariableInfo>,
|
|
145
|
+
): ResolvedSourceInfo[] {
|
|
146
|
+
// Prevent infinite recursion
|
|
147
|
+
if (visited.has(path)) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
visited.add(path);
|
|
151
|
+
|
|
152
|
+
// First, check if this path is directly controllable
|
|
153
|
+
const directResolution = resolvePathToControllable(
|
|
154
|
+
path,
|
|
155
|
+
attributesMap,
|
|
156
|
+
equivalentSignatureVariables,
|
|
157
|
+
fullToShortPathMap,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
if (directResolution.isControllable && directResolution.resolvedPath) {
|
|
161
|
+
return [{ path: directResolution.resolvedPath }];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Look up derivedFrom info for this path
|
|
165
|
+
// First check conditionalUsages, then fall back to derivedVariables
|
|
166
|
+
const usage = conditionalUsages[path]?.[0];
|
|
167
|
+
let derivedFrom = usage?.derivedFrom;
|
|
168
|
+
|
|
169
|
+
// CRITICAL: If not found in conditionalUsages, check derivedVariables
|
|
170
|
+
// This handles intermediate derived variables like `isInCurrentRun` that aren't
|
|
171
|
+
// directly used in conditionals but ARE derived from data sources
|
|
172
|
+
if (!derivedFrom && derivedVariables?.[path]) {
|
|
173
|
+
derivedFrom = derivedVariables[path];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!derivedFrom) {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const { operation, sourcePath, sourcePaths } = derivedFrom;
|
|
181
|
+
|
|
182
|
+
// For OR/AND operations, recursively expand all source paths
|
|
183
|
+
if ((operation === 'or' || operation === 'and') && sourcePaths) {
|
|
184
|
+
const allSources: ResolvedSourceInfo[] = [];
|
|
185
|
+
|
|
186
|
+
for (const sp of sourcePaths) {
|
|
187
|
+
const expanded = expandDerivedVariableToSources(
|
|
188
|
+
sp,
|
|
189
|
+
conditionalUsages,
|
|
190
|
+
attributesMap,
|
|
191
|
+
equivalentSignatureVariables,
|
|
192
|
+
fullToShortPathMap,
|
|
193
|
+
visited,
|
|
194
|
+
derivedVariables,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// Add all expanded sources
|
|
198
|
+
for (const source of expanded) {
|
|
199
|
+
// Avoid duplicates
|
|
200
|
+
if (!allSources.some((s) => s.path === source.path)) {
|
|
201
|
+
allSources.push(source);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return allSources;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// For single-source operations (arrayIncludes, arraySome, notNull, etc.)
|
|
210
|
+
if (sourcePath) {
|
|
211
|
+
// Try to resolve the source path directly
|
|
212
|
+
const sourceResolution = resolvePathToControllable(
|
|
213
|
+
sourcePath,
|
|
214
|
+
attributesMap,
|
|
215
|
+
equivalentSignatureVariables,
|
|
216
|
+
fullToShortPathMap,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
if (sourceResolution.isControllable && sourceResolution.resolvedPath) {
|
|
220
|
+
return [{ path: sourceResolution.resolvedPath, operation }];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// If not directly resolvable, recursively expand
|
|
224
|
+
return expandDerivedVariableToSources(
|
|
225
|
+
sourcePath,
|
|
226
|
+
conditionalUsages,
|
|
227
|
+
attributesMap,
|
|
228
|
+
equivalentSignatureVariables,
|
|
229
|
+
fullToShortPathMap,
|
|
230
|
+
visited,
|
|
231
|
+
derivedVariables,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return [];
|
|
57
236
|
}
|
|
58
237
|
|
|
59
238
|
/**
|
|
@@ -113,6 +292,52 @@ function cleanSourceDataPath(sourceDataPath: string): string | null {
|
|
|
113
292
|
return actualPath;
|
|
114
293
|
}
|
|
115
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Strip .length suffix from a path if present.
|
|
297
|
+
*
|
|
298
|
+
* When we have a path like "items.length", the controllable attribute is "items"
|
|
299
|
+
* (the array), not "items.length". The length is derived from the array contents.
|
|
300
|
+
*
|
|
301
|
+
* This ensures that execution flows reference the actual controllable attribute
|
|
302
|
+
* rather than the derived .length property.
|
|
303
|
+
*/
|
|
304
|
+
function stripLengthSuffix(path: string): string {
|
|
305
|
+
if (path.endsWith('.length')) {
|
|
306
|
+
return path.slice(0, -7); // Remove ".length" (7 characters)
|
|
307
|
+
}
|
|
308
|
+
return path;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Extract the controllable base path from a path that may contain method calls.
|
|
313
|
+
*
|
|
314
|
+
* This handles complex expressions like:
|
|
315
|
+
* - `scenarios.filter((s) => s.active).length` → `scenarios`
|
|
316
|
+
* - `users.some((u) => u.role === 'admin')` → `users`
|
|
317
|
+
* - `items.map(x => x.name).join(', ')` → `items`
|
|
318
|
+
*
|
|
319
|
+
* The controllable base is the path that can be mocked - we can control
|
|
320
|
+
* what `scenarios` contains, but we can't control what `.filter()` returns.
|
|
321
|
+
*
|
|
322
|
+
* @param path - The path that may contain method calls
|
|
323
|
+
* @returns The controllable base path with method calls stripped
|
|
324
|
+
*/
|
|
325
|
+
function extractControllableBase(path: string): string {
|
|
326
|
+
// First strip .length suffix if present
|
|
327
|
+
const pathWithoutLength = stripLengthSuffix(path);
|
|
328
|
+
|
|
329
|
+
// Use cleanPathOfNonTransformingFunctions to strip method calls like .filter(), .some()
|
|
330
|
+
const cleanedPath = cleanPathOfNonTransformingFunctions(pathWithoutLength);
|
|
331
|
+
|
|
332
|
+
// If the cleaned path is different, return it
|
|
333
|
+
if (cleanedPath !== pathWithoutLength) {
|
|
334
|
+
return cleanedPath;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Otherwise, return the path with just .length stripped
|
|
338
|
+
return pathWithoutLength;
|
|
339
|
+
}
|
|
340
|
+
|
|
116
341
|
/**
|
|
117
342
|
* Find a path in attributesMap, using fullToShortPathMap to verify the path is controllable.
|
|
118
343
|
*
|
|
@@ -183,6 +408,20 @@ function findInAttributesMapForPath(
|
|
|
183
408
|
return null;
|
|
184
409
|
}
|
|
185
410
|
|
|
411
|
+
/**
|
|
412
|
+
* Generate a slug from a path for use in flow IDs and exclusive groups.
|
|
413
|
+
*/
|
|
414
|
+
function pathToSlug(path: string): string {
|
|
415
|
+
return path
|
|
416
|
+
.replace(/\[\d+\]/g, '')
|
|
417
|
+
.replace(/\[\]/g, '')
|
|
418
|
+
.replace(/\(\)/g, '')
|
|
419
|
+
.replace(/\.functionCallReturnValue/g, '')
|
|
420
|
+
.replace(/[<>]/g, '')
|
|
421
|
+
.replace(/\./g, '-')
|
|
422
|
+
.toLowerCase();
|
|
423
|
+
}
|
|
424
|
+
|
|
186
425
|
/**
|
|
187
426
|
* Generate a human-readable name from a path.
|
|
188
427
|
* Extracts the last meaningful part of the path.
|
|
@@ -244,6 +483,12 @@ function inferValueType(
|
|
|
244
483
|
/**
|
|
245
484
|
* Generate flows from a single conditional usage.
|
|
246
485
|
* Sets impact to 'high' if the conditional controls JSX rendering.
|
|
486
|
+
*
|
|
487
|
+
* When the usage has a `constraintExpression`, it represents a complex expression
|
|
488
|
+
* that can't be simply resolved (e.g., `scenarios.filter(x => x.active).length > 1`).
|
|
489
|
+
* In this case:
|
|
490
|
+
* - `attributePath` is set to the controllable base (e.g., `scenarios`)
|
|
491
|
+
* - `constraint` is set to the full expression for LLM reasoning
|
|
247
492
|
*/
|
|
248
493
|
function generateFlowsFromUsage(
|
|
249
494
|
usage: ConditionalUsage,
|
|
@@ -258,6 +503,14 @@ function generateFlowsFromUsage(
|
|
|
258
503
|
? 'high'
|
|
259
504
|
: 'medium';
|
|
260
505
|
|
|
506
|
+
// When there's a constraintExpression, use the controllable base for attributePath
|
|
507
|
+
// and pass through the constraint for LLM reasoning
|
|
508
|
+
const hasConstraint = !!usage.constraintExpression;
|
|
509
|
+
const attributePath = hasConstraint
|
|
510
|
+
? extractControllableBase(resolvedPath)
|
|
511
|
+
: stripLengthSuffix(resolvedPath);
|
|
512
|
+
const constraint = usage.constraintExpression;
|
|
513
|
+
|
|
261
514
|
if (usage.conditionType === 'truthiness') {
|
|
262
515
|
// Generate both truthy and falsy flows
|
|
263
516
|
const isNegated = usage.isNegated ?? false;
|
|
@@ -269,10 +522,11 @@ function generateFlowsFromUsage(
|
|
|
269
522
|
description: `When ${baseName.toLowerCase()} is ${isNegated ? 'falsy' : 'truthy'}`,
|
|
270
523
|
requiredValues: [
|
|
271
524
|
{
|
|
272
|
-
attributePath
|
|
525
|
+
attributePath,
|
|
273
526
|
value: isNegated ? 'falsy' : 'truthy',
|
|
274
527
|
comparison: isNegated ? 'falsy' : 'truthy',
|
|
275
528
|
valueType: 'boolean',
|
|
529
|
+
constraint,
|
|
276
530
|
},
|
|
277
531
|
],
|
|
278
532
|
impact,
|
|
@@ -292,10 +546,11 @@ function generateFlowsFromUsage(
|
|
|
292
546
|
description: `When ${baseName.toLowerCase()} is ${isNegated ? 'truthy' : 'falsy'}`,
|
|
293
547
|
requiredValues: [
|
|
294
548
|
{
|
|
295
|
-
attributePath
|
|
549
|
+
attributePath,
|
|
296
550
|
value: isNegated ? 'truthy' : 'falsy',
|
|
297
551
|
comparison: isNegated ? 'truthy' : 'falsy',
|
|
298
552
|
valueType: 'boolean',
|
|
553
|
+
constraint,
|
|
299
554
|
},
|
|
300
555
|
],
|
|
301
556
|
impact,
|
|
@@ -321,10 +576,11 @@ function generateFlowsFromUsage(
|
|
|
321
576
|
description: `When ${baseName.toLowerCase()} equals "${value}"`,
|
|
322
577
|
requiredValues: [
|
|
323
578
|
{
|
|
324
|
-
attributePath
|
|
579
|
+
attributePath,
|
|
325
580
|
value: value,
|
|
326
581
|
comparison: 'equals',
|
|
327
582
|
valueType: inferValueType(value),
|
|
583
|
+
constraint,
|
|
328
584
|
},
|
|
329
585
|
],
|
|
330
586
|
impact,
|
|
@@ -381,7 +637,7 @@ function generateFlowFromCompound(
|
|
|
381
637
|
}
|
|
382
638
|
|
|
383
639
|
requiredValues.push({
|
|
384
|
-
attributePath: resolvedPath,
|
|
640
|
+
attributePath: stripLengthSuffix(resolvedPath),
|
|
385
641
|
value,
|
|
386
642
|
comparison,
|
|
387
643
|
valueType: inferValueType(value),
|
|
@@ -412,6 +668,60 @@ function generateFlowFromCompound(
|
|
|
412
668
|
};
|
|
413
669
|
}
|
|
414
670
|
|
|
671
|
+
/**
|
|
672
|
+
* Expand a compound conditional with OR groups into multiple condition sets.
|
|
673
|
+
*
|
|
674
|
+
* For a compound like `A && (B || C)`:
|
|
675
|
+
* - Conditions: [{ path: 'A' }, { path: 'B', orGroupId: 'or_xxx' }, { path: 'C', orGroupId: 'or_xxx' }]
|
|
676
|
+
* - Returns: [[A, B], [A, C]] - two sets of conditions
|
|
677
|
+
*
|
|
678
|
+
* For multiple OR groups like `A && (B || C) && (D || E)`:
|
|
679
|
+
* - Returns: [[A, B, D], [A, B, E], [A, C, D], [A, C, E]]
|
|
680
|
+
*/
|
|
681
|
+
function expandOrGroups(
|
|
682
|
+
conditions: CompoundConditional['conditions'],
|
|
683
|
+
): CompoundConditional['conditions'][] {
|
|
684
|
+
// Separate conditions into mandatory (no orGroupId) and OR groups
|
|
685
|
+
const mandatory = conditions.filter((c) => !c.orGroupId);
|
|
686
|
+
const orGroups = new Map<string, typeof conditions>();
|
|
687
|
+
|
|
688
|
+
for (const condition of conditions) {
|
|
689
|
+
if (condition.orGroupId) {
|
|
690
|
+
const group = orGroups.get(condition.orGroupId) ?? [];
|
|
691
|
+
group.push(condition);
|
|
692
|
+
orGroups.set(condition.orGroupId, group);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// If no OR groups, return the original conditions
|
|
697
|
+
if (orGroups.size === 0) {
|
|
698
|
+
return [conditions];
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Generate all combinations by picking one condition from each OR group
|
|
702
|
+
const groupArrays = Array.from(orGroups.values());
|
|
703
|
+
const combinations: CompoundConditional['conditions'][] = [];
|
|
704
|
+
|
|
705
|
+
function generateCombinations(
|
|
706
|
+
index: number,
|
|
707
|
+
current: typeof conditions,
|
|
708
|
+
): void {
|
|
709
|
+
if (index === groupArrays.length) {
|
|
710
|
+
// We've picked one from each OR group - combine with mandatory conditions
|
|
711
|
+
combinations.push([...mandatory, ...current]);
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// Pick each option from the current OR group
|
|
716
|
+
for (const option of groupArrays[index]) {
|
|
717
|
+
generateCombinations(index + 1, [...current, option]);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
generateCombinations(0, []);
|
|
722
|
+
return combinations;
|
|
723
|
+
}
|
|
724
|
+
|
|
415
725
|
/**
|
|
416
726
|
* Generate execution flows from conditional usages using pure static analysis.
|
|
417
727
|
*
|
|
@@ -457,8 +767,8 @@ function normalizePathForDeduplication(
|
|
|
457
767
|
*/
|
|
458
768
|
function translateChildPathToParent(
|
|
459
769
|
childPath: string,
|
|
460
|
-
childEquivalentSignatureVariables: Record<string, string>,
|
|
461
|
-
parentEquivalentSignatureVariables: Record<string, string>,
|
|
770
|
+
childEquivalentSignatureVariables: Record<string, string | string[]>,
|
|
771
|
+
parentEquivalentSignatureVariables: Record<string, string | string[]>,
|
|
462
772
|
childName: string,
|
|
463
773
|
): string | null {
|
|
464
774
|
// Extract the root variable from the child path
|
|
@@ -485,7 +795,11 @@ function translateChildPathToParent(
|
|
|
485
795
|
|
|
486
796
|
// Look up the child's equivalence for this root variable
|
|
487
797
|
// e.g., childEquiv[selectedScenario] = "signature[0].selectedScenario"
|
|
488
|
-
|
|
798
|
+
// Handle array case (OR expressions) - use first element if array
|
|
799
|
+
const rawChildPropPath = childEquivalentSignatureVariables[rootVar];
|
|
800
|
+
const childPropPath = Array.isArray(rawChildPropPath)
|
|
801
|
+
? rawChildPropPath[0]
|
|
802
|
+
: rawChildPropPath;
|
|
489
803
|
|
|
490
804
|
if (!childPropPath) {
|
|
491
805
|
// No mapping found - this might be internal state, not a prop
|
|
@@ -498,7 +812,11 @@ function translateChildPathToParent(
|
|
|
498
812
|
|
|
499
813
|
// Look up parent's equivalence to find what value was passed to this prop
|
|
500
814
|
// e.g., parentEquiv["ChildName().signature[0].selectedScenario"] = "selectedScenario"
|
|
501
|
-
|
|
815
|
+
// Handle array case (OR expressions) - use first element if array
|
|
816
|
+
const rawParentValue = parentEquivalentSignatureVariables[fullChildPropPath];
|
|
817
|
+
const parentValue = Array.isArray(rawParentValue)
|
|
818
|
+
? rawParentValue[0]
|
|
819
|
+
: rawParentValue;
|
|
502
820
|
|
|
503
821
|
if (!parentValue) {
|
|
504
822
|
// No parent mapping found - log ALL parent keys that contain the childName
|
|
@@ -524,6 +842,8 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
524
842
|
equivalentSignatureVariables,
|
|
525
843
|
fullToShortPathMap,
|
|
526
844
|
childComponentData,
|
|
845
|
+
derivedVariables,
|
|
846
|
+
sourceEquivalencies,
|
|
527
847
|
} = args;
|
|
528
848
|
|
|
529
849
|
const flows: ExecutionFlow[] = [];
|
|
@@ -591,7 +911,8 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
591
911
|
// This handles cases like: const hasAnalysis = analysis !== null
|
|
592
912
|
// where hasAnalysis is not in attributesMap but analysis is
|
|
593
913
|
if (!resolvedPath && usage.derivedFrom) {
|
|
594
|
-
const { sourcePath, sourcePaths } =
|
|
914
|
+
const { operation, sourcePath, sourcePaths, comparedValue } =
|
|
915
|
+
usage.derivedFrom;
|
|
595
916
|
|
|
596
917
|
// For single-source derivations (notNull, equals, etc.)
|
|
597
918
|
if (sourcePath) {
|
|
@@ -607,8 +928,277 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
607
928
|
}
|
|
608
929
|
}
|
|
609
930
|
|
|
610
|
-
// For
|
|
611
|
-
//
|
|
931
|
+
// For equals/notEquals derivations with comparedValue, generate comparison flows
|
|
932
|
+
// e.g., canEdit derived from user.role === 'admin'
|
|
933
|
+
// When canEdit is used in truthiness check, we need:
|
|
934
|
+
// - Truthy flow: user.role = 'admin' (comparison: 'equals')
|
|
935
|
+
// - Falsy flow: user.role != 'admin' (comparison: 'notEquals')
|
|
936
|
+
if (
|
|
937
|
+
(operation === 'equals' || operation === 'notEquals') &&
|
|
938
|
+
comparedValue !== undefined &&
|
|
939
|
+
resolvedPath &&
|
|
940
|
+
usage.conditionType === 'truthiness'
|
|
941
|
+
) {
|
|
942
|
+
const baseName = generateNameFromPath(usage.path);
|
|
943
|
+
const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
|
|
944
|
+
? 'high'
|
|
945
|
+
: 'medium';
|
|
946
|
+
|
|
947
|
+
const isNegated = usage.isNegated ?? false;
|
|
948
|
+
|
|
949
|
+
// For equals derivation:
|
|
950
|
+
// - Truthy check (!negated): needs value = comparedValue (equals)
|
|
951
|
+
// - Falsy check (negated): needs value != comparedValue (notEquals)
|
|
952
|
+
// For notEquals derivation: inverse of above
|
|
953
|
+
const isEqualsDerivation = operation === 'equals';
|
|
954
|
+
const truthyNeedsEquals = isEqualsDerivation !== isNegated;
|
|
955
|
+
|
|
956
|
+
// Generate truthy flow
|
|
957
|
+
const truthyFlow: ExecutionFlow = {
|
|
958
|
+
id: generateFlowId(usage.path, 'truthy'),
|
|
959
|
+
name: `${baseName} True`,
|
|
960
|
+
description: `When ${baseName.toLowerCase()} is truthy (${resolvedPath} ${truthyNeedsEquals ? '=' : '!='} ${comparedValue})`,
|
|
961
|
+
requiredValues: [
|
|
962
|
+
{
|
|
963
|
+
attributePath: stripLengthSuffix(resolvedPath),
|
|
964
|
+
value: comparedValue,
|
|
965
|
+
comparison: truthyNeedsEquals ? 'equals' : 'notEquals',
|
|
966
|
+
valueType: inferValueType(comparedValue),
|
|
967
|
+
},
|
|
968
|
+
],
|
|
969
|
+
impact,
|
|
970
|
+
sourceLocation: usage.sourceLocation
|
|
971
|
+
? {
|
|
972
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
973
|
+
column: usage.sourceLocation.column,
|
|
974
|
+
}
|
|
975
|
+
: undefined,
|
|
976
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
977
|
+
};
|
|
978
|
+
|
|
979
|
+
// Generate falsy flow
|
|
980
|
+
const falsyFlow: ExecutionFlow = {
|
|
981
|
+
id: generateFlowId(usage.path, 'falsy'),
|
|
982
|
+
name: `${baseName} False`,
|
|
983
|
+
description: `When ${baseName.toLowerCase()} is falsy (${resolvedPath} ${truthyNeedsEquals ? '!=' : '='} ${comparedValue})`,
|
|
984
|
+
requiredValues: [
|
|
985
|
+
{
|
|
986
|
+
attributePath: stripLengthSuffix(resolvedPath),
|
|
987
|
+
value: comparedValue,
|
|
988
|
+
comparison: truthyNeedsEquals ? 'notEquals' : 'equals',
|
|
989
|
+
valueType: inferValueType(comparedValue),
|
|
990
|
+
},
|
|
991
|
+
],
|
|
992
|
+
impact,
|
|
993
|
+
sourceLocation: usage.sourceLocation
|
|
994
|
+
? {
|
|
995
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
996
|
+
column: usage.sourceLocation.column,
|
|
997
|
+
}
|
|
998
|
+
: undefined,
|
|
999
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
// Add flows and skip normal flow generation
|
|
1003
|
+
if (!seenFlowIds.has(truthyFlow.id)) {
|
|
1004
|
+
seenFlowIds.add(truthyFlow.id);
|
|
1005
|
+
flows.push(truthyFlow);
|
|
1006
|
+
}
|
|
1007
|
+
if (!seenFlowIds.has(falsyFlow.id)) {
|
|
1008
|
+
seenFlowIds.add(falsyFlow.id);
|
|
1009
|
+
flows.push(falsyFlow);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// For OR derivations with negation, we need ALL sources to be falsy
|
|
1016
|
+
// e.g., !isBusy where isBusy = isRunning || isQueued || isPending
|
|
1017
|
+
// For the falsy flow, ALL sources must be falsy
|
|
1018
|
+
if (
|
|
1019
|
+
operation === 'or' &&
|
|
1020
|
+
usage.conditionType === 'truthiness' &&
|
|
1021
|
+
usage.isNegated === true &&
|
|
1022
|
+
sourcePaths &&
|
|
1023
|
+
sourcePaths.length > 0
|
|
1024
|
+
) {
|
|
1025
|
+
// Use expandDerivedVariableToSources to recursively resolve all sources
|
|
1026
|
+
const allSources = expandDerivedVariableToSources(
|
|
1027
|
+
usage.path,
|
|
1028
|
+
conditionalUsages,
|
|
1029
|
+
attributesMap,
|
|
1030
|
+
equivalentSignatureVariables,
|
|
1031
|
+
fullToShortPathMap,
|
|
1032
|
+
new Set(),
|
|
1033
|
+
derivedVariables,
|
|
1034
|
+
);
|
|
1035
|
+
|
|
1036
|
+
if (allSources.length > 0) {
|
|
1037
|
+
// Generate a compound-style flow with all sources set to falsy
|
|
1038
|
+
const baseName = generateNameFromPath(usage.path);
|
|
1039
|
+
const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
|
|
1040
|
+
? 'high'
|
|
1041
|
+
: 'medium';
|
|
1042
|
+
|
|
1043
|
+
const requiredValues: ExecutionFlow['requiredValues'] =
|
|
1044
|
+
allSources.map((source) => ({
|
|
1045
|
+
attributePath: source.path,
|
|
1046
|
+
value: 'falsy',
|
|
1047
|
+
comparison: 'falsy' as const,
|
|
1048
|
+
valueType: 'boolean' as const,
|
|
1049
|
+
}));
|
|
1050
|
+
|
|
1051
|
+
// Create a single falsy flow with all sources
|
|
1052
|
+
const falsyFlow: ExecutionFlow = {
|
|
1053
|
+
id: generateFlowId(usage.path, 'falsy'),
|
|
1054
|
+
name: `${baseName} False`,
|
|
1055
|
+
description: `When ${baseName.toLowerCase()} is falsy (all sources are falsy)`,
|
|
1056
|
+
requiredValues,
|
|
1057
|
+
impact,
|
|
1058
|
+
sourceLocation: usage.sourceLocation
|
|
1059
|
+
? {
|
|
1060
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
1061
|
+
column: usage.sourceLocation.column,
|
|
1062
|
+
}
|
|
1063
|
+
: undefined,
|
|
1064
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
// Create a truthy flow - for OR, ANY source being truthy is sufficient
|
|
1068
|
+
// We use the first resolvable source for the truthy flow
|
|
1069
|
+
const firstSource = allSources[0];
|
|
1070
|
+
const truthyFlow: ExecutionFlow = {
|
|
1071
|
+
id: generateFlowId(usage.path, 'truthy'),
|
|
1072
|
+
name: `${baseName} True`,
|
|
1073
|
+
description: `When ${baseName.toLowerCase()} is truthy`,
|
|
1074
|
+
requiredValues: [
|
|
1075
|
+
{
|
|
1076
|
+
attributePath: firstSource.path,
|
|
1077
|
+
value: 'truthy',
|
|
1078
|
+
comparison: 'truthy',
|
|
1079
|
+
valueType: 'boolean',
|
|
1080
|
+
},
|
|
1081
|
+
],
|
|
1082
|
+
impact,
|
|
1083
|
+
sourceLocation: usage.sourceLocation
|
|
1084
|
+
? {
|
|
1085
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
1086
|
+
column: usage.sourceLocation.column,
|
|
1087
|
+
}
|
|
1088
|
+
: undefined,
|
|
1089
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
1090
|
+
};
|
|
1091
|
+
|
|
1092
|
+
// Add both flows (falsy needs all sources, truthy needs one)
|
|
1093
|
+
if (!seenFlowIds.has(falsyFlow.id)) {
|
|
1094
|
+
seenFlowIds.add(falsyFlow.id);
|
|
1095
|
+
flows.push(falsyFlow);
|
|
1096
|
+
}
|
|
1097
|
+
if (!seenFlowIds.has(truthyFlow.id)) {
|
|
1098
|
+
seenFlowIds.add(truthyFlow.id);
|
|
1099
|
+
flows.push(truthyFlow);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// Skip the normal flow generation for this usage
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// For AND derivations without negation, we need ALL sources to be truthy
|
|
1108
|
+
// e.g., isReady where isReady = hasData && isLoaded && isValid
|
|
1109
|
+
// For the truthy flow, ALL sources must be truthy
|
|
1110
|
+
// For negated AND (!isReady), ANY source being falsy is sufficient (fallback behavior)
|
|
1111
|
+
if (
|
|
1112
|
+
operation === 'and' &&
|
|
1113
|
+
usage.conditionType === 'truthiness' &&
|
|
1114
|
+
usage.isNegated !== true &&
|
|
1115
|
+
sourcePaths &&
|
|
1116
|
+
sourcePaths.length > 0
|
|
1117
|
+
) {
|
|
1118
|
+
// Use expandDerivedVariableToSources to recursively resolve all sources
|
|
1119
|
+
const allSources = expandDerivedVariableToSources(
|
|
1120
|
+
usage.path,
|
|
1121
|
+
conditionalUsages,
|
|
1122
|
+
attributesMap,
|
|
1123
|
+
equivalentSignatureVariables,
|
|
1124
|
+
fullToShortPathMap,
|
|
1125
|
+
new Set(),
|
|
1126
|
+
derivedVariables,
|
|
1127
|
+
);
|
|
1128
|
+
|
|
1129
|
+
if (allSources.length > 0) {
|
|
1130
|
+
// Generate a compound-style flow with all sources set to truthy
|
|
1131
|
+
const baseName = generateNameFromPath(usage.path);
|
|
1132
|
+
const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
|
|
1133
|
+
? 'high'
|
|
1134
|
+
: 'medium';
|
|
1135
|
+
|
|
1136
|
+
const requiredValues: ExecutionFlow['requiredValues'] =
|
|
1137
|
+
allSources.map((source) => ({
|
|
1138
|
+
attributePath: source.path,
|
|
1139
|
+
value: 'truthy',
|
|
1140
|
+
comparison: 'truthy' as const,
|
|
1141
|
+
valueType: 'boolean' as const,
|
|
1142
|
+
}));
|
|
1143
|
+
|
|
1144
|
+
// Create a truthy flow with all sources
|
|
1145
|
+
const truthyFlow: ExecutionFlow = {
|
|
1146
|
+
id: generateFlowId(usage.path, 'truthy'),
|
|
1147
|
+
name: `${baseName} True`,
|
|
1148
|
+
description: `When ${baseName.toLowerCase()} is truthy (all sources are truthy)`,
|
|
1149
|
+
requiredValues,
|
|
1150
|
+
impact,
|
|
1151
|
+
sourceLocation: usage.sourceLocation
|
|
1152
|
+
? {
|
|
1153
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
1154
|
+
column: usage.sourceLocation.column,
|
|
1155
|
+
}
|
|
1156
|
+
: undefined,
|
|
1157
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
// Create a falsy flow - for AND, ANY source being falsy is sufficient
|
|
1161
|
+
// We use the first resolvable source for the falsy flow
|
|
1162
|
+
const firstSource = allSources[0];
|
|
1163
|
+
const falsyFlow: ExecutionFlow = {
|
|
1164
|
+
id: generateFlowId(usage.path, 'falsy'),
|
|
1165
|
+
name: `${baseName} False`,
|
|
1166
|
+
description: `When ${baseName.toLowerCase()} is falsy`,
|
|
1167
|
+
requiredValues: [
|
|
1168
|
+
{
|
|
1169
|
+
attributePath: firstSource.path,
|
|
1170
|
+
value: 'falsy',
|
|
1171
|
+
comparison: 'falsy',
|
|
1172
|
+
valueType: 'boolean',
|
|
1173
|
+
},
|
|
1174
|
+
],
|
|
1175
|
+
impact,
|
|
1176
|
+
sourceLocation: usage.sourceLocation
|
|
1177
|
+
? {
|
|
1178
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
1179
|
+
column: usage.sourceLocation.column,
|
|
1180
|
+
}
|
|
1181
|
+
: undefined,
|
|
1182
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
1183
|
+
};
|
|
1184
|
+
|
|
1185
|
+
// Add both flows (truthy needs all sources, falsy needs one)
|
|
1186
|
+
if (!seenFlowIds.has(truthyFlow.id)) {
|
|
1187
|
+
seenFlowIds.add(truthyFlow.id);
|
|
1188
|
+
flows.push(truthyFlow);
|
|
1189
|
+
}
|
|
1190
|
+
if (!seenFlowIds.has(falsyFlow.id)) {
|
|
1191
|
+
seenFlowIds.add(falsyFlow.id);
|
|
1192
|
+
flows.push(falsyFlow);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// Skip the normal flow generation for this usage
|
|
1196
|
+
continue;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// For multi-source derivations (or, and) without special handling,
|
|
1201
|
+
// try the first resolvable path as a fallback
|
|
612
1202
|
if (!resolvedPath && sourcePaths && sourcePaths.length > 0) {
|
|
613
1203
|
for (const sp of sourcePaths) {
|
|
614
1204
|
const resolution = resolvePathToControllable(
|
|
@@ -661,32 +1251,203 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
661
1251
|
|
|
662
1252
|
// Process compound conditionals
|
|
663
1253
|
for (const compound of compoundConditionals) {
|
|
664
|
-
//
|
|
665
|
-
|
|
666
|
-
|
|
1254
|
+
// Expand OR groups into separate condition sets
|
|
1255
|
+
// For example, `A && (B || C)` becomes [[A, B], [A, C]]
|
|
1256
|
+
const expandedConditionSets = expandOrGroups(compound.conditions);
|
|
1257
|
+
|
|
1258
|
+
// Process each expanded condition set as a separate potential flow
|
|
1259
|
+
for (const conditionSet of expandedConditionSets) {
|
|
1260
|
+
// First, check if ALL paths in this condition set are controllable (or can be expanded to controllable sources)
|
|
1261
|
+
const resolvedPaths = new Map<string, string>();
|
|
1262
|
+
// Track expanded sources for derived variables (path -> array of expanded sources)
|
|
1263
|
+
const expandedSources = new Map<
|
|
1264
|
+
string,
|
|
1265
|
+
Array<{ path: string; operation?: DerivedVariableOperation }>
|
|
1266
|
+
>();
|
|
1267
|
+
let allControllable = true;
|
|
1268
|
+
|
|
1269
|
+
for (const condition of conditionSet) {
|
|
1270
|
+
// Check if this condition path has derivation info
|
|
1271
|
+
// First check conditionalUsages, then fall back to derivedVariables
|
|
1272
|
+
const usagesForPath = conditionalUsages[condition.path];
|
|
1273
|
+
let derivedFromInfo = usagesForPath?.find(
|
|
1274
|
+
(u) => u.derivedFrom?.operation,
|
|
1275
|
+
)?.derivedFrom;
|
|
1276
|
+
|
|
1277
|
+
// CRITICAL: Also check derivedVariables for intermediate derived variables
|
|
1278
|
+
if (!derivedFromInfo && derivedVariables?.[condition.path]) {
|
|
1279
|
+
derivedFromInfo = derivedVariables[condition.path];
|
|
1280
|
+
}
|
|
667
1281
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
1282
|
+
if (derivedFromInfo) {
|
|
1283
|
+
// This is a derived variable - expand to its sources
|
|
1284
|
+
const sources = expandDerivedVariableToSources(
|
|
1285
|
+
condition.path,
|
|
1286
|
+
conditionalUsages,
|
|
1287
|
+
attributesMap,
|
|
1288
|
+
equivalentSignatureVariables,
|
|
1289
|
+
fullToShortPathMap,
|
|
1290
|
+
new Set(),
|
|
1291
|
+
derivedVariables,
|
|
1292
|
+
);
|
|
1293
|
+
|
|
1294
|
+
if (sources.length > 0) {
|
|
1295
|
+
// Store the expanded sources for this condition
|
|
1296
|
+
expandedSources.set(condition.path, sources);
|
|
1297
|
+
// Use the first source's path for the resolvedPaths map (for ID generation)
|
|
1298
|
+
resolvedPaths.set(condition.path, sources[0].path);
|
|
1299
|
+
} else {
|
|
1300
|
+
// Derived variable but no controllable sources found
|
|
1301
|
+
allControllable = false;
|
|
1302
|
+
break;
|
|
1303
|
+
}
|
|
1304
|
+
} else {
|
|
1305
|
+
// Not a derived variable - resolve directly
|
|
1306
|
+
const resolution = resolvePathToControllable(
|
|
1307
|
+
condition.path,
|
|
1308
|
+
attributesMap,
|
|
1309
|
+
equivalentSignatureVariables,
|
|
1310
|
+
fullToShortPathMap,
|
|
1311
|
+
);
|
|
1312
|
+
|
|
1313
|
+
if (!resolution.isControllable || !resolution.resolvedPath) {
|
|
1314
|
+
allControllable = false;
|
|
1315
|
+
break;
|
|
1316
|
+
}
|
|
675
1317
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
break;
|
|
1318
|
+
resolvedPaths.set(condition.path, resolution.resolvedPath);
|
|
1319
|
+
}
|
|
679
1320
|
}
|
|
680
1321
|
|
|
681
|
-
|
|
682
|
-
|
|
1322
|
+
// Only create a flow if ALL paths are controllable
|
|
1323
|
+
if (allControllable && resolvedPaths.size > 0) {
|
|
1324
|
+
// If any conditions were expanded from derived variables, we need to build a custom flow
|
|
1325
|
+
if (expandedSources.size > 0) {
|
|
1326
|
+
const requiredValues: ExecutionFlow['requiredValues'] = [];
|
|
1327
|
+
|
|
1328
|
+
for (const condition of conditionSet) {
|
|
1329
|
+
const sources = expandedSources.get(condition.path);
|
|
1330
|
+
|
|
1331
|
+
if (sources) {
|
|
1332
|
+
// This condition was expanded - add all its sources
|
|
1333
|
+
// Determine the required value based on condition type and derivation operation
|
|
1334
|
+
const usagesForPath = conditionalUsages[condition.path];
|
|
1335
|
+
let expandedDerivedFrom = usagesForPath?.find(
|
|
1336
|
+
(u) => u.derivedFrom?.operation,
|
|
1337
|
+
)?.derivedFrom;
|
|
1338
|
+
|
|
1339
|
+
// Also check derivedVariables for intermediate derived variables
|
|
1340
|
+
if (!expandedDerivedFrom && derivedVariables?.[condition.path]) {
|
|
1341
|
+
expandedDerivedFrom = derivedVariables[condition.path];
|
|
1342
|
+
}
|
|
1343
|
+
const operation = expandedDerivedFrom?.operation;
|
|
1344
|
+
|
|
1345
|
+
for (const source of sources) {
|
|
1346
|
+
// For OR-derived truthy: ANY source truthy
|
|
1347
|
+
// For AND-derived truthy: ALL sources truthy
|
|
1348
|
+
// For negated: inverse
|
|
1349
|
+
let value: string;
|
|
1350
|
+
let comparison: ExecutionFlow['requiredValues'][0]['comparison'];
|
|
1351
|
+
|
|
1352
|
+
if (condition.conditionType === 'truthiness') {
|
|
1353
|
+
const isNegated = condition.isNegated === true;
|
|
1354
|
+
// For OR: truthy needs ANY source truthy, falsy needs ALL sources falsy
|
|
1355
|
+
// For AND: truthy needs ALL sources truthy, falsy needs ANY source falsy
|
|
1356
|
+
// In compound conditionals, we generate the truthy path by default
|
|
1357
|
+
// (the compound expression must be truthy)
|
|
1358
|
+
if (operation === 'or') {
|
|
1359
|
+
// For OR-derived, truthy means we need at least one source truthy
|
|
1360
|
+
// We'll use the first source as truthy (simplification)
|
|
1361
|
+
value = isNegated ? 'falsy' : 'truthy';
|
|
1362
|
+
} else if (operation === 'and') {
|
|
1363
|
+
// For AND-derived, truthy means ALL sources truthy
|
|
1364
|
+
value = isNegated ? 'falsy' : 'truthy';
|
|
1365
|
+
} else {
|
|
1366
|
+
value = isNegated ? 'falsy' : 'truthy';
|
|
1367
|
+
}
|
|
1368
|
+
comparison = isNegated ? 'falsy' : 'truthy';
|
|
1369
|
+
} else {
|
|
1370
|
+
value = 'truthy';
|
|
1371
|
+
comparison = 'truthy';
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
requiredValues.push({
|
|
1375
|
+
attributePath: source.path,
|
|
1376
|
+
value,
|
|
1377
|
+
comparison,
|
|
1378
|
+
valueType: 'boolean' as const,
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
} else {
|
|
1382
|
+
// This condition was resolved directly
|
|
1383
|
+
const resolvedPath = resolvedPaths.get(condition.path);
|
|
1384
|
+
if (resolvedPath) {
|
|
1385
|
+
let value: string;
|
|
1386
|
+
let comparison: ExecutionFlow['requiredValues'][0]['comparison'];
|
|
1387
|
+
|
|
1388
|
+
if (condition.conditionType === 'truthiness') {
|
|
1389
|
+
value = condition.isNegated ? 'falsy' : 'truthy';
|
|
1390
|
+
comparison = condition.isNegated ? 'falsy' : 'truthy';
|
|
1391
|
+
} else {
|
|
1392
|
+
value =
|
|
1393
|
+
condition.requiredValue?.toString() ??
|
|
1394
|
+
condition.comparedValues?.[0] ??
|
|
1395
|
+
'truthy';
|
|
1396
|
+
comparison = 'equals';
|
|
1397
|
+
}
|
|
683
1398
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
1399
|
+
requiredValues.push({
|
|
1400
|
+
attributePath: stripLengthSuffix(resolvedPath),
|
|
1401
|
+
value,
|
|
1402
|
+
comparison,
|
|
1403
|
+
valueType: inferValueType(value),
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
if (requiredValues.length > 0) {
|
|
1410
|
+
const impact: ExecutionFlow['impact'] =
|
|
1411
|
+
compound.controlsJsxRendering ? 'high' : 'medium';
|
|
1412
|
+
|
|
1413
|
+
// Generate a combined ID from all paths
|
|
1414
|
+
const pathParts = requiredValues
|
|
1415
|
+
.map((rv) => {
|
|
1416
|
+
const name = generateNameFromPath(rv.attributePath);
|
|
1417
|
+
return name.toLowerCase().replace(/\s+/g, '-');
|
|
1418
|
+
})
|
|
1419
|
+
.join('-and-');
|
|
1420
|
+
|
|
1421
|
+
const compoundFlow: ExecutionFlow = {
|
|
1422
|
+
id: `${pathParts}-${requiredValues.map((rv) => rv.value).join('-')}`,
|
|
1423
|
+
name: generateNameFromPath(requiredValues[0].attributePath),
|
|
1424
|
+
description: `When ${requiredValues.map((rv) => `${generateNameFromPath(rv.attributePath).toLowerCase()} is ${rv.value}`).join(' and ')}`,
|
|
1425
|
+
impact,
|
|
1426
|
+
requiredValues,
|
|
1427
|
+
sourceLocation: compound.sourceLocation,
|
|
1428
|
+
};
|
|
1429
|
+
|
|
1430
|
+
if (!seenFlowIds.has(compoundFlow.id)) {
|
|
1431
|
+
seenFlowIds.add(compoundFlow.id);
|
|
1432
|
+
flows.push(compoundFlow);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
} else {
|
|
1436
|
+
// No derived variables - use the original generateFlowFromCompound
|
|
1437
|
+
// Create a modified compound with just this condition set
|
|
1438
|
+
const modifiedCompound = {
|
|
1439
|
+
...compound,
|
|
1440
|
+
conditions: conditionSet,
|
|
1441
|
+
};
|
|
1442
|
+
const compoundFlow = generateFlowFromCompound(
|
|
1443
|
+
modifiedCompound,
|
|
1444
|
+
resolvedPaths,
|
|
1445
|
+
);
|
|
1446
|
+
if (compoundFlow && !seenFlowIds.has(compoundFlow.id)) {
|
|
1447
|
+
seenFlowIds.add(compoundFlow.id);
|
|
1448
|
+
flows.push(compoundFlow);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
690
1451
|
}
|
|
691
1452
|
}
|
|
692
1453
|
}
|
|
@@ -733,7 +1494,18 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
733
1494
|
fullToShortPathMap,
|
|
734
1495
|
);
|
|
735
1496
|
|
|
1497
|
+
// Only use controllable paths for gating conditions (whitelist approach).
|
|
1498
|
+
// Do NOT fall back to equivalentSignatureVariables because those may contain
|
|
1499
|
+
// uncontrollable paths like useState that cannot be mocked.
|
|
1500
|
+
let resolvedVarPath: string | null = null;
|
|
736
1501
|
if (varResolution.isControllable && varResolution.resolvedPath) {
|
|
1502
|
+
resolvedVarPath = varResolution.resolvedPath;
|
|
1503
|
+
}
|
|
1504
|
+
// Note: We intentionally do NOT fall back to equivalentSignatureVariables here
|
|
1505
|
+
// because that would allow uncontrollable paths (like useState) to be added
|
|
1506
|
+
// as gating conditions.
|
|
1507
|
+
|
|
1508
|
+
if (resolvedVarPath) {
|
|
737
1509
|
const isNegated = (gatingCondition as any).isNegated === true;
|
|
738
1510
|
const isNotEquals = operator === '!=' || operator === '!==';
|
|
739
1511
|
// Determine the effective value for this gating condition
|
|
@@ -744,7 +1516,7 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
744
1516
|
const needsExactValue = isNegated !== isNotEquals;
|
|
745
1517
|
|
|
746
1518
|
gatingRequiredValues.push({
|
|
747
|
-
attributePath:
|
|
1519
|
+
attributePath: resolvedVarPath,
|
|
748
1520
|
value: needsExactValue ? 'falsy' : comparedValue,
|
|
749
1521
|
comparison: needsExactValue ? 'falsy' : 'equals',
|
|
750
1522
|
});
|
|
@@ -810,7 +1582,9 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
810
1582
|
// For non-negated &&: all parts must be truthy
|
|
811
1583
|
// For negated ||: all parts must be falsy (DeMorgan: !(A || B) = !A && !B)
|
|
812
1584
|
gatingRequiredValues.push({
|
|
813
|
-
attributePath:
|
|
1585
|
+
attributePath: stripLengthSuffix(
|
|
1586
|
+
partResolution.resolvedPath,
|
|
1587
|
+
),
|
|
814
1588
|
value: isNegated ? 'falsy' : 'truthy',
|
|
815
1589
|
comparison: isNegated ? 'falsy' : 'truthy',
|
|
816
1590
|
});
|
|
@@ -827,16 +1601,27 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
827
1601
|
fullToShortPathMap,
|
|
828
1602
|
);
|
|
829
1603
|
|
|
1604
|
+
// Only use controllable paths for gating conditions (whitelist approach).
|
|
1605
|
+
// Do NOT fall back to equivalentSignatureVariables because those may contain
|
|
1606
|
+
// uncontrollable paths like useState that cannot be mocked.
|
|
1607
|
+
let resolvedGatingPath: string | null = null;
|
|
830
1608
|
if (
|
|
831
1609
|
gatingResolution.isControllable &&
|
|
832
1610
|
gatingResolution.resolvedPath
|
|
833
1611
|
) {
|
|
1612
|
+
resolvedGatingPath = gatingResolution.resolvedPath;
|
|
1613
|
+
}
|
|
1614
|
+
// Note: We intentionally do NOT fall back to equivalentSignatureVariables here
|
|
1615
|
+
// because that would allow uncontrollable paths (like useState) to be added
|
|
1616
|
+
// as gating conditions.
|
|
1617
|
+
|
|
1618
|
+
if (resolvedGatingPath) {
|
|
834
1619
|
// For truthiness conditions on gating, check if the condition is negated
|
|
835
1620
|
// e.g., ternary else branch: isError ? <ErrorView /> : <SuccessView />
|
|
836
1621
|
// SuccessView has isNegated: true, meaning it renders when isError is falsy
|
|
837
1622
|
const isNegated = (gatingCondition as any).isNegated === true;
|
|
838
1623
|
gatingRequiredValues.push({
|
|
839
|
-
attributePath:
|
|
1624
|
+
attributePath: resolvedGatingPath,
|
|
840
1625
|
value: isNegated ? 'falsy' : 'truthy',
|
|
841
1626
|
comparison: isNegated ? 'falsy' : 'truthy',
|
|
842
1627
|
});
|
|
@@ -855,6 +1640,9 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
855
1640
|
childData.conditionalUsages,
|
|
856
1641
|
)) {
|
|
857
1642
|
for (const usage of usages) {
|
|
1643
|
+
// Debug logging (disabled - set to true for troubleshooting child flow resolution)
|
|
1644
|
+
const shouldDebugChild = false;
|
|
1645
|
+
|
|
858
1646
|
// Skip usages that are part of compound conditionals (handled separately)
|
|
859
1647
|
// Fix 33: Only skip if the chainId is in the child's compound conditionals
|
|
860
1648
|
if (usage.chainId && childCompoundChainIds.has(usage.chainId)) {
|
|
@@ -869,6 +1657,18 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
869
1657
|
childPath = usage.derivedFrom.sourcePath;
|
|
870
1658
|
}
|
|
871
1659
|
|
|
1660
|
+
if (shouldDebugChild) {
|
|
1661
|
+
console.log(
|
|
1662
|
+
`[DEBUG CHILD ${childName}] Processing usage path: ${usage.path}`,
|
|
1663
|
+
);
|
|
1664
|
+
console.log(
|
|
1665
|
+
`[DEBUG CHILD ${childName}] childPath (after derivedFrom): ${childPath}`,
|
|
1666
|
+
);
|
|
1667
|
+
console.log(
|
|
1668
|
+
`[DEBUG CHILD ${childName}] sourceDataPath: ${usage.sourceDataPath}`,
|
|
1669
|
+
);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
872
1672
|
// Translate the child path to a parent path
|
|
873
1673
|
let translatedPath = translateChildPathToParent(
|
|
874
1674
|
childPath,
|
|
@@ -877,6 +1677,12 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
877
1677
|
childName,
|
|
878
1678
|
);
|
|
879
1679
|
|
|
1680
|
+
if (shouldDebugChild) {
|
|
1681
|
+
console.log(
|
|
1682
|
+
`[DEBUG CHILD ${childName}] translatedPath (from translateChildPathToParent): ${translatedPath}`,
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1685
|
+
|
|
880
1686
|
// If translation failed but we have sourceDataPath, try to extract the prop path from it
|
|
881
1687
|
// sourceDataPath format: "ChildName.signature[n].propPath.rest" → extract "propPath.rest"
|
|
882
1688
|
if (!translatedPath && usage.sourceDataPath) {
|
|
@@ -885,15 +1691,26 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
885
1691
|
);
|
|
886
1692
|
if (signatureMatch) {
|
|
887
1693
|
translatedPath = signatureMatch[1];
|
|
1694
|
+
if (shouldDebugChild) {
|
|
1695
|
+
console.log(
|
|
1696
|
+
`[DEBUG CHILD ${childName}] translatedPath (from sourceDataPath regex): ${translatedPath}`,
|
|
1697
|
+
);
|
|
1698
|
+
}
|
|
888
1699
|
}
|
|
889
1700
|
}
|
|
890
1701
|
|
|
891
1702
|
if (!translatedPath) {
|
|
892
1703
|
// Could not translate - skip this usage
|
|
1704
|
+
if (shouldDebugChild) {
|
|
1705
|
+
console.log(
|
|
1706
|
+
`[DEBUG CHILD ${childName}] SKIPPED - no translation found`,
|
|
1707
|
+
);
|
|
1708
|
+
}
|
|
893
1709
|
continue;
|
|
894
1710
|
}
|
|
895
1711
|
|
|
896
1712
|
// Now resolve the translated path in the parent context
|
|
1713
|
+
// First, try standard resolution
|
|
897
1714
|
const resolution = resolvePathToControllable(
|
|
898
1715
|
translatedPath,
|
|
899
1716
|
attributesMap,
|
|
@@ -901,12 +1718,176 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
901
1718
|
fullToShortPathMap,
|
|
902
1719
|
);
|
|
903
1720
|
|
|
904
|
-
if (
|
|
905
|
-
|
|
906
|
-
|
|
1721
|
+
if (shouldDebugChild) {
|
|
1722
|
+
console.log(
|
|
1723
|
+
`[DEBUG CHILD ${childName}] resolvePathToControllable result:`,
|
|
1724
|
+
);
|
|
1725
|
+
console.log(
|
|
1726
|
+
`[DEBUG CHILD ${childName}] isControllable: ${resolution.isControllable}`,
|
|
1727
|
+
);
|
|
1728
|
+
console.log(
|
|
1729
|
+
`[DEBUG CHILD ${childName}] resolvedPath: ${resolution.resolvedPath}`,
|
|
1730
|
+
);
|
|
1731
|
+
console.log(
|
|
1732
|
+
`[DEBUG CHILD ${childName}] chain: ${resolution.resolutionChain.join(' -> ')}`,
|
|
1733
|
+
);
|
|
907
1734
|
}
|
|
908
1735
|
|
|
909
|
-
|
|
1736
|
+
// Only create flows for controllable paths (whitelist approach).
|
|
1737
|
+
// If the path doesn't resolve to something in attributesMap, skip it.
|
|
1738
|
+
// This prevents creating flows for useState values which are not
|
|
1739
|
+
// controllable via mock data injection.
|
|
1740
|
+
let resolvedPath = resolution.resolvedPath;
|
|
1741
|
+
|
|
1742
|
+
if (!resolution.isControllable || !resolvedPath) {
|
|
1743
|
+
// Path is not controllable via standard resolution.
|
|
1744
|
+
// Try fallback: For useState values (cyScope*().functionCallReturnValue),
|
|
1745
|
+
// look for a related URL parameter like "varNameFromUrl" that might
|
|
1746
|
+
// control the initial state.
|
|
1747
|
+
//
|
|
1748
|
+
// Example: viewMode → cyScope20().functionCallReturnValue (useState value)
|
|
1749
|
+
// Fallback: viewModeFromUrl → segments[2] (URL param that initializes the useState)
|
|
1750
|
+
const useStateMatch = translatedPath.match(
|
|
1751
|
+
/^cyScope\d+\(\)\.functionCallReturnValue$/,
|
|
1752
|
+
);
|
|
1753
|
+
|
|
1754
|
+
if (useStateMatch) {
|
|
1755
|
+
// Find what variable this useState value corresponds to by looking
|
|
1756
|
+
// for entries like "varName": "cyScope20()" in equivalentSignatureVariables
|
|
1757
|
+
const useStatePattern = translatedPath.replace(
|
|
1758
|
+
/\.functionCallReturnValue$/,
|
|
1759
|
+
'',
|
|
1760
|
+
); // e.g., "cyScope20()"
|
|
1761
|
+
|
|
1762
|
+
// Find the variable name that maps to this useState
|
|
1763
|
+
let useStateVarName: string | null = null;
|
|
1764
|
+
for (const [varName, varPath] of Object.entries(
|
|
1765
|
+
equivalentSignatureVariables,
|
|
1766
|
+
)) {
|
|
1767
|
+
if (varPath === useStatePattern) {
|
|
1768
|
+
useStateVarName = varName;
|
|
1769
|
+
break;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
if (shouldDebugChild) {
|
|
1774
|
+
console.log(
|
|
1775
|
+
`[DEBUG CHILD ${childName}] useState fallback: looking for URL param`,
|
|
1776
|
+
);
|
|
1777
|
+
console.log(
|
|
1778
|
+
`[DEBUG CHILD ${childName}] useStatePattern: ${useStatePattern}`,
|
|
1779
|
+
);
|
|
1780
|
+
console.log(
|
|
1781
|
+
`[DEBUG CHILD ${childName}] useStateVarName: ${useStateVarName}`,
|
|
1782
|
+
);
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
if (useStateVarName) {
|
|
1786
|
+
// Look for a related URL param like "varNameFromUrl"
|
|
1787
|
+
const urlParamName = `${useStateVarName}FromUrl`;
|
|
1788
|
+
const urlParamPath = equivalentSignatureVariables[urlParamName];
|
|
1789
|
+
|
|
1790
|
+
if (shouldDebugChild) {
|
|
1791
|
+
console.log(
|
|
1792
|
+
`[DEBUG CHILD ${childName}] urlParamName: ${urlParamName}`,
|
|
1793
|
+
);
|
|
1794
|
+
console.log(
|
|
1795
|
+
`[DEBUG CHILD ${childName}] urlParamPath: ${urlParamPath}`,
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
if (urlParamPath) {
|
|
1800
|
+
// For useState values initialized from URL params, use the
|
|
1801
|
+
// URL param variable name directly (e.g., "viewModeFromUrl")
|
|
1802
|
+
// rather than fully resolving it. This keeps the path meaningful
|
|
1803
|
+
// for scenario generation and avoids overly generic paths like
|
|
1804
|
+
// "useParams().functionCallReturnValue.*".
|
|
1805
|
+
//
|
|
1806
|
+
// The flow will use the URL param name as the attributePath,
|
|
1807
|
+
// which gets properly resolved when generating mock data.
|
|
1808
|
+
resolvedPath = urlParamName;
|
|
1809
|
+
if (shouldDebugChild) {
|
|
1810
|
+
console.log(
|
|
1811
|
+
`[DEBUG CHILD ${childName}] useState fallback SUCCESS: using URL param name ${resolvedPath}`,
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
// Fallback 2: Try sourceEquivalencies to find the actual data source
|
|
1819
|
+
// This handles the case where props flow through useState but originate
|
|
1820
|
+
// from a mockable data source (e.g., API call, fetcher).
|
|
1821
|
+
//
|
|
1822
|
+
// Example: WorkoutsView receives `workouts` prop which in parent is stored
|
|
1823
|
+
// in useState, but ultimately comes from a Supabase query.
|
|
1824
|
+
// sourceEquivalencies tells us: "WorkoutsView().signature[0].workouts" → "createClient()...data"
|
|
1825
|
+
if (!resolvedPath && sourceEquivalencies) {
|
|
1826
|
+
// Build the child prop path to look up in sourceEquivalencies
|
|
1827
|
+
// Format: "ChildName().signature[0].propName"
|
|
1828
|
+
// First, find what prop this child path maps to
|
|
1829
|
+
let childPropName: string | null = null;
|
|
1830
|
+
for (const [varName, varPath] of Object.entries(
|
|
1831
|
+
childData.equivalentSignatureVariables,
|
|
1832
|
+
)) {
|
|
1833
|
+
// Check if childPath starts with this variable name
|
|
1834
|
+
// e.g., childPath = "workouts.length", varName = "workouts", varPath = "signature[0].workouts"
|
|
1835
|
+
if (
|
|
1836
|
+
childPath === varName ||
|
|
1837
|
+
childPath.startsWith(`${varName}.`)
|
|
1838
|
+
) {
|
|
1839
|
+
childPropName = varName;
|
|
1840
|
+
break;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
if (childPropName) {
|
|
1845
|
+
// Build the full sourceEquivalencies key
|
|
1846
|
+
const sourceEquivKey = `${childName}().signature[0].${childPropName}`;
|
|
1847
|
+
|
|
1848
|
+
const sourceEquivEntry = sourceEquivalencies[sourceEquivKey];
|
|
1849
|
+
if (sourceEquivEntry && sourceEquivEntry.length > 0) {
|
|
1850
|
+
const dataSourcePath = sourceEquivEntry[0].schemaPath;
|
|
1851
|
+
|
|
1852
|
+
// Check if this data source path is controllable
|
|
1853
|
+
const dataSourceResolution = resolvePathToControllable(
|
|
1854
|
+
dataSourcePath,
|
|
1855
|
+
attributesMap,
|
|
1856
|
+
equivalentSignatureVariables,
|
|
1857
|
+
fullToShortPathMap,
|
|
1858
|
+
);
|
|
1859
|
+
|
|
1860
|
+
if (
|
|
1861
|
+
dataSourceResolution.isControllable &&
|
|
1862
|
+
dataSourceResolution.resolvedPath
|
|
1863
|
+
) {
|
|
1864
|
+
// Preserve any suffix from the child path
|
|
1865
|
+
// e.g., childPath = "workouts.length" → suffix = ".length"
|
|
1866
|
+
const suffix = childPath.startsWith(`${childPropName}.`)
|
|
1867
|
+
? childPath.slice(childPropName.length)
|
|
1868
|
+
: '';
|
|
1869
|
+
resolvedPath = dataSourceResolution.resolvedPath + suffix;
|
|
1870
|
+
|
|
1871
|
+
if (shouldDebugChild) {
|
|
1872
|
+
console.log(
|
|
1873
|
+
`[DEBUG CHILD ${childName}] sourceEquivalencies fallback SUCCESS: using data source ${resolvedPath}`,
|
|
1874
|
+
);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
// If still not resolved after fallback, skip
|
|
1882
|
+
if (!resolvedPath) {
|
|
1883
|
+
if (shouldDebugChild) {
|
|
1884
|
+
console.log(
|
|
1885
|
+
`[DEBUG CHILD ${childName}] SKIPPED - path not controllable`,
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1888
|
+
continue;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
910
1891
|
|
|
911
1892
|
// Check for duplicates
|
|
912
1893
|
const normalizedPath = normalizePathForDeduplication(
|
|
@@ -915,6 +1896,11 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
915
1896
|
);
|
|
916
1897
|
|
|
917
1898
|
if (seenNormalizedPaths.has(normalizedPath)) {
|
|
1899
|
+
if (shouldDebugChild) {
|
|
1900
|
+
console.log(
|
|
1901
|
+
`[DEBUG CHILD ${childName}] SKIPPED - duplicate normalized path: ${normalizedPath}`,
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
918
1904
|
continue;
|
|
919
1905
|
}
|
|
920
1906
|
seenNormalizedPaths.add(normalizedPath);
|
|
@@ -931,6 +1917,17 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
931
1917
|
resolvedPath,
|
|
932
1918
|
);
|
|
933
1919
|
|
|
1920
|
+
if (shouldDebugChild) {
|
|
1921
|
+
console.log(
|
|
1922
|
+
`[DEBUG CHILD ${childName}] GENERATING ${usageFlows.length} flows with resolvedPath: ${resolvedPath}`,
|
|
1923
|
+
);
|
|
1924
|
+
for (const f of usageFlows) {
|
|
1925
|
+
console.log(
|
|
1926
|
+
`[DEBUG CHILD ${childName}] - Flow ID: ${f.id}, path: ${f.requiredValues[0]?.attributePath}`,
|
|
1927
|
+
);
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
|
|
934
1931
|
// Add gating conditions to each flow
|
|
935
1932
|
for (const flow of usageFlows) {
|
|
936
1933
|
// Add gating required values to the flow
|
|
@@ -967,7 +1964,7 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
967
1964
|
// Process child's compound conditionals
|
|
968
1965
|
for (const compound of childData.compoundConditionals) {
|
|
969
1966
|
const resolvedPaths = new Map<string, string>();
|
|
970
|
-
let
|
|
1967
|
+
let allResolvable = true;
|
|
971
1968
|
|
|
972
1969
|
for (const condition of compound.conditions) {
|
|
973
1970
|
// Determine the child path to translate
|
|
@@ -982,7 +1979,7 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
982
1979
|
);
|
|
983
1980
|
|
|
984
1981
|
if (!translatedPath) {
|
|
985
|
-
|
|
1982
|
+
allResolvable = false;
|
|
986
1983
|
break;
|
|
987
1984
|
}
|
|
988
1985
|
|
|
@@ -993,22 +1990,273 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
993
1990
|
fullToShortPathMap,
|
|
994
1991
|
);
|
|
995
1992
|
|
|
996
|
-
|
|
997
|
-
|
|
1993
|
+
// Only create flows for controllable paths (whitelist approach).
|
|
1994
|
+
// If the path doesn't resolve to something in attributesMap, skip it.
|
|
1995
|
+
// This prevents creating flows for useState values which are not
|
|
1996
|
+
// controllable via mock data injection.
|
|
1997
|
+
let resolvedPath = resolution.resolvedPath;
|
|
1998
|
+
|
|
1999
|
+
if (!resolution.isControllable || !resolvedPath) {
|
|
2000
|
+
// Path is not controllable via standard resolution.
|
|
2001
|
+
// Try fallback: For useState values (cyScope*().functionCallReturnValue),
|
|
2002
|
+
// look for a related URL parameter like "varNameFromUrl" that might
|
|
2003
|
+
// control the initial state.
|
|
2004
|
+
const useStateMatch = translatedPath.match(
|
|
2005
|
+
/^cyScope\d+\(\)\.functionCallReturnValue$/,
|
|
2006
|
+
);
|
|
2007
|
+
|
|
2008
|
+
if (useStateMatch) {
|
|
2009
|
+
const useStatePattern = translatedPath.replace(
|
|
2010
|
+
/\.functionCallReturnValue$/,
|
|
2011
|
+
'',
|
|
2012
|
+
);
|
|
2013
|
+
|
|
2014
|
+
// Find the variable name that maps to this useState
|
|
2015
|
+
let useStateVarName: string | null = null;
|
|
2016
|
+
for (const [varName, varPath] of Object.entries(
|
|
2017
|
+
equivalentSignatureVariables,
|
|
2018
|
+
)) {
|
|
2019
|
+
if (varPath === useStatePattern) {
|
|
2020
|
+
useStateVarName = varName;
|
|
2021
|
+
break;
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
if (useStateVarName) {
|
|
2026
|
+
const urlParamName = `${useStateVarName}FromUrl`;
|
|
2027
|
+
const urlParamPath = equivalentSignatureVariables[urlParamName];
|
|
2028
|
+
|
|
2029
|
+
if (urlParamPath) {
|
|
2030
|
+
resolvedPath = urlParamName;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
if (!resolvedPath) {
|
|
2037
|
+
allResolvable = false;
|
|
998
2038
|
break;
|
|
999
2039
|
}
|
|
1000
2040
|
|
|
1001
|
-
resolvedPaths.set(condition.path,
|
|
2041
|
+
resolvedPaths.set(condition.path, resolvedPath);
|
|
1002
2042
|
}
|
|
1003
2043
|
|
|
1004
|
-
if (
|
|
2044
|
+
if (allResolvable && resolvedPaths.size > 0) {
|
|
1005
2045
|
const compoundFlow = generateFlowFromCompound(
|
|
1006
2046
|
compound,
|
|
1007
2047
|
resolvedPaths,
|
|
1008
2048
|
);
|
|
1009
|
-
if (compoundFlow
|
|
1010
|
-
|
|
1011
|
-
|
|
2049
|
+
if (compoundFlow) {
|
|
2050
|
+
// Add gating conditions to compound flow (same as regular usage flows)
|
|
2051
|
+
if (gatingRequiredValues.length > 0) {
|
|
2052
|
+
// Filter out any gating values that are already in the flow
|
|
2053
|
+
const existingPaths = new Set(
|
|
2054
|
+
compoundFlow.requiredValues.map((rv) => rv.attributePath),
|
|
2055
|
+
);
|
|
2056
|
+
const newGatingValues = gatingRequiredValues.filter(
|
|
2057
|
+
(gv) => !existingPaths.has(gv.attributePath),
|
|
2058
|
+
);
|
|
2059
|
+
compoundFlow.requiredValues = [
|
|
2060
|
+
...compoundFlow.requiredValues,
|
|
2061
|
+
...newGatingValues,
|
|
2062
|
+
];
|
|
2063
|
+
|
|
2064
|
+
// Update the flow ID to include gating conditions
|
|
2065
|
+
if (newGatingValues.length > 0) {
|
|
2066
|
+
const gatingIdPart = newGatingValues
|
|
2067
|
+
.map((gv) => `${gv.attributePath}-${gv.value}`)
|
|
2068
|
+
.join('-');
|
|
2069
|
+
compoundFlow.id = `${compoundFlow.id}-gated-${gatingIdPart}`;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
if (!seenFlowIds.has(compoundFlow.id)) {
|
|
2074
|
+
seenFlowIds.add(compoundFlow.id);
|
|
2075
|
+
flows.push(compoundFlow);
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// Process child's jsxRenderingUsages (array.map flows)
|
|
2082
|
+
// This generates array variation flows (empty, few, many) for arrays rendered in child
|
|
2083
|
+
if (childData.jsxRenderingUsages) {
|
|
2084
|
+
for (const jsxUsage of childData.jsxRenderingUsages) {
|
|
2085
|
+
// Translate the child path to a parent path
|
|
2086
|
+
const translatedPath = translateChildPathToParent(
|
|
2087
|
+
jsxUsage.path,
|
|
2088
|
+
childData.equivalentSignatureVariables,
|
|
2089
|
+
equivalentSignatureVariables,
|
|
2090
|
+
childName,
|
|
2091
|
+
);
|
|
2092
|
+
|
|
2093
|
+
if (!translatedPath) {
|
|
2094
|
+
continue;
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
// Resolve to controllable path in parent context
|
|
2098
|
+
const resolution = resolvePathToControllable(
|
|
2099
|
+
translatedPath,
|
|
2100
|
+
attributesMap,
|
|
2101
|
+
equivalentSignatureVariables,
|
|
2102
|
+
fullToShortPathMap,
|
|
2103
|
+
);
|
|
2104
|
+
|
|
2105
|
+
let resolvedPath = resolution.resolvedPath;
|
|
2106
|
+
|
|
2107
|
+
// Try sourceEquivalencies fallback if not controllable
|
|
2108
|
+
if (!resolution.isControllable || !resolvedPath) {
|
|
2109
|
+
if (sourceEquivalencies) {
|
|
2110
|
+
// Build the sourceEquivalencies key
|
|
2111
|
+
// The child path (e.g., "workouts") maps to a prop path (e.g., "signature[0].workouts")
|
|
2112
|
+
let childPropName: string | null = null;
|
|
2113
|
+
for (const [varName, varPath] of Object.entries(
|
|
2114
|
+
childData.equivalentSignatureVariables,
|
|
2115
|
+
)) {
|
|
2116
|
+
if (
|
|
2117
|
+
jsxUsage.path === varName ||
|
|
2118
|
+
jsxUsage.path.startsWith(`${varName}.`)
|
|
2119
|
+
) {
|
|
2120
|
+
childPropName = varName;
|
|
2121
|
+
break;
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
if (childPropName) {
|
|
2126
|
+
const sourceEquivKey = `${childName}().signature[0].${childPropName}`;
|
|
2127
|
+
const sourceEquivEntry = sourceEquivalencies[sourceEquivKey];
|
|
2128
|
+
|
|
2129
|
+
if (sourceEquivEntry && sourceEquivEntry.length > 0) {
|
|
2130
|
+
const dataSourcePath = sourceEquivEntry[0].schemaPath;
|
|
2131
|
+
|
|
2132
|
+
const dataSourceResolution = resolvePathToControllable(
|
|
2133
|
+
dataSourcePath,
|
|
2134
|
+
attributesMap,
|
|
2135
|
+
equivalentSignatureVariables,
|
|
2136
|
+
fullToShortPathMap,
|
|
2137
|
+
);
|
|
2138
|
+
|
|
2139
|
+
if (
|
|
2140
|
+
dataSourceResolution.isControllable &&
|
|
2141
|
+
dataSourceResolution.resolvedPath
|
|
2142
|
+
) {
|
|
2143
|
+
resolvedPath = dataSourceResolution.resolvedPath;
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
if (!resolvedPath) {
|
|
2151
|
+
continue;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
// Check for duplicates
|
|
2155
|
+
const normalizedPath = normalizePathForDeduplication(
|
|
2156
|
+
resolvedPath,
|
|
2157
|
+
fullToShortPathMap,
|
|
2158
|
+
);
|
|
2159
|
+
const dedupeKey = `${normalizedPath}:${jsxUsage.renderingType}`;
|
|
2160
|
+
if (seenNormalizedPaths.has(dedupeKey)) {
|
|
2161
|
+
continue;
|
|
2162
|
+
}
|
|
2163
|
+
seenNormalizedPaths.add(dedupeKey);
|
|
2164
|
+
|
|
2165
|
+
// Generate array variation flows for array-map rendering
|
|
2166
|
+
if (jsxUsage.renderingType === 'array-map') {
|
|
2167
|
+
const baseName = generateNameFromPath(resolvedPath);
|
|
2168
|
+
const pathSlug = pathToSlug(resolvedPath);
|
|
2169
|
+
const exclusiveGroup = `array-length-${pathSlug}`;
|
|
2170
|
+
|
|
2171
|
+
// Empty array flow
|
|
2172
|
+
const emptyFlow: ExecutionFlow = {
|
|
2173
|
+
id: `${pathSlug}-empty-array`,
|
|
2174
|
+
name: `${baseName} Empty`,
|
|
2175
|
+
description: `When ${baseName.toLowerCase()} array is empty`,
|
|
2176
|
+
requiredValues: [
|
|
2177
|
+
{
|
|
2178
|
+
attributePath: resolvedPath,
|
|
2179
|
+
value: '0',
|
|
2180
|
+
comparison: 'length<',
|
|
2181
|
+
valueType: 'array',
|
|
2182
|
+
},
|
|
2183
|
+
...gatingRequiredValues,
|
|
2184
|
+
],
|
|
2185
|
+
impact: 'medium',
|
|
2186
|
+
exclusiveGroup,
|
|
2187
|
+
sourceLocation: jsxUsage.sourceLocation
|
|
2188
|
+
? {
|
|
2189
|
+
lineNumber: jsxUsage.sourceLocation.lineNumber,
|
|
2190
|
+
column: jsxUsage.sourceLocation.column,
|
|
2191
|
+
}
|
|
2192
|
+
: undefined,
|
|
2193
|
+
codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
|
|
2194
|
+
};
|
|
2195
|
+
|
|
2196
|
+
if (!seenFlowIds.has(emptyFlow.id)) {
|
|
2197
|
+
seenFlowIds.add(emptyFlow.id);
|
|
2198
|
+
flows.push(emptyFlow);
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
// Few items flow (1-3)
|
|
2202
|
+
const fewFlow: ExecutionFlow = {
|
|
2203
|
+
id: `${pathSlug}-few-items`,
|
|
2204
|
+
name: `${baseName} Few Items`,
|
|
2205
|
+
description: `When ${baseName.toLowerCase()} array has 1-3 items`,
|
|
2206
|
+
requiredValues: [
|
|
2207
|
+
{
|
|
2208
|
+
attributePath: resolvedPath,
|
|
2209
|
+
value: '3',
|
|
2210
|
+
comparison: 'length<',
|
|
2211
|
+
valueType: 'array',
|
|
2212
|
+
},
|
|
2213
|
+
...gatingRequiredValues,
|
|
2214
|
+
],
|
|
2215
|
+
impact: 'low',
|
|
2216
|
+
exclusiveGroup,
|
|
2217
|
+
sourceLocation: jsxUsage.sourceLocation
|
|
2218
|
+
? {
|
|
2219
|
+
lineNumber: jsxUsage.sourceLocation.lineNumber,
|
|
2220
|
+
column: jsxUsage.sourceLocation.column,
|
|
2221
|
+
}
|
|
2222
|
+
: undefined,
|
|
2223
|
+
codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
|
|
2224
|
+
};
|
|
2225
|
+
|
|
2226
|
+
if (!seenFlowIds.has(fewFlow.id)) {
|
|
2227
|
+
seenFlowIds.add(fewFlow.id);
|
|
2228
|
+
flows.push(fewFlow);
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
// Many items flow (10+)
|
|
2232
|
+
const manyFlow: ExecutionFlow = {
|
|
2233
|
+
id: `${pathSlug}-many-items`,
|
|
2234
|
+
name: `${baseName} Many Items`,
|
|
2235
|
+
description: `When ${baseName.toLowerCase()} array has many items`,
|
|
2236
|
+
requiredValues: [
|
|
2237
|
+
{
|
|
2238
|
+
attributePath: resolvedPath,
|
|
2239
|
+
value: '10',
|
|
2240
|
+
comparison: 'length>',
|
|
2241
|
+
valueType: 'array',
|
|
2242
|
+
},
|
|
2243
|
+
...gatingRequiredValues,
|
|
2244
|
+
],
|
|
2245
|
+
impact: 'low',
|
|
2246
|
+
exclusiveGroup,
|
|
2247
|
+
sourceLocation: jsxUsage.sourceLocation
|
|
2248
|
+
? {
|
|
2249
|
+
lineNumber: jsxUsage.sourceLocation.lineNumber,
|
|
2250
|
+
column: jsxUsage.sourceLocation.column,
|
|
2251
|
+
}
|
|
2252
|
+
: undefined,
|
|
2253
|
+
codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
|
|
2254
|
+
};
|
|
2255
|
+
|
|
2256
|
+
if (!seenFlowIds.has(manyFlow.id)) {
|
|
2257
|
+
seenFlowIds.add(manyFlow.id);
|
|
2258
|
+
flows.push(manyFlow);
|
|
2259
|
+
}
|
|
1012
2260
|
}
|
|
1013
2261
|
}
|
|
1014
2262
|
}
|