@codeyam/codeyam-cli 0.1.0-staging.8778565 → 0.1.0-staging.87dd4be

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 (310) 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 +82 -0
  8. package/analyzer-template/packages/database/src/lib/loadEntities.ts +0 -6
  9. package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +0 -65
  10. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +5 -0
  11. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
  12. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +84 -0
  13. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  14. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts.map +1 -1
  15. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js +0 -6
  16. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js.map +1 -1
  17. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts.map +1 -1
  18. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js +0 -25
  19. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js.map +1 -1
  20. package/codeyam-cli/src/cli.js +9 -0
  21. package/codeyam-cli/src/cli.js.map +1 -1
  22. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js +51 -0
  23. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js.map +1 -0
  24. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +56 -0
  25. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -0
  26. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +101 -47
  27. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
  28. package/codeyam-cli/src/commands/editor.js +2369 -346
  29. package/codeyam-cli/src/commands/editor.js.map +1 -1
  30. package/codeyam-cli/src/commands/editorIsolateArgs.js +25 -0
  31. package/codeyam-cli/src/commands/editorIsolateArgs.js.map +1 -0
  32. package/codeyam-cli/src/commands/init.js +69 -34
  33. package/codeyam-cli/src/commands/init.js.map +1 -1
  34. package/codeyam-cli/src/commands/telemetry.js +37 -0
  35. package/codeyam-cli/src/commands/telemetry.js.map +1 -0
  36. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js +173 -0
  37. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js.map +1 -0
  38. package/codeyam-cli/src/utils/__tests__/editorApi.test.js +18 -8
  39. package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
  40. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +2046 -1
  41. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  42. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js +76 -0
  43. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js.map +1 -0
  44. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js +137 -0
  45. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js.map +1 -0
  46. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js +100 -0
  47. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js.map +1 -0
  48. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +76 -3
  49. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
  50. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +381 -0
  51. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -0
  52. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +202 -1
  53. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
  54. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js +435 -0
  55. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js.map +1 -0
  56. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +88 -1
  57. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
  58. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +47 -1
  59. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
  60. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +70 -0
  61. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -1
  62. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +1335 -1
  63. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  64. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +67 -0
  65. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
  66. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js +143 -0
  67. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js.map +1 -0
  68. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js +66 -0
  69. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js.map +1 -0
  70. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js +53 -0
  71. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js.map +1 -0
  72. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +363 -11
  73. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  74. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js +177 -0
  75. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js.map +1 -0
  76. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js +30 -2
  77. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js.map +1 -1
  78. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js +118 -0
  79. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js.map +1 -0
  80. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +284 -0
  81. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -0
  82. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +649 -223
  83. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
  84. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +1 -0
  85. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  86. package/codeyam-cli/src/utils/__tests__/telemetry.test.js +159 -0
  87. package/codeyam-cli/src/utils/__tests__/telemetry.test.js.map +1 -0
  88. package/codeyam-cli/src/utils/analysisRunner.js +3 -1
  89. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  90. package/codeyam-cli/src/utils/analyzer.js +9 -0
  91. package/codeyam-cli/src/utils/analyzer.js.map +1 -1
  92. package/codeyam-cli/src/utils/analyzerFinalization.js +100 -0
  93. package/codeyam-cli/src/utils/analyzerFinalization.js.map +1 -0
  94. package/codeyam-cli/src/utils/backgroundServer.js +3 -9
  95. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  96. package/codeyam-cli/src/utils/database.js +37 -2
  97. package/codeyam-cli/src/utils/database.js.map +1 -1
  98. package/codeyam-cli/src/utils/editorApi.js +11 -5
  99. package/codeyam-cli/src/utils/editorApi.js.map +1 -1
  100. package/codeyam-cli/src/utils/editorAudit.js +372 -5
  101. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  102. package/codeyam-cli/src/utils/editorBroadcastViewport.js +26 -0
  103. package/codeyam-cli/src/utils/editorBroadcastViewport.js.map +1 -0
  104. package/codeyam-cli/src/utils/editorDeleteScenario.js +67 -0
  105. package/codeyam-cli/src/utils/editorDeleteScenario.js.map +1 -0
  106. package/codeyam-cli/src/utils/editorEntityChangeStatus.js +13 -7
  107. package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
  108. package/codeyam-cli/src/utils/editorEntityHelpers.js +144 -0
  109. package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -0
  110. package/codeyam-cli/src/utils/editorLoaderHelpers.js +72 -1
  111. package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
  112. package/codeyam-cli/src/utils/editorMigration.js +224 -0
  113. package/codeyam-cli/src/utils/editorMigration.js.map +1 -0
  114. package/codeyam-cli/src/utils/editorPreview.js +31 -0
  115. package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
  116. package/codeyam-cli/src/utils/editorRecapture.js +109 -0
  117. package/codeyam-cli/src/utils/editorRecapture.js.map +1 -0
  118. package/codeyam-cli/src/utils/editorScenarioSwitch.js +24 -2
  119. package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -1
  120. package/codeyam-cli/src/utils/editorScenarios.js +458 -0
  121. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  122. package/codeyam-cli/src/utils/editorSeedAdapter.js +253 -4
  123. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
  124. package/codeyam-cli/src/utils/editorShouldRevalidate.js +21 -0
  125. package/codeyam-cli/src/utils/editorShouldRevalidate.js.map +1 -0
  126. package/codeyam-cli/src/utils/entityChangeStatus.js +53 -6
  127. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  128. package/codeyam-cli/src/utils/entityChangeStatus.server.js +41 -3
  129. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  130. package/codeyam-cli/src/utils/fileWatcher.js +38 -0
  131. package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
  132. package/codeyam-cli/src/utils/glossaryAdd.js +74 -0
  133. package/codeyam-cli/src/utils/glossaryAdd.js.map +1 -0
  134. package/codeyam-cli/src/utils/install-skills.js +14 -0
  135. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  136. package/codeyam-cli/src/utils/parseRegisterArg.js.map +1 -1
  137. package/codeyam-cli/src/utils/progress.js +2 -2
  138. package/codeyam-cli/src/utils/progress.js.map +1 -1
  139. package/codeyam-cli/src/utils/routePatternMatching.js +129 -0
  140. package/codeyam-cli/src/utils/routePatternMatching.js.map +1 -0
  141. package/codeyam-cli/src/utils/scenarioCoverage.js +77 -0
  142. package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -0
  143. package/codeyam-cli/src/utils/scenariosManifest.js +269 -74
  144. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  145. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +1 -0
  146. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  147. package/codeyam-cli/src/utils/simulationGateMiddleware.js +8 -1
  148. package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
  149. package/codeyam-cli/src/utils/slugUtils.js +25 -0
  150. package/codeyam-cli/src/utils/slugUtils.js.map +1 -0
  151. package/codeyam-cli/src/utils/syncMocksMiddleware.js +2 -2
  152. package/codeyam-cli/src/utils/syncMocksMiddleware.js.map +1 -1
  153. package/codeyam-cli/src/utils/telemetry.js +106 -0
  154. package/codeyam-cli/src/utils/telemetry.js.map +1 -0
  155. package/codeyam-cli/src/utils/telemetryMiddleware.js +22 -0
  156. package/codeyam-cli/src/utils/telemetryMiddleware.js.map +1 -0
  157. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +35 -0
  158. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -0
  159. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +80 -0
  160. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -0
  161. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +218 -0
  162. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  163. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +217 -0
  164. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -0
  165. package/codeyam-cli/src/webserver/app/lib/clientErrors.js +71 -0
  166. package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -0
  167. package/codeyam-cli/src/webserver/app/lib/git.js +3 -2
  168. package/codeyam-cli/src/webserver/app/lib/git.js.map +1 -1
  169. package/codeyam-cli/src/webserver/app/types/editor.js +8 -0
  170. package/codeyam-cli/src/webserver/app/types/editor.js.map +1 -0
  171. package/codeyam-cli/src/webserver/backgroundServer.js +60 -61
  172. package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
  173. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-CLe80MMu.js +1 -0
  174. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BcgbViKV.js → EntityItem-Crt_KN_U.js} +3 -3
  175. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-CQgyEGV-.js +1 -0
  176. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CQIG2qda.js → EntityTypeIcon-CD7lGABo.js} +1 -1
  177. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-CgTNOhnu.js +1 -0
  178. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-CKeQT5Ty.js +25 -0
  179. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-D3s1MFkb.js +3 -0
  180. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-BU_OAEMP.js → LoadingDots-By5zI316.js} +1 -1
  181. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-ceAyBX-H.js → LogViewer-CM5zg40N.js} +3 -3
  182. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-BzHcG7SE.js → ReportIssueModal-C2PLkej3.js} +2 -2
  183. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-DanvyBPb.js +1 -0
  184. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-0DY_NKil.js → ScenarioViewer-DUMfcNVK.js} +3 -3
  185. package/codeyam-cli/src/webserver/build/client/assets/Spinner-D0LgAaSa.js +34 -0
  186. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-CK7-NaPZ.js +1 -0
  187. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-BA_Ry-rs.js +1 -0
  188. package/codeyam-cli/src/webserver/build/client/assets/{_index-DLxKhri3.js → _index-BAWd-Xjf.js} +2 -2
  189. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BcY3q6nt.js → activity.(_tab)-BOARiB-g.js} +3 -3
  190. package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-Duc5hnl7.js → addon-web-links-CHx25PAe.js} +1 -1
  191. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bni3iiUj.js → agent-transcripts-Bg3e7q4S.js} +3 -3
  192. package/codeyam-cli/src/webserver/build/client/assets/api.editor-recapture-stale-l0sNRNKZ.js +1 -0
  193. package/codeyam-cli/src/webserver/build/client/assets/api.editor-rename-scenario-l0sNRNKZ.js +1 -0
  194. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-seed-state-l0sNRNKZ.js +1 -0
  195. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-coverage-l0sNRNKZ.js +1 -0
  196. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-prompt-l0sNRNKZ.js +1 -0
  197. package/codeyam-cli/src/webserver/build/client/assets/api.editor-session-l0sNRNKZ.js +1 -0
  198. package/codeyam-cli/src/webserver/build/client/assets/{book-open-BYOypzCa.js → book-open-CL-lMgHh.js} +1 -1
  199. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-C_Pmso5S.js → chevron-down-GmAjGS9-.js} +1 -1
  200. package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-BAdwhyCx.js +43 -0
  201. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-BVMi9VA5.js → circle-check-DFcQkN5j.js} +1 -1
  202. package/codeyam-cli/src/webserver/build/client/assets/{copy-n2FB0_Sw.js → copy-C6iF61Xs.js} +1 -1
  203. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-CC6AbExI.js → createLucideIcon-4ImjHTVC.js} +1 -1
  204. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-C8y4mmyv.js +1 -0
  205. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-Gbk_i5Js.js +1 -0
  206. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-Bnx7yUP0.js +58 -0
  207. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-oepecPae.js +41 -0
  208. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BF4oLwaE.js → entity._sha._-Blfy9UlN.js} +2 -2
  209. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-KTQuL0aj.js +6 -0
  210. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-C6eeL24i.js +6 -0
  211. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-DQM8E7L4.js +6 -0
  212. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMvVHNXU.js → entity._sha_.edit._scenarioId-CAoXLsQr.js} +2 -2
  213. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-DTvKq3TY.js → entry.client-SuW9syRS.js} +6 -6
  214. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-Daa96Fr1.js +1 -0
  215. package/codeyam-cli/src/webserver/build/client/assets/files-D-xGrg29.js +1 -0
  216. package/codeyam-cli/src/webserver/build/client/assets/git-Bq_fbXP5.js +1 -0
  217. package/codeyam-cli/src/webserver/build/client/assets/globals-fAqOD9ex.css +1 -0
  218. package/codeyam-cli/src/webserver/build/client/assets/{index-BcvgDzbZ.js → index-Bp1l4hSv.js} +1 -1
  219. package/codeyam-cli/src/webserver/build/client/assets/{index-10oVnAAH.js → index-CWV9XZiG.js} +1 -1
  220. package/codeyam-cli/src/webserver/build/client/assets/{index-yHOVb4rc.js → index-DE3jI_dv.js} +1 -1
  221. package/codeyam-cli/src/webserver/build/client/assets/jsx-runtime-D_zvdyIk.js +9 -0
  222. package/codeyam-cli/src/webserver/build/client/assets/labs-B_IX45ih.js +1 -0
  223. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DaAZ_H2w.js → loader-circle-De-7qQ2u.js} +1 -1
  224. package/codeyam-cli/src/webserver/build/client/assets/manifest-b9d4d267.js +1 -0
  225. package/codeyam-cli/src/webserver/build/client/assets/memory-Cx2xEx7s.js +101 -0
  226. package/codeyam-cli/src/webserver/build/client/assets/{pause-f5-1lKBt.js → pause-CFxEKL1u.js} +1 -1
  227. package/codeyam-cli/src/webserver/build/client/assets/root-DB3O9_9j.js +67 -0
  228. package/codeyam-cli/src/webserver/build/client/assets/{search-Di64LWVb.js → search-BdBb5aqc.js} +1 -1
  229. package/codeyam-cli/src/webserver/build/client/assets/settings-DdE-Untf.js +1 -0
  230. package/codeyam-cli/src/webserver/build/client/assets/simulations-DSCdE99u.js +1 -0
  231. package/codeyam-cli/src/webserver/build/client/assets/{terminal-Br7MOqts.js → terminal-CrplD4b1.js} +1 -1
  232. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BLdiCuG-.js → triangle-alert-DqJ0j69l.js} +1 -1
  233. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-DhXHbEjP.js +1 -0
  234. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-C14nCb1q.js → useLastLogLine-BNd5hYuW.js} +1 -1
  235. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-Cy5Qg_UR.js +1 -0
  236. package/codeyam-cli/src/webserver/build/client/assets/useToast-5HR2j9ZE.js +1 -0
  237. package/codeyam-cli/src/webserver/build/client/sound-test.html +98 -0
  238. package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-CGwTN3V2.js +13 -0
  239. package/codeyam-cli/src/webserver/build/server/assets/index-D4meMKy3.js +1 -0
  240. package/codeyam-cli/src/webserver/build/server/assets/init-odGJ_c2-.js +10 -0
  241. package/codeyam-cli/src/webserver/build/server/assets/progress-CHTtrxFG.js +1 -0
  242. package/codeyam-cli/src/webserver/build/server/assets/server-build-TmPfF7pT.js +552 -0
  243. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  244. package/codeyam-cli/src/webserver/build-info.json +5 -5
  245. package/codeyam-cli/src/webserver/editorProxy.js +208 -17
  246. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  247. package/codeyam-cli/src/webserver/idleDetector.js +106 -0
  248. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -0
  249. package/codeyam-cli/src/webserver/mockStateEvents.js +28 -0
  250. package/codeyam-cli/src/webserver/mockStateEvents.js.map +1 -0
  251. package/codeyam-cli/src/webserver/public/sound-test.html +98 -0
  252. package/codeyam-cli/src/webserver/scripts/journalCapture.ts +36 -0
  253. package/codeyam-cli/src/webserver/server.js +87 -4
  254. package/codeyam-cli/src/webserver/server.js.map +1 -1
  255. package/codeyam-cli/src/webserver/terminalServer.js +140 -35
  256. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  257. package/codeyam-cli/templates/chrome-extension-react/README.md +46 -0
  258. package/codeyam-cli/templates/chrome-extension-react/package.json +1 -0
  259. package/codeyam-cli/templates/codeyam-editor-claude.md +84 -5
  260. package/codeyam-cli/templates/codeyam-editor-reference.md +214 -0
  261. package/codeyam-cli/templates/editor-step-hook.py +114 -24
  262. package/codeyam-cli/templates/expo-react-native/README.md +41 -0
  263. package/codeyam-cli/templates/expo-react-native/package.json +1 -0
  264. package/codeyam-cli/templates/nextjs-prisma-sqlite/DATABASE.md +14 -0
  265. package/codeyam-cli/templates/nextjs-prisma-sqlite/README.md +53 -0
  266. package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +2 -1
  267. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +42 -7
  268. package/codeyam-cli/templates/nextjs-prisma-supabase/README.md +52 -0
  269. package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +2 -1
  270. package/codeyam-cli/templates/seed-adapters/supabase.ts +282 -0
  271. package/codeyam-cli/templates/skills/codeyam-dev-mode/SKILL.md +1 -1
  272. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +76 -10
  273. package/package.json +2 -1
  274. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +84 -0
  275. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  276. package/packages/database/src/lib/loadEntities.js +0 -6
  277. package/packages/database/src/lib/loadEntities.js.map +1 -1
  278. package/packages/database/src/lib/updateCommitMetadata.js +0 -25
  279. package/packages/database/src/lib/updateCommitMetadata.js.map +1 -1
  280. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-BPXZwM4t.js +0 -1
  281. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-g3saevPb.js +0 -1
  282. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-Bu6c6aDe.js +0 -1
  283. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-DYFW3lDD.js +0 -25
  284. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DLeucoVX.js +0 -3
  285. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-BED4B6sP.js +0 -1
  286. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bb5uFQ5V.js +0 -34
  287. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-C8OKAR5x.js +0 -1
  288. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +0 -1
  289. package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-C4pqxYJB.js +0 -51
  290. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Csi0_PMl.js +0 -1
  291. package/codeyam-cli/src/webserver/build/client/assets/editor-DgN1LTTt.js +0 -10
  292. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-BLQMSKZa.js +0 -41
  293. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-C7YX6r3H.js +0 -6
  294. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CF164ouH.js +0 -6
  295. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-p9hhkjJM.js +0 -6
  296. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-cPo8LiG3.js +0 -1
  297. package/codeyam-cli/src/webserver/build/client/assets/files-BZrlFE1F.js +0 -1
  298. package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +0 -1
  299. package/codeyam-cli/src/webserver/build/client/assets/globals-BkWJ_UNc.css +0 -1
  300. package/codeyam-cli/src/webserver/build/client/assets/labs-Zk7ryIM1.js +0 -1
  301. package/codeyam-cli/src/webserver/build/client/assets/manifest-c26eb85b.js +0 -1
  302. package/codeyam-cli/src/webserver/build/client/assets/memory-Bl2rpw8u.js +0 -96
  303. package/codeyam-cli/src/webserver/build/client/assets/root-ClvYBUSA.js +0 -67
  304. package/codeyam-cli/src/webserver/build/client/assets/settings-0OrEMU6J.js +0 -1
  305. package/codeyam-cli/src/webserver/build/client/assets/simulations-DWT-CvLy.js +0 -1
  306. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-CrAK28Bc.js +0 -1
  307. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-O-jkvSPx.js +0 -1
  308. package/codeyam-cli/src/webserver/build/client/assets/useToast-9FIWuYfK.js +0 -1
  309. package/codeyam-cli/src/webserver/build/server/assets/index-DflIr5SD.js +0 -1
  310. package/codeyam-cli/src/webserver/build/server/assets/server-build-OhKy839M.js +0 -416
@@ -1,246 +1,672 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import * as os from 'os';
4
- import { readManifest, writeManifest, addScenarioToManifest, removeScenarioFromManifest, buildManifestFromRows, syncManifestToDatabase, } from "../scenariosManifest.js";
4
+ import { scanScenarioFiles, writeScenarioMetadata, writeScenarioDataFile, deleteScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, migrateScenarioFormats, } from "../scenariosManifest.js";
5
5
  function makeTmpDir() {
6
6
  return fs.mkdtempSync(path.join(os.tmpdir(), 'manifest-test-'));
7
7
  }
8
- function makeEntry(overrides = {}) {
8
+ function makeMetadata(overrides = {}) {
9
9
  return {
10
- id: 'test-id-1',
11
10
  name: 'Test Scenario',
12
11
  description: null,
13
12
  componentName: null,
14
13
  componentPath: null,
15
14
  url: '/',
16
- mockDataFile: 'editor-scenarios/test-id-1.json',
17
- screenshotFile: null,
15
+ type: 'application',
16
+ screenshotPath: null,
17
+ viewportWidth: null,
18
+ viewportHeight: null,
18
19
  createdAt: '2024-01-01T00:00:00.000Z',
19
20
  updatedAt: '2024-01-01T00:00:00.000Z',
20
21
  ...overrides,
21
22
  };
22
23
  }
23
- describe('scenariosManifest', () => {
24
- describe('readManifest / writeManifest round-trip', () => {
25
- it('should write and read back a manifest', () => {
26
- const tmp = makeTmpDir();
27
- const manifest = {
28
- version: 1,
29
- updatedAt: '2024-01-01T00:00:00.000Z',
30
- scenarios: [makeEntry()],
31
- };
32
- writeManifest(tmp, manifest);
33
- const result = readManifest(tmp);
34
- expect(result).toEqual(manifest);
35
- });
36
- it('should return null for missing file', () => {
37
- const tmp = makeTmpDir();
38
- expect(readManifest(tmp)).toBeNull();
39
- });
40
- });
41
- describe('addScenarioToManifest', () => {
42
- it('should create manifest if none exists', () => {
43
- const tmp = makeTmpDir();
44
- const entry = makeEntry();
45
- addScenarioToManifest(tmp, entry);
46
- const manifest = readManifest(tmp);
47
- expect(manifest).not.toBeNull();
48
- expect(manifest.scenarios).toHaveLength(1);
49
- expect(manifest.scenarios[0].id).toBe('test-id-1');
50
- });
51
- it('should append to existing manifest', () => {
52
- const tmp = makeTmpDir();
53
- addScenarioToManifest(tmp, makeEntry({ id: 'a', name: 'A' }));
54
- addScenarioToManifest(tmp, makeEntry({ id: 'b', name: 'B' }));
55
- const manifest = readManifest(tmp);
56
- expect(manifest.scenarios).toHaveLength(2);
57
- });
58
- it('should deduplicate by id (update existing)', () => {
59
- const tmp = makeTmpDir();
60
- addScenarioToManifest(tmp, makeEntry({ id: 'a', name: 'Original' }));
61
- addScenarioToManifest(tmp, makeEntry({ id: 'a', name: 'Updated' }));
62
- const manifest = readManifest(tmp);
63
- expect(manifest.scenarios).toHaveLength(1);
64
- expect(manifest.scenarios[0].name).toBe('Updated');
65
- });
66
- });
67
- describe('removeScenarioFromManifest', () => {
68
- it('should remove a scenario by id', () => {
69
- const tmp = makeTmpDir();
70
- addScenarioToManifest(tmp, makeEntry({ id: 'a', name: 'A' }));
71
- addScenarioToManifest(tmp, makeEntry({ id: 'b', name: 'B' }));
72
- removeScenarioFromManifest(tmp, 'a');
73
- const manifest = readManifest(tmp);
74
- expect(manifest.scenarios).toHaveLength(1);
75
- expect(manifest.scenarios[0].id).toBe('b');
76
- });
77
- it('should handle removing from nonexistent manifest', () => {
78
- const tmp = makeTmpDir();
79
- // Should not throw
80
- removeScenarioFromManifest(tmp, 'nonexistent');
81
- });
82
- it('should handle removing nonexistent id', () => {
83
- const tmp = makeTmpDir();
84
- addScenarioToManifest(tmp, makeEntry({ id: 'a', name: 'A' }));
85
- removeScenarioFromManifest(tmp, 'nonexistent');
86
- const manifest = readManifest(tmp);
87
- expect(manifest.scenarios).toHaveLength(1);
88
- });
89
- });
90
- describe('buildManifestFromRows', () => {
91
- it('should build manifest from database-like rows', () => {
92
- const rows = [
93
- {
94
- id: 'row-1',
95
- name: 'Scenario 1',
96
- description: 'A scenario',
97
- component_name: 'Button',
98
- component_path: 'app/components/Button.tsx',
99
- url: '/isolated-components/Button?s=Default',
100
- screenshot_path: 'screenshots/row-1.png',
101
- created_at: '2024-01-01 00:00:00',
102
- updated_at: '2024-01-02 00:00:00',
103
- },
104
- {
105
- id: 'row-2',
106
- name: 'Scenario 2',
107
- description: null,
108
- component_name: null,
109
- component_path: null,
110
- url: '/',
111
- screenshot_path: null,
112
- created_at: '2024-01-03 00:00:00',
113
- updated_at: '2024-01-03 00:00:00',
114
- },
115
- ];
116
- const manifest = buildManifestFromRows(rows);
117
- expect(manifest.version).toBe(1);
118
- expect(manifest.scenarios).toHaveLength(2);
119
- expect(manifest.scenarios[0]).toEqual({
120
- id: 'row-1',
121
- name: 'Scenario 1',
122
- description: 'A scenario',
123
- componentName: 'Button',
124
- componentPath: 'app/components/Button.tsx',
125
- url: '/isolated-components/Button?s=Default',
126
- mockDataFile: 'editor-scenarios/row-1.json',
127
- screenshotFile: 'editor-scenarios/screenshots/row-1.png',
128
- createdAt: '2024-01-01 00:00:00',
129
- updatedAt: '2024-01-02 00:00:00',
130
- });
131
- expect(manifest.scenarios[1].screenshotFile).toBeNull();
132
- });
133
- it('should handle empty rows', () => {
134
- const manifest = buildManifestFromRows([]);
135
- expect(manifest.scenarios).toEqual([]);
136
- });
137
- });
138
- describe('syncManifestToDatabase', () => {
139
- it('should insert missing scenarios', async () => {
140
- const tmp = makeTmpDir();
141
- const entry = makeEntry({ id: 'new-1', name: 'New Scenario' });
142
- addScenarioToManifest(tmp, entry);
143
- const existingRows = [];
144
- const insertedRows = [];
145
- const updatedRows = [];
146
- const result = await syncManifestToDatabase(tmp, 'project-1', existingRows, (row) => {
147
- insertedRows.push(row);
148
- }, (id, row) => {
149
- updatedRows.push({ id, ...row });
150
- });
151
- expect(result.inserted).toBe(1);
152
- expect(result.updated).toBe(0);
153
- expect(result.skipped).toBe(0);
154
- expect(insertedRows).toHaveLength(1);
155
- expect(insertedRows[0].id).toBe('new-1');
156
- expect(insertedRows[0].project_id).toBe('project-1');
157
- });
158
- it('should skip scenarios already in DB with same updatedAt', async () => {
159
- const tmp = makeTmpDir();
160
- const entry = makeEntry({
161
- id: 'existing-1',
162
- name: 'Existing',
163
- updatedAt: '2024-01-01T00:00:00.000Z',
164
- });
165
- addScenarioToManifest(tmp, entry);
166
- const existingRows = [
167
- { id: 'existing-1', updated_at: '2024-01-01T00:00:00.000Z' },
168
- ];
169
- const insertedRows = [];
170
- const updatedRows = [];
171
- const result = await syncManifestToDatabase(tmp, 'project-1', existingRows, (row) => {
172
- insertedRows.push(row);
173
- }, (id, row) => {
174
- updatedRows.push({ id, ...row });
175
- });
176
- expect(result.inserted).toBe(0);
177
- expect(result.updated).toBe(0);
178
- expect(result.skipped).toBe(1);
179
- });
180
- it('should update scenarios newer than DB row', async () => {
181
- const tmp = makeTmpDir();
182
- const entry = makeEntry({
183
- id: 'existing-1',
184
- name: 'Updated Name',
185
- updatedAt: '2024-02-01T00:00:00.000Z',
186
- });
187
- addScenarioToManifest(tmp, entry);
188
- const existingRows = [
189
- { id: 'existing-1', updated_at: '2024-01-01T00:00:00.000Z' },
190
- ];
191
- const insertedRows = [];
192
- const updatedRows = [];
193
- const result = await syncManifestToDatabase(tmp, 'project-1', existingRows, (row) => {
194
- insertedRows.push(row);
195
- }, (id, row) => {
196
- updatedRows.push({ id, ...row });
197
- });
198
- expect(result.inserted).toBe(0);
199
- expect(result.updated).toBe(1);
200
- expect(result.skipped).toBe(0);
201
- expect(updatedRows[0].name).toBe('Updated Name');
202
- });
203
- it('should handle empty manifest', async () => {
204
- const tmp = makeTmpDir();
205
- writeManifest(tmp, { version: 1, updatedAt: '', scenarios: [] });
206
- const result = await syncManifestToDatabase(tmp, 'project-1', [], () => { }, () => { });
207
- expect(result.inserted).toBe(0);
208
- expect(result.updated).toBe(0);
209
- expect(result.skipped).toBe(0);
210
- });
211
- it('should handle no manifest file', async () => {
212
- const tmp = makeTmpDir();
213
- const result = await syncManifestToDatabase(tmp, 'project-1', [], () => { }, () => { });
214
- expect(result.inserted).toBe(0);
215
- expect(result.updated).toBe(0);
216
- expect(result.skipped).toBe(0);
217
- });
218
- it('should handle mixed insert/update/skip', async () => {
219
- const tmp = makeTmpDir();
220
- addScenarioToManifest(tmp, makeEntry({
221
- id: 'new-1',
222
- name: 'New',
223
- updatedAt: '2024-01-01T00:00:00.000Z',
224
- }));
225
- addScenarioToManifest(tmp, makeEntry({
226
- id: 'updated-1',
24
+ function writeScenarioFile(tmp, id, data, metadata) {
25
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
26
+ fs.mkdirSync(dir, { recursive: true });
27
+ const content = metadata ? { _metadata: metadata, ...data } : data;
28
+ fs.writeFileSync(path.join(dir, `${id}.json`), JSON.stringify(content, null, 2));
29
+ }
30
+ // ─── scanScenarioFiles ────────────────────────────────────────────
31
+ describe('scanScenarioFiles', () => {
32
+ it('should find scenario files with _metadata', () => {
33
+ const tmp = makeTmpDir();
34
+ writeScenarioFile(tmp, 'abc-123', { seed: {} }, makeMetadata({ name: 'Home Page' }));
35
+ writeScenarioFile(tmp, 'def-456', { seed: {} }, makeMetadata({ name: 'About Page' }));
36
+ const results = scanScenarioFiles(tmp);
37
+ expect(results).toHaveLength(2);
38
+ const names = results.map((r) => r.metadata.name).sort();
39
+ expect(names).toEqual(['About Page', 'Home Page']);
40
+ });
41
+ it('should skip files without _metadata', () => {
42
+ const tmp = makeTmpDir();
43
+ writeScenarioFile(tmp, 'with-meta', { seed: {} }, makeMetadata());
44
+ writeScenarioFile(tmp, 'no-meta', { seed: {} }); // no metadata
45
+ const results = scanScenarioFiles(tmp);
46
+ expect(results).toHaveLength(1);
47
+ expect(results[0].id).toBe('with-meta');
48
+ });
49
+ it('should skip .seed.json files', () => {
50
+ const tmp = makeTmpDir();
51
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
52
+ fs.mkdirSync(dir, { recursive: true });
53
+ writeScenarioFile(tmp, 'abc', { seed: {} }, makeMetadata());
54
+ // Write a .seed.json file that has _metadata (shouldn't happen but be safe)
55
+ fs.writeFileSync(path.join(dir, 'abc.seed.json'), JSON.stringify({ _metadata: makeMetadata() }));
56
+ const results = scanScenarioFiles(tmp);
57
+ expect(results).toHaveLength(1);
58
+ expect(results[0].id).toBe('abc');
59
+ });
60
+ it('should skip client-errors.json', () => {
61
+ const tmp = makeTmpDir();
62
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
63
+ fs.mkdirSync(dir, { recursive: true });
64
+ writeScenarioFile(tmp, 'abc', { seed: {} }, makeMetadata());
65
+ fs.writeFileSync(path.join(dir, 'client-errors.json'), JSON.stringify({ _metadata: makeMetadata() }));
66
+ const results = scanScenarioFiles(tmp);
67
+ expect(results).toHaveLength(1);
68
+ });
69
+ it('should return empty array for missing directory', () => {
70
+ const tmp = makeTmpDir();
71
+ expect(scanScenarioFiles(tmp)).toEqual([]);
72
+ });
73
+ it('should extract id from filename', () => {
74
+ const tmp = makeTmpDir();
75
+ writeScenarioFile(tmp, 'my-uuid-here', { seed: {} }, makeMetadata());
76
+ const results = scanScenarioFiles(tmp);
77
+ expect(results[0].id).toBe('my-uuid-here');
78
+ });
79
+ });
80
+ // ─── writeScenarioMetadata ────────────────────────────────────────
81
+ describe('writeScenarioMetadata', () => {
82
+ it('should add _metadata to existing scenario file', () => {
83
+ const tmp = makeTmpDir();
84
+ writeScenarioFile(tmp, 'abc', { type: 'application', seed: { users: [] } });
85
+ writeScenarioMetadata(tmp, 'abc', makeMetadata({ name: 'Updated' }));
86
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'abc.json');
87
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
88
+ expect(content._metadata.name).toBe('Updated');
89
+ expect(content.type).toBe('application'); // original data preserved
90
+ expect(content.seed).toEqual({ users: [] }); // original data preserved
91
+ });
92
+ it('should update existing _metadata', () => {
93
+ const tmp = makeTmpDir();
94
+ writeScenarioFile(tmp, 'abc', { seed: {} }, makeMetadata({ name: 'Old' }));
95
+ writeScenarioMetadata(tmp, 'abc', makeMetadata({ name: 'New' }));
96
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'abc.json');
97
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
98
+ expect(content._metadata.name).toBe('New');
99
+ });
100
+ it('should not create file if it does not exist', () => {
101
+ const tmp = makeTmpDir();
102
+ writeScenarioMetadata(tmp, 'nonexistent', makeMetadata());
103
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'nonexistent.json');
104
+ expect(fs.existsSync(filePath)).toBe(false);
105
+ });
106
+ });
107
+ // ─── deleteScenarioFiles ──────────────────────────────────────────
108
+ describe('deleteScenarioFiles', () => {
109
+ it('should delete both .json and .seed.json', () => {
110
+ const tmp = makeTmpDir();
111
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
112
+ fs.mkdirSync(dir, { recursive: true });
113
+ fs.writeFileSync(path.join(dir, 'abc.json'), '{}');
114
+ fs.writeFileSync(path.join(dir, 'abc.seed.json'), '{}');
115
+ deleteScenarioFiles(tmp, 'abc');
116
+ expect(fs.existsSync(path.join(dir, 'abc.json'))).toBe(false);
117
+ expect(fs.existsSync(path.join(dir, 'abc.seed.json'))).toBe(false);
118
+ });
119
+ it('should not throw for missing files', () => {
120
+ const tmp = makeTmpDir();
121
+ expect(() => deleteScenarioFiles(tmp, 'nonexistent')).not.toThrow();
122
+ });
123
+ });
124
+ // ─── backfillScenarioMetadata ─────────────────────────────────────
125
+ describe('backfillScenarioMetadata', () => {
126
+ it('should add _metadata to files that lack it', () => {
127
+ const tmp = makeTmpDir();
128
+ // Write a file WITHOUT _metadata (legacy)
129
+ writeScenarioFile(tmp, 'abc-123', {
130
+ type: 'application',
131
+ seed: { users: [] },
132
+ });
133
+ const updated = backfillScenarioMetadata(tmp, [
134
+ {
135
+ id: 'abc-123',
136
+ name: 'Home Page',
137
+ description: 'Default view',
138
+ component_name: null,
139
+ component_path: null,
140
+ url: '/',
141
+ type: 'application',
142
+ created_at: '2024-01-01T00:00:00.000Z',
143
+ updated_at: '2024-01-02T00:00:00.000Z',
144
+ },
145
+ ]);
146
+ expect(updated).toBe(1);
147
+ // Verify _metadata was written
148
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'abc-123.json');
149
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
150
+ expect(content._metadata.name).toBe('Home Page');
151
+ expect(content._metadata.url).toBe('/');
152
+ // Original data preserved
153
+ expect(content.type).toBe('application');
154
+ expect(content.seed).toEqual({ users: [] });
155
+ });
156
+ it('should skip files that already have _metadata', () => {
157
+ const tmp = makeTmpDir();
158
+ writeScenarioFile(tmp, 'abc-123', { seed: {} }, makeMetadata({ name: 'Existing' }));
159
+ const updated = backfillScenarioMetadata(tmp, [
160
+ {
161
+ id: 'abc-123',
162
+ name: 'From DB',
163
+ description: null,
164
+ component_name: null,
165
+ component_path: null,
166
+ url: '/',
167
+ type: 'application',
168
+ created_at: '2024-01-01T00:00:00.000Z',
169
+ updated_at: '2024-01-01T00:00:00.000Z',
170
+ },
171
+ ]);
172
+ expect(updated).toBe(0);
173
+ // Verify original _metadata preserved
174
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'abc-123.json');
175
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
176
+ expect(content._metadata.name).toBe('Existing');
177
+ });
178
+ it('should skip DB rows with no matching file', () => {
179
+ const tmp = makeTmpDir();
180
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
181
+ fs.mkdirSync(dir, { recursive: true });
182
+ const updated = backfillScenarioMetadata(tmp, [
183
+ {
184
+ id: 'nonexistent',
185
+ name: 'Ghost',
186
+ description: null,
187
+ component_name: null,
188
+ component_path: null,
189
+ url: '/',
190
+ type: null,
191
+ created_at: '2024-01-01T00:00:00.000Z',
192
+ updated_at: '2024-01-01T00:00:00.000Z',
193
+ },
194
+ ]);
195
+ expect(updated).toBe(0);
196
+ });
197
+ it('should preserve dimensions, screenshotPaths, and pageFilePath from DB rows', () => {
198
+ const tmp = makeTmpDir();
199
+ writeScenarioFile(tmp, 'dim-1', {
200
+ type: 'application',
201
+ seed: { items: [] },
202
+ });
203
+ const updated = backfillScenarioMetadata(tmp, [
204
+ {
205
+ id: 'dim-1',
206
+ name: 'Multi-Dimension',
207
+ description: null,
208
+ component_name: 'Dashboard',
209
+ component_path: 'app/components/Dashboard.tsx',
210
+ url: '/',
211
+ type: 'application',
212
+ screenshot_path: 'screenshots/dim-1.png',
213
+ viewport_width: 1440,
214
+ viewport_height: 900,
215
+ dimensions: JSON.stringify(['Desktop', 'Mobile']),
216
+ screenshot_paths: JSON.stringify({
217
+ Desktop: 'screenshots/dim-1.png',
218
+ Mobile: 'screenshots/dim-1-mobile.png',
219
+ }),
220
+ page_file_path: 'app/page.tsx',
221
+ created_at: '2024-01-01T00:00:00.000Z',
222
+ updated_at: '2024-01-02T00:00:00.000Z',
223
+ },
224
+ ]);
225
+ expect(updated).toBe(1);
226
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'dim-1.json');
227
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
228
+ expect(content._metadata.dimensions).toEqual(['Desktop', 'Mobile']);
229
+ expect(content._metadata.screenshotPaths).toEqual({
230
+ Desktop: 'screenshots/dim-1.png',
231
+ Mobile: 'screenshots/dim-1-mobile.png',
232
+ });
233
+ expect(content._metadata.pageFilePath).toBe('app/page.tsx');
234
+ });
235
+ it('should handle mix of files with and without _metadata', () => {
236
+ const tmp = makeTmpDir();
237
+ writeScenarioFile(tmp, 'has-meta', { seed: {} }, makeMetadata());
238
+ writeScenarioFile(tmp, 'no-meta', { type: 'application', seed: {} });
239
+ const updated = backfillScenarioMetadata(tmp, [
240
+ {
241
+ id: 'has-meta',
242
+ name: 'A',
243
+ description: null,
244
+ component_name: null,
245
+ component_path: null,
246
+ url: '/',
247
+ type: null,
248
+ created_at: '2024-01-01T00:00:00.000Z',
249
+ updated_at: '2024-01-01T00:00:00.000Z',
250
+ },
251
+ {
252
+ id: 'no-meta',
253
+ name: 'B',
254
+ description: null,
255
+ component_name: null,
256
+ component_path: null,
257
+ url: '/about',
258
+ type: 'application',
259
+ created_at: '2024-01-01T00:00:00.000Z',
260
+ updated_at: '2024-01-01T00:00:00.000Z',
261
+ },
262
+ ]);
263
+ expect(updated).toBe(1); // Only no-meta was updated
264
+ });
265
+ });
266
+ // ─── syncScenarioFilesToDatabase ──────────────────────────────────
267
+ describe('syncScenarioFilesToDatabase', () => {
268
+ it('should insert scenarios from _metadata files', async () => {
269
+ const tmp = makeTmpDir();
270
+ writeScenarioFile(tmp, 'new-1', { seed: {} }, makeMetadata({ name: 'New Scenario' }));
271
+ const insertedRows = [];
272
+ const result = await syncScenarioFilesToDatabase(tmp, 'project-1', [], (row) => {
273
+ insertedRows.push(row);
274
+ }, () => { });
275
+ expect(result.inserted).toBe(1);
276
+ expect(insertedRows[0].id).toBe('new-1');
277
+ expect(insertedRows[0].name).toBe('New Scenario');
278
+ expect(insertedRows[0].project_id).toBe('project-1');
279
+ expect(insertedRows[0].type).toBe('application');
280
+ });
281
+ it('should sync type, screenshot_path, and viewport from _metadata', async () => {
282
+ const tmp = makeTmpDir();
283
+ writeScenarioFile(tmp, 'full-1', { seed: {} }, makeMetadata({
284
+ name: 'Full Scenario',
285
+ type: 'user',
286
+ screenshotPath: 'screenshots/full-1.png',
287
+ viewportWidth: 1280,
288
+ viewportHeight: 720,
289
+ }));
290
+ const insertedRows = [];
291
+ await syncScenarioFilesToDatabase(tmp, 'project-1', [], (row) => {
292
+ insertedRows.push(row);
293
+ }, () => { });
294
+ expect(insertedRows[0].type).toBe('user');
295
+ expect(insertedRows[0].screenshot_path).toBe('screenshots/full-1.png');
296
+ expect(insertedRows[0].viewport_width).toBe(1280);
297
+ expect(insertedRows[0].viewport_height).toBe(720);
298
+ });
299
+ it('should skip scenarios already in DB with same updatedAt', async () => {
300
+ const tmp = makeTmpDir();
301
+ writeScenarioFile(tmp, 'existing-1', { seed: {} }, makeMetadata({ updatedAt: '2024-01-01T00:00:00.000Z' }));
302
+ const result = await syncScenarioFilesToDatabase(tmp, 'project-1', [{ id: 'existing-1', updated_at: '2024-01-01T00:00:00.000Z' }], () => { }, () => { });
303
+ expect(result.skipped).toBe(1);
304
+ expect(result.inserted).toBe(0);
305
+ expect(result.updated).toBe(0);
306
+ });
307
+ it('should update scenarios newer than DB row', async () => {
308
+ const tmp = makeTmpDir();
309
+ writeScenarioFile(tmp, 'existing-1', { seed: {} }, makeMetadata({ name: 'Updated', updatedAt: '2024-02-01T00:00:00.000Z' }));
310
+ const updatedRows = [];
311
+ const result = await syncScenarioFilesToDatabase(tmp, 'project-1', [{ id: 'existing-1', updated_at: '2024-01-01T00:00:00.000Z' }], () => { }, (id, row) => {
312
+ updatedRows.push({ id, ...row });
313
+ });
314
+ expect(result.updated).toBe(1);
315
+ expect(updatedRows[0].name).toBe('Updated');
316
+ });
317
+ it('should handle empty directory', async () => {
318
+ const tmp = makeTmpDir();
319
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
320
+ fs.mkdirSync(dir, { recursive: true });
321
+ const result = await syncScenarioFilesToDatabase(tmp, 'project-1', [], () => { }, () => { });
322
+ expect(result.inserted).toBe(0);
323
+ });
324
+ it('should handle mixed insert/update/skip', async () => {
325
+ const tmp = makeTmpDir();
326
+ writeScenarioFile(tmp, 'new-1', { seed: {} }, makeMetadata({ name: 'New', updatedAt: '2024-01-01T00:00:00.000Z' }));
327
+ writeScenarioFile(tmp, 'updated-1', { seed: {} }, makeMetadata({ name: 'Updated', updatedAt: '2024-02-01T00:00:00.000Z' }));
328
+ writeScenarioFile(tmp, 'current-1', { seed: {} }, makeMetadata({ name: 'Current', updatedAt: '2024-01-01T00:00:00.000Z' }));
329
+ const result = await syncScenarioFilesToDatabase(tmp, 'project-1', [
330
+ { id: 'updated-1', updated_at: '2024-01-01T00:00:00.000Z' },
331
+ { id: 'current-1', updated_at: '2024-01-01T00:00:00.000Z' },
332
+ ], () => { }, () => { });
333
+ expect(result.inserted).toBe(1);
334
+ expect(result.updated).toBe(1);
335
+ expect(result.skipped).toBe(1);
336
+ });
337
+ it('should pass entitySha and displayName from JSON _metadata on insert', async () => {
338
+ const tmp = makeTmpDir();
339
+ writeScenarioFile(tmp, 'with-entity', { seed: {} }, {
340
+ ...makeMetadata({ name: 'With Entity', componentName: 'MyComponent' }),
341
+ entitySha: 'abc123sha',
342
+ displayName: 'MyComponent',
343
+ });
344
+ const insertedRows = [];
345
+ await syncScenarioFilesToDatabase(tmp, 'project-1', [], (row) => {
346
+ insertedRows.push(row);
347
+ }, () => { });
348
+ expect(insertedRows[0].entity_sha).toBe('abc123sha');
349
+ expect(insertedRows[0].display_name).toBe('MyComponent');
350
+ });
351
+ it('should pass entitySha and displayName from JSON _metadata on update', async () => {
352
+ const tmp = makeTmpDir();
353
+ writeScenarioFile(tmp, 'existing-1', { seed: {} }, {
354
+ ...makeMetadata({
227
355
  name: 'Updated',
228
356
  updatedAt: '2024-02-01T00:00:00.000Z',
229
- }));
230
- addScenarioToManifest(tmp, makeEntry({
231
- id: 'current-1',
232
- name: 'Current',
233
- updatedAt: '2024-01-01T00:00:00.000Z',
234
- }));
235
- const existingRows = [
236
- { id: 'updated-1', updated_at: '2024-01-01T00:00:00.000Z' },
237
- { id: 'current-1', updated_at: '2024-01-01T00:00:00.000Z' },
238
- ];
239
- const result = await syncManifestToDatabase(tmp, 'project-1', existingRows, () => { }, () => { });
240
- expect(result.inserted).toBe(1);
241
- expect(result.updated).toBe(1);
242
- expect(result.skipped).toBe(1);
357
+ }),
358
+ entitySha: 'def456sha',
359
+ displayName: 'UpdatedComponent',
360
+ });
361
+ const updatedRows = [];
362
+ await syncScenarioFilesToDatabase(tmp, 'project-1', [{ id: 'existing-1', updated_at: '2024-01-01T00:00:00.000Z' }], () => { }, (id, row) => {
363
+ updatedRows.push({ id, ...row });
364
+ });
365
+ expect(updatedRows[0].entity_sha).toBe('def456sha');
366
+ expect(updatedRows[0].display_name).toBe('UpdatedComponent');
367
+ });
368
+ it('should pass null entitySha and displayName when missing from JSON', async () => {
369
+ const tmp = makeTmpDir();
370
+ writeScenarioFile(tmp, 'no-entity', { seed: {} }, makeMetadata({ name: 'No Entity' }));
371
+ const insertedRows = [];
372
+ await syncScenarioFilesToDatabase(tmp, 'project-1', [], (row) => {
373
+ insertedRows.push(row);
374
+ }, () => { });
375
+ expect(insertedRows[0].entity_sha).toBeNull();
376
+ expect(insertedRows[0].display_name).toBeNull();
377
+ });
378
+ });
379
+ // ─── migrateScenarioFormats ──────────────────────────────────────
380
+ describe('migrateScenarioFormats', () => {
381
+ function writeConfigWithSizes(tmp, screenSizes, defaultScreenSize) {
382
+ const dir = path.join(tmp, '.codeyam');
383
+ fs.mkdirSync(dir, { recursive: true });
384
+ fs.writeFileSync(path.join(dir, 'config.json'), JSON.stringify({
385
+ ...(defaultScreenSize ? { defaultScreenSize } : {}),
386
+ ...(Object.keys(screenSizes).length > 0 ? { screenSizes } : {}),
387
+ }));
388
+ }
389
+ it('should resolve null viewport using project defaultScreenSize', () => {
390
+ const tmp = makeTmpDir();
391
+ writeConfigWithSizes(tmp, { Desktop: { width: 1440, height: 900 } }, { name: 'Desktop', width: 1440, height: 900 });
392
+ writeScenarioFile(tmp, 'null-vp', { seed: {} }, makeMetadata({
393
+ name: 'Home Page',
394
+ viewportWidth: null,
395
+ viewportHeight: null,
396
+ }));
397
+ const result = migrateScenarioFormats(tmp);
398
+ expect(result.fixed).toBe(1);
399
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'null-vp.json');
400
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
401
+ expect(content._metadata.viewportWidth).toBe(1440);
402
+ expect(content._metadata.viewportHeight).toBe(900);
403
+ });
404
+ it('should wrap single dimension into dimensions array', () => {
405
+ const tmp = makeTmpDir();
406
+ writeConfigWithSizes(tmp, { Mobile: { width: 375, height: 667 } });
407
+ writeScenarioFile(tmp, 'single-dim', { seed: {} }, makeMetadata({
408
+ name: 'Mobile View',
409
+ dimension: 'Mobile',
410
+ viewportWidth: 375,
411
+ viewportHeight: 667,
412
+ }));
413
+ const result = migrateScenarioFormats(tmp);
414
+ expect(result.fixed).toBe(1);
415
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'single-dim.json');
416
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
417
+ expect(content._metadata.dimensions).toEqual(['Mobile']);
418
+ // Legacy dimension property should be stripped
419
+ expect(content._metadata.dimension).toBeUndefined();
420
+ });
421
+ it('should build screenshotPaths from single screenshotPath + dimension', () => {
422
+ const tmp = makeTmpDir();
423
+ writeConfigWithSizes(tmp, { Desktop: { width: 1440, height: 900 } });
424
+ writeScenarioFile(tmp, 'single-ss', { seed: {} }, makeMetadata({
425
+ name: 'Dashboard',
426
+ dimension: 'Desktop',
427
+ screenshotPath: 'screenshots/single-ss.png',
428
+ viewportWidth: 1440,
429
+ viewportHeight: 900,
430
+ }));
431
+ const result = migrateScenarioFormats(tmp);
432
+ expect(result.fixed).toBe(1);
433
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'single-ss.json');
434
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
435
+ expect(content._metadata.screenshotPaths).toEqual({
436
+ Desktop: 'screenshots/single-ss.png',
437
+ });
438
+ });
439
+ it('should skip files that are already in the correct format', () => {
440
+ const tmp = makeTmpDir();
441
+ writeConfigWithSizes(tmp, { Desktop: { width: 1440, height: 900 } });
442
+ writeScenarioFile(tmp, 'good-fmt', { seed: {} }, makeMetadata({
443
+ name: 'Good Scenario',
444
+ viewportWidth: 1440,
445
+ viewportHeight: 900,
446
+ dimensions: ['Desktop'],
447
+ screenshotPaths: { Desktop: 'screenshots/good-fmt.png' },
448
+ screenshotPath: 'screenshots/good-fmt.png',
449
+ }));
450
+ const result = migrateScenarioFormats(tmp);
451
+ expect(result.fixed).toBe(0);
452
+ expect(result.scanned).toBe(1);
453
+ });
454
+ it('should fix multiple issues in a single file', () => {
455
+ const tmp = makeTmpDir();
456
+ writeConfigWithSizes(tmp, { Tablet: { width: 768, height: 1024 } }, { name: 'Tablet', width: 768, height: 1024 });
457
+ // File has null viewport, dimension set but no dimensions array, screenshotPath but no map
458
+ writeScenarioFile(tmp, 'multi-fix', { seed: {} }, makeMetadata({
459
+ name: 'Tablet View',
460
+ dimension: 'Tablet',
461
+ viewportWidth: null,
462
+ viewportHeight: null,
463
+ screenshotPath: 'screenshots/multi-fix.png',
464
+ }));
465
+ const result = migrateScenarioFormats(tmp);
466
+ expect(result.fixed).toBe(1);
467
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'multi-fix.json');
468
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
469
+ // Null viewport resolved via dimension name lookup
470
+ expect(content._metadata.viewportWidth).toBe(768);
471
+ expect(content._metadata.viewportHeight).toBe(1024);
472
+ // dimensions array created from single dimension
473
+ expect(content._metadata.dimensions).toEqual(['Tablet']);
474
+ // screenshotPaths map created from single screenshotPath
475
+ expect(content._metadata.screenshotPaths).toEqual({
476
+ Tablet: 'screenshots/multi-fix.png',
477
+ });
478
+ });
479
+ it('should use hardcoded fallback (1280x720) when no project config exists', () => {
480
+ const tmp = makeTmpDir();
481
+ // No config.json at all
482
+ writeScenarioFile(tmp, 'no-config', { seed: {} }, makeMetadata({
483
+ name: 'Bare Scenario',
484
+ viewportWidth: null,
485
+ viewportHeight: null,
486
+ }));
487
+ const result = migrateScenarioFormats(tmp);
488
+ expect(result.fixed).toBe(1);
489
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'no-config.json');
490
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
491
+ expect(content._metadata.viewportWidth).toBe(1280);
492
+ expect(content._metadata.viewportHeight).toBe(720);
493
+ });
494
+ it('should handle empty scenarios directory', () => {
495
+ const tmp = makeTmpDir();
496
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
497
+ fs.mkdirSync(dir, { recursive: true });
498
+ const result = migrateScenarioFormats(tmp);
499
+ expect(result.scanned).toBe(0);
500
+ expect(result.fixed).toBe(0);
501
+ });
502
+ it('should handle missing scenarios directory', () => {
503
+ const tmp = makeTmpDir();
504
+ const result = migrateScenarioFormats(tmp);
505
+ expect(result.scanned).toBe(0);
506
+ expect(result.fixed).toBe(0);
507
+ });
508
+ it('should preserve all non-metadata data in the file', () => {
509
+ const tmp = makeTmpDir();
510
+ writeConfigWithSizes(tmp, {}, { name: 'Desktop', width: 1440, height: 900 });
511
+ // Write file with extra data alongside _metadata
512
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
513
+ fs.mkdirSync(dir, { recursive: true });
514
+ const fileContent = {
515
+ _metadata: makeMetadata({
516
+ name: 'With Data',
517
+ viewportWidth: null,
518
+ viewportHeight: null,
519
+ }),
520
+ type: 'application',
521
+ seed: { users: [{ id: 1, name: 'Alice' }] },
522
+ session: { cookieValue: 'abc123' },
523
+ };
524
+ fs.writeFileSync(path.join(dir, 'with-data.json'), JSON.stringify(fileContent, null, 2));
525
+ migrateScenarioFormats(tmp);
526
+ const content = JSON.parse(fs.readFileSync(path.join(dir, 'with-data.json'), 'utf8'));
527
+ expect(content.type).toBe('application');
528
+ expect(content.seed).toEqual({ users: [{ id: 1, name: 'Alice' }] });
529
+ expect(content.session).toEqual({ cookieValue: 'abc123' });
530
+ // And metadata was fixed
531
+ expect(content._metadata.viewportWidth).toBe(1440);
532
+ });
533
+ });
534
+ // ─── writeScenarioDataFile ───────────────────────────────────────
535
+ function readScenarioJson(tmp, id) {
536
+ return JSON.parse(fs.readFileSync(path.join(tmp, '.codeyam', 'editor-scenarios', `${id}.json`), 'utf8'));
537
+ }
538
+ describe('writeScenarioDataFile', () => {
539
+ it('should create new file with metadata and data when no existing file', () => {
540
+ const tmp = makeTmpDir();
541
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
542
+ fs.mkdirSync(dir, { recursive: true });
543
+ const metadata = makeMetadata({ name: 'Library - Rich Data' });
544
+ writeScenarioDataFile(tmp, 'new-scenario', metadata, {
545
+ type: 'application',
546
+ seed: { user: [{ id: 1, name: 'Alice' }] },
547
+ });
548
+ const content = readScenarioJson(tmp, 'new-scenario');
549
+ expect(content._metadata.name).toBe('Library - Rich Data');
550
+ expect(content.type).toBe('application');
551
+ expect(content.seed).toEqual({ user: [{ id: 1, name: 'Alice' }] });
552
+ });
553
+ it('should preserve existing seed data when re-registering without seed', () => {
554
+ const tmp = makeTmpDir();
555
+ // Create scenario with seed data (as if previously registered with seed)
556
+ writeScenarioFile(tmp, 'app-scenario', {
557
+ type: 'application',
558
+ seed: { article: [{ id: 1, title: 'Climate Report' }] },
559
+ externalApis: { 'GET /api/weather': { body: { temp: 72 } } },
560
+ }, makeMetadata({ name: 'Library - Rich Data' }));
561
+ // Re-register without seed (simulating Claude re-registering after code change)
562
+ const newMetadata = makeMetadata({
563
+ name: 'Library - Rich Data',
564
+ updatedAt: '2024-02-01T00:00:00.000Z',
565
+ });
566
+ writeScenarioDataFile(tmp, 'app-scenario', newMetadata, {});
567
+ const content = readScenarioJson(tmp, 'app-scenario');
568
+ // Metadata should be updated
569
+ expect(content._metadata.updatedAt).toBe('2024-02-01T00:00:00.000Z');
570
+ // Existing data fields should be preserved
571
+ expect(content.type).toBe('application');
572
+ expect(content.seed).toEqual({
573
+ article: [{ id: 1, title: 'Climate Report' }],
574
+ });
575
+ expect(content.externalApis).toEqual({
576
+ 'GET /api/weather': { body: { temp: 72 } },
577
+ });
578
+ });
579
+ it('should preserve sessionCookies when updating seed', () => {
580
+ const tmp = makeTmpDir();
581
+ // Scenario with seed + sessionCookies (set by seed adapter after auth)
582
+ writeScenarioFile(tmp, 'auth-scenario', {
583
+ type: 'application',
584
+ seed: { user: [{ id: 1, email: 'alice@example.com' }] },
585
+ sessionCookies: [{ name: 'session', value: 'abc123' }],
586
+ }, makeMetadata({ name: 'Logged In User' }));
587
+ // Re-register with updated seed but no sessionCookies in the override
588
+ writeScenarioDataFile(tmp, 'auth-scenario', makeMetadata({ name: 'Logged In User' }), {
589
+ type: 'application',
590
+ seed: { user: [{ id: 1, email: 'bob@example.com' }] },
591
+ });
592
+ const content = readScenarioJson(tmp, 'auth-scenario');
593
+ // Seed should be updated
594
+ expect(content.seed).toEqual({
595
+ user: [{ id: 1, email: 'bob@example.com' }],
596
+ });
597
+ // Session cookies should be preserved (not overwritten)
598
+ expect(content.sessionCookies).toEqual([
599
+ { name: 'session', value: 'abc123' },
600
+ ]);
601
+ });
602
+ it('should overwrite fields that are explicitly provided', () => {
603
+ const tmp = makeTmpDir();
604
+ writeScenarioFile(tmp, 'update-scenario', {
605
+ type: 'application',
606
+ seed: { user: [{ id: 1 }] },
607
+ localStorage: { theme: 'dark' },
608
+ }, makeMetadata({ name: 'Old Name' }));
609
+ writeScenarioDataFile(tmp, 'update-scenario', makeMetadata({ name: 'New Name' }), {
610
+ seed: { user: [{ id: 1 }, { id: 2 }] },
611
+ localStorage: { theme: 'light', lang: 'en' },
243
612
  });
613
+ const content = readScenarioJson(tmp, 'update-scenario');
614
+ expect(content._metadata.name).toBe('New Name');
615
+ expect(content.seed).toEqual({ user: [{ id: 1 }, { id: 2 }] });
616
+ expect(content.localStorage).toEqual({ theme: 'light', lang: 'en' });
617
+ // type was not in overrides, should be preserved
618
+ expect(content.type).toBe('application');
619
+ });
620
+ it('should handle component scenarios with mockData spread at top level', () => {
621
+ const tmp = makeTmpDir();
622
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
623
+ fs.mkdirSync(dir, { recursive: true });
624
+ writeScenarioDataFile(tmp, 'comp-scenario', makeMetadata({
625
+ name: 'TaskCard - Default',
626
+ type: 'component',
627
+ componentName: 'TaskCard',
628
+ }), {
629
+ props: { title: 'My Task', done: false },
630
+ localStorage: { settings: '{}' },
631
+ });
632
+ const content = readScenarioJson(tmp, 'comp-scenario');
633
+ expect(content.props).toEqual({ title: 'My Task', done: false });
634
+ expect(content.localStorage).toEqual({ settings: '{}' });
635
+ });
636
+ it('should create directories if they do not exist', () => {
637
+ const tmp = makeTmpDir();
638
+ // No .codeyam/editor-scenarios/ yet
639
+ writeScenarioDataFile(tmp, 'first-scenario', makeMetadata({ name: 'First' }), { type: 'application' });
640
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'first-scenario.json');
641
+ expect(fs.existsSync(filePath)).toBe(true);
642
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
643
+ expect(content.type).toBe('application');
644
+ });
645
+ it('should preserve existing fields from all scenario data categories', () => {
646
+ const tmp = makeTmpDir();
647
+ // Scenario with every possible field populated
648
+ writeScenarioFile(tmp, 'full-scenario', {
649
+ type: 'application',
650
+ seed: { items: [{ id: 1 }] },
651
+ externalApis: { 'GET /weather': { body: {} } },
652
+ session: { cookieValue: 'sess123' },
653
+ auth: { email: 'a@b.com', password: 'pass' },
654
+ localStorage: { key: 'val' },
655
+ sessionCookies: [{ name: 'tok', value: 'xyz' }],
656
+ }, makeMetadata({ name: 'Full Scenario' }));
657
+ // Re-register with ONLY metadata update (no data overrides at all)
658
+ writeScenarioDataFile(tmp, 'full-scenario', makeMetadata({
659
+ name: 'Full Scenario',
660
+ updatedAt: '2024-06-01T00:00:00.000Z',
661
+ }));
662
+ const content = readScenarioJson(tmp, 'full-scenario');
663
+ expect(content.type).toBe('application');
664
+ expect(content.seed).toEqual({ items: [{ id: 1 }] });
665
+ expect(content.externalApis).toEqual({ 'GET /weather': { body: {} } });
666
+ expect(content.session).toEqual({ cookieValue: 'sess123' });
667
+ expect(content.auth).toEqual({ email: 'a@b.com', password: 'pass' });
668
+ expect(content.localStorage).toEqual({ key: 'val' });
669
+ expect(content.sessionCookies).toEqual([{ name: 'tok', value: 'xyz' }]);
244
670
  });
245
671
  });
246
672
  //# sourceMappingURL=scenariosManifest.test.js.map