@codeyam/codeyam-cli 0.1.11 → 0.1.13

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 (214) 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 +2 -2
  4. package/analyzer-template/packages/ai/package.json +1 -1
  5. package/analyzer-template/packages/aws/package.json +1 -1
  6. package/analyzer-template/packages/database/package.json +1 -1
  7. package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +42 -16
  8. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +3 -1
  9. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
  10. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +44 -16
  11. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  12. package/codeyam-cli/src/cli.js +9 -0
  13. package/codeyam-cli/src/cli.js.map +1 -1
  14. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js +51 -0
  15. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js.map +1 -0
  16. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +11 -0
  17. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -1
  18. package/codeyam-cli/src/commands/editor.js +1360 -201
  19. package/codeyam-cli/src/commands/editor.js.map +1 -1
  20. package/codeyam-cli/src/commands/editorIsolateArgs.js +25 -0
  21. package/codeyam-cli/src/commands/editorIsolateArgs.js.map +1 -0
  22. package/codeyam-cli/src/commands/telemetry.js +37 -0
  23. package/codeyam-cli/src/commands/telemetry.js.map +1 -0
  24. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +893 -1
  25. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  26. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js +76 -0
  27. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js.map +1 -0
  28. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js +100 -0
  29. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js.map +1 -0
  30. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +76 -3
  31. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
  32. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +261 -0
  33. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -0
  34. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +75 -1
  35. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
  36. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js +435 -0
  37. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js.map +1 -0
  38. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +441 -17
  39. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  40. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +67 -0
  41. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
  42. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js +143 -0
  43. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js.map +1 -0
  44. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js +66 -0
  45. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js.map +1 -0
  46. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js +53 -0
  47. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js.map +1 -0
  48. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +67 -9
  49. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  50. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js +118 -0
  51. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js.map +1 -0
  52. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +40 -1
  53. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
  54. package/codeyam-cli/src/utils/__tests__/telemetry.test.js +159 -0
  55. package/codeyam-cli/src/utils/__tests__/telemetry.test.js.map +1 -0
  56. package/codeyam-cli/src/utils/analysisRunner.js +3 -1
  57. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  58. package/codeyam-cli/src/utils/editorAudit.js +145 -0
  59. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  60. package/codeyam-cli/src/utils/editorBroadcastViewport.js +26 -0
  61. package/codeyam-cli/src/utils/editorBroadcastViewport.js.map +1 -0
  62. package/codeyam-cli/src/utils/editorDeleteScenario.js +67 -0
  63. package/codeyam-cli/src/utils/editorDeleteScenario.js.map +1 -0
  64. package/codeyam-cli/src/utils/editorEntityChangeStatus.js +13 -7
  65. package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
  66. package/codeyam-cli/src/utils/editorEntityHelpers.js +129 -0
  67. package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -0
  68. package/codeyam-cli/src/utils/editorLoaderHelpers.js +40 -1
  69. package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
  70. package/codeyam-cli/src/utils/editorMigration.js +224 -0
  71. package/codeyam-cli/src/utils/editorMigration.js.map +1 -0
  72. package/codeyam-cli/src/utils/editorScenarios.js +163 -2
  73. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  74. package/codeyam-cli/src/utils/editorSeedAdapter.js +253 -4
  75. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
  76. package/codeyam-cli/src/utils/editorShouldRevalidate.js +21 -0
  77. package/codeyam-cli/src/utils/editorShouldRevalidate.js.map +1 -0
  78. package/codeyam-cli/src/utils/entityChangeStatus.js +19 -2
  79. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  80. package/codeyam-cli/src/utils/entityChangeStatus.server.js +7 -3
  81. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  82. package/codeyam-cli/src/utils/fileWatcher.js +38 -0
  83. package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
  84. package/codeyam-cli/src/utils/install-skills.js +9 -0
  85. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  86. package/codeyam-cli/src/utils/routePatternMatching.js +129 -0
  87. package/codeyam-cli/src/utils/routePatternMatching.js.map +1 -0
  88. package/codeyam-cli/src/utils/scenarioCoverage.js +8 -9
  89. package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -1
  90. package/codeyam-cli/src/utils/scenariosManifest.js +18 -10
  91. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  92. package/codeyam-cli/src/utils/telemetry.js +106 -0
  93. package/codeyam-cli/src/utils/telemetry.js.map +1 -0
  94. package/codeyam-cli/src/utils/telemetryMiddleware.js +22 -0
  95. package/codeyam-cli/src/utils/telemetryMiddleware.js.map +1 -0
  96. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +35 -0
  97. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -0
  98. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +61 -0
  99. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  100. package/codeyam-cli/src/webserver/app/types/editor.js +8 -0
  101. package/codeyam-cli/src/webserver/app/types/editor.js.map +1 -0
  102. package/codeyam-cli/src/webserver/backgroundServer.js +18 -4
  103. package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
  104. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-CzTDWkF2.js +1 -0
  105. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BcgbViKV.js → EntityItem-BFbq6iFk.js} +3 -3
  106. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-CQgyEGV-.js +1 -0
  107. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CQIG2qda.js → EntityTypeIcon-B6OMi58N.js} +1 -1
  108. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-DuYodzo1.js +1 -0
  109. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-CXo9EeCl.js +25 -0
  110. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DYCNb2It.js +3 -0
  111. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-BU_OAEMP.js → LoadingDots-By5zI316.js} +1 -1
  112. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-ceAyBX-H.js → LogViewer-CZgY3sxX.js} +3 -3
  113. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-BzHcG7SE.js → ReportIssueModal-CnYYwRDw.js} +2 -2
  114. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-CDoF7ZpU.js +1 -0
  115. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-TSD3C211.js → ScenarioViewer-DrnfvaLL.js} +3 -3
  116. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Df3UCi8k.js +34 -0
  117. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-CK7-NaPZ.js +1 -0
  118. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-DRKR9T0U.js +1 -0
  119. package/codeyam-cli/src/webserver/build/client/assets/{_index-DLxKhri3.js → _index-ClR-g3tY.js} +2 -2
  120. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BcY3q6nt.js → activity.(_tab)-DTH6ydEA.js} +3 -3
  121. package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-Duc5hnl7.js → addon-web-links-74hnHF59.js} +1 -1
  122. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bni3iiUj.js → agent-transcripts-B8CYhCO9.js} +3 -3
  123. package/codeyam-cli/src/webserver/build/client/assets/api.editor-rename-scenario-l0sNRNKZ.js +1 -0
  124. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-seed-state-l0sNRNKZ.js +1 -0
  125. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-prompt-l0sNRNKZ.js +1 -0
  126. package/codeyam-cli/src/webserver/build/client/assets/{book-open-BYOypzCa.js → book-open-CLaoh4ac.js} +1 -1
  127. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-C_Pmso5S.js → chevron-down-BZ2DZxbW.js} +1 -1
  128. package/codeyam-cli/src/webserver/build/client/assets/{chunk-JZWAC4HX-C4pqxYJB.js → chunk-JZWAC4HX-BBXArFPl.js} +13 -21
  129. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-BVMi9VA5.js → circle-check-CT4unAk-.js} +1 -1
  130. package/codeyam-cli/src/webserver/build/client/assets/{copy-n2FB0_Sw.js → copy-zK0B6Nu-.js} +1 -1
  131. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-CC6AbExI.js → createLucideIcon-DJB0YQJL.js} +1 -1
  132. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-CkXFP_i-.js +1 -0
  133. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-DPw7NZHc.js +1 -0
  134. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-CjC3_6JI.js +58 -0
  135. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-DBa7T2FK.js +41 -0
  136. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-DwCV5__E.js → entity._sha._-BqAN7hyG.js} +2 -2
  137. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-BOi8kpwd.js +6 -0
  138. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-Dg1NhIms.js +6 -0
  139. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-CJX6kkkV.js +6 -0
  140. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMvVHNXU.js → entity._sha_.edit._scenarioId-BhVjZhKg.js} +2 -2
  141. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-DTvKq3TY.js → entry.client-_gzKltPN.js} +6 -6
  142. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-Daa96Fr1.js +1 -0
  143. package/codeyam-cli/src/webserver/build/client/assets/files-CV_17tZS.js +1 -0
  144. package/codeyam-cli/src/webserver/build/client/assets/git-D-YXmMbR.js +1 -0
  145. package/codeyam-cli/src/webserver/build/client/assets/globals-DRvOjyO3.css +1 -0
  146. package/codeyam-cli/src/webserver/build/client/assets/{index-yHOVb4rc.js → index-Blo6EK8G.js} +1 -1
  147. package/codeyam-cli/src/webserver/build/client/assets/{index-10oVnAAH.js → index-BsX0F-9C.js} +1 -1
  148. package/codeyam-cli/src/webserver/build/client/assets/{index-BcvgDzbZ.js → index-CCrgCshv.js} +1 -1
  149. package/codeyam-cli/src/webserver/build/client/assets/jsx-runtime-D_zvdyIk.js +9 -0
  150. package/codeyam-cli/src/webserver/build/client/assets/labs-Byazq8Pv.js +1 -0
  151. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DaAZ_H2w.js → loader-circle-DVQ0oHR7.js} +1 -1
  152. package/codeyam-cli/src/webserver/build/client/assets/manifest-75b1b319.js +1 -0
  153. package/codeyam-cli/src/webserver/build/client/assets/{memory-9gnxSZlb.js → memory-b-VmA2Vj.js} +2 -2
  154. package/codeyam-cli/src/webserver/build/client/assets/{pause-f5-1lKBt.js → pause-DGcndCAa.js} +1 -1
  155. package/codeyam-cli/src/webserver/build/client/assets/{root-DBjt6o04.js → root-F-k2uYj5.js} +15 -15
  156. package/codeyam-cli/src/webserver/build/client/assets/{search-Di64LWVb.js → search-C0Uw0bcK.js} +1 -1
  157. package/codeyam-cli/src/webserver/build/client/assets/settings-OoNgHIfW.js +1 -0
  158. package/codeyam-cli/src/webserver/build/client/assets/simulations-Bcemfu8a.js +1 -0
  159. package/codeyam-cli/src/webserver/build/client/assets/{terminal-Br7MOqts.js → terminal-BgMmG7R9.js} +1 -1
  160. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BLdiCuG-.js → triangle-alert-Cs87hJYK.js} +1 -1
  161. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-BR3Rs7JY.js +1 -0
  162. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-C14nCb1q.js → useLastLogLine-BxxP_XF9.js} +1 -1
  163. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-BermyNU5.js +1 -0
  164. package/codeyam-cli/src/webserver/build/client/assets/useToast-a_QN_W9_.js +1 -0
  165. package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-lv2ooewK.js +13 -0
  166. package/codeyam-cli/src/webserver/build/server/assets/{index-DsZjKspK.js → index-Im3Smyei.js} +1 -1
  167. package/codeyam-cli/src/webserver/build/server/assets/init-BjuAFKGM.js +10 -0
  168. package/codeyam-cli/src/webserver/build/server/assets/progress-CHTtrxFG.js +1 -0
  169. package/codeyam-cli/src/webserver/build/server/assets/server-build-CNjF0B9B.js +551 -0
  170. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  171. package/codeyam-cli/src/webserver/build-info.json +5 -5
  172. package/codeyam-cli/src/webserver/editorProxy.js +112 -13
  173. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  174. package/codeyam-cli/src/webserver/mockStateEvents.js +28 -0
  175. package/codeyam-cli/src/webserver/mockStateEvents.js.map +1 -0
  176. package/codeyam-cli/src/webserver/server.js +41 -0
  177. package/codeyam-cli/src/webserver/server.js.map +1 -1
  178. package/codeyam-cli/src/webserver/terminalServer.js +74 -8
  179. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  180. package/codeyam-cli/templates/editor-step-hook.py +104 -20
  181. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +42 -7
  182. package/codeyam-cli/templates/seed-adapters/supabase.ts +282 -0
  183. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +62 -0
  184. package/package.json +2 -1
  185. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +44 -16
  186. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  187. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-BPXZwM4t.js +0 -1
  188. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-g3saevPb.js +0 -1
  189. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-Bu6c6aDe.js +0 -1
  190. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-DYFW3lDD.js +0 -25
  191. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DLeucoVX.js +0 -3
  192. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-BED4B6sP.js +0 -1
  193. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bb5uFQ5V.js +0 -34
  194. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-C8OKAR5x.js +0 -1
  195. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +0 -1
  196. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Ii3inc0_.js +0 -1
  197. package/codeyam-cli/src/webserver/build/client/assets/editor-16o0AIFV.js +0 -15
  198. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-7Uga8I59.js +0 -41
  199. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-BwKcai0j.js +0 -6
  200. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CHMiAog3.js +0 -6
  201. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-p9hhkjJM.js +0 -6
  202. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-cPo8LiG3.js +0 -1
  203. package/codeyam-cli/src/webserver/build/client/assets/files-BZrlFE1F.js +0 -1
  204. package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +0 -1
  205. package/codeyam-cli/src/webserver/build/client/assets/globals-CQPR0pFR.css +0 -1
  206. package/codeyam-cli/src/webserver/build/client/assets/labs-Zk7ryIM1.js +0 -1
  207. package/codeyam-cli/src/webserver/build/client/assets/manifest-76e7b62c.js +0 -1
  208. package/codeyam-cli/src/webserver/build/client/assets/settings-0OrEMU6J.js +0 -1
  209. package/codeyam-cli/src/webserver/build/client/assets/simulations-DWT-CvLy.js +0 -1
  210. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-C-_hOl_g.js +0 -1
  211. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-O-jkvSPx.js +0 -1
  212. package/codeyam-cli/src/webserver/build/client/assets/useToast-9FIWuYfK.js +0 -1
  213. package/codeyam-cli/src/webserver/build/server/assets/init-DdqKD2p4.js +0 -10
  214. package/codeyam-cli/src/webserver/build/server/assets/server-build-CKKeWtVK.js +0 -444
@@ -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, } 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')
@@ -1055,5 +1040,444 @@ describe('editorScenarios', () => {
1055
1040
  expect(isRowInFeatureSession({ created_at: '2026-03-12 14:30:00', updated_at: null }, featureStart)).toBe(true);
1056
1041
  });
1057
1042
  });
1043
+ describe('upsertEditorScenario with entitySha and displayName', () => {
1044
+ let db;
1045
+ let rawDb;
1046
+ const projectId = 'test-project-id';
1047
+ beforeEach(async () => {
1048
+ rawDb = new Database(':memory:');
1049
+ db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
1050
+ await db.schema
1051
+ .createTable('editor_scenarios')
1052
+ .addColumn('id', 'varchar', (col) => col.primaryKey())
1053
+ .addColumn('project_id', 'varchar', (col) => col.notNull())
1054
+ .addColumn('name', 'varchar', (col) => col.notNull())
1055
+ .addColumn('description', 'text')
1056
+ .addColumn('component_name', 'varchar')
1057
+ .addColumn('component_path', 'varchar')
1058
+ .addColumn('url', 'varchar')
1059
+ .addColumn('type', 'varchar')
1060
+ .addColumn('screenshot_path', 'varchar')
1061
+ .addColumn('viewport_width', 'integer')
1062
+ .addColumn('viewport_height', 'integer')
1063
+ .addColumn('dimension', 'varchar')
1064
+ .addColumn('dimensions', 'text')
1065
+ .addColumn('screenshot_paths', 'text')
1066
+ .addColumn('page_file_path', 'varchar')
1067
+ .addColumn('entity_sha', 'varchar')
1068
+ .addColumn('display_name', 'varchar')
1069
+ .addColumn('created_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1070
+ .addColumn('updated_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1071
+ .execute();
1072
+ });
1073
+ afterEach(async () => {
1074
+ await db.destroy();
1075
+ });
1076
+ it('should store entitySha and displayName on insert', async () => {
1077
+ const result = await upsertEditorScenario(db, {
1078
+ projectId,
1079
+ name: 'Home - Default',
1080
+ description: null,
1081
+ componentName: null,
1082
+ componentPath: null,
1083
+ url: '/',
1084
+ type: 'application',
1085
+ viewportWidth: 400,
1086
+ viewportHeight: 600,
1087
+ entitySha: 'abc123',
1088
+ displayName: 'Home',
1089
+ });
1090
+ expect(result.isNew).toBe(true);
1091
+ const row = rawDb
1092
+ .prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
1093
+ .get(result.scenarioId);
1094
+ expect(row.entity_sha).toBe('abc123');
1095
+ expect(row.display_name).toBe('Home');
1096
+ });
1097
+ it('should update entitySha and displayName on upsert', async () => {
1098
+ // Insert first
1099
+ await upsertEditorScenario(db, {
1100
+ projectId,
1101
+ name: 'Home - Default',
1102
+ description: null,
1103
+ componentName: null,
1104
+ componentPath: null,
1105
+ url: '/',
1106
+ type: 'application',
1107
+ viewportWidth: 400,
1108
+ viewportHeight: 600,
1109
+ entitySha: 'old-sha',
1110
+ displayName: 'OldName',
1111
+ });
1112
+ // Upsert with new values
1113
+ const result = await upsertEditorScenario(db, {
1114
+ projectId,
1115
+ name: 'Home - Default',
1116
+ description: null,
1117
+ componentName: null,
1118
+ componentPath: null,
1119
+ url: '/',
1120
+ type: 'application',
1121
+ viewportWidth: 400,
1122
+ viewportHeight: 600,
1123
+ entitySha: 'new-sha',
1124
+ displayName: 'Home',
1125
+ });
1126
+ expect(result.isNew).toBe(false);
1127
+ const row = rawDb
1128
+ .prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
1129
+ .get(result.scenarioId);
1130
+ expect(row.entity_sha).toBe('new-sha');
1131
+ expect(row.display_name).toBe('Home');
1132
+ });
1133
+ it('should not overwrite entitySha when not provided', async () => {
1134
+ // Insert with SHA
1135
+ const first = await upsertEditorScenario(db, {
1136
+ projectId,
1137
+ name: 'Home - Default',
1138
+ description: null,
1139
+ componentName: null,
1140
+ componentPath: null,
1141
+ url: '/',
1142
+ type: 'application',
1143
+ viewportWidth: 400,
1144
+ viewportHeight: 600,
1145
+ entitySha: 'keep-this',
1146
+ displayName: 'Home',
1147
+ });
1148
+ // Upsert without entitySha/displayName
1149
+ await upsertEditorScenario(db, {
1150
+ projectId,
1151
+ name: 'Home - Default',
1152
+ description: 'updated',
1153
+ componentName: null,
1154
+ componentPath: null,
1155
+ url: '/',
1156
+ type: 'application',
1157
+ viewportWidth: 400,
1158
+ viewportHeight: 600,
1159
+ });
1160
+ const row = rawDb
1161
+ .prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
1162
+ .get(first.scenarioId);
1163
+ expect(row.entity_sha).toBe('keep-this');
1164
+ expect(row.display_name).toBe('Home');
1165
+ });
1166
+ });
1167
+ describe('backfillEntityShaOnScenarios', () => {
1168
+ let db;
1169
+ let rawDb;
1170
+ const projectId = 'test-project-id';
1171
+ beforeEach(async () => {
1172
+ rawDb = new Database(':memory:');
1173
+ db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
1174
+ await db.schema
1175
+ .createTable('editor_scenarios')
1176
+ .addColumn('id', 'varchar', (col) => col.primaryKey())
1177
+ .addColumn('project_id', 'varchar', (col) => col.notNull())
1178
+ .addColumn('name', 'varchar', (col) => col.notNull())
1179
+ .addColumn('description', 'text')
1180
+ .addColumn('component_name', 'varchar')
1181
+ .addColumn('component_path', 'varchar')
1182
+ .addColumn('url', 'varchar')
1183
+ .addColumn('type', 'varchar')
1184
+ .addColumn('screenshot_path', 'varchar')
1185
+ .addColumn('viewport_width', 'integer')
1186
+ .addColumn('viewport_height', 'integer')
1187
+ .addColumn('dimension', 'varchar')
1188
+ .addColumn('dimensions', 'text')
1189
+ .addColumn('screenshot_paths', 'text')
1190
+ .addColumn('page_file_path', 'varchar')
1191
+ .addColumn('entity_sha', 'varchar')
1192
+ .addColumn('display_name', 'varchar')
1193
+ .addColumn('created_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1194
+ .addColumn('updated_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1195
+ .execute();
1196
+ });
1197
+ afterEach(async () => {
1198
+ await db.destroy();
1199
+ });
1200
+ it('should backfill component scenario by component_path match', async () => {
1201
+ // Insert a scenario with null entity_sha but with component_path
1202
+ rawDb
1203
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height)
1204
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1205
+ .run('sc-1', projectId, 'Header - Default', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720);
1206
+ const result = await backfillEntityShaOnScenarios(db, [
1207
+ {
1208
+ sha: 'entity-sha-1',
1209
+ name: 'Header',
1210
+ filePath: 'src/components/Header.tsx',
1211
+ },
1212
+ ]);
1213
+ expect(result.updated).toBe(1);
1214
+ const row = rawDb
1215
+ .prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
1216
+ .get('sc-1');
1217
+ expect(row.entity_sha).toBe('entity-sha-1');
1218
+ expect(row.display_name).toBe('Header');
1219
+ });
1220
+ it('should backfill page scenario by page_file_path match and derive display_name from route', async () => {
1221
+ // Insert a scenario with null entity_sha but with page_file_path
1222
+ rawDb
1223
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, page_file_path, url, type, viewport_width, viewport_height)
1224
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
1225
+ .run('sc-2', projectId, 'Feedback - Default', 'app/feedback/page.tsx', '/feedback', 'application', 1280, 720);
1226
+ const result = await backfillEntityShaOnScenarios(db, [
1227
+ {
1228
+ sha: 'entity-sha-2',
1229
+ name: 'Feedback',
1230
+ filePath: 'app/feedback/page.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-2');
1237
+ expect(row.entity_sha).toBe('entity-sha-2');
1238
+ // display_name derived from page_file_path via routeDisplayName(buildRoutePattern())
1239
+ expect(row.display_name).toBe('Feedback');
1240
+ });
1241
+ it('should update scenarios with stale entity_sha to latest', async () => {
1242
+ // Margo bug: entity code changes create new versions with new SHAs,
1243
+ // but scenarios still point to the old SHA. Since journals preserve
1244
+ // screenshots and everything is in git, we always want scenarios
1245
+ // pointing to the latest entity version.
1246
+ rawDb
1247
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha, display_name)
1248
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1249
+ .run('sc-3', projectId, 'Header - Dark', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720, 'old-sha', 'Header');
1250
+ const result = await backfillEntityShaOnScenarios(db, [
1251
+ {
1252
+ sha: 'latest-sha',
1253
+ name: 'Header',
1254
+ filePath: 'src/components/Header.tsx',
1255
+ },
1256
+ ]);
1257
+ expect(result.updated).toBe(1);
1258
+ const row = rawDb
1259
+ .prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
1260
+ .get('sc-3');
1261
+ expect(row.entity_sha).toBe('latest-sha');
1262
+ });
1263
+ it('should not update scenarios already pointing to the latest SHA', async () => {
1264
+ rawDb
1265
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha, display_name)
1266
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1267
+ .run('sc-3b', projectId, 'Header - Dark', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720, 'current-sha', 'Header');
1268
+ const result = await backfillEntityShaOnScenarios(db, [
1269
+ {
1270
+ sha: 'current-sha',
1271
+ name: 'Header',
1272
+ filePath: 'src/components/Header.tsx',
1273
+ },
1274
+ ]);
1275
+ expect(result.updated).toBe(0);
1276
+ });
1277
+ it('should skip scenarios with no matching entity', async () => {
1278
+ rawDb
1279
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height)
1280
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1281
+ .run('sc-4', projectId, 'Footer - Default', 'Footer', 'src/components/Footer.tsx', '/isolated-components/Footer', 'component', 1280, 720);
1282
+ // Entities list doesn't include Footer.tsx
1283
+ const result = await backfillEntityShaOnScenarios(db, [
1284
+ {
1285
+ sha: 'entity-sha-1',
1286
+ name: 'Header',
1287
+ filePath: 'src/components/Header.tsx',
1288
+ },
1289
+ ]);
1290
+ expect(result.updated).toBe(0);
1291
+ const row = rawDb
1292
+ .prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
1293
+ .get('sc-4');
1294
+ expect(row.entity_sha).toBeNull();
1295
+ });
1296
+ it('should match component scenarios by name+path, not just path (multi-entity files)', async () => {
1297
+ // A single file can export multiple components (e.g., FullPageLibrary.tsx
1298
+ // exports FullPageLibrary, FullPageEmptyState, FullPageArticleCard).
1299
+ // Each component scenario should get the SHA for its specific entity,
1300
+ // not whichever entity happens to be last in the map.
1301
+ rawDb
1302
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha)
1303
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1304
+ .run('sc-fplib', projectId, 'FullPageLibrary - Default', 'FullPageLibrary', 'src/library/FullPageLibrary.tsx', '/isolated-components/FullPageLibrary', 'component', 1280, 720, 'old-fplib-sha');
1305
+ rawDb
1306
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha)
1307
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1308
+ .run('sc-fpempty', projectId, 'FullPageEmptyState - Default', 'FullPageEmptyState', 'src/library/FullPageLibrary.tsx', '/isolated-components/FullPageEmptyState', 'component', 1280, 720, 'old-fpempty-sha');
1309
+ const result = await backfillEntityShaOnScenarios(db, [
1310
+ {
1311
+ sha: 'new-fplib-sha',
1312
+ name: 'FullPageLibrary',
1313
+ filePath: 'src/library/FullPageLibrary.tsx',
1314
+ },
1315
+ {
1316
+ sha: 'new-fpempty-sha',
1317
+ name: 'FullPageEmptyState',
1318
+ filePath: 'src/library/FullPageLibrary.tsx',
1319
+ },
1320
+ ]);
1321
+ expect(result.updated).toBe(2);
1322
+ const fplib = rawDb
1323
+ .prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
1324
+ .get('sc-fplib');
1325
+ expect(fplib.entity_sha).toBe('new-fplib-sha');
1326
+ const fpempty = rawDb
1327
+ .prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
1328
+ .get('sc-fpempty');
1329
+ expect(fpempty.entity_sha).toBe('new-fpempty-sha');
1330
+ });
1331
+ });
1332
+ describe('countScenariosNeedingEntityBackfill', () => {
1333
+ let db;
1334
+ let rawDb;
1335
+ const projectId = 'test-project-id';
1336
+ beforeEach(async () => {
1337
+ rawDb = new Database(':memory:');
1338
+ db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
1339
+ await db.schema
1340
+ .createTable('editor_scenarios')
1341
+ .addColumn('id', 'varchar', (col) => col.primaryKey())
1342
+ .addColumn('project_id', 'varchar', (col) => col.notNull())
1343
+ .addColumn('name', 'varchar', (col) => col.notNull())
1344
+ .addColumn('description', 'text')
1345
+ .addColumn('component_name', 'varchar')
1346
+ .addColumn('component_path', 'varchar')
1347
+ .addColumn('url', 'varchar')
1348
+ .addColumn('type', 'varchar')
1349
+ .addColumn('screenshot_path', 'varchar')
1350
+ .addColumn('viewport_width', 'integer')
1351
+ .addColumn('viewport_height', 'integer')
1352
+ .addColumn('dimension', 'varchar')
1353
+ .addColumn('dimensions', 'text')
1354
+ .addColumn('screenshot_paths', 'text')
1355
+ .addColumn('page_file_path', 'varchar')
1356
+ .addColumn('entity_sha', 'varchar')
1357
+ .addColumn('display_name', 'varchar')
1358
+ .addColumn('created_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1359
+ .addColumn('updated_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
1360
+ .execute();
1361
+ });
1362
+ afterEach(async () => {
1363
+ await db.destroy();
1364
+ });
1365
+ it('should NOT count scenarios that already have entity_sha', async () => {
1366
+ rawDb
1367
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha)
1368
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1369
+ .run('sc-1', projectId, 'Header - Default', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720, 'existing-sha');
1370
+ const count = await countScenariosNeedingEntityBackfill(db);
1371
+ expect(count).toBe(0);
1372
+ });
1373
+ it('should count scenarios with null entity_sha and page_file_path', async () => {
1374
+ rawDb
1375
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, page_file_path, url, type, viewport_width, viewport_height)
1376
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
1377
+ .run('sc-2', projectId, 'Home - Default', 'app/page.tsx', '/', 'application', 1280, 720);
1378
+ const count = await countScenariosNeedingEntityBackfill(db);
1379
+ expect(count).toBe(1);
1380
+ });
1381
+ it('should count scenarios with null entity_sha and component_path', async () => {
1382
+ rawDb
1383
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height)
1384
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1385
+ .run('sc-3', projectId, 'Footer - Default', 'Footer', 'src/components/Footer.tsx', '/isolated-components/Footer', 'component', 1280, 720);
1386
+ const count = await countScenariosNeedingEntityBackfill(db);
1387
+ expect(count).toBe(1);
1388
+ });
1389
+ it('should NOT count scenarios with null entity_sha and no file paths', async () => {
1390
+ rawDb
1391
+ .prepare(`INSERT INTO editor_scenarios (id, project_id, name, url, type, viewport_width, viewport_height)
1392
+ VALUES (?, ?, ?, ?, ?, ?, ?)`)
1393
+ .run('sc-4', projectId, 'No File Path', '/', 'application', 1280, 720);
1394
+ const count = await countScenariosNeedingEntityBackfill(db);
1395
+ expect(count).toBe(0);
1396
+ });
1397
+ });
1398
+ describe('validateEntityLinkageForAppScenario', () => {
1399
+ let tmpDir;
1400
+ let glossaryPath;
1401
+ beforeEach(() => {
1402
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'entity-linkage-'));
1403
+ const codeyamDir = path.join(tmpDir, '.codeyam');
1404
+ fs.mkdirSync(codeyamDir, { recursive: true });
1405
+ glossaryPath = path.join(codeyamDir, 'glossary.json');
1406
+ });
1407
+ afterEach(() => {
1408
+ fs.rmSync(tmpDir, { recursive: true, force: true });
1409
+ });
1410
+ it('should return error when file is not in glossary', () => {
1411
+ // Write a glossary that does NOT include the target file
1412
+ fs.writeFileSync(glossaryPath, JSON.stringify([
1413
+ { name: 'Header', filePath: 'src/components/Header.tsx' },
1414
+ ]));
1415
+ const result = validateEntityLinkageForAppScenario({
1416
+ lookupFilePath: 'src/App.tsx',
1417
+ scenarioType: 'application',
1418
+ projectRoot: tmpDir,
1419
+ });
1420
+ expect(result.valid).toBe(false);
1421
+ expect(result.error).toContain('No glossary entry found');
1422
+ expect(result.error).toContain('src/App.tsx');
1423
+ });
1424
+ it('should return needsAnalysis when file IS in glossary but no entity exists', () => {
1425
+ // Write a glossary that includes the target file
1426
+ fs.writeFileSync(glossaryPath, JSON.stringify([
1427
+ { name: 'App', filePath: 'src/App.tsx', description: 'Main app' },
1428
+ ]));
1429
+ const result = validateEntityLinkageForAppScenario({
1430
+ lookupFilePath: 'src/App.tsx',
1431
+ scenarioType: 'application',
1432
+ projectRoot: tmpDir,
1433
+ });
1434
+ expect(result.valid).toBe(true);
1435
+ expect(result.needsAnalysis).toBe(true);
1436
+ });
1437
+ it('should skip validation for component-type scenarios', () => {
1438
+ // No glossary file exists at all
1439
+ const result = validateEntityLinkageForAppScenario({
1440
+ lookupFilePath: 'src/components/Header.tsx',
1441
+ scenarioType: 'component',
1442
+ projectRoot: tmpDir,
1443
+ });
1444
+ expect(result.valid).toBe(true);
1445
+ expect(result.needsAnalysis).toBeUndefined();
1446
+ });
1447
+ it('should skip validation when lookupFilePath is null', () => {
1448
+ const result = validateEntityLinkageForAppScenario({
1449
+ lookupFilePath: null,
1450
+ scenarioType: 'application',
1451
+ projectRoot: tmpDir,
1452
+ });
1453
+ expect(result.valid).toBe(true);
1454
+ expect(result.needsAnalysis).toBeUndefined();
1455
+ });
1456
+ it('should handle glossary wrapped in object format', () => {
1457
+ // LLMs sometimes write glossary as {"components": [...]}
1458
+ fs.writeFileSync(glossaryPath, JSON.stringify({
1459
+ components: [
1460
+ { name: 'App', filePath: 'src/App.tsx', description: 'Main app' },
1461
+ ],
1462
+ }));
1463
+ const result = validateEntityLinkageForAppScenario({
1464
+ lookupFilePath: 'src/App.tsx',
1465
+ scenarioType: 'user',
1466
+ projectRoot: tmpDir,
1467
+ });
1468
+ expect(result.valid).toBe(true);
1469
+ expect(result.needsAnalysis).toBe(true);
1470
+ });
1471
+ it('should return error when glossary file does not exist', () => {
1472
+ // Don't create a glossary file
1473
+ const result = validateEntityLinkageForAppScenario({
1474
+ lookupFilePath: 'src/App.tsx',
1475
+ scenarioType: 'application',
1476
+ projectRoot: tmpDir,
1477
+ });
1478
+ expect(result.valid).toBe(false);
1479
+ expect(result.error).toContain('No glossary entry found');
1480
+ });
1481
+ });
1058
1482
  });
1059
1483
  //# sourceMappingURL=editorScenarios.test.js.map