@codeyam/codeyam-cli 0.1.0-staging.2ea44f6 → 0.1.0-staging.30dc541
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 +3 -3
- package/analyzer-template/packages/ai/package.json +1 -1
- package/analyzer-template/packages/aws/package.json +1 -1
- package/analyzer-template/packages/database/package.json +1 -1
- package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +99 -0
- package/analyzer-template/packages/database/src/lib/loadEntities.ts +0 -6
- package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +0 -65
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +8 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +101 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.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/updateCommitMetadata.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js +0 -25
- package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.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/types/src/enums/ProjectFramework.ts +2 -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/codeyam-cli/src/cli.js +24 -0
- package/codeyam-cli/src/cli.js.map +1 -1
- package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js +51 -0
- package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js.map +1 -0
- package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +56 -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/editor.js +3077 -509
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/commands/editorIsolateArgs.js +25 -0
- package/codeyam-cli/src/commands/editorIsolateArgs.js.map +1 -0
- package/codeyam-cli/src/commands/init.js +69 -34
- package/codeyam-cli/src/commands/init.js.map +1 -1
- package/codeyam-cli/src/commands/telemetry.js +37 -0
- package/codeyam-cli/src/commands/telemetry.js.map +1 -0
- 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 +173 -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__/editorApi.test.js +18 -8
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +2201 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js +76 -0
- package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js +137 -0
- package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js +100 -0
- package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js +152 -3
- package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +76 -3
- package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +381 -0
- package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js +67 -0
- package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorImageVerifier.test.js +140 -12
- package/codeyam-cli/src/utils/__tests__/editorImageVerifier.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js +238 -2
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +202 -1
- package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorMigration.test.js +435 -0
- package/codeyam-cli/src/utils/__tests__/editorMigration.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +191 -5
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
- 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 +291 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +1437 -2
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +280 -0
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js +143 -0
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js +66 -0
- package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js +53 -0
- package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +641 -45
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js +177 -0
- package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js +122 -0
- package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js +129 -0
- package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js +118 -0
- package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +284 -0
- package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +649 -223
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +14 -5
- package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/telemetry.test.js +159 -0
- package/codeyam-cli/src/utils/__tests__/telemetry.test.js.map +1 -0
- 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/analysisRunner.js +3 -1
- package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
- 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 +100 -0
- package/codeyam-cli/src/utils/analyzerFinalization.js.map +1 -0
- package/codeyam-cli/src/utils/backgroundServer.js +93 -17
- package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/utils/database.js +37 -2
- package/codeyam-cli/src/utils/database.js.map +1 -1
- package/codeyam-cli/src/utils/editorApi.js +11 -5
- package/codeyam-cli/src/utils/editorApi.js.map +1 -1
- package/codeyam-cli/src/utils/editorAudit.js +410 -6
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
- package/codeyam-cli/src/utils/editorBroadcastViewport.js +26 -0
- package/codeyam-cli/src/utils/editorBroadcastViewport.js.map +1 -0
- package/codeyam-cli/src/utils/editorDeleteScenario.js +67 -0
- package/codeyam-cli/src/utils/editorDeleteScenario.js.map +1 -0
- package/codeyam-cli/src/utils/editorDevServer.js +89 -1
- package/codeyam-cli/src/utils/editorDevServer.js.map +1 -1
- package/codeyam-cli/src/utils/editorEntityChangeStatus.js +13 -7
- package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
- package/codeyam-cli/src/utils/editorEntityHelpers.js +144 -0
- package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -0
- package/codeyam-cli/src/utils/editorGuard.js +36 -0
- package/codeyam-cli/src/utils/editorGuard.js.map +1 -0
- package/codeyam-cli/src/utils/editorImageVerifier.js +45 -10
- package/codeyam-cli/src/utils/editorImageVerifier.js.map +1 -1
- package/codeyam-cli/src/utils/editorJournal.js +78 -3
- package/codeyam-cli/src/utils/editorJournal.js.map +1 -1
- package/codeyam-cli/src/utils/editorLoaderHelpers.js +72 -1
- package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
- package/codeyam-cli/src/utils/editorMigration.js +224 -0
- package/codeyam-cli/src/utils/editorMigration.js.map +1 -0
- package/codeyam-cli/src/utils/editorPreview.js +43 -2
- package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
- package/codeyam-cli/src/utils/editorRecapture.js +109 -0
- package/codeyam-cli/src/utils/editorRecapture.js.map +1 -0
- package/codeyam-cli/src/utils/editorScenarioSwitch.js +134 -0
- package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -0
- package/codeyam-cli/src/utils/editorScenarios.js +488 -0
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
- package/codeyam-cli/src/utils/editorSeedAdapter.js +422 -0
- package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -0
- package/codeyam-cli/src/utils/editorShouldRevalidate.js +21 -0
- package/codeyam-cli/src/utils/editorShouldRevalidate.js.map +1 -0
- package/codeyam-cli/src/utils/entityChangeStatus.js +89 -21
- package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
- package/codeyam-cli/src/utils/entityChangeStatus.server.js +97 -8
- package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
- package/codeyam-cli/src/utils/fileWatcher.js +38 -0
- package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
- package/codeyam-cli/src/utils/glossaryAdd.js +74 -0
- package/codeyam-cli/src/utils/glossaryAdd.js.map +1 -0
- package/codeyam-cli/src/utils/install-skills.js +14 -0
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/parseRegisterArg.js +31 -0
- package/codeyam-cli/src/utils/parseRegisterArg.js.map +1 -0
- package/codeyam-cli/src/utils/progress.js +2 -2
- package/codeyam-cli/src/utils/progress.js.map +1 -1
- package/codeyam-cli/src/utils/routePatternMatching.js +129 -0
- package/codeyam-cli/src/utils/routePatternMatching.js.map +1 -0
- package/codeyam-cli/src/utils/scenarioCoverage.js +77 -0
- package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -0
- package/codeyam-cli/src/utils/scenariosManifest.js +269 -74
- package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
- package/codeyam-cli/src/utils/serverState.js +30 -0
- package/codeyam-cli/src/utils/serverState.js.map +1 -1
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +14 -5
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
- package/codeyam-cli/src/utils/simulationGateMiddleware.js +17 -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/telemetry.js +106 -0
- package/codeyam-cli/src/utils/telemetry.js.map +1 -0
- package/codeyam-cli/src/utils/telemetryMiddleware.js +22 -0
- package/codeyam-cli/src/utils/telemetryMiddleware.js.map +1 -0
- package/codeyam-cli/src/utils/webappDetection.js +21 -0
- package/codeyam-cli/src/utils/webappDetection.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +35 -0
- package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -0
- package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +80 -0
- package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -0
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +628 -0
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -0
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +228 -0
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -0
- package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js +79 -0
- package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js.map +1 -0
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js +71 -0
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -0
- package/codeyam-cli/src/webserver/app/lib/git.js +3 -2
- package/codeyam-cli/src/webserver/app/lib/git.js.map +1 -1
- package/codeyam-cli/src/webserver/app/types/editor.js +8 -0
- package/codeyam-cli/src/webserver/app/types/editor.js.map +1 -0
- package/codeyam-cli/src/webserver/backgroundServer.js +60 -61
- package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/CopyButton-CLe80MMu.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-C76mRRiF.js → EntityItem-Crt_KN_U.js} +5 -5
- package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-CQgyEGV-.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CobE682z.js → EntityTypeIcon-CD7lGABo.js} +9 -9
- package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-CgTNOhnu.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-CKeQT5Ty.js +25 -0
- package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-D3s1MFkb.js +3 -0
- package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-BU_OAEMP.js → LoadingDots-By5zI316.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-ceAyBX-H.js → LogViewer-CM5zg40N.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-djPLI-WV.js → ReportIssueModal-C2PLkej3.js} +4 -4
- package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-DanvyBPb.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-ZlRKbhrq.js → ScenarioViewer-DUMfcNVK.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/Spinner-D0LgAaSa.js +34 -0
- package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-CK7-NaPZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-BA_Ry-rs.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{_index-C96V0n15.js → _index-BAWd-Xjf.js} +4 -4
- package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BpKzcsJz.js → activity.(_tab)-BOARiB-g.js} +8 -8
- package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-Duc5hnl7.js → addon-web-links-CHx25PAe.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-D9hemwl6.js → agent-transcripts-Bg3e7q4S.js} +7 -7
- 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-recapture-stale-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-rename-scenario-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-seed-state-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-prompt-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-session-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{book-open-D_nMCFmP.js → book-open-CL-lMgHh.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-BH2h1Ea2.js → chevron-down-GmAjGS9-.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-BAdwhyCx.js +43 -0
- package/codeyam-cli/src/webserver/build/client/assets/{circle-check-DyIKORY6.js → circle-check-DFcQkN5j.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{copy-NDbZjXao.js → copy-C6iF61Xs.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/createLucideIcon-4ImjHTVC.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/dev.empty-C8y4mmyv.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor._tab-Gbk_i5Js.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-CGzKlIHg.js +58 -0
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-oepecPae.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-DItJnD8s.js → entity._sha._-Blfy9UlN.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-KTQuL0aj.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-C6eeL24i.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-DQM8E7L4.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMvVHNXU.js → entity._sha_.edit._scenarioId-CAoXLsQr.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{entry.client-DTvKq3TY.js → entry.client-SuW9syRS.js} +6 -6
- package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-Daa96Fr1.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/files-D-xGrg29.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/git-Bq_fbXP5.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/globals-Yn9W3zp3.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{index-BcvgDzbZ.js → index-Bp1l4hSv.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-10oVnAAH.js → index-CWV9XZiG.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-yHOVb4rc.js → index-DE3jI_dv.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/jsx-runtime-D_zvdyIk.js +9 -0
- package/codeyam-cli/src/webserver/build/client/assets/labs-B_IX45ih.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-BAXYRVEO.js → loader-circle-De-7qQ2u.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/manifest-2ef99f38.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/memory-Cx2xEx7s.js +101 -0
- package/codeyam-cli/src/webserver/build/client/assets/{pause-DTAcYxBt.js → pause-CFxEKL1u.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/root-BxUQigda.js +67 -0
- package/codeyam-cli/src/webserver/build/client/assets/{search-fKo7v0Zo.js → search-BdBb5aqc.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/settings-DdE-Untf.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/simulations-DSCdE99u.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{terminal-BG4heKCG.js → terminal-CrplD4b1.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-DtSmdtM4.js → triangle-alert-DqJ0j69l.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-DhXHbEjP.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-C14nCb1q.js → useLastLogLine-BNd5hYuW.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/useReportContext-Cy5Qg_UR.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/useToast-5HR2j9ZE.js +1 -0
- package/codeyam-cli/src/webserver/build/client/sound-test.html +98 -0
- package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-BPmOG9bE.js +13 -0
- package/codeyam-cli/src/webserver/build/server/assets/index-Cd-ufawF.js +1 -0
- package/codeyam-cli/src/webserver/build/server/assets/init-CzeBGOto.js +10 -0
- package/codeyam-cli/src/webserver/build/server/assets/progress-CHTtrxFG.js +1 -0
- package/codeyam-cli/src/webserver/build/server/assets/server-build-Dht7CKXY.js +552 -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/editorProxy.js +586 -17
- package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
- package/codeyam-cli/src/webserver/idleDetector.js +106 -0
- package/codeyam-cli/src/webserver/idleDetector.js.map +1 -0
- package/codeyam-cli/src/webserver/mockStateEvents.js +28 -0
- package/codeyam-cli/src/webserver/mockStateEvents.js.map +1 -0
- package/codeyam-cli/src/webserver/public/sound-test.html +98 -0
- package/codeyam-cli/src/webserver/scripts/codeyam-preload.mjs +21 -3
- package/codeyam-cli/src/webserver/scripts/journalCapture.ts +130 -4
- package/codeyam-cli/src/webserver/server.js +100 -34
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +242 -48
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/__tests__/editor-step-hook.prompt-capture.test.ts +118 -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 +86 -5
- package/codeyam-cli/templates/codeyam-editor-reference.md +214 -0
- package/codeyam-cli/templates/editor-step-hook.py +193 -54
- 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/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/lib/prisma.ts +9 -4
- package/codeyam-cli/templates/nextjs-prisma-sqlite/env +4 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/gitignore +1 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +2 -1
- package/codeyam-cli/templates/nextjs-prisma-sqlite/prisma/seed.ts +4 -1
- package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +127 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/README.md +52 -0
- package/codeyam-cli/templates/{nextjs-prisma-sqlite/PRISMA_SETUP.md → nextjs-prisma-supabase/SUPABASE_SETUP.md} +37 -17
- 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/seed-adapters/supabase.ts +282 -0
- package/codeyam-cli/templates/skills/codeyam-dev-mode/SKILL.md +1 -1
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +90 -9
- package/package.json +2 -1
- package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +101 -0
- package/packages/database/src/lib/kysely/tables/editorScenariosTable.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/updateCommitMetadata.js +0 -25
- package/packages/database/src/lib/updateCommitMetadata.js.map +1 -1
- package/packages/types/src/enums/ProjectFramework.js +2 -0
- package/packages/types/src/enums/ProjectFramework.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/CopyButton-DmJveP3T.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-g3saevPb.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-Bu6c6aDe.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-DYFW3lDD.js +0 -25
- package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DLeucoVX.js +0 -3
- package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-BED4B6sP.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bb5uFQ5V.js +0 -34
- package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-C8OKAR5x.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-C4pqxYJB.js +0 -51
- package/codeyam-cli/src/webserver/build/client/assets/createLucideIcon-CMT1jU2q.js +0 -21
- package/codeyam-cli/src/webserver/build/client/assets/dev.empty-CltMNppm.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor-Rfq_y0VR.js +0 -10
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-GNwaLSmC.js +0 -41
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-CCa2trIL.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CF164ouH.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-p9hhkjJM.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-cPo8LiG3.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/files-DO4CZ16O.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/git-CdN8sCqs.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/globals-Bd0cs8vw.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/labs-Zk7ryIM1.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-9ab0aba3.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/memory-Dg0mvYrI.js +0 -96
- package/codeyam-cli/src/webserver/build/client/assets/root-3ciuWk-c.js +0 -67
- package/codeyam-cli/src/webserver/build/client/assets/settings-DfuTtcJP.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/simulations-B3aOzpCZ.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-CrAK28Bc.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useReportContext-O-jkvSPx.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useToast-9FIWuYfK.js +0 -1
- package/codeyam-cli/src/webserver/build/server/assets/index-DCxIbVvl.js +0 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-E-peu3XZ.js +0 -367
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { buildReverseDependencyGraph, classifyDirectChanges, computeEntityChangeStatus, buildChangedFilesMap, pageNameFromUrl, buildEntityInfosFromScenarios, buildEntityInfosFromGlossary, filterGroupsByChangeStatus, filterGlossaryByChangeStatus, filterScenarioScreenshotsByChangeStatus, } from "../entityChangeStatus.js";
|
|
2
|
-
import { scanPageFilePaths, detectFirstFeature, readFeatureStartedAt, } from "../entityChangeStatus.server.js";
|
|
1
|
+
import { buildReverseDependencyGraph, classifyDirectChanges, computeEntityChangeStatus, buildChangedFilesMap, pageNameFromUrl, isIsolationUrl, scenarioEntityName, buildEntityInfosFromScenarios, buildEntityInfosFromGlossary, filterGroupsByChangeStatus, filterGlossaryByChangeStatus, filterScenarioScreenshotsByChangeStatus, } from "../entityChangeStatus.js";
|
|
2
|
+
import { scanPageFilePaths, detectFirstFeature, readFeatureStartedAt, readEditorStep, readEditorSessionId, } from "../entityChangeStatus.server.js";
|
|
3
3
|
import * as fsModule from 'fs';
|
|
4
4
|
import * as pathModule from 'path';
|
|
5
5
|
import * as os from 'os';
|
|
@@ -128,10 +128,20 @@ describe('entityChangeStatus', () => {
|
|
|
128
128
|
// This is an edge case — empty string is falsy so returns Home
|
|
129
129
|
expect(pageNameFromUrl('')).toBe('Home');
|
|
130
130
|
});
|
|
131
|
+
it('should return Home for root path with query string', () => {
|
|
132
|
+
expect(pageNameFromUrl('/?demo=empty')).toBe('Home');
|
|
133
|
+
expect(pageNameFromUrl('/?state=seeded')).toBe('Home');
|
|
134
|
+
expect(pageNameFromUrl('/?tab=overview&sort=date')).toBe('Home');
|
|
135
|
+
});
|
|
131
136
|
it('should handle dynamic route segments', () => {
|
|
132
137
|
expect(pageNameFromUrl('/drinks/[id]')).toBe('Drinks');
|
|
133
138
|
expect(pageNameFromUrl('/users/[userId]/posts')).toBe('Users');
|
|
134
139
|
});
|
|
140
|
+
it('should strip file extensions from URL segments', () => {
|
|
141
|
+
expect(pageNameFromUrl('/library.html')).toBe('Library');
|
|
142
|
+
expect(pageNameFromUrl('/about.htm')).toBe('About');
|
|
143
|
+
expect(pageNameFromUrl('/library.html?article=a1')).toBe('Library');
|
|
144
|
+
});
|
|
135
145
|
});
|
|
136
146
|
// ── buildEntityInfosFromScenarios ─────────────────────────────────────
|
|
137
147
|
describe('buildEntityInfosFromScenarios', () => {
|
|
@@ -1044,6 +1054,34 @@ describe('entityChangeStatus', () => {
|
|
|
1044
1054
|
expect(result).toHaveLength(1);
|
|
1045
1055
|
expect(result[0].importedBy).toBeDefined();
|
|
1046
1056
|
});
|
|
1057
|
+
it('should use displayName for non-app/ pageFilePath instead of collapsing to Home', () => {
|
|
1058
|
+
// Margo bug: Chrome extension has two entry points:
|
|
1059
|
+
// "/" → src/popup/App.tsx (Home)
|
|
1060
|
+
// "/library.html" → src/library/FullPageLibrary.tsx (Library)
|
|
1061
|
+
// Without displayName, buildRoutePattern('src/library/FullPageLibrary.tsx')
|
|
1062
|
+
// returns '/' since it doesn't start with 'app/', collapsing both to "Home".
|
|
1063
|
+
const scenarios = [
|
|
1064
|
+
{
|
|
1065
|
+
componentName: null,
|
|
1066
|
+
componentPath: null,
|
|
1067
|
+
url: '/',
|
|
1068
|
+
pageFilePath: 'src/popup/App.tsx',
|
|
1069
|
+
displayName: 'Home',
|
|
1070
|
+
},
|
|
1071
|
+
{
|
|
1072
|
+
componentName: null,
|
|
1073
|
+
componentPath: null,
|
|
1074
|
+
url: '/library.html',
|
|
1075
|
+
pageFilePath: 'src/library/FullPageLibrary.tsx',
|
|
1076
|
+
displayName: 'Library',
|
|
1077
|
+
},
|
|
1078
|
+
];
|
|
1079
|
+
const result = buildEntityInfosFromScenarios(scenarios, {}, []);
|
|
1080
|
+
expect(result).toHaveLength(2);
|
|
1081
|
+
expect(result.map((e) => e.name).sort()).toEqual(['Home', 'Library']);
|
|
1082
|
+
expect(result.find((e) => e.name === 'Home')?.filePath).toBe('src/popup/App.tsx');
|
|
1083
|
+
expect(result.find((e) => e.name === 'Library')?.filePath).toBe('src/library/FullPageLibrary.tsx');
|
|
1084
|
+
});
|
|
1047
1085
|
});
|
|
1048
1086
|
// ── Full pipeline regression tests ───────────────────────────────
|
|
1049
1087
|
describe('full pipeline regression tests', () => {
|
|
@@ -1298,81 +1336,274 @@ describe('entityChangeStatus', () => {
|
|
|
1298
1336
|
expect(result).toEqual([]);
|
|
1299
1337
|
});
|
|
1300
1338
|
});
|
|
1339
|
+
// ── scenarioEntityName ──────────────────────────────────────────────
|
|
1340
|
+
describe('scenarioEntityName', () => {
|
|
1341
|
+
it('should return componentName when set', () => {
|
|
1342
|
+
expect(scenarioEntityName({
|
|
1343
|
+
componentName: 'ReviewCard',
|
|
1344
|
+
url: '/isolated-components/ReviewCard?s=Default',
|
|
1345
|
+
})).toBe('ReviewCard');
|
|
1346
|
+
});
|
|
1347
|
+
it('should return page name from URL when componentName is null', () => {
|
|
1348
|
+
expect(scenarioEntityName({ componentName: null, url: '/drinks/1' })).toBe('Drinks');
|
|
1349
|
+
});
|
|
1350
|
+
it('should return Home for root URL when componentName is null', () => {
|
|
1351
|
+
expect(scenarioEntityName({ componentName: null, url: '/' })).toBe('Home');
|
|
1352
|
+
});
|
|
1353
|
+
it('should return Home when both componentName and url are null', () => {
|
|
1354
|
+
expect(scenarioEntityName({ componentName: null, url: null })).toBe('Home');
|
|
1355
|
+
});
|
|
1356
|
+
it('should return page name when componentName is undefined', () => {
|
|
1357
|
+
expect(scenarioEntityName({ url: '/settings' })).toBe('Settings');
|
|
1358
|
+
});
|
|
1359
|
+
it('should return Home when no fields provided', () => {
|
|
1360
|
+
expect(scenarioEntityName({})).toBe('Home');
|
|
1361
|
+
});
|
|
1362
|
+
});
|
|
1301
1363
|
// ── filterScenarioScreenshotsByChangeStatus ───────────────────────────
|
|
1302
1364
|
describe('filterScenarioScreenshotsByChangeStatus', () => {
|
|
1303
|
-
const screenshots = [
|
|
1304
|
-
{ name: 'Busy Month', path: 'busy.png' },
|
|
1305
|
-
{ name: 'Empty Calendar', path: 'empty.png' },
|
|
1306
|
-
{ name: 'EventPill - Default', path: 'pill.png' },
|
|
1307
|
-
{ name: 'ErrorState - Server Error', path: 'error.png' },
|
|
1308
|
-
{ name: 'CalendarGrid - Default', path: 'grid.png' },
|
|
1309
|
-
];
|
|
1310
|
-
const pageNames = new Set(['Home']);
|
|
1311
1365
|
it('should return all screenshots when entityChangeStatus is undefined', () => {
|
|
1312
|
-
|
|
1366
|
+
const screenshots = [
|
|
1367
|
+
{ name: 'Busy Month', path: 'busy.png', componentName: null, url: '/' },
|
|
1368
|
+
{
|
|
1369
|
+
name: 'EventPill - Default',
|
|
1370
|
+
path: 'pill.png',
|
|
1371
|
+
componentName: 'EventPill',
|
|
1372
|
+
url: '/isolated-components/EventPill',
|
|
1373
|
+
},
|
|
1374
|
+
];
|
|
1375
|
+
expect(filterScenarioScreenshotsByChangeStatus(screenshots, undefined)).toEqual(screenshots);
|
|
1313
1376
|
});
|
|
1314
1377
|
it('should return all screenshots when entityChangeStatus is empty', () => {
|
|
1315
|
-
|
|
1378
|
+
const screenshots = [
|
|
1379
|
+
{ name: 'Busy Month', path: 'busy.png', componentName: null, url: '/' },
|
|
1380
|
+
];
|
|
1381
|
+
expect(filterScenarioScreenshotsByChangeStatus(screenshots, {})).toEqual(screenshots);
|
|
1316
1382
|
});
|
|
1317
|
-
it('should
|
|
1383
|
+
it('should include component scenarios whose entity has a change status', () => {
|
|
1384
|
+
const screenshots = [
|
|
1385
|
+
{
|
|
1386
|
+
name: 'EventPill - Default',
|
|
1387
|
+
path: 'pill.png',
|
|
1388
|
+
componentName: 'EventPill',
|
|
1389
|
+
url: '/isolated-components/EventPill',
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
name: 'ErrorState - Server Error',
|
|
1393
|
+
path: 'error.png',
|
|
1394
|
+
componentName: 'ErrorState',
|
|
1395
|
+
url: '/isolated-components/ErrorState',
|
|
1396
|
+
},
|
|
1397
|
+
{
|
|
1398
|
+
name: 'CalendarGrid - Default',
|
|
1399
|
+
path: 'grid.png',
|
|
1400
|
+
componentName: 'CalendarGrid',
|
|
1401
|
+
url: '/isolated-components/CalendarGrid',
|
|
1402
|
+
},
|
|
1403
|
+
];
|
|
1318
1404
|
const status = {
|
|
1319
1405
|
ErrorState: { status: 'new' },
|
|
1320
|
-
|
|
1406
|
+
EventPill: { status: 'edited' },
|
|
1321
1407
|
};
|
|
1322
|
-
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status
|
|
1323
|
-
// App-level screenshots (no prefix) match because Home page has status
|
|
1324
|
-
// "ErrorState - *" matches the ErrorState entity
|
|
1408
|
+
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status);
|
|
1325
1409
|
expect(result.map((s) => s.name)).toEqual([
|
|
1326
|
-
'
|
|
1327
|
-
'Empty Calendar',
|
|
1410
|
+
'EventPill - Default',
|
|
1328
1411
|
'ErrorState - Server Error',
|
|
1329
1412
|
]);
|
|
1330
1413
|
});
|
|
1331
|
-
it('should
|
|
1332
|
-
const
|
|
1333
|
-
{ name: 'Busy Month', path: 'busy.png' },
|
|
1334
|
-
{
|
|
1414
|
+
it('should include page scenarios whose page entity has a change status', () => {
|
|
1415
|
+
const screenshots = [
|
|
1416
|
+
{ name: 'Busy Month', path: 'busy.png', componentName: null, url: '/' },
|
|
1417
|
+
{
|
|
1418
|
+
name: 'Empty Calendar',
|
|
1419
|
+
path: 'empty.png',
|
|
1420
|
+
componentName: null,
|
|
1421
|
+
url: '/',
|
|
1422
|
+
},
|
|
1335
1423
|
];
|
|
1336
1424
|
const status = {
|
|
1337
1425
|
Home: { status: 'edited' },
|
|
1338
1426
|
};
|
|
1339
|
-
const result = filterScenarioScreenshotsByChangeStatus(
|
|
1340
|
-
expect(result).toEqual(
|
|
1427
|
+
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status);
|
|
1428
|
+
expect(result).toEqual(screenshots);
|
|
1341
1429
|
});
|
|
1342
|
-
it('should exclude
|
|
1430
|
+
it('should exclude page scenarios when their page entity has no status', () => {
|
|
1431
|
+
const screenshots = [
|
|
1432
|
+
{ name: 'Busy Month', path: 'busy.png', componentName: null, url: '/' },
|
|
1433
|
+
{
|
|
1434
|
+
name: 'EventPill - Default',
|
|
1435
|
+
path: 'pill.png',
|
|
1436
|
+
componentName: 'EventPill',
|
|
1437
|
+
url: '/isolated-components/EventPill',
|
|
1438
|
+
},
|
|
1439
|
+
];
|
|
1343
1440
|
const status = {
|
|
1344
1441
|
SomeComponent: { status: 'new' },
|
|
1345
1442
|
};
|
|
1346
|
-
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status
|
|
1347
|
-
//
|
|
1348
|
-
// SomeComponent doesn't match
|
|
1443
|
+
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status);
|
|
1444
|
+
// Home page has no status, so "Busy Month" is excluded
|
|
1445
|
+
// SomeComponent doesn't match EventPill either
|
|
1349
1446
|
expect(result).toEqual([]);
|
|
1350
1447
|
});
|
|
1351
|
-
it('should include
|
|
1448
|
+
it('should include both page and component scenarios when both entities have status', () => {
|
|
1449
|
+
const screenshots = [
|
|
1450
|
+
{ name: 'Busy Month', path: 'busy.png', componentName: null, url: '/' },
|
|
1451
|
+
{
|
|
1452
|
+
name: 'Empty Calendar',
|
|
1453
|
+
path: 'empty.png',
|
|
1454
|
+
componentName: null,
|
|
1455
|
+
url: '/',
|
|
1456
|
+
},
|
|
1457
|
+
{
|
|
1458
|
+
name: 'EventPill - Default',
|
|
1459
|
+
path: 'pill.png',
|
|
1460
|
+
componentName: 'EventPill',
|
|
1461
|
+
url: '/isolated-components/EventPill',
|
|
1462
|
+
},
|
|
1463
|
+
];
|
|
1352
1464
|
const status = {
|
|
1353
1465
|
Home: { status: 'edited' },
|
|
1354
1466
|
EventPill: { status: 'new' },
|
|
1355
1467
|
};
|
|
1356
|
-
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status
|
|
1468
|
+
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status);
|
|
1357
1469
|
expect(result.map((s) => s.name)).toEqual([
|
|
1358
1470
|
'Busy Month',
|
|
1359
1471
|
'Empty Calendar',
|
|
1360
1472
|
'EventPill - Default',
|
|
1361
1473
|
]);
|
|
1362
1474
|
});
|
|
1363
|
-
it('should handle
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
{
|
|
1475
|
+
it('should handle page scenarios with dashes in names using URL, not name parsing', () => {
|
|
1476
|
+
// The original bug: "Drink Detail - Earl Grey" was parsed as component "Drink Detail"
|
|
1477
|
+
const screenshots = [
|
|
1478
|
+
{
|
|
1479
|
+
name: 'Drink Detail - Earl Grey',
|
|
1480
|
+
path: 'earl.png',
|
|
1481
|
+
componentName: null,
|
|
1482
|
+
url: '/drinks/1',
|
|
1483
|
+
},
|
|
1484
|
+
{
|
|
1485
|
+
name: 'Drink Detail - No Reviews',
|
|
1486
|
+
path: 'no-reviews.png',
|
|
1487
|
+
componentName: null,
|
|
1488
|
+
url: '/drinks/1',
|
|
1489
|
+
},
|
|
1490
|
+
{
|
|
1491
|
+
name: 'ReviewCard - Default',
|
|
1492
|
+
path: 'review.png',
|
|
1493
|
+
componentName: 'ReviewCard',
|
|
1494
|
+
url: '/isolated-components/ReviewCard',
|
|
1495
|
+
},
|
|
1496
|
+
{
|
|
1497
|
+
name: 'Full Catalog',
|
|
1498
|
+
path: 'catalog.png',
|
|
1499
|
+
componentName: null,
|
|
1500
|
+
url: '/',
|
|
1501
|
+
},
|
|
1502
|
+
];
|
|
1503
|
+
const status = {
|
|
1504
|
+
Drinks: { status: 'new' },
|
|
1505
|
+
ReviewCard: { status: 'new' },
|
|
1506
|
+
Home: { status: 'edited' },
|
|
1507
|
+
};
|
|
1508
|
+
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status);
|
|
1509
|
+
expect(result.map((s) => s.name)).toEqual([
|
|
1510
|
+
'Drink Detail - Earl Grey',
|
|
1511
|
+
'Drink Detail - No Reviews',
|
|
1512
|
+
'ReviewCard - Default',
|
|
1513
|
+
'Full Catalog',
|
|
1514
|
+
]);
|
|
1515
|
+
});
|
|
1516
|
+
it('should exclude page scenarios for unchanged pages even with dashes in names', () => {
|
|
1517
|
+
const screenshots = [
|
|
1518
|
+
{
|
|
1519
|
+
name: 'Drink Detail - Earl Grey',
|
|
1520
|
+
path: 'earl.png',
|
|
1521
|
+
componentName: null,
|
|
1522
|
+
url: '/drinks/1',
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
name: 'ReviewCard - Default',
|
|
1526
|
+
path: 'review.png',
|
|
1527
|
+
componentName: 'ReviewCard',
|
|
1528
|
+
url: '/isolated-components/ReviewCard',
|
|
1529
|
+
},
|
|
1530
|
+
];
|
|
1531
|
+
const status = {
|
|
1532
|
+
ReviewCard: { status: 'edited' },
|
|
1533
|
+
};
|
|
1534
|
+
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status);
|
|
1535
|
+
// Drinks page has no status, so "Drink Detail" page scenarios are excluded
|
|
1536
|
+
expect(result.map((s) => s.name)).toEqual(['ReviewCard - Default']);
|
|
1537
|
+
});
|
|
1538
|
+
it('should use componentName for matching, not the name prefix', () => {
|
|
1539
|
+
const screenshots = [
|
|
1540
|
+
{
|
|
1541
|
+
name: 'Card Loading State - Spinner',
|
|
1542
|
+
path: 'spinner.png',
|
|
1543
|
+
componentName: 'LoadingCard',
|
|
1544
|
+
url: '/isolated-components/LoadingCard',
|
|
1545
|
+
},
|
|
1546
|
+
];
|
|
1547
|
+
const status = {
|
|
1548
|
+
LoadingCard: { status: 'new' },
|
|
1549
|
+
};
|
|
1550
|
+
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status);
|
|
1551
|
+
expect(result.map((s) => s.name)).toEqual([
|
|
1552
|
+
'Card Loading State - Spinner',
|
|
1553
|
+
]);
|
|
1554
|
+
});
|
|
1555
|
+
it('should exclude component scenario when its entity has no status', () => {
|
|
1556
|
+
const screenshots = [
|
|
1557
|
+
{
|
|
1558
|
+
name: 'Card Loading State - Spinner',
|
|
1559
|
+
path: 'spinner.png',
|
|
1560
|
+
componentName: 'LoadingCard',
|
|
1561
|
+
url: '/isolated-components/LoadingCard',
|
|
1562
|
+
},
|
|
1563
|
+
];
|
|
1564
|
+
const status = {
|
|
1565
|
+
SomeOtherComponent: { status: 'new' },
|
|
1566
|
+
};
|
|
1567
|
+
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status);
|
|
1568
|
+
expect(result).toEqual([]);
|
|
1569
|
+
});
|
|
1570
|
+
it('should filter per-page independently (not all-or-nothing for pages)', () => {
|
|
1571
|
+
// Only Drinks page changed, not Home — scenarios for each page should be filtered independently
|
|
1572
|
+
const screenshots = [
|
|
1573
|
+
{
|
|
1574
|
+
name: 'Full Catalog',
|
|
1575
|
+
path: 'catalog.png',
|
|
1576
|
+
componentName: null,
|
|
1577
|
+
url: '/',
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
name: 'Drink Detail - Earl Grey',
|
|
1581
|
+
path: 'earl.png',
|
|
1582
|
+
componentName: null,
|
|
1583
|
+
url: '/drinks/1',
|
|
1584
|
+
},
|
|
1585
|
+
];
|
|
1586
|
+
const status = {
|
|
1587
|
+
Drinks: { status: 'new' },
|
|
1588
|
+
};
|
|
1589
|
+
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status);
|
|
1590
|
+
// Home page has no status, so "Full Catalog" is excluded
|
|
1591
|
+
// Drinks page has status, so "Drink Detail" is included
|
|
1592
|
+
expect(result.map((s) => s.name)).toEqual(['Drink Detail - Earl Grey']);
|
|
1593
|
+
});
|
|
1594
|
+
it('should fall back to name-based parsing when componentName is not present (backward compat)', () => {
|
|
1595
|
+
const screenshots = [
|
|
1367
1596
|
{ name: 'EventPill - Default', path: 'pill.png' },
|
|
1597
|
+
{ name: 'Full Calendar', path: 'cal.png' },
|
|
1368
1598
|
];
|
|
1369
1599
|
const status = {
|
|
1600
|
+
EventPill: { status: 'new' },
|
|
1370
1601
|
Home: { status: 'edited' },
|
|
1371
1602
|
};
|
|
1372
|
-
const result = filterScenarioScreenshotsByChangeStatus(
|
|
1603
|
+
const result = filterScenarioScreenshotsByChangeStatus(screenshots, status);
|
|
1373
1604
|
expect(result.map((s) => s.name)).toEqual([
|
|
1374
|
-
'
|
|
1375
|
-
'
|
|
1605
|
+
'EventPill - Default',
|
|
1606
|
+
'Full Calendar',
|
|
1376
1607
|
]);
|
|
1377
1608
|
});
|
|
1378
1609
|
});
|
|
@@ -1398,7 +1629,7 @@ describe('entityChangeStatus', () => {
|
|
|
1398
1629
|
});
|
|
1399
1630
|
fsModule.writeFileSync(pathModule.join(appDir, 'settings', 'profile', 'page.tsx'), '');
|
|
1400
1631
|
const result = scanPageFilePaths(tempDir);
|
|
1401
|
-
expect(result).toEqual({
|
|
1632
|
+
expect(result.map).toEqual({
|
|
1402
1633
|
Home: 'app/page.tsx',
|
|
1403
1634
|
Drinks: 'app/drinks/page.tsx',
|
|
1404
1635
|
Settings: 'app/settings/profile/page.tsx',
|
|
@@ -1409,20 +1640,96 @@ describe('entityChangeStatus', () => {
|
|
|
1409
1640
|
fsModule.mkdirSync(appDir, { recursive: true });
|
|
1410
1641
|
fsModule.writeFileSync(pathModule.join(appDir, 'page.js'), '');
|
|
1411
1642
|
const result = scanPageFilePaths(tempDir);
|
|
1412
|
-
expect(result).toEqual({ Home: 'app/page.js' });
|
|
1643
|
+
expect(result.map).toEqual({ Home: 'app/page.js' });
|
|
1413
1644
|
});
|
|
1414
|
-
it('should skip
|
|
1645
|
+
it('should skip isolated-components directories', () => {
|
|
1415
1646
|
const appDir = pathModule.join(tempDir, 'app');
|
|
1416
|
-
fsModule.mkdirSync(pathModule.join(appDir, '
|
|
1647
|
+
fsModule.mkdirSync(pathModule.join(appDir, 'isolated-components', 'test'), {
|
|
1417
1648
|
recursive: true,
|
|
1418
1649
|
});
|
|
1419
|
-
fsModule.writeFileSync(pathModule.join(appDir, '
|
|
1650
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'isolated-components', 'test', 'page.tsx'), '');
|
|
1420
1651
|
const result = scanPageFilePaths(tempDir);
|
|
1421
|
-
expect(result).toEqual({});
|
|
1652
|
+
expect(result.map).toEqual({});
|
|
1422
1653
|
});
|
|
1423
1654
|
it('should return empty map when app/ does not exist', () => {
|
|
1424
1655
|
const result = scanPageFilePaths(tempDir);
|
|
1425
|
-
expect(result).toEqual({});
|
|
1656
|
+
expect(result.map).toEqual({});
|
|
1657
|
+
});
|
|
1658
|
+
// ── Expo Router support ──────────────────────────────────────────────
|
|
1659
|
+
it('should find index.tsx as Home page (Expo Router)', () => {
|
|
1660
|
+
const appDir = pathModule.join(tempDir, 'app');
|
|
1661
|
+
fsModule.mkdirSync(appDir, { recursive: true });
|
|
1662
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'index.tsx'), '');
|
|
1663
|
+
const result = scanPageFilePaths(tempDir);
|
|
1664
|
+
expect(result.map).toEqual({ Home: 'app/index.tsx' });
|
|
1665
|
+
});
|
|
1666
|
+
it('should find named route files as pages (Expo Router)', () => {
|
|
1667
|
+
const appDir = pathModule.join(tempDir, 'app');
|
|
1668
|
+
fsModule.mkdirSync(appDir, { recursive: true });
|
|
1669
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'index.tsx'), '');
|
|
1670
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'add-tea.tsx'), '');
|
|
1671
|
+
fsModule.mkdirSync(pathModule.join(appDir, 'tea'), { recursive: true });
|
|
1672
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'tea', '[id].tsx'), '');
|
|
1673
|
+
const result = scanPageFilePaths(tempDir);
|
|
1674
|
+
expect(result.map).toEqual({
|
|
1675
|
+
Home: 'app/index.tsx',
|
|
1676
|
+
'Add-tea': 'app/add-tea.tsx',
|
|
1677
|
+
Tea: 'app/tea/[id].tsx',
|
|
1678
|
+
});
|
|
1679
|
+
});
|
|
1680
|
+
it('should skip _layout.tsx files (Expo Router)', () => {
|
|
1681
|
+
const appDir = pathModule.join(tempDir, 'app');
|
|
1682
|
+
fsModule.mkdirSync(appDir, { recursive: true });
|
|
1683
|
+
fsModule.writeFileSync(pathModule.join(appDir, '_layout.tsx'), '');
|
|
1684
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'index.tsx'), '');
|
|
1685
|
+
const result = scanPageFilePaths(tempDir);
|
|
1686
|
+
expect(result.map).toEqual({ Home: 'app/index.tsx' });
|
|
1687
|
+
});
|
|
1688
|
+
it('should handle route groups like (tabs) transparently (Expo Router)', () => {
|
|
1689
|
+
const appDir = pathModule.join(tempDir, 'app');
|
|
1690
|
+
const tabsDir = pathModule.join(appDir, '(tabs)');
|
|
1691
|
+
fsModule.mkdirSync(tabsDir, { recursive: true });
|
|
1692
|
+
fsModule.writeFileSync(pathModule.join(tabsDir, '_layout.tsx'), '');
|
|
1693
|
+
fsModule.writeFileSync(pathModule.join(tabsDir, 'index.tsx'), '');
|
|
1694
|
+
fsModule.writeFileSync(pathModule.join(tabsDir, 'settings.tsx'), '');
|
|
1695
|
+
const result = scanPageFilePaths(tempDir);
|
|
1696
|
+
expect(result.map).toEqual({
|
|
1697
|
+
Home: 'app/(tabs)/index.tsx',
|
|
1698
|
+
Settings: 'app/(tabs)/settings.tsx',
|
|
1699
|
+
});
|
|
1700
|
+
});
|
|
1701
|
+
it('should prefer page.tsx over index.tsx when both exist (Next.js priority)', () => {
|
|
1702
|
+
const appDir = pathModule.join(tempDir, 'app');
|
|
1703
|
+
fsModule.mkdirSync(appDir, { recursive: true });
|
|
1704
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'page.tsx'), '');
|
|
1705
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'index.tsx'), '');
|
|
1706
|
+
const result = scanPageFilePaths(tempDir);
|
|
1707
|
+
expect(result.map).toEqual({ Home: 'app/page.tsx' });
|
|
1708
|
+
});
|
|
1709
|
+
it('should include all page files in allFiles when multiple routes share a page name', () => {
|
|
1710
|
+
const appDir = pathModule.join(tempDir, 'app');
|
|
1711
|
+
fsModule.mkdirSync(appDir, { recursive: true });
|
|
1712
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'page.tsx'), '');
|
|
1713
|
+
fsModule.mkdirSync(pathModule.join(appDir, 'feedback', '[id]', 'edit'), {
|
|
1714
|
+
recursive: true,
|
|
1715
|
+
});
|
|
1716
|
+
fsModule.mkdirSync(pathModule.join(appDir, 'feedback', 'new'), {
|
|
1717
|
+
recursive: true,
|
|
1718
|
+
});
|
|
1719
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'feedback', '[id]', 'page.tsx'), '');
|
|
1720
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'feedback', '[id]', 'edit', 'page.tsx'), '');
|
|
1721
|
+
fsModule.writeFileSync(pathModule.join(appDir, 'feedback', 'new', 'page.tsx'), '');
|
|
1722
|
+
const result = scanPageFilePaths(tempDir);
|
|
1723
|
+
// Map picks one canonical file per page name
|
|
1724
|
+
expect(result.map['Home']).toBe('app/page.tsx');
|
|
1725
|
+
expect(result.map['Feedback']).toBeDefined();
|
|
1726
|
+
// allFiles includes ALL page files, even those that collided on page name
|
|
1727
|
+
expect(result.allFiles.sort()).toEqual([
|
|
1728
|
+
'app/feedback/[id]/edit/page.tsx',
|
|
1729
|
+
'app/feedback/[id]/page.tsx',
|
|
1730
|
+
'app/feedback/new/page.tsx',
|
|
1731
|
+
'app/page.tsx',
|
|
1732
|
+
]);
|
|
1426
1733
|
});
|
|
1427
1734
|
});
|
|
1428
1735
|
// ── detectFirstFeature ─────────────────────────────────────────────────
|
|
@@ -1489,5 +1796,294 @@ describe('entityChangeStatus', () => {
|
|
|
1489
1796
|
expect(readFeatureStartedAt(tempDir)).toBeNull();
|
|
1490
1797
|
});
|
|
1491
1798
|
});
|
|
1799
|
+
// ── readEditorStep ──────────────────────────────────────────────────────
|
|
1800
|
+
describe('readEditorStep', () => {
|
|
1801
|
+
let tempDir;
|
|
1802
|
+
beforeEach(() => {
|
|
1803
|
+
tempDir = fsModule.mkdtempSync(pathModule.join(os.tmpdir(), 'editorstep-test-'));
|
|
1804
|
+
fsModule.mkdirSync(pathModule.join(tempDir, '.codeyam'), {
|
|
1805
|
+
recursive: true,
|
|
1806
|
+
});
|
|
1807
|
+
});
|
|
1808
|
+
afterEach(() => {
|
|
1809
|
+
fsModule.rmSync(tempDir, { recursive: true, force: true });
|
|
1810
|
+
});
|
|
1811
|
+
it('should return step and label from editor-step.json', () => {
|
|
1812
|
+
fsModule.writeFileSync(pathModule.join(tempDir, '.codeyam', 'editor-step.json'), JSON.stringify({ step: 3, label: 'Confirm', feature: 'Add login' }));
|
|
1813
|
+
expect(readEditorStep(tempDir)).toEqual({ step: 3, label: 'Confirm' });
|
|
1814
|
+
});
|
|
1815
|
+
it('should return null when file does not exist', () => {
|
|
1816
|
+
expect(readEditorStep(tempDir)).toBeNull();
|
|
1817
|
+
});
|
|
1818
|
+
it('should return null when step field is missing', () => {
|
|
1819
|
+
fsModule.writeFileSync(pathModule.join(tempDir, '.codeyam', 'editor-step.json'), JSON.stringify({ feature: 'Add login' }));
|
|
1820
|
+
expect(readEditorStep(tempDir)).toBeNull();
|
|
1821
|
+
});
|
|
1822
|
+
it('should return null for invalid JSON', () => {
|
|
1823
|
+
fsModule.writeFileSync(pathModule.join(tempDir, '.codeyam', 'editor-step.json'), 'not json');
|
|
1824
|
+
expect(readEditorStep(tempDir)).toBeNull();
|
|
1825
|
+
});
|
|
1826
|
+
});
|
|
1827
|
+
// ── readEditorSessionId ─────────────────────────────────────────────────
|
|
1828
|
+
describe('readEditorSessionId', () => {
|
|
1829
|
+
let tempDir;
|
|
1830
|
+
beforeEach(() => {
|
|
1831
|
+
tempDir = fsModule.mkdtempSync(pathModule.join(os.tmpdir(), 'sessionid-test-'));
|
|
1832
|
+
fsModule.mkdirSync(pathModule.join(tempDir, '.codeyam'), {
|
|
1833
|
+
recursive: true,
|
|
1834
|
+
});
|
|
1835
|
+
});
|
|
1836
|
+
afterEach(() => {
|
|
1837
|
+
fsModule.rmSync(tempDir, { recursive: true, force: true });
|
|
1838
|
+
});
|
|
1839
|
+
it('should return session ID from claude-session-id.txt', () => {
|
|
1840
|
+
const uuid = '550e8400-e29b-41d4-a716-446655440000';
|
|
1841
|
+
fsModule.writeFileSync(pathModule.join(tempDir, '.codeyam', 'claude-session-id.txt'), uuid);
|
|
1842
|
+
expect(readEditorSessionId(tempDir)).toBe(uuid);
|
|
1843
|
+
});
|
|
1844
|
+
it('should trim whitespace from session ID', () => {
|
|
1845
|
+
fsModule.writeFileSync(pathModule.join(tempDir, '.codeyam', 'claude-session-id.txt'), ' abc-123 \n');
|
|
1846
|
+
expect(readEditorSessionId(tempDir)).toBe('abc-123');
|
|
1847
|
+
});
|
|
1848
|
+
it('should return null when file does not exist', () => {
|
|
1849
|
+
expect(readEditorSessionId(tempDir)).toBeNull();
|
|
1850
|
+
});
|
|
1851
|
+
it('should return null when file is empty', () => {
|
|
1852
|
+
fsModule.writeFileSync(pathModule.join(tempDir, '.codeyam', 'claude-session-id.txt'), '');
|
|
1853
|
+
expect(readEditorSessionId(tempDir)).toBeNull();
|
|
1854
|
+
});
|
|
1855
|
+
});
|
|
1856
|
+
describe('isIsolationUrl', () => {
|
|
1857
|
+
it('should return true for /isolated-components/ URLs', () => {
|
|
1858
|
+
expect(isIsolationUrl('/isolated-components/Foo')).toBe(true);
|
|
1859
|
+
expect(isIsolationUrl('/isolated-components/Foo?s=Default')).toBe(true);
|
|
1860
|
+
});
|
|
1861
|
+
it('should return true for /codeyam-isolate/ URLs', () => {
|
|
1862
|
+
expect(isIsolationUrl('/codeyam-isolate/Foo')).toBe(true);
|
|
1863
|
+
expect(isIsolationUrl('/codeyam-isolate/Foo?s=Default')).toBe(true);
|
|
1864
|
+
});
|
|
1865
|
+
it('should return false for regular page URLs', () => {
|
|
1866
|
+
expect(isIsolationUrl('/library')).toBe(false);
|
|
1867
|
+
expect(isIsolationUrl('/drinks/1')).toBe(false);
|
|
1868
|
+
expect(isIsolationUrl('/')).toBe(false);
|
|
1869
|
+
});
|
|
1870
|
+
it('should return false for null/undefined', () => {
|
|
1871
|
+
expect(isIsolationUrl(null)).toBe(false);
|
|
1872
|
+
expect(isIsolationUrl(undefined)).toBe(false);
|
|
1873
|
+
});
|
|
1874
|
+
});
|
|
1875
|
+
// ── buildReverseDependencyGraph: filePath-based importer resolution ────
|
|
1876
|
+
describe('buildReverseDependencyGraph — filePath-based resolution', () => {
|
|
1877
|
+
it('should resolve importer via filePath when DB name differs from display name', () => {
|
|
1878
|
+
// In Margo Reader: ArticleTable's importedBy lists "LibraryPage" as importer name
|
|
1879
|
+
// under filePath "app/library/page.tsx". But the entity list has name "Library"
|
|
1880
|
+
// with filePath "app/library/page.tsx". The reverse graph should map
|
|
1881
|
+
// ArticleTable → {"Library"} (not "LibraryPage").
|
|
1882
|
+
const entities = [
|
|
1883
|
+
{
|
|
1884
|
+
name: 'ArticleTable',
|
|
1885
|
+
filePath: 'app/components/ArticleTable.tsx',
|
|
1886
|
+
importedBy: {
|
|
1887
|
+
'app/library/page.tsx': {
|
|
1888
|
+
LibraryPage: { shas: ['sha1'] },
|
|
1889
|
+
},
|
|
1890
|
+
},
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
name: 'Library',
|
|
1894
|
+
filePath: 'app/library/page.tsx',
|
|
1895
|
+
},
|
|
1896
|
+
];
|
|
1897
|
+
const graph = buildReverseDependencyGraph(entities);
|
|
1898
|
+
expect(graph.get('ArticleTable')).toEqual(new Set(['Library']));
|
|
1899
|
+
});
|
|
1900
|
+
it('should fall back to DB importer name when filePath has no matching entity', () => {
|
|
1901
|
+
// When the importer filePath doesn't match any entity in the list,
|
|
1902
|
+
// fall back to the DB's importer name.
|
|
1903
|
+
const entities = [
|
|
1904
|
+
{
|
|
1905
|
+
name: 'Button',
|
|
1906
|
+
filePath: 'components/Button.tsx',
|
|
1907
|
+
importedBy: {
|
|
1908
|
+
'components/UnknownPage.tsx': {
|
|
1909
|
+
SomeComponent: { shas: ['sha1'] },
|
|
1910
|
+
},
|
|
1911
|
+
},
|
|
1912
|
+
},
|
|
1913
|
+
];
|
|
1914
|
+
const graph = buildReverseDependencyGraph(entities);
|
|
1915
|
+
expect(graph.get('Button')).toEqual(new Set(['SomeComponent']));
|
|
1916
|
+
});
|
|
1917
|
+
it('should resolve multiple importers via filePath correctly', () => {
|
|
1918
|
+
const entities = [
|
|
1919
|
+
{
|
|
1920
|
+
name: 'ArticleTableRow',
|
|
1921
|
+
filePath: 'app/components/ArticleTableRow.tsx',
|
|
1922
|
+
importedBy: {
|
|
1923
|
+
'app/components/ArticleTable.tsx': {
|
|
1924
|
+
ArticleTable: { shas: ['sha1'] },
|
|
1925
|
+
},
|
|
1926
|
+
'app/library/page.tsx': {
|
|
1927
|
+
LibraryPage: { shas: ['sha2'] },
|
|
1928
|
+
},
|
|
1929
|
+
},
|
|
1930
|
+
},
|
|
1931
|
+
{
|
|
1932
|
+
name: 'ArticleTable',
|
|
1933
|
+
filePath: 'app/components/ArticleTable.tsx',
|
|
1934
|
+
},
|
|
1935
|
+
{
|
|
1936
|
+
name: 'Library',
|
|
1937
|
+
filePath: 'app/library/page.tsx',
|
|
1938
|
+
},
|
|
1939
|
+
];
|
|
1940
|
+
const graph = buildReverseDependencyGraph(entities);
|
|
1941
|
+
expect(graph.get('ArticleTableRow')).toEqual(new Set(['ArticleTable', 'Library']));
|
|
1942
|
+
});
|
|
1943
|
+
});
|
|
1944
|
+
// ── buildEntityInfosFromScenarios: filePath-based metadata matching ────
|
|
1945
|
+
describe('buildEntityInfosFromScenarios — filePath-based metadata matching', () => {
|
|
1946
|
+
it('should attach importedBy metadata when entity names differ but filePaths match', () => {
|
|
1947
|
+
// Page scenario with displayName "Library" but DB entity named "LibraryPage"
|
|
1948
|
+
// with filePath "app/library/page.tsx". The metadata lookup should match by
|
|
1949
|
+
// filePath and attach importedBy.
|
|
1950
|
+
const scenarios = [
|
|
1951
|
+
{
|
|
1952
|
+
componentName: null,
|
|
1953
|
+
componentPath: null,
|
|
1954
|
+
pageFilePath: 'app/library/page.tsx',
|
|
1955
|
+
url: '/library',
|
|
1956
|
+
displayName: 'Library',
|
|
1957
|
+
},
|
|
1958
|
+
];
|
|
1959
|
+
const entitiesWithMetadata = [
|
|
1960
|
+
{
|
|
1961
|
+
name: 'LibraryPage',
|
|
1962
|
+
filePath: 'app/library/page.tsx',
|
|
1963
|
+
metadata: {
|
|
1964
|
+
importedBy: {
|
|
1965
|
+
'app/layout.tsx': {
|
|
1966
|
+
RootLayout: { shas: ['sha1'] },
|
|
1967
|
+
},
|
|
1968
|
+
},
|
|
1969
|
+
},
|
|
1970
|
+
},
|
|
1971
|
+
];
|
|
1972
|
+
const result = buildEntityInfosFromScenarios(scenarios, {}, entitiesWithMetadata);
|
|
1973
|
+
expect(result).toHaveLength(1);
|
|
1974
|
+
expect(result[0].name).toBe('Library');
|
|
1975
|
+
expect(result[0].importedBy).toEqual({
|
|
1976
|
+
'app/layout.tsx': {
|
|
1977
|
+
RootLayout: { shas: ['sha1'] },
|
|
1978
|
+
},
|
|
1979
|
+
});
|
|
1980
|
+
});
|
|
1981
|
+
it('should prefer name match over filePath match in multi-entity files', () => {
|
|
1982
|
+
// Bug: app/components/ArticleTable.tsx exports both Props (rowid 33) and
|
|
1983
|
+
// ArticleTable (rowid 34). find() with OR condition matches Props first
|
|
1984
|
+
// via filePath, so ArticleTable gets Props' importedBy (missing the
|
|
1985
|
+
// critical ArticleTable → LibraryPage edge). Fix: try name match first.
|
|
1986
|
+
const scenarios = [
|
|
1987
|
+
{
|
|
1988
|
+
componentName: 'ArticleTable',
|
|
1989
|
+
componentPath: 'app/components/ArticleTable.tsx',
|
|
1990
|
+
},
|
|
1991
|
+
];
|
|
1992
|
+
const entitiesWithMetadata = [
|
|
1993
|
+
{
|
|
1994
|
+
name: 'Props',
|
|
1995
|
+
filePath: 'app/components/ArticleTable.tsx',
|
|
1996
|
+
metadata: {
|
|
1997
|
+
importedBy: {
|
|
1998
|
+
'app/components/ArticleTable.tsx': {
|
|
1999
|
+
ArticleTable: { shas: ['sha-internal'] },
|
|
2000
|
+
},
|
|
2001
|
+
},
|
|
2002
|
+
},
|
|
2003
|
+
},
|
|
2004
|
+
{
|
|
2005
|
+
name: 'ArticleTable',
|
|
2006
|
+
filePath: 'app/components/ArticleTable.tsx',
|
|
2007
|
+
metadata: {
|
|
2008
|
+
importedBy: {
|
|
2009
|
+
'app/library/page.tsx': {
|
|
2010
|
+
LibraryPage: { shas: ['sha1'] },
|
|
2011
|
+
},
|
|
2012
|
+
},
|
|
2013
|
+
},
|
|
2014
|
+
},
|
|
2015
|
+
];
|
|
2016
|
+
const result = buildEntityInfosFromScenarios(scenarios, {}, entitiesWithMetadata);
|
|
2017
|
+
expect(result).toHaveLength(1);
|
|
2018
|
+
expect(result[0].name).toBe('ArticleTable');
|
|
2019
|
+
// Must get ArticleTable's importedBy (LibraryPage), NOT Props' importedBy
|
|
2020
|
+
expect(result[0].importedBy).toEqual({
|
|
2021
|
+
'app/library/page.tsx': {
|
|
2022
|
+
LibraryPage: { shas: ['sha1'] },
|
|
2023
|
+
},
|
|
2024
|
+
});
|
|
2025
|
+
});
|
|
2026
|
+
it('should still match by name when names are identical', () => {
|
|
2027
|
+
const scenarios = [
|
|
2028
|
+
{
|
|
2029
|
+
componentName: 'ArticleTable',
|
|
2030
|
+
componentPath: 'app/components/ArticleTable.tsx',
|
|
2031
|
+
},
|
|
2032
|
+
];
|
|
2033
|
+
const entitiesWithMetadata = [
|
|
2034
|
+
{
|
|
2035
|
+
name: 'ArticleTable',
|
|
2036
|
+
filePath: 'app/components/ArticleTable.tsx',
|
|
2037
|
+
metadata: {
|
|
2038
|
+
importedBy: {
|
|
2039
|
+
'app/library/page.tsx': {
|
|
2040
|
+
LibraryPage: { shas: ['sha1'] },
|
|
2041
|
+
},
|
|
2042
|
+
},
|
|
2043
|
+
},
|
|
2044
|
+
},
|
|
2045
|
+
];
|
|
2046
|
+
const result = buildEntityInfosFromScenarios(scenarios, {}, entitiesWithMetadata);
|
|
2047
|
+
expect(result[0].importedBy).toBeDefined();
|
|
2048
|
+
});
|
|
2049
|
+
});
|
|
2050
|
+
// ── End-to-end: filePath resolution enables impact propagation ────
|
|
2051
|
+
describe('end-to-end: filePath resolution enables page impact detection', () => {
|
|
2052
|
+
it('should detect Library page as impacted when ArticleTable is edited', () => {
|
|
2053
|
+
// This is the Margo Reader bug: ArticleTable is edited, Library page
|
|
2054
|
+
// should be detected as impacted because ArticleTable's importedBy
|
|
2055
|
+
// metadata shows it's imported by "app/library/page.tsx" → "Library".
|
|
2056
|
+
const changedFiles = new Map([
|
|
2057
|
+
['app/components/ArticleTable.tsx', 'edited'],
|
|
2058
|
+
]);
|
|
2059
|
+
const entities = [
|
|
2060
|
+
{
|
|
2061
|
+
name: 'ArticleTable',
|
|
2062
|
+
filePath: 'app/components/ArticleTable.tsx',
|
|
2063
|
+
importedBy: {
|
|
2064
|
+
'app/library/page.tsx': {
|
|
2065
|
+
LibraryPage: { shas: ['sha1'] },
|
|
2066
|
+
},
|
|
2067
|
+
},
|
|
2068
|
+
},
|
|
2069
|
+
{
|
|
2070
|
+
name: 'Library',
|
|
2071
|
+
filePath: 'app/library/page.tsx',
|
|
2072
|
+
},
|
|
2073
|
+
];
|
|
2074
|
+
const result = computeEntityChangeStatus(changedFiles, entities);
|
|
2075
|
+
expect(result['ArticleTable']).toEqual({ status: 'edited' });
|
|
2076
|
+
expect(result['Library']).toEqual({
|
|
2077
|
+
status: 'impacted',
|
|
2078
|
+
impactedBy: [
|
|
2079
|
+
{
|
|
2080
|
+
name: 'ArticleTable',
|
|
2081
|
+
filePath: 'app/components/ArticleTable.tsx',
|
|
2082
|
+
changeType: 'edited',
|
|
2083
|
+
},
|
|
2084
|
+
],
|
|
2085
|
+
});
|
|
2086
|
+
});
|
|
2087
|
+
});
|
|
1492
2088
|
});
|
|
1493
2089
|
//# sourceMappingURL=entityChangeStatus.test.js.map
|