@codeyam/codeyam-cli 0.1.0-staging.8b51541 → 0.1.0-staging.8c89b23

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.
Files changed (452) hide show
  1. package/analyzer-template/.build-info.json +8 -8
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +6 -6
  4. package/analyzer-template/packages/ai/package.json +1 -1
  5. package/analyzer-template/packages/ai/src/lib/astScopes/methodSemantics.ts +135 -0
  6. package/analyzer-template/packages/ai/src/lib/astScopes/nodeToSource.ts +19 -0
  7. package/analyzer-template/packages/ai/src/lib/astScopes/paths.ts +11 -4
  8. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +36 -9
  9. package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.ts +10 -3
  10. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +16 -6
  11. package/analyzer-template/packages/analyze/index.ts +4 -1
  12. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +28 -2
  13. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +5 -36
  14. package/analyzer-template/packages/analyze/src/lib/files/analyze/findOrCreateEntity.ts +10 -6
  15. package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +9 -12
  16. package/analyzer-template/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.ts +21 -0
  17. package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +82 -10
  18. package/analyzer-template/packages/analyze/src/lib/files/analyzeChange.ts +4 -0
  19. package/analyzer-template/packages/analyze/src/lib/files/analyzeInitial.ts +4 -0
  20. package/analyzer-template/packages/analyze/src/lib/files/analyzeNextRoute.ts +8 -3
  21. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +239 -58
  22. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +1684 -1462
  23. package/analyzer-template/packages/aws/package.json +7 -7
  24. package/analyzer-template/packages/database/package.json +3 -3
  25. package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +42 -16
  26. package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +25 -15
  27. package/analyzer-template/packages/database/src/lib/loadEntity.ts +19 -8
  28. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +3 -1
  29. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
  30. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +44 -16
  31. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  32. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
  33. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +7 -1
  34. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
  35. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.d.ts +4 -1
  36. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.d.ts.map +1 -1
  37. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js +5 -5
  38. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js.map +1 -1
  39. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.d.ts +3 -1
  40. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.d.ts.map +1 -1
  41. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js +22 -1
  42. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js.map +1 -1
  43. package/analyzer-template/packages/utils/src/lib/fs/rsyncCopy.ts +27 -0
  44. package/analyzer-template/project/analyzeFileEntities.ts +26 -0
  45. package/analyzer-template/project/runMultiScenarioServer.ts +26 -3
  46. package/background/src/lib/virtualized/project/analyzeFileEntities.js +22 -0
  47. package/background/src/lib/virtualized/project/analyzeFileEntities.js.map +1 -1
  48. package/background/src/lib/virtualized/project/runMultiScenarioServer.js +23 -3
  49. package/background/src/lib/virtualized/project/runMultiScenarioServer.js.map +1 -1
  50. package/codeyam-cli/src/cli.js +24 -0
  51. package/codeyam-cli/src/cli.js.map +1 -1
  52. package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js +47 -0
  53. package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js.map +1 -0
  54. package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js +71 -0
  55. package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js.map +1 -0
  56. package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js +30 -0
  57. package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js.map +1 -0
  58. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js +51 -0
  59. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js.map +1 -0
  60. package/codeyam-cli/src/commands/__tests__/editor.statePersistence.test.js +55 -0
  61. package/codeyam-cli/src/commands/__tests__/editor.statePersistence.test.js.map +1 -0
  62. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +20 -9
  63. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -1
  64. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +39 -3
  65. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
  66. package/codeyam-cli/src/commands/editor.js +3489 -553
  67. package/codeyam-cli/src/commands/editor.js.map +1 -1
  68. package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js +23 -0
  69. package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js.map +1 -0
  70. package/codeyam-cli/src/commands/editorIsolateArgs.js +25 -0
  71. package/codeyam-cli/src/commands/editorIsolateArgs.js.map +1 -0
  72. package/codeyam-cli/src/commands/init.js +21 -0
  73. package/codeyam-cli/src/commands/init.js.map +1 -1
  74. package/codeyam-cli/src/commands/telemetry.js +37 -0
  75. package/codeyam-cli/src/commands/telemetry.js.map +1 -0
  76. package/codeyam-cli/src/data/designSystems.js +27 -0
  77. package/codeyam-cli/src/data/designSystems.js.map +1 -0
  78. package/codeyam-cli/src/data/techStacks.js +1 -1
  79. package/codeyam-cli/src/utils/__tests__/editorApi.test.js +44 -0
  80. package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
  81. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +3174 -1
  82. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  83. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js +76 -0
  84. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js.map +1 -0
  85. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js +137 -0
  86. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js.map +1 -0
  87. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js +100 -0
  88. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js.map +1 -0
  89. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +76 -3
  90. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
  91. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +381 -0
  92. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -0
  93. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js +67 -0
  94. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js.map +1 -0
  95. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +75 -1
  96. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
  97. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js +435 -0
  98. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js.map +1 -0
  99. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +11 -3
  100. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
  101. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +98 -1
  102. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
  103. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js +941 -0
  104. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js.map +1 -0
  105. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +190 -0
  106. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -1
  107. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +726 -17
  108. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  109. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +201 -1
  110. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
  111. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js +143 -0
  112. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js.map +1 -0
  113. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js +66 -0
  114. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js.map +1 -0
  115. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js +53 -0
  116. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js.map +1 -0
  117. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +333 -11
  118. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  119. package/codeyam-cli/src/utils/__tests__/envFile.test.js +125 -0
  120. package/codeyam-cli/src/utils/__tests__/envFile.test.js.map +1 -0
  121. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js +177 -0
  122. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js.map +1 -0
  123. package/codeyam-cli/src/utils/__tests__/handoffContext.test.js +500 -0
  124. package/codeyam-cli/src/utils/__tests__/handoffContext.test.js.map +1 -0
  125. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js +16 -1
  126. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js.map +1 -1
  127. package/codeyam-cli/src/utils/__tests__/manualEntityAnalysis.test.js +302 -0
  128. package/codeyam-cli/src/utils/__tests__/manualEntityAnalysis.test.js.map +1 -0
  129. package/codeyam-cli/src/utils/__tests__/registerScenarioResult.test.js +127 -0
  130. package/codeyam-cli/src/utils/__tests__/registerScenarioResult.test.js.map +1 -0
  131. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js +118 -0
  132. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js.map +1 -0
  133. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +57 -0
  134. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -1
  135. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +220 -2
  136. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
  137. package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js +84 -0
  138. package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js.map +1 -0
  139. package/codeyam-cli/src/utils/__tests__/telemetry.test.js +159 -0
  140. package/codeyam-cli/src/utils/__tests__/telemetry.test.js.map +1 -0
  141. package/codeyam-cli/src/utils/__tests__/testRunner.test.js +216 -0
  142. package/codeyam-cli/src/utils/__tests__/testRunner.test.js.map +1 -0
  143. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js +6 -0
  144. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js.map +1 -1
  145. package/codeyam-cli/src/utils/analysisRunner.js +39 -8
  146. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  147. package/codeyam-cli/src/utils/analyzer.js +11 -1
  148. package/codeyam-cli/src/utils/analyzer.js.map +1 -1
  149. package/codeyam-cli/src/utils/backgroundServer.js +1 -1
  150. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  151. package/codeyam-cli/src/utils/designSystemShowcase.js +810 -0
  152. package/codeyam-cli/src/utils/designSystemShowcase.js.map +1 -0
  153. package/codeyam-cli/src/utils/editorApi.js +16 -0
  154. package/codeyam-cli/src/utils/editorApi.js.map +1 -1
  155. package/codeyam-cli/src/utils/editorAudit.js +649 -10
  156. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  157. package/codeyam-cli/src/utils/editorBroadcastViewport.js +26 -0
  158. package/codeyam-cli/src/utils/editorBroadcastViewport.js.map +1 -0
  159. package/codeyam-cli/src/utils/editorDeleteScenario.js +67 -0
  160. package/codeyam-cli/src/utils/editorDeleteScenario.js.map +1 -0
  161. package/codeyam-cli/src/utils/editorEntityChangeStatus.js +13 -7
  162. package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
  163. package/codeyam-cli/src/utils/editorEntityHelpers.js +144 -0
  164. package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -0
  165. package/codeyam-cli/src/utils/editorGuard.js +36 -0
  166. package/codeyam-cli/src/utils/editorGuard.js.map +1 -0
  167. package/codeyam-cli/src/utils/editorLoaderHelpers.js +40 -1
  168. package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
  169. package/codeyam-cli/src/utils/editorMigration.js +224 -0
  170. package/codeyam-cli/src/utils/editorMigration.js.map +1 -0
  171. package/codeyam-cli/src/utils/editorPreview.js +5 -3
  172. package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
  173. package/codeyam-cli/src/utils/editorRecapture.js +109 -0
  174. package/codeyam-cli/src/utils/editorRecapture.js.map +1 -0
  175. package/codeyam-cli/src/utils/editorRoadmap.js +530 -0
  176. package/codeyam-cli/src/utils/editorRoadmap.js.map +1 -0
  177. package/codeyam-cli/src/utils/editorScenarioSwitch.js +39 -2
  178. package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -1
  179. package/codeyam-cli/src/utils/editorScenarios.js +307 -7
  180. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  181. package/codeyam-cli/src/utils/editorSeedAdapter.js +308 -6
  182. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
  183. package/codeyam-cli/src/utils/editorShouldRevalidate.js +21 -0
  184. package/codeyam-cli/src/utils/editorShouldRevalidate.js.map +1 -0
  185. package/codeyam-cli/src/utils/entityChangeStatus.js +50 -5
  186. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  187. package/codeyam-cli/src/utils/entityChangeStatus.server.js +38 -3
  188. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  189. package/codeyam-cli/src/utils/envFile.js +90 -0
  190. package/codeyam-cli/src/utils/envFile.js.map +1 -0
  191. package/codeyam-cli/src/utils/fileWatcher.js +38 -0
  192. package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
  193. package/codeyam-cli/src/utils/glossaryAdd.js +74 -0
  194. package/codeyam-cli/src/utils/glossaryAdd.js.map +1 -0
  195. package/codeyam-cli/src/utils/handoffContext.js +257 -0
  196. package/codeyam-cli/src/utils/handoffContext.js.map +1 -0
  197. package/codeyam-cli/src/utils/install-skills.js +50 -6
  198. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  199. package/codeyam-cli/src/utils/manualEntityAnalysis.js +196 -0
  200. package/codeyam-cli/src/utils/manualEntityAnalysis.js.map +1 -0
  201. package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js +159 -0
  202. package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js.map +1 -0
  203. package/codeyam-cli/src/utils/queue/job.js +35 -6
  204. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  205. package/codeyam-cli/src/utils/registerScenarioResult.js +52 -0
  206. package/codeyam-cli/src/utils/registerScenarioResult.js.map +1 -0
  207. package/codeyam-cli/src/utils/routePatternMatching.js +129 -0
  208. package/codeyam-cli/src/utils/routePatternMatching.js.map +1 -0
  209. package/codeyam-cli/src/utils/scenarioCoverage.js +12 -10
  210. package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -1
  211. package/codeyam-cli/src/utils/scenariosManifest.js +84 -12
  212. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  213. package/codeyam-cli/src/utils/screenshotHash.js +26 -0
  214. package/codeyam-cli/src/utils/screenshotHash.js.map +1 -0
  215. package/codeyam-cli/src/utils/simulationGateMiddleware.js +9 -0
  216. package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
  217. package/codeyam-cli/src/utils/telemetry.js +106 -0
  218. package/codeyam-cli/src/utils/telemetry.js.map +1 -0
  219. package/codeyam-cli/src/utils/telemetryMiddleware.js +22 -0
  220. package/codeyam-cli/src/utils/telemetryMiddleware.js.map +1 -0
  221. package/codeyam-cli/src/utils/testResultCache.js +53 -0
  222. package/codeyam-cli/src/utils/testResultCache.js.map +1 -0
  223. package/codeyam-cli/src/utils/testResultCache.server.js +81 -0
  224. package/codeyam-cli/src/utils/testResultCache.server.js.map +1 -0
  225. package/codeyam-cli/src/utils/testResultCache.server.test.js +187 -0
  226. package/codeyam-cli/src/utils/testResultCache.server.test.js.map +1 -0
  227. package/codeyam-cli/src/utils/testResultCache.test.js +230 -0
  228. package/codeyam-cli/src/utils/testResultCache.test.js.map +1 -0
  229. package/codeyam-cli/src/utils/testRunner.js +193 -1
  230. package/codeyam-cli/src/utils/testRunner.js.map +1 -1
  231. package/codeyam-cli/src/utils/webappDetection.js +4 -2
  232. package/codeyam-cli/src/utils/webappDetection.js.map +1 -1
  233. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js +99 -0
  234. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js.map +1 -0
  235. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +35 -0
  236. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -0
  237. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +68 -1
  238. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -1
  239. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +203 -8
  240. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  241. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +190 -21
  242. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
  243. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js +135 -0
  244. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js.map +1 -0
  245. package/codeyam-cli/src/webserver/app/lib/clientErrors.js +22 -1
  246. package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -1
  247. package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
  248. package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js +34 -0
  249. package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js.map +1 -0
  250. package/codeyam-cli/src/webserver/app/types/editor.js +8 -0
  251. package/codeyam-cli/src/webserver/app/types/editor.js.map +1 -0
  252. package/codeyam-cli/src/webserver/backgroundServer.js +60 -61
  253. package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
  254. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-DTBZZfSk.js +1 -0
  255. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BcgbViKV.js → EntityItem-BxclONWq.js} +3 -3
  256. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-CQgyEGV-.js +1 -0
  257. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CQIG2qda.js → EntityTypeIcon-BsnEOJZ_.js} +1 -1
  258. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-ByaELMbv.js +1 -0
  259. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-6WjVfhxX.js +25 -0
  260. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-ChX-Hp7W.js +3 -0
  261. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-BU_OAEMP.js → LoadingDots-By5zI316.js} +1 -1
  262. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-ceAyBX-H.js → LogViewer-C-9zQdXg.js} +3 -3
  263. package/codeyam-cli/src/webserver/build/client/assets/MiniClaudeChat-Bs2_Oua4.js +36 -0
  264. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-BzHcG7SE.js → ReportIssueModal-DQsceHVv.js} +2 -2
  265. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-DThcm_9M.js +1 -0
  266. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-TSD3C211.js → ScenarioViewer-Cl4oOA3A.js} +3 -3
  267. package/codeyam-cli/src/webserver/build/client/assets/Spinner-CIil5-gb.js +34 -0
  268. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-CK7-NaPZ.js +1 -0
  269. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-BqkA9zyZ.js +1 -0
  270. package/codeyam-cli/src/webserver/build/client/assets/{_index-DLxKhri3.js → _index-DnOgyseQ.js} +2 -2
  271. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BcY3q6nt.js → activity.(_tab)-DqM9hbNE.js} +3 -3
  272. package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-Duc5hnl7.js → addon-web-links-C58dYPwR.js} +1 -1
  273. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bni3iiUj.js → agent-transcripts-B8NCeOrm.js} +3 -3
  274. package/codeyam-cli/src/webserver/build/client/assets/api.editor-github-verify-l0sNRNKZ.js +1 -0
  275. package/codeyam-cli/src/webserver/build/client/assets/api.editor-handoff-l0sNRNKZ.js +1 -0
  276. package/codeyam-cli/src/webserver/build/client/assets/api.editor-hosting-verify-l0sNRNKZ.js +1 -0
  277. package/codeyam-cli/src/webserver/build/client/assets/api.editor-recapture-stale-l0sNRNKZ.js +1 -0
  278. package/codeyam-cli/src/webserver/build/client/assets/api.editor-rename-scenario-l0sNRNKZ.js +1 -0
  279. package/codeyam-cli/src/webserver/build/client/assets/api.editor-roadmap-l0sNRNKZ.js +1 -0
  280. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-scenario-data-l0sNRNKZ.js +1 -0
  281. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-seed-state-l0sNRNKZ.js +1 -0
  282. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-prompt-l0sNRNKZ.js +1 -0
  283. package/codeyam-cli/src/webserver/build/client/assets/api.editor-schema-l0sNRNKZ.js +1 -0
  284. package/codeyam-cli/src/webserver/build/client/assets/api.editor-verify-routes-l0sNRNKZ.js +1 -0
  285. package/codeyam-cli/src/webserver/build/client/assets/api.interactive-switch-scenario-l0sNRNKZ.js +1 -0
  286. package/codeyam-cli/src/webserver/build/client/assets/{book-open-BYOypzCa.js → book-open-BFSIqZgO.js} +1 -1
  287. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-C_Pmso5S.js → chevron-down-B9fDzFVh.js} +1 -1
  288. package/codeyam-cli/src/webserver/build/client/assets/chunk-UVKPFVEO-Bmq2apuh.js +43 -0
  289. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-BVMi9VA5.js → circle-check-DLPObLUx.js} +1 -1
  290. package/codeyam-cli/src/webserver/build/client/assets/{copy-n2FB0_Sw.js → copy-DXEmO0TD.js} +1 -1
  291. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-CC6AbExI.js → createLucideIcon-BwyFiRot.js} +1 -1
  292. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-Coe5NhbS.js +1 -0
  293. package/codeyam-cli/src/webserver/build/client/assets/{cy-logo-cli-CCKUIm0S.svg → cy-logo-cli-DoA97ML3.svg} +2 -2
  294. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-iRhRIFlp.js +1 -0
  295. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-BZPBzV73.js +1 -0
  296. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-D4cIaQC3.js +161 -0
  297. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-C6fEYHrh.js +41 -0
  298. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-DwCV5__E.js → entity._sha._-pc-vc6wO.js} +13 -12
  299. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-C8AyYgYT.js +6 -0
  300. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-DziaVQX1.js +6 -0
  301. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-BTcpgIpC.js +6 -0
  302. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMvVHNXU.js → entity._sha_.edit._scenarioId-D_O_ajfZ.js} +2 -2
  303. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-DTvKq3TY.js → entry.client-j1Vi0bco.js} +6 -6
  304. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-Daa96Fr1.js +1 -0
  305. package/codeyam-cli/src/webserver/build/client/assets/files-kuny2Q_s.js +1 -0
  306. package/codeyam-cli/src/webserver/build/client/assets/git-DgCZPMie.js +1 -0
  307. package/codeyam-cli/src/webserver/build/client/assets/globals-D4iGjX2m.css +1 -0
  308. package/codeyam-cli/src/webserver/build/client/assets/{index-BcvgDzbZ.js → index-BliGSSpl.js} +1 -1
  309. package/codeyam-cli/src/webserver/build/client/assets/{index-yHOVb4rc.js → index-SqjQKTdH.js} +1 -1
  310. package/codeyam-cli/src/webserver/build/client/assets/{index-10oVnAAH.js → index-vyrZD2g4.js} +1 -1
  311. package/codeyam-cli/src/webserver/build/client/assets/jsx-runtime-D_zvdyIk.js +9 -0
  312. package/codeyam-cli/src/webserver/build/client/assets/labs-c3yLxSEp.js +1 -0
  313. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DaAZ_H2w.js → loader-circle-D-q28GLF.js} +1 -1
  314. package/codeyam-cli/src/webserver/build/client/assets/manifest-985e63d6.js +1 -0
  315. package/codeyam-cli/src/webserver/build/client/assets/{memory-9gnxSZlb.js → memory-CEWIUC4t.js} +2 -2
  316. package/codeyam-cli/src/webserver/build/client/assets/{pause-f5-1lKBt.js → pause-BP6fitdh.js} +1 -1
  317. package/codeyam-cli/src/webserver/build/client/assets/root-BRyJWJBA.js +80 -0
  318. package/codeyam-cli/src/webserver/build/client/assets/{search-Di64LWVb.js → search-BooqacKS.js} +1 -1
  319. package/codeyam-cli/src/webserver/build/client/assets/settings-BM0nbryO.js +1 -0
  320. package/codeyam-cli/src/webserver/build/client/assets/simulations-ovy6FjRY.js +1 -0
  321. package/codeyam-cli/src/webserver/build/client/assets/{terminal-Br7MOqts.js → terminal-DHemCJIs.js} +1 -1
  322. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BLdiCuG-.js → triangle-alert-D87ekDl8.js} +1 -1
  323. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-Dk0Tciqg.js +1 -0
  324. package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-C8QvIe05.js +2 -0
  325. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-jkCytuYz.js +1 -0
  326. package/codeyam-cli/src/webserver/build/client/assets/useToast-BgqkixU9.js +1 -0
  327. package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-RX_oAulJ.js +16 -0
  328. package/codeyam-cli/src/webserver/build/server/assets/{index-DsZjKspK.js → index-DRb-tJ77.js} +1 -1
  329. package/codeyam-cli/src/webserver/build/server/assets/init-CzFOE1qs.js +14 -0
  330. package/codeyam-cli/src/webserver/build/server/assets/progress-CHTtrxFG.js +1 -0
  331. package/codeyam-cli/src/webserver/build/server/assets/server-build-CPKq4t0v.js +765 -0
  332. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  333. package/codeyam-cli/src/webserver/build-info.json +5 -5
  334. package/codeyam-cli/src/webserver/editorProxy.js +244 -20
  335. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  336. package/codeyam-cli/src/webserver/idleDetector.js +65 -8
  337. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -1
  338. package/codeyam-cli/src/webserver/mockStateEvents.js +28 -0
  339. package/codeyam-cli/src/webserver/mockStateEvents.js.map +1 -0
  340. package/codeyam-cli/src/webserver/scripts/journalCapture.ts +53 -0
  341. package/codeyam-cli/src/webserver/server.js +160 -14
  342. package/codeyam-cli/src/webserver/server.js.map +1 -1
  343. package/codeyam-cli/src/webserver/terminalServer.js +248 -28
  344. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  345. package/codeyam-cli/templates/__tests__/editor-step-hook.prompt-capture.test.ts +118 -0
  346. package/codeyam-cli/templates/codeyam-editor-claude.md +2 -0
  347. package/codeyam-cli/templates/codeyam-editor-codex.md +61 -0
  348. package/codeyam-cli/templates/codeyam-editor-gemini.md +59 -0
  349. package/codeyam-cli/templates/codeyam-editor-reference.md +216 -0
  350. package/codeyam-cli/templates/design-systems/clean-dashboard-design-system.md +255 -0
  351. package/codeyam-cli/templates/design-systems/editorial-design-system.md +267 -0
  352. package/codeyam-cli/templates/design-systems/mono-brutalist-design-system.md +256 -0
  353. package/codeyam-cli/templates/design-systems/neo-brutalist-design-system.md +294 -0
  354. package/codeyam-cli/templates/editor-step-hook.py +191 -60
  355. package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +204 -5
  356. package/codeyam-cli/templates/expo-react-native/__tests__/.gitkeep +0 -0
  357. package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +6 -3
  358. package/codeyam-cli/templates/expo-react-native/app/index.tsx +36 -0
  359. package/codeyam-cli/templates/expo-react-native/app.json +11 -0
  360. package/codeyam-cli/templates/expo-react-native/babel.config.js +1 -0
  361. package/codeyam-cli/templates/expo-react-native/gitignore +2 -0
  362. package/codeyam-cli/templates/expo-react-native/global.css +7 -0
  363. package/codeyam-cli/templates/expo-react-native/lib/theme.ts +73 -0
  364. package/codeyam-cli/templates/expo-react-native/package.json +32 -16
  365. package/codeyam-cli/templates/expo-react-native/patches/expo-modules-autolinking+3.0.24.patch +29 -0
  366. package/codeyam-cli/templates/isolation-route/expo-router.tsx.template +54 -0
  367. package/codeyam-cli/templates/nextjs-prisma-sqlite/gitignore +1 -0
  368. package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +1 -1
  369. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +88 -40
  370. package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +1 -1
  371. package/codeyam-cli/templates/seed-adapters/supabase.ts +363 -0
  372. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +96 -1
  373. package/package.json +2 -1
  374. package/packages/ai/src/lib/astScopes/methodSemantics.js +99 -0
  375. package/packages/ai/src/lib/astScopes/methodSemantics.js.map +1 -1
  376. package/packages/ai/src/lib/astScopes/nodeToSource.js +16 -0
  377. package/packages/ai/src/lib/astScopes/nodeToSource.js.map +1 -1
  378. package/packages/ai/src/lib/astScopes/paths.js +12 -3
  379. package/packages/ai/src/lib/astScopes/paths.js.map +1 -1
  380. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +27 -10
  381. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  382. package/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.js +9 -2
  383. package/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.js.map +1 -1
  384. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +14 -4
  385. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  386. package/packages/analyze/index.js +1 -1
  387. package/packages/analyze/index.js.map +1 -1
  388. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +16 -2
  389. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  390. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +6 -26
  391. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  392. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js +3 -2
  393. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js.map +1 -1
  394. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +9 -7
  395. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
  396. package/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.js +14 -0
  397. package/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.js.map +1 -1
  398. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +44 -11
  399. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  400. package/packages/analyze/src/lib/files/analyzeChange.js +1 -0
  401. package/packages/analyze/src/lib/files/analyzeChange.js.map +1 -1
  402. package/packages/analyze/src/lib/files/analyzeInitial.js +1 -0
  403. package/packages/analyze/src/lib/files/analyzeInitial.js.map +1 -1
  404. package/packages/analyze/src/lib/files/analyzeNextRoute.js +5 -1
  405. package/packages/analyze/src/lib/files/analyzeNextRoute.js.map +1 -1
  406. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +120 -28
  407. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  408. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +1368 -1193
  409. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  410. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +44 -16
  411. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  412. package/packages/database/src/lib/loadAnalysis.js +7 -1
  413. package/packages/database/src/lib/loadAnalysis.js.map +1 -1
  414. package/packages/database/src/lib/loadEntity.js +5 -5
  415. package/packages/database/src/lib/loadEntity.js.map +1 -1
  416. package/packages/utils/src/lib/fs/rsyncCopy.js +22 -1
  417. package/packages/utils/src/lib/fs/rsyncCopy.js.map +1 -1
  418. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-BPXZwM4t.js +0 -1
  419. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-g3saevPb.js +0 -1
  420. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-Bu6c6aDe.js +0 -1
  421. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-DYFW3lDD.js +0 -25
  422. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DLeucoVX.js +0 -3
  423. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-BED4B6sP.js +0 -1
  424. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bb5uFQ5V.js +0 -34
  425. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-C8OKAR5x.js +0 -1
  426. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +0 -1
  427. package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-C4pqxYJB.js +0 -51
  428. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-DcX-ZS3p.js +0 -1
  429. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Ii3inc0_.js +0 -1
  430. package/codeyam-cli/src/webserver/build/client/assets/editor-16o0AIFV.js +0 -15
  431. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-7Uga8I59.js +0 -41
  432. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-BwKcai0j.js +0 -6
  433. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CHMiAog3.js +0 -6
  434. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-p9hhkjJM.js +0 -6
  435. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-cPo8LiG3.js +0 -1
  436. package/codeyam-cli/src/webserver/build/client/assets/files-BZrlFE1F.js +0 -1
  437. package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +0 -1
  438. package/codeyam-cli/src/webserver/build/client/assets/globals-CQPR0pFR.css +0 -1
  439. package/codeyam-cli/src/webserver/build/client/assets/labs-Zk7ryIM1.js +0 -1
  440. package/codeyam-cli/src/webserver/build/client/assets/manifest-76e7b62c.js +0 -1
  441. package/codeyam-cli/src/webserver/build/client/assets/root-DBjt6o04.js +0 -67
  442. package/codeyam-cli/src/webserver/build/client/assets/settings-0OrEMU6J.js +0 -1
  443. package/codeyam-cli/src/webserver/build/client/assets/simulations-DWT-CvLy.js +0 -1
  444. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-C-_hOl_g.js +0 -1
  445. package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-C14nCb1q.js +0 -2
  446. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-O-jkvSPx.js +0 -1
  447. package/codeyam-cli/src/webserver/build/client/assets/useToast-9FIWuYfK.js +0 -1
  448. package/codeyam-cli/src/webserver/build/server/assets/init-DdqKD2p4.js +0 -10
  449. package/codeyam-cli/src/webserver/build/server/assets/server-build-CKKeWtVK.js +0 -444
  450. package/codeyam-cli/templates/expo-react-native/app/(tabs)/_layout.tsx +0 -33
  451. package/codeyam-cli/templates/expo-react-native/app/(tabs)/index.tsx +0 -12
  452. package/codeyam-cli/templates/expo-react-native/app/(tabs)/settings.tsx +0 -12
@@ -3,7 +3,7 @@ import os from 'os';
3
3
  import path from 'path';
4
4
  import Database from 'better-sqlite3';
5
5
  import { Kysely, SqliteDialect } from 'kysely';
6
- import { deduplicateByName, generateScenarioSlug, convertIsoToSqliteTimestamp, determineCaptureUrl, resolvePreviewNavPath, clearEditorState, clearEditorUserPrompt, readDefaultScreenSize, readScreenSizes, resolveScenarioViewport, resolveViewportWithProjectDefault, upsertEditorScenario, cleanupScenarioFiles, readPreservedConfigProperties, validateStepTransition, slugifyDimension, isRowInFeatureSession, } from "../editorScenarios.js";
6
+ import { deduplicateByName, generateScenarioSlug, convertIsoToSqliteTimestamp, determineCaptureUrl, resolvePreviewNavPath, clearEditorState, clearEditorUserPrompt, readDefaultScreenSize, readScreenSizes, resolveScenarioViewport, resolveViewportWithProjectDefault, upsertEditorScenario, cleanupScenarioFiles, readPreservedConfigProperties, validateStepTransition, slugifyDimension, isRowInFeatureSession, backfillEntityShaOnScenarios, countScenariosNeedingEntityBackfill, validateEntityLinkageForAppScenario, validateScenarioCategorization, resolveSeedFrom, } from "../editorScenarios.js";
7
7
  describe('editorScenarios', () => {
8
8
  describe('deduplicateByName', () => {
9
9
  it('should keep only the last item for each key', () => {
@@ -497,7 +497,6 @@ describe('editorScenarios', () => {
497
497
  type: null,
498
498
  viewportWidth: 1280,
499
499
  viewportHeight: 720,
500
- dimension: null,
501
500
  });
502
501
  expect(result.isNew).toBe(true);
503
502
  expect(result.cleanedUpIds).toEqual([]);
@@ -522,7 +521,6 @@ describe('editorScenarios', () => {
522
521
  type: null,
523
522
  viewportWidth: 1280,
524
523
  viewportHeight: 720,
525
- dimension: null,
526
524
  });
527
525
  // Second registration with same name
528
526
  const second = await upsertEditorScenario(db, {
@@ -535,7 +533,6 @@ describe('editorScenarios', () => {
535
533
  type: null,
536
534
  viewportWidth: 800,
537
535
  viewportHeight: 600,
538
- dimension: null,
539
536
  });
540
537
  // Should reuse the same ID
541
538
  expect(second.scenarioId).toBe(first.scenarioId);
@@ -576,7 +573,6 @@ describe('editorScenarios', () => {
576
573
  type: null,
577
574
  viewportWidth: 1280,
578
575
  viewportHeight: 720,
579
- dimension: null,
580
576
  });
581
577
  expect(result.isNew).toBe(false);
582
578
  // Should return the IDs of cleaned-up duplicates
@@ -600,7 +596,6 @@ describe('editorScenarios', () => {
600
596
  type: null,
601
597
  viewportWidth: 1280,
602
598
  viewportHeight: 720,
603
- dimension: null,
604
599
  });
605
600
  await upsertEditorScenario(db, {
606
601
  projectId,
@@ -612,7 +607,6 @@ describe('editorScenarios', () => {
612
607
  type: null,
613
608
  viewportWidth: 1280,
614
609
  viewportHeight: 720,
615
- dimension: null,
616
610
  });
617
611
  const rows = await db
618
612
  .selectFrom('editor_scenarios')
@@ -631,7 +625,6 @@ describe('editorScenarios', () => {
631
625
  type: 'application',
632
626
  viewportWidth: 1280,
633
627
  viewportHeight: 720,
634
- dimension: null,
635
628
  });
636
629
  const result2 = await upsertEditorScenario(db, {
637
630
  projectId: 'project-b',
@@ -643,7 +636,6 @@ describe('editorScenarios', () => {
643
636
  type: 'application',
644
637
  viewportWidth: 1280,
645
638
  viewportHeight: 720,
646
- dimension: null,
647
639
  });
648
640
  expect(result1.scenarioId).not.toBe(result2.scenarioId);
649
641
  expect(result1.isNew).toBe(true);
@@ -848,7 +840,7 @@ describe('editorScenarios', () => {
848
840
  afterEach(async () => {
849
841
  await db.destroy();
850
842
  });
851
- it('should store dimensions as JSON array and set dimension to first element', async () => {
843
+ it('should store dimensions as JSON array', async () => {
852
844
  const result = await upsertEditorScenario(db, {
853
845
  projectId,
854
846
  name: 'Home Page',
@@ -859,7 +851,6 @@ describe('editorScenarios', () => {
859
851
  type: 'application',
860
852
  viewportWidth: 1440,
861
853
  viewportHeight: 900,
862
- dimension: 'Desktop',
863
854
  dimensions: ['Desktop', 'Mobile'],
864
855
  screenshotPaths: null,
865
856
  });
@@ -873,8 +864,6 @@ describe('editorScenarios', () => {
873
864
  'Desktop',
874
865
  'Mobile',
875
866
  ]);
876
- // dimension (singular) should be set to first element for backward compat
877
- expect(row.dimension).toBe('Desktop');
878
867
  });
879
868
  it('should store screenshot_paths as JSON object', async () => {
880
869
  const screenshotPaths = {
@@ -891,7 +880,6 @@ describe('editorScenarios', () => {
891
880
  type: 'application',
892
881
  viewportWidth: 1440,
893
882
  viewportHeight: 900,
894
- dimension: 'Desktop',
895
883
  dimensions: ['Desktop', 'Mobile'],
896
884
  screenshotPaths,
897
885
  });
@@ -913,7 +901,6 @@ describe('editorScenarios', () => {
913
901
  type: 'application',
914
902
  viewportWidth: 1440,
915
903
  viewportHeight: 900,
916
- dimension: 'Desktop',
917
904
  dimensions: ['Desktop'],
918
905
  screenshotPaths: { Desktop: 'screenshots/old--desktop.png' },
919
906
  });
@@ -927,7 +914,6 @@ describe('editorScenarios', () => {
927
914
  type: 'application',
928
915
  viewportWidth: 1440,
929
916
  viewportHeight: 900,
930
- dimension: 'Desktop',
931
917
  dimensions: ['Desktop', 'Mobile'],
932
918
  screenshotPaths: {
933
919
  Desktop: 'screenshots/new--desktop.png',
@@ -960,7 +946,6 @@ describe('editorScenarios', () => {
960
946
  type: null,
961
947
  viewportWidth: 1280,
962
948
  viewportHeight: 720,
963
- dimension: null,
964
949
  });
965
950
  const row = await db
966
951
  .selectFrom('editor_scenarios')
@@ -1024,6 +1009,26 @@ describe('editorScenarios', () => {
1024
1009
  expect(err).not.toBeNull();
1025
1010
  expect(err).toContain('step 14');
1026
1011
  });
1012
+ it('should reject rapid-fire advance when step started < 3s ago', () => {
1013
+ const recentStart = new Date(Date.now() - 1000).toISOString(); // 1s ago
1014
+ const err = validateStepTransition(4, 3, recentStart);
1015
+ expect(err).not.toBeNull();
1016
+ expect(err).toContain('only 1s ago');
1017
+ expect(err).toContain('checklist');
1018
+ });
1019
+ it('should allow advance when step started > 3s ago', () => {
1020
+ const oldStart = new Date(Date.now() - 5000).toISOString(); // 5s ago
1021
+ expect(validateStepTransition(4, 3, oldStart)).toBeNull();
1022
+ });
1023
+ it('should not enforce time guard on step 1 → 2 transition', () => {
1024
+ const recentStart = new Date(Date.now() - 2000).toISOString();
1025
+ // currentStep=1, so guard only applies for currentStep >= 2
1026
+ expect(validateStepTransition(2, 1, recentStart)).toBeNull();
1027
+ });
1028
+ it('should not enforce time guard when re-running the same step', () => {
1029
+ const recentStart = new Date(Date.now() - 2000).toISOString();
1030
+ expect(validateStepTransition(5, 5, recentStart)).toBeNull();
1031
+ });
1027
1032
  });
1028
1033
  describe('isRowInFeatureSession', () => {
1029
1034
  const featureStart = '2026-03-12 14:01:31';
@@ -1055,5 +1060,709 @@ describe('editorScenarios', () => {
1055
1060
  expect(isRowInFeatureSession({ created_at: '2026-03-12 14:30:00', updated_at: null }, featureStart)).toBe(true);
1056
1061
  });
1057
1062
  });
1063
+ describe('upsertEditorScenario with entitySha and displayName', () => {
1064
+ let db;
1065
+ let rawDb;
1066
+ const projectId = 'test-project-id';
1067
+ beforeEach(async () => {
1068
+ rawDb = new Database(':memory:');
1069
+ db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
1070
+ await db.schema
1071
+ .createTable('editor_scenarios')
1072
+ .addColumn('id', 'varchar', (col) => col.primaryKey())
1073
+ .addColumn('project_id', 'varchar', (col) => col.notNull())
1074
+ .addColumn('name', 'varchar', (col) => col.notNull())
1075
+ .addColumn('description', 'text')
1076
+ .addColumn('component_name', 'varchar')
1077
+ .addColumn('component_path', 'varchar')
1078
+ .addColumn('url', 'varchar')
1079
+ .addColumn('type', 'varchar')
1080
+ .addColumn('screenshot_path', 'varchar')
1081
+ .addColumn('viewport_width', 'integer')
1082
+ .addColumn('viewport_height', 'integer')
1083
+ .addColumn('dimension', 'varchar')
1084
+ .addColumn('dimensions', 'text')
1085
+ .addColumn('screenshot_paths', 'text')
1086
+ .addColumn('page_file_path', 'varchar')
1087
+ .addColumn('entity_sha', 'varchar')
1088
+ .addColumn('display_name', 'varchar')
1089
+ .addColumn('created_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1090
+ .addColumn('updated_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1091
+ .execute();
1092
+ });
1093
+ afterEach(async () => {
1094
+ await db.destroy();
1095
+ });
1096
+ it('should store entitySha and displayName on insert', async () => {
1097
+ const result = await upsertEditorScenario(db, {
1098
+ projectId,
1099
+ name: 'Home - Default',
1100
+ description: null,
1101
+ componentName: null,
1102
+ componentPath: null,
1103
+ url: '/',
1104
+ type: 'application',
1105
+ viewportWidth: 400,
1106
+ viewportHeight: 600,
1107
+ entitySha: 'abc123',
1108
+ displayName: 'Home',
1109
+ });
1110
+ expect(result.isNew).toBe(true);
1111
+ const row = rawDb
1112
+ .prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
1113
+ .get(result.scenarioId);
1114
+ expect(row.entity_sha).toBe('abc123');
1115
+ expect(row.display_name).toBe('Home');
1116
+ });
1117
+ it('should update entitySha and displayName on upsert', async () => {
1118
+ // Insert first
1119
+ await upsertEditorScenario(db, {
1120
+ projectId,
1121
+ name: 'Home - Default',
1122
+ description: null,
1123
+ componentName: null,
1124
+ componentPath: null,
1125
+ url: '/',
1126
+ type: 'application',
1127
+ viewportWidth: 400,
1128
+ viewportHeight: 600,
1129
+ entitySha: 'old-sha',
1130
+ displayName: 'OldName',
1131
+ });
1132
+ // Upsert with new values
1133
+ const result = await upsertEditorScenario(db, {
1134
+ projectId,
1135
+ name: 'Home - Default',
1136
+ description: null,
1137
+ componentName: null,
1138
+ componentPath: null,
1139
+ url: '/',
1140
+ type: 'application',
1141
+ viewportWidth: 400,
1142
+ viewportHeight: 600,
1143
+ entitySha: 'new-sha',
1144
+ displayName: 'Home',
1145
+ });
1146
+ expect(result.isNew).toBe(false);
1147
+ const row = rawDb
1148
+ .prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
1149
+ .get(result.scenarioId);
1150
+ expect(row.entity_sha).toBe('new-sha');
1151
+ expect(row.display_name).toBe('Home');
1152
+ });
1153
+ it('should not overwrite entitySha when not provided', async () => {
1154
+ // Insert with SHA
1155
+ const first = await upsertEditorScenario(db, {
1156
+ projectId,
1157
+ name: 'Home - Default',
1158
+ description: null,
1159
+ componentName: null,
1160
+ componentPath: null,
1161
+ url: '/',
1162
+ type: 'application',
1163
+ viewportWidth: 400,
1164
+ viewportHeight: 600,
1165
+ entitySha: 'keep-this',
1166
+ displayName: 'Home',
1167
+ });
1168
+ // Upsert without entitySha/displayName
1169
+ await upsertEditorScenario(db, {
1170
+ projectId,
1171
+ name: 'Home - Default',
1172
+ description: 'updated',
1173
+ componentName: null,
1174
+ componentPath: null,
1175
+ url: '/',
1176
+ type: 'application',
1177
+ viewportWidth: 400,
1178
+ viewportHeight: 600,
1179
+ });
1180
+ const row = rawDb
1181
+ .prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
1182
+ .get(first.scenarioId);
1183
+ expect(row.entity_sha).toBe('keep-this');
1184
+ expect(row.display_name).toBe('Home');
1185
+ });
1186
+ });
1187
+ describe('backfillEntityShaOnScenarios', () => {
1188
+ let db;
1189
+ let rawDb;
1190
+ const projectId = 'test-project-id';
1191
+ beforeEach(async () => {
1192
+ rawDb = new Database(':memory:');
1193
+ db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
1194
+ await db.schema
1195
+ .createTable('editor_scenarios')
1196
+ .addColumn('id', 'varchar', (col) => col.primaryKey())
1197
+ .addColumn('project_id', 'varchar', (col) => col.notNull())
1198
+ .addColumn('name', 'varchar', (col) => col.notNull())
1199
+ .addColumn('description', 'text')
1200
+ .addColumn('component_name', 'varchar')
1201
+ .addColumn('component_path', 'varchar')
1202
+ .addColumn('url', 'varchar')
1203
+ .addColumn('type', 'varchar')
1204
+ .addColumn('screenshot_path', 'varchar')
1205
+ .addColumn('viewport_width', 'integer')
1206
+ .addColumn('viewport_height', 'integer')
1207
+ .addColumn('dimension', 'varchar')
1208
+ .addColumn('dimensions', 'text')
1209
+ .addColumn('screenshot_paths', 'text')
1210
+ .addColumn('page_file_path', 'varchar')
1211
+ .addColumn('entity_sha', 'varchar')
1212
+ .addColumn('display_name', 'varchar')
1213
+ .addColumn('created_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1214
+ .addColumn('updated_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1215
+ .execute();
1216
+ });
1217
+ afterEach(async () => {
1218
+ await db.destroy();
1219
+ });
1220
+ it('should backfill component scenario by component_path match', async () => {
1221
+ // Insert a scenario with null entity_sha but with component_path
1222
+ rawDb
1223
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height)
1224
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1225
+ .run('sc-1', projectId, 'Header - Default', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720);
1226
+ const result = await backfillEntityShaOnScenarios(db, [
1227
+ {
1228
+ sha: 'entity-sha-1',
1229
+ name: 'Header',
1230
+ filePath: 'src/components/Header.tsx',
1231
+ },
1232
+ ]);
1233
+ expect(result.updated).toBe(1);
1234
+ const row = rawDb
1235
+ .prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
1236
+ .get('sc-1');
1237
+ expect(row.entity_sha).toBe('entity-sha-1');
1238
+ expect(row.display_name).toBe('Header');
1239
+ });
1240
+ it('should backfill page scenario by page_file_path match and derive display_name from route', async () => {
1241
+ // Insert a scenario with null entity_sha but with page_file_path
1242
+ rawDb
1243
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, page_file_path, url, type, viewport_width, viewport_height)
1244
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
1245
+ .run('sc-2', projectId, 'Feedback - Default', 'app/feedback/page.tsx', '/feedback', 'application', 1280, 720);
1246
+ const result = await backfillEntityShaOnScenarios(db, [
1247
+ {
1248
+ sha: 'entity-sha-2',
1249
+ name: 'Feedback',
1250
+ filePath: 'app/feedback/page.tsx',
1251
+ },
1252
+ ]);
1253
+ expect(result.updated).toBe(1);
1254
+ const row = rawDb
1255
+ .prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
1256
+ .get('sc-2');
1257
+ expect(row.entity_sha).toBe('entity-sha-2');
1258
+ // display_name derived from page_file_path via routeDisplayName(buildRoutePattern())
1259
+ expect(row.display_name).toBe('Feedback');
1260
+ });
1261
+ it('should update scenarios with stale entity_sha to latest', async () => {
1262
+ // Margo bug: entity code changes create new versions with new SHAs,
1263
+ // but scenarios still point to the old SHA. Since journals preserve
1264
+ // screenshots and everything is in git, we always want scenarios
1265
+ // pointing to the latest entity version.
1266
+ rawDb
1267
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha, display_name)
1268
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1269
+ .run('sc-3', projectId, 'Header - Dark', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720, 'old-sha', 'Header');
1270
+ const result = await backfillEntityShaOnScenarios(db, [
1271
+ {
1272
+ sha: 'latest-sha',
1273
+ name: 'Header',
1274
+ filePath: 'src/components/Header.tsx',
1275
+ },
1276
+ ]);
1277
+ expect(result.updated).toBe(1);
1278
+ const row = rawDb
1279
+ .prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
1280
+ .get('sc-3');
1281
+ expect(row.entity_sha).toBe('latest-sha');
1282
+ });
1283
+ it('should not update scenarios already pointing to the latest SHA', async () => {
1284
+ rawDb
1285
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha, display_name)
1286
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1287
+ .run('sc-3b', projectId, 'Header - Dark', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720, 'current-sha', 'Header');
1288
+ const result = await backfillEntityShaOnScenarios(db, [
1289
+ {
1290
+ sha: 'current-sha',
1291
+ name: 'Header',
1292
+ filePath: 'src/components/Header.tsx',
1293
+ },
1294
+ ]);
1295
+ expect(result.updated).toBe(0);
1296
+ });
1297
+ it('should skip scenarios with no matching entity', async () => {
1298
+ rawDb
1299
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height)
1300
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1301
+ .run('sc-4', projectId, 'Footer - Default', 'Footer', 'src/components/Footer.tsx', '/isolated-components/Footer', 'component', 1280, 720);
1302
+ // Entities list doesn't include Footer.tsx
1303
+ const result = await backfillEntityShaOnScenarios(db, [
1304
+ {
1305
+ sha: 'entity-sha-1',
1306
+ name: 'Header',
1307
+ filePath: 'src/components/Header.tsx',
1308
+ },
1309
+ ]);
1310
+ expect(result.updated).toBe(0);
1311
+ const row = rawDb
1312
+ .prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
1313
+ .get('sc-4');
1314
+ expect(row.entity_sha).toBeNull();
1315
+ });
1316
+ it('should match component scenarios by name+path, not just path (multi-entity files)', async () => {
1317
+ // A single file can export multiple components (e.g., FullPageLibrary.tsx
1318
+ // exports FullPageLibrary, FullPageEmptyState, FullPageArticleCard).
1319
+ // Each component scenario should get the SHA for its specific entity,
1320
+ // not whichever entity happens to be last in the map.
1321
+ rawDb
1322
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha)
1323
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1324
+ .run('sc-fplib', projectId, 'FullPageLibrary - Default', 'FullPageLibrary', 'src/library/FullPageLibrary.tsx', '/isolated-components/FullPageLibrary', 'component', 1280, 720, 'old-fplib-sha');
1325
+ rawDb
1326
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha)
1327
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1328
+ .run('sc-fpempty', projectId, 'FullPageEmptyState - Default', 'FullPageEmptyState', 'src/library/FullPageLibrary.tsx', '/isolated-components/FullPageEmptyState', 'component', 1280, 720, 'old-fpempty-sha');
1329
+ const result = await backfillEntityShaOnScenarios(db, [
1330
+ {
1331
+ sha: 'new-fplib-sha',
1332
+ name: 'FullPageLibrary',
1333
+ filePath: 'src/library/FullPageLibrary.tsx',
1334
+ },
1335
+ {
1336
+ sha: 'new-fpempty-sha',
1337
+ name: 'FullPageEmptyState',
1338
+ filePath: 'src/library/FullPageLibrary.tsx',
1339
+ },
1340
+ ]);
1341
+ expect(result.updated).toBe(2);
1342
+ const fplib = rawDb
1343
+ .prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
1344
+ .get('sc-fplib');
1345
+ expect(fplib.entity_sha).toBe('new-fplib-sha');
1346
+ const fpempty = rawDb
1347
+ .prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
1348
+ .get('sc-fpempty');
1349
+ expect(fpempty.entity_sha).toBe('new-fpempty-sha');
1350
+ });
1351
+ it('should prefer default export entity for page scenarios when file has multiple entities', async () => {
1352
+ // Bug: src/popup/App.tsx has multiple entities (App visual, getInitialView library,
1353
+ // View type). Page scenarios should link to the default export (App), not whichever
1354
+ // entity happens to come first in the array.
1355
+ rawDb
1356
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, page_file_path, url, type, viewport_width, viewport_height)
1357
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
1358
+ .run('sc-page-1', projectId, 'Save View - First Time User', 'src/popup/App.tsx', '/', 'application', 400, 600);
1359
+ // Pass multiple entities for the same file — default export should win
1360
+ // regardless of order. Here the default export (App) appears first but
1361
+ // gets overwritten by later non-default entities in the naive set() impl.
1362
+ const result = await backfillEntityShaOnScenarios(db, [
1363
+ {
1364
+ sha: 'app-sha',
1365
+ name: 'App',
1366
+ filePath: 'src/popup/App.tsx',
1367
+ isDefaultExport: true,
1368
+ },
1369
+ {
1370
+ sha: 'get-initial-view-sha',
1371
+ name: 'getInitialView',
1372
+ filePath: 'src/popup/App.tsx',
1373
+ isDefaultExport: false,
1374
+ },
1375
+ {
1376
+ sha: 'view-type-sha',
1377
+ name: 'View',
1378
+ filePath: 'src/popup/App.tsx',
1379
+ isDefaultExport: false,
1380
+ },
1381
+ ]);
1382
+ expect(result.updated).toBe(1);
1383
+ const row = rawDb
1384
+ .prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
1385
+ .get('sc-page-1');
1386
+ expect(row.entity_sha).toBe('app-sha');
1387
+ });
1388
+ it('should update page scenario to latest entity version when multiple versions exist', async () => {
1389
+ // Real-world bug: App.tsx entity gets versioned (code changes create new SHAs).
1390
+ // The scenario points to the oldest version's SHA. When sync receives all 3
1391
+ // versions of the same default-export entity, it should update the scenario
1392
+ // to the LATEST version, not keep it stuck on the oldest.
1393
+ rawDb
1394
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, page_file_path, url, type, viewport_width, viewport_height, entity_sha, display_name)
1395
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1396
+ .run('sc-home', projectId, 'Welcome Screen — First Install', 'src/popup/App.tsx', '/', 'application', 400, 600, 'version-1-sha', // stale — points to oldest version
1397
+ 'Home');
1398
+ // Pass all 3 versions — all default exports for the same file.
1399
+ // The sync should pick the LAST one (latest) since callers pass
1400
+ // entities in creation order.
1401
+ const result = await backfillEntityShaOnScenarios(db, [
1402
+ {
1403
+ sha: 'version-1-sha',
1404
+ name: 'App',
1405
+ filePath: 'src/popup/App.tsx',
1406
+ isDefaultExport: true,
1407
+ },
1408
+ {
1409
+ sha: 'version-2-sha',
1410
+ name: 'App',
1411
+ filePath: 'src/popup/App.tsx',
1412
+ isDefaultExport: true,
1413
+ },
1414
+ {
1415
+ sha: 'version-3-sha',
1416
+ name: 'App',
1417
+ filePath: 'src/popup/App.tsx',
1418
+ isDefaultExport: true,
1419
+ },
1420
+ ]);
1421
+ expect(result.updated).toBe(1);
1422
+ const row = rawDb
1423
+ .prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
1424
+ .get('sc-home');
1425
+ expect(row.entity_sha).toBe('version-3-sha');
1426
+ });
1427
+ });
1428
+ describe('countScenariosNeedingEntityBackfill', () => {
1429
+ let db;
1430
+ let rawDb;
1431
+ const projectId = 'test-project-id';
1432
+ beforeEach(async () => {
1433
+ rawDb = new Database(':memory:');
1434
+ db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
1435
+ await db.schema
1436
+ .createTable('editor_scenarios')
1437
+ .addColumn('id', 'varchar', (col) => col.primaryKey())
1438
+ .addColumn('project_id', 'varchar', (col) => col.notNull())
1439
+ .addColumn('name', 'varchar', (col) => col.notNull())
1440
+ .addColumn('description', 'text')
1441
+ .addColumn('component_name', 'varchar')
1442
+ .addColumn('component_path', 'varchar')
1443
+ .addColumn('url', 'varchar')
1444
+ .addColumn('type', 'varchar')
1445
+ .addColumn('screenshot_path', 'varchar')
1446
+ .addColumn('viewport_width', 'integer')
1447
+ .addColumn('viewport_height', 'integer')
1448
+ .addColumn('dimension', 'varchar')
1449
+ .addColumn('dimensions', 'text')
1450
+ .addColumn('screenshot_paths', 'text')
1451
+ .addColumn('page_file_path', 'varchar')
1452
+ .addColumn('entity_sha', 'varchar')
1453
+ .addColumn('display_name', 'varchar')
1454
+ .addColumn('created_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1455
+ .addColumn('updated_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1456
+ .execute();
1457
+ });
1458
+ afterEach(async () => {
1459
+ await db.destroy();
1460
+ });
1461
+ it('should NOT count scenarios that already have entity_sha', async () => {
1462
+ rawDb
1463
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha)
1464
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1465
+ .run('sc-1', projectId, 'Header - Default', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720, 'existing-sha');
1466
+ const count = await countScenariosNeedingEntityBackfill(db);
1467
+ expect(count).toBe(0);
1468
+ });
1469
+ it('should count scenarios with null entity_sha and page_file_path', async () => {
1470
+ rawDb
1471
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, page_file_path, url, type, viewport_width, viewport_height)
1472
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
1473
+ .run('sc-2', projectId, 'Home - Default', 'app/page.tsx', '/', 'application', 1280, 720);
1474
+ const count = await countScenariosNeedingEntityBackfill(db);
1475
+ expect(count).toBe(1);
1476
+ });
1477
+ it('should count scenarios with null entity_sha and component_path', async () => {
1478
+ rawDb
1479
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height)
1480
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1481
+ .run('sc-3', projectId, 'Footer - Default', 'Footer', 'src/components/Footer.tsx', '/isolated-components/Footer', 'component', 1280, 720);
1482
+ const count = await countScenariosNeedingEntityBackfill(db);
1483
+ expect(count).toBe(1);
1484
+ });
1485
+ it('should NOT count scenarios with null entity_sha and no file paths', async () => {
1486
+ rawDb
1487
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, url, type, viewport_width, viewport_height)
1488
+ VALUES (?, ?, ?, ?, ?, ?, ?)`)
1489
+ .run('sc-4', projectId, 'No File Path', '/', 'application', 1280, 720);
1490
+ const count = await countScenariosNeedingEntityBackfill(db);
1491
+ expect(count).toBe(0);
1492
+ });
1493
+ });
1494
+ describe('validateEntityLinkageForAppScenario', () => {
1495
+ let tmpDir;
1496
+ let glossaryPath;
1497
+ beforeEach(() => {
1498
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'entity-linkage-'));
1499
+ const codeyamDir = path.join(tmpDir, '.codeyam');
1500
+ fs.mkdirSync(codeyamDir, { recursive: true });
1501
+ glossaryPath = path.join(codeyamDir, 'glossary.json');
1502
+ });
1503
+ afterEach(() => {
1504
+ fs.rmSync(tmpDir, { recursive: true, force: true });
1505
+ });
1506
+ it('should return error when file is not in glossary', () => {
1507
+ // Write a glossary that does NOT include the target file
1508
+ fs.writeFileSync(glossaryPath, JSON.stringify([
1509
+ { name: 'Header', filePath: 'src/components/Header.tsx' },
1510
+ ]));
1511
+ const result = validateEntityLinkageForAppScenario({
1512
+ lookupFilePath: 'src/App.tsx',
1513
+ scenarioType: 'application',
1514
+ projectRoot: tmpDir,
1515
+ });
1516
+ expect(result.valid).toBe(false);
1517
+ expect(result.error).toContain('No glossary entry found');
1518
+ expect(result.error).toContain('src/App.tsx');
1519
+ });
1520
+ it('should return needsAnalysis when file IS in glossary but no entity exists', () => {
1521
+ // Write a glossary that includes the target file
1522
+ fs.writeFileSync(glossaryPath, JSON.stringify([
1523
+ { name: 'App', filePath: 'src/App.tsx', description: 'Main app' },
1524
+ ]));
1525
+ const result = validateEntityLinkageForAppScenario({
1526
+ lookupFilePath: 'src/App.tsx',
1527
+ scenarioType: 'application',
1528
+ projectRoot: tmpDir,
1529
+ });
1530
+ expect(result.valid).toBe(true);
1531
+ expect(result.needsAnalysis).toBe(true);
1532
+ });
1533
+ it('should skip validation for component-type scenarios', () => {
1534
+ // No glossary file exists at all
1535
+ const result = validateEntityLinkageForAppScenario({
1536
+ lookupFilePath: 'src/components/Header.tsx',
1537
+ scenarioType: 'component',
1538
+ projectRoot: tmpDir,
1539
+ });
1540
+ expect(result.valid).toBe(true);
1541
+ expect(result.needsAnalysis).toBeUndefined();
1542
+ });
1543
+ it('should skip validation when lookupFilePath is null', () => {
1544
+ const result = validateEntityLinkageForAppScenario({
1545
+ lookupFilePath: null,
1546
+ scenarioType: 'application',
1547
+ projectRoot: tmpDir,
1548
+ });
1549
+ expect(result.valid).toBe(true);
1550
+ expect(result.needsAnalysis).toBeUndefined();
1551
+ });
1552
+ it('should handle glossary wrapped in object format', () => {
1553
+ // LLMs sometimes write glossary as {"components": [...]}
1554
+ fs.writeFileSync(glossaryPath, JSON.stringify({
1555
+ components: [
1556
+ { name: 'App', filePath: 'src/App.tsx', description: 'Main app' },
1557
+ ],
1558
+ }));
1559
+ const result = validateEntityLinkageForAppScenario({
1560
+ lookupFilePath: 'src/App.tsx',
1561
+ scenarioType: 'user',
1562
+ projectRoot: tmpDir,
1563
+ });
1564
+ expect(result.valid).toBe(true);
1565
+ expect(result.needsAnalysis).toBe(true);
1566
+ });
1567
+ it('should return error when glossary file does not exist', () => {
1568
+ // Don't create a glossary file
1569
+ const result = validateEntityLinkageForAppScenario({
1570
+ lookupFilePath: 'src/App.tsx',
1571
+ scenarioType: 'application',
1572
+ projectRoot: tmpDir,
1573
+ });
1574
+ expect(result.valid).toBe(false);
1575
+ expect(result.error).toContain('No glossary entry found');
1576
+ });
1577
+ });
1578
+ describe('validateScenarioCategorization', () => {
1579
+ it('should reject componentName with non-isolation URL', () => {
1580
+ const result = validateScenarioCategorization({
1581
+ componentName: 'Library',
1582
+ url: '/library',
1583
+ });
1584
+ expect(result.valid).toBe(false);
1585
+ expect(result.error).toBeDefined();
1586
+ expect(result.error).toContain('componentName');
1587
+ });
1588
+ it('should accept componentName with /isolated-components/ URL', () => {
1589
+ const result = validateScenarioCategorization({
1590
+ componentName: 'Library',
1591
+ url: '/isolated-components/Library?s=Default',
1592
+ });
1593
+ expect(result.valid).toBe(true);
1594
+ });
1595
+ it('should accept componentName with /codeyam-isolate/ URL', () => {
1596
+ const result = validateScenarioCategorization({
1597
+ componentName: 'Library',
1598
+ url: '/codeyam-isolate/Library?s=Default',
1599
+ });
1600
+ expect(result.valid).toBe(true);
1601
+ });
1602
+ it('should accept application scenario without componentName', () => {
1603
+ const result = validateScenarioCategorization({
1604
+ url: '/library',
1605
+ type: 'application',
1606
+ });
1607
+ expect(result.valid).toBe(true);
1608
+ });
1609
+ it('should accept component with no URL', () => {
1610
+ const result = validateScenarioCategorization({
1611
+ componentName: 'Library',
1612
+ url: null,
1613
+ });
1614
+ expect(result.valid).toBe(true);
1615
+ });
1616
+ it('should suggest both fixes in error message', () => {
1617
+ const result = validateScenarioCategorization({
1618
+ componentName: 'Library',
1619
+ url: '/library',
1620
+ });
1621
+ expect(result.valid).toBe(false);
1622
+ // Should suggest changing URL to isolation route
1623
+ expect(result.error).toContain('/isolated-components/');
1624
+ // Should suggest removing componentName
1625
+ expect(result.error).toContain('remove');
1626
+ });
1627
+ });
1628
+ describe('resolveSeedFrom', () => {
1629
+ let tmpDir;
1630
+ let db;
1631
+ beforeEach(() => {
1632
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seedfrom-'));
1633
+ fs.mkdirSync(path.join(tmpDir, '.codeyam', 'editor-scenarios'), {
1634
+ recursive: true,
1635
+ });
1636
+ const raw = new Database(':memory:');
1637
+ raw.exec(`
1638
+ CREATE TABLE editor_scenarios (
1639
+ id TEXT PRIMARY KEY,
1640
+ project_id TEXT,
1641
+ name TEXT,
1642
+ description TEXT,
1643
+ component_name TEXT,
1644
+ component_path TEXT,
1645
+ url TEXT,
1646
+ type TEXT,
1647
+ screenshot_path TEXT,
1648
+ viewport_width INTEGER,
1649
+ viewport_height INTEGER,
1650
+ dimensions TEXT,
1651
+ screenshot_paths TEXT,
1652
+ page_file_path TEXT,
1653
+ entity_sha TEXT,
1654
+ display_name TEXT,
1655
+ created_at TEXT DEFAULT (datetime('now')),
1656
+ updated_at TEXT DEFAULT (datetime('now'))
1657
+ )
1658
+ `);
1659
+ db = new Kysely({ dialect: new SqliteDialect({ database: raw }) });
1660
+ });
1661
+ afterEach(() => {
1662
+ fs.rmSync(tmpDir, { recursive: true, force: true });
1663
+ });
1664
+ it('should resolve seed data by scenario ID', async () => {
1665
+ const scenarioId = 'aaaa-bbbb-cccc-dddd';
1666
+ const seedData = { article: [{ id: '1', title: 'Test' }] };
1667
+ // Write the seed file
1668
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'editor-scenarios', `${scenarioId}.seed.json`), JSON.stringify(seedData));
1669
+ const result = await resolveSeedFrom(tmpDir, db, scenarioId);
1670
+ expect(result).toEqual(seedData);
1671
+ });
1672
+ it('should resolve seed data by scenario name', async () => {
1673
+ const scenarioId = 'eeee-ffff-1111-2222';
1674
+ const seedData = { user: [{ id: '1', name: 'Alice' }] };
1675
+ // Write the seed file
1676
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'editor-scenarios', `${scenarioId}.seed.json`), JSON.stringify(seedData));
1677
+ // Insert scenario into DB so name lookup works
1678
+ await db
1679
+ .insertInto('editor_scenarios')
1680
+ .values({
1681
+ id: scenarioId,
1682
+ project_id: 'proj-1',
1683
+ name: 'Library - Rich Data',
1684
+ viewport_width: 1280,
1685
+ viewport_height: 720,
1686
+ })
1687
+ .execute();
1688
+ const result = await resolveSeedFrom(tmpDir, db, 'Library - Rich Data');
1689
+ expect(result).toEqual(seedData);
1690
+ });
1691
+ it('should fall back to seed from scenario data file when no .seed.json exists', async () => {
1692
+ const scenarioId = 'cccc-dddd-eeee-ffff';
1693
+ const seedData = { tag: [{ id: '1', name: 'AI' }] };
1694
+ // Write the scenario data file (not the .seed.json)
1695
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'editor-scenarios', `${scenarioId}.json`), JSON.stringify({ seed: seedData, _metadata: { name: 'Test' } }));
1696
+ const result = await resolveSeedFrom(tmpDir, db, scenarioId);
1697
+ expect(result).toEqual(seedData);
1698
+ });
1699
+ it('should return null when scenario not found', async () => {
1700
+ const result = await resolveSeedFrom(tmpDir, db, 'nonexistent');
1701
+ expect(result).toBeNull();
1702
+ });
1703
+ it('should return null when scenario exists but has no seed data', async () => {
1704
+ const scenarioId = 'no-seed-1111-2222';
1705
+ // Scenario data file with no seed
1706
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'editor-scenarios', `${scenarioId}.json`), JSON.stringify({ _metadata: { name: 'No Seed' } }));
1707
+ const result = await resolveSeedFrom(tmpDir, db, scenarioId);
1708
+ expect(result).toBeNull();
1709
+ });
1710
+ it('should return null when seed.json contains invalid JSON', async () => {
1711
+ const scenarioId = 'bad-json-1111';
1712
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'editor-scenarios', `${scenarioId}.seed.json`), '{ this is not valid json }}}');
1713
+ const result = await resolveSeedFrom(tmpDir, db, scenarioId);
1714
+ expect(result).toBeNull();
1715
+ });
1716
+ it('should return null when scenario data file contains invalid JSON', async () => {
1717
+ const scenarioId = 'bad-data-2222';
1718
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'editor-scenarios', `${scenarioId}.json`), 'not json at all');
1719
+ const result = await resolveSeedFrom(tmpDir, db, scenarioId);
1720
+ expect(result).toBeNull();
1721
+ });
1722
+ it('should pick the most recent scenario when multiple share the same name', async () => {
1723
+ const oldId = 'old-scenario-1111';
1724
+ const newId = 'new-scenario-2222';
1725
+ const oldSeed = { article: [{ id: '1', title: 'Old' }] };
1726
+ const newSeed = { article: [{ id: '2', title: 'New' }] };
1727
+ // Write seed files for both
1728
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'editor-scenarios', `${oldId}.seed.json`), JSON.stringify(oldSeed));
1729
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'editor-scenarios', `${newId}.seed.json`), JSON.stringify(newSeed));
1730
+ // Insert both with the same name, different created_at
1731
+ await db
1732
+ .insertInto('editor_scenarios')
1733
+ .values({
1734
+ id: oldId,
1735
+ project_id: 'proj-1',
1736
+ name: 'Shared Name',
1737
+ viewport_width: 1280,
1738
+ viewport_height: 720,
1739
+ created_at: '2026-01-01 00:00:00',
1740
+ })
1741
+ .execute();
1742
+ await db
1743
+ .insertInto('editor_scenarios')
1744
+ .values({
1745
+ id: newId,
1746
+ project_id: 'proj-1',
1747
+ name: 'Shared Name',
1748
+ viewport_width: 1280,
1749
+ viewport_height: 720,
1750
+ created_at: '2026-03-01 00:00:00',
1751
+ })
1752
+ .execute();
1753
+ const result = await resolveSeedFrom(tmpDir, db, 'Shared Name');
1754
+ expect(result).toEqual(newSeed);
1755
+ });
1756
+ it('should prefer .seed.json over inline seed in data file', async () => {
1757
+ const scenarioId = 'prefer-seed-file';
1758
+ const seedFileData = { article: [{ id: '1', title: 'From seed file' }] };
1759
+ const inlineData = { article: [{ id: '2', title: 'From inline' }] };
1760
+ // Write both files
1761
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'editor-scenarios', `${scenarioId}.seed.json`), JSON.stringify(seedFileData));
1762
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'editor-scenarios', `${scenarioId}.json`), JSON.stringify({ seed: inlineData }));
1763
+ const result = await resolveSeedFrom(tmpDir, db, scenarioId);
1764
+ expect(result).toEqual(seedFileData);
1765
+ });
1766
+ });
1058
1767
  });
1059
1768
  //# sourceMappingURL=editorScenarios.test.js.map