@codeyam/codeyam-cli 0.1.0-staging.1669d45 → 0.1.0-staging.1a2737b
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 +19 -19
- package/analyzer-template/packages/ai/index.ts +16 -2
- package/analyzer-template/packages/ai/package.json +2 -2
- package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +110 -52
- 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/forInStatementHandler.ts +10 -17
- package/analyzer-template/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.ts +6 -126
- package/analyzer-template/packages/ai/src/lib/astScopes/processExpression.ts +656 -28
- package/analyzer-template/packages/ai/src/lib/astScopes/sharedPatterns.ts +28 -0
- package/analyzer-template/packages/ai/src/lib/astScopes/types.ts +94 -7
- package/analyzer-template/packages/ai/src/lib/completionCall.ts +198 -34
- package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +1331 -254
- package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/JavascriptFrameworkManager.ts +5 -1
- 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 +54 -3
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +124 -17
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/coerceObjectsToPrimitivesBySchema.ts +70 -0
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.ts +140 -14
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.ts +179 -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 +393 -97
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.ts +129 -0
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/stripNullableMarkers.ts +35 -0
- package/analyzer-template/packages/ai/src/lib/dataStructureChunking.ts +183 -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/generateEntityDataStructure.ts +58 -3
- package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +936 -7
- package/analyzer-template/packages/ai/src/lib/generateEntityScenarios.ts +35 -6
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +515 -6
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.ts +1 -1
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionals.ts +1540 -75
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.ts +239 -0
- package/analyzer-template/packages/ai/src/lib/isolateScopes.ts +51 -3
- package/analyzer-template/packages/ai/src/lib/mergeJsonTypeDefinitions.ts +5 -0
- package/analyzer-template/packages/ai/src/lib/mergeStatements.ts +90 -96
- package/analyzer-template/packages/ai/src/lib/promptGenerators/collapseNullableObjects.ts +118 -0
- 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 +44 -7
- 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/index.ts +2 -0
- 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/nodes/getNodeType.ts +1 -0
- 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 +99 -22
- package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +19 -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/TransformationTracer.ts +1315 -0
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +193 -76
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +87 -25
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +269 -22
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +118 -10
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +647 -73
- 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/index.ts +1 -0
- package/analyzer-template/packages/analyze/src/lib/utils/getFileByPath.ts +19 -0
- package/analyzer-template/packages/aws/package.json +10 -10
- package/analyzer-template/packages/database/package.json +1 -1
- package/analyzer-template/packages/database/src/lib/analysisBranchToDb.ts +1 -1
- package/analyzer-template/packages/database/src/lib/analysisToDb.ts +1 -1
- package/analyzer-template/packages/database/src/lib/branchToDb.ts +1 -1
- package/analyzer-template/packages/database/src/lib/commitBranchToDb.ts +1 -1
- package/analyzer-template/packages/database/src/lib/commitToDb.ts +1 -1
- package/analyzer-template/packages/database/src/lib/fileToDb.ts +1 -1
- package/analyzer-template/packages/database/src/lib/kysely/db.ts +14 -1
- package/analyzer-template/packages/database/src/lib/kysely/tables/commitsTable.ts +6 -0
- package/analyzer-template/packages/database/src/lib/kysely/tables/debugReportsTable.ts +1 -1
- package/analyzer-template/packages/database/src/lib/kysely/tables/labsRequestsTable.ts +52 -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/projectToDb.ts +1 -1
- package/analyzer-template/packages/database/src/lib/saveFiles.ts +1 -1
- package/analyzer-template/packages/database/src/lib/scenarioToDb.ts +1 -1
- package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +7 -14
- package/analyzer-template/packages/database/src/lib/userScenarioToDb.ts +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/analysisBranchToDb.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/analysisBranchToDb.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/analysisToDb.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/analysisToDb.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/branchToDb.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/branchToDb.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/commitBranchToDb.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/commitBranchToDb.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/commitToDb.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/commitToDb.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/fileToDb.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/fileToDb.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.d.ts +2 -0
- 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 +11 -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/kysely/tables/debugReportsTable.d.ts +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/labsRequestsTable.d.ts +23 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/labsRequestsTable.d.ts.map +1 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/labsRequestsTable.js +35 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/labsRequestsTable.js.map +1 -0
- 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/projectToDb.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/projectToDb.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/saveFiles.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/saveFiles.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/scenarioToDb.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/scenarioToDb.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/ProjectMetadata.d.ts +7 -0
- package/analyzer-template/packages/github/dist/types/src/types/ProjectMetadata.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/ProjectMetadata.ts +7 -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/ProjectMetadata.d.ts +7 -0
- package/analyzer-template/packages/utils/dist/types/src/types/ProjectMetadata.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/fs/rsyncCopy.d.ts.map +1 -1
- package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js +93 -2
- package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js.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/fs/rsyncCopy.ts +108 -2
- 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 +436 -44
- 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 +75 -7
- 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 +295 -10
- package/analyzer-template/project/writeScenarioComponents.ts +237 -32
- package/analyzer-template/project/writeSimpleRoot.ts +21 -11
- package/analyzer-template/scripts/comboWorkerLoop.cjs +98 -50
- package/background/src/lib/local/createLocalAnalyzer.js +1 -1
- package/background/src/lib/local/createLocalAnalyzer.js.map +1 -1
- 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 +359 -14
- 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 +62 -7
- 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 +251 -6
- package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
- package/background/src/lib/virtualized/project/writeScenarioComponents.js +173 -30
- package/background/src/lib/virtualized/project/writeScenarioComponents.js.map +1 -1
- package/background/src/lib/virtualized/project/writeSimpleRoot.js +21 -11
- package/background/src/lib/virtualized/project/writeSimpleRoot.js.map +1 -1
- package/codeyam-cli/scripts/apply-setup.js +180 -0
- package/codeyam-cli/scripts/apply-setup.js.map +1 -1
- package/codeyam-cli/src/cli.js +32 -18
- package/codeyam-cli/src/cli.js.map +1 -1
- package/codeyam-cli/src/codeyam-cli.js +18 -2
- package/codeyam-cli/src/codeyam-cli.js.map +1 -1
- package/codeyam-cli/src/commands/analyze.js +4 -2
- package/codeyam-cli/src/commands/analyze.js.map +1 -1
- package/codeyam-cli/src/commands/baseline.js +2 -0
- package/codeyam-cli/src/commands/baseline.js.map +1 -1
- package/codeyam-cli/src/commands/debug.js +9 -5
- package/codeyam-cli/src/commands/debug.js.map +1 -1
- package/codeyam-cli/src/commands/default.js +31 -20
- package/codeyam-cli/src/commands/default.js.map +1 -1
- package/codeyam-cli/src/commands/detect-universal-mocks.js +2 -0
- package/codeyam-cli/src/commands/detect-universal-mocks.js.map +1 -1
- package/codeyam-cli/src/commands/init.js +49 -257
- package/codeyam-cli/src/commands/init.js.map +1 -1
- package/codeyam-cli/src/commands/memory.js +307 -0
- package/codeyam-cli/src/commands/memory.js.map +1 -0
- package/codeyam-cli/src/commands/recapture.js +2 -0
- package/codeyam-cli/src/commands/recapture.js.map +1 -1
- package/codeyam-cli/src/commands/setup-sandbox.js +2 -0
- package/codeyam-cli/src/commands/setup-sandbox.js.map +1 -1
- package/codeyam-cli/src/commands/setup-simulations.js +284 -0
- package/codeyam-cli/src/commands/setup-simulations.js.map +1 -0
- package/codeyam-cli/src/commands/test-startup.js +2 -0
- package/codeyam-cli/src/commands/test-startup.js.map +1 -1
- package/codeyam-cli/src/commands/verify.js +14 -2
- package/codeyam-cli/src/commands/verify.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/npmVersionCheck.test.js +179 -0
- package/codeyam-cli/src/utils/__tests__/npmVersionCheck.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +128 -82
- 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/analyzer.js +7 -0
- package/codeyam-cli/src/utils/analyzer.js.map +1 -1
- package/codeyam-cli/src/utils/backgroundServer.js +90 -19
- package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/utils/generateReport.js +2 -2
- package/codeyam-cli/src/utils/install-skills.js +77 -38
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/labsAutoCheck.js +19 -0
- package/codeyam-cli/src/utils/labsAutoCheck.js.map +1 -0
- package/codeyam-cli/src/utils/npmVersionCheck.js +76 -0
- package/codeyam-cli/src/utils/npmVersionCheck.js.map +1 -0
- package/codeyam-cli/src/utils/progress.js +7 -0
- package/codeyam-cli/src/utils/progress.js.map +1 -1
- package/codeyam-cli/src/utils/queue/job.js +5 -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/requireSimulations.js +10 -0
- package/codeyam-cli/src/utils/requireSimulations.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/confusionDetector.test.js +82 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/confusionDetector.test.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/contextBuilder.test.js +230 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/contextBuilder.test.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/assertRules.js +67 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/assertRules.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/captureFixture.js +105 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/captureFixture.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/loadCapturedFixture.js +34 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/loadCapturedFixture.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/runClaude.js +162 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/runClaude.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/setupTempProject.js +74 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/setupTempProject.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/ruleReflectionE2E.test.js +376 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/ruleReflectionE2E.test.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/promptBuilder.test.js +116 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/promptBuilder.test.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/transcriptParser.test.js +127 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/transcriptParser.test.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/confusionDetector.js +50 -0
- package/codeyam-cli/src/utils/ruleReflection/confusionDetector.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/contextBuilder.js +116 -0
- package/codeyam-cli/src/utils/ruleReflection/contextBuilder.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/index.js +5 -0
- package/codeyam-cli/src/utils/ruleReflection/index.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/promptBuilder.js +44 -0
- package/codeyam-cli/src/utils/ruleReflection/promptBuilder.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/transcriptParser.js +85 -0
- package/codeyam-cli/src/utils/ruleReflection/transcriptParser.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/types.js +5 -0
- package/codeyam-cli/src/utils/ruleReflection/types.js.map +1 -0
- package/codeyam-cli/src/utils/rules/__tests__/ruleState.test.js +293 -0
- package/codeyam-cli/src/utils/rules/__tests__/ruleState.test.js.map +1 -0
- package/codeyam-cli/src/utils/rules/index.js +6 -0
- package/codeyam-cli/src/utils/rules/index.js.map +1 -0
- package/codeyam-cli/src/utils/rules/parser.js +83 -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/ruleState.js +150 -0
- package/codeyam-cli/src/utils/rules/ruleState.js.map +1 -0
- package/codeyam-cli/src/utils/rules/staleness.js +137 -0
- package/codeyam-cli/src/utils/rules/staleness.js.map +1 -0
- package/codeyam-cli/src/utils/serverState.js +37 -10
- package/codeyam-cli/src/utils/serverState.js.map +1 -1
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +21 -42
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
- package/codeyam-cli/src/utils/simulationGateMiddleware.js +138 -0
- package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -0
- package/codeyam-cli/src/utils/syncMocksMiddleware.js +5 -24
- package/codeyam-cli/src/utils/syncMocksMiddleware.js.map +1 -1
- package/codeyam-cli/src/utils/versionInfo.js +25 -0
- package/codeyam-cli/src/utils/versionInfo.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/dependency-smoke.test.js +66 -0
- package/codeyam-cli/src/webserver/__tests__/dependency-smoke.test.js.map +1 -0
- package/codeyam-cli/src/webserver/app/lib/database.js +22 -6
- package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
- package/codeyam-cli/src/webserver/app/lib/dbNotifier.js.map +1 -1
- package/codeyam-cli/src/webserver/backgroundServer.js +50 -0
- package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/webserver/bootstrap.js +51 -0
- package/codeyam-cli/src/webserver/bootstrap.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/CopyButton-jNYXRRNI.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/EntityItem-bwuHPyTa.js +11 -0
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeBadge-COi5OvsN.js → EntityTypeBadge-CvzqMxcu.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-BwdQv49w.js → EntityTypeIcon-BH0XDim7.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-CEleMv_j.js → InlineSpinner-EhOseatT.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-D68KarMg.js → InteractivePreview-yjIHlOGa.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-L75Wvqgw.js → LibraryFunctionPreview-Cq5o8jL4.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-C53WM8qn.js → LoadingDots-BvMu2i-g.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-CrNkmy4i.js → LogViewer-kgBTLoJD.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/ReportIssueModal-BzPgx-xO.js +11 -0
- package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-CQifa1n-.js → SafeScreenshot-CwZrv-Ok.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-CyaBFX7l.js → ScenarioViewer-BX2Ny2Qj.js} +3 -13
- package/codeyam-cli/src/webserver/build/client/assets/{TruncatedFilePath-D36O1rzU.js → TruncatedFilePath-CDpEprKa.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/_index-BRx8ZGZo.js +11 -0
- package/codeyam-cli/src/webserver/build/client/assets/activity.(_tab)-4S4yPfFw.js +27 -0
- package/codeyam-cli/src/webserver/build/client/assets/agent-transcripts-DHKuQSmR.js +17 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.agent-transcripts-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.health-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.labs-unlock-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/api.save-fixture-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/book-open-D4IPYH_y.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-DgTPh8H-.js → chevron-down-CG65viiV.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{chunk-EPOLDU6W-DdQKK6on.js → chunk-JZWAC4HX-DB3aFuEO.js} +12 -12
- package/codeyam-cli/src/webserver/build/client/assets/{circle-check-Dmr2bb1R.js → circle-check-igfMr5DY.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/copy-Coc4o_8c.js +11 -0
- package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-Do4ZLUYa.js → createLucideIcon-D1zB-pYc.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/dev.empty-JTAjQ54M.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-CbdFyxZh.js → entity._sha._-B0h9AqE6.js} +12 -12
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-B4iCfs5M.js → entity._sha.scenarios._scenarioId.fullscreen-DjLxr2JB.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-wDWZZO1W.js → entity._sha_.create-scenario-CtYowLOt.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMbl7MeQ.js → entity._sha_.edit._scenarioId-PePWg17F.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entry.client-5wRKRIH9.js → entry.client-I-Wo99C_.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{fileTableUtils-DD3SDH7t.js → fileTableUtils-9sMMAiWJ.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/files-Co65J0s3.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{git-zXjT7J0G.js → git-BdHOxVfg.js} +8 -8
- package/codeyam-cli/src/webserver/build/client/assets/globals-BSZfYCkU.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{index-DLbXwndH.js → index-CUM5iXwc.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-gPZ-lad1.js → index-_417gcQW.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/labs-BK0C1H1T.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-BsPXJ81F.js → loader-circle-TzRHMVog.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-040dab1c.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/memory-UIDVz141.js +92 -0
- package/codeyam-cli/src/webserver/build/client/assets/pause-hjzB7t2z.js +11 -0
- package/codeyam-cli/src/webserver/build/client/assets/root-D1WadSdf.js +62 -0
- package/codeyam-cli/src/webserver/build/client/assets/{search-P2FKIUql.js → search-DcAwD_Ln.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/settings-CclxrcPK.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{simulations-L18M6-kN.js → simulations-DVNJVQgD.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/terminal-DbEAHMbA.js +11 -0
- package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BDz7kbVA.js → triangle-alert-CAD5b1o_.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-29dDmbH8.js → useCustomSizes-BqgrAzs3.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-BUm0UVJm.js → useLastLogLine-DAFqfEDH.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-CkIOKTrZ.js → useReportContext-DZlYx2c4.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useToast-KKw5kTn-.js → useToast-ihdMtlf6.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/index-B3dE0r28.js +1 -0
- package/codeyam-cli/src/webserver/build/server/assets/server-build-DYbfdxa3.js +273 -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:debug.md → codeyam-debug.md} +48 -4
- package/codeyam-cli/templates/codeyam-diagnose.md +481 -0
- package/codeyam-cli/templates/codeyam-memory-hook.sh +199 -0
- package/codeyam-cli/templates/codeyam-memory.md +396 -0
- package/codeyam-cli/templates/codeyam-new-rule.md +13 -0
- package/codeyam-cli/templates/{codeyam:setup.md → codeyam-setup.md} +13 -1
- package/codeyam-cli/templates/{codeyam:sim.md → codeyam-sim.md} +1 -1
- package/codeyam-cli/templates/{codeyam:test.md → codeyam-test.md} +1 -1
- package/codeyam-cli/templates/{codeyam:verify.md → codeyam-verify.md} +1 -1
- package/codeyam-cli/templates/rule-notification-hook.py +56 -0
- package/codeyam-cli/templates/rule-reflection-hook.py +627 -0
- package/codeyam-cli/templates/rules-instructions.md +132 -0
- package/package.json +18 -15
- package/packages/ai/index.js +7 -3
- package/packages/ai/index.js.map +1 -1
- package/packages/ai/src/lib/analyzeScope.js +91 -30
- 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/forInStatementHandler.js +10 -14
- package/packages/ai/src/lib/astScopes/patterns/forInStatementHandler.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 +518 -28
- package/packages/ai/src/lib/astScopes/processExpression.js.map +1 -1
- package/packages/ai/src/lib/astScopes/sharedPatterns.js +25 -0
- package/packages/ai/src/lib/astScopes/sharedPatterns.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 +1061 -174
- package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/JavascriptFrameworkManager.js +5 -1
- package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/JavascriptFrameworkManager.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 +52 -3
- package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +106 -13
- package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/coerceObjectsToPrimitivesBySchema.js +63 -0
- package/packages/ai/src/lib/dataStructure/helpers/coerceObjectsToPrimitivesBySchema.js.map +1 -0
- package/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.js +122 -12
- package/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js +173 -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 +333 -86
- 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 +130 -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/generateEntityDataStructure.js +46 -2
- package/packages/ai/src/lib/generateEntityDataStructure.js.map +1 -1
- package/packages/ai/src/lib/generateEntityScenarioData.js +734 -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 +376 -4
- package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
- package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js +1124 -59
- 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/isolateScopes.js +39 -3
- package/packages/ai/src/lib/isolateScopes.js.map +1 -1
- package/packages/ai/src/lib/mergeJsonTypeDefinitions.js +5 -0
- package/packages/ai/src/lib/mergeJsonTypeDefinitions.js.map +1 -1
- 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/collapseNullableObjects.js +97 -0
- package/packages/ai/src/lib/promptGenerators/collapseNullableObjects.js.map +1 -0
- 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 +30 -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/index.js +1 -0
- package/packages/analyze/index.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/nodes/getNodeType.js +1 -0
- package/packages/analyze/src/lib/asts/nodes/getNodeType.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 +72 -10
- package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +17 -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/TransformationTracer.js +880 -0
- package/packages/analyze/src/lib/files/scenarios/TransformationTracer.js.map +1 -0
- package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +164 -68
- package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +75 -21
- package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +185 -20
- 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 +542 -53
- 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/index.js +1 -0
- package/packages/analyze/src/lib/index.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/analysisBranchToDb.js +1 -1
- package/packages/database/src/lib/analysisBranchToDb.js.map +1 -1
- package/packages/database/src/lib/analysisToDb.js +1 -1
- package/packages/database/src/lib/analysisToDb.js.map +1 -1
- package/packages/database/src/lib/branchToDb.js +1 -1
- package/packages/database/src/lib/branchToDb.js.map +1 -1
- package/packages/database/src/lib/commitBranchToDb.js +1 -1
- package/packages/database/src/lib/commitBranchToDb.js.map +1 -1
- package/packages/database/src/lib/commitToDb.js +1 -1
- package/packages/database/src/lib/commitToDb.js.map +1 -1
- package/packages/database/src/lib/fileToDb.js +1 -1
- package/packages/database/src/lib/fileToDb.js.map +1 -1
- package/packages/database/src/lib/kysely/db.js +11 -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/kysely/tables/labsRequestsTable.js +35 -0
- package/packages/database/src/lib/kysely/tables/labsRequestsTable.js.map +1 -0
- 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/projectToDb.js +1 -1
- package/packages/database/src/lib/projectToDb.js.map +1 -1
- package/packages/database/src/lib/saveFiles.js +1 -1
- package/packages/database/src/lib/saveFiles.js.map +1 -1
- package/packages/database/src/lib/scenarioToDb.js +1 -1
- package/packages/database/src/lib/scenarioToDb.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/fs/rsyncCopy.js +93 -2
- package/packages/utils/src/lib/fs/rsyncCopy.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 +8 -76
- 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/client/assets/settings-B2eDuBj8.js +0 -1
- 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
- package/codeyam-cli/templates/codeyam:diagnose.md +0 -625
|
@@ -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
|
/**
|
|
@@ -80,11 +259,15 @@ function cleanSourceDataPath(sourceDataPath: string): string | null {
|
|
|
80
259
|
sourceDataPath.match(/\.functionCallReturnValue/g) || []
|
|
81
260
|
).length;
|
|
82
261
|
|
|
83
|
-
//
|
|
84
|
-
//
|
|
262
|
+
// For chained function calls (e.g., fetch().json()) or paths with non-standard
|
|
263
|
+
// fn call patterns, return the original path so findInAttributesMapForPath can
|
|
264
|
+
// try to look it up in fullToShortPathMap. If it doesn't match, the caller
|
|
265
|
+
// falls through to fallback resolution anyway.
|
|
85
266
|
if (fnCallReturnValues > 1 || emptyFnCalls !== 1) {
|
|
86
|
-
|
|
87
|
-
|
|
267
|
+
console.log(
|
|
268
|
+
`[cleanSourceDataPath] chained/non-standard path (fnCallRVs=${fnCallReturnValues}, emptyFnCalls=${emptyFnCalls}), returning original: "${sourceDataPath}"`,
|
|
269
|
+
);
|
|
270
|
+
return sourceDataPath;
|
|
88
271
|
}
|
|
89
272
|
|
|
90
273
|
// Find the "()" which marks the function call
|
|
@@ -113,6 +296,131 @@ function cleanSourceDataPath(sourceDataPath: string): string | null {
|
|
|
113
296
|
return actualPath;
|
|
114
297
|
}
|
|
115
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Strip .length suffix from a path if present.
|
|
301
|
+
*
|
|
302
|
+
* When we have a path like "items.length", the controllable attribute is "items"
|
|
303
|
+
* (the array), not "items.length". The length is derived from the array contents.
|
|
304
|
+
*
|
|
305
|
+
* This ensures that execution flows reference the actual controllable attribute
|
|
306
|
+
* rather than the derived .length property.
|
|
307
|
+
*/
|
|
308
|
+
function stripLengthSuffix(path: string): string {
|
|
309
|
+
if (path.endsWith('.length')) {
|
|
310
|
+
return path.slice(0, -7); // Remove ".length" (7 characters)
|
|
311
|
+
}
|
|
312
|
+
return path;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Remove contradictory required values from a compound flow.
|
|
317
|
+
*
|
|
318
|
+
* When a lifecycle boolean (like isLoadingAuditData) is traced to a fetch call's
|
|
319
|
+
* return value, a negated condition (!isLoadingAuditData) produces "falsy" on
|
|
320
|
+
* the fetch path. But if another condition in the same compound requires data
|
|
321
|
+
* from a sub-path of that fetch (e.g., topPaths length > 0), the "falsy" on the
|
|
322
|
+
* parent path contradicts it — a null/falsy response has no .json() to call.
|
|
323
|
+
*
|
|
324
|
+
* This function removes "falsy" required values whose attributePath is a prefix
|
|
325
|
+
* of another required value's attributePath. The child data requirement already
|
|
326
|
+
* implies the parent (fetch) succeeded.
|
|
327
|
+
*/
|
|
328
|
+
function removeContradictoryFalsyValues(
|
|
329
|
+
requiredValues: ExecutionFlow['requiredValues'],
|
|
330
|
+
): ExecutionFlow['requiredValues'] {
|
|
331
|
+
return requiredValues.filter((rv) => {
|
|
332
|
+
if (rv.comparison === 'falsy') {
|
|
333
|
+
const hasChildRequirement = requiredValues.some(
|
|
334
|
+
(other) =>
|
|
335
|
+
other !== rv &&
|
|
336
|
+
other.attributePath.startsWith(rv.attributePath + '.'),
|
|
337
|
+
);
|
|
338
|
+
if (hasChildRequirement) {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return true;
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Generate a human-readable description snippet for a required value,
|
|
348
|
+
* incorporating the comparison type so the LLM understands the intent.
|
|
349
|
+
*/
|
|
350
|
+
function describeRequiredValue(rv: ExecutionFlow['requiredValues'][0]): string {
|
|
351
|
+
const name = generateNameFromPath(rv.attributePath).toLowerCase();
|
|
352
|
+
switch (rv.comparison) {
|
|
353
|
+
case 'truthy':
|
|
354
|
+
return `${name} is present`;
|
|
355
|
+
case 'falsy':
|
|
356
|
+
return `${name} is absent`;
|
|
357
|
+
case 'length>':
|
|
358
|
+
return rv.value === '0'
|
|
359
|
+
? `${name} has items`
|
|
360
|
+
: `${name} has more than ${rv.value} items`;
|
|
361
|
+
case 'length<':
|
|
362
|
+
return `${name} has fewer than ${rv.value} items`;
|
|
363
|
+
case 'equals':
|
|
364
|
+
return `${name} is ${rv.value}`;
|
|
365
|
+
case 'exists':
|
|
366
|
+
return `${name} exists`;
|
|
367
|
+
case 'not-exists':
|
|
368
|
+
return `${name} does not exist`;
|
|
369
|
+
default:
|
|
370
|
+
return `${name} is ${rv.value}`;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Check whether a resolved path has child entries in the fullToShortPathMap.
|
|
376
|
+
*
|
|
377
|
+
* When a lifecycle boolean (e.g., isLoadingAuditData) resolves to a parent path
|
|
378
|
+
* like fetch(...).functionCallReturnValue, and that path has children (like
|
|
379
|
+
* .json().functionCallReturnValue.topPaths), individual truthy/falsy flows on
|
|
380
|
+
* the parent are misleading. Compound flows with specific child requirements
|
|
381
|
+
* provide better guidance for mock data generation.
|
|
382
|
+
*/
|
|
383
|
+
function hasChildPathsInMap(
|
|
384
|
+
resolvedPath: string,
|
|
385
|
+
fullToShortPathMap: Record<string, string>,
|
|
386
|
+
): boolean {
|
|
387
|
+
return Object.keys(fullToShortPathMap).some(
|
|
388
|
+
(fullPath) =>
|
|
389
|
+
fullPath.startsWith(resolvedPath + '.') ||
|
|
390
|
+
fullPath.startsWith(resolvedPath + '['),
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Extract the controllable base path from a path that may contain method calls.
|
|
396
|
+
*
|
|
397
|
+
* This handles complex expressions like:
|
|
398
|
+
* - `scenarios.filter((s) => s.active).length` → `scenarios`
|
|
399
|
+
* - `users.some((u) => u.role === 'admin')` → `users`
|
|
400
|
+
* - `items.map(x => x.name).join(', ')` → `items`
|
|
401
|
+
*
|
|
402
|
+
* The controllable base is the path that can be mocked - we can control
|
|
403
|
+
* what `scenarios` contains, but we can't control what `.filter()` returns.
|
|
404
|
+
*
|
|
405
|
+
* @param path - The path that may contain method calls
|
|
406
|
+
* @returns The controllable base path with method calls stripped
|
|
407
|
+
*/
|
|
408
|
+
function extractControllableBase(path: string): string {
|
|
409
|
+
// First strip .length suffix if present
|
|
410
|
+
const pathWithoutLength = stripLengthSuffix(path);
|
|
411
|
+
|
|
412
|
+
// Use cleanPathOfNonTransformingFunctions to strip method calls like .filter(), .some()
|
|
413
|
+
const cleanedPath = cleanPathOfNonTransformingFunctions(pathWithoutLength);
|
|
414
|
+
|
|
415
|
+
// If the cleaned path is different, return it
|
|
416
|
+
if (cleanedPath !== pathWithoutLength) {
|
|
417
|
+
return cleanedPath;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Otherwise, return the path with just .length stripped
|
|
421
|
+
return pathWithoutLength;
|
|
422
|
+
}
|
|
423
|
+
|
|
116
424
|
/**
|
|
117
425
|
* Find a path in attributesMap, using fullToShortPathMap to verify the path is controllable.
|
|
118
426
|
*
|
|
@@ -125,13 +433,16 @@ function cleanSourceDataPath(sourceDataPath: string): string | null {
|
|
|
125
433
|
* The sourceDataPath contains full paths (e.g., "useLoaderData<LoaderData>().functionCallReturnValue.entity.sha")
|
|
126
434
|
* The fullToShortPathMap maps full paths to short paths
|
|
127
435
|
*/
|
|
128
|
-
function findInAttributesMapForPath(
|
|
436
|
+
export function findInAttributesMapForPath(
|
|
129
437
|
path: string,
|
|
130
438
|
attributesMap: Record<string, string>,
|
|
131
439
|
fullToShortPathMap: Record<string, string>,
|
|
132
440
|
): string | null {
|
|
133
441
|
// Direct match in attributesMap (already a short path)
|
|
134
442
|
if (path in attributesMap) {
|
|
443
|
+
console.log(
|
|
444
|
+
`[findInAttributesMapForPath] "${path}" → DIRECT match in attributesMap`,
|
|
445
|
+
);
|
|
135
446
|
return path;
|
|
136
447
|
}
|
|
137
448
|
|
|
@@ -140,19 +451,31 @@ function findInAttributesMapForPath(
|
|
|
140
451
|
if (path in fullToShortPathMap) {
|
|
141
452
|
const shortPath = fullToShortPathMap[path];
|
|
142
453
|
if (shortPath in attributesMap) {
|
|
454
|
+
console.log(
|
|
455
|
+
`[findInAttributesMapForPath] "${path}" → fullToShortPathMap match: shortPath="${shortPath}" found in attributesMap`,
|
|
456
|
+
);
|
|
143
457
|
return path; // Return FULL path to preserve data source context
|
|
144
458
|
}
|
|
459
|
+
console.log(
|
|
460
|
+
`[findInAttributesMapForPath] "${path}" → fullToShortPathMap match shortPath="${shortPath}" but NOT in attributesMap`,
|
|
461
|
+
);
|
|
145
462
|
}
|
|
146
463
|
|
|
147
464
|
// Normalized match (array indices [N] → [])
|
|
148
465
|
const normalizedPath = path.replace(/\[\d+\]/g, '[]');
|
|
149
466
|
if (normalizedPath !== path) {
|
|
150
467
|
if (normalizedPath in attributesMap) {
|
|
468
|
+
console.log(
|
|
469
|
+
`[findInAttributesMapForPath] "${path}" → normalized "${normalizedPath}" DIRECT match in attributesMap`,
|
|
470
|
+
);
|
|
151
471
|
return normalizedPath;
|
|
152
472
|
}
|
|
153
473
|
if (normalizedPath in fullToShortPathMap) {
|
|
154
474
|
const shortPath = fullToShortPathMap[normalizedPath];
|
|
155
475
|
if (shortPath in attributesMap) {
|
|
476
|
+
console.log(
|
|
477
|
+
`[findInAttributesMapForPath] "${path}" → normalized "${normalizedPath}" fullToShortPathMap match: shortPath="${shortPath}"`,
|
|
478
|
+
);
|
|
156
479
|
return normalizedPath; // Return normalized FULL path
|
|
157
480
|
}
|
|
158
481
|
}
|
|
@@ -164,25 +487,92 @@ function findInAttributesMapForPath(
|
|
|
164
487
|
// and we need to find matching short path prefix
|
|
165
488
|
for (const attrPath of Object.keys(attributesMap)) {
|
|
166
489
|
if (path.startsWith(attrPath + '.') || path.startsWith(attrPath + '[')) {
|
|
167
|
-
|
|
490
|
+
console.log(
|
|
491
|
+
`[findInAttributesMapForPath] "${path}" → PREFIX match: starts with attributesMap key "${attrPath}"`,
|
|
492
|
+
);
|
|
168
493
|
return path;
|
|
169
494
|
}
|
|
170
495
|
}
|
|
171
496
|
|
|
172
497
|
// Try suffix matching: if the path ends with ".X.Y.Z" and attributesMap has "X.Y.Z"
|
|
173
498
|
// Return the FULL input path to preserve data source context
|
|
174
|
-
for
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
)
|
|
179
|
-
|
|
499
|
+
// Skip suffix matching for chained function calls (multiple .functionCallReturnValue segments)
|
|
500
|
+
// to avoid false matches: e.g., fetch(...).json().functionCallReturnValue.data falsely matching
|
|
501
|
+
// "data" from a completely different data source like useFetcher
|
|
502
|
+
const fnCallReturnValueCount = (
|
|
503
|
+
path.match(/\.functionCallReturnValue/g) || []
|
|
504
|
+
).length;
|
|
505
|
+
if (fnCallReturnValueCount <= 1) {
|
|
506
|
+
for (const attrPath of Object.keys(attributesMap)) {
|
|
507
|
+
if (
|
|
508
|
+
path.endsWith('.' + attrPath) ||
|
|
509
|
+
path.endsWith('.' + attrPath.replace(/\[\d+\]/g, '[]'))
|
|
510
|
+
) {
|
|
511
|
+
console.log(
|
|
512
|
+
`[findInAttributesMapForPath] "${path}" → SUFFIX match: ends with attributesMap key "${attrPath}"`,
|
|
513
|
+
);
|
|
514
|
+
return path; // Return FULL path, not short attrPath
|
|
515
|
+
}
|
|
180
516
|
}
|
|
181
517
|
}
|
|
182
518
|
|
|
519
|
+
// Try child path matching against fullToShortPathMap keys
|
|
520
|
+
// If the path starts with a known full path + '.' or '[', it's a child
|
|
521
|
+
// of a controllable path. Build the equivalent short child path.
|
|
522
|
+
// e.g., path = "fetch(...).fCRV.json().fCRV.topPaths.length"
|
|
523
|
+
// fullToShortPathMap has "fetch(...).fCRV.json().fCRV.topPaths" → "json().fCRV.topPaths"
|
|
524
|
+
// → check if "json().fCRV.topPaths.length" is in attributesMap
|
|
525
|
+
for (const [fullPath, shortPath] of Object.entries(fullToShortPathMap)) {
|
|
526
|
+
if (path.startsWith(fullPath + '.') || path.startsWith(fullPath + '[')) {
|
|
527
|
+
const suffix = path.slice(fullPath.length); // e.g., ".length"
|
|
528
|
+
const shortChildPath = shortPath + suffix;
|
|
529
|
+
if (shortChildPath in attributesMap) {
|
|
530
|
+
console.log(
|
|
531
|
+
`[findInAttributesMapForPath] "${path}" → CHILD of fullToShortPathMap key "${fullPath}": shortChildPath="${shortChildPath}" found in attributesMap`,
|
|
532
|
+
);
|
|
533
|
+
return path; // Return full path to preserve data source context
|
|
534
|
+
}
|
|
535
|
+
// Also check if the base short path is an array and suffix is .length
|
|
536
|
+
if (suffix === '.length' && shortPath in attributesMap) {
|
|
537
|
+
console.log(
|
|
538
|
+
`[findInAttributesMapForPath] "${path}" → CHILD .length of fullToShortPathMap key "${fullPath}": base shortPath="${shortPath}" is in attributesMap (array .length)`,
|
|
539
|
+
);
|
|
540
|
+
return path; // Array .length is controllable via the array
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Try parent matching: if the path is a prefix of any fullToShortPathMap key,
|
|
546
|
+
// it's a parent of controllable data and therefore controllable itself
|
|
547
|
+
for (const fullPath of Object.keys(fullToShortPathMap)) {
|
|
548
|
+
if (fullPath.startsWith(path + '.') || fullPath.startsWith(path + '[')) {
|
|
549
|
+
console.log(
|
|
550
|
+
`[findInAttributesMapForPath] "${path}" → PARENT match: fullToShortPathMap key "${fullPath}" starts with this path`,
|
|
551
|
+
);
|
|
552
|
+
return path;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
console.log(
|
|
557
|
+
`[findInAttributesMapForPath] "${path}" → NO MATCH (checked ${Object.keys(attributesMap).length} attributesMap keys, ${Object.keys(fullToShortPathMap).length} fullToShortPathMap keys)`,
|
|
558
|
+
);
|
|
183
559
|
return null;
|
|
184
560
|
}
|
|
185
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Generate a slug from a path for use in flow IDs and exclusive groups.
|
|
564
|
+
*/
|
|
565
|
+
function pathToSlug(path: string): string {
|
|
566
|
+
return path
|
|
567
|
+
.replace(/\[\d+\]/g, '')
|
|
568
|
+
.replace(/\[\]/g, '')
|
|
569
|
+
.replace(/\(\)/g, '')
|
|
570
|
+
.replace(/\.functionCallReturnValue/g, '')
|
|
571
|
+
.replace(/[<>]/g, '')
|
|
572
|
+
.replace(/\./g, '-')
|
|
573
|
+
.toLowerCase();
|
|
574
|
+
}
|
|
575
|
+
|
|
186
576
|
/**
|
|
187
577
|
* Generate a human-readable name from a path.
|
|
188
578
|
* Extracts the last meaningful part of the path.
|
|
@@ -244,6 +634,12 @@ function inferValueType(
|
|
|
244
634
|
/**
|
|
245
635
|
* Generate flows from a single conditional usage.
|
|
246
636
|
* Sets impact to 'high' if the conditional controls JSX rendering.
|
|
637
|
+
*
|
|
638
|
+
* When the usage has a `constraintExpression`, it represents a complex expression
|
|
639
|
+
* that can't be simply resolved (e.g., `scenarios.filter(x => x.active).length > 1`).
|
|
640
|
+
* In this case:
|
|
641
|
+
* - `attributePath` is set to the controllable base (e.g., `scenarios`)
|
|
642
|
+
* - `constraint` is set to the full expression for LLM reasoning
|
|
247
643
|
*/
|
|
248
644
|
function generateFlowsFromUsage(
|
|
249
645
|
usage: ConditionalUsage,
|
|
@@ -258,6 +654,14 @@ function generateFlowsFromUsage(
|
|
|
258
654
|
? 'high'
|
|
259
655
|
: 'medium';
|
|
260
656
|
|
|
657
|
+
// When there's a constraintExpression, use the controllable base for attributePath
|
|
658
|
+
// and pass through the constraint for LLM reasoning
|
|
659
|
+
const hasConstraint = !!usage.constraintExpression;
|
|
660
|
+
const attributePath = hasConstraint
|
|
661
|
+
? extractControllableBase(resolvedPath)
|
|
662
|
+
: stripLengthSuffix(resolvedPath);
|
|
663
|
+
const constraint = usage.constraintExpression;
|
|
664
|
+
|
|
261
665
|
if (usage.conditionType === 'truthiness') {
|
|
262
666
|
// Generate both truthy and falsy flows
|
|
263
667
|
const isNegated = usage.isNegated ?? false;
|
|
@@ -269,10 +673,11 @@ function generateFlowsFromUsage(
|
|
|
269
673
|
description: `When ${baseName.toLowerCase()} is ${isNegated ? 'falsy' : 'truthy'}`,
|
|
270
674
|
requiredValues: [
|
|
271
675
|
{
|
|
272
|
-
attributePath
|
|
676
|
+
attributePath,
|
|
273
677
|
value: isNegated ? 'falsy' : 'truthy',
|
|
274
678
|
comparison: isNegated ? 'falsy' : 'truthy',
|
|
275
679
|
valueType: 'boolean',
|
|
680
|
+
constraint,
|
|
276
681
|
},
|
|
277
682
|
],
|
|
278
683
|
impact,
|
|
@@ -292,10 +697,11 @@ function generateFlowsFromUsage(
|
|
|
292
697
|
description: `When ${baseName.toLowerCase()} is ${isNegated ? 'truthy' : 'falsy'}`,
|
|
293
698
|
requiredValues: [
|
|
294
699
|
{
|
|
295
|
-
attributePath
|
|
700
|
+
attributePath,
|
|
296
701
|
value: isNegated ? 'truthy' : 'falsy',
|
|
297
702
|
comparison: isNegated ? 'truthy' : 'falsy',
|
|
298
703
|
valueType: 'boolean',
|
|
704
|
+
constraint,
|
|
299
705
|
},
|
|
300
706
|
],
|
|
301
707
|
impact,
|
|
@@ -321,10 +727,11 @@ function generateFlowsFromUsage(
|
|
|
321
727
|
description: `When ${baseName.toLowerCase()} equals "${value}"`,
|
|
322
728
|
requiredValues: [
|
|
323
729
|
{
|
|
324
|
-
attributePath
|
|
730
|
+
attributePath,
|
|
325
731
|
value: value,
|
|
326
732
|
comparison: 'equals',
|
|
327
733
|
valueType: inferValueType(value),
|
|
734
|
+
constraint,
|
|
328
735
|
},
|
|
329
736
|
],
|
|
330
737
|
impact,
|
|
@@ -377,32 +784,51 @@ function generateFlowFromCompound(
|
|
|
377
784
|
condition.requiredValue?.toString() ??
|
|
378
785
|
condition.comparedValues?.[0] ??
|
|
379
786
|
'truthy';
|
|
380
|
-
comparison
|
|
787
|
+
// Map comparison operator to flow comparison type
|
|
788
|
+
const op = condition.comparisonOperator;
|
|
789
|
+
if (op === '>' || op === '>=') {
|
|
790
|
+
comparison = 'length>';
|
|
791
|
+
} else if (op === '<' || op === '<=') {
|
|
792
|
+
comparison = 'length<';
|
|
793
|
+
} else {
|
|
794
|
+
comparison = 'equals';
|
|
795
|
+
}
|
|
381
796
|
}
|
|
382
797
|
|
|
383
798
|
requiredValues.push({
|
|
384
|
-
attributePath: resolvedPath,
|
|
799
|
+
attributePath: stripLengthSuffix(resolvedPath),
|
|
385
800
|
value,
|
|
386
801
|
comparison,
|
|
387
802
|
valueType: inferValueType(value),
|
|
388
803
|
});
|
|
389
804
|
}
|
|
390
805
|
|
|
391
|
-
//
|
|
392
|
-
const
|
|
806
|
+
// Remove contradictory "falsy" values where a child path requires data
|
|
807
|
+
const cleanedValues = removeContradictoryFalsyValues(requiredValues);
|
|
808
|
+
|
|
809
|
+
if (cleanedValues.length === 0) {
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Generate a combined ID from all paths + values to distinguish different comparisons
|
|
814
|
+
const pathParts = cleanedValues
|
|
393
815
|
.map((rv) => {
|
|
394
816
|
const name = generateNameFromPath(rv.attributePath);
|
|
395
|
-
|
|
817
|
+
const suffix =
|
|
818
|
+
rv.comparison === 'truthy' || rv.comparison === 'falsy'
|
|
819
|
+
? `-${rv.comparison}`
|
|
820
|
+
: `-${rv.comparison}-${rv.value}`;
|
|
821
|
+
return name.toLowerCase().replace(/\s+/g, '-') + suffix;
|
|
396
822
|
})
|
|
397
823
|
.join('-and-');
|
|
398
824
|
|
|
399
825
|
return {
|
|
400
826
|
id: `compound-${pathParts}`,
|
|
401
|
-
name:
|
|
827
|
+
name: cleanedValues
|
|
402
828
|
.map((rv) => generateNameFromPath(rv.attributePath))
|
|
403
829
|
.join(' + '),
|
|
404
|
-
description: `When ${
|
|
405
|
-
requiredValues,
|
|
830
|
+
description: `When ${cleanedValues.map((rv) => describeRequiredValue(rv)).join(' and ')}`,
|
|
831
|
+
requiredValues: cleanedValues,
|
|
406
832
|
impact,
|
|
407
833
|
sourceLocation: {
|
|
408
834
|
lineNumber: compound.sourceLocation.lineNumber,
|
|
@@ -412,6 +838,60 @@ function generateFlowFromCompound(
|
|
|
412
838
|
};
|
|
413
839
|
}
|
|
414
840
|
|
|
841
|
+
/**
|
|
842
|
+
* Expand a compound conditional with OR groups into multiple condition sets.
|
|
843
|
+
*
|
|
844
|
+
* For a compound like `A && (B || C)`:
|
|
845
|
+
* - Conditions: [{ path: 'A' }, { path: 'B', orGroupId: 'or_xxx' }, { path: 'C', orGroupId: 'or_xxx' }]
|
|
846
|
+
* - Returns: [[A, B], [A, C]] - two sets of conditions
|
|
847
|
+
*
|
|
848
|
+
* For multiple OR groups like `A && (B || C) && (D || E)`:
|
|
849
|
+
* - Returns: [[A, B, D], [A, B, E], [A, C, D], [A, C, E]]
|
|
850
|
+
*/
|
|
851
|
+
function expandOrGroups(
|
|
852
|
+
conditions: CompoundConditional['conditions'],
|
|
853
|
+
): CompoundConditional['conditions'][] {
|
|
854
|
+
// Separate conditions into mandatory (no orGroupId) and OR groups
|
|
855
|
+
const mandatory = conditions.filter((c) => !c.orGroupId);
|
|
856
|
+
const orGroups = new Map<string, typeof conditions>();
|
|
857
|
+
|
|
858
|
+
for (const condition of conditions) {
|
|
859
|
+
if (condition.orGroupId) {
|
|
860
|
+
const group = orGroups.get(condition.orGroupId) ?? [];
|
|
861
|
+
group.push(condition);
|
|
862
|
+
orGroups.set(condition.orGroupId, group);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// If no OR groups, return the original conditions
|
|
867
|
+
if (orGroups.size === 0) {
|
|
868
|
+
return [conditions];
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Generate all combinations by picking one condition from each OR group
|
|
872
|
+
const groupArrays = Array.from(orGroups.values());
|
|
873
|
+
const combinations: CompoundConditional['conditions'][] = [];
|
|
874
|
+
|
|
875
|
+
function generateCombinations(
|
|
876
|
+
index: number,
|
|
877
|
+
current: typeof conditions,
|
|
878
|
+
): void {
|
|
879
|
+
if (index === groupArrays.length) {
|
|
880
|
+
// We've picked one from each OR group - combine with mandatory conditions
|
|
881
|
+
combinations.push([...mandatory, ...current]);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Pick each option from the current OR group
|
|
886
|
+
for (const option of groupArrays[index]) {
|
|
887
|
+
generateCombinations(index + 1, [...current, option]);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
generateCombinations(0, []);
|
|
892
|
+
return combinations;
|
|
893
|
+
}
|
|
894
|
+
|
|
415
895
|
/**
|
|
416
896
|
* Generate execution flows from conditional usages using pure static analysis.
|
|
417
897
|
*
|
|
@@ -457,8 +937,8 @@ function normalizePathForDeduplication(
|
|
|
457
937
|
*/
|
|
458
938
|
function translateChildPathToParent(
|
|
459
939
|
childPath: string,
|
|
460
|
-
childEquivalentSignatureVariables: Record<string, string>,
|
|
461
|
-
parentEquivalentSignatureVariables: Record<string, string>,
|
|
940
|
+
childEquivalentSignatureVariables: Record<string, string | string[]>,
|
|
941
|
+
parentEquivalentSignatureVariables: Record<string, string | string[]>,
|
|
462
942
|
childName: string,
|
|
463
943
|
): string | null {
|
|
464
944
|
// Extract the root variable from the child path
|
|
@@ -485,7 +965,11 @@ function translateChildPathToParent(
|
|
|
485
965
|
|
|
486
966
|
// Look up the child's equivalence for this root variable
|
|
487
967
|
// e.g., childEquiv[selectedScenario] = "signature[0].selectedScenario"
|
|
488
|
-
|
|
968
|
+
// Handle array case (OR expressions) - use first element if array
|
|
969
|
+
const rawChildPropPath = childEquivalentSignatureVariables[rootVar];
|
|
970
|
+
const childPropPath = Array.isArray(rawChildPropPath)
|
|
971
|
+
? rawChildPropPath[0]
|
|
972
|
+
: rawChildPropPath;
|
|
489
973
|
|
|
490
974
|
if (!childPropPath) {
|
|
491
975
|
// No mapping found - this might be internal state, not a prop
|
|
@@ -498,7 +982,11 @@ function translateChildPathToParent(
|
|
|
498
982
|
|
|
499
983
|
// Look up parent's equivalence to find what value was passed to this prop
|
|
500
984
|
// e.g., parentEquiv["ChildName().signature[0].selectedScenario"] = "selectedScenario"
|
|
501
|
-
|
|
985
|
+
// Handle array case (OR expressions) - use first element if array
|
|
986
|
+
const rawParentValue = parentEquivalentSignatureVariables[fullChildPropPath];
|
|
987
|
+
const parentValue = Array.isArray(rawParentValue)
|
|
988
|
+
? rawParentValue[0]
|
|
989
|
+
: rawParentValue;
|
|
502
990
|
|
|
503
991
|
if (!parentValue) {
|
|
504
992
|
// No parent mapping found - log ALL parent keys that contain the childName
|
|
@@ -524,11 +1012,29 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
524
1012
|
equivalentSignatureVariables,
|
|
525
1013
|
fullToShortPathMap,
|
|
526
1014
|
childComponentData,
|
|
1015
|
+
derivedVariables,
|
|
1016
|
+
sourceEquivalencies,
|
|
527
1017
|
} = args;
|
|
528
1018
|
|
|
529
1019
|
const flows: ExecutionFlow[] = [];
|
|
530
1020
|
const seenFlowIds = new Set<string>();
|
|
531
1021
|
|
|
1022
|
+
console.log(
|
|
1023
|
+
`[genFlowsFromConditionals] INPUT: ${Object.keys(conditionalUsages).length} conditional paths, ${Object.keys(attributesMap).length} attributesMap entries, ${Object.keys(fullToShortPathMap).length} fullToShortPathMap entries, ${Object.keys(equivalentSignatureVariables).length} equivSigVars, ${compoundConditionals.length} compound conditionals`,
|
|
1024
|
+
);
|
|
1025
|
+
console.log(
|
|
1026
|
+
`[genFlowsFromConditionals] conditionalUsages keys: [${Object.keys(conditionalUsages).join(', ')}]`,
|
|
1027
|
+
);
|
|
1028
|
+
console.log(
|
|
1029
|
+
`[genFlowsFromConditionals] attributesMap keys: [${Object.keys(attributesMap).join(', ')}]`,
|
|
1030
|
+
);
|
|
1031
|
+
console.log(
|
|
1032
|
+
`[genFlowsFromConditionals] fullToShortPathMap: ${JSON.stringify(fullToShortPathMap)}`,
|
|
1033
|
+
);
|
|
1034
|
+
console.log(
|
|
1035
|
+
`[genFlowsFromConditionals] equivalentSignatureVariables: ${JSON.stringify(equivalentSignatureVariables)}`,
|
|
1036
|
+
);
|
|
1037
|
+
|
|
532
1038
|
// Track normalized resolved paths to prevent duplicate flows
|
|
533
1039
|
// This handles the case where we have usages for both:
|
|
534
1040
|
// - "hasNewerVersion" (short path from destructured variable)
|
|
@@ -546,9 +1052,16 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
546
1052
|
for (const usage of usages) {
|
|
547
1053
|
// Skip usages that are part of compound conditionals (handled separately)
|
|
548
1054
|
if (usage.chainId && compoundChainIds.has(usage.chainId)) {
|
|
1055
|
+
console.log(
|
|
1056
|
+
`[genFlowsFromConditionals] "${usage.path}" SKIP: part of compound conditional chain=${usage.chainId}`,
|
|
1057
|
+
);
|
|
549
1058
|
continue;
|
|
550
1059
|
}
|
|
551
1060
|
|
|
1061
|
+
console.log(
|
|
1062
|
+
`[genFlowsFromConditionals] --- Processing "${usage.path}" (type=${usage.conditionType}, negated=${usage.isNegated}, sourceDataPath="${usage.sourceDataPath ?? '(none)'}", derivedFrom=${usage.derivedFrom ? JSON.stringify(usage.derivedFrom) : 'none'})`,
|
|
1063
|
+
);
|
|
1064
|
+
|
|
552
1065
|
// First, try to use pre-computed sourceDataPath if available
|
|
553
1066
|
let resolvedPath: string | null = null;
|
|
554
1067
|
|
|
@@ -558,7 +1071,9 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
558
1071
|
// should become "useLoaderData<LoaderData>().functionCallReturnValue.entity.sha"
|
|
559
1072
|
// Returns null for malformed paths (e.g., chained function calls like fetch().json())
|
|
560
1073
|
const cleanedPath = cleanSourceDataPath(usage.sourceDataPath);
|
|
561
|
-
|
|
1074
|
+
console.log(
|
|
1075
|
+
`[genFlowsFromConditionals] "${usage.path}" cleanSourceDataPath("${usage.sourceDataPath}") → "${cleanedPath}"`,
|
|
1076
|
+
);
|
|
562
1077
|
if (cleanedPath) {
|
|
563
1078
|
// Verify the cleaned path exists in attributesMap
|
|
564
1079
|
const pathMatch = findInAttributesMapForPath(
|
|
@@ -566,6 +1081,9 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
566
1081
|
attributesMap,
|
|
567
1082
|
fullToShortPathMap,
|
|
568
1083
|
);
|
|
1084
|
+
console.log(
|
|
1085
|
+
`[genFlowsFromConditionals] "${usage.path}" findInAttributesMapForPath("${cleanedPath}") → ${pathMatch ? `"${pathMatch}"` : 'null (not found)'}`,
|
|
1086
|
+
);
|
|
569
1087
|
if (pathMatch) {
|
|
570
1088
|
resolvedPath = pathMatch;
|
|
571
1089
|
}
|
|
@@ -575,13 +1093,18 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
575
1093
|
|
|
576
1094
|
// Fall back to resolution via equivalentSignatureVariables
|
|
577
1095
|
if (!resolvedPath) {
|
|
1096
|
+
console.log(
|
|
1097
|
+
`[genFlowsFromConditionals] "${usage.path}" sourceDataPath resolution failed, trying resolvePathToControllable("${usage.path}")...`,
|
|
1098
|
+
);
|
|
578
1099
|
const resolution = resolvePathToControllable(
|
|
579
1100
|
usage.path,
|
|
580
1101
|
attributesMap,
|
|
581
1102
|
equivalentSignatureVariables,
|
|
582
1103
|
fullToShortPathMap,
|
|
583
1104
|
);
|
|
584
|
-
|
|
1105
|
+
console.log(
|
|
1106
|
+
`[genFlowsFromConditionals] "${usage.path}" resolvePathToControllable → isControllable=${resolution.isControllable}, resolvedPath="${resolution.resolvedPath ?? '(none)'}"`,
|
|
1107
|
+
);
|
|
585
1108
|
if (resolution.isControllable && resolution.resolvedPath) {
|
|
586
1109
|
resolvedPath = resolution.resolvedPath;
|
|
587
1110
|
}
|
|
@@ -591,8 +1114,11 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
591
1114
|
// This handles cases like: const hasAnalysis = analysis !== null
|
|
592
1115
|
// where hasAnalysis is not in attributesMap but analysis is
|
|
593
1116
|
if (!resolvedPath && usage.derivedFrom) {
|
|
594
|
-
const { sourcePath, sourcePaths } =
|
|
595
|
-
|
|
1117
|
+
const { operation, sourcePath, sourcePaths, comparedValue } =
|
|
1118
|
+
usage.derivedFrom;
|
|
1119
|
+
console.log(
|
|
1120
|
+
`[genFlowsFromConditionals] "${usage.path}" trying derivedFrom: operation=${operation}, sourcePath="${sourcePath ?? '(none)'}", sourcePaths=${sourcePaths ? JSON.stringify(sourcePaths) : '(none)'}, comparedValue="${comparedValue ?? '(none)'}"`,
|
|
1121
|
+
);
|
|
596
1122
|
// For single-source derivations (notNull, equals, etc.)
|
|
597
1123
|
if (sourcePath) {
|
|
598
1124
|
const resolution = resolvePathToControllable(
|
|
@@ -601,14 +1127,282 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
601
1127
|
equivalentSignatureVariables,
|
|
602
1128
|
fullToShortPathMap,
|
|
603
1129
|
);
|
|
604
|
-
|
|
605
1130
|
if (resolution.isControllable && resolution.resolvedPath) {
|
|
606
1131
|
resolvedPath = resolution.resolvedPath;
|
|
607
1132
|
}
|
|
608
1133
|
}
|
|
609
1134
|
|
|
610
|
-
// For
|
|
611
|
-
//
|
|
1135
|
+
// For equals/notEquals derivations with comparedValue, generate comparison flows
|
|
1136
|
+
// e.g., canEdit derived from user.role === 'admin'
|
|
1137
|
+
// When canEdit is used in truthiness check, we need:
|
|
1138
|
+
// - Truthy flow: user.role = 'admin' (comparison: 'equals')
|
|
1139
|
+
// - Falsy flow: user.role != 'admin' (comparison: 'notEquals')
|
|
1140
|
+
if (
|
|
1141
|
+
(operation === 'equals' || operation === 'notEquals') &&
|
|
1142
|
+
comparedValue !== undefined &&
|
|
1143
|
+
resolvedPath &&
|
|
1144
|
+
usage.conditionType === 'truthiness'
|
|
1145
|
+
) {
|
|
1146
|
+
const baseName = generateNameFromPath(usage.path);
|
|
1147
|
+
const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
|
|
1148
|
+
? 'high'
|
|
1149
|
+
: 'medium';
|
|
1150
|
+
|
|
1151
|
+
const isNegated = usage.isNegated ?? false;
|
|
1152
|
+
|
|
1153
|
+
// For equals derivation:
|
|
1154
|
+
// - Truthy check (!negated): needs value = comparedValue (equals)
|
|
1155
|
+
// - Falsy check (negated): needs value != comparedValue (notEquals)
|
|
1156
|
+
// For notEquals derivation: inverse of above
|
|
1157
|
+
const isEqualsDerivation = operation === 'equals';
|
|
1158
|
+
const truthyNeedsEquals = isEqualsDerivation !== isNegated;
|
|
1159
|
+
|
|
1160
|
+
// Generate truthy flow
|
|
1161
|
+
const truthyFlow: ExecutionFlow = {
|
|
1162
|
+
id: generateFlowId(usage.path, 'truthy'),
|
|
1163
|
+
name: `${baseName} True`,
|
|
1164
|
+
description: `When ${baseName.toLowerCase()} is truthy (${resolvedPath} ${truthyNeedsEquals ? '=' : '!='} ${comparedValue})`,
|
|
1165
|
+
requiredValues: [
|
|
1166
|
+
{
|
|
1167
|
+
attributePath: stripLengthSuffix(resolvedPath),
|
|
1168
|
+
value: comparedValue,
|
|
1169
|
+
comparison: truthyNeedsEquals ? 'equals' : 'notEquals',
|
|
1170
|
+
valueType: inferValueType(comparedValue),
|
|
1171
|
+
},
|
|
1172
|
+
],
|
|
1173
|
+
impact,
|
|
1174
|
+
sourceLocation: usage.sourceLocation
|
|
1175
|
+
? {
|
|
1176
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
1177
|
+
column: usage.sourceLocation.column,
|
|
1178
|
+
}
|
|
1179
|
+
: undefined,
|
|
1180
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
// Generate falsy flow
|
|
1184
|
+
const falsyFlow: ExecutionFlow = {
|
|
1185
|
+
id: generateFlowId(usage.path, 'falsy'),
|
|
1186
|
+
name: `${baseName} False`,
|
|
1187
|
+
description: `When ${baseName.toLowerCase()} is falsy (${resolvedPath} ${truthyNeedsEquals ? '!=' : '='} ${comparedValue})`,
|
|
1188
|
+
requiredValues: [
|
|
1189
|
+
{
|
|
1190
|
+
attributePath: stripLengthSuffix(resolvedPath),
|
|
1191
|
+
value: comparedValue,
|
|
1192
|
+
comparison: truthyNeedsEquals ? 'notEquals' : 'equals',
|
|
1193
|
+
valueType: inferValueType(comparedValue),
|
|
1194
|
+
},
|
|
1195
|
+
],
|
|
1196
|
+
impact,
|
|
1197
|
+
sourceLocation: usage.sourceLocation
|
|
1198
|
+
? {
|
|
1199
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
1200
|
+
column: usage.sourceLocation.column,
|
|
1201
|
+
}
|
|
1202
|
+
: undefined,
|
|
1203
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
// Add flows and skip normal flow generation
|
|
1207
|
+
if (!seenFlowIds.has(truthyFlow.id)) {
|
|
1208
|
+
seenFlowIds.add(truthyFlow.id);
|
|
1209
|
+
flows.push(truthyFlow);
|
|
1210
|
+
}
|
|
1211
|
+
if (!seenFlowIds.has(falsyFlow.id)) {
|
|
1212
|
+
seenFlowIds.add(falsyFlow.id);
|
|
1213
|
+
flows.push(falsyFlow);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// For OR derivations with negation, we need ALL sources to be falsy
|
|
1220
|
+
// e.g., !isBusy where isBusy = isRunning || isQueued || isPending
|
|
1221
|
+
// For the falsy flow, ALL sources must be falsy
|
|
1222
|
+
if (
|
|
1223
|
+
operation === 'or' &&
|
|
1224
|
+
usage.conditionType === 'truthiness' &&
|
|
1225
|
+
usage.isNegated === true &&
|
|
1226
|
+
sourcePaths &&
|
|
1227
|
+
sourcePaths.length > 0
|
|
1228
|
+
) {
|
|
1229
|
+
// Use expandDerivedVariableToSources to recursively resolve all sources
|
|
1230
|
+
const allSources = expandDerivedVariableToSources(
|
|
1231
|
+
usage.path,
|
|
1232
|
+
conditionalUsages,
|
|
1233
|
+
attributesMap,
|
|
1234
|
+
equivalentSignatureVariables,
|
|
1235
|
+
fullToShortPathMap,
|
|
1236
|
+
new Set(),
|
|
1237
|
+
derivedVariables,
|
|
1238
|
+
);
|
|
1239
|
+
|
|
1240
|
+
if (allSources.length > 0) {
|
|
1241
|
+
// Generate a compound-style flow with all sources set to falsy
|
|
1242
|
+
const baseName = generateNameFromPath(usage.path);
|
|
1243
|
+
const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
|
|
1244
|
+
? 'high'
|
|
1245
|
+
: 'medium';
|
|
1246
|
+
|
|
1247
|
+
const requiredValues: ExecutionFlow['requiredValues'] =
|
|
1248
|
+
allSources.map((source) => ({
|
|
1249
|
+
attributePath: source.path,
|
|
1250
|
+
value: 'falsy',
|
|
1251
|
+
comparison: 'falsy' as const,
|
|
1252
|
+
valueType: 'boolean' as const,
|
|
1253
|
+
}));
|
|
1254
|
+
|
|
1255
|
+
// Create a single falsy flow with all sources
|
|
1256
|
+
const falsyFlow: ExecutionFlow = {
|
|
1257
|
+
id: generateFlowId(usage.path, 'falsy'),
|
|
1258
|
+
name: `${baseName} False`,
|
|
1259
|
+
description: `When ${baseName.toLowerCase()} is falsy (all sources are falsy)`,
|
|
1260
|
+
requiredValues,
|
|
1261
|
+
impact,
|
|
1262
|
+
sourceLocation: usage.sourceLocation
|
|
1263
|
+
? {
|
|
1264
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
1265
|
+
column: usage.sourceLocation.column,
|
|
1266
|
+
}
|
|
1267
|
+
: undefined,
|
|
1268
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
// Create a truthy flow - for OR, ANY source being truthy is sufficient
|
|
1272
|
+
// We use the first resolvable source for the truthy flow
|
|
1273
|
+
const firstSource = allSources[0];
|
|
1274
|
+
const truthyFlow: ExecutionFlow = {
|
|
1275
|
+
id: generateFlowId(usage.path, 'truthy'),
|
|
1276
|
+
name: `${baseName} True`,
|
|
1277
|
+
description: `When ${baseName.toLowerCase()} is truthy`,
|
|
1278
|
+
requiredValues: [
|
|
1279
|
+
{
|
|
1280
|
+
attributePath: firstSource.path,
|
|
1281
|
+
value: 'truthy',
|
|
1282
|
+
comparison: 'truthy',
|
|
1283
|
+
valueType: 'boolean',
|
|
1284
|
+
},
|
|
1285
|
+
],
|
|
1286
|
+
impact,
|
|
1287
|
+
sourceLocation: usage.sourceLocation
|
|
1288
|
+
? {
|
|
1289
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
1290
|
+
column: usage.sourceLocation.column,
|
|
1291
|
+
}
|
|
1292
|
+
: undefined,
|
|
1293
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
1294
|
+
};
|
|
1295
|
+
|
|
1296
|
+
// Add both flows (falsy needs all sources, truthy needs one)
|
|
1297
|
+
if (!seenFlowIds.has(falsyFlow.id)) {
|
|
1298
|
+
seenFlowIds.add(falsyFlow.id);
|
|
1299
|
+
flows.push(falsyFlow);
|
|
1300
|
+
}
|
|
1301
|
+
if (!seenFlowIds.has(truthyFlow.id)) {
|
|
1302
|
+
seenFlowIds.add(truthyFlow.id);
|
|
1303
|
+
flows.push(truthyFlow);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// Skip the normal flow generation for this usage
|
|
1307
|
+
continue;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// For AND derivations without negation, we need ALL sources to be truthy
|
|
1312
|
+
// e.g., isReady where isReady = hasData && isLoaded && isValid
|
|
1313
|
+
// For the truthy flow, ALL sources must be truthy
|
|
1314
|
+
// For negated AND (!isReady), ANY source being falsy is sufficient (fallback behavior)
|
|
1315
|
+
if (
|
|
1316
|
+
operation === 'and' &&
|
|
1317
|
+
usage.conditionType === 'truthiness' &&
|
|
1318
|
+
usage.isNegated !== true &&
|
|
1319
|
+
sourcePaths &&
|
|
1320
|
+
sourcePaths.length > 0
|
|
1321
|
+
) {
|
|
1322
|
+
// Use expandDerivedVariableToSources to recursively resolve all sources
|
|
1323
|
+
const allSources = expandDerivedVariableToSources(
|
|
1324
|
+
usage.path,
|
|
1325
|
+
conditionalUsages,
|
|
1326
|
+
attributesMap,
|
|
1327
|
+
equivalentSignatureVariables,
|
|
1328
|
+
fullToShortPathMap,
|
|
1329
|
+
new Set(),
|
|
1330
|
+
derivedVariables,
|
|
1331
|
+
);
|
|
1332
|
+
|
|
1333
|
+
if (allSources.length > 0) {
|
|
1334
|
+
// Generate a compound-style flow with all sources set to truthy
|
|
1335
|
+
const baseName = generateNameFromPath(usage.path);
|
|
1336
|
+
const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
|
|
1337
|
+
? 'high'
|
|
1338
|
+
: 'medium';
|
|
1339
|
+
|
|
1340
|
+
const requiredValues: ExecutionFlow['requiredValues'] =
|
|
1341
|
+
allSources.map((source) => ({
|
|
1342
|
+
attributePath: source.path,
|
|
1343
|
+
value: 'truthy',
|
|
1344
|
+
comparison: 'truthy' as const,
|
|
1345
|
+
valueType: 'boolean' as const,
|
|
1346
|
+
}));
|
|
1347
|
+
|
|
1348
|
+
// Create a truthy flow with all sources
|
|
1349
|
+
const truthyFlow: ExecutionFlow = {
|
|
1350
|
+
id: generateFlowId(usage.path, 'truthy'),
|
|
1351
|
+
name: `${baseName} True`,
|
|
1352
|
+
description: `When ${baseName.toLowerCase()} is truthy (all sources are truthy)`,
|
|
1353
|
+
requiredValues,
|
|
1354
|
+
impact,
|
|
1355
|
+
sourceLocation: usage.sourceLocation
|
|
1356
|
+
? {
|
|
1357
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
1358
|
+
column: usage.sourceLocation.column,
|
|
1359
|
+
}
|
|
1360
|
+
: undefined,
|
|
1361
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
1362
|
+
};
|
|
1363
|
+
|
|
1364
|
+
// Create a falsy flow - for AND, ANY source being falsy is sufficient
|
|
1365
|
+
// We use the first resolvable source for the falsy flow
|
|
1366
|
+
const firstSource = allSources[0];
|
|
1367
|
+
const falsyFlow: ExecutionFlow = {
|
|
1368
|
+
id: generateFlowId(usage.path, 'falsy'),
|
|
1369
|
+
name: `${baseName} False`,
|
|
1370
|
+
description: `When ${baseName.toLowerCase()} is falsy`,
|
|
1371
|
+
requiredValues: [
|
|
1372
|
+
{
|
|
1373
|
+
attributePath: firstSource.path,
|
|
1374
|
+
value: 'falsy',
|
|
1375
|
+
comparison: 'falsy',
|
|
1376
|
+
valueType: 'boolean',
|
|
1377
|
+
},
|
|
1378
|
+
],
|
|
1379
|
+
impact,
|
|
1380
|
+
sourceLocation: usage.sourceLocation
|
|
1381
|
+
? {
|
|
1382
|
+
lineNumber: usage.sourceLocation.lineNumber,
|
|
1383
|
+
column: usage.sourceLocation.column,
|
|
1384
|
+
}
|
|
1385
|
+
: undefined,
|
|
1386
|
+
codeSnippet: usage.sourceLocation?.codeSnippet,
|
|
1387
|
+
};
|
|
1388
|
+
|
|
1389
|
+
// Add both flows (truthy needs all sources, falsy needs one)
|
|
1390
|
+
if (!seenFlowIds.has(truthyFlow.id)) {
|
|
1391
|
+
seenFlowIds.add(truthyFlow.id);
|
|
1392
|
+
flows.push(truthyFlow);
|
|
1393
|
+
}
|
|
1394
|
+
if (!seenFlowIds.has(falsyFlow.id)) {
|
|
1395
|
+
seenFlowIds.add(falsyFlow.id);
|
|
1396
|
+
flows.push(falsyFlow);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
// Skip the normal flow generation for this usage
|
|
1400
|
+
continue;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
// For multi-source derivations (or, and) without special handling,
|
|
1405
|
+
// try the first resolvable path as a fallback
|
|
612
1406
|
if (!resolvedPath && sourcePaths && sourcePaths.length > 0) {
|
|
613
1407
|
for (const sp of sourcePaths) {
|
|
614
1408
|
const resolution = resolvePathToControllable(
|
|
@@ -628,6 +1422,9 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
628
1422
|
|
|
629
1423
|
if (!resolvedPath) {
|
|
630
1424
|
// Path is not controllable - skip (no invalid flows possible)
|
|
1425
|
+
console.log(
|
|
1426
|
+
`[genFlowsFromConditionals] "${usage.path}" SKIP: not controllable (no resolvedPath after all attempts)`,
|
|
1427
|
+
);
|
|
631
1428
|
continue;
|
|
632
1429
|
}
|
|
633
1430
|
|
|
@@ -642,10 +1439,33 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
642
1439
|
// Skip if we've already generated flows for this normalized path
|
|
643
1440
|
// This prevents duplicate flows when we have usages for both short and full paths
|
|
644
1441
|
if (seenNormalizedPaths.has(normalizedPath)) {
|
|
1442
|
+
console.log(
|
|
1443
|
+
`[genFlowsFromConditionals] "${usage.path}" SKIP: duplicate normalizedPath="${normalizedPath}" (resolvedPath="${resolvedPath}")`,
|
|
1444
|
+
);
|
|
645
1445
|
continue;
|
|
646
1446
|
}
|
|
647
1447
|
seenNormalizedPaths.add(normalizedPath);
|
|
648
1448
|
|
|
1449
|
+
// Skip individual truthy/falsy flows on parent paths that have child data entries.
|
|
1450
|
+
// Lifecycle booleans (like isLoadingAuditData) traced to fetch(...).functionCallReturnValue
|
|
1451
|
+
// produce misleading truthy/falsy flows: "truthy" can't show loading (mock resolves instantly),
|
|
1452
|
+
// "falsy" tells the LLM to return null (breaking .json()). Compound flows with specific child
|
|
1453
|
+
// data requirements provide the correct mock guidance.
|
|
1454
|
+
if (
|
|
1455
|
+
usage.conditionType === 'truthiness' &&
|
|
1456
|
+
resolvedPath &&
|
|
1457
|
+
hasChildPathsInMap(resolvedPath, fullToShortPathMap)
|
|
1458
|
+
) {
|
|
1459
|
+
console.log(
|
|
1460
|
+
`[genFlowsFromConditionals] "${usage.path}" SKIP: parent path "${resolvedPath}" has child data paths — compound flows will handle this`,
|
|
1461
|
+
);
|
|
1462
|
+
continue;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
console.log(
|
|
1466
|
+
`[genFlowsFromConditionals] "${usage.path}" RESOLVED → resolvedPath="${resolvedPath}", normalizedPath="${normalizedPath}" — generating flows`,
|
|
1467
|
+
);
|
|
1468
|
+
|
|
649
1469
|
// Generate flows for this controllable usage
|
|
650
1470
|
const usageFlows = generateFlowsFromUsage(usage, resolvedPath);
|
|
651
1471
|
|
|
@@ -654,6 +1474,9 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
654
1474
|
if (!seenFlowIds.has(flow.id)) {
|
|
655
1475
|
seenFlowIds.add(flow.id);
|
|
656
1476
|
flows.push(flow);
|
|
1477
|
+
console.log(
|
|
1478
|
+
`[genFlowsFromConditionals] "${usage.path}" FLOW ADDED: id="${flow.id}", requiredValues=${JSON.stringify(flow.requiredValues.map((rv) => ({ attr: rv.attributePath, val: rv.value })))}`,
|
|
1479
|
+
);
|
|
657
1480
|
}
|
|
658
1481
|
}
|
|
659
1482
|
}
|
|
@@ -661,32 +1484,277 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
661
1484
|
|
|
662
1485
|
// Process compound conditionals
|
|
663
1486
|
for (const compound of compoundConditionals) {
|
|
664
|
-
//
|
|
665
|
-
|
|
666
|
-
|
|
1487
|
+
// Expand OR groups into separate condition sets
|
|
1488
|
+
// For example, `A && (B || C)` becomes [[A, B], [A, C]]
|
|
1489
|
+
const expandedConditionSets = expandOrGroups(compound.conditions);
|
|
1490
|
+
|
|
1491
|
+
// Process each expanded condition set as a separate potential flow
|
|
1492
|
+
for (const conditionSet of expandedConditionSets) {
|
|
1493
|
+
// First, check if ALL paths in this condition set are controllable (or can be expanded to controllable sources)
|
|
1494
|
+
const resolvedPaths = new Map<string, string>();
|
|
1495
|
+
// Track expanded sources for derived variables (path -> array of expanded sources)
|
|
1496
|
+
const expandedSources = new Map<
|
|
1497
|
+
string,
|
|
1498
|
+
Array<{ path: string; operation?: DerivedVariableOperation }>
|
|
1499
|
+
>();
|
|
1500
|
+
let allControllable = true;
|
|
1501
|
+
|
|
1502
|
+
for (const condition of conditionSet) {
|
|
1503
|
+
// Check if this condition path has derivation info
|
|
1504
|
+
// First check conditionalUsages, then fall back to derivedVariables
|
|
1505
|
+
const usagesForPath = conditionalUsages[condition.path];
|
|
1506
|
+
let derivedFromInfo = usagesForPath?.find(
|
|
1507
|
+
(u) => u.derivedFrom?.operation,
|
|
1508
|
+
)?.derivedFrom;
|
|
1509
|
+
|
|
1510
|
+
// CRITICAL: Also check derivedVariables for intermediate derived variables
|
|
1511
|
+
if (!derivedFromInfo && derivedVariables?.[condition.path]) {
|
|
1512
|
+
derivedFromInfo = derivedVariables[condition.path];
|
|
1513
|
+
}
|
|
667
1514
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
1515
|
+
if (derivedFromInfo) {
|
|
1516
|
+
// This is a derived variable - expand to its sources
|
|
1517
|
+
const sources = expandDerivedVariableToSources(
|
|
1518
|
+
condition.path,
|
|
1519
|
+
conditionalUsages,
|
|
1520
|
+
attributesMap,
|
|
1521
|
+
equivalentSignatureVariables,
|
|
1522
|
+
fullToShortPathMap,
|
|
1523
|
+
new Set(),
|
|
1524
|
+
derivedVariables,
|
|
1525
|
+
);
|
|
1526
|
+
|
|
1527
|
+
if (sources.length > 0) {
|
|
1528
|
+
// Store the expanded sources for this condition
|
|
1529
|
+
expandedSources.set(condition.path, sources);
|
|
1530
|
+
// Use the first source's path for the resolvedPaths map (for ID generation)
|
|
1531
|
+
resolvedPaths.set(condition.path, sources[0].path);
|
|
1532
|
+
} else {
|
|
1533
|
+
// Derived variable expansion failed — try sourceDataPath fallback
|
|
1534
|
+
// This handles cases where the derivation chain goes through useMemo/useState
|
|
1535
|
+
// but the enriched sourceDataPath already traced to the actual data source
|
|
1536
|
+
const usageWithSource = usagesForPath?.find(
|
|
1537
|
+
(u) => u.sourceDataPath,
|
|
1538
|
+
);
|
|
1539
|
+
let derivedFallbackPath: string | null = null;
|
|
1540
|
+
|
|
1541
|
+
if (usageWithSource?.sourceDataPath) {
|
|
1542
|
+
const cleanedPath = cleanSourceDataPath(
|
|
1543
|
+
usageWithSource.sourceDataPath,
|
|
1544
|
+
);
|
|
1545
|
+
if (cleanedPath) {
|
|
1546
|
+
const pathMatch = findInAttributesMapForPath(
|
|
1547
|
+
cleanedPath,
|
|
1548
|
+
attributesMap,
|
|
1549
|
+
fullToShortPathMap,
|
|
1550
|
+
);
|
|
1551
|
+
if (pathMatch) {
|
|
1552
|
+
derivedFallbackPath = pathMatch;
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
if (derivedFallbackPath) {
|
|
1558
|
+
resolvedPaths.set(condition.path, derivedFallbackPath);
|
|
1559
|
+
console.log(
|
|
1560
|
+
`[genFlowsFromConditionals] COMPOUND "${condition.path}" derived expansion failed but sourceDataPath fallback resolved → "${derivedFallbackPath}"`,
|
|
1561
|
+
);
|
|
1562
|
+
} else {
|
|
1563
|
+
// Truly not controllable
|
|
1564
|
+
console.log(
|
|
1565
|
+
`[genFlowsFromConditionals] COMPOUND "${condition.path}" derived but no controllable sources and no sourceDataPath fallback → NOT controllable`,
|
|
1566
|
+
);
|
|
1567
|
+
allControllable = false;
|
|
1568
|
+
break;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
} else {
|
|
1572
|
+
// Not a derived variable - resolve directly
|
|
1573
|
+
// First try sourceDataPath from the usage (same as individual processing)
|
|
1574
|
+
let compoundResolvedPath: string | null = null;
|
|
1575
|
+
|
|
1576
|
+
const usageWithSource = usagesForPath?.find((u) => u.sourceDataPath);
|
|
1577
|
+
if (usageWithSource?.sourceDataPath) {
|
|
1578
|
+
const cleanedPath = cleanSourceDataPath(
|
|
1579
|
+
usageWithSource.sourceDataPath,
|
|
1580
|
+
);
|
|
1581
|
+
if (cleanedPath) {
|
|
1582
|
+
const pathMatch = findInAttributesMapForPath(
|
|
1583
|
+
cleanedPath,
|
|
1584
|
+
attributesMap,
|
|
1585
|
+
fullToShortPathMap,
|
|
1586
|
+
);
|
|
1587
|
+
if (pathMatch) {
|
|
1588
|
+
compoundResolvedPath = pathMatch;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
if (!compoundResolvedPath) {
|
|
1594
|
+
const resolution = resolvePathToControllable(
|
|
1595
|
+
condition.path,
|
|
1596
|
+
attributesMap,
|
|
1597
|
+
equivalentSignatureVariables,
|
|
1598
|
+
fullToShortPathMap,
|
|
1599
|
+
);
|
|
675
1600
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
1601
|
+
if (resolution.isControllable && resolution.resolvedPath) {
|
|
1602
|
+
compoundResolvedPath = resolution.resolvedPath;
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
if (!compoundResolvedPath) {
|
|
1607
|
+
allControllable = false;
|
|
1608
|
+
break;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
resolvedPaths.set(condition.path, compoundResolvedPath);
|
|
1612
|
+
}
|
|
679
1613
|
}
|
|
680
1614
|
|
|
681
|
-
|
|
682
|
-
|
|
1615
|
+
// Only create a flow if ALL paths are controllable
|
|
1616
|
+
if (allControllable && resolvedPaths.size > 0) {
|
|
1617
|
+
// If any conditions were expanded from derived variables, we need to build a custom flow
|
|
1618
|
+
if (expandedSources.size > 0) {
|
|
1619
|
+
const requiredValues: ExecutionFlow['requiredValues'] = [];
|
|
1620
|
+
|
|
1621
|
+
for (const condition of conditionSet) {
|
|
1622
|
+
const sources = expandedSources.get(condition.path);
|
|
1623
|
+
|
|
1624
|
+
if (sources) {
|
|
1625
|
+
// This condition was expanded - add all its sources
|
|
1626
|
+
// Determine the required value based on condition type and derivation operation
|
|
1627
|
+
const usagesForPath = conditionalUsages[condition.path];
|
|
1628
|
+
let expandedDerivedFrom = usagesForPath?.find(
|
|
1629
|
+
(u) => u.derivedFrom?.operation,
|
|
1630
|
+
)?.derivedFrom;
|
|
1631
|
+
|
|
1632
|
+
// Also check derivedVariables for intermediate derived variables
|
|
1633
|
+
if (!expandedDerivedFrom && derivedVariables?.[condition.path]) {
|
|
1634
|
+
expandedDerivedFrom = derivedVariables[condition.path];
|
|
1635
|
+
}
|
|
1636
|
+
const operation = expandedDerivedFrom?.operation;
|
|
1637
|
+
|
|
1638
|
+
for (const source of sources) {
|
|
1639
|
+
// For OR-derived truthy: ANY source truthy
|
|
1640
|
+
// For AND-derived truthy: ALL sources truthy
|
|
1641
|
+
// For negated: inverse
|
|
1642
|
+
let value: string;
|
|
1643
|
+
let comparison: ExecutionFlow['requiredValues'][0]['comparison'];
|
|
1644
|
+
|
|
1645
|
+
if (condition.conditionType === 'truthiness') {
|
|
1646
|
+
const isNegated = condition.isNegated === true;
|
|
1647
|
+
// For OR: truthy needs ANY source truthy, falsy needs ALL sources falsy
|
|
1648
|
+
// For AND: truthy needs ALL sources truthy, falsy needs ANY source falsy
|
|
1649
|
+
// In compound conditionals, we generate the truthy path by default
|
|
1650
|
+
// (the compound expression must be truthy)
|
|
1651
|
+
if (operation === 'or') {
|
|
1652
|
+
// For OR-derived, truthy means we need at least one source truthy
|
|
1653
|
+
// We'll use the first source as truthy (simplification)
|
|
1654
|
+
value = isNegated ? 'falsy' : 'truthy';
|
|
1655
|
+
} else if (operation === 'and') {
|
|
1656
|
+
// For AND-derived, truthy means ALL sources truthy
|
|
1657
|
+
value = isNegated ? 'falsy' : 'truthy';
|
|
1658
|
+
} else {
|
|
1659
|
+
value = isNegated ? 'falsy' : 'truthy';
|
|
1660
|
+
}
|
|
1661
|
+
comparison = isNegated ? 'falsy' : 'truthy';
|
|
1662
|
+
} else {
|
|
1663
|
+
value = 'truthy';
|
|
1664
|
+
comparison = 'truthy';
|
|
1665
|
+
}
|
|
683
1666
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
1667
|
+
requiredValues.push({
|
|
1668
|
+
attributePath: source.path,
|
|
1669
|
+
value,
|
|
1670
|
+
comparison,
|
|
1671
|
+
valueType: 'boolean' as const,
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1674
|
+
} else {
|
|
1675
|
+
// This condition was resolved directly
|
|
1676
|
+
const resolvedPath = resolvedPaths.get(condition.path);
|
|
1677
|
+
if (resolvedPath) {
|
|
1678
|
+
let value: string;
|
|
1679
|
+
let comparison: ExecutionFlow['requiredValues'][0]['comparison'];
|
|
1680
|
+
|
|
1681
|
+
if (condition.conditionType === 'truthiness') {
|
|
1682
|
+
value = condition.isNegated ? 'falsy' : 'truthy';
|
|
1683
|
+
comparison = condition.isNegated ? 'falsy' : 'truthy';
|
|
1684
|
+
} else {
|
|
1685
|
+
value =
|
|
1686
|
+
condition.requiredValue?.toString() ??
|
|
1687
|
+
condition.comparedValues?.[0] ??
|
|
1688
|
+
'truthy';
|
|
1689
|
+
const op = condition.comparisonOperator;
|
|
1690
|
+
if (op === '>' || op === '>=') {
|
|
1691
|
+
comparison = 'length>';
|
|
1692
|
+
} else if (op === '<' || op === '<=') {
|
|
1693
|
+
comparison = 'length<';
|
|
1694
|
+
} else {
|
|
1695
|
+
comparison = 'equals';
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
requiredValues.push({
|
|
1700
|
+
attributePath: stripLengthSuffix(resolvedPath),
|
|
1701
|
+
value,
|
|
1702
|
+
comparison,
|
|
1703
|
+
valueType: inferValueType(value),
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// Remove contradictory "falsy" values where a child path requires data
|
|
1710
|
+
const cleanedValues = removeContradictoryFalsyValues(requiredValues);
|
|
1711
|
+
|
|
1712
|
+
if (cleanedValues.length > 0) {
|
|
1713
|
+
const impact: ExecutionFlow['impact'] =
|
|
1714
|
+
compound.controlsJsxRendering ? 'high' : 'medium';
|
|
1715
|
+
|
|
1716
|
+
// Generate a combined ID from all paths + values
|
|
1717
|
+
const pathParts = cleanedValues
|
|
1718
|
+
.map((rv) => {
|
|
1719
|
+
const name = generateNameFromPath(rv.attributePath);
|
|
1720
|
+
const suffix =
|
|
1721
|
+
rv.comparison === 'truthy' || rv.comparison === 'falsy'
|
|
1722
|
+
? `-${rv.comparison}`
|
|
1723
|
+
: `-${rv.comparison}-${rv.value}`;
|
|
1724
|
+
return name.toLowerCase().replace(/\s+/g, '-') + suffix;
|
|
1725
|
+
})
|
|
1726
|
+
.join('-and-');
|
|
1727
|
+
|
|
1728
|
+
const compoundFlow: ExecutionFlow = {
|
|
1729
|
+
id: `${pathParts}`,
|
|
1730
|
+
name: generateNameFromPath(cleanedValues[0].attributePath),
|
|
1731
|
+
description: `When ${cleanedValues.map((rv) => describeRequiredValue(rv)).join(' and ')}`,
|
|
1732
|
+
impact,
|
|
1733
|
+
requiredValues: cleanedValues,
|
|
1734
|
+
sourceLocation: compound.sourceLocation,
|
|
1735
|
+
};
|
|
1736
|
+
|
|
1737
|
+
if (!seenFlowIds.has(compoundFlow.id)) {
|
|
1738
|
+
seenFlowIds.add(compoundFlow.id);
|
|
1739
|
+
flows.push(compoundFlow);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
} else {
|
|
1743
|
+
// No derived variables - use the original generateFlowFromCompound
|
|
1744
|
+
// Create a modified compound with just this condition set
|
|
1745
|
+
const modifiedCompound = {
|
|
1746
|
+
...compound,
|
|
1747
|
+
conditions: conditionSet,
|
|
1748
|
+
};
|
|
1749
|
+
const compoundFlow = generateFlowFromCompound(
|
|
1750
|
+
modifiedCompound,
|
|
1751
|
+
resolvedPaths,
|
|
1752
|
+
);
|
|
1753
|
+
if (compoundFlow && !seenFlowIds.has(compoundFlow.id)) {
|
|
1754
|
+
seenFlowIds.add(compoundFlow.id);
|
|
1755
|
+
flows.push(compoundFlow);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
690
1758
|
}
|
|
691
1759
|
}
|
|
692
1760
|
}
|
|
@@ -733,7 +1801,18 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
733
1801
|
fullToShortPathMap,
|
|
734
1802
|
);
|
|
735
1803
|
|
|
1804
|
+
// Only use controllable paths for gating conditions (whitelist approach).
|
|
1805
|
+
// Do NOT fall back to equivalentSignatureVariables because those may contain
|
|
1806
|
+
// uncontrollable paths like useState that cannot be mocked.
|
|
1807
|
+
let resolvedVarPath: string | null = null;
|
|
736
1808
|
if (varResolution.isControllable && varResolution.resolvedPath) {
|
|
1809
|
+
resolvedVarPath = varResolution.resolvedPath;
|
|
1810
|
+
}
|
|
1811
|
+
// Note: We intentionally do NOT fall back to equivalentSignatureVariables here
|
|
1812
|
+
// because that would allow uncontrollable paths (like useState) to be added
|
|
1813
|
+
// as gating conditions.
|
|
1814
|
+
|
|
1815
|
+
if (resolvedVarPath) {
|
|
737
1816
|
const isNegated = (gatingCondition as any).isNegated === true;
|
|
738
1817
|
const isNotEquals = operator === '!=' || operator === '!==';
|
|
739
1818
|
// Determine the effective value for this gating condition
|
|
@@ -744,7 +1823,7 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
744
1823
|
const needsExactValue = isNegated !== isNotEquals;
|
|
745
1824
|
|
|
746
1825
|
gatingRequiredValues.push({
|
|
747
|
-
attributePath:
|
|
1826
|
+
attributePath: resolvedVarPath,
|
|
748
1827
|
value: needsExactValue ? 'falsy' : comparedValue,
|
|
749
1828
|
comparison: needsExactValue ? 'falsy' : 'equals',
|
|
750
1829
|
});
|
|
@@ -810,7 +1889,9 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
810
1889
|
// For non-negated &&: all parts must be truthy
|
|
811
1890
|
// For negated ||: all parts must be falsy (DeMorgan: !(A || B) = !A && !B)
|
|
812
1891
|
gatingRequiredValues.push({
|
|
813
|
-
attributePath:
|
|
1892
|
+
attributePath: stripLengthSuffix(
|
|
1893
|
+
partResolution.resolvedPath,
|
|
1894
|
+
),
|
|
814
1895
|
value: isNegated ? 'falsy' : 'truthy',
|
|
815
1896
|
comparison: isNegated ? 'falsy' : 'truthy',
|
|
816
1897
|
});
|
|
@@ -827,16 +1908,27 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
827
1908
|
fullToShortPathMap,
|
|
828
1909
|
);
|
|
829
1910
|
|
|
1911
|
+
// Only use controllable paths for gating conditions (whitelist approach).
|
|
1912
|
+
// Do NOT fall back to equivalentSignatureVariables because those may contain
|
|
1913
|
+
// uncontrollable paths like useState that cannot be mocked.
|
|
1914
|
+
let resolvedGatingPath: string | null = null;
|
|
830
1915
|
if (
|
|
831
1916
|
gatingResolution.isControllable &&
|
|
832
1917
|
gatingResolution.resolvedPath
|
|
833
1918
|
) {
|
|
1919
|
+
resolvedGatingPath = gatingResolution.resolvedPath;
|
|
1920
|
+
}
|
|
1921
|
+
// Note: We intentionally do NOT fall back to equivalentSignatureVariables here
|
|
1922
|
+
// because that would allow uncontrollable paths (like useState) to be added
|
|
1923
|
+
// as gating conditions.
|
|
1924
|
+
|
|
1925
|
+
if (resolvedGatingPath) {
|
|
834
1926
|
// For truthiness conditions on gating, check if the condition is negated
|
|
835
1927
|
// e.g., ternary else branch: isError ? <ErrorView /> : <SuccessView />
|
|
836
1928
|
// SuccessView has isNegated: true, meaning it renders when isError is falsy
|
|
837
1929
|
const isNegated = (gatingCondition as any).isNegated === true;
|
|
838
1930
|
gatingRequiredValues.push({
|
|
839
|
-
attributePath:
|
|
1931
|
+
attributePath: resolvedGatingPath,
|
|
840
1932
|
value: isNegated ? 'falsy' : 'truthy',
|
|
841
1933
|
comparison: isNegated ? 'falsy' : 'truthy',
|
|
842
1934
|
});
|
|
@@ -894,6 +1986,7 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
894
1986
|
}
|
|
895
1987
|
|
|
896
1988
|
// Now resolve the translated path in the parent context
|
|
1989
|
+
// First, try standard resolution
|
|
897
1990
|
const resolution = resolvePathToControllable(
|
|
898
1991
|
translatedPath,
|
|
899
1992
|
attributesMap,
|
|
@@ -901,12 +1994,124 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
901
1994
|
fullToShortPathMap,
|
|
902
1995
|
);
|
|
903
1996
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1997
|
+
// Only create flows for controllable paths (whitelist approach).
|
|
1998
|
+
// If the path doesn't resolve to something in attributesMap, skip it.
|
|
1999
|
+
// This prevents creating flows for useState values which are not
|
|
2000
|
+
// controllable via mock data injection.
|
|
2001
|
+
let resolvedPath = resolution.resolvedPath;
|
|
2002
|
+
|
|
2003
|
+
if (!resolution.isControllable || !resolvedPath) {
|
|
2004
|
+
// Path is not controllable via standard resolution.
|
|
2005
|
+
// Try fallback: For useState values (cyScope*().functionCallReturnValue),
|
|
2006
|
+
// look for a related URL parameter like "varNameFromUrl" that might
|
|
2007
|
+
// control the initial state.
|
|
2008
|
+
//
|
|
2009
|
+
// Example: viewMode → cyScope20().functionCallReturnValue (useState value)
|
|
2010
|
+
// Fallback: viewModeFromUrl → segments[2] (URL param that initializes the useState)
|
|
2011
|
+
const useStateMatch = translatedPath.match(
|
|
2012
|
+
/^cyScope\d+\(\)\.functionCallReturnValue$/,
|
|
2013
|
+
);
|
|
2014
|
+
|
|
2015
|
+
if (useStateMatch) {
|
|
2016
|
+
// Find what variable this useState value corresponds to by looking
|
|
2017
|
+
// for entries like "varName": "cyScope20()" in equivalentSignatureVariables
|
|
2018
|
+
const useStatePattern = translatedPath.replace(
|
|
2019
|
+
/\.functionCallReturnValue$/,
|
|
2020
|
+
'',
|
|
2021
|
+
); // e.g., "cyScope20()"
|
|
2022
|
+
|
|
2023
|
+
// Find the variable name that maps to this useState
|
|
2024
|
+
let useStateVarName: string | null = null;
|
|
2025
|
+
for (const [varName, varPath] of Object.entries(
|
|
2026
|
+
equivalentSignatureVariables,
|
|
2027
|
+
)) {
|
|
2028
|
+
if (varPath === useStatePattern) {
|
|
2029
|
+
useStateVarName = varName;
|
|
2030
|
+
break;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
if (useStateVarName) {
|
|
2035
|
+
// Look for a related URL param like "varNameFromUrl"
|
|
2036
|
+
const urlParamName = `${useStateVarName}FromUrl`;
|
|
2037
|
+
const urlParamPath = equivalentSignatureVariables[urlParamName];
|
|
2038
|
+
|
|
2039
|
+
if (urlParamPath) {
|
|
2040
|
+
// For useState values initialized from URL params, use the
|
|
2041
|
+
// URL param variable name directly (e.g., "viewModeFromUrl")
|
|
2042
|
+
// rather than fully resolving it. This keeps the path meaningful
|
|
2043
|
+
// for scenario generation and avoids overly generic paths like
|
|
2044
|
+
// "useParams().functionCallReturnValue.*".
|
|
2045
|
+
//
|
|
2046
|
+
// The flow will use the URL param name as the attributePath,
|
|
2047
|
+
// which gets properly resolved when generating mock data.
|
|
2048
|
+
resolvedPath = urlParamName;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
// Fallback 2: Try sourceEquivalencies to find the actual data source
|
|
2054
|
+
// This handles the case where props flow through useState but originate
|
|
2055
|
+
// from a mockable data source (e.g., API call, fetcher).
|
|
2056
|
+
//
|
|
2057
|
+
// Example: WorkoutsView receives `workouts` prop which in parent is stored
|
|
2058
|
+
// in useState, but ultimately comes from a Supabase query.
|
|
2059
|
+
// sourceEquivalencies tells us: "WorkoutsView().signature[0].workouts" → "createClient()...data"
|
|
2060
|
+
if (!resolvedPath && sourceEquivalencies) {
|
|
2061
|
+
// Build the child prop path to look up in sourceEquivalencies
|
|
2062
|
+
// Format: "ChildName().signature[0].propName"
|
|
2063
|
+
// First, find what prop this child path maps to
|
|
2064
|
+
let childPropName: string | null = null;
|
|
2065
|
+
for (const [varName, varPath] of Object.entries(
|
|
2066
|
+
childData.equivalentSignatureVariables,
|
|
2067
|
+
)) {
|
|
2068
|
+
// Check if childPath starts with this variable name
|
|
2069
|
+
// e.g., childPath = "workouts.length", varName = "workouts", varPath = "signature[0].workouts"
|
|
2070
|
+
if (
|
|
2071
|
+
childPath === varName ||
|
|
2072
|
+
childPath.startsWith(`${varName}.`)
|
|
2073
|
+
) {
|
|
2074
|
+
childPropName = varName;
|
|
2075
|
+
break;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
908
2078
|
|
|
909
|
-
|
|
2079
|
+
if (childPropName) {
|
|
2080
|
+
// Build the full sourceEquivalencies key
|
|
2081
|
+
const sourceEquivKey = `${childName}().signature[0].${childPropName}`;
|
|
2082
|
+
|
|
2083
|
+
const sourceEquivEntry = sourceEquivalencies[sourceEquivKey];
|
|
2084
|
+
if (sourceEquivEntry && sourceEquivEntry.length > 0) {
|
|
2085
|
+
const dataSourcePath = sourceEquivEntry[0].schemaPath;
|
|
2086
|
+
|
|
2087
|
+
// Check if this data source path is controllable
|
|
2088
|
+
const dataSourceResolution = resolvePathToControllable(
|
|
2089
|
+
dataSourcePath,
|
|
2090
|
+
attributesMap,
|
|
2091
|
+
equivalentSignatureVariables,
|
|
2092
|
+
fullToShortPathMap,
|
|
2093
|
+
);
|
|
2094
|
+
|
|
2095
|
+
if (
|
|
2096
|
+
dataSourceResolution.isControllable &&
|
|
2097
|
+
dataSourceResolution.resolvedPath
|
|
2098
|
+
) {
|
|
2099
|
+
// Preserve any suffix from the child path
|
|
2100
|
+
// e.g., childPath = "workouts.length" → suffix = ".length"
|
|
2101
|
+
const suffix = childPath.startsWith(`${childPropName}.`)
|
|
2102
|
+
? childPath.slice(childPropName.length)
|
|
2103
|
+
: '';
|
|
2104
|
+
resolvedPath = dataSourceResolution.resolvedPath + suffix;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// If still not resolved after fallback, skip
|
|
2111
|
+
if (!resolvedPath) {
|
|
2112
|
+
continue;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
910
2115
|
|
|
911
2116
|
// Check for duplicates
|
|
912
2117
|
const normalizedPath = normalizePathForDeduplication(
|
|
@@ -967,7 +2172,7 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
967
2172
|
// Process child's compound conditionals
|
|
968
2173
|
for (const compound of childData.compoundConditionals) {
|
|
969
2174
|
const resolvedPaths = new Map<string, string>();
|
|
970
|
-
let
|
|
2175
|
+
let allResolvable = true;
|
|
971
2176
|
|
|
972
2177
|
for (const condition of compound.conditions) {
|
|
973
2178
|
// Determine the child path to translate
|
|
@@ -982,7 +2187,7 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
982
2187
|
);
|
|
983
2188
|
|
|
984
2189
|
if (!translatedPath) {
|
|
985
|
-
|
|
2190
|
+
allResolvable = false;
|
|
986
2191
|
break;
|
|
987
2192
|
}
|
|
988
2193
|
|
|
@@ -993,27 +2198,287 @@ export default function generateExecutionFlowsFromConditionals(
|
|
|
993
2198
|
fullToShortPathMap,
|
|
994
2199
|
);
|
|
995
2200
|
|
|
996
|
-
|
|
997
|
-
|
|
2201
|
+
// Only create flows for controllable paths (whitelist approach).
|
|
2202
|
+
// If the path doesn't resolve to something in attributesMap, skip it.
|
|
2203
|
+
// This prevents creating flows for useState values which are not
|
|
2204
|
+
// controllable via mock data injection.
|
|
2205
|
+
let resolvedPath = resolution.resolvedPath;
|
|
2206
|
+
|
|
2207
|
+
if (!resolution.isControllable || !resolvedPath) {
|
|
2208
|
+
// Path is not controllable via standard resolution.
|
|
2209
|
+
// Try fallback: For useState values (cyScope*().functionCallReturnValue),
|
|
2210
|
+
// look for a related URL parameter like "varNameFromUrl" that might
|
|
2211
|
+
// control the initial state.
|
|
2212
|
+
const useStateMatch = translatedPath.match(
|
|
2213
|
+
/^cyScope\d+\(\)\.functionCallReturnValue$/,
|
|
2214
|
+
);
|
|
2215
|
+
|
|
2216
|
+
if (useStateMatch) {
|
|
2217
|
+
const useStatePattern = translatedPath.replace(
|
|
2218
|
+
/\.functionCallReturnValue$/,
|
|
2219
|
+
'',
|
|
2220
|
+
);
|
|
2221
|
+
|
|
2222
|
+
// Find the variable name that maps to this useState
|
|
2223
|
+
let useStateVarName: string | null = null;
|
|
2224
|
+
for (const [varName, varPath] of Object.entries(
|
|
2225
|
+
equivalentSignatureVariables,
|
|
2226
|
+
)) {
|
|
2227
|
+
if (varPath === useStatePattern) {
|
|
2228
|
+
useStateVarName = varName;
|
|
2229
|
+
break;
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
if (useStateVarName) {
|
|
2234
|
+
const urlParamName = `${useStateVarName}FromUrl`;
|
|
2235
|
+
const urlParamPath = equivalentSignatureVariables[urlParamName];
|
|
2236
|
+
|
|
2237
|
+
if (urlParamPath) {
|
|
2238
|
+
resolvedPath = urlParamName;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
if (!resolvedPath) {
|
|
2245
|
+
allResolvable = false;
|
|
998
2246
|
break;
|
|
999
2247
|
}
|
|
1000
2248
|
|
|
1001
|
-
resolvedPaths.set(condition.path,
|
|
2249
|
+
resolvedPaths.set(condition.path, resolvedPath);
|
|
1002
2250
|
}
|
|
1003
2251
|
|
|
1004
|
-
if (
|
|
2252
|
+
if (allResolvable && resolvedPaths.size > 0) {
|
|
1005
2253
|
const compoundFlow = generateFlowFromCompound(
|
|
1006
2254
|
compound,
|
|
1007
2255
|
resolvedPaths,
|
|
1008
2256
|
);
|
|
1009
|
-
if (compoundFlow
|
|
1010
|
-
|
|
1011
|
-
|
|
2257
|
+
if (compoundFlow) {
|
|
2258
|
+
// Add gating conditions to compound flow (same as regular usage flows)
|
|
2259
|
+
if (gatingRequiredValues.length > 0) {
|
|
2260
|
+
// Filter out any gating values that are already in the flow
|
|
2261
|
+
const existingPaths = new Set(
|
|
2262
|
+
compoundFlow.requiredValues.map((rv) => rv.attributePath),
|
|
2263
|
+
);
|
|
2264
|
+
const newGatingValues = gatingRequiredValues.filter(
|
|
2265
|
+
(gv) => !existingPaths.has(gv.attributePath),
|
|
2266
|
+
);
|
|
2267
|
+
compoundFlow.requiredValues = [
|
|
2268
|
+
...compoundFlow.requiredValues,
|
|
2269
|
+
...newGatingValues,
|
|
2270
|
+
];
|
|
2271
|
+
|
|
2272
|
+
// Update the flow ID to include gating conditions
|
|
2273
|
+
if (newGatingValues.length > 0) {
|
|
2274
|
+
const gatingIdPart = newGatingValues
|
|
2275
|
+
.map((gv) => `${gv.attributePath}-${gv.value}`)
|
|
2276
|
+
.join('-');
|
|
2277
|
+
compoundFlow.id = `${compoundFlow.id}-gated-${gatingIdPart}`;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
if (!seenFlowIds.has(compoundFlow.id)) {
|
|
2282
|
+
seenFlowIds.add(compoundFlow.id);
|
|
2283
|
+
flows.push(compoundFlow);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
// Process child's jsxRenderingUsages (array.map flows)
|
|
2290
|
+
// This generates array variation flows (empty, few, many) for arrays rendered in child
|
|
2291
|
+
if (childData.jsxRenderingUsages) {
|
|
2292
|
+
for (const jsxUsage of childData.jsxRenderingUsages) {
|
|
2293
|
+
// Translate the child path to a parent path
|
|
2294
|
+
const translatedPath = translateChildPathToParent(
|
|
2295
|
+
jsxUsage.path,
|
|
2296
|
+
childData.equivalentSignatureVariables,
|
|
2297
|
+
equivalentSignatureVariables,
|
|
2298
|
+
childName,
|
|
2299
|
+
);
|
|
2300
|
+
|
|
2301
|
+
if (!translatedPath) {
|
|
2302
|
+
continue;
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
// Resolve to controllable path in parent context
|
|
2306
|
+
const resolution = resolvePathToControllable(
|
|
2307
|
+
translatedPath,
|
|
2308
|
+
attributesMap,
|
|
2309
|
+
equivalentSignatureVariables,
|
|
2310
|
+
fullToShortPathMap,
|
|
2311
|
+
);
|
|
2312
|
+
|
|
2313
|
+
let resolvedPath = resolution.resolvedPath;
|
|
2314
|
+
|
|
2315
|
+
// Try sourceEquivalencies fallback if not controllable
|
|
2316
|
+
if (!resolution.isControllable || !resolvedPath) {
|
|
2317
|
+
if (sourceEquivalencies) {
|
|
2318
|
+
// Build the sourceEquivalencies key
|
|
2319
|
+
// The child path (e.g., "workouts") maps to a prop path (e.g., "signature[0].workouts")
|
|
2320
|
+
let childPropName: string | null = null;
|
|
2321
|
+
for (const [varName, varPath] of Object.entries(
|
|
2322
|
+
childData.equivalentSignatureVariables,
|
|
2323
|
+
)) {
|
|
2324
|
+
if (
|
|
2325
|
+
jsxUsage.path === varName ||
|
|
2326
|
+
jsxUsage.path.startsWith(`${varName}.`)
|
|
2327
|
+
) {
|
|
2328
|
+
childPropName = varName;
|
|
2329
|
+
break;
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
if (childPropName) {
|
|
2334
|
+
const sourceEquivKey = `${childName}().signature[0].${childPropName}`;
|
|
2335
|
+
const sourceEquivEntry = sourceEquivalencies[sourceEquivKey];
|
|
2336
|
+
|
|
2337
|
+
if (sourceEquivEntry && sourceEquivEntry.length > 0) {
|
|
2338
|
+
const dataSourcePath = sourceEquivEntry[0].schemaPath;
|
|
2339
|
+
|
|
2340
|
+
const dataSourceResolution = resolvePathToControllable(
|
|
2341
|
+
dataSourcePath,
|
|
2342
|
+
attributesMap,
|
|
2343
|
+
equivalentSignatureVariables,
|
|
2344
|
+
fullToShortPathMap,
|
|
2345
|
+
);
|
|
2346
|
+
|
|
2347
|
+
if (
|
|
2348
|
+
dataSourceResolution.isControllable &&
|
|
2349
|
+
dataSourceResolution.resolvedPath
|
|
2350
|
+
) {
|
|
2351
|
+
resolvedPath = dataSourceResolution.resolvedPath;
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
if (!resolvedPath) {
|
|
2359
|
+
continue;
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
// Check for duplicates
|
|
2363
|
+
const normalizedPath = normalizePathForDeduplication(
|
|
2364
|
+
resolvedPath,
|
|
2365
|
+
fullToShortPathMap,
|
|
2366
|
+
);
|
|
2367
|
+
const dedupeKey = `${normalizedPath}:${jsxUsage.renderingType}`;
|
|
2368
|
+
if (seenNormalizedPaths.has(dedupeKey)) {
|
|
2369
|
+
continue;
|
|
2370
|
+
}
|
|
2371
|
+
seenNormalizedPaths.add(dedupeKey);
|
|
2372
|
+
|
|
2373
|
+
// Generate array variation flows for array-map rendering
|
|
2374
|
+
if (jsxUsage.renderingType === 'array-map') {
|
|
2375
|
+
const baseName = generateNameFromPath(resolvedPath);
|
|
2376
|
+
const pathSlug = pathToSlug(resolvedPath);
|
|
2377
|
+
const exclusiveGroup = `array-length-${pathSlug}`;
|
|
2378
|
+
|
|
2379
|
+
// Empty array flow
|
|
2380
|
+
const emptyFlow: ExecutionFlow = {
|
|
2381
|
+
id: `${pathSlug}-empty-array`,
|
|
2382
|
+
name: `${baseName} Empty`,
|
|
2383
|
+
description: `When ${baseName.toLowerCase()} array is empty`,
|
|
2384
|
+
requiredValues: [
|
|
2385
|
+
{
|
|
2386
|
+
attributePath: resolvedPath,
|
|
2387
|
+
value: '0',
|
|
2388
|
+
comparison: 'length<',
|
|
2389
|
+
valueType: 'array',
|
|
2390
|
+
},
|
|
2391
|
+
...gatingRequiredValues,
|
|
2392
|
+
],
|
|
2393
|
+
impact: 'medium',
|
|
2394
|
+
exclusiveGroup,
|
|
2395
|
+
sourceLocation: jsxUsage.sourceLocation
|
|
2396
|
+
? {
|
|
2397
|
+
lineNumber: jsxUsage.sourceLocation.lineNumber,
|
|
2398
|
+
column: jsxUsage.sourceLocation.column,
|
|
2399
|
+
}
|
|
2400
|
+
: undefined,
|
|
2401
|
+
codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
|
|
2402
|
+
};
|
|
2403
|
+
|
|
2404
|
+
if (!seenFlowIds.has(emptyFlow.id)) {
|
|
2405
|
+
seenFlowIds.add(emptyFlow.id);
|
|
2406
|
+
flows.push(emptyFlow);
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
// Few items flow (1-3)
|
|
2410
|
+
const fewFlow: ExecutionFlow = {
|
|
2411
|
+
id: `${pathSlug}-few-items`,
|
|
2412
|
+
name: `${baseName} Few Items`,
|
|
2413
|
+
description: `When ${baseName.toLowerCase()} array has 1-3 items`,
|
|
2414
|
+
requiredValues: [
|
|
2415
|
+
{
|
|
2416
|
+
attributePath: resolvedPath,
|
|
2417
|
+
value: '3',
|
|
2418
|
+
comparison: 'length<',
|
|
2419
|
+
valueType: 'array',
|
|
2420
|
+
},
|
|
2421
|
+
...gatingRequiredValues,
|
|
2422
|
+
],
|
|
2423
|
+
impact: 'low',
|
|
2424
|
+
exclusiveGroup,
|
|
2425
|
+
sourceLocation: jsxUsage.sourceLocation
|
|
2426
|
+
? {
|
|
2427
|
+
lineNumber: jsxUsage.sourceLocation.lineNumber,
|
|
2428
|
+
column: jsxUsage.sourceLocation.column,
|
|
2429
|
+
}
|
|
2430
|
+
: undefined,
|
|
2431
|
+
codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
|
|
2432
|
+
};
|
|
2433
|
+
|
|
2434
|
+
if (!seenFlowIds.has(fewFlow.id)) {
|
|
2435
|
+
seenFlowIds.add(fewFlow.id);
|
|
2436
|
+
flows.push(fewFlow);
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
// Many items flow (10+)
|
|
2440
|
+
const manyFlow: ExecutionFlow = {
|
|
2441
|
+
id: `${pathSlug}-many-items`,
|
|
2442
|
+
name: `${baseName} Many Items`,
|
|
2443
|
+
description: `When ${baseName.toLowerCase()} array has many items`,
|
|
2444
|
+
requiredValues: [
|
|
2445
|
+
{
|
|
2446
|
+
attributePath: resolvedPath,
|
|
2447
|
+
value: '10',
|
|
2448
|
+
comparison: 'length>',
|
|
2449
|
+
valueType: 'array',
|
|
2450
|
+
},
|
|
2451
|
+
...gatingRequiredValues,
|
|
2452
|
+
],
|
|
2453
|
+
impact: 'low',
|
|
2454
|
+
exclusiveGroup,
|
|
2455
|
+
sourceLocation: jsxUsage.sourceLocation
|
|
2456
|
+
? {
|
|
2457
|
+
lineNumber: jsxUsage.sourceLocation.lineNumber,
|
|
2458
|
+
column: jsxUsage.sourceLocation.column,
|
|
2459
|
+
}
|
|
2460
|
+
: undefined,
|
|
2461
|
+
codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
|
|
2462
|
+
};
|
|
2463
|
+
|
|
2464
|
+
if (!seenFlowIds.has(manyFlow.id)) {
|
|
2465
|
+
seenFlowIds.add(manyFlow.id);
|
|
2466
|
+
flows.push(manyFlow);
|
|
2467
|
+
}
|
|
1012
2468
|
}
|
|
1013
2469
|
}
|
|
1014
2470
|
}
|
|
1015
2471
|
}
|
|
1016
2472
|
}
|
|
1017
2473
|
|
|
2474
|
+
console.log(
|
|
2475
|
+
`[genFlowsFromConditionals] RESULT: ${flows.length} total flows generated`,
|
|
2476
|
+
);
|
|
2477
|
+
for (const flow of flows) {
|
|
2478
|
+
console.log(
|
|
2479
|
+
`[genFlowsFromConditionals] FLOW: id="${flow.id}" requiredValues=[${flow.requiredValues.map((rv) => `${rv.attributePath}=${rv.value}`).join(', ')}]`,
|
|
2480
|
+
);
|
|
2481
|
+
}
|
|
2482
|
+
|
|
1018
2483
|
return flows;
|
|
1019
2484
|
}
|