@pikku/cli 0.12.151 → 0.12.153

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 (275) hide show
  1. package/console-app/assets/{HelpPanel-CxjgE40i.js → HelpPanel-0XQ4CyKC.js} +1 -1
  2. package/console-app/assets/{abnfDiagram-VCTEODGH-UPW1PlxM.js → abnfDiagram-VCTEODGH-DZTF_2aX.js} +1 -1
  3. package/console-app/assets/architecture-7GRP2DOG-NotVOJ5Y.js +1 -0
  4. package/console-app/assets/{architectureDiagram-5GKGNRK7-B3XFrvZk.js → architectureDiagram-5GKGNRK7-Re4-6X-k.js} +1 -1
  5. package/console-app/assets/{blockDiagram-I7D4REHJ-Ca0v9UJQ.js → blockDiagram-I7D4REHJ-Dbj7SnRK.js} +1 -1
  6. package/console-app/assets/{c4Diagram-7LVT6UL2-CLewVsj_.js → c4Diagram-7LVT6UL2-DUr02Wxh.js} +1 -1
  7. package/console-app/assets/channel-DU63_zXh.js +1 -0
  8. package/console-app/assets/{chunk-4HAMMTFA-BwhbhOD9.js → chunk-4HAMMTFA-DjatzNf_.js} +1 -1
  9. package/console-app/assets/{chunk-75Z2AOVW-C7nAgNk0.js → chunk-75Z2AOVW-BGsmWEdU.js} +1 -1
  10. package/console-app/assets/{chunk-DU6HZSFF-D4yYAcHt.js → chunk-DU6HZSFF-CC7WiVgI.js} +1 -1
  11. package/console-app/assets/{chunk-F27PBJKO-UHllOPps.js → chunk-F27PBJKO-BATuW9Ej.js} +1 -1
  12. package/console-app/assets/{chunk-GMAD6QVW-C0l_vm7T.js → chunk-GMAD6QVW-CgHNo00B.js} +1 -1
  13. package/console-app/assets/{chunk-GVQU2GXP-h-H2Oird.js → chunk-GVQU2GXP-VT_xckd9.js} +1 -1
  14. package/console-app/assets/{chunk-IMKFNOWR-DJ_W9CSg.js → chunk-IMKFNOWR-BhSVbWtF.js} +1 -1
  15. package/console-app/assets/{chunk-L3NEJ4N5-Ce9oniwE.js → chunk-L3NEJ4N5-HUF6a3Xl.js} +1 -1
  16. package/console-app/assets/{chunk-OSK3NFVY-CdGE5qxS.js → chunk-OSK3NFVY-C_LPDGIB.js} +1 -1
  17. package/console-app/assets/{chunk-P2QGCYS3-DK1pHDdz.js → chunk-P2QGCYS3-lWDBgw4Q.js} +1 -1
  18. package/console-app/assets/{chunk-POPQ4Y6H-DjevEFNo.js → chunk-POPQ4Y6H--6JTzmZJ.js} +1 -1
  19. package/console-app/assets/{chunk-PWAF6VOD-BVF5N1No.js → chunk-PWAF6VOD-C1GqaJJs.js} +1 -1
  20. package/console-app/assets/{chunk-SHT3W25Y-BX8We-wN.js → chunk-SHT3W25Y-Dd_4Zpdj.js} +1 -1
  21. package/console-app/assets/{chunk-SVP7TREG-Cl0GgAPC.js → chunk-SVP7TREG-B8OY7dfD.js} +1 -1
  22. package/console-app/assets/{chunk-TICWLB2K-IWZAWDhu.js → chunk-TICWLB2K-B8S7Hmnu.js} +1 -1
  23. package/console-app/assets/chunk-XXDRQBXY-BLuHuEZN.js +1 -0
  24. package/console-app/assets/classDiagram-ZZMXUADV-BTYfUi7u.js +1 -0
  25. package/console-app/assets/classDiagram-v2-VYDZK3BY-BTYfUi7u.js +1 -0
  26. package/console-app/assets/{cose-bilkent-JH36ORCC-GsQ0JAnL.js → cose-bilkent-JH36ORCC-C6RM8oPO.js} +1 -1
  27. package/console-app/assets/{cynefin-OW5HDTMX-hZsAZp_5.js → cynefin-OW5HDTMX-COh9rFYY.js} +1 -1
  28. package/console-app/assets/{cynefinDiagram-5FMLGOSQ-CsyMovx1.js → cynefinDiagram-5FMLGOSQ-Dd9AR17w.js} +1 -1
  29. package/console-app/assets/{dagre-BP8XEFz-.js → dagre-BDMvDAer.js} +1 -1
  30. package/console-app/assets/{dagre-GXQ25YYZ-Jg5285rx.js → dagre-GXQ25YYZ-C_nBUPgr.js} +1 -1
  31. package/console-app/assets/{diagram-S7CK7UJ4-n9JiR8HK.js → diagram-S7CK7UJ4-BGOobQeC.js} +1 -1
  32. package/console-app/assets/{diagram-UQ7AKVKN-Cde-dgbI.js → diagram-UQ7AKVKN-CAF_vcj0.js} +1 -1
  33. package/console-app/assets/{diagram-VSXAHHWV-CPZUg4FP.js → diagram-VSXAHHWV-q-aY0Nsc.js} +1 -1
  34. package/console-app/assets/{diagram-VX7I27RA-B2rCDWvn.js → diagram-VX7I27RA-CnUibSOe.js} +1 -1
  35. package/console-app/assets/{diagram-Z3DM3KII-UwO0hte3.js → diagram-Z3DM3KII-DY0V5kOJ.js} +1 -1
  36. package/console-app/assets/{ebnfDiagram-PWID7BFC-BGJ_PBlS.js → ebnfDiagram-PWID7BFC-CXp4CmEF.js} +1 -1
  37. package/console-app/assets/{erDiagram-RLTQ6QDP-CKsTScr3.js → erDiagram-RLTQ6QDP-CKnfXmPl.js} +1 -1
  38. package/console-app/assets/eventmodeling-NTZA5JFV-CBfQhnG8.js +1 -0
  39. package/console-app/assets/flowDiagram-HODETNUW-BnY3G7p6.js +1 -0
  40. package/console-app/assets/{ganttDiagram-EL5Y4UJY-koeTkIEA.js → ganttDiagram-EL5Y4UJY-D6v_RJ7o.js} +1 -1
  41. package/console-app/assets/{gitGraph-4MIJSDKK-DyRPbcM4.js → gitGraph-4MIJSDKK-y3kWDddY.js} +1 -1
  42. package/console-app/assets/{gitGraphDiagram-WWUBYQGX-DSIejrYw.js → gitGraphDiagram-WWUBYQGX-CBaE24KN.js} +1 -1
  43. package/console-app/assets/{graphlib-BpQ_697T.js → graphlib-DAA_TPmU.js} +1 -1
  44. package/console-app/assets/{index-Du9_fRSw.js → index-HuTd6kcC.js} +147 -147
  45. package/console-app/assets/{info-A6RAGUB7-C63f9an_.js → info-A6RAGUB7-BvxfRJ9-.js} +1 -1
  46. package/console-app/assets/{infoDiagram-27XIBGKW-Dz5gMcUF.js → infoDiagram-27XIBGKW-BlFmgW96.js} +1 -1
  47. package/console-app/assets/{ishikawaDiagram-5VMMS53U-m-nTAZTH.js → ishikawaDiagram-5VMMS53U-Dpwhnwwq.js} +1 -1
  48. package/console-app/assets/{journeyDiagram-3NMN7TZE-C7mjFY7i.js → journeyDiagram-3NMN7TZE-iOSeOQma.js} +1 -1
  49. package/console-app/assets/{kanban-definition-UXKFOSKX-Cm7PqOOP.js → kanban-definition-UXKFOSKX-DFGTB8S5.js} +1 -1
  50. package/console-app/assets/{line-RZNRGurC.js → line-B_5qZKoM.js} +1 -1
  51. package/console-app/assets/{linear-C2IGWMlF.js → linear-BmXUYu4J.js} +1 -1
  52. package/console-app/assets/{mermaid-parser.core-DN1yDqgB.js → mermaid-parser.core-DyPi3Lbr.js} +3 -3
  53. package/console-app/assets/{mermaid.core-DDRs3TbC.js → mermaid.core-S4y9jX8K.js} +4 -4
  54. package/console-app/assets/{mindmap-definition-YA3MSWOX-o6riOwlF.js → mindmap-definition-YA3MSWOX-Bl09mCaS.js} +1 -1
  55. package/console-app/assets/{packet-AYTQ26CC-B89F6gXG.js → packet-AYTQ26CC-B5zoasmU.js} +1 -1
  56. package/console-app/assets/{pegDiagram-XKGWAZYB-BQ1AAIaV.js → pegDiagram-XKGWAZYB-CAa0SOHf.js} +1 -1
  57. package/console-app/assets/{pie-WAS4IAKB-CzuwQlQz.js → pie-WAS4IAKB-C91xXCXY.js} +1 -1
  58. package/console-app/assets/{pieDiagram-E7YTZNPT-DlcGHWGT.js → pieDiagram-E7YTZNPT-CmUHVYwz.js} +1 -1
  59. package/console-app/assets/{quadrantDiagram-AXDQQJYC-DdITwqw-.js → quadrantDiagram-AXDQQJYC-XWsej4ss.js} +1 -1
  60. package/console-app/assets/{radar-RG4KPBEZ-DlJ29jAN.js → radar-RG4KPBEZ-B7ieCnSw.js} +1 -1
  61. package/console-app/assets/{railroad-74A4TZTK-BL49fdM8.js → railroad-74A4TZTK-KTKjMg4U.js} +1 -1
  62. package/console-app/assets/railroad-abnf-HS5TGJTU-e9AlsYmr.js +1 -0
  63. package/console-app/assets/railroad-ebnf-LZEXJU2U-BVX8UG1z.js +1 -0
  64. package/console-app/assets/railroad-peg-WCYAUIDC-BzWQFvD9.js +1 -0
  65. package/console-app/assets/{railroadDiagram-O6MQD6OU-BNgQGtF5.js → railroadDiagram-O6MQD6OU-oh1FjI2K.js} +1 -1
  66. package/console-app/assets/{requirementDiagram-BXWQKSXE-48lE_WeD.js → requirementDiagram-BXWQKSXE-D6z7hLfj.js} +1 -1
  67. package/console-app/assets/{sankeyDiagram-P5KCCOFB-WmTardCP.js → sankeyDiagram-P5KCCOFB-DFb92jfp.js} +1 -1
  68. package/console-app/assets/{sequenceDiagram-WJ2MYXX4-Bd5cHDMs.js → sequenceDiagram-WJ2MYXX4-BRKj-PG1.js} +1 -1
  69. package/console-app/assets/{src-BTXg_NtK.js → src-DYBVouJk.js} +1 -1
  70. package/console-app/assets/{stateDiagram-D77RDMKH-DBoCkqGv.js → stateDiagram-D77RDMKH-BfTLNw5t.js} +1 -1
  71. package/console-app/assets/stateDiagram-v2-MP3YSRHH-B8sMNnnF.js +1 -0
  72. package/console-app/assets/{swimlanes-42K2YHIH-lecUPBaK.js → swimlanes-42K2YHIH-DXkNYzX9.js} +1 -1
  73. package/console-app/assets/swimlanesDiagram-VR7AAH4N-CiZdLDyk.js +8 -0
  74. package/console-app/assets/{timeline-definition-24CTP7MA-CWq0OWm1.js → timeline-definition-24CTP7MA-B8m2V75P.js} +1 -1
  75. package/console-app/assets/{treeView-Q6P3EWNA-C7lEeqUz.js → treeView-Q6P3EWNA-BisqOzat.js} +1 -1
  76. package/console-app/assets/{treemap-WGGIJYW6-Bkx2DAoI.js → treemap-WGGIJYW6-DOxEXpb1.js} +1 -1
  77. package/console-app/assets/{vennDiagram-4TSXK5OY-Bw_kGRL3.js → vennDiagram-4TSXK5OY-ZeuU7o23.js} +1 -1
  78. package/console-app/assets/{wardley-WFR3VGLG-V0zki2L0.js → wardley-WFR3VGLG-sspeOMuz.js} +1 -1
  79. package/console-app/assets/{wardleyDiagram-VM6X3IG4-C5vrwEHM.js → wardleyDiagram-VM6X3IG4-CF2vzNJV.js} +1 -1
  80. package/console-app/assets/{xychartDiagram-S5SC5T6Z-B9uA0moC.js → xychartDiagram-S5SC5T6Z-BR9XBq3s.js} +1 -1
  81. package/console-app/index.html +1 -1
  82. package/dist/.pikku/addon/index.d.ts +1 -1
  83. package/dist/.pikku/addon/index.js +1 -1
  84. package/dist/.pikku/addon/pikku-addon-types.gen.d.ts +1 -1
  85. package/dist/.pikku/addon/pikku-addon-types.gen.js +1 -1
  86. package/dist/.pikku/agent/index.d.ts +1 -1
  87. package/dist/.pikku/agent/index.js +1 -1
  88. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  89. package/dist/.pikku/agent/pikku-model-aliases.gen.js +1 -1
  90. package/dist/.pikku/analytics/index.d.ts +1 -1
  91. package/dist/.pikku/analytics/index.js +1 -1
  92. package/dist/.pikku/analytics/pikku-analytics-types.gen.d.ts +1 -1
  93. package/dist/.pikku/analytics/pikku-analytics-types.gen.js +1 -1
  94. package/dist/.pikku/auth/index.d.ts +1 -1
  95. package/dist/.pikku/auth/index.js +1 -1
  96. package/dist/.pikku/auth/pikku-auth-types.gen.d.ts +1 -1
  97. package/dist/.pikku/auth/pikku-auth-types.gen.js +1 -1
  98. package/dist/.pikku/channel/index.d.ts +1 -1
  99. package/dist/.pikku/channel/index.js +1 -1
  100. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  101. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  102. package/dist/.pikku/cli/index.d.ts +1 -1
  103. package/dist/.pikku/cli/index.js +1 -1
  104. package/dist/.pikku/cli/pikku-cli-channel.js +21 -1
  105. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  106. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  107. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  108. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  109. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +38 -1
  110. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  111. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  112. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  113. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +94 -1
  114. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  115. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  116. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  117. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  118. package/dist/.pikku/error/index.d.ts +1 -1
  119. package/dist/.pikku/error/index.js +1 -1
  120. package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
  121. package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
  122. package/dist/.pikku/function/index.d.ts +1 -1
  123. package/dist/.pikku/function/index.js +1 -1
  124. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  125. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  126. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  127. package/dist/.pikku/function/pikku-functions-meta.gen.json +78 -2
  128. package/dist/.pikku/function/pikku-functions.gen.js +1 -1
  129. package/dist/.pikku/gateway/index.d.ts +1 -1
  130. package/dist/.pikku/gateway/index.js +1 -1
  131. package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
  132. package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
  133. package/dist/.pikku/http/index.d.ts +1 -1
  134. package/dist/.pikku/http/index.js +1 -1
  135. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  136. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  137. package/dist/.pikku/mcp/index.d.ts +1 -1
  138. package/dist/.pikku/mcp/index.js +1 -1
  139. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  140. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  141. package/dist/.pikku/middleware/index.d.ts +1 -1
  142. package/dist/.pikku/middleware/index.js +1 -1
  143. package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +1 -1
  144. package/dist/.pikku/middleware/pikku-middleware-types.gen.js +1 -1
  145. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  146. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
  147. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  148. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  149. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  150. package/dist/.pikku/queue/index.d.ts +1 -1
  151. package/dist/.pikku/queue/index.js +1 -1
  152. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  153. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  154. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  155. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  156. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  157. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  158. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +4 -0
  159. package/dist/.pikku/scenarios/index.d.ts +1 -1
  160. package/dist/.pikku/scenarios/index.js +1 -1
  161. package/dist/.pikku/scenarios/pikku-personas.gen.js +1 -1
  162. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  163. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  164. package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +1 -1
  165. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  166. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  167. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  168. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  169. package/dist/.pikku/scheduler/index.d.ts +1 -1
  170. package/dist/.pikku/scheduler/index.js +1 -1
  171. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  172. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  173. package/dist/.pikku/schemas/register.gen.js +17 -1
  174. package/dist/.pikku/schemas/schemas/ExamplesAddInput.schema.json +1 -0
  175. package/dist/.pikku/schemas/schemas/ExamplesAddOutput.schema.json +1 -0
  176. package/dist/.pikku/schemas/schemas/ExamplesListInput.schema.json +1 -0
  177. package/dist/.pikku/schemas/schemas/ExamplesListOutput.schema.json +1 -0
  178. package/dist/.pikku/schemas/schemas/ExamplesShowInput.schema.json +1 -0
  179. package/dist/.pikku/schemas/schemas/ExamplesShowOutput.schema.json +1 -0
  180. package/dist/.pikku/schemas/schemas/FabricChangesFileInput.schema.json +1 -0
  181. package/dist/.pikku/schemas/schemas/FabricChangesFileOutput.schema.json +1 -0
  182. package/dist/.pikku/scopes/index.d.ts +1 -1
  183. package/dist/.pikku/scopes/index.js +1 -1
  184. package/dist/.pikku/scopes/pikku-flags.gen.d.ts +1 -1
  185. package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
  186. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
  187. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  188. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  189. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  190. package/dist/.pikku/secrets/index.d.ts +1 -1
  191. package/dist/.pikku/secrets/index.js +1 -1
  192. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  193. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  194. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  195. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  196. package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
  197. package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
  198. package/dist/.pikku/setup/index.d.ts +1 -1
  199. package/dist/.pikku/setup/index.js +1 -1
  200. package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
  201. package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
  202. package/dist/.pikku/trigger/index.d.ts +1 -1
  203. package/dist/.pikku/trigger/index.js +1 -1
  204. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  205. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  206. package/dist/.pikku/variables/index.d.ts +1 -1
  207. package/dist/.pikku/variables/index.js +1 -1
  208. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  209. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  210. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  211. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  212. package/dist/.pikku/workflow/index.d.ts +1 -1
  213. package/dist/.pikku/workflow/index.js +1 -1
  214. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  215. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  216. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  217. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  218. package/dist/bin/pikku-bin.mjs +2 -2
  219. package/dist/src/cli.wiring.js +51 -0
  220. package/dist/src/examples.gen.d.ts +17 -0
  221. package/dist/src/examples.gen.js +7 -0
  222. package/dist/src/fabric/fabric-commands.d.ts +37 -0
  223. package/dist/src/fabric/fabric-commands.js +32 -1
  224. package/dist/src/fabric/functions/changes-file.function.d.ts +64 -0
  225. package/dist/src/fabric/functions/changes-file.function.js +56 -0
  226. package/dist/src/fabric/functions/validate.function.d.ts +34 -0
  227. package/dist/src/fabric/functions/validate.function.js +118 -0
  228. package/dist/src/fabric/lib/config.d.ts +1 -0
  229. package/dist/src/fabric/lib/config.js +1 -1
  230. package/dist/src/functions/commands/dev-address.d.ts +27 -0
  231. package/dist/src/functions/commands/dev-address.js +45 -0
  232. package/dist/src/functions/commands/dev.js +6 -0
  233. package/dist/src/functions/commands/environment.d.ts +8 -0
  234. package/dist/src/functions/commands/environment.js +15 -0
  235. package/dist/src/functions/commands/examples.d.ts +170 -0
  236. package/dist/src/functions/commands/examples.js +28 -0
  237. package/dist/src/functions/commands/scenario.js +356 -273
  238. package/dist/src/functions/db/local-db.d.ts +12 -0
  239. package/dist/src/functions/db/local-db.js +1 -1
  240. package/dist/src/functions/db/postgres/scenario-baseline.d.ts +3 -0
  241. package/dist/src/functions/db/postgres/scenario-baseline.js +112 -0
  242. package/dist/src/functions/db/scenario-baseline.d.ts +51 -0
  243. package/dist/src/functions/db/scenario-baseline.js +60 -0
  244. package/dist/src/functions/db/sqlite/scenario-baseline.d.ts +3 -0
  245. package/dist/src/functions/db/sqlite/scenario-baseline.js +77 -0
  246. package/dist/src/functions/examples/command-schemas.d.ts +69 -0
  247. package/dist/src/functions/examples/command-schemas.js +15 -0
  248. package/dist/src/functions/examples/corpus.d.ts +115 -0
  249. package/dist/src/functions/examples/corpus.js +307 -0
  250. package/dist/src/functions/examples/project.d.ts +64 -0
  251. package/dist/src/functions/examples/project.js +363 -0
  252. package/dist/src/functions/examples/render.d.ts +4 -0
  253. package/dist/src/functions/examples/render.js +101 -0
  254. package/dist/src/functions/examples/run.d.ts +9 -0
  255. package/dist/src/functions/examples/run.js +242 -0
  256. package/dist/src/functions/examples/schemas.d.ts +65 -0
  257. package/dist/src/functions/examples/schemas.js +76 -0
  258. package/dist/src/functions/surface/render-surface-doc.js +32 -2
  259. package/dist/tsconfig.tsbuildinfo +1 -1
  260. package/package.json +5 -4
  261. package/snippets-meta.json +2 -0
  262. package/snippets.json +2 -0
  263. package/surface.json +1 -1
  264. package/console-app/assets/architecture-7GRP2DOG-BQN972Vc.js +0 -1
  265. package/console-app/assets/channel-B8xtOmL3.js +0 -1
  266. package/console-app/assets/chunk-XXDRQBXY-CGWcS5Ol.js +0 -1
  267. package/console-app/assets/classDiagram-ZZMXUADV-OGsmOlmO.js +0 -1
  268. package/console-app/assets/classDiagram-v2-VYDZK3BY-OGsmOlmO.js +0 -1
  269. package/console-app/assets/eventmodeling-NTZA5JFV-CnZOTLz3.js +0 -1
  270. package/console-app/assets/flowDiagram-HODETNUW-TYMCrte8.js +0 -1
  271. package/console-app/assets/railroad-abnf-HS5TGJTU-D9Ej_sin.js +0 -1
  272. package/console-app/assets/railroad-ebnf-LZEXJU2U-CjzlC3qv.js +0 -1
  273. package/console-app/assets/railroad-peg-WCYAUIDC-BPa6lIrj.js +0 -1
  274. package/console-app/assets/stateDiagram-v2-MP3YSRHH-BLbaZawL.js +0 -1
  275. package/console-app/assets/swimlanesDiagram-VR7AAH4N-CM_5HS_7.js +0 -8
@@ -15,7 +15,8 @@ import { resolvePersonas } from '../../utils/resolve-personas.js';
15
15
  import { resolvePersonaCredentials } from '../../utils/persona-credentials.js';
16
16
  import { spawnDevServer } from '../../server/spawn-dev-server.js';
17
17
  import { buildScenarioPlan } from './scenario-plan.js';
18
- import { resolveEnvironment } from './environment.js';
18
+ import { resolveEnvironment, isLocalUrl } from './environment.js';
19
+ import { readDevAddress } from './dev-address.js';
19
20
  import { createDevAgentRunner } from './dev-agent-runner.js';
20
21
  const isScenario = (wf) => wf?.scenario === true;
21
22
  const listScenarios = (state) => Object.entries(state.workflows?.meta ?? {})
@@ -106,6 +107,16 @@ export const scenarioRun = pikkuSessionlessFunc({
106
107
  if (apiUrl || appUrl) {
107
108
  logger.info(`Overriding '${environment}': apiUrl ${env.apiUrl}${env.appUrl ? `, appUrl ${env.appUrl}` : ''}`);
108
109
  }
110
+ // A dev server that could not take the port it was asked for is the ordinary
111
+ // way a run against a configured environment meets a connection refused, and
112
+ // nothing in the failure says so. Said here, once, with the flag that fixes it.
113
+ if (!apiUrl && !spawn) {
114
+ const running = readDevAddress(config.runtimeDir ?? join(config.rootDir, '.pikku-runtime'));
115
+ if (running && running.apiUrl !== env.apiUrl) {
116
+ logger.warn(`A \`pikku dev\` is serving ${running.apiUrl}, but '${environment}' targets ${env.apiUrl}. ` +
117
+ `Run against the one that is up with --api-url ${running.apiUrl}, or let this command start its own with --spawn.`);
118
+ }
119
+ }
109
120
  if (spawn) {
110
121
  const { hostname, port } = new URL(env.apiUrl);
111
122
  const resolvedPort = Number(port || 80);
@@ -309,310 +320,382 @@ export const scenarioRun = pikkuSessionlessFunc({
309
320
  return provider;
310
321
  })()
311
322
  : undefined);
323
+ // Captured once, from the migrated and seeded database the suite was
324
+ // pointed at, and replayed before every scenario below. The project's own
325
+ // code has no part in it: a scenario reset is the runner reaching into a
326
+ // local database, never an RPC the deployed bundle carries.
327
+ let databaseBaseline;
328
+ if (config.scenarios?.reset?.enabled) {
329
+ if (env.production) {
330
+ throw new Error(`scenarios.reset is on and '${environment}' is marked production. A reset replaces its rows with seed data; it will not run against one.`);
331
+ }
332
+ if (!isLocalUrl(env.apiUrl)) {
333
+ throw new Error(`scenarios.reset is on but '${environment}' targets ${env.apiUrl}. The reset works on the database directly, so it only runs against a server on this machine.`);
334
+ }
335
+ // Imported here rather than at the top because the db stack statically
336
+ // pulls in kysely and better-auth, which a project running scenarios
337
+ // without this feature has no reason to have installed.
338
+ const { loadUserConfigForDb } = await import('./db-shared.js');
339
+ const { resolveDb } = await import('../db/local-db.js');
340
+ const { captureScenarioBaseline } = await import('../db/scenario-baseline.js');
341
+ const userConfig = await loadUserConfigForDb({ config, logger });
342
+ const resolved = userConfig &&
343
+ resolveDb(userConfig, config.rootDir, config.outDir, config.runtimeDir, config.db);
344
+ if (!resolved) {
345
+ throw new Error(`scenarios.reset is on but no database is configured — set sqliteDb or postgresUrl in your createConfig.`);
346
+ }
347
+ databaseBaseline = await captureScenarioBaseline(resolved, config.scenarios.reset);
348
+ logger.info(`scenario reset: captured ${databaseBaseline.tables.length} tables as the per-scenario baseline`);
349
+ }
312
350
  const results = [];
313
351
  // Opened before the first scenario and written to as each one finishes, so
314
352
  // a suite that dies on its fortieth still leaves the thirty-nine behind —
315
353
  // and the console can show a run while it is still going.
316
354
  const runStore = new FileScenarioRunStore({ dir: captureDir });
317
355
  const startedAtIso = new Date().toISOString();
318
- await runStore.start({
319
- runId: captureRunId,
320
- environment,
321
- surface: runSurface,
322
- status: 'running',
323
- startedAt: startedAtIso,
324
- results: [],
325
- skipped,
326
- hookFailures: [],
327
- });
328
- /**
329
- * The step ladder is read back off the recorded run, so it needs no live
330
- * step events — it is the same data the console renders. Joining it to the
331
- * declared prose happens here, where the inspector state is; laying it out
332
- * is the formatter's job.
333
- */
334
- const readRunSteps = async (service, runId, flowName) => {
335
- const prose = collectScenarioStepProse(state.workflows?.meta?.[flowName], functionsMeta, state.personas?.definitions ?? []);
336
- const steps = (await service.getRunSteps(runId)).map((step) => ({
337
- stepName: step.stepName,
338
- status: step.status,
339
- durationMs: step.succeededAt
340
- ? step.succeededAt.getTime() - step.createdAt.getTime()
341
- : undefined,
342
- error: step.error?.message,
343
- stack: step.error?.stack,
344
- expected: step.error?.expected,
345
- input: step.data,
346
- stepFunc: step.rpcName,
347
- }));
348
- return {
349
- rows: scenarioStepRows(steps, prose),
350
- failure: scenarioFailureFromSteps(steps, prose),
351
- };
352
- };
353
- const coverageActor = coverage ? Object.values(actors)[0] : undefined;
354
- let coverageActive = Boolean(coverageActor);
355
- if (coverage && !coverageActor) {
356
- logger.warn('--coverage requires at least one configured actor — skipping coverage.');
357
- }
358
- const scenarioCoverage = {};
359
- const invokeCoverage = async (rpcName) => {
360
- if (!coverageActive || !coverageActor)
361
- return null;
362
- try {
363
- return await coverageActor.invoke(rpcName, null);
364
- }
365
- catch (e) {
366
- coverageActive = false;
367
- logger.warn(`Coverage disabled — '${rpcName}' failed against '${environment}': ${e?.message ?? e}. ` +
368
- `Is the server running with --coverage and "scaffold.scenarios" enabled in pikku.config.json?`);
369
- return null;
370
- }
371
- };
372
- /**
373
- * A feature hook is not a pikku function and not a run — the feature is a
374
- * grouping, not something durable. It gets the same three arguments a
375
- * scenario body gets, the CLI's own singletons included, and its result is
376
- * discarded.
377
- *
378
- * Its context is *feature*-scoped — shared by that feature's `before` and
379
- * `after`, and deliberately not the context the group's scenarios see:
380
- * one bag across a group is the invisible coupling a Cucumber world had.
381
- */
382
- const singletonServices = pikkuState(null, 'package', 'singletonServices');
383
- const runFeatureHook = async (hook, context) => {
384
- await hook(singletonServices, undefined, {
385
- actors,
386
- scenario: { context },
356
+ try {
357
+ await runStore.start({
358
+ runId: captureRunId,
359
+ environment,
360
+ surface: runSurface,
361
+ status: 'running',
362
+ startedAt: startedAtIso,
363
+ results: [],
364
+ skipped,
365
+ hookFailures: [],
387
366
  });
388
- };
389
- const hookFailures = [];
390
- /**
391
- * What a result carries beyond its own outcome: which registration ran,
392
- * which feature grouped it, and the tags it was selected by. Snapshotted
393
- * into the record because a run read back next week is describing a suite
394
- * whose source has moved on.
395
- */
396
- const identify = (result, scenarioName, feature) => {
397
- const tags = state.workflows?.meta?.[scenarioName]?.tags;
398
- return {
399
- ...result,
400
- scenarioName,
401
- ...(feature ? { feature } : {}),
402
- ...(tags?.length ? { tags } : {}),
367
+ /**
368
+ * The step ladder is read back off the recorded run, so it needs no live
369
+ * step events — it is the same data the console renders. Joining it to the
370
+ * declared prose happens here, where the inspector state is; laying it out
371
+ * is the formatter's job.
372
+ */
373
+ const readRunSteps = async (service, runId, flowName) => {
374
+ const prose = collectScenarioStepProse(state.workflows?.meta?.[flowName], functionsMeta, state.personas?.definitions ?? []);
375
+ const steps = (await service.getRunSteps(runId)).map((step) => ({
376
+ stepName: step.stepName,
377
+ status: step.status,
378
+ durationMs: step.succeededAt
379
+ ? step.succeededAt.getTime() - step.createdAt.getTime()
380
+ : undefined,
381
+ error: step.error?.message,
382
+ stack: step.error?.stack,
383
+ expected: step.error?.expected,
384
+ input: step.data,
385
+ stepFunc: step.rpcName,
386
+ }));
387
+ return {
388
+ rows: scenarioStepRows(steps, prose),
389
+ failure: scenarioFailureFromSteps(steps, prose),
390
+ };
403
391
  };
404
- };
405
- const runEntry = async (label, scenarioName, data, feature) => {
406
- const startedAt = Date.now();
407
- // Before the scenario, not after it: the last scenario's window is left
408
- // open for headed debugging, while this one still starts clean.
409
- await browserLifecycle.reset();
410
- // After the reset, which is what closes the previous scenario's context
411
- // and finalises its video.
412
- browserLifecycle.beginScenario(label);
413
- if (coverageActive) {
414
- const reset = await invokeCoverage('pikkuScenarioResetLiveCoverage');
415
- if (reset && reset.enabled === false) {
392
+ const coverageActor = coverage ? Object.values(actors)[0] : undefined;
393
+ let coverageActive = Boolean(coverageActor);
394
+ if (coverage && !coverageActor) {
395
+ logger.warn('--coverage requires at least one configured actor — skipping coverage.');
396
+ }
397
+ const scenarioCoverage = {};
398
+ const invokeCoverage = async (rpcName) => {
399
+ if (!coverageActive || !coverageActor)
400
+ return null;
401
+ try {
402
+ return await coverageActor.invoke(rpcName, null);
403
+ }
404
+ catch (e) {
416
405
  coverageActive = false;
417
- logger.warn(`Coverage disabled — '${environment}' is not collecting (start the server with --coverage).`);
406
+ logger.warn(`Coverage disabled — '${rpcName}' failed against '${environment}': ${e?.message ?? e}. ` +
407
+ `Is the server running with --coverage and "scaffold.scenarios" enabled in pikku.config.json?`);
408
+ return null;
418
409
  }
419
- }
420
- try {
421
- await coverageActor?.invoke('pikkuScenarioResetStubs', null);
422
- }
423
- catch { }
424
- let runId;
425
- let runError;
426
- try {
427
- // The id comes back through the callback rather than the return value
428
- // because a failing scenario throws instead of returning — and a failed
429
- // run is exactly the one whose steps are worth reading.
430
- ;
431
- ({ runId } = await workflowService.startWorkflow(scenarioName, data, { type: 'cli' }, guardRpc, { actors, onRunCreated: (id) => (runId = id) }));
432
- const run = await workflowService.getRun(runId);
433
- if (run?.status === 'completed') {
434
- results.push({
435
- name: label,
436
- status: 'passed',
437
- durationMs: Date.now() - startedAt,
438
- output: run.output,
439
- });
410
+ };
411
+ /**
412
+ * A feature hook is not a pikku function and not a run — the feature is a
413
+ * grouping, not something durable. It gets the same three arguments a
414
+ * scenario body gets, the CLI's own singletons included, and its result is
415
+ * discarded.
416
+ *
417
+ * Its context is *feature*-scoped — shared by that feature's `before` and
418
+ * `after`, and deliberately not the context the group's scenarios see:
419
+ * one bag across a group is the invisible coupling a Cucumber world had.
420
+ */
421
+ const singletonServices = pikkuState(null, 'package', 'singletonServices');
422
+ const runFeatureHook = async (hook, context) => {
423
+ await hook(singletonServices, undefined, {
424
+ actors,
425
+ scenario: { context },
426
+ });
427
+ };
428
+ const hookFailures = [];
429
+ /**
430
+ * What a result carries beyond its own outcome: which registration ran,
431
+ * which feature grouped it, and the tags it was selected by. Snapshotted
432
+ * into the record because a run read back next week is describing a suite
433
+ * whose source has moved on.
434
+ */
435
+ const identify = (result, scenarioName, feature) => {
436
+ const tags = state.workflows?.meta?.[scenarioName]?.tags;
437
+ return {
438
+ ...result,
439
+ scenarioName,
440
+ ...(feature ? { feature } : {}),
441
+ ...(tags?.length ? { tags } : {}),
442
+ };
443
+ };
444
+ const runEntry = async (label, scenarioName, data, feature) => {
445
+ const startedAt = Date.now();
446
+ if (databaseBaseline) {
447
+ try {
448
+ await databaseBaseline.restore();
449
+ }
450
+ catch (e) {
451
+ const result = identify({
452
+ name: label,
453
+ status: 'failed',
454
+ durationMs: Date.now() - startedAt,
455
+ error: `database reset failed: ${e?.message ?? e}`,
456
+ }, scenarioName, feature);
457
+ results.push(result);
458
+ await runStore.recordScenario(captureRunId, result);
459
+ return;
460
+ }
440
461
  }
441
- else {
442
- runError = run?.error;
462
+ // Before the scenario, not after it: the last scenario's window is left
463
+ // open for headed debugging, while this one still starts clean.
464
+ await browserLifecycle.reset();
465
+ // After the reset, which is what closes the previous scenario's context
466
+ // and finalises its video.
467
+ browserLifecycle.beginScenario(label);
468
+ if (coverageActive) {
469
+ const reset = await invokeCoverage('pikkuScenarioResetLiveCoverage');
470
+ if (reset && reset.enabled === false) {
471
+ coverageActive = false;
472
+ logger.warn(`Coverage disabled — '${environment}' is not collecting (start the server with --coverage).`);
473
+ }
474
+ }
475
+ try {
476
+ await coverageActor?.invoke('pikkuScenarioResetStubs', null);
477
+ }
478
+ catch { }
479
+ let runId;
480
+ let runError;
481
+ try {
482
+ // The id comes back through the callback rather than the return value
483
+ // because a failing scenario throws instead of returning — and a failed
484
+ // run is exactly the one whose steps are worth reading.
485
+ ;
486
+ ({ runId } = await workflowService.startWorkflow(scenarioName, data, { type: 'cli' }, guardRpc, { actors, onRunCreated: (id) => (runId = id) }));
487
+ const run = await workflowService.getRun(runId);
488
+ if (run?.status === 'completed') {
489
+ results.push({
490
+ name: label,
491
+ status: 'passed',
492
+ durationMs: Date.now() - startedAt,
493
+ output: run.output,
494
+ });
495
+ }
496
+ else {
497
+ runError = run?.error;
498
+ results.push({
499
+ name: label,
500
+ status: 'failed',
501
+ durationMs: Date.now() - startedAt,
502
+ error: run?.error?.message ?? `status: ${run?.status}`,
503
+ });
504
+ }
505
+ }
506
+ catch (e) {
507
+ runError = { stack: e?.stack };
443
508
  results.push({
444
509
  name: label,
445
510
  status: 'failed',
446
511
  durationMs: Date.now() - startedAt,
447
- error: run?.error?.message ?? `status: ${run?.status}`,
512
+ error: e?.message ?? String(e),
448
513
  });
449
514
  }
450
- }
451
- catch (e) {
452
- runError = { stack: e?.stack };
453
- results.push({
454
- name: label,
455
- status: 'failed',
456
- durationMs: Date.now() - startedAt,
457
- error: e?.message ?? String(e),
458
- });
459
- }
460
- const result = results[results.length - 1];
461
- let stepFailure;
462
- if (runId) {
463
- const read = await readRunSteps(workflowService, runId, scenarioName);
464
- result.steps = read.rows;
465
- stepFailure = read.failure;
466
- }
467
- if (result.status === 'failed') {
468
- result.failure = {
469
- // A scenario can also fail outside any step — a hook, or the start
470
- // itself — and then the run's own error is all there is to report.
471
- ...(stepFailure ?? {
472
- message: result.error ?? 'unknown failure',
473
- stack: runError?.stack,
474
- expected: runError?.expected,
475
- }),
476
- browser: await browserLifecycle.captureFailure(label),
477
- };
478
- }
479
- // Told here, acted on at the next scenario's reset — that is what closes
480
- // these windows and finalises the video this outcome decides the fate of.
481
- browserLifecycle.endScenario(result.status);
482
- Object.assign(result, identify(result, scenarioName, feature));
483
- await runStore.recordScenario(captureRunId, result);
484
- if (coverageActive) {
485
- const report = await invokeCoverage('pikkuScenarioTakeLiveCoverage');
486
- if (report) {
487
- scenarioCoverage[label] = report;
488
- const covered = report.functions?.filter((f) => f.status === 'covered' || f.status === 'partial');
489
- logger.info(` coverage: ${covered?.length ?? 0}/${report.summary?.total ?? 0} functions exercised by '${label}'`);
490
- }
491
- }
492
- };
493
- for (const group of groups) {
494
- const groupName = group.featureName ?? group.featureId;
495
- const label = (entry) => {
496
- const data = entry.data ? ` ${JSON.stringify(entry.data)}` : '';
497
- return groupName
498
- ? `${groupName} › ${entry.scenarioName}${data}`
499
- : entry.scenarioName;
500
- };
501
- const featureContext = {};
502
- let beforeError;
503
- if (group.before) {
504
- try {
505
- await runFeatureHook(group.before, featureContext);
515
+ const result = results[results.length - 1];
516
+ let stepFailure;
517
+ if (runId) {
518
+ const read = await readRunSteps(workflowService, runId, scenarioName);
519
+ result.steps = read.rows;
520
+ stepFailure = read.failure;
506
521
  }
507
- catch (e) {
508
- beforeError = e;
522
+ if (result.status === 'failed') {
523
+ result.failure = {
524
+ // A scenario can also fail outside any step — a hook, or the start
525
+ // itself — and then the run's own error is all there is to report.
526
+ ...(stepFailure ?? {
527
+ message: result.error ?? 'unknown failure',
528
+ stack: runError?.stack,
529
+ expected: runError?.expected,
530
+ }),
531
+ browser: await browserLifecycle.captureFailure(label),
532
+ };
509
533
  }
510
- }
511
- try {
512
- if (beforeError) {
513
- // Setup failed, so nothing in the group ran. Reporting them as failed
514
- // rather than skipped is the honest reading: they did not pass.
515
- for (const entry of group.entries) {
516
- const result = identify({
517
- name: label(entry),
518
- status: 'failed',
519
- durationMs: 0,
520
- error: `feature '${groupName}' before hook failed: ${beforeError?.message ?? beforeError}`,
521
- }, entry.scenarioName, groupName);
522
- results.push(result);
523
- await runStore.recordScenario(captureRunId, result);
534
+ // Told here, acted on at the next scenario's reset — that is what closes
535
+ // these windows and finalises the video this outcome decides the fate of.
536
+ browserLifecycle.endScenario(result.status);
537
+ Object.assign(result, identify(result, scenarioName, feature));
538
+ await runStore.recordScenario(captureRunId, result);
539
+ if (coverageActive) {
540
+ const report = await invokeCoverage('pikkuScenarioTakeLiveCoverage');
541
+ if (report) {
542
+ scenarioCoverage[label] = report;
543
+ const covered = report.functions?.filter((f) => f.status === 'covered' || f.status === 'partial');
544
+ logger.info(` coverage: ${covered?.length ?? 0}/${report.summary?.total ?? 0} functions exercised by '${label}'`);
524
545
  }
525
546
  }
526
- else {
527
- for (const entry of group.entries) {
528
- await runEntry(label(entry), entry.scenarioName, entry.data, groupName);
547
+ };
548
+ for (const group of groups) {
549
+ const groupName = group.featureName ?? group.featureId;
550
+ const label = (entry) => {
551
+ const data = entry.data ? ` ${JSON.stringify(entry.data)}` : '';
552
+ return groupName
553
+ ? `${groupName} › ${entry.scenarioName}${data}`
554
+ : entry.scenarioName;
555
+ };
556
+ const featureContext = {};
557
+ let beforeError;
558
+ let beforeStage = 'before hook';
559
+ if (databaseBaseline && group.before) {
560
+ // The hook builds on the seed, never on the last feature's leftovers,
561
+ // and what it builds is then captured as the state each of this
562
+ // feature's scenarios is rolled back to.
563
+ try {
564
+ await databaseBaseline.restore();
565
+ }
566
+ catch (e) {
567
+ beforeStage = 'database reset';
568
+ beforeError = e;
529
569
  }
530
570
  }
531
- }
532
- finally {
533
- if (group.after) {
571
+ if (!beforeError && group.before) {
534
572
  try {
535
- await runFeatureHook(group.after, featureContext);
573
+ await runFeatureHook(group.before, featureContext);
574
+ await databaseBaseline?.pushFeatureLayer();
536
575
  }
537
576
  catch (e) {
538
- hookFailures.push(`feature '${groupName}' after hook failed: ${e?.message ?? e}`);
577
+ beforeError = e;
578
+ }
579
+ }
580
+ try {
581
+ if (beforeError) {
582
+ // Setup failed, so nothing in the group ran. Reporting them as failed
583
+ // rather than skipped is the honest reading: they did not pass.
584
+ for (const entry of group.entries) {
585
+ const result = identify({
586
+ name: label(entry),
587
+ status: 'failed',
588
+ durationMs: 0,
589
+ error: `${groupName ? `feature '${groupName}' ` : ''}${beforeStage} failed: ${beforeError?.message ?? beforeError}`,
590
+ }, entry.scenarioName, groupName);
591
+ results.push(result);
592
+ await runStore.recordScenario(captureRunId, result);
593
+ }
594
+ }
595
+ else {
596
+ for (const entry of group.entries) {
597
+ await runEntry(label(entry), entry.scenarioName, entry.data, groupName);
598
+ }
539
599
  }
540
600
  }
601
+ finally {
602
+ if (group.after) {
603
+ try {
604
+ await runFeatureHook(group.after, featureContext);
605
+ }
606
+ catch (e) {
607
+ hookFailures.push(`feature '${groupName}' after hook failed: ${e?.message ?? e}`);
608
+ }
609
+ }
610
+ await databaseBaseline?.popFeatureLayer();
611
+ }
541
612
  }
542
- }
543
- await browserLifecycle.close();
544
- // Collected after the browser has closed, because a video is only finalised
545
- // when its context is — and renamed again by the encode that close() runs.
546
- // This is the first moment the answer is complete.
547
- const artifacts = browserLifecycle.artifacts();
548
- await runStore.attachArtifacts(captureRunId, artifacts);
549
- const failed = results.filter((r) => r.status === 'failed');
550
- await runStore.finish(captureRunId, {
551
- status: failed.length > 0 || hookFailures.length > 0 ? 'failed' : 'passed',
552
- finishedAt: new Date().toISOString(),
553
- skipped,
554
- hookFailures,
555
- });
556
- // A capture nobody can find is a capture nobody looks at, and looking at
557
- // them is the entire point of the flags. Announced only when the run
558
- // actually filed something: every run leaves a record behind, and most of
559
- // them have no images or footage to go with it.
560
- if (artifacts.length > 0) {
561
- logger.info(`Captures → ${join(capture.dir, capture.runId)}`);
562
- }
563
- if (coverage && Object.keys(scenarioCoverage).length > 0) {
564
- const coverageDir = join(resolve(config.rootDir, config.outDir), 'coverage');
565
- mkdirSync(coverageDir, { recursive: true });
566
- const outFile = join(coverageDir, 'scenario-coverage.json');
567
- writeFileSync(outFile, JSON.stringify({
568
- generatedAt: new Date().toISOString(),
569
- environment,
570
- scenarios: scenarioCoverage,
571
- }, null, 2) + '\n');
572
- logger.info(`Scenario coverage → ${outFile}`);
573
- }
574
- const report = { environment, results, skipped, hookFailures };
575
- for (const { level, text } of formatScenarioReport(report, {
576
- trace,
577
- projectRoot: config.rootDir,
578
- })) {
579
- const write = logger[level].bind(logger);
580
- write(text);
581
- }
582
- // How much of the run actually happened on the surface it targeted. Every
583
- // step counts, so a step that fell back to the server lowers the ratio
584
- // rather than needing a footnote. Assertions that fell back are named
585
- // separately — those are sentences claiming an observation nobody made.
586
- const surfaceCoverage = { onSurface: 0, total: 0 };
587
- const unwitnessed = new Set();
588
- for (const name of scenarioNames) {
589
- const scenario = scenarioSurfaceCoverage(state.workflows?.meta?.[name], functionsMeta, runSurface);
590
- surfaceCoverage.onSurface += scenario.onSurface;
591
- surfaceCoverage.total += scenario.total;
592
- for (const step of scenario.unwitnessed)
593
- unwitnessed.add(step);
594
- }
595
- if (runSurface !== 'default' && surfaceCoverage.total > 0) {
596
- const line = `${surfaceCoverage.onSurface}/${surfaceCoverage.total} steps ran on ${runSurface}`;
597
- if (unwitnessed.size === 0) {
598
- logger.info(line);
613
+ await browserLifecycle.close();
614
+ // Collected after the browser has closed, because a video is only finalised
615
+ // when its context is — and renamed again by the encode that close() runs.
616
+ // This is the first moment the answer is complete.
617
+ const artifacts = browserLifecycle.artifacts();
618
+ await runStore.attachArtifacts(captureRunId, artifacts);
619
+ const failed = results.filter((r) => r.status === 'failed');
620
+ await runStore.finish(captureRunId, {
621
+ status: failed.length > 0 || hookFailures.length > 0 ? 'failed' : 'passed',
622
+ finishedAt: new Date().toISOString(),
623
+ skipped,
624
+ hookFailures,
625
+ });
626
+ // A capture nobody can find is a capture nobody looks at, and looking at
627
+ // them is the entire point of the flags. Announced only when the run
628
+ // actually filed something: every run leaves a record behind, and most of
629
+ // them have no images or footage to go with it.
630
+ if (artifacts.length > 0) {
631
+ logger.info(`Captures → ${join(capture.dir, capture.runId)}`);
599
632
  }
600
- else {
601
- const write = logger[strict ? 'error' : 'warn'].bind(logger);
602
- write(`${line} — asserted server-side only: ${[...unwitnessed].join(', ')}`);
633
+ if (coverage && Object.keys(scenarioCoverage).length > 0) {
634
+ const coverageDir = join(resolve(config.rootDir, config.outDir), 'coverage');
635
+ mkdirSync(coverageDir, { recursive: true });
636
+ const outFile = join(coverageDir, 'scenario-coverage.json');
637
+ writeFileSync(outFile, JSON.stringify({
638
+ generatedAt: new Date().toISOString(),
639
+ environment,
640
+ scenarios: scenarioCoverage,
641
+ }, null, 2) + '\n');
642
+ logger.info(`Scenario coverage → ${outFile}`);
643
+ }
644
+ const report = { environment, results, skipped, hookFailures };
645
+ for (const { level, text } of formatScenarioReport(report, {
646
+ trace,
647
+ projectRoot: config.rootDir,
648
+ })) {
649
+ const write = logger[level].bind(logger);
650
+ write(text);
651
+ }
652
+ // How much of the run actually happened on the surface it targeted. Every
653
+ // step counts, so a step that fell back to the server lowers the ratio
654
+ // rather than needing a footnote. Assertions that fell back are named
655
+ // separately — those are sentences claiming an observation nobody made.
656
+ const surfaceCoverage = { onSurface: 0, total: 0 };
657
+ const unwitnessed = new Set();
658
+ for (const name of scenarioNames) {
659
+ const scenario = scenarioSurfaceCoverage(state.workflows?.meta?.[name], functionsMeta, runSurface);
660
+ surfaceCoverage.onSurface += scenario.onSurface;
661
+ surfaceCoverage.total += scenario.total;
662
+ for (const step of scenario.unwitnessed)
663
+ unwitnessed.add(step);
664
+ }
665
+ if (runSurface !== 'default' && surfaceCoverage.total > 0) {
666
+ const line = `${surfaceCoverage.onSurface}/${surfaceCoverage.total} steps ran on ${runSurface}`;
667
+ if (unwitnessed.size === 0) {
668
+ logger.info(line);
669
+ }
670
+ else {
671
+ const write = logger[strict ? 'error' : 'warn'].bind(logger);
672
+ write(`${line} — asserted server-side only: ${[...unwitnessed].join(', ')}`);
673
+ }
674
+ }
675
+ // Exiting 0 here makes "62 held back" and "62 passed" indistinguishable
676
+ // to CI, which is how a whole browser suite went unrun.
677
+ if (unrunnable.length > 0) {
678
+ logger.error(`${unrunnable.length} scenario(s) could not run on '${runSurface}' — no binding for that surface and no default to fall back to. ` +
679
+ `Run them on the surface they are written for (--run browser), or hold them back explicitly with --exclude-tags.`);
680
+ }
681
+ if (failed.length > 0 ||
682
+ hookFailures.length > 0 ||
683
+ unrunnable.length > 0 ||
684
+ (strict && unwitnessed.size > 0)) {
685
+ process.exitCode = 1;
603
686
  }
604
687
  }
605
- // Exiting 0 here makes "62 held back" and "62 passed" indistinguishable
606
- // to CI, which is how a whole browser suite went unrun.
607
- if (unrunnable.length > 0) {
608
- logger.error(`${unrunnable.length} scenario(s) could not run on '${runSurface}' — no binding for that surface and no default to fall back to. ` +
609
- `Run them on the surface they are written for (--run browser), or hold them back explicitly with --exclude-tags.`);
610
- }
611
- if (failed.length > 0 ||
612
- hookFailures.length > 0 ||
613
- unrunnable.length > 0 ||
614
- (strict && unwitnessed.size > 0)) {
615
- process.exitCode = 1;
688
+ finally {
689
+ if (databaseBaseline) {
690
+ // The copies are tables like any other, so a database left holding them
691
+ // reads as schema drift the next time anything introspects it.
692
+ try {
693
+ await databaseBaseline.drop();
694
+ }
695
+ catch (e) {
696
+ logger.warn(`scenario reset: could not drop the baseline copies: ${e?.message ?? e}`);
697
+ }
698
+ }
616
699
  }
617
700
  },
618
701
  });