@pikku/cli 0.12.159 → 0.12.162

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 (255) hide show
  1. package/console-app/assets/{HelpPanel-C06T226y.js → HelpPanel-DR6oJReA.js} +1 -1
  2. package/console-app/assets/{abnfDiagram-VCTEODGH-BVwdT3Es.js → abnfDiagram-VCTEODGH-C6mt0F-I.js} +1 -1
  3. package/console-app/assets/architecture-7GRP2DOG-C3gcY2os.js +1 -0
  4. package/console-app/assets/{architectureDiagram-5GKGNRK7-XfzqZ3mW.js → architectureDiagram-5GKGNRK7-BPUTuzrJ.js} +1 -1
  5. package/console-app/assets/{blockDiagram-I7D4REHJ-B5omSqDK.js → blockDiagram-I7D4REHJ-IE8X3BZC.js} +1 -1
  6. package/console-app/assets/{c4Diagram-7LVT6UL2-DkPe7xCg.js → c4Diagram-7LVT6UL2--HhbjXSJ.js} +1 -1
  7. package/console-app/assets/channel-CGrTWPVh.js +1 -0
  8. package/console-app/assets/{chunk-4HAMMTFA-QfSXT_X7.js → chunk-4HAMMTFA-B2Cljp_-.js} +1 -1
  9. package/console-app/assets/{chunk-75Z2AOVW-h7UnPWNo.js → chunk-75Z2AOVW-D4684ofZ.js} +1 -1
  10. package/console-app/assets/{chunk-DU6HZSFF-VaMy-LOI.js → chunk-DU6HZSFF-BXipkMhY.js} +1 -1
  11. package/console-app/assets/{chunk-F27PBJKO-toywqOqS.js → chunk-F27PBJKO-BsSS2TqC.js} +1 -1
  12. package/console-app/assets/{chunk-GMAD6QVW-Cpa-YKLB.js → chunk-GMAD6QVW-DJqDw8yU.js} +1 -1
  13. package/console-app/assets/{chunk-GVQU2GXP-BWSHPLNL.js → chunk-GVQU2GXP-CguL9TtP.js} +1 -1
  14. package/console-app/assets/{chunk-IMKFNOWR-yhQzfQuj.js → chunk-IMKFNOWR-DoB5kv29.js} +1 -1
  15. package/console-app/assets/{chunk-L3NEJ4N5-C8pAfDlo.js → chunk-L3NEJ4N5-BspKBmrG.js} +1 -1
  16. package/console-app/assets/{chunk-OSK3NFVY-749yk2fa.js → chunk-OSK3NFVY-DzLs9ozF.js} +1 -1
  17. package/console-app/assets/{chunk-P2QGCYS3-C1Y9GyYT.js → chunk-P2QGCYS3-DAxcyv2x.js} +1 -1
  18. package/console-app/assets/{chunk-POPQ4Y6H-DzI9aRsX.js → chunk-POPQ4Y6H-DyLFn-sn.js} +1 -1
  19. package/console-app/assets/{chunk-PWAF6VOD-D3fAPSwz.js → chunk-PWAF6VOD-VPwoG6QJ.js} +1 -1
  20. package/console-app/assets/{chunk-SHT3W25Y-D9w7LzM9.js → chunk-SHT3W25Y-C0R9Ptyj.js} +1 -1
  21. package/console-app/assets/{chunk-SVP7TREG-D55MjOJw.js → chunk-SVP7TREG-cLgcNTyh.js} +1 -1
  22. package/console-app/assets/{chunk-TICWLB2K-DV8q7H-z.js → chunk-TICWLB2K-C3se3Npx.js} +1 -1
  23. package/console-app/assets/chunk-XXDRQBXY-DLdC0jYP.js +1 -0
  24. package/console-app/assets/classDiagram-ZZMXUADV-yt7BCzQS.js +1 -0
  25. package/console-app/assets/classDiagram-v2-VYDZK3BY-yt7BCzQS.js +1 -0
  26. package/console-app/assets/{cose-bilkent-JH36ORCC-CLedPW9a.js → cose-bilkent-JH36ORCC-L5Nhs9N1.js} +1 -1
  27. package/console-app/assets/{cynefin-OW5HDTMX-CS5NKw2p.js → cynefin-OW5HDTMX-jbgNKIs_.js} +1 -1
  28. package/console-app/assets/{cynefinDiagram-5FMLGOSQ-DGTHW90E.js → cynefinDiagram-5FMLGOSQ-D10NQDH4.js} +1 -1
  29. package/console-app/assets/{dagre-BNQdos_2.js → dagre-DrsKN-Kd.js} +1 -1
  30. package/console-app/assets/{dagre-GXQ25YYZ-DHZ8wtwQ.js → dagre-GXQ25YYZ-NDp4fO1z.js} +1 -1
  31. package/console-app/assets/{diagram-S7CK7UJ4-C41Begsy.js → diagram-S7CK7UJ4-B-9PMbK8.js} +1 -1
  32. package/console-app/assets/{diagram-UQ7AKVKN-CU8D-sRT.js → diagram-UQ7AKVKN-NXSn7aYB.js} +1 -1
  33. package/console-app/assets/{diagram-VSXAHHWV-DTTiXYnQ.js → diagram-VSXAHHWV-DrZgXRIZ.js} +1 -1
  34. package/console-app/assets/{diagram-VX7I27RA-BBy91pxm.js → diagram-VX7I27RA-V3OLFF3v.js} +1 -1
  35. package/console-app/assets/{diagram-Z3DM3KII-D50xTYe5.js → diagram-Z3DM3KII-CawCD3BR.js} +1 -1
  36. package/console-app/assets/{ebnfDiagram-PWID7BFC-BHjnFZgE.js → ebnfDiagram-PWID7BFC-QScQWlzV.js} +1 -1
  37. package/console-app/assets/{erDiagram-RLTQ6QDP-B4-yVo9b.js → erDiagram-RLTQ6QDP-DIhmCYb6.js} +1 -1
  38. package/console-app/assets/eventmodeling-NTZA5JFV-BbwPCOs2.js +1 -0
  39. package/console-app/assets/flowDiagram-HODETNUW-B-xUNJdv.js +1 -0
  40. package/console-app/assets/{ganttDiagram-EL5Y4UJY-D1T-2BlO.js → ganttDiagram-EL5Y4UJY-Bcg0H_3-.js} +1 -1
  41. package/console-app/assets/{gitGraph-4MIJSDKK-ClqKTUfQ.js → gitGraph-4MIJSDKK-BUYXp4LH.js} +1 -1
  42. package/console-app/assets/{gitGraphDiagram-WWUBYQGX-ruq2MAhM.js → gitGraphDiagram-WWUBYQGX-DRxwgliX.js} +1 -1
  43. package/console-app/assets/{graphlib-D-vLYPdw.js → graphlib-BXZn3TnO.js} +1 -1
  44. package/console-app/assets/{index-BIEZPuyK.js → index-D7ATEpJh.js} +27 -27
  45. package/console-app/assets/{info-A6RAGUB7-BGI-8HNB.js → info-A6RAGUB7-BAfA_BVB.js} +1 -1
  46. package/console-app/assets/{infoDiagram-27XIBGKW-BGURgKHv.js → infoDiagram-27XIBGKW-JPbCbDjB.js} +1 -1
  47. package/console-app/assets/{ishikawaDiagram-5VMMS53U-DzgAaPTR.js → ishikawaDiagram-5VMMS53U-528xNFds.js} +1 -1
  48. package/console-app/assets/{journeyDiagram-3NMN7TZE-BsyWad8z.js → journeyDiagram-3NMN7TZE-CpnC76my.js} +1 -1
  49. package/console-app/assets/{kanban-definition-UXKFOSKX-g1nO2rWF.js → kanban-definition-UXKFOSKX-7v_OexVf.js} +1 -1
  50. package/console-app/assets/{line-CeFoQ5y-.js → line-BXTtRzTv.js} +1 -1
  51. package/console-app/assets/{linear-C4Pk-YMX.js → linear-BXIxFuy8.js} +1 -1
  52. package/console-app/assets/{mermaid-parser.core-U7wquvx8.js → mermaid-parser.core-Bn81UzgU.js} +3 -3
  53. package/console-app/assets/{mermaid.core-8TRpFRm9.js → mermaid.core-CShTFU_y.js} +4 -4
  54. package/console-app/assets/{mindmap-definition-YA3MSWOX-C7lzwqBX.js → mindmap-definition-YA3MSWOX-Dozk2qNE.js} +1 -1
  55. package/console-app/assets/{packet-AYTQ26CC-DiQV_rU3.js → packet-AYTQ26CC-CQ1T9gCW.js} +1 -1
  56. package/console-app/assets/{pegDiagram-XKGWAZYB-63ajP7-W.js → pegDiagram-XKGWAZYB-CAKNYwkF.js} +1 -1
  57. package/console-app/assets/{pie-WAS4IAKB-B5q_o6dr.js → pie-WAS4IAKB-XBhMekP0.js} +1 -1
  58. package/console-app/assets/{pieDiagram-E7YTZNPT-BTbvj-by.js → pieDiagram-E7YTZNPT-CQH9g9Im.js} +1 -1
  59. package/console-app/assets/{quadrantDiagram-AXDQQJYC-CulqsAYd.js → quadrantDiagram-AXDQQJYC-BYry9NEL.js} +1 -1
  60. package/console-app/assets/{radar-RG4KPBEZ-CW_KoURS.js → radar-RG4KPBEZ-BHk3GM2h.js} +1 -1
  61. package/console-app/assets/{railroad-74A4TZTK-CUmF6uBQ.js → railroad-74A4TZTK-B-GsJq3u.js} +1 -1
  62. package/console-app/assets/railroad-abnf-HS5TGJTU-Dqco3Udm.js +1 -0
  63. package/console-app/assets/railroad-ebnf-LZEXJU2U-DZqyOzxq.js +1 -0
  64. package/console-app/assets/railroad-peg-WCYAUIDC-DtfiBwRd.js +1 -0
  65. package/console-app/assets/{railroadDiagram-O6MQD6OU-WlkBu_N3.js → railroadDiagram-O6MQD6OU-CAIv58C8.js} +1 -1
  66. package/console-app/assets/{requirementDiagram-BXWQKSXE-CJK_CbPR.js → requirementDiagram-BXWQKSXE-DJ3f5870.js} +1 -1
  67. package/console-app/assets/{sankeyDiagram-P5KCCOFB-C466WOH8.js → sankeyDiagram-P5KCCOFB-YrQnQsb7.js} +1 -1
  68. package/console-app/assets/{sequenceDiagram-WJ2MYXX4-CV0dCcj0.js → sequenceDiagram-WJ2MYXX4-B-tyzF2R.js} +1 -1
  69. package/console-app/assets/{src-P4Nw8sY0.js → src-DV6LWVlf.js} +1 -1
  70. package/console-app/assets/{stateDiagram-D77RDMKH-HaLGm8V9.js → stateDiagram-D77RDMKH-vMePRkxV.js} +1 -1
  71. package/console-app/assets/stateDiagram-v2-MP3YSRHH-Cojsq9SW.js +1 -0
  72. package/console-app/assets/{swimlanes-42K2YHIH-Bi-O5vWa.js → swimlanes-42K2YHIH-CqQsTH0R.js} +1 -1
  73. package/console-app/assets/swimlanesDiagram-VR7AAH4N-0DUuvodn.js +8 -0
  74. package/console-app/assets/{timeline-definition-24CTP7MA-B4h-gx-i.js → timeline-definition-24CTP7MA-D3OGq7c8.js} +1 -1
  75. package/console-app/assets/{treeView-Q6P3EWNA-CXJ_pHQw.js → treeView-Q6P3EWNA-DZA143I6.js} +1 -1
  76. package/console-app/assets/{treemap-WGGIJYW6-M84p2a_3.js → treemap-WGGIJYW6-N5Dr2J8o.js} +1 -1
  77. package/console-app/assets/{vennDiagram-4TSXK5OY-BesF4k_r.js → vennDiagram-4TSXK5OY-CjQrrdPE.js} +1 -1
  78. package/console-app/assets/{wardley-WFR3VGLG-n9ulickP.js → wardley-WFR3VGLG-CWrUebhA.js} +1 -1
  79. package/console-app/assets/{wardleyDiagram-VM6X3IG4-BG0nnWDf.js → wardleyDiagram-VM6X3IG4-Bd-nerY3.js} +1 -1
  80. package/console-app/assets/{xychartDiagram-S5SC5T6Z-BtOW7fpF.js → xychartDiagram-S5SC5T6Z-CK3-JaeF.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 +6 -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-types.gen.d.ts +1 -1
  110. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  111. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  112. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +25 -0
  113. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  114. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  115. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  116. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  117. package/dist/.pikku/error/index.d.ts +1 -1
  118. package/dist/.pikku/error/index.js +1 -1
  119. package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
  120. package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
  121. package/dist/.pikku/function/index.d.ts +1 -1
  122. package/dist/.pikku/function/index.js +1 -1
  123. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  124. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  125. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  126. package/dist/.pikku/function/pikku-functions-meta.gen.json +22 -5
  127. package/dist/.pikku/function/pikku-functions.gen.js +1 -1
  128. package/dist/.pikku/gateway/index.d.ts +1 -1
  129. package/dist/.pikku/gateway/index.js +1 -1
  130. package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
  131. package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
  132. package/dist/.pikku/http/index.d.ts +1 -1
  133. package/dist/.pikku/http/index.js +1 -1
  134. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  135. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  136. package/dist/.pikku/mcp/index.d.ts +1 -1
  137. package/dist/.pikku/mcp/index.js +1 -1
  138. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  139. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  140. package/dist/.pikku/middleware/index.d.ts +1 -1
  141. package/dist/.pikku/middleware/index.js +1 -1
  142. package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +1 -1
  143. package/dist/.pikku/middleware/pikku-middleware-types.gen.js +1 -1
  144. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  145. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
  146. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  147. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  148. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  149. package/dist/.pikku/queue/index.d.ts +1 -1
  150. package/dist/.pikku/queue/index.js +1 -1
  151. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  152. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  153. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  154. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  155. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  156. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  157. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -0
  158. package/dist/.pikku/scenarios/index.d.ts +1 -1
  159. package/dist/.pikku/scenarios/index.js +1 -1
  160. package/dist/.pikku/scenarios/pikku-personas.gen.js +1 -1
  161. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  162. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  163. package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +7 -1
  164. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  165. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  166. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  167. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  168. package/dist/.pikku/scheduler/index.d.ts +1 -1
  169. package/dist/.pikku/scheduler/index.js +1 -1
  170. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  171. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  172. package/dist/.pikku/schemas/register.gen.js +3 -1
  173. package/dist/.pikku/schemas/schemas/ScenarioGuideInput.schema.json +1 -0
  174. package/dist/.pikku/scopes/index.d.ts +1 -1
  175. package/dist/.pikku/scopes/index.js +1 -1
  176. package/dist/.pikku/scopes/pikku-flags.gen.d.ts +1 -1
  177. package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
  178. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
  179. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  180. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  181. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  182. package/dist/.pikku/secrets/index.d.ts +1 -1
  183. package/dist/.pikku/secrets/index.js +1 -1
  184. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  185. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  186. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  187. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  188. package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
  189. package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
  190. package/dist/.pikku/setup/index.d.ts +1 -1
  191. package/dist/.pikku/setup/index.js +1 -1
  192. package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
  193. package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
  194. package/dist/.pikku/trigger/index.d.ts +1 -1
  195. package/dist/.pikku/trigger/index.js +1 -1
  196. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  197. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  198. package/dist/.pikku/variables/index.d.ts +1 -1
  199. package/dist/.pikku/variables/index.js +1 -1
  200. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  201. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  202. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  203. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  204. package/dist/.pikku/workflow/index.d.ts +1 -1
  205. package/dist/.pikku/workflow/index.js +1 -1
  206. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  207. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  208. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  209. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  210. package/dist/bin/pikku-bin.mjs +2 -2
  211. package/dist/src/cli.wiring.js +25 -1
  212. package/dist/src/deploy/analyzer/analyzer.js +7 -1
  213. package/dist/src/deploy/build-pipeline.d.ts +2 -0
  214. package/dist/src/deploy/build-pipeline.js +4 -3
  215. package/dist/src/functions/commands/deploy-apply.js +1 -0
  216. package/dist/src/functions/commands/deploy-plan.js +1 -0
  217. package/dist/src/functions/commands/scenario-guide.d.ts +184 -0
  218. package/dist/src/functions/commands/scenario-guide.js +313 -0
  219. package/dist/src/functions/commands/scenario-ladder.d.ts +3 -1
  220. package/dist/src/functions/commands/scenario-ladder.js +1 -0
  221. package/dist/src/functions/commands/scenario-plan.d.ts +17 -1
  222. package/dist/src/functions/commands/scenario-plan.js +15 -0
  223. package/dist/src/functions/commands/scenario.d.ts +30 -0
  224. package/dist/src/functions/commands/scenario.js +186 -22
  225. package/dist/src/functions/commands/serve.js +7 -2
  226. package/dist/src/functions/surface/collect-snippets.d.ts +1 -13
  227. package/dist/src/functions/surface/collect-snippets.js +1 -83
  228. package/dist/src/functions/validate/type-identity-checks.d.ts +3 -2
  229. package/dist/src/functions/validate/type-identity-checks.js +74 -5
  230. package/dist/src/functions/validate/validate-registry.js +3 -3
  231. package/dist/src/functions/wirings/auth/serialize-auth-gen.js +33 -1
  232. package/dist/src/functions/wirings/functions/serialize-addon-types.js +4 -2
  233. package/dist/src/functions/wirings/rpc/pikku-command-react-query.js +3 -2
  234. package/dist/src/functions/wirings/rpc/serialize-react-query-hooks.d.ts +1 -1
  235. package/dist/src/functions/wirings/rpc/serialize-react-query-hooks.js +54 -3
  236. package/dist/src/functions/wirings/scenarios/serialize-feature-meta.js +1 -0
  237. package/dist/src/functions/wirings/workflow/serialize-scenario-types.js +6 -0
  238. package/dist/src/services.js +3 -1
  239. package/dist/tsconfig.tsbuildinfo +1 -1
  240. package/package.json +10 -10
  241. package/snippets-meta.json +1 -0
  242. package/snippets.json +1 -0
  243. package/surface.json +7 -3
  244. package/console-app/assets/architecture-7GRP2DOG-3y7bWtag.js +0 -1
  245. package/console-app/assets/channel-CTiluQVw.js +0 -1
  246. package/console-app/assets/chunk-XXDRQBXY-DBtYQykd.js +0 -1
  247. package/console-app/assets/classDiagram-ZZMXUADV-BnWa-i9n.js +0 -1
  248. package/console-app/assets/classDiagram-v2-VYDZK3BY-BnWa-i9n.js +0 -1
  249. package/console-app/assets/eventmodeling-NTZA5JFV-C_IpFSCw.js +0 -1
  250. package/console-app/assets/flowDiagram-HODETNUW-Q90rgVpx.js +0 -1
  251. package/console-app/assets/railroad-abnf-HS5TGJTU-CcPw9q8a.js +0 -1
  252. package/console-app/assets/railroad-ebnf-LZEXJU2U-Ci_fZics.js +0 -1
  253. package/console-app/assets/railroad-peg-WCYAUIDC-DTsDykSf.js +0 -1
  254. package/console-app/assets/stateDiagram-v2-MP3YSRHH--GM14Hz5.js +0 -1
  255. package/console-app/assets/swimlanesDiagram-VR7AAH4N-_6rRi6Wt.js +0 -8
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { resolve, join } from 'node:path';
3
- import { mkdirSync, writeFileSync } from 'node:fs';
2
+ import { resolve, join, dirname, relative, sep } from 'node:path';
3
+ import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
4
+ import { glob } from 'tinyglobby';
4
5
  import { pikkuSessionlessFunc } from '#pikku/function';
5
6
  import { InMemoryWorkflowService } from '@pikku/core/services';
6
7
  import { FileScenarioRunStore } from '@pikku/core/services';
@@ -14,7 +15,8 @@ import { resolveScenarioBrowserProvider, scenarioBrowserLifecycle, } from './sce
14
15
  import { resolvePersonas } from '../../utils/resolve-personas.js';
15
16
  import { resolvePersonaCredentials } from '../../utils/persona-credentials.js';
16
17
  import { spawnDevServer } from '../../server/spawn-dev-server.js';
17
- import { buildScenarioPlan } from './scenario-plan.js';
18
+ import { checkGuideCoverage, featureEvidence, guideStep, parseGuideLock, parseGuidePage, renderGuideLock, renderGuidePage, } from './scenario-guide.js';
19
+ import { buildScenarioPlan, identifyScenarioResult } from './scenario-plan.js';
18
20
  import { resolveEnvironment, isLocalUrl } from './environment.js';
19
21
  import { readDevAddress } from './dev-address.js';
20
22
  import { createDevAgentRunner } from './dev-agent-runner.js';
@@ -354,11 +356,18 @@ export const scenarioRun = pikkuSessionlessFunc({
354
356
  const runStore = new FileScenarioRunStore({ dir: captureDir });
355
357
  const startedAtIso = new Date().toISOString();
356
358
  try {
359
+ const selection = {
360
+ ...(split(flows) ? { flows: split(flows) } : {}),
361
+ ...(split(features) ? { features: split(features) } : {}),
362
+ ...(split(tags) ? { tags: split(tags) } : {}),
363
+ ...(split(excludeTags) ? { excludeTags: split(excludeTags) } : {}),
364
+ };
357
365
  await runStore.start({
358
366
  runId: captureRunId,
359
367
  environment,
360
368
  surface: runSurface,
361
369
  status: 'running',
370
+ ...(Object.keys(selection).length > 0 ? { selection } : {}),
362
371
  startedAt: startedAtIso,
363
372
  results: [],
364
373
  skipped,
@@ -371,6 +380,11 @@ export const scenarioRun = pikkuSessionlessFunc({
371
380
  * is the formatter's job.
372
381
  */
373
382
  const readRunSteps = async (service, runId, flowName) => {
383
+ // Taken from the run rather than derived from the ladder: the video
384
+ // clock starts when an actor's window opens, which is somewhere after
385
+ // step one, and every step that never touched a browser burns scenario
386
+ // time while the recording sits still.
387
+ const videoOffsets = scenarioService.takeStepVideoOffsets(runId);
374
388
  const prose = collectScenarioStepProse(state.workflows?.meta?.[flowName], functionsMeta, state.personas?.definitions ?? []);
375
389
  const steps = (await service.getRunSteps(runId)).map((step) => ({
376
390
  stepName: step.stepName,
@@ -383,6 +397,7 @@ export const scenarioRun = pikkuSessionlessFunc({
383
397
  expected: step.error?.expected,
384
398
  input: step.data,
385
399
  stepFunc: step.rpcName,
400
+ video: videoOffsets.get(step.stepName),
386
401
  }));
387
402
  return {
388
403
  rows: scenarioStepRows(steps, prose),
@@ -427,33 +442,29 @@ export const scenarioRun = pikkuSessionlessFunc({
427
442
  };
428
443
  const hookFailures = [];
429
444
  /**
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
445
+ * Which registration ran and which feature grouped it, snapshotted into
446
+ * the record because a run read back next week is describing a suite
433
447
  * whose source has moved on.
434
448
  */
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) => {
449
+ const identityOf = (scenarioName, group) => ({
450
+ scenarioName,
451
+ featureId: group?.featureId,
452
+ featureName: group?.featureName,
453
+ tags: state.workflows?.meta?.[scenarioName]?.tags,
454
+ });
455
+ const runEntry = async (label, scenarioName, data, identity) => {
445
456
  const startedAt = Date.now();
446
457
  if (databaseBaseline) {
447
458
  try {
448
459
  await databaseBaseline.restore();
449
460
  }
450
461
  catch (e) {
451
- const result = identify({
462
+ const result = identifyScenarioResult({
452
463
  name: label,
453
464
  status: 'failed',
454
465
  durationMs: Date.now() - startedAt,
455
466
  error: `database reset failed: ${e?.message ?? e}`,
456
- }, scenarioName, feature);
467
+ }, identity);
457
468
  results.push(result);
458
469
  await runStore.recordScenario(captureRunId, result);
459
470
  return;
@@ -534,7 +545,7 @@ export const scenarioRun = pikkuSessionlessFunc({
534
545
  // Told here, acted on at the next scenario's reset — that is what closes
535
546
  // these windows and finalises the video this outcome decides the fate of.
536
547
  browserLifecycle.endScenario(result.status);
537
- Object.assign(result, identify(result, scenarioName, feature));
548
+ Object.assign(result, identifyScenarioResult(result, identity));
538
549
  await runStore.recordScenario(captureRunId, result);
539
550
  if (coverageActive) {
540
551
  const report = await invokeCoverage('pikkuScenarioTakeLiveCoverage');
@@ -582,19 +593,19 @@ export const scenarioRun = pikkuSessionlessFunc({
582
593
  // Setup failed, so nothing in the group ran. Reporting them as failed
583
594
  // rather than skipped is the honest reading: they did not pass.
584
595
  for (const entry of group.entries) {
585
- const result = identify({
596
+ const result = identifyScenarioResult({
586
597
  name: label(entry),
587
598
  status: 'failed',
588
599
  durationMs: 0,
589
600
  error: `${groupName ? `feature '${groupName}' ` : ''}${beforeStage} failed: ${beforeError?.message ?? beforeError}`,
590
- }, entry.scenarioName, groupName);
601
+ }, identityOf(entry.scenarioName, group));
591
602
  results.push(result);
592
603
  await runStore.recordScenario(captureRunId, result);
593
604
  }
594
605
  }
595
606
  else {
596
607
  for (const entry of group.entries) {
597
- await runEntry(label(entry), entry.scenarioName, entry.data, groupName);
608
+ await runEntry(label(entry), entry.scenarioName, entry.data, identityOf(entry.scenarioName, group));
598
609
  }
599
610
  }
600
611
  }
@@ -699,3 +710,156 @@ export const scenarioRun = pikkuSessionlessFunc({
699
710
  }
700
711
  },
701
712
  });
713
+ /**
714
+ * `pikku scenario guide` — the suite, written out as markdown.
715
+ *
716
+ * Structure comes from the registry (which features exist, what their scenarios
717
+ * are called), and evidence from the latest run record (what the steps actually
718
+ * said, and what they filed). Editorial prose comes from the project's own
719
+ * `docs/` sources, which cite a feature by leaving the marker pair where its
720
+ * block belongs; this merges the three and writes one markdown file per source.
721
+ * It renders nothing, resolves no asset URLs and calls no model: the writing is
722
+ * somebody else's concern, and this is the compiler that keeps it honest.
723
+ */
724
+ export const scenarioGuide = pikkuSessionlessFunc({
725
+ func: async ({ logger, config, getInspectorState }, { docs = 'docs', output, runId, allowUndocumented = false, artifactBase }) => {
726
+ const state = await getInspectorState(false, false, false, true);
727
+ const outDir = resolve(config.rootDir, config.outDir);
728
+ await loadScenarioBootstrap(outDir);
729
+ const { features: registeredFeatures } = collectRegisteredWirings();
730
+ const runsDir = join(outDir, 'scenario-runs');
731
+ const runStore = new FileScenarioRunStore({ dir: runsDir });
732
+ const latest = runId ?? (await runStore.list({ limit: 1 }))[0]?.runId;
733
+ const record = latest ? await runStore.get(latest) : undefined;
734
+ if (!record) {
735
+ logger.error(runId
736
+ ? `No run '${runId}' under ${runsDir}.`
737
+ : `No scenario run under ${runsDir} — run \`pikku scenario run <environment> --screenshots\` first, since a guide is written out of what a run recorded.`);
738
+ process.exitCode = 1;
739
+ return;
740
+ }
741
+ if (record.status !== 'passed') {
742
+ logger.error(`Run '${record.runId}' is ${record.status}. A guide is a claim that the product does what the page says, so it is only ever written out of a run that passed.`);
743
+ process.exitCode = 1;
744
+ return;
745
+ }
746
+ if (record.selection) {
747
+ const narrowed = Object.entries(record.selection)
748
+ .map(([flag, values]) => `--${flag} ${values.join(',')}`)
749
+ .join(' ');
750
+ logger.error(`Run '${record.runId}' was narrowed (${narrowed}), so it is missing scenarios the suite has. Guide pages would be written as though those flows do not exist — run the whole suite, or pass --run-id for one that was.`);
751
+ process.exitCode = 1;
752
+ return;
753
+ }
754
+ const workflowsMeta = state.workflows?.meta ?? {};
755
+ const features = [...registeredFeatures].map(([id, feature]) => {
756
+ const name = feature.name ?? id;
757
+ return {
758
+ id,
759
+ name,
760
+ ...(feature.description ? { description: feature.description } : {}),
761
+ document: feature.document !== false,
762
+ scenarios: record.results
763
+ // By id, never by the display name: a title is rewritten freely,
764
+ // and two features are allowed to share one.
765
+ .filter((result) => result.featureId === id ||
766
+ (result.featureId === undefined && result.feature === name))
767
+ .map((result) => {
768
+ const meta = result.scenarioName
769
+ ? workflowsMeta[result.scenarioName]
770
+ : undefined;
771
+ return {
772
+ name: result.scenarioName ?? result.name,
773
+ title: meta?.title ?? result.name,
774
+ ...(meta?.description ? { description: meta.description } : {}),
775
+ steps: (result.steps ?? []).map((step) => guideStep(step.sentence)),
776
+ screenshots: (result.artifacts ?? [])
777
+ .filter((artifact) => artifact.kind === 'screenshot')
778
+ .map((artifact) => ({
779
+ ...(artifact.id ? { id: artifact.id } : {}),
780
+ ...(artifact.name ? { name: artifact.name } : {}),
781
+ path: artifact.path,
782
+ })),
783
+ videos: (result.artifacts ?? [])
784
+ .filter((artifact) => artifact.kind === 'video')
785
+ .map((artifact) => ({
786
+ ...(artifact.id ? { id: artifact.id } : {}),
787
+ ...(artifact.actor ? { actor: artifact.actor } : {}),
788
+ path: artifact.path,
789
+ })),
790
+ };
791
+ }),
792
+ };
793
+ });
794
+ const docsDir = resolve(config.rootDir, docs);
795
+ const sources = (await glob('**/*.md', { cwd: docsDir, onlyFiles: true })).sort();
796
+ const pages = [];
797
+ for (const source of sources) {
798
+ pages.push(parseGuidePage(source, readFileSync(join(docsDir, source), 'utf-8')));
799
+ }
800
+ const lockPath = join(docsDir, '.guide.lock');
801
+ const lock = existsSync(lockPath)
802
+ ? parseGuideLock(readFileSync(lockPath, 'utf-8'))
803
+ : {};
804
+ const coverage = checkGuideCoverage(features, pages, lock);
805
+ for (const { path, featureId } of coverage.unknown) {
806
+ logger.error(`${join(docs, path)} cites '${featureId}', which is not a registered feature — a page describing something that no longer exists.`);
807
+ }
808
+ for (const { path, featureId } of coverage.optedOut) {
809
+ logger.error(`${join(docs, path)} cites '${featureId}', which declares \`document: false\`.`);
810
+ }
811
+ for (const { path, featureId } of coverage.figureless) {
812
+ logger.warn(`${join(docs, path)} cites '${featureId}', whose run filed no screenshot — the block renders empty. Take one with \`actor.screenshot(...)\` in a scenario the feature owns.`);
813
+ }
814
+ for (const { path, featureId, locked, current } of coverage.stale) {
815
+ logger.warn(`${join(docs, path)} was written against '${featureId}' at ${locked}, which is now ${current} — the flow moved, so re-read the prose around that block.`);
816
+ }
817
+ for (const featureId of coverage.missing) {
818
+ const message = `Feature '${featureId}' is cited by no page. Place \`<!-- pikku:guide feature=${featureId} -->\` and \`<!-- /pikku:guide -->\` in a page under ${docs}/, or set \`document: false\` on the feature.`;
819
+ if (allowUndocumented) {
820
+ logger.warn(message);
821
+ }
822
+ else {
823
+ logger.error(message);
824
+ }
825
+ }
826
+ // A compiler that has found an error does not emit. Undocumented features
827
+ // are the one failure that can be downgraded, because a suite mid-way
828
+ // through being written still wants its pages built.
829
+ if (coverage.unknown.length > 0 ||
830
+ coverage.optedOut.length > 0 ||
831
+ (!allowUndocumented && coverage.missing.length > 0)) {
832
+ process.exitCode = 1;
833
+ return;
834
+ }
835
+ const byId = new Map(features.map((feature) => [feature.id, feature]));
836
+ const outputDir = output
837
+ ? resolve(config.rootDir, output)
838
+ : join(outDir, 'guide');
839
+ const artifactRoot = join(runsDir, record.runId);
840
+ for (const page of pages) {
841
+ const target = join(outputDir, page.path);
842
+ // Relative, forward-slashed and computed per page: a guide is markdown
843
+ // with ordinary image refs, and whoever consumes it rewrites the paths.
844
+ // `artifactBase` replaces it with one prefix for every page, for a host
845
+ // that serves the artifacts at a fixed address rather than beside the
846
+ // markdown — and it stays as given, since only the caller knows whether
847
+ // it is a path, a route or an origin.
848
+ const base = artifactBase
849
+ ? artifactBase.endsWith('/')
850
+ ? artifactBase
851
+ : `${artifactBase}/`
852
+ : `${relative(dirname(target), artifactRoot).split(sep).join('/')}/`;
853
+ const markdown = renderGuidePage(page, byId, base);
854
+ mkdirSync(dirname(target), { recursive: true });
855
+ writeFileSync(target, markdown);
856
+ }
857
+ writeFileSync(lockPath, renderGuideLock(features, coverage.cited));
858
+ logger.info(`${pages.length} page(s) → ${outputDir} (run ${record.runId}, ${coverage.cited.length} documented feature(s))`);
859
+ for (const feature of features) {
860
+ if (feature.document) {
861
+ logger.debug(` ${feature.id} ${featureEvidence(feature)}`);
862
+ }
863
+ }
864
+ },
865
+ });
@@ -180,8 +180,13 @@ export const serve = pikkuSessionlessFunc({
180
180
  ? await resolveFrontendMount(config.frontend)
181
181
  : undefined;
182
182
  // The console goes first so a frontend mounted at `/` cannot claim
183
- // `/console` before the console's own mount is offered the request.
184
- const staticMounts = [consoleMount, frontendMount].filter((mount) => Boolean(mount));
183
+ // `/console` before the console's own mount is offered the request, and
184
+ // the app's own mounts are kept ahead of the catch-all frontend.
185
+ const staticMounts = [
186
+ ...(consoleMount ? [consoleMount] : []),
187
+ ...(userConfig.staticMounts ?? []),
188
+ ...(frontendMount ? [frontendMount] : []),
189
+ ];
185
190
  const pikkuServer = devServerRunner.createServer({
186
191
  ...userConfig,
187
192
  hostname: bindHostname,
@@ -1,13 +1 @@
1
- export declare class UnclosedSnippetError extends Error {
2
- constructor(name: string, file: string);
3
- }
4
- export declare class DuplicateSnippetError extends Error {
5
- constructor(name: string, file: string, other: string);
6
- }
7
- /**
8
- * The `// @snippet start <name>` regions in a project's source, so an example
9
- * in a doc comment can name one instead of restating it. The same mechanism the
10
- * website uses to pull its code blocks out of the shop template: the code that
11
- * reaches the reader is the code that compiles, and it cannot drift.
12
- */
13
- export declare const collectSnippets: (projectDir: string, into?: Map<string, string>, origins?: Map<string, string>) => Promise<Map<string, string>>;
1
+ export { collectSnippets, UnclosedSnippetError, DuplicateSnippetError, } from '@pikku/skills';
@@ -1,83 +1 @@
1
- import { readdir, readFile } from 'node:fs/promises';
2
- import { join, relative } from 'node:path';
3
- const START = /^\s*(?:\/\/|--)\s*@snippet start (\S+)\s*$/;
4
- const END = /^\s*(?:\/\/|--)\s*@snippet end (\S+)\s*$/;
5
- const SOURCE_EXTENSIONS = ['.ts', '.tsx', '.sql'];
6
- const SKIPPED_DIRECTORIES = new Set(['node_modules', '.pikku', 'dist', '.git']);
7
- export class UnclosedSnippetError extends Error {
8
- constructor(name, file) {
9
- super(`The snippet "${name}" opens in ${file} and never closes. Add "// @snippet end ${name}".`);
10
- this.name = 'UnclosedSnippetError';
11
- }
12
- }
13
- export class DuplicateSnippetError extends Error {
14
- constructor(name, file, other) {
15
- super(`The snippet "${name}" is defined twice, in ${other} and ${file}. Snippet names are the reference an example uses, so they have to be unique.`);
16
- this.name = 'DuplicateSnippetError';
17
- }
18
- }
19
- const dedent = (lines) => {
20
- const indents = lines
21
- .filter((line) => line.trim().length > 0)
22
- .map((line) => line.length - line.trimStart().length);
23
- const shortest = indents.length > 0 ? Math.min(...indents) : 0;
24
- return lines
25
- .map((line) => line.slice(shortest))
26
- .join('\n')
27
- .trim();
28
- };
29
- const sourceFilesIn = async (directory, found = []) => {
30
- for (const entry of await readdir(directory, { withFileTypes: true })) {
31
- const full = join(directory, entry.name);
32
- if (entry.isDirectory()) {
33
- if (SKIPPED_DIRECTORIES.has(entry.name) || entry.name.startsWith('.')) {
34
- continue;
35
- }
36
- await sourceFilesIn(full, found);
37
- }
38
- else if (SOURCE_EXTENSIONS.some((ext) => entry.name.endsWith(ext))) {
39
- found.push(full);
40
- }
41
- }
42
- return found;
43
- };
44
- /**
45
- * The `// @snippet start <name>` regions in a project's source, so an example
46
- * in a doc comment can name one instead of restating it. The same mechanism the
47
- * website uses to pull its code blocks out of the shop template: the code that
48
- * reaches the reader is the code that compiles, and it cannot drift.
49
- */
50
- export const collectSnippets = async (projectDir, into = new Map(), origins = new Map()) => {
51
- for (const file of await sourceFilesIn(projectDir)) {
52
- const where = relative(projectDir, file);
53
- const lines = (await readFile(file, 'utf8')).split('\n');
54
- const open = new Map();
55
- for (const line of lines) {
56
- const end = line.match(END);
57
- if (end?.[1] && open.has(end[1])) {
58
- const name = end[1];
59
- const existing = origins.get(name);
60
- if (existing)
61
- throw new DuplicateSnippetError(name, where, existing);
62
- into.set(name, dedent(open.get(name)));
63
- origins.set(name, where);
64
- open.delete(name);
65
- continue;
66
- }
67
- const start = line.match(START);
68
- if (start?.[1]) {
69
- if (!open.has(start[1]))
70
- open.set(start[1], []);
71
- continue;
72
- }
73
- if (end)
74
- continue;
75
- for (const body of open.values())
76
- body.push(line);
77
- }
78
- const [unclosed] = open.keys();
79
- if (unclosed)
80
- throw new UnclosedSnippetError(unclosed, where);
81
- }
82
- return into;
83
- };
1
+ export { collectSnippets, UnclosedSnippetError, DuplicateSnippetError, } from '@pikku/skills';
@@ -2,8 +2,9 @@ import type { ValidateFinding } from './persona-checks.js';
2
2
  /**
3
3
  * A linked dependency resolves its own imports from its own checkout, so it can
4
4
  * hand the project types built against a different version of a shared package.
5
- * The symptom is a typecheck that OOMs rather than one that fails, so nothing
6
- * else in this validator would catch it.
5
+ * An installed pikku package can do the same from its own store entry. The
6
+ * symptom is a typecheck that OOMs rather than one that fails, so nothing else
7
+ * in this validator would catch it.
7
8
  *
8
9
  * Runs against the workspace root only. The mismatch is a property of the
9
10
  * install, not of any one package in it, so running per workspace package would
@@ -153,18 +153,87 @@ async function externalDependencyRoots(root) {
153
153
  }
154
154
  return found;
155
155
  }
156
+ /**
157
+ * Every installed `@pikku/*` package, by the real directory its imports resolve
158
+ * from. Probed under the root and each workspace package, because an isolated
159
+ * install links a dependency only under the package that declares it.
160
+ */
161
+ async function installedPikkuPackages(root) {
162
+ const bases = [root];
163
+ for (const group of ['packages', 'apps', 'backends']) {
164
+ const entries = await readdir(join(root, group), {
165
+ withFileTypes: true,
166
+ }).catch(() => []);
167
+ for (const d of entries) {
168
+ if (d.isDirectory())
169
+ bases.push(join(root, group, d.name));
170
+ }
171
+ }
172
+ const found = new Map();
173
+ for (const base of bases) {
174
+ const scope = join(base, 'node_modules', '@pikku');
175
+ const entries = await readdir(scope).catch(() => []);
176
+ for (const entry of entries) {
177
+ const real = await realpath(join(scope, entry)).catch(() => null);
178
+ if (real && !found.has(real))
179
+ found.set(real, `@pikku/${entry}`);
180
+ }
181
+ }
182
+ return found;
183
+ }
184
+ /**
185
+ * An installed pikku package can resolve its own copy of a type-identity
186
+ * package — bun's isolated store keeps one beside each entry — so the CLI can
187
+ * read the project's zod schemas with a different zod than wrote them. No link
188
+ * leaves the project, so the linked-dependency check never sees it.
189
+ *
190
+ * Only packages that declare the dependency are compared: walking up from one
191
+ * that never asked for it lands on whatever the store hoisted, which says
192
+ * nothing about what that package loads.
193
+ */
194
+ async function runInstallSkewChecks(root) {
195
+ const findings = [];
196
+ const installed = await installedPikkuPackages(root);
197
+ for (const pkg of TYPE_IDENTITY_PKGS) {
198
+ const local = await projectResolvedVersion(root, pkg);
199
+ if (!local)
200
+ continue;
201
+ for (const [real, name] of installed) {
202
+ if (name === pkg)
203
+ continue;
204
+ const manifest = await readJsonSafe(join(real, 'package.json'));
205
+ if (!manifest?.dependencies?.[pkg] && !manifest?.peerDependencies?.[pkg])
206
+ continue;
207
+ const theirs = await versionResolvedFrom(real, pkg);
208
+ if (!theirs || theirs === local.version)
209
+ continue;
210
+ findings.push({
211
+ id: `skewed-type-identity-${name.replace(/[@/]/g, '-')}-${pkg.replace(/[@/]/g, '-')}`,
212
+ severity: 'error',
213
+ message: `${name} resolves ${pkg}@${theirs}, while this project resolves ${pkg}@${local.version} — two copies of ${pkg} in one install`,
214
+ path: real,
215
+ fixHint: [
216
+ `${name} reads what the project builds with ${pkg} using its own copy, and TypeScript treats the two as unrelated types, so codegen can fail on schemas that are correct or exhaust the heap instead of reporting an error.`,
217
+ `Pin one ${pkg} for the whole install — "overrides": { "${pkg}": "${local.version}" } in the root package.json (bun/npm; "resolutions" for yarn) — and reinstall.`,
218
+ ].join('\n'),
219
+ });
220
+ }
221
+ }
222
+ return findings;
223
+ }
156
224
  /**
157
225
  * A linked dependency resolves its own imports from its own checkout, so it can
158
226
  * hand the project types built against a different version of a shared package.
159
- * The symptom is a typecheck that OOMs rather than one that fails, so nothing
160
- * else in this validator would catch it.
227
+ * An installed pikku package can do the same from its own store entry. The
228
+ * symptom is a typecheck that OOMs rather than one that fails, so nothing else
229
+ * in this validator would catch it.
161
230
  *
162
231
  * Runs against the workspace root only. The mismatch is a property of the
163
232
  * install, not of any one package in it, so running per workspace package would
164
233
  * report the same pair once per package.
165
234
  */
166
235
  export async function runTypeIdentityChecks(root) {
167
- const findings = [];
236
+ const findings = await runInstallSkewChecks(root);
168
237
  const external = await externalDependencyRoots(root);
169
238
  if (external.size === 0)
170
239
  return findings;
@@ -208,11 +277,11 @@ export async function warnOnSplitTypeIdentity(rootDir, logger) {
208
277
  const findings = await runTypeIdentityChecks(rootDir).catch(() => []);
209
278
  if (findings.length === 0)
210
279
  return;
211
- logger.warn(`[${ErrorCode.SPLIT_TYPE_IDENTITY}] ${findings.length === 1 ? 'A linked dependency resolves a shared package' : 'Linked dependencies resolve shared packages'} at a different version than this project:\n` +
280
+ logger.warn(`[${ErrorCode.SPLIT_TYPE_IDENTITY}] ${findings.length === 1 ? 'A dependency resolves a shared package' : 'Dependencies resolve shared packages'} at a different version than this project:\n` +
212
281
  findings.map((f) => ` ${f.message}`).join('\n') +
213
282
  `\nTypeScript treats each pair as two unrelated types and structurally compares them\n` +
214
283
  `wherever they meet, which inside a generic inference chain can exhaust the heap —\n` +
215
284
  `codegen then dies of memory pressure instead of reporting an error.\n\n` +
216
- `Fix: align the versions so both trees resolve one copy, or drop the link and\n` +
285
+ `Fix: align the versions so the install resolves one copy, or drop the link and\n` +
217
286
  `reinstall. Set PIKKU_SKIP_TYPE_IDENTITY_CHECK=1 to silence this.`);
218
287
  }
@@ -82,9 +82,9 @@ export const CHECKS = [
82
82
  },
83
83
  {
84
84
  id: 'type-identity',
85
- subject: 'linked dependencies',
86
- // Root of an installed tree only. A linked dependency is a property of the
87
- // install as a whole, so running this per workspace package reports the
85
+ subject: 'shared dependency versions',
86
+ // Root of an installed tree only. A linked or skewed dependency is a
87
+ // property of the install as a whole, so running this per workspace package reports the
88
88
  // same pair N times — and with nothing installed there is nothing to
89
89
  // compare.
90
90
  applies: async ({ dir, label }) => label === '.' && existsSync(join(dir, 'node_modules')),
@@ -72,7 +72,8 @@ export const serializeAuthGen = (definition, providers, authFile, leaf, packageM
72
72
  '',
73
73
  `import { defineSecret } from '@pikku/core/secret'`,
74
74
  ];
75
- const hasVariables = known.some((name) => PROVIDER_REGISTRY[name].variables);
75
+ const hasVariables = known.some((name) => PROVIDER_REGISTRY[name].variables) ||
76
+ definition.cookieCache;
76
77
  if (hasVariables) {
77
78
  secrets.push(`import { defineVariable } from '@pikku/core/variable'`);
78
79
  }
@@ -133,6 +134,37 @@ export const serializeAuthGen = (definition, providers, authFile, leaf, packageM
133
134
  secrets.push('');
134
135
  }
135
136
  }
137
+ // The stateless session's cookie lifetime, declared only on the cookieCache
138
+ // path because that is the only path where it decides anything. There the
139
+ // signed `session_data` cookie is the ONLY thing authenticating a request —
140
+ // `betterAuthStatelessSession` verifies it with the secret and never reaches
141
+ // the database — and an app typically calls no `/api/auth/*` route once
142
+ // someone is signed in, so nothing rewrites the cookie. better-auth's own
143
+ // 300s default therefore signs everyone out five minutes after they log in.
144
+ //
145
+ // @pikku/better-auth reads this variable and applies it to the instance, so
146
+ // the app's own `betterAuth({ session: { cookieCache } })` needs no `maxAge`.
147
+ // It is declared rather than hardcoded because the lifetime is also the
148
+ // longest a ban or a revoked session can go unnoticed, which is a per-stage
149
+ // policy decision. Optional: unset means the one-day default.
150
+ //
151
+ // The schema is a string, not a coerced number. `TypedVariablesService`
152
+ // returns a stored host value UNPARSED and runs the schema only to resolve a
153
+ // default, so a `z.coerce.number()` here would never fire on a real value and
154
+ // would only mislead whoever read it.
155
+ if (definition.cookieCache) {
156
+ secrets.push(`export const SessionCookieCacheMaxAgeSchema = z.string().default('86400')`);
157
+ secrets.push('');
158
+ secrets.push(`defineVariable({`);
159
+ secrets.push(` name: 'sessionCookieCacheMaxAge',`);
160
+ secrets.push(` displayName: 'Session Cookie Lifetime (seconds)',`);
161
+ secrets.push(` description: 'How long the signed session cookie is trusted before the API re-reads the session from the database. Also the longest a ban or a revoked session can go unnoticed. Defaults to 86400 (one day).',`);
162
+ secrets.push(` variableId: 'SESSION_COOKIE_CACHE_MAX_AGE',`);
163
+ secrets.push(` schema: SessionCookieCacheMaxAgeSchema,`);
164
+ secrets.push(` optional: true,`);
165
+ secrets.push(`})`);
166
+ secrets.push('');
167
+ }
136
168
  // Wiring file: handler + catch-all routes. When cookieCache is enabled the
137
169
  // global session middleware is split into its own file (see below) so this
138
170
  // file's auth.wiring import (the full better-auth server) isn't dragged into
@@ -37,7 +37,7 @@ import type { WireAddonConfig } from '@pikku/core/addon'
37
37
  export { wireRemoteAddon } from '@pikku/core/addon'
38
38
 
39
39
  /**
40
- * The functions each installed addon publishes. \`mcp\` names them.
40
+ * The functions each installed addon publishes. \`mcp\` and \`expose\` name them.
41
41
  */
42
42
  type AddonFunctions = {
43
43
  ${entries}
@@ -52,11 +52,13 @@ type AddonFunctionName<Package extends string> =
52
52
  *
53
53
  * \`mcp: true\` offers every function the addon itself declared as a tool; a list
54
54
  * names the functions to offer, checked against the ones the addon publishes.
55
+ * \`expose\` works the same way for \`rpc.exposed\`, and \`false\` exposes none.
55
56
  */
56
57
  export const wireAddon = <Package extends string>(
57
- config: Omit<WireAddonConfig, 'package' | 'mcp'> & {
58
+ config: Omit<WireAddonConfig, 'package' | 'mcp' | 'expose'> & {
58
59
  package: Package
59
60
  mcp?: boolean | AddonFunctionName<Package>[]
61
+ expose?: boolean | AddonFunctionName<Package>[]
60
62
  }
61
63
  ): void => wireAddonCore(config)
62
64
  `;
@@ -14,14 +14,15 @@ export const pikkuReactQuery = pikkuSessionlessFunc({
14
14
  });
15
15
  return;
16
16
  }
17
- const { workflows } = await getInspectorState();
17
+ const { workflows, auth } = await getInspectorState();
18
18
  const hasWorkflows = Object.keys(workflows?.meta ?? {}).length > 0;
19
19
  const rpcMapPath = getFileImportRelativePath(reactQueryFile, rpcMapDeclarationFile, packageMappings);
20
20
  let workflowMapPath;
21
21
  if (hasWorkflows) {
22
22
  workflowMapPath = getFileImportRelativePath(reactQueryFile, workflowMapDeclarationFile, packageMappings);
23
23
  }
24
- const content = serializeReactQueryHooks(rpcMapPath, workflowMapPath);
24
+ // `useSession` only exists for an app that has auth at all.
25
+ const content = serializeReactQueryHooks(rpcMapPath, workflowMapPath, !!auth.definition);
25
26
  await writeFileInDir(logger, reactQueryFile, content);
26
27
  },
27
28
  middleware: [
@@ -1 +1 @@
1
- export declare const serializeReactQueryHooks: (rpcMapPath: string, workflowMapPath?: string) => string;
1
+ export declare const serializeReactQueryHooks: (rpcMapPath: string, workflowMapPath?: string, hasAuth?: boolean) => string;