@codeyam/codeyam-cli 0.1.7 → 0.1.9
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 +9 -9
- package/analyzer-template/packages/ai/package.json +1 -1
- package/analyzer-template/packages/ai/src/lib/astScopes/astScopeAnalyzer.ts +34 -3
- package/analyzer-template/packages/ai/src/lib/completionCall.ts +14 -2
- package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +27 -0
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/coercePrimitivesToArraysBySchema.ts +62 -0
- package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +78 -2
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +0 -33
- package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +19 -7
- package/analyzer-template/packages/analyze/src/lib/asts/index.ts +7 -2
- package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +9 -1
- package/analyzer-template/packages/analyze/src/lib/files/analyze/dependencyResolver.ts +0 -6
- package/analyzer-template/packages/analyze/src/lib/files/analyze/findOrCreateEntity.ts +12 -0
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/TransformationTracer.ts +65 -28
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +83 -0
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +0 -98
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +23 -4
- package/analyzer-template/packages/aws/package.json +2 -2
- package/analyzer-template/packages/database/index.ts +1 -0
- package/analyzer-template/packages/database/package.json +3 -3
- package/analyzer-template/packages/database/src/lib/kysely/db.ts +8 -0
- package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +93 -0
- package/analyzer-template/packages/database/src/lib/loadCommits.ts +31 -20
- package/analyzer-template/packages/database/src/lib/loadEntities.ts +0 -6
- package/analyzer-template/packages/database/src/lib/loadReadyToBeCapturedAnalyses.ts +0 -5
- package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +94 -143
- package/analyzer-template/packages/database/src/lib/updateFreshAnalysisStatus.ts +58 -42
- package/analyzer-template/packages/database/src/lib/updateFreshAnalysisStatusWithScenarios.ts +81 -65
- package/analyzer-template/packages/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.ts +29 -1
- package/analyzer-template/packages/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.ts +33 -5
- package/analyzer-template/packages/github/dist/database/index.d.ts +1 -0
- package/analyzer-template/packages/github/dist/database/index.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/index.js +1 -0
- package/analyzer-template/packages/github/dist/database/index.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 +5 -0
- 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/editorScenariosTable.d.ts +25 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +76 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/scenariosTable.d.ts +5 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/scenariosTable.d.ts.map +1 -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 +23 -13
- 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.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js +0 -6
- package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadReadyToBeCapturedAnalyses.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadReadyToBeCapturedAnalyses.js +1 -4
- package/analyzer-template/packages/github/dist/database/src/lib/loadReadyToBeCapturedAnalyses.js.map +1 -1
- 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 +76 -90
- package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/updateFreshAnalysisStatus.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/updateFreshAnalysisStatus.js +41 -30
- package/analyzer-template/packages/github/dist/database/src/lib/updateFreshAnalysisStatus.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/updateFreshAnalysisStatusWithScenarios.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/updateFreshAnalysisStatusWithScenarios.js +68 -57
- package/analyzer-template/packages/github/dist/database/src/lib/updateFreshAnalysisStatusWithScenarios.js.map +1 -1
- package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.js +29 -1
- package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.js.map +1 -1
- package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.js +33 -5
- package/analyzer-template/packages/github/dist/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.js.map +1 -1
- package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.d.ts +2 -0
- package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.js +2 -0
- package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.js.map +1 -1
- package/analyzer-template/packages/github/dist/types/src/types/ProjectMetadata.d.ts +1 -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/Scenario.d.ts +10 -0
- package/analyzer-template/packages/github/dist/types/src/types/Scenario.d.ts.map +1 -1
- package/analyzer-template/packages/github/package.json +1 -1
- package/analyzer-template/packages/types/src/enums/ProjectFramework.ts +2 -0
- package/analyzer-template/packages/types/src/types/ProjectMetadata.ts +1 -0
- package/analyzer-template/packages/types/src/types/Scenario.ts +10 -0
- package/analyzer-template/packages/ui-components/package.json +1 -1
- package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.d.ts +2 -0
- package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.d.ts.map +1 -1
- package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.js +2 -0
- package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.js.map +1 -1
- package/analyzer-template/packages/utils/dist/types/src/types/ProjectMetadata.d.ts +1 -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/Scenario.d.ts +10 -0
- package/analyzer-template/packages/utils/dist/types/src/types/Scenario.d.ts.map +1 -1
- package/analyzer-template/playwright/captureFromUrl.ts +89 -82
- package/analyzer-template/project/constructMockCode.ts +136 -43
- package/analyzer-template/project/reconcileMockDataKeys.ts +19 -14
- package/analyzer-template/project/start.ts +3 -0
- package/analyzer-template/project/startScenarioCapture.ts +9 -0
- package/analyzer-template/project/writeClientLogRoute.ts +125 -0
- package/analyzer-template/project/writeMockDataTsx.ts +17 -0
- package/analyzer-template/project/writeScenarioComponents.ts +36 -7
- package/analyzer-template/tsconfig.json +13 -1
- package/background/src/lib/virtualized/project/constructMockCode.js +115 -34
- package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
- package/background/src/lib/virtualized/project/reconcileMockDataKeys.js +17 -11
- package/background/src/lib/virtualized/project/reconcileMockDataKeys.js.map +1 -1
- package/background/src/lib/virtualized/project/start.js +2 -0
- package/background/src/lib/virtualized/project/start.js.map +1 -1
- package/background/src/lib/virtualized/project/startScenarioCapture.js +5 -0
- package/background/src/lib/virtualized/project/startScenarioCapture.js.map +1 -1
- package/background/src/lib/virtualized/project/writeClientLogRoute.js +110 -0
- package/background/src/lib/virtualized/project/writeClientLogRoute.js.map +1 -0
- package/background/src/lib/virtualized/project/writeMockDataTsx.js +12 -0
- package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
- package/background/src/lib/virtualized/project/writeScenarioComponents.js +29 -7
- package/background/src/lib/virtualized/project/writeScenarioComponents.js.map +1 -1
- package/codeyam-cli/scripts/apply-setup.js +208 -11
- package/codeyam-cli/scripts/apply-setup.js.map +1 -1
- package/codeyam-cli/src/__tests__/memory-scripts/filter-session.test.js +196 -0
- package/codeyam-cli/src/__tests__/memory-scripts/filter-session.test.js.map +1 -0
- package/codeyam-cli/src/__tests__/memory-scripts/read-json-field.test.js +114 -0
- package/codeyam-cli/src/__tests__/memory-scripts/read-json-field.test.js.map +1 -0
- package/codeyam-cli/src/__tests__/memory-scripts/ripgrep-fallback.test.js +149 -0
- package/codeyam-cli/src/__tests__/memory-scripts/ripgrep-fallback.test.js.map +1 -0
- package/codeyam-cli/src/cli.js +2 -0
- package/codeyam-cli/src/cli.js.map +1 -1
- package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +45 -0
- package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -0
- package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +101 -47
- package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
- package/codeyam-cli/src/commands/analyze.js +17 -7
- package/codeyam-cli/src/commands/analyze.js.map +1 -1
- package/codeyam-cli/src/commands/default.js +14 -2
- package/codeyam-cli/src/commands/default.js.map +1 -1
- package/codeyam-cli/src/commands/editor.js +3215 -0
- package/codeyam-cli/src/commands/editor.js.map +1 -0
- package/codeyam-cli/src/commands/init.js +107 -45
- package/codeyam-cli/src/commands/init.js.map +1 -1
- package/codeyam-cli/src/data/techStacks.js +77 -0
- package/codeyam-cli/src/data/techStacks.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js +144 -0
- package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/backgroundServer.test.js +46 -0
- package/codeyam-cli/src/utils/__tests__/backgroundServer.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/devServerState.test.js +134 -0
- package/codeyam-cli/src/utils/__tests__/devServerState.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js +127 -0
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +855 -0
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorCapture.test.js +93 -0
- package/codeyam-cli/src/utils/__tests__/editorCapture.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js +304 -0
- package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +121 -0
- package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorImageVerifier.test.js +294 -0
- package/codeyam-cli/src/utils/__tests__/editorImageVerifier.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js +542 -0
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +520 -0
- package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorMockState.test.js +270 -0
- package/codeyam-cli/src/utils/__tests__/editorMockState.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js +217 -0
- package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +339 -0
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +153 -0
- package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioLookup.test.js +139 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioLookup.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +221 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +855 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +213 -0
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +1742 -0
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/git.editor.test.js +134 -0
- package/codeyam-cli/src/utils/__tests__/git.editor.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js +107 -0
- package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js +101 -0
- package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/pathIgnoring.test.js +9 -0
- package/codeyam-cli/src/utils/__tests__/pathIgnoring.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/project.test.js +65 -0
- package/codeyam-cli/src/utils/__tests__/project.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +227 -0
- package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js +121 -0
- package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +300 -0
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +50 -4
- package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/templateConsistency.test.js +51 -0
- package/codeyam-cli/src/utils/__tests__/templateConsistency.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/webappDetection.test.js +142 -0
- package/codeyam-cli/src/utils/__tests__/webappDetection.test.js.map +1 -0
- package/codeyam-cli/src/utils/analyzer.js +9 -0
- package/codeyam-cli/src/utils/analyzer.js.map +1 -1
- package/codeyam-cli/src/utils/analyzerFinalization.js +96 -0
- package/codeyam-cli/src/utils/analyzerFinalization.js.map +1 -0
- package/codeyam-cli/src/utils/backgroundServer.js +104 -12
- package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/utils/buildFlags.js +4 -0
- package/codeyam-cli/src/utils/buildFlags.js.map +1 -0
- package/codeyam-cli/src/utils/database.js +37 -2
- package/codeyam-cli/src/utils/database.js.map +1 -1
- package/codeyam-cli/src/utils/devModeEvents.js +40 -0
- package/codeyam-cli/src/utils/devModeEvents.js.map +1 -0
- package/codeyam-cli/src/utils/devServerState.js +71 -0
- package/codeyam-cli/src/utils/devServerState.js.map +1 -0
- package/codeyam-cli/src/utils/editorApi.js +73 -0
- package/codeyam-cli/src/utils/editorApi.js.map +1 -0
- package/codeyam-cli/src/utils/editorAudit.js +176 -0
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -0
- package/codeyam-cli/src/utils/editorCapture.js +102 -0
- package/codeyam-cli/src/utils/editorCapture.js.map +1 -0
- package/codeyam-cli/src/utils/editorDevServer.js +197 -0
- package/codeyam-cli/src/utils/editorDevServer.js.map +1 -0
- package/codeyam-cli/src/utils/editorEntityChangeStatus.js +44 -0
- package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -0
- package/codeyam-cli/src/utils/editorImageVerifier.js +155 -0
- package/codeyam-cli/src/utils/editorImageVerifier.js.map +1 -0
- package/codeyam-cli/src/utils/editorJournal.js +225 -0
- package/codeyam-cli/src/utils/editorJournal.js.map +1 -0
- package/codeyam-cli/src/utils/editorLoaderHelpers.js +113 -0
- package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -0
- package/codeyam-cli/src/utils/editorMockState.js +248 -0
- package/codeyam-cli/src/utils/editorMockState.js.map +1 -0
- package/codeyam-cli/src/utils/editorPreloadHelpers.js +135 -0
- package/codeyam-cli/src/utils/editorPreloadHelpers.js.map +1 -0
- package/codeyam-cli/src/utils/editorPreview.js +132 -0
- package/codeyam-cli/src/utils/editorPreview.js.map +1 -0
- package/codeyam-cli/src/utils/editorScenarioSwitch.js +112 -0
- package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -0
- package/codeyam-cli/src/utils/editorScenarios.js +332 -0
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -0
- package/codeyam-cli/src/utils/editorSeedAdapter.js +173 -0
- package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -0
- package/codeyam-cli/src/utils/entityChangeStatus.js +349 -0
- package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -0
- package/codeyam-cli/src/utils/entityChangeStatus.server.js +158 -0
- package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -0
- package/codeyam-cli/src/utils/fileMetadata.js +5 -0
- package/codeyam-cli/src/utils/fileMetadata.js.map +1 -1
- package/codeyam-cli/src/utils/fileWatcher.js +25 -9
- package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
- package/codeyam-cli/src/utils/git.js +103 -0
- package/codeyam-cli/src/utils/git.js.map +1 -1
- package/codeyam-cli/src/utils/install-skills.js +55 -13
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/interactiveSyncWatcher.js +126 -0
- package/codeyam-cli/src/utils/interactiveSyncWatcher.js.map +1 -0
- package/codeyam-cli/src/utils/parseRegisterArg.js +31 -0
- package/codeyam-cli/src/utils/parseRegisterArg.js.map +1 -0
- package/codeyam-cli/src/utils/pathIgnoring.js +19 -7
- package/codeyam-cli/src/utils/pathIgnoring.js.map +1 -1
- package/codeyam-cli/src/utils/project.js +15 -5
- package/codeyam-cli/src/utils/project.js.map +1 -1
- package/codeyam-cli/src/utils/queue/__tests__/heartbeat.test.js +11 -11
- package/codeyam-cli/src/utils/queue/__tests__/heartbeat.test.js.map +1 -1
- package/codeyam-cli/src/utils/queue/__tests__/manager.test.js +22 -0
- package/codeyam-cli/src/utils/queue/__tests__/manager.test.js.map +1 -1
- package/codeyam-cli/src/utils/queue/heartbeat.js +13 -5
- package/codeyam-cli/src/utils/queue/heartbeat.js.map +1 -1
- package/codeyam-cli/src/utils/queue/job.js +70 -1
- package/codeyam-cli/src/utils/queue/job.js.map +1 -1
- package/codeyam-cli/src/utils/queue/manager.js +7 -6
- package/codeyam-cli/src/utils/queue/manager.js.map +1 -1
- package/codeyam-cli/src/utils/scenarioCoverage.js +75 -0
- package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -0
- package/codeyam-cli/src/utils/scenarioMarkers.js +134 -0
- package/codeyam-cli/src/utils/scenarioMarkers.js.map +1 -0
- package/codeyam-cli/src/utils/scenariosManifest.js +159 -0
- package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -0
- package/codeyam-cli/src/utils/serverState.js +57 -2
- package/codeyam-cli/src/utils/serverState.js.map +1 -1
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +82 -11
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
- package/codeyam-cli/src/utils/simulationGateMiddleware.js +8 -1
- package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
- package/codeyam-cli/src/utils/slugUtils.js +25 -0
- package/codeyam-cli/src/utils/slugUtils.js.map +1 -0
- package/codeyam-cli/src/utils/syncMocksMiddleware.js +2 -2
- package/codeyam-cli/src/utils/syncMocksMiddleware.js.map +1 -1
- package/codeyam-cli/src/utils/testRunner.js +158 -0
- package/codeyam-cli/src/utils/testRunner.js.map +1 -0
- package/codeyam-cli/src/utils/webappDetection.js +35 -2
- package/codeyam-cli/src/utils/webappDetection.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +40 -0
- package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -0
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +567 -0
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -0
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js +65 -0
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -0
- package/codeyam-cli/src/webserver/app/lib/database.js +41 -27
- 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/app/lib/git.js +397 -0
- package/codeyam-cli/src/webserver/app/lib/git.js.map +1 -0
- package/codeyam-cli/src/webserver/backgroundServer.js +108 -18
- package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{CopyButton-CtmbP4Gl.js → CopyButton-BPXZwM4t.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-DlMph_Hm.js → EntityItem-BcgbViKV.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeBadge-B-0PjGOU.js → EntityTypeBadge-g3saevPb.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-DN9eiJAO.js → EntityTypeIcon-CQIG2qda.js} +9 -9
- package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-Bu6c6aDe.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-rE_fI2h2.js → InteractivePreview-DYFW3lDD.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-CnatsCw2.js → LibraryFunctionPreview-DLeucoVX.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-CSP6DZrh.js → LoadingDots-BU_OAEMP.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-CMK8Q7yk.js → LogViewer-ceAyBX-H.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-TCV_HBjy.js → ReportIssueModal-BzHcG7SE.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-CG2uh31y.js → SafeScreenshot-BED4B6sP.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-CU_TDYd8.js → ScenarioViewer-Bd-hxofb.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bb5uFQ5V.js +34 -0
- package/codeyam-cli/src/webserver/build/client/assets/{TruncatedFilePath-D7IoaWUW.js → TruncatedFilePath-C8OKAR5x.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{_index-B8z7mjR-.js → _index-DLxKhri3.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-DZu78RI1.js → activity.(_tab)-BcY3q6nt.js} +6 -6
- package/codeyam-cli/src/webserver/build/client/assets/addon-canvas-DpzMmAy5.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/addon-fit-YJmn1quW.js +12 -0
- package/codeyam-cli/src/webserver/build/client/assets/addon-web-links-Duc5hnl7.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/addon-webgl-DI8QOUvO.js +58 -0
- package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Dm5RS9il.js → agent-transcripts-Bni3iiUj.js} +5 -5
- package/codeyam-cli/src/webserver/build/client/assets/api.dev-mode-events-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-audit-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-capture-scenario-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-client-errors-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-commit-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-dev-server-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-entity-status-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-file-diff-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-file-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-journal-entry-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-journal-image._-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-journal-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-journal-screenshot-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-journal-update-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-load-commit-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-project-info-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-refresh-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-register-scenario-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-coverage-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-data-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-image._-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenarios-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-switch-scenario-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-test-results-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{book-open-Bp5FLkd4.js → book-open-BYOypzCa.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-DQJA9f4o.js → chevron-down-C_Pmso5S.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{chunk-JZWAC4HX-7VptmeIr.js → chunk-JZWAC4HX-C4pqxYJB.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{circle-check-B6C4LY9o.js → circle-check-BVMi9VA5.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{copy-6nzYCu0G.js → copy-n2FB0_Sw.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/createLucideIcon-CC6AbExI.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/dev.empty-BsDh6TSF.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor-PBc_6L9R.js +10 -0
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-4FzHlcNn.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-C6PQhwY5.js → entity._sha._-BsDXNp45.js} +9 -9
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-BgAqUtTZ.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-Bmshgrij.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-p9hhkjJM.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-C7ysA4Jq.js → entity._sha_.edit._scenarioId-BMvVHNXU.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{entry.client-CU6EUArK.js → entry.client-DTvKq3TY.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{fileTableUtils-EWpfFU4X.js → fileTableUtils-cPo8LiG3.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{files-CrxAoWIL.js → files-BZrlFE1F.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/globals-B8vTTNy2.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{index-7-1FmlHo.js → index-10oVnAAH.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-DuYcwYp_.js → index-BcvgDzbZ.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/index-yHOVb4rc.js +15 -0
- package/codeyam-cli/src/webserver/build/client/assets/{labs-CPPVOSWB.js → labs-Zk7ryIM1.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-BnDcD54R.js → loader-circle-DaAZ_H2w.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/manifest-65850841.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/memory-9gnxSZlb.js +101 -0
- package/codeyam-cli/src/webserver/build/client/assets/{pause-DhQX2g22.js → pause-f5-1lKBt.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/root-BwX8YgFb.js +67 -0
- package/codeyam-cli/src/webserver/build/client/assets/{search-DborVoKD.js → search-Di64LWVb.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{settings-BWunYSXt.js → settings-0OrEMU6J.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{simulations-BtrtCYJg.js → simulations-DWT-CvLy.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{terminal-Bs4NC-VZ.js → terminal-Br7MOqts.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-DTf3Jojp.js → triangle-alert-BLdiCuG-.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-BE43Hjti.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-C14nCb1q.js +2 -0
- package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-BsQb6rFd.js → useReportContext-O-jkvSPx.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useToast-BOur3mUv.js → useToast-9FIWuYfK.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/xterm-BqvuqXEL.js +27 -0
- package/codeyam-cli/src/webserver/build/server/assets/index-DEEQf4pi.js +1 -0
- package/codeyam-cli/src/webserver/build/server/assets/init-CkWmyFY2.js +10 -0
- package/codeyam-cli/src/webserver/build/server/assets/server-build-BHi-9O8W.js +439 -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/src/webserver/devServer.js +39 -5
- package/codeyam-cli/src/webserver/devServer.js.map +1 -1
- package/codeyam-cli/src/webserver/editorProxy.js +877 -0
- package/codeyam-cli/src/webserver/editorProxy.js.map +1 -0
- package/codeyam-cli/src/webserver/scripts/codeyam-preload.mjs +414 -0
- package/codeyam-cli/src/webserver/scripts/journalCapture.ts +230 -0
- package/codeyam-cli/src/webserver/server.js +258 -1
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +726 -0
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -0
- package/codeyam-cli/templates/chrome-extension-react/EXTENSION_SETUP.md +75 -0
- package/codeyam-cli/templates/chrome-extension-react/README.md +46 -0
- package/codeyam-cli/templates/chrome-extension-react/gitignore +15 -0
- package/codeyam-cli/templates/chrome-extension-react/index.html +12 -0
- package/codeyam-cli/templates/chrome-extension-react/package.json +27 -0
- package/codeyam-cli/templates/chrome-extension-react/popup.html +12 -0
- package/codeyam-cli/templates/chrome-extension-react/public/manifest.json +15 -0
- package/codeyam-cli/templates/chrome-extension-react/src/background/service-worker.ts +7 -0
- package/codeyam-cli/templates/chrome-extension-react/src/globals.css +6 -0
- package/codeyam-cli/templates/chrome-extension-react/src/lib/storage.ts +37 -0
- package/codeyam-cli/templates/chrome-extension-react/src/popup/App.tsx +12 -0
- package/codeyam-cli/templates/chrome-extension-react/src/popup/main.tsx +10 -0
- package/codeyam-cli/templates/chrome-extension-react/tsconfig.json +24 -0
- package/codeyam-cli/templates/chrome-extension-react/vite.config.ts +41 -0
- package/codeyam-cli/templates/codeyam-editor-claude.md +147 -0
- package/codeyam-cli/templates/editor-step-hook.py +236 -0
- package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +89 -0
- package/codeyam-cli/templates/expo-react-native/README.md +41 -0
- package/codeyam-cli/templates/expo-react-native/app/(tabs)/_layout.tsx +33 -0
- package/codeyam-cli/templates/expo-react-native/app/(tabs)/index.tsx +12 -0
- package/codeyam-cli/templates/expo-react-native/app/(tabs)/settings.tsx +12 -0
- package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +12 -0
- package/codeyam-cli/templates/expo-react-native/app.json +18 -0
- package/codeyam-cli/templates/expo-react-native/babel.config.js +9 -0
- package/codeyam-cli/templates/expo-react-native/gitignore +12 -0
- package/codeyam-cli/templates/expo-react-native/global.css +3 -0
- package/codeyam-cli/templates/expo-react-native/lib/storage.ts +32 -0
- package/codeyam-cli/templates/expo-react-native/metro.config.js +6 -0
- package/codeyam-cli/templates/expo-react-native/nativewind-env.d.ts +1 -0
- package/codeyam-cli/templates/expo-react-native/package.json +38 -0
- package/codeyam-cli/templates/expo-react-native/tailwind.config.js +10 -0
- package/codeyam-cli/templates/expo-react-native/tsconfig.json +10 -0
- package/codeyam-cli/templates/isolation-route/next-app.tsx.template +80 -0
- package/codeyam-cli/templates/isolation-route/next-pages.tsx.template +79 -0
- package/codeyam-cli/templates/isolation-route/vite-react.tsx.template +78 -0
- package/codeyam-cli/templates/msw/browser-setup.ts.template +47 -0
- package/codeyam-cli/templates/msw/handler-router.ts.template +47 -0
- package/codeyam-cli/templates/msw/server-setup.ts.template +52 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/AUTH_PATTERNS.md +308 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/AUTH_UPGRADE.md +304 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/DATABASE.md +126 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/FEATURE_PATTERNS.md +37 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/README.md +53 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/app/api/todos/route.ts +17 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/app/codeyam-isolate/layout.tsx +12 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/app/globals.css +26 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/app/layout.tsx +34 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/app/lib/prisma.ts +24 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/app/page.tsx +10 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/env +4 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/eslint.config.mjs +11 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/gitignore +64 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/next.config.ts +14 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +39 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/postcss.config.mjs +7 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/prisma/schema.prisma +27 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/prisma/seed.ts +40 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/prisma.config.ts +12 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +92 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/tsconfig.json +34 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/vitest.config.ts +13 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/README.md +52 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/SUPABASE_SETUP.md +104 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/api/todos/route.ts +17 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/globals.css +26 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/layout.tsx +34 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/lib/prisma.ts +20 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/lib/supabase.ts +12 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/page.tsx +10 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/env +9 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/eslint.config.mjs +11 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/gitignore +40 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/next.config.ts +11 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +37 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/postcss.config.mjs +7 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/prisma/schema.prisma +27 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/prisma/seed.ts +39 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/prisma.config.ts +12 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/tsconfig.json +34 -0
- package/codeyam-cli/templates/skills/codeyam-dev-mode/SKILL.md +237 -0
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +148 -0
- package/codeyam-cli/templates/{codeyam-memory.md → skills/codeyam-memory/SKILL.md} +215 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/deprecated-prompt.md +100 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/detect-deprecated-patterns.mjs +139 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/find-exports.mjs +52 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/misleading-api-prompt.md +117 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/lib/read-json-field.mjs +61 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/lib/ripgrep-fallback.mjs +155 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/analyze-prompt.md +46 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/cleanup.mjs +13 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/filter-session.mjs +95 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/preprocess.mjs +160 -0
- package/package.json +17 -10
- package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js +22 -4
- package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js.map +1 -1
- package/packages/ai/src/lib/completionCall.js +10 -2
- package/packages/ai/src/lib/completionCall.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +21 -0
- package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/coercePrimitivesToArraysBySchema.js +54 -0
- package/packages/ai/src/lib/dataStructure/helpers/coercePrimitivesToArraysBySchema.js.map +1 -0
- package/packages/ai/src/lib/dataStructure/helpers/stripNullableMarkers.js +34 -0
- package/packages/ai/src/lib/dataStructure/helpers/stripNullableMarkers.js.map +1 -0
- package/packages/ai/src/lib/generateEntityScenarioData.js +57 -2
- package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
- package/packages/ai/src/lib/generateExecutionFlows.js +0 -11
- package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
- package/packages/analyze/src/lib/ProjectAnalyzer.js +13 -4
- package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
- package/packages/analyze/src/lib/asts/index.js +4 -2
- package/packages/analyze/src/lib/asts/index.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +8 -1
- package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/dependencyResolver.js +0 -5
- package/packages/analyze/src/lib/files/analyze/dependencyResolver.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js +9 -0
- package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/TransformationTracer.js +54 -27
- package/packages/analyze/src/lib/files/scenarios/TransformationTracer.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +65 -0
- package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +0 -40
- package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +18 -4
- package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
- package/packages/database/index.js +1 -0
- package/packages/database/index.js.map +1 -1
- package/packages/database/src/lib/kysely/db.js +5 -0
- package/packages/database/src/lib/kysely/db.js.map +1 -1
- package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +76 -0
- package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -0
- package/packages/database/src/lib/loadCommits.js +23 -13
- package/packages/database/src/lib/loadCommits.js.map +1 -1
- package/packages/database/src/lib/loadEntities.js +0 -6
- package/packages/database/src/lib/loadEntities.js.map +1 -1
- package/packages/database/src/lib/loadReadyToBeCapturedAnalyses.js +1 -4
- package/packages/database/src/lib/loadReadyToBeCapturedAnalyses.js.map +1 -1
- package/packages/database/src/lib/updateCommitMetadata.js +76 -90
- package/packages/database/src/lib/updateCommitMetadata.js.map +1 -1
- package/packages/database/src/lib/updateFreshAnalysisStatus.js +41 -30
- package/packages/database/src/lib/updateFreshAnalysisStatus.js.map +1 -1
- package/packages/database/src/lib/updateFreshAnalysisStatusWithScenarios.js +68 -57
- package/packages/database/src/lib/updateFreshAnalysisStatusWithScenarios.js.map +1 -1
- package/packages/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.js +29 -1
- package/packages/generate/src/lib/componentScenarioPage/generateScenarioClientWrapper.js.map +1 -1
- package/packages/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.js +33 -5
- package/packages/generate/src/lib/componentScenarioPage/getIFrameMessageListenerCode.js.map +1 -1
- package/packages/types/src/enums/ProjectFramework.js +2 -0
- package/packages/types/src/enums/ProjectFramework.js.map +1 -1
- package/scripts/npm-post-install.cjs +34 -0
- package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-C1rIyZdV.js +0 -34
- package/codeyam-cli/src/webserver/build/client/assets/createLucideIcon-D-QUFOwe.js +0 -21
- package/codeyam-cli/src/webserver/build/client/assets/dev.empty-DmzSmblj.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-DVTcUnur.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-BVgNO76F.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/git-BldHtKeW.js +0 -15
- package/codeyam-cli/src/webserver/build/client/assets/globals-CLmFdUae.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-717e346a.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/memory-0wMU4KXe.js +0 -93
- package/codeyam-cli/src/webserver/build/client/assets/root-DqfSDjyQ.js +0 -62
- package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-D_bDZyDU.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-DZp6rrQD.js +0 -2
- package/codeyam-cli/src/webserver/build/server/assets/index-B8jmgmn2.js +0 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-9OU4lmvL.js +0 -285
- package/scripts/finalize-analyzer.cjs +0 -13
- /package/codeyam-cli/templates/{codeyam-diagnose.md → commands/codeyam-diagnose.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-debug.md → skills/codeyam-debug/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-new-rule.md → skills/codeyam-new-rule/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-setup.md → skills/codeyam-setup/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-sim.md → skills/codeyam-sim/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-test.md → skills/codeyam-test/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-verify.md → skills/codeyam-verify/SKILL.md} +0 -0
|
@@ -0,0 +1,855 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import Database from 'better-sqlite3';
|
|
5
|
+
import { Kysely, SqliteDialect } from 'kysely';
|
|
6
|
+
import { deduplicateByName, generateScenarioSlug, convertIsoToSqliteTimestamp, determineCaptureUrl, resolvePreviewNavPath, clearEditorState, clearEditorUserPrompt, readDefaultScreenSize, readScreenSizes, resolveScenarioViewport, resolveViewportWithProjectDefault, upsertEditorScenario, cleanupScenarioFiles, readPreservedConfigProperties, validateStepTransition, } from "../editorScenarios.js";
|
|
7
|
+
describe('editorScenarios', () => {
|
|
8
|
+
describe('deduplicateByName', () => {
|
|
9
|
+
it('should keep only the last item for each key', () => {
|
|
10
|
+
const items = [
|
|
11
|
+
{ name: 'Default', id: '1', value: 'old' },
|
|
12
|
+
{ name: 'Dark Mode', id: '2', value: 'old' },
|
|
13
|
+
{ name: 'Default', id: '3', value: 'new' },
|
|
14
|
+
];
|
|
15
|
+
const result = deduplicateByName(items, (item) => item.name);
|
|
16
|
+
expect(result).toHaveLength(2);
|
|
17
|
+
expect(result.find((r) => r.name === 'Default')?.id).toBe('3');
|
|
18
|
+
expect(result.find((r) => r.name === 'Dark Mode')?.id).toBe('2');
|
|
19
|
+
});
|
|
20
|
+
it('should return empty array for empty input', () => {
|
|
21
|
+
expect(deduplicateByName([], (x) => x.name)).toEqual([]);
|
|
22
|
+
});
|
|
23
|
+
it('should not lose screenshots when re-registered scenario has null screenshot (filter before dedup)', () => {
|
|
24
|
+
// Simulates the journal screenshot lookup flow:
|
|
25
|
+
// Scenarios ordered by created_at ASC from DB, some re-registered with null screenshot
|
|
26
|
+
const scenarios = [
|
|
27
|
+
{
|
|
28
|
+
name: 'Full Catalog',
|
|
29
|
+
screenshot_path: 'screenshots/aaa.png',
|
|
30
|
+
id: 'aaa',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'API Error',
|
|
34
|
+
screenshot_path: 'screenshots/bbb.png',
|
|
35
|
+
id: 'bbb',
|
|
36
|
+
},
|
|
37
|
+
{ name: 'API Error', screenshot_path: null, id: 'ccc' }, // re-registered, capture in progress
|
|
38
|
+
];
|
|
39
|
+
// BUG: dedup-then-filter loses "API Error" entirely because dedup picks
|
|
40
|
+
// the latest row (null screenshot), then filter removes it.
|
|
41
|
+
const buggyDeduped = deduplicateByName(scenarios, (s) => s.name);
|
|
42
|
+
const buggyResult = buggyDeduped.filter((s) => s.screenshot_path);
|
|
43
|
+
// This would only contain "Full Catalog" — "API Error" is gone!
|
|
44
|
+
expect(buggyResult).toHaveLength(1); // demonstrates the bug
|
|
45
|
+
// FIX: filter-then-dedup preserves the latest row that HAS a screenshot.
|
|
46
|
+
const filtered = scenarios.filter((s) => s.screenshot_path);
|
|
47
|
+
const fixedResult = deduplicateByName(filtered, (s) => s.name);
|
|
48
|
+
expect(fixedResult).toHaveLength(2);
|
|
49
|
+
expect(fixedResult.find((s) => s.name === 'API Error')?.id).toBe('bbb');
|
|
50
|
+
expect(fixedResult.find((s) => s.name === 'Full Catalog')?.id).toBe('aaa');
|
|
51
|
+
});
|
|
52
|
+
it('should preserve order of first appearance', () => {
|
|
53
|
+
const items = [
|
|
54
|
+
{ name: 'A', v: 1 },
|
|
55
|
+
{ name: 'B', v: 2 },
|
|
56
|
+
{ name: 'A', v: 3 },
|
|
57
|
+
];
|
|
58
|
+
const result = deduplicateByName(items, (i) => i.name);
|
|
59
|
+
// Map preserves insertion order — A first, then B
|
|
60
|
+
expect(result.map((r) => r.name)).toEqual(['A', 'B']);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('generateScenarioSlug', () => {
|
|
64
|
+
it('should replace non-alphanumeric chars with underscores', () => {
|
|
65
|
+
expect(generateScenarioSlug('Dark Mode')).toBe('Dark_Mode');
|
|
66
|
+
});
|
|
67
|
+
it('should collapse consecutive special characters', () => {
|
|
68
|
+
expect(generateScenarioSlug('Hello -- World!!')).toBe('Hello_World_');
|
|
69
|
+
});
|
|
70
|
+
it('should preserve underscores and alphanumeric characters', () => {
|
|
71
|
+
expect(generateScenarioSlug('Already_Valid123')).toBe('Already_Valid123');
|
|
72
|
+
});
|
|
73
|
+
it('should handle empty string', () => {
|
|
74
|
+
expect(generateScenarioSlug('')).toBe('');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe('convertIsoToSqliteTimestamp', () => {
|
|
78
|
+
it('should convert ISO 8601 to SQLite format', () => {
|
|
79
|
+
expect(convertIsoToSqliteTimestamp('2026-02-28T19:00:00.000Z')).toBe('2026-02-28 19:00:00');
|
|
80
|
+
});
|
|
81
|
+
it('should handle timestamps without milliseconds', () => {
|
|
82
|
+
expect(convertIsoToSqliteTimestamp('2026-02-28T19:00:00Z')).toBe('2026-02-28 19:00:00Z');
|
|
83
|
+
});
|
|
84
|
+
it('should replace T with space', () => {
|
|
85
|
+
expect(convertIsoToSqliteTimestamp('2026-01-15T08:30:45.123Z')).toBe('2026-01-15 08:30:45');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('determineCaptureUrl', () => {
|
|
89
|
+
it('should combine path with proxy URL when path-based and proxy available', () => {
|
|
90
|
+
expect(determineCaptureUrl('/isolated-components/DrinkCard?s=Default', 'http://localhost:3112', 'http://localhost:3000')).toBe('http://localhost:3112/isolated-components/DrinkCard?s=Default');
|
|
91
|
+
});
|
|
92
|
+
it('should work with path field normalized to url (path alias)', () => {
|
|
93
|
+
// When Claude writes "path": "/drinks/1" instead of "url": "/drinks/1",
|
|
94
|
+
// the register endpoint normalizes path → url before calling determineCaptureUrl.
|
|
95
|
+
// This test verifies that determineCaptureUrl handles the resulting value correctly.
|
|
96
|
+
const normalizedUrl = '/drinks/1'; // after normalization: body.url = body.path
|
|
97
|
+
expect(determineCaptureUrl(normalizedUrl, 'http://localhost:3112', 'http://localhost:3000')).toBe('http://localhost:3112/drinks/1');
|
|
98
|
+
});
|
|
99
|
+
it('should use full URL directly when not path-based', () => {
|
|
100
|
+
expect(determineCaptureUrl('http://external.com/page', 'http://localhost:3112', 'http://localhost:3000')).toBe('http://external.com/page');
|
|
101
|
+
});
|
|
102
|
+
it('should fall back to proxy root when no URL provided', () => {
|
|
103
|
+
expect(determineCaptureUrl(null, 'http://localhost:3112', 'http://localhost:3000')).toBe('http://localhost:3112');
|
|
104
|
+
});
|
|
105
|
+
it('should fall back to dev server when no URL and no proxy', () => {
|
|
106
|
+
expect(determineCaptureUrl(null, null, 'http://localhost:3000')).toBe('http://localhost:3000');
|
|
107
|
+
});
|
|
108
|
+
it('should return null when nothing available', () => {
|
|
109
|
+
expect(determineCaptureUrl(null, null, null)).toBeNull();
|
|
110
|
+
});
|
|
111
|
+
it('should fall back to dev server for path when proxy unavailable', () => {
|
|
112
|
+
// Path-based but proxy is null — can't route through proxy
|
|
113
|
+
expect(determineCaptureUrl('/some-path', null, 'http://localhost:3000')).toBe('http://localhost:3000');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe('clearEditorState', () => {
|
|
117
|
+
let tmpDir;
|
|
118
|
+
let codeyamDir;
|
|
119
|
+
beforeEach(() => {
|
|
120
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'editor-state-'));
|
|
121
|
+
codeyamDir = path.join(tmpDir, '.codeyam');
|
|
122
|
+
fs.mkdirSync(codeyamDir, { recursive: true });
|
|
123
|
+
});
|
|
124
|
+
afterEach(() => {
|
|
125
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
126
|
+
});
|
|
127
|
+
it('should delete the editor-step.json state file', () => {
|
|
128
|
+
fs.writeFileSync(path.join(codeyamDir, 'editor-step.json'), JSON.stringify({ step: 5, label: 'Extract' }));
|
|
129
|
+
clearEditorState(tmpDir);
|
|
130
|
+
expect(fs.existsSync(path.join(codeyamDir, 'editor-step.json'))).toBe(false);
|
|
131
|
+
});
|
|
132
|
+
it('should preserve editor-user-prompt.txt (captured by hook before step 1)', () => {
|
|
133
|
+
// The hook captures the user's feature request BEFORE step 1 calls clearEditorState.
|
|
134
|
+
// If clearEditorState deletes the prompt file, the next unrelated user message
|
|
135
|
+
// (e.g. "ok" approving a database reset) gets captured as the feature prompt.
|
|
136
|
+
fs.writeFileSync(path.join(codeyamDir, 'editor-step.json'), JSON.stringify({ step: 13, label: 'Present' }));
|
|
137
|
+
fs.writeFileSync(path.join(codeyamDir, 'editor-user-prompt.txt'), 'Could we add a page for each individual drink?');
|
|
138
|
+
clearEditorState(tmpDir);
|
|
139
|
+
expect(fs.readFileSync(path.join(codeyamDir, 'editor-user-prompt.txt'), 'utf8')).toBe('Could we add a page for each individual drink?');
|
|
140
|
+
});
|
|
141
|
+
it('should not throw if state file does not exist', () => {
|
|
142
|
+
expect(() => clearEditorState(tmpDir)).not.toThrow();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
describe('clearEditorUserPrompt', () => {
|
|
146
|
+
let tmpDir;
|
|
147
|
+
let codeyamDir;
|
|
148
|
+
beforeEach(() => {
|
|
149
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'editor-prompt-'));
|
|
150
|
+
codeyamDir = path.join(tmpDir, '.codeyam');
|
|
151
|
+
fs.mkdirSync(codeyamDir, { recursive: true });
|
|
152
|
+
});
|
|
153
|
+
afterEach(() => {
|
|
154
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
155
|
+
});
|
|
156
|
+
it('should delete the prompt file', () => {
|
|
157
|
+
fs.writeFileSync(path.join(codeyamDir, 'editor-user-prompt.txt'), 'old feature prompt');
|
|
158
|
+
clearEditorUserPrompt(tmpDir);
|
|
159
|
+
expect(fs.existsSync(path.join(codeyamDir, 'editor-user-prompt.txt'))).toBe(false);
|
|
160
|
+
});
|
|
161
|
+
it('should not throw if file does not exist', () => {
|
|
162
|
+
expect(() => clearEditorUserPrompt(tmpDir)).not.toThrow();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe('resolvePreviewNavPath', () => {
|
|
166
|
+
it('should use explicit path when provided', () => {
|
|
167
|
+
expect(resolvePreviewNavPath('/explicit', '/scenario-url')).toBe('/explicit');
|
|
168
|
+
});
|
|
169
|
+
it('should fall back to scenario URL when no explicit path', () => {
|
|
170
|
+
expect(resolvePreviewNavPath(null, '/drinks/1')).toBe('/drinks/1');
|
|
171
|
+
});
|
|
172
|
+
it('should return undefined when neither path nor URL provided', () => {
|
|
173
|
+
expect(resolvePreviewNavPath(null, null)).toBeUndefined();
|
|
174
|
+
});
|
|
175
|
+
it('should return undefined for empty strings', () => {
|
|
176
|
+
expect(resolvePreviewNavPath('', '')).toBeUndefined();
|
|
177
|
+
});
|
|
178
|
+
it('should prefer explicit path over scenario URL', () => {
|
|
179
|
+
expect(resolvePreviewNavPath('/', '/drinks/1')).toBe('/');
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
describe('register endpoint path→url normalization (logic verification)', () => {
|
|
183
|
+
// These tests verify the normalization logic used in api.editor-register-scenario.ts:
|
|
184
|
+
// body.url = body.url || body.path || undefined;
|
|
185
|
+
// The route can't be unit-tested directly (ESM import.meta), so we verify
|
|
186
|
+
// the normalization + downstream logic here.
|
|
187
|
+
function normalizeUrl(body) {
|
|
188
|
+
// Mirror the exact line from api.editor-register-scenario.ts line 152
|
|
189
|
+
return body.url || body.path || undefined;
|
|
190
|
+
}
|
|
191
|
+
it('should use url when provided', () => {
|
|
192
|
+
expect(normalizeUrl({ url: '/drinks/1' })).toBe('/drinks/1');
|
|
193
|
+
});
|
|
194
|
+
it('should fall back to path when url not provided', () => {
|
|
195
|
+
expect(normalizeUrl({ path: '/drinks/1' })).toBe('/drinks/1');
|
|
196
|
+
});
|
|
197
|
+
it('should prefer url over path', () => {
|
|
198
|
+
expect(normalizeUrl({ url: '/explicit', path: '/fallback' })).toBe('/explicit');
|
|
199
|
+
});
|
|
200
|
+
it('should return undefined when neither provided', () => {
|
|
201
|
+
expect(normalizeUrl({})).toBeUndefined();
|
|
202
|
+
});
|
|
203
|
+
it('should work end-to-end: path → normalize → determineCaptureUrl', () => {
|
|
204
|
+
// Full pipeline: Claude writes "path": "/drinks/1"
|
|
205
|
+
// → register normalizes to url: "/drinks/1"
|
|
206
|
+
// → determineCaptureUrl combines with proxy
|
|
207
|
+
const body = { path: '/drinks/1' };
|
|
208
|
+
const normalizedUrl = normalizeUrl(body);
|
|
209
|
+
const captureUrl = determineCaptureUrl(normalizedUrl || null, 'http://localhost:4100', 'http://localhost:3000');
|
|
210
|
+
expect(captureUrl).toBe('http://localhost:4100/drinks/1');
|
|
211
|
+
});
|
|
212
|
+
it('should work end-to-end: no url → normalize → proxy root', () => {
|
|
213
|
+
// Claude omits both url and path
|
|
214
|
+
// → normalize returns undefined
|
|
215
|
+
// → determineCaptureUrl falls back to proxy root
|
|
216
|
+
const body = {};
|
|
217
|
+
const normalizedUrl = normalizeUrl(body);
|
|
218
|
+
const captureUrl = determineCaptureUrl(normalizedUrl || null, 'http://localhost:4100', 'http://localhost:3000');
|
|
219
|
+
expect(captureUrl).toBe('http://localhost:4100');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
describe('readDefaultScreenSize', () => {
|
|
223
|
+
let tmpDir;
|
|
224
|
+
beforeEach(() => {
|
|
225
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'screen-size-test-'));
|
|
226
|
+
fs.mkdirSync(path.join(tmpDir, '.codeyam'), { recursive: true });
|
|
227
|
+
});
|
|
228
|
+
afterEach(() => {
|
|
229
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
230
|
+
});
|
|
231
|
+
it('should return width and height from config', () => {
|
|
232
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
|
|
233
|
+
defaultScreenSize: { name: 'Mobile', width: 375, height: 667 },
|
|
234
|
+
}));
|
|
235
|
+
const result = readDefaultScreenSize(tmpDir);
|
|
236
|
+
expect(result).toEqual({ width: 375, height: 667 });
|
|
237
|
+
});
|
|
238
|
+
it('should return null when no defaultScreenSize in config', () => {
|
|
239
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({ projectTitle: 'Test' }));
|
|
240
|
+
const result = readDefaultScreenSize(tmpDir);
|
|
241
|
+
expect(result).toBeNull();
|
|
242
|
+
});
|
|
243
|
+
it('should return null when config file does not exist', () => {
|
|
244
|
+
const result = readDefaultScreenSize(tmpDir);
|
|
245
|
+
expect(result).toBeNull();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
describe('resolveScenarioViewport', () => {
|
|
249
|
+
it('should use explicit viewport when provided', () => {
|
|
250
|
+
const result = resolveScenarioViewport({
|
|
251
|
+
bodyWidth: 800,
|
|
252
|
+
bodyHeight: 600,
|
|
253
|
+
projectDefault: { width: 375, height: 667 },
|
|
254
|
+
});
|
|
255
|
+
expect(result).toEqual({ width: 800, height: 600 });
|
|
256
|
+
});
|
|
257
|
+
it('should use project default when no explicit viewport', () => {
|
|
258
|
+
const result = resolveScenarioViewport({
|
|
259
|
+
projectDefault: { width: 375, height: 667 },
|
|
260
|
+
});
|
|
261
|
+
expect(result).toEqual({ width: 375, height: 667 });
|
|
262
|
+
});
|
|
263
|
+
it('should fall back to 1280x720 when neither is provided', () => {
|
|
264
|
+
const result = resolveScenarioViewport({});
|
|
265
|
+
expect(result).toEqual({ width: 1280, height: 720 });
|
|
266
|
+
});
|
|
267
|
+
it('should use project default for missing dimensions', () => {
|
|
268
|
+
const result = resolveScenarioViewport({
|
|
269
|
+
bodyWidth: 800,
|
|
270
|
+
projectDefault: { width: 375, height: 667 },
|
|
271
|
+
});
|
|
272
|
+
// bodyWidth provided, bodyHeight not — should use project default height
|
|
273
|
+
expect(result).toEqual({ width: 800, height: 667 });
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
describe('resolveViewportWithProjectDefault', () => {
|
|
277
|
+
let tmpDir;
|
|
278
|
+
beforeEach(() => {
|
|
279
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'viewport-project-default-'));
|
|
280
|
+
fs.mkdirSync(path.join(tmpDir, '.codeyam'), { recursive: true });
|
|
281
|
+
});
|
|
282
|
+
afterEach(() => {
|
|
283
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
284
|
+
});
|
|
285
|
+
it('should use custom default from config.json when no explicit viewport provided', () => {
|
|
286
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
|
|
287
|
+
defaultScreenSize: { name: 'iPhone 15', width: 393, height: 852 },
|
|
288
|
+
}));
|
|
289
|
+
const result = resolveViewportWithProjectDefault({
|
|
290
|
+
codeyamRoot: tmpDir,
|
|
291
|
+
});
|
|
292
|
+
expect(result).toEqual({ width: 393, height: 852 });
|
|
293
|
+
});
|
|
294
|
+
it('should prefer explicit viewport over project default', () => {
|
|
295
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
|
|
296
|
+
defaultScreenSize: { name: 'iPhone 15', width: 393, height: 852 },
|
|
297
|
+
}));
|
|
298
|
+
const result = resolveViewportWithProjectDefault({
|
|
299
|
+
bodyWidth: 1024,
|
|
300
|
+
bodyHeight: 768,
|
|
301
|
+
codeyamRoot: tmpDir,
|
|
302
|
+
});
|
|
303
|
+
expect(result).toEqual({ width: 1024, height: 768 });
|
|
304
|
+
});
|
|
305
|
+
it('should fall back to 1280x720 when no config and no explicit viewport', () => {
|
|
306
|
+
const result = resolveViewportWithProjectDefault({
|
|
307
|
+
codeyamRoot: tmpDir,
|
|
308
|
+
});
|
|
309
|
+
expect(result).toEqual({ width: 1280, height: 720 });
|
|
310
|
+
});
|
|
311
|
+
it('should fall back to 1280x720 when config has no defaultScreenSize', () => {
|
|
312
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({ projectTitle: 'Test' }));
|
|
313
|
+
const result = resolveViewportWithProjectDefault({
|
|
314
|
+
codeyamRoot: tmpDir,
|
|
315
|
+
});
|
|
316
|
+
expect(result).toEqual({ width: 1280, height: 720 });
|
|
317
|
+
});
|
|
318
|
+
it('should resolve dimension name to screenSizes entry', () => {
|
|
319
|
+
// Claude registers "Home - Mobile" with dimension: "Mobile"
|
|
320
|
+
// → resolveViewportWithProjectDefault looks up "Mobile" in screenSizes
|
|
321
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
|
|
322
|
+
screenSizes: {
|
|
323
|
+
Desktop: { width: 1440, height: 900 },
|
|
324
|
+
Mobile: { width: 375, height: 667 },
|
|
325
|
+
},
|
|
326
|
+
}));
|
|
327
|
+
const result = resolveViewportWithProjectDefault({
|
|
328
|
+
dimension: 'Mobile',
|
|
329
|
+
codeyamRoot: tmpDir,
|
|
330
|
+
});
|
|
331
|
+
expect(result).toEqual({ width: 375, height: 667 });
|
|
332
|
+
});
|
|
333
|
+
it('should prefer explicit viewport over dimension name', () => {
|
|
334
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
|
|
335
|
+
screenSizes: {
|
|
336
|
+
Mobile: { width: 375, height: 667 },
|
|
337
|
+
},
|
|
338
|
+
}));
|
|
339
|
+
const result = resolveViewportWithProjectDefault({
|
|
340
|
+
bodyWidth: 800,
|
|
341
|
+
bodyHeight: 600,
|
|
342
|
+
dimension: 'Mobile',
|
|
343
|
+
codeyamRoot: tmpDir,
|
|
344
|
+
});
|
|
345
|
+
expect(result).toEqual({ width: 800, height: 600 });
|
|
346
|
+
});
|
|
347
|
+
it('should prefer dimension name over project defaultScreenSize', () => {
|
|
348
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
|
|
349
|
+
defaultScreenSize: { name: 'Desktop', width: 1440, height: 900 },
|
|
350
|
+
screenSizes: {
|
|
351
|
+
Tablet: { width: 768, height: 1024 },
|
|
352
|
+
},
|
|
353
|
+
}));
|
|
354
|
+
const result = resolveViewportWithProjectDefault({
|
|
355
|
+
dimension: 'Tablet',
|
|
356
|
+
codeyamRoot: tmpDir,
|
|
357
|
+
});
|
|
358
|
+
expect(result).toEqual({ width: 768, height: 1024 });
|
|
359
|
+
});
|
|
360
|
+
it('should fall back to defaultScreenSize when dimension name not found in screenSizes', () => {
|
|
361
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
|
|
362
|
+
defaultScreenSize: { name: 'Desktop', width: 1440, height: 900 },
|
|
363
|
+
screenSizes: {
|
|
364
|
+
Mobile: { width: 375, height: 667 },
|
|
365
|
+
},
|
|
366
|
+
}));
|
|
367
|
+
const result = resolveViewportWithProjectDefault({
|
|
368
|
+
dimension: 'NonExistent',
|
|
369
|
+
codeyamRoot: tmpDir,
|
|
370
|
+
});
|
|
371
|
+
expect(result).toEqual({ width: 1440, height: 900 });
|
|
372
|
+
});
|
|
373
|
+
it('should use inherited viewport from existing scenario instead of chrome-extension project default', () => {
|
|
374
|
+
// BUG: When re-registering a scenario after a code change, the register
|
|
375
|
+
// endpoint creates a new DB record. If Claude doesn't pass viewport info,
|
|
376
|
+
// the resolution falls to defaultScreenSize (chrome-extension size for
|
|
377
|
+
// extension projects). But the original scenario had desktop viewport.
|
|
378
|
+
//
|
|
379
|
+
// FIX: The register endpoint looks up the existing scenario by name and
|
|
380
|
+
// passes its viewport_width/viewport_height as bodyWidth/bodyHeight,
|
|
381
|
+
// so the inherited desktop size takes precedence over the project default.
|
|
382
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
|
|
383
|
+
defaultScreenSize: { name: 'Popup', width: 400, height: 600 },
|
|
384
|
+
}));
|
|
385
|
+
// Without inherited values → falls to chrome-extension default
|
|
386
|
+
const withoutInherited = resolveViewportWithProjectDefault({
|
|
387
|
+
codeyamRoot: tmpDir,
|
|
388
|
+
});
|
|
389
|
+
expect(withoutInherited).toEqual({ width: 400, height: 600 });
|
|
390
|
+
// With inherited values from existing scenario → uses desktop size
|
|
391
|
+
const withInherited = resolveViewportWithProjectDefault({
|
|
392
|
+
bodyWidth: 1280,
|
|
393
|
+
bodyHeight: 720,
|
|
394
|
+
codeyamRoot: tmpDir,
|
|
395
|
+
});
|
|
396
|
+
expect(withInherited).toEqual({ width: 1280, height: 720 });
|
|
397
|
+
});
|
|
398
|
+
it('should use inherited dimension from existing scenario instead of project default', () => {
|
|
399
|
+
// Same bug but for dimension-based scenarios: the existing scenario had
|
|
400
|
+
// dimension "Desktop" stored, and the re-registration should inherit it.
|
|
401
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
|
|
402
|
+
defaultScreenSize: { name: 'Popup', width: 400, height: 600 },
|
|
403
|
+
screenSizes: {
|
|
404
|
+
Desktop: { width: 1440, height: 900 },
|
|
405
|
+
Popup: { width: 400, height: 600 },
|
|
406
|
+
},
|
|
407
|
+
}));
|
|
408
|
+
// Without inherited dimension → falls to project default (Popup)
|
|
409
|
+
const withoutInherited = resolveViewportWithProjectDefault({
|
|
410
|
+
codeyamRoot: tmpDir,
|
|
411
|
+
});
|
|
412
|
+
expect(withoutInherited).toEqual({ width: 400, height: 600 });
|
|
413
|
+
// With inherited dimension from existing scenario → resolves to Desktop
|
|
414
|
+
const withInherited = resolveViewportWithProjectDefault({
|
|
415
|
+
dimension: 'Desktop',
|
|
416
|
+
codeyamRoot: tmpDir,
|
|
417
|
+
});
|
|
418
|
+
expect(withInherited).toEqual({ width: 1440, height: 900 });
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
describe('readScreenSizes', () => {
|
|
422
|
+
let tmpDir;
|
|
423
|
+
beforeEach(() => {
|
|
424
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'screen-sizes-test-'));
|
|
425
|
+
fs.mkdirSync(path.join(tmpDir, '.codeyam'), { recursive: true });
|
|
426
|
+
});
|
|
427
|
+
afterEach(() => {
|
|
428
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
429
|
+
});
|
|
430
|
+
it('should return screenSizes map from config', () => {
|
|
431
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
|
|
432
|
+
screenSizes: {
|
|
433
|
+
Desktop: { width: 1440, height: 900 },
|
|
434
|
+
Mobile: { width: 375, height: 667 },
|
|
435
|
+
Tablet: { width: 768, height: 1024 },
|
|
436
|
+
},
|
|
437
|
+
}));
|
|
438
|
+
const result = readScreenSizes(tmpDir);
|
|
439
|
+
expect(result).toEqual({
|
|
440
|
+
Desktop: { width: 1440, height: 900 },
|
|
441
|
+
Mobile: { width: 375, height: 667 },
|
|
442
|
+
Tablet: { width: 768, height: 1024 },
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
it('should return empty object when no screenSizes in config', () => {
|
|
446
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({ projectTitle: 'Test' }));
|
|
447
|
+
const result = readScreenSizes(tmpDir);
|
|
448
|
+
expect(result).toEqual({});
|
|
449
|
+
});
|
|
450
|
+
it('should return empty object when config file does not exist', () => {
|
|
451
|
+
const result = readScreenSizes(tmpDir);
|
|
452
|
+
expect(result).toEqual({});
|
|
453
|
+
});
|
|
454
|
+
it('should return empty object when screenSizes is not an object', () => {
|
|
455
|
+
fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({ screenSizes: 'invalid' }));
|
|
456
|
+
const result = readScreenSizes(tmpDir);
|
|
457
|
+
expect(result).toEqual({});
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
describe('upsertEditorScenario', () => {
|
|
461
|
+
let db;
|
|
462
|
+
let rawDb;
|
|
463
|
+
const projectId = 'test-project-id';
|
|
464
|
+
beforeEach(async () => {
|
|
465
|
+
rawDb = new Database(':memory:');
|
|
466
|
+
db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
|
|
467
|
+
// Create the editor_scenarios table matching the real schema
|
|
468
|
+
await db.schema
|
|
469
|
+
.createTable('editor_scenarios')
|
|
470
|
+
.addColumn('id', 'varchar', (col) => col.primaryKey())
|
|
471
|
+
.addColumn('project_id', 'varchar', (col) => col.notNull())
|
|
472
|
+
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
473
|
+
.addColumn('description', 'text')
|
|
474
|
+
.addColumn('component_name', 'varchar')
|
|
475
|
+
.addColumn('component_path', 'varchar')
|
|
476
|
+
.addColumn('url', 'varchar')
|
|
477
|
+
.addColumn('type', 'varchar')
|
|
478
|
+
.addColumn('screenshot_path', 'varchar')
|
|
479
|
+
.addColumn('viewport_width', 'integer')
|
|
480
|
+
.addColumn('viewport_height', 'integer')
|
|
481
|
+
.addColumn('dimension', 'varchar')
|
|
482
|
+
.addColumn('created_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
|
|
483
|
+
.addColumn('updated_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
|
|
484
|
+
.execute();
|
|
485
|
+
});
|
|
486
|
+
afterEach(async () => {
|
|
487
|
+
await db.destroy();
|
|
488
|
+
});
|
|
489
|
+
it('should insert a new scenario when none exists with same name', async () => {
|
|
490
|
+
const result = await upsertEditorScenario(db, {
|
|
491
|
+
projectId,
|
|
492
|
+
name: 'ArticleRow - Default',
|
|
493
|
+
description: null,
|
|
494
|
+
componentName: 'ArticleRow',
|
|
495
|
+
componentPath: 'src/components/ArticleRow.tsx',
|
|
496
|
+
url: '/isolated-components/ArticleRow?s=Default',
|
|
497
|
+
type: null,
|
|
498
|
+
viewportWidth: 1280,
|
|
499
|
+
viewportHeight: 720,
|
|
500
|
+
dimension: null,
|
|
501
|
+
});
|
|
502
|
+
expect(result.isNew).toBe(true);
|
|
503
|
+
expect(result.cleanedUpIds).toEqual([]);
|
|
504
|
+
// Verify exactly 1 row in DB
|
|
505
|
+
const rows = await db
|
|
506
|
+
.selectFrom('editor_scenarios')
|
|
507
|
+
.selectAll()
|
|
508
|
+
.execute();
|
|
509
|
+
expect(rows).toHaveLength(1);
|
|
510
|
+
expect(rows[0].id).toBe(result.scenarioId);
|
|
511
|
+
expect(rows[0].name).toBe('ArticleRow - Default');
|
|
512
|
+
});
|
|
513
|
+
it('should update existing scenario instead of creating a duplicate', async () => {
|
|
514
|
+
// First registration
|
|
515
|
+
const first = await upsertEditorScenario(db, {
|
|
516
|
+
projectId,
|
|
517
|
+
name: 'ArticleRow - Default',
|
|
518
|
+
description: null,
|
|
519
|
+
componentName: 'ArticleRow',
|
|
520
|
+
componentPath: 'src/components/ArticleRow.tsx',
|
|
521
|
+
url: '/isolated-components/ArticleRow?s=Default',
|
|
522
|
+
type: null,
|
|
523
|
+
viewportWidth: 1280,
|
|
524
|
+
viewportHeight: 720,
|
|
525
|
+
dimension: null,
|
|
526
|
+
});
|
|
527
|
+
// Second registration with same name
|
|
528
|
+
const second = await upsertEditorScenario(db, {
|
|
529
|
+
projectId,
|
|
530
|
+
name: 'ArticleRow - Default',
|
|
531
|
+
description: 'updated description',
|
|
532
|
+
componentName: 'ArticleRow',
|
|
533
|
+
componentPath: 'src/components/ArticleRow.tsx',
|
|
534
|
+
url: '/isolated-components/ArticleRow?s=Default',
|
|
535
|
+
type: null,
|
|
536
|
+
viewportWidth: 800,
|
|
537
|
+
viewportHeight: 600,
|
|
538
|
+
dimension: null,
|
|
539
|
+
});
|
|
540
|
+
// Should reuse the same ID
|
|
541
|
+
expect(second.scenarioId).toBe(first.scenarioId);
|
|
542
|
+
expect(second.isNew).toBe(false);
|
|
543
|
+
// Should have exactly 1 row, not 2
|
|
544
|
+
const rows = await db
|
|
545
|
+
.selectFrom('editor_scenarios')
|
|
546
|
+
.selectAll()
|
|
547
|
+
.execute();
|
|
548
|
+
expect(rows).toHaveLength(1);
|
|
549
|
+
expect(rows[0].description).toBe('updated description');
|
|
550
|
+
expect(rows[0].viewport_width).toBe(800);
|
|
551
|
+
});
|
|
552
|
+
it('should clean up duplicate rows from past always-insert behavior', async () => {
|
|
553
|
+
// Simulate old behavior: 3 rows with the same name (accumulated duplicates)
|
|
554
|
+
for (let i = 0; i < 3; i++) {
|
|
555
|
+
await db
|
|
556
|
+
.insertInto('editor_scenarios')
|
|
557
|
+
.values({
|
|
558
|
+
id: `old-id-${i}`,
|
|
559
|
+
project_id: projectId,
|
|
560
|
+
name: 'ArticleRow - Default',
|
|
561
|
+
url: '/isolated-components/ArticleRow?s=Default',
|
|
562
|
+
viewport_width: 1280,
|
|
563
|
+
viewport_height: 720,
|
|
564
|
+
created_at: new Date(Date.now() + i * 1000).toISOString(),
|
|
565
|
+
})
|
|
566
|
+
.execute();
|
|
567
|
+
}
|
|
568
|
+
// Now upsert — should consolidate to 1 row
|
|
569
|
+
const result = await upsertEditorScenario(db, {
|
|
570
|
+
projectId,
|
|
571
|
+
name: 'ArticleRow - Default',
|
|
572
|
+
description: null,
|
|
573
|
+
componentName: 'ArticleRow',
|
|
574
|
+
componentPath: 'src/components/ArticleRow.tsx',
|
|
575
|
+
url: '/isolated-components/ArticleRow?s=Default',
|
|
576
|
+
type: null,
|
|
577
|
+
viewportWidth: 1280,
|
|
578
|
+
viewportHeight: 720,
|
|
579
|
+
dimension: null,
|
|
580
|
+
});
|
|
581
|
+
expect(result.isNew).toBe(false);
|
|
582
|
+
// Should return the IDs of cleaned-up duplicates
|
|
583
|
+
expect(result.cleanedUpIds).toHaveLength(2);
|
|
584
|
+
// Should have exactly 1 row remaining
|
|
585
|
+
const rows = await db
|
|
586
|
+
.selectFrom('editor_scenarios')
|
|
587
|
+
.selectAll()
|
|
588
|
+
.execute();
|
|
589
|
+
expect(rows).toHaveLength(1);
|
|
590
|
+
expect(rows[0].id).toBe(result.scenarioId);
|
|
591
|
+
});
|
|
592
|
+
it('should not conflict with scenarios of different names', async () => {
|
|
593
|
+
await upsertEditorScenario(db, {
|
|
594
|
+
projectId,
|
|
595
|
+
name: 'ArticleRow - Default',
|
|
596
|
+
description: null,
|
|
597
|
+
componentName: 'ArticleRow',
|
|
598
|
+
componentPath: 'src/components/ArticleRow.tsx',
|
|
599
|
+
url: '/isolated-components/ArticleRow?s=Default',
|
|
600
|
+
type: null,
|
|
601
|
+
viewportWidth: 1280,
|
|
602
|
+
viewportHeight: 720,
|
|
603
|
+
dimension: null,
|
|
604
|
+
});
|
|
605
|
+
await upsertEditorScenario(db, {
|
|
606
|
+
projectId,
|
|
607
|
+
name: 'ArticleCard - Default',
|
|
608
|
+
description: null,
|
|
609
|
+
componentName: 'ArticleCard',
|
|
610
|
+
componentPath: 'src/components/ArticleCard.tsx',
|
|
611
|
+
url: '/isolated-components/ArticleCard?s=Default',
|
|
612
|
+
type: null,
|
|
613
|
+
viewportWidth: 1280,
|
|
614
|
+
viewportHeight: 720,
|
|
615
|
+
dimension: null,
|
|
616
|
+
});
|
|
617
|
+
const rows = await db
|
|
618
|
+
.selectFrom('editor_scenarios')
|
|
619
|
+
.selectAll()
|
|
620
|
+
.execute();
|
|
621
|
+
expect(rows).toHaveLength(2);
|
|
622
|
+
});
|
|
623
|
+
it('should scope upsert to the same project — different projects can have same scenario name', async () => {
|
|
624
|
+
const result1 = await upsertEditorScenario(db, {
|
|
625
|
+
projectId: 'project-a',
|
|
626
|
+
name: 'Home - Default',
|
|
627
|
+
description: null,
|
|
628
|
+
componentName: null,
|
|
629
|
+
componentPath: null,
|
|
630
|
+
url: '/',
|
|
631
|
+
type: 'application',
|
|
632
|
+
viewportWidth: 1280,
|
|
633
|
+
viewportHeight: 720,
|
|
634
|
+
dimension: null,
|
|
635
|
+
});
|
|
636
|
+
const result2 = await upsertEditorScenario(db, {
|
|
637
|
+
projectId: 'project-b',
|
|
638
|
+
name: 'Home - Default',
|
|
639
|
+
description: null,
|
|
640
|
+
componentName: null,
|
|
641
|
+
componentPath: null,
|
|
642
|
+
url: '/',
|
|
643
|
+
type: 'application',
|
|
644
|
+
viewportWidth: 1280,
|
|
645
|
+
viewportHeight: 720,
|
|
646
|
+
dimension: null,
|
|
647
|
+
});
|
|
648
|
+
expect(result1.scenarioId).not.toBe(result2.scenarioId);
|
|
649
|
+
expect(result1.isNew).toBe(true);
|
|
650
|
+
expect(result2.isNew).toBe(true);
|
|
651
|
+
const rows = await db
|
|
652
|
+
.selectFrom('editor_scenarios')
|
|
653
|
+
.selectAll()
|
|
654
|
+
.execute();
|
|
655
|
+
expect(rows).toHaveLength(2);
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
describe('cleanupScenarioFiles', () => {
|
|
659
|
+
let tmpDir;
|
|
660
|
+
let scenariosDir;
|
|
661
|
+
let screenshotsDir;
|
|
662
|
+
beforeEach(() => {
|
|
663
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'scenario-cleanup-'));
|
|
664
|
+
scenariosDir = path.join(tmpDir, '.codeyam', 'editor-scenarios');
|
|
665
|
+
screenshotsDir = path.join(scenariosDir, 'screenshots');
|
|
666
|
+
fs.mkdirSync(screenshotsDir, { recursive: true });
|
|
667
|
+
});
|
|
668
|
+
afterEach(() => {
|
|
669
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
670
|
+
});
|
|
671
|
+
it('should delete .json, .seed.json, and screenshot files for given IDs', () => {
|
|
672
|
+
// Create files for two scenario IDs
|
|
673
|
+
fs.writeFileSync(path.join(scenariosDir, 'old-id-1.json'), '{"mock":"data"}');
|
|
674
|
+
fs.writeFileSync(path.join(scenariosDir, 'old-id-1.seed.json'), '{"seed":"data"}');
|
|
675
|
+
fs.writeFileSync(path.join(screenshotsDir, 'old-id-1.png'), 'fake-png-data');
|
|
676
|
+
fs.writeFileSync(path.join(scenariosDir, 'old-id-2.json'), '{"mock":"data2"}');
|
|
677
|
+
cleanupScenarioFiles(tmpDir, ['old-id-1', 'old-id-2']);
|
|
678
|
+
expect(fs.existsSync(path.join(scenariosDir, 'old-id-1.json'))).toBe(false);
|
|
679
|
+
expect(fs.existsSync(path.join(scenariosDir, 'old-id-1.seed.json'))).toBe(false);
|
|
680
|
+
expect(fs.existsSync(path.join(screenshotsDir, 'old-id-1.png'))).toBe(false);
|
|
681
|
+
expect(fs.existsSync(path.join(scenariosDir, 'old-id-2.json'))).toBe(false);
|
|
682
|
+
});
|
|
683
|
+
it('should not throw when files do not exist', () => {
|
|
684
|
+
expect(() => cleanupScenarioFiles(tmpDir, ['nonexistent-id'])).not.toThrow();
|
|
685
|
+
});
|
|
686
|
+
it('should not delete files for unrelated scenario IDs', () => {
|
|
687
|
+
fs.writeFileSync(path.join(scenariosDir, 'keep-me.json'), '{"keep":"this"}');
|
|
688
|
+
cleanupScenarioFiles(tmpDir, ['delete-me']);
|
|
689
|
+
expect(fs.existsSync(path.join(scenariosDir, 'keep-me.json'))).toBe(true);
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
describe('readPreservedConfigProperties', () => {
|
|
693
|
+
let tmpDir;
|
|
694
|
+
beforeEach(() => {
|
|
695
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'config-preserve-'));
|
|
696
|
+
fs.mkdirSync(path.join(tmpDir, '.codeyam'), { recursive: true });
|
|
697
|
+
});
|
|
698
|
+
afterEach(() => {
|
|
699
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
700
|
+
});
|
|
701
|
+
it('should preserve defaultScreenSize from existing config', () => {
|
|
702
|
+
const configPath = path.join(tmpDir, '.codeyam', 'config.json');
|
|
703
|
+
fs.writeFileSync(configPath, JSON.stringify({
|
|
704
|
+
projectSlug: 'my-project',
|
|
705
|
+
defaultScreenSize: { name: 'Popup', width: 400, height: 600 },
|
|
706
|
+
webapps: [],
|
|
707
|
+
}));
|
|
708
|
+
const preserved = readPreservedConfigProperties(configPath);
|
|
709
|
+
expect(preserved.defaultScreenSize).toEqual({
|
|
710
|
+
name: 'Popup',
|
|
711
|
+
width: 400,
|
|
712
|
+
height: 600,
|
|
713
|
+
});
|
|
714
|
+
});
|
|
715
|
+
it('should preserve screenSizes from existing config', () => {
|
|
716
|
+
const configPath = path.join(tmpDir, '.codeyam', 'config.json');
|
|
717
|
+
fs.writeFileSync(configPath, JSON.stringify({
|
|
718
|
+
projectSlug: 'my-project',
|
|
719
|
+
screenSizes: {
|
|
720
|
+
Desktop: { width: 1440, height: 900 },
|
|
721
|
+
Mobile: { width: 375, height: 667 },
|
|
722
|
+
},
|
|
723
|
+
}));
|
|
724
|
+
const preserved = readPreservedConfigProperties(configPath);
|
|
725
|
+
expect(preserved.screenSizes).toEqual({
|
|
726
|
+
Desktop: { width: 1440, height: 900 },
|
|
727
|
+
Mobile: { width: 375, height: 667 },
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
it('should preserve projectTitle and projectDescription', () => {
|
|
731
|
+
const configPath = path.join(tmpDir, '.codeyam', 'config.json');
|
|
732
|
+
fs.writeFileSync(configPath, JSON.stringify({
|
|
733
|
+
projectSlug: 'my-project',
|
|
734
|
+
projectTitle: 'My Chrome Extension',
|
|
735
|
+
projectDescription: 'A cool extension',
|
|
736
|
+
}));
|
|
737
|
+
const preserved = readPreservedConfigProperties(configPath);
|
|
738
|
+
expect(preserved.projectTitle).toBe('My Chrome Extension');
|
|
739
|
+
expect(preserved.projectDescription).toBe('A cool extension');
|
|
740
|
+
});
|
|
741
|
+
it('should preserve all user-configured properties at once', () => {
|
|
742
|
+
const configPath = path.join(tmpDir, '.codeyam', 'config.json');
|
|
743
|
+
fs.writeFileSync(configPath, JSON.stringify({
|
|
744
|
+
projectSlug: 'my-project',
|
|
745
|
+
packageManager: 'npm',
|
|
746
|
+
webapps: [],
|
|
747
|
+
defaultScreenSize: { name: 'Popup', width: 400, height: 600 },
|
|
748
|
+
screenSizes: { Desktop: { width: 1440, height: 900 } },
|
|
749
|
+
projectTitle: 'My Chrome Extension',
|
|
750
|
+
projectDescription: 'A cool extension',
|
|
751
|
+
}));
|
|
752
|
+
const preserved = readPreservedConfigProperties(configPath);
|
|
753
|
+
expect(preserved).toEqual({
|
|
754
|
+
defaultScreenSize: { name: 'Popup', width: 400, height: 600 },
|
|
755
|
+
screenSizes: { Desktop: { width: 1440, height: 900 } },
|
|
756
|
+
projectTitle: 'My Chrome Extension',
|
|
757
|
+
projectDescription: 'A cool extension',
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
it('should NOT preserve auto-detected properties like projectSlug and webapps', () => {
|
|
761
|
+
const configPath = path.join(tmpDir, '.codeyam', 'config.json');
|
|
762
|
+
fs.writeFileSync(configPath, JSON.stringify({
|
|
763
|
+
projectSlug: 'my-project',
|
|
764
|
+
packageManager: 'npm',
|
|
765
|
+
webapps: [{ path: '.', framework: 'next' }],
|
|
766
|
+
createdAt: '2026-01-01',
|
|
767
|
+
defaultScreenSize: { name: 'Popup', width: 400, height: 600 },
|
|
768
|
+
}));
|
|
769
|
+
const preserved = readPreservedConfigProperties(configPath);
|
|
770
|
+
// Only user-configured properties should survive
|
|
771
|
+
expect(preserved).toEqual({
|
|
772
|
+
defaultScreenSize: { name: 'Popup', width: 400, height: 600 },
|
|
773
|
+
});
|
|
774
|
+
expect(preserved).not.toHaveProperty('projectSlug');
|
|
775
|
+
expect(preserved).not.toHaveProperty('packageManager');
|
|
776
|
+
expect(preserved).not.toHaveProperty('webapps');
|
|
777
|
+
expect(preserved).not.toHaveProperty('createdAt');
|
|
778
|
+
});
|
|
779
|
+
it('should return empty object when config does not exist', () => {
|
|
780
|
+
const configPath = path.join(tmpDir, '.codeyam', 'nonexistent.json');
|
|
781
|
+
const preserved = readPreservedConfigProperties(configPath);
|
|
782
|
+
expect(preserved).toEqual({});
|
|
783
|
+
});
|
|
784
|
+
it('should return empty object when config has no preservable properties', () => {
|
|
785
|
+
const configPath = path.join(tmpDir, '.codeyam', 'config.json');
|
|
786
|
+
fs.writeFileSync(configPath, JSON.stringify({
|
|
787
|
+
projectSlug: 'my-project',
|
|
788
|
+
webapps: [],
|
|
789
|
+
}));
|
|
790
|
+
const preserved = readPreservedConfigProperties(configPath);
|
|
791
|
+
expect(preserved).toEqual({});
|
|
792
|
+
});
|
|
793
|
+
it('should return empty object when config is invalid JSON', () => {
|
|
794
|
+
const configPath = path.join(tmpDir, '.codeyam', 'config.json');
|
|
795
|
+
fs.writeFileSync(configPath, 'not valid json');
|
|
796
|
+
const preserved = readPreservedConfigProperties(configPath);
|
|
797
|
+
expect(preserved).toEqual({});
|
|
798
|
+
});
|
|
799
|
+
});
|
|
800
|
+
describe('validateStepTransition', () => {
|
|
801
|
+
it('should allow step 1 with no current state', () => {
|
|
802
|
+
expect(validateStepTransition(1, null)).toBeNull();
|
|
803
|
+
});
|
|
804
|
+
it('should allow step 1 even when on a later step (restart)', () => {
|
|
805
|
+
expect(validateStepTransition(1, 5)).toBeNull();
|
|
806
|
+
});
|
|
807
|
+
it('should allow advancing to the next step', () => {
|
|
808
|
+
expect(validateStepTransition(2, 1)).toBeNull();
|
|
809
|
+
expect(validateStepTransition(3, 2)).toBeNull();
|
|
810
|
+
expect(validateStepTransition(13, 12)).toBeNull();
|
|
811
|
+
});
|
|
812
|
+
it('should allow re-running the current step', () => {
|
|
813
|
+
expect(validateStepTransition(3, 3)).toBeNull();
|
|
814
|
+
expect(validateStepTransition(7, 7)).toBeNull();
|
|
815
|
+
});
|
|
816
|
+
it('should allow going back to a previous step', () => {
|
|
817
|
+
expect(validateStepTransition(3, 5)).toBeNull();
|
|
818
|
+
});
|
|
819
|
+
it('should reject skipping steps forward', () => {
|
|
820
|
+
const err = validateStepTransition(5, 2);
|
|
821
|
+
expect(err).not.toBeNull();
|
|
822
|
+
expect(err).toContain('step 2');
|
|
823
|
+
expect(err).toContain('step 5');
|
|
824
|
+
});
|
|
825
|
+
it('should reject jumping to step 13 from step 3', () => {
|
|
826
|
+
const err = validateStepTransition(13, 3);
|
|
827
|
+
expect(err).not.toBeNull();
|
|
828
|
+
expect(err).toContain('step 4');
|
|
829
|
+
});
|
|
830
|
+
it('should reject step 2 with no current state', () => {
|
|
831
|
+
const err = validateStepTransition(2, null);
|
|
832
|
+
expect(err).not.toBeNull();
|
|
833
|
+
expect(err).toContain('step 1');
|
|
834
|
+
});
|
|
835
|
+
it('should reject step 13 with no current state', () => {
|
|
836
|
+
const err = validateStepTransition(13, null);
|
|
837
|
+
expect(err).not.toBeNull();
|
|
838
|
+
});
|
|
839
|
+
it('should allow advancing from step 13 to step 14', () => {
|
|
840
|
+
expect(validateStepTransition(14, 13)).toBeNull();
|
|
841
|
+
});
|
|
842
|
+
it('should allow advancing from step 14 to step 15', () => {
|
|
843
|
+
expect(validateStepTransition(15, 14)).toBeNull();
|
|
844
|
+
});
|
|
845
|
+
it('should allow advancing from step 15 to step 16', () => {
|
|
846
|
+
expect(validateStepTransition(16, 15)).toBeNull();
|
|
847
|
+
});
|
|
848
|
+
it('should reject skipping from step 13 to step 15', () => {
|
|
849
|
+
const err = validateStepTransition(15, 13);
|
|
850
|
+
expect(err).not.toBeNull();
|
|
851
|
+
expect(err).toContain('step 14');
|
|
852
|
+
});
|
|
853
|
+
});
|
|
854
|
+
});
|
|
855
|
+
//# sourceMappingURL=editorScenarios.test.js.map
|