@pikku/cli 0.12.152 → 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.
- package/console-app/assets/{HelpPanel-CxjgE40i.js → HelpPanel-0XQ4CyKC.js} +1 -1
- package/console-app/assets/{abnfDiagram-VCTEODGH-UPW1PlxM.js → abnfDiagram-VCTEODGH-DZTF_2aX.js} +1 -1
- package/console-app/assets/architecture-7GRP2DOG-NotVOJ5Y.js +1 -0
- package/console-app/assets/{architectureDiagram-5GKGNRK7-B3XFrvZk.js → architectureDiagram-5GKGNRK7-Re4-6X-k.js} +1 -1
- package/console-app/assets/{blockDiagram-I7D4REHJ-Ca0v9UJQ.js → blockDiagram-I7D4REHJ-Dbj7SnRK.js} +1 -1
- package/console-app/assets/{c4Diagram-7LVT6UL2-CLewVsj_.js → c4Diagram-7LVT6UL2-DUr02Wxh.js} +1 -1
- package/console-app/assets/channel-DU63_zXh.js +1 -0
- package/console-app/assets/{chunk-4HAMMTFA-BwhbhOD9.js → chunk-4HAMMTFA-DjatzNf_.js} +1 -1
- package/console-app/assets/{chunk-75Z2AOVW-C7nAgNk0.js → chunk-75Z2AOVW-BGsmWEdU.js} +1 -1
- package/console-app/assets/{chunk-DU6HZSFF-D4yYAcHt.js → chunk-DU6HZSFF-CC7WiVgI.js} +1 -1
- package/console-app/assets/{chunk-F27PBJKO-UHllOPps.js → chunk-F27PBJKO-BATuW9Ej.js} +1 -1
- package/console-app/assets/{chunk-GMAD6QVW-C0l_vm7T.js → chunk-GMAD6QVW-CgHNo00B.js} +1 -1
- package/console-app/assets/{chunk-GVQU2GXP-h-H2Oird.js → chunk-GVQU2GXP-VT_xckd9.js} +1 -1
- package/console-app/assets/{chunk-IMKFNOWR-DJ_W9CSg.js → chunk-IMKFNOWR-BhSVbWtF.js} +1 -1
- package/console-app/assets/{chunk-L3NEJ4N5-Ce9oniwE.js → chunk-L3NEJ4N5-HUF6a3Xl.js} +1 -1
- package/console-app/assets/{chunk-OSK3NFVY-CdGE5qxS.js → chunk-OSK3NFVY-C_LPDGIB.js} +1 -1
- package/console-app/assets/{chunk-P2QGCYS3-DK1pHDdz.js → chunk-P2QGCYS3-lWDBgw4Q.js} +1 -1
- package/console-app/assets/{chunk-POPQ4Y6H-DjevEFNo.js → chunk-POPQ4Y6H--6JTzmZJ.js} +1 -1
- package/console-app/assets/{chunk-PWAF6VOD-BVF5N1No.js → chunk-PWAF6VOD-C1GqaJJs.js} +1 -1
- package/console-app/assets/{chunk-SHT3W25Y-BX8We-wN.js → chunk-SHT3W25Y-Dd_4Zpdj.js} +1 -1
- package/console-app/assets/{chunk-SVP7TREG-Cl0GgAPC.js → chunk-SVP7TREG-B8OY7dfD.js} +1 -1
- package/console-app/assets/{chunk-TICWLB2K-IWZAWDhu.js → chunk-TICWLB2K-B8S7Hmnu.js} +1 -1
- package/console-app/assets/chunk-XXDRQBXY-BLuHuEZN.js +1 -0
- package/console-app/assets/classDiagram-ZZMXUADV-BTYfUi7u.js +1 -0
- package/console-app/assets/classDiagram-v2-VYDZK3BY-BTYfUi7u.js +1 -0
- package/console-app/assets/{cose-bilkent-JH36ORCC-GsQ0JAnL.js → cose-bilkent-JH36ORCC-C6RM8oPO.js} +1 -1
- package/console-app/assets/{cynefin-OW5HDTMX-hZsAZp_5.js → cynefin-OW5HDTMX-COh9rFYY.js} +1 -1
- package/console-app/assets/{cynefinDiagram-5FMLGOSQ-CsyMovx1.js → cynefinDiagram-5FMLGOSQ-Dd9AR17w.js} +1 -1
- package/console-app/assets/{dagre-BP8XEFz-.js → dagre-BDMvDAer.js} +1 -1
- package/console-app/assets/{dagre-GXQ25YYZ-Jg5285rx.js → dagre-GXQ25YYZ-C_nBUPgr.js} +1 -1
- package/console-app/assets/{diagram-S7CK7UJ4-n9JiR8HK.js → diagram-S7CK7UJ4-BGOobQeC.js} +1 -1
- package/console-app/assets/{diagram-UQ7AKVKN-Cde-dgbI.js → diagram-UQ7AKVKN-CAF_vcj0.js} +1 -1
- package/console-app/assets/{diagram-VSXAHHWV-CPZUg4FP.js → diagram-VSXAHHWV-q-aY0Nsc.js} +1 -1
- package/console-app/assets/{diagram-VX7I27RA-B2rCDWvn.js → diagram-VX7I27RA-CnUibSOe.js} +1 -1
- package/console-app/assets/{diagram-Z3DM3KII-UwO0hte3.js → diagram-Z3DM3KII-DY0V5kOJ.js} +1 -1
- package/console-app/assets/{ebnfDiagram-PWID7BFC-BGJ_PBlS.js → ebnfDiagram-PWID7BFC-CXp4CmEF.js} +1 -1
- package/console-app/assets/{erDiagram-RLTQ6QDP-CKsTScr3.js → erDiagram-RLTQ6QDP-CKnfXmPl.js} +1 -1
- package/console-app/assets/eventmodeling-NTZA5JFV-CBfQhnG8.js +1 -0
- package/console-app/assets/flowDiagram-HODETNUW-BnY3G7p6.js +1 -0
- package/console-app/assets/{ganttDiagram-EL5Y4UJY-koeTkIEA.js → ganttDiagram-EL5Y4UJY-D6v_RJ7o.js} +1 -1
- package/console-app/assets/{gitGraph-4MIJSDKK-DyRPbcM4.js → gitGraph-4MIJSDKK-y3kWDddY.js} +1 -1
- package/console-app/assets/{gitGraphDiagram-WWUBYQGX-DSIejrYw.js → gitGraphDiagram-WWUBYQGX-CBaE24KN.js} +1 -1
- package/console-app/assets/{graphlib-BpQ_697T.js → graphlib-DAA_TPmU.js} +1 -1
- package/console-app/assets/{index-Du9_fRSw.js → index-HuTd6kcC.js} +147 -147
- package/console-app/assets/{info-A6RAGUB7-C63f9an_.js → info-A6RAGUB7-BvxfRJ9-.js} +1 -1
- package/console-app/assets/{infoDiagram-27XIBGKW-Dz5gMcUF.js → infoDiagram-27XIBGKW-BlFmgW96.js} +1 -1
- package/console-app/assets/{ishikawaDiagram-5VMMS53U-m-nTAZTH.js → ishikawaDiagram-5VMMS53U-Dpwhnwwq.js} +1 -1
- package/console-app/assets/{journeyDiagram-3NMN7TZE-C7mjFY7i.js → journeyDiagram-3NMN7TZE-iOSeOQma.js} +1 -1
- package/console-app/assets/{kanban-definition-UXKFOSKX-Cm7PqOOP.js → kanban-definition-UXKFOSKX-DFGTB8S5.js} +1 -1
- package/console-app/assets/{line-RZNRGurC.js → line-B_5qZKoM.js} +1 -1
- package/console-app/assets/{linear-C2IGWMlF.js → linear-BmXUYu4J.js} +1 -1
- package/console-app/assets/{mermaid-parser.core-DN1yDqgB.js → mermaid-parser.core-DyPi3Lbr.js} +3 -3
- package/console-app/assets/{mermaid.core-DDRs3TbC.js → mermaid.core-S4y9jX8K.js} +4 -4
- package/console-app/assets/{mindmap-definition-YA3MSWOX-o6riOwlF.js → mindmap-definition-YA3MSWOX-Bl09mCaS.js} +1 -1
- package/console-app/assets/{packet-AYTQ26CC-B89F6gXG.js → packet-AYTQ26CC-B5zoasmU.js} +1 -1
- package/console-app/assets/{pegDiagram-XKGWAZYB-BQ1AAIaV.js → pegDiagram-XKGWAZYB-CAa0SOHf.js} +1 -1
- package/console-app/assets/{pie-WAS4IAKB-CzuwQlQz.js → pie-WAS4IAKB-C91xXCXY.js} +1 -1
- package/console-app/assets/{pieDiagram-E7YTZNPT-DlcGHWGT.js → pieDiagram-E7YTZNPT-CmUHVYwz.js} +1 -1
- package/console-app/assets/{quadrantDiagram-AXDQQJYC-DdITwqw-.js → quadrantDiagram-AXDQQJYC-XWsej4ss.js} +1 -1
- package/console-app/assets/{radar-RG4KPBEZ-DlJ29jAN.js → radar-RG4KPBEZ-B7ieCnSw.js} +1 -1
- package/console-app/assets/{railroad-74A4TZTK-BL49fdM8.js → railroad-74A4TZTK-KTKjMg4U.js} +1 -1
- package/console-app/assets/railroad-abnf-HS5TGJTU-e9AlsYmr.js +1 -0
- package/console-app/assets/railroad-ebnf-LZEXJU2U-BVX8UG1z.js +1 -0
- package/console-app/assets/railroad-peg-WCYAUIDC-BzWQFvD9.js +1 -0
- package/console-app/assets/{railroadDiagram-O6MQD6OU-BNgQGtF5.js → railroadDiagram-O6MQD6OU-oh1FjI2K.js} +1 -1
- package/console-app/assets/{requirementDiagram-BXWQKSXE-48lE_WeD.js → requirementDiagram-BXWQKSXE-D6z7hLfj.js} +1 -1
- package/console-app/assets/{sankeyDiagram-P5KCCOFB-WmTardCP.js → sankeyDiagram-P5KCCOFB-DFb92jfp.js} +1 -1
- package/console-app/assets/{sequenceDiagram-WJ2MYXX4-Bd5cHDMs.js → sequenceDiagram-WJ2MYXX4-BRKj-PG1.js} +1 -1
- package/console-app/assets/{src-BTXg_NtK.js → src-DYBVouJk.js} +1 -1
- package/console-app/assets/{stateDiagram-D77RDMKH-DBoCkqGv.js → stateDiagram-D77RDMKH-BfTLNw5t.js} +1 -1
- package/console-app/assets/stateDiagram-v2-MP3YSRHH-B8sMNnnF.js +1 -0
- package/console-app/assets/{swimlanes-42K2YHIH-lecUPBaK.js → swimlanes-42K2YHIH-DXkNYzX9.js} +1 -1
- package/console-app/assets/swimlanesDiagram-VR7AAH4N-CiZdLDyk.js +8 -0
- package/console-app/assets/{timeline-definition-24CTP7MA-CWq0OWm1.js → timeline-definition-24CTP7MA-B8m2V75P.js} +1 -1
- package/console-app/assets/{treeView-Q6P3EWNA-C7lEeqUz.js → treeView-Q6P3EWNA-BisqOzat.js} +1 -1
- package/console-app/assets/{treemap-WGGIJYW6-Bkx2DAoI.js → treemap-WGGIJYW6-DOxEXpb1.js} +1 -1
- package/console-app/assets/{vennDiagram-4TSXK5OY-Bw_kGRL3.js → vennDiagram-4TSXK5OY-ZeuU7o23.js} +1 -1
- package/console-app/assets/{wardley-WFR3VGLG-V0zki2L0.js → wardley-WFR3VGLG-sspeOMuz.js} +1 -1
- package/console-app/assets/{wardleyDiagram-VM6X3IG4-C5vrwEHM.js → wardleyDiagram-VM6X3IG4-CF2vzNJV.js} +1 -1
- package/console-app/assets/{xychartDiagram-S5SC5T6Z-B9uA0moC.js → xychartDiagram-S5SC5T6Z-BR9XBq3s.js} +1 -1
- package/console-app/index.html +1 -1
- package/dist/.pikku/addon/index.d.ts +1 -1
- package/dist/.pikku/addon/index.js +1 -1
- package/dist/.pikku/addon/pikku-addon-types.gen.d.ts +1 -1
- package/dist/.pikku/addon/pikku-addon-types.gen.js +1 -1
- package/dist/.pikku/agent/index.d.ts +1 -1
- package/dist/.pikku/agent/index.js +1 -1
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/agent/pikku-model-aliases.gen.js +1 -1
- package/dist/.pikku/analytics/index.d.ts +1 -1
- package/dist/.pikku/analytics/index.js +1 -1
- package/dist/.pikku/analytics/pikku-analytics-types.gen.d.ts +1 -1
- package/dist/.pikku/analytics/pikku-analytics-types.gen.js +1 -1
- package/dist/.pikku/auth/index.d.ts +1 -1
- package/dist/.pikku/auth/index.js +1 -1
- package/dist/.pikku/auth/pikku-auth-types.gen.d.ts +1 -1
- package/dist/.pikku/auth/pikku-auth-types.gen.js +1 -1
- package/dist/.pikku/channel/index.d.ts +1 -1
- package/dist/.pikku/channel/index.js +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/cli/index.d.ts +1 -1
- package/dist/.pikku/cli/index.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +16 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +56 -0
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/error/index.d.ts +1 -1
- package/dist/.pikku/error/index.js +1 -1
- package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
- package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
- package/dist/.pikku/function/index.d.ts +1 -1
- package/dist/.pikku/function/index.js +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +59 -2
- package/dist/.pikku/function/pikku-functions.gen.js +1 -1
- package/dist/.pikku/gateway/index.d.ts +1 -1
- package/dist/.pikku/gateway/index.js +1 -1
- package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
- package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
- package/dist/.pikku/http/index.d.ts +1 -1
- package/dist/.pikku/http/index.js +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/mcp/index.d.ts +1 -1
- package/dist/.pikku/mcp/index.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/middleware/index.d.ts +1 -1
- package/dist/.pikku/middleware/index.js +1 -1
- package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +1 -1
- package/dist/.pikku/middleware/pikku-middleware-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +1 -1
- package/dist/.pikku/queue/index.d.ts +1 -1
- package/dist/.pikku/queue/index.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +3 -0
- package/dist/.pikku/scenarios/index.d.ts +1 -1
- package/dist/.pikku/scenarios/index.js +1 -1
- package/dist/.pikku/scenarios/pikku-personas.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
- package/dist/.pikku/scheduler/index.d.ts +1 -1
- package/dist/.pikku/scheduler/index.js +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +13 -1
- package/dist/.pikku/schemas/schemas/ExamplesAddInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/ExamplesAddOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/ExamplesListInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/ExamplesListOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/ExamplesShowInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/ExamplesShowOutput.schema.json +1 -0
- package/dist/.pikku/scopes/index.d.ts +1 -1
- package/dist/.pikku/scopes/index.js +1 -1
- package/dist/.pikku/scopes/pikku-flags.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
- package/dist/.pikku/secrets/index.d.ts +1 -1
- package/dist/.pikku/secrets/index.js +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/setup/index.d.ts +1 -1
- package/dist/.pikku/setup/index.js +1 -1
- package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
- package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
- package/dist/.pikku/trigger/index.d.ts +1 -1
- package/dist/.pikku/trigger/index.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/index.d.ts +1 -1
- package/dist/.pikku/variables/index.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/index.d.ts +1 -1
- package/dist/.pikku/workflow/index.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/bin/pikku-bin.mjs +2 -2
- package/dist/src/cli.wiring.js +51 -0
- package/dist/src/examples.gen.d.ts +17 -0
- package/dist/src/examples.gen.js +7 -0
- package/dist/src/fabric/functions/validate.function.d.ts +34 -0
- package/dist/src/fabric/functions/validate.function.js +118 -0
- package/dist/src/fabric/lib/config.d.ts +1 -0
- package/dist/src/fabric/lib/config.js +1 -1
- package/dist/src/functions/commands/dev-address.d.ts +27 -0
- package/dist/src/functions/commands/dev-address.js +45 -0
- package/dist/src/functions/commands/dev.js +6 -0
- package/dist/src/functions/commands/environment.d.ts +8 -0
- package/dist/src/functions/commands/environment.js +15 -0
- package/dist/src/functions/commands/examples.d.ts +170 -0
- package/dist/src/functions/commands/examples.js +28 -0
- package/dist/src/functions/commands/scenario.js +356 -273
- package/dist/src/functions/db/local-db.d.ts +12 -0
- package/dist/src/functions/db/local-db.js +1 -1
- package/dist/src/functions/db/postgres/scenario-baseline.d.ts +3 -0
- package/dist/src/functions/db/postgres/scenario-baseline.js +112 -0
- package/dist/src/functions/db/scenario-baseline.d.ts +51 -0
- package/dist/src/functions/db/scenario-baseline.js +60 -0
- package/dist/src/functions/db/sqlite/scenario-baseline.d.ts +3 -0
- package/dist/src/functions/db/sqlite/scenario-baseline.js +77 -0
- package/dist/src/functions/examples/command-schemas.d.ts +69 -0
- package/dist/src/functions/examples/command-schemas.js +15 -0
- package/dist/src/functions/examples/corpus.d.ts +115 -0
- package/dist/src/functions/examples/corpus.js +307 -0
- package/dist/src/functions/examples/project.d.ts +64 -0
- package/dist/src/functions/examples/project.js +363 -0
- package/dist/src/functions/examples/render.d.ts +4 -0
- package/dist/src/functions/examples/render.js +101 -0
- package/dist/src/functions/examples/run.d.ts +9 -0
- package/dist/src/functions/examples/run.js +242 -0
- package/dist/src/functions/examples/schemas.d.ts +65 -0
- package/dist/src/functions/examples/schemas.js +76 -0
- package/dist/src/functions/surface/render-surface-doc.js +32 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -4
- package/snippets-meta.json +2 -0
- package/snippets.json +2 -0
- package/surface.json +1 -1
- package/console-app/assets/architecture-7GRP2DOG-BQN972Vc.js +0 -1
- package/console-app/assets/channel-B8xtOmL3.js +0 -1
- package/console-app/assets/chunk-XXDRQBXY-CGWcS5Ol.js +0 -1
- package/console-app/assets/classDiagram-ZZMXUADV-OGsmOlmO.js +0 -1
- package/console-app/assets/classDiagram-v2-VYDZK3BY-OGsmOlmO.js +0 -1
- package/console-app/assets/eventmodeling-NTZA5JFV-CnZOTLz3.js +0 -1
- package/console-app/assets/flowDiagram-HODETNUW-TYMCrte8.js +0 -1
- package/console-app/assets/railroad-abnf-HS5TGJTU-D9Ej_sin.js +0 -1
- package/console-app/assets/railroad-ebnf-LZEXJU2U-CjzlC3qv.js +0 -1
- package/console-app/assets/railroad-peg-WCYAUIDC-BPa6lIrj.js +0 -1
- package/console-app/assets/stateDiagram-v2-MP3YSRHH-BLbaZawL.js +0 -1
- 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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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 — '${
|
|
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
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
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
|
-
|
|
442
|
-
|
|
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:
|
|
512
|
+
error: e?.message ?? String(e),
|
|
448
513
|
});
|
|
449
514
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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
|
-
|
|
508
|
-
|
|
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
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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.
|
|
573
|
+
await runFeatureHook(group.before, featureContext);
|
|
574
|
+
await databaseBaseline?.pushFeatureLayer();
|
|
536
575
|
}
|
|
537
576
|
catch (e) {
|
|
538
|
-
|
|
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
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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
|
-
|
|
601
|
-
const
|
|
602
|
-
|
|
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
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
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
|
});
|