@pikku/cli 0.12.123 → 0.12.125

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 (262) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/console-app/assets/{abnfDiagram-N423BO3Z-BR5_-R36.js → abnfDiagram-N423BO3Z-B2suyn6i.js} +1 -1
  3. package/console-app/assets/architecture-TIHT7OUA-DmG0bXDT.js +1 -0
  4. package/console-app/assets/{architectureDiagram-T3A2C74G-PWMb8XVe.js → architectureDiagram-T3A2C74G-BQ5Dfvpo.js} +1 -1
  5. package/console-app/assets/{blockDiagram-VBNYF7ZC-BRFlIAtN.js → blockDiagram-VBNYF7ZC-D4pVRDiF.js} +1 -1
  6. package/console-app/assets/{c4Diagram-5PPSVZJV-DnxGac5c.js → c4Diagram-5PPSVZJV-Da04jGpq.js} +1 -1
  7. package/console-app/assets/channel-BgCT5gon.js +1 -0
  8. package/console-app/assets/{chunk-2GRJ4B5K-B09NFK9f.js → chunk-2GRJ4B5K-CwODIPP4.js} +1 -1
  9. package/console-app/assets/{chunk-4I5QYGJK-Di1FL5uh.js → chunk-4I5QYGJK-Bep4akjR.js} +1 -1
  10. package/console-app/assets/{chunk-5RXB4S5H-BkFSNR63.js → chunk-5RXB4S5H-CGyCRU9L.js} +1 -1
  11. package/console-app/assets/{chunk-6Q2QTUOP-L6D94YxI.js → chunk-6Q2QTUOP-D0IK0SMT.js} +1 -1
  12. package/console-app/assets/{chunk-7Z6QIM7H-DfzYcuKW.js → chunk-7Z6QIM7H-D12vS1jX.js} +1 -1
  13. package/console-app/assets/{chunk-GF5L2VYU-CA5m-Xs7.js → chunk-GF5L2VYU-kOLwTzIA.js} +1 -1
  14. package/console-app/assets/{chunk-I66GZJ75-Hl61byF7.js → chunk-I66GZJ75-Cb2nDoa3.js} +1 -1
  15. package/console-app/assets/{chunk-JQJVKLGR-CgLeeXhv.js → chunk-JQJVKLGR-B3a_ctfQ.js} +1 -1
  16. package/console-app/assets/{chunk-KBJHAD2P-9mRl0jD6.js → chunk-KBJHAD2P-DeP2Lqwp.js} +1 -1
  17. package/console-app/assets/{chunk-NSK5VX7P-D7NmS_w1.js → chunk-NSK5VX7P-BO8vlGrO.js} +1 -1
  18. package/console-app/assets/{chunk-QR6OTTB3-KdKTpLyy.js → chunk-QR6OTTB3-sX5V0Lok.js} +1 -1
  19. package/console-app/assets/{chunk-RYQCIY6F-DeEpMb3d.js → chunk-RYQCIY6F-DoCObEhk.js} +1 -1
  20. package/console-app/assets/{chunk-UBXNYLIW-DKYUw71c.js → chunk-UBXNYLIW-BndMXU5r.js} +1 -1
  21. package/console-app/assets/{chunk-W5SLKNZC-B2fxVl1I.js → chunk-W5SLKNZC--VrZN8EC.js} +1 -1
  22. package/console-app/assets/{chunk-WRU74C26-CHP8CDJx.js → chunk-WRU74C26-DdS0mzXO.js} +1 -1
  23. package/console-app/assets/chunk-XXDRQBXY-EP2C9tTO.js +1 -0
  24. package/console-app/assets/classDiagram-JCYQIIEL-D9wGqB4J.js +1 -0
  25. package/console-app/assets/classDiagram-v2-OCEON4UE-D9wGqB4J.js +1 -0
  26. package/console-app/assets/{cose-bilkent-JH36ORCC-Bv8nl8zW.js → cose-bilkent-JH36ORCC-C5EnHkFn.js} +1 -1
  27. package/console-app/assets/{cynefin-VYW2F7L2-CSj7rmbq.js → cynefin-VYW2F7L2-9irdkTt4.js} +1 -1
  28. package/console-app/assets/{cynefinDiagram-MW4NZA55-C3xwJ3tQ.js → cynefinDiagram-MW4NZA55-DY19660c.js} +1 -1
  29. package/console-app/assets/{dagre-VZM6K2ZE-B_2qJCCS.js → dagre-VZM6K2ZE-BYFZSaSb.js} +1 -1
  30. package/console-app/assets/{dagre-DUPxteSi.js → dagre-nXgDYqvf.js} +1 -1
  31. package/console-app/assets/{diagram-7IWD3JNH-IH1BOccH.js → diagram-7IWD3JNH-oDftiTp1.js} +1 -1
  32. package/console-app/assets/{diagram-B4RE2ZJO-BVvJvAvO.js → diagram-B4RE2ZJO-Cbj-pPbH.js} +1 -1
  33. package/console-app/assets/{diagram-LBJQPF4R-CyDL-OzR.js → diagram-LBJQPF4R-Ch53oSKw.js} +1 -1
  34. package/console-app/assets/{diagram-Q27KOJAE-BwUGm_12.js → diagram-Q27KOJAE-DA7K814v.js} +1 -1
  35. package/console-app/assets/{diagram-UB23O5K3-TPo3mAAN.js → diagram-UB23O5K3-B-_C-ctS.js} +1 -1
  36. package/console-app/assets/{ebnfDiagram-BXEA7PRR-D3zthE3L.js → ebnfDiagram-BXEA7PRR-DsOdsKXy.js} +1 -1
  37. package/console-app/assets/{erDiagram-JOGREHBK-DrJSdPyj.js → erDiagram-JOGREHBK-XB1mP5_A.js} +1 -1
  38. package/console-app/assets/eventmodeling-45OFAUF4-NKFCZ8Fj.js +1 -0
  39. package/console-app/assets/flowDiagram-UKHOOZJN-BpdPkmum.js +1 -0
  40. package/console-app/assets/{ganttDiagram-PKOTCBZU-xWWCXZUc.js → ganttDiagram-PKOTCBZU-knk0XSHi.js} +1 -1
  41. package/console-app/assets/{gitGraph-TEB2WS4Q-DqccwZ0r.js → gitGraph-TEB2WS4Q-COJnK2tM.js} +1 -1
  42. package/console-app/assets/{gitGraphDiagram-DS77QQ5N-BrQMGv-z.js → gitGraphDiagram-DS77QQ5N-sF0v605q.js} +1 -1
  43. package/console-app/assets/{index-Mq-YbqNO.css → index-C8MC-uMB.css} +1 -1
  44. package/console-app/assets/{index-BP1o9ux1.js → index-DMnbsgzs.js} +175 -175
  45. package/console-app/assets/{info-DKCQHKI2-DYYsb_ec.js → info-DKCQHKI2-Cbatr0w6.js} +1 -1
  46. package/console-app/assets/{infoDiagram-6WML65LV-B659BK5Z.js → infoDiagram-6WML65LV-93TFPCu2.js} +1 -1
  47. package/console-app/assets/{ishikawaDiagram-WSZJBQD7-nAST6NVR.js → ishikawaDiagram-WSZJBQD7-BBR8_Qms.js} +1 -1
  48. package/console-app/assets/{journeyDiagram-NVQOT4AX-DvMfDp0L.js → journeyDiagram-NVQOT4AX-zG55yDoc.js} +1 -1
  49. package/console-app/assets/{kanban-definition-27J2QSJJ-XSWRq0KN.js → kanban-definition-27J2QSJJ-DonGcEAY.js} +1 -1
  50. package/console-app/assets/{line-BhOX1huR.js → line-D1WdVm1S.js} +1 -1
  51. package/console-app/assets/{linear-DmCFkJVF.js → linear-D2GQL4Cm.js} +1 -1
  52. package/console-app/assets/{mermaid-parser.core-3dL4TN1f.js → mermaid-parser.core-b4yKyO56.js} +3 -3
  53. package/console-app/assets/{mermaid.core-DaXl8RjK.js → mermaid.core-Cwfec1gr.js} +4 -4
  54. package/console-app/assets/{mindmap-definition-FAOFIHXS-BVSaEdh8.js → mindmap-definition-FAOFIHXS-D0G4yKgw.js} +1 -1
  55. package/console-app/assets/{packet-7NZHBO7P-DmXSSPjQ.js → packet-7NZHBO7P-DDa4HL2T.js} +1 -1
  56. package/console-app/assets/{pegDiagram-VL7TDLO6-Cd94zu6b.js → pegDiagram-VL7TDLO6-fPdN5VZs.js} +1 -1
  57. package/console-app/assets/{pie-RZYD4A2V-BfIstVk8.js → pie-RZYD4A2V-DRHURUqo.js} +1 -1
  58. package/console-app/assets/{pieDiagram-7S7Q4E2Y-Jxlih0Om.js → pieDiagram-7S7Q4E2Y-DMYn8ga_.js} +1 -1
  59. package/console-app/assets/{quadrantDiagram-CIZ2JOQS-DMa-9Zmv.js → quadrantDiagram-CIZ2JOQS-C190OamC.js} +1 -1
  60. package/console-app/assets/{radar-I7S5WNFK-BhvNZ__k.js → radar-I7S5WNFK-Djvqzz9A.js} +1 -1
  61. package/console-app/assets/{railroad-3IZDKUUU-C7yHVxVy.js → railroad-3IZDKUUU-C9YvFeFq.js} +1 -1
  62. package/console-app/assets/railroad-abnf-AHOZXSZD-B3CYVW4p.js +1 -0
  63. package/console-app/assets/railroad-ebnf-EBAXGLYW-DrZ2yF62.js +1 -0
  64. package/console-app/assets/railroad-peg-LSFZ7HO6-CWAmMyxd.js +1 -0
  65. package/console-app/assets/{railroadDiagram-AXF67PYL-DsBXjXIE.js → railroadDiagram-AXF67PYL-IRFg1VrU.js} +1 -1
  66. package/console-app/assets/{requirementDiagram-LRYGKXZP-C5JX79TM.js → requirementDiagram-LRYGKXZP-L-7ubbcT.js} +1 -1
  67. package/console-app/assets/{sankeyDiagram-W5VNT64P-DFGcEBC2.js → sankeyDiagram-W5VNT64P-DBttV6Gt.js} +1 -1
  68. package/console-app/assets/{sequenceDiagram-SI44F4Z6-BUa6ak9R.js → sequenceDiagram-SI44F4Z6-Bg15oKAB.js} +1 -1
  69. package/console-app/assets/{src-BJFofs4v.js → src-DUC7tLWb.js} +1 -1
  70. package/console-app/assets/{stateDiagram-OKZ733FA-DFbCY_t8.js → stateDiagram-OKZ733FA-PWl0XGs5.js} +1 -1
  71. package/console-app/assets/stateDiagram-v2-UEYNNEHI-Celbgz4u.js +1 -0
  72. package/console-app/assets/{swimlanes-SLNWSIFB-Cl9G39FU.js → swimlanes-SLNWSIFB-CyXEtGBV.js} +1 -1
  73. package/console-app/assets/swimlanesDiagram-ULZ7WXOC-zxpNaB4U.js +8 -0
  74. package/console-app/assets/{timeline-definition-Z64GVDOM-ClWBr3pJ.js → timeline-definition-Z64GVDOM-BLA-DhBt.js} +1 -1
  75. package/console-app/assets/{treeView-QDETBFTQ-DIQ61Y8k.js → treeView-QDETBFTQ-Bfnmn2UY.js} +1 -1
  76. package/console-app/assets/{treemap-6X3UGDF4-KbZtNSyG.js → treemap-6X3UGDF4-DTA0wveO.js} +1 -1
  77. package/console-app/assets/{vennDiagram-T6HMQDX7-BY367OUV.js → vennDiagram-T6HMQDX7-CLXYZln0.js} +1 -1
  78. package/console-app/assets/{wardley-OPB4EBWU-BZc1RNrx.js → wardley-OPB4EBWU-zwxqisG0.js} +1 -1
  79. package/console-app/assets/{wardleyDiagram-T6FBY63Y-D45un7IA.js → wardleyDiagram-T6FBY63Y-hKtKvNlv.js} +1 -1
  80. package/console-app/assets/{xychartDiagram-ELKLHX3M-Ci5YwcnQ.js → xychartDiagram-ELKLHX3M-BGf8p4bo.js} +1 -1
  81. package/console-app/index.html +2 -2
  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/auth/index.d.ts +1 -1
  91. package/dist/.pikku/auth/index.js +1 -1
  92. package/dist/.pikku/auth/pikku-auth-types.gen.d.ts +1 -1
  93. package/dist/.pikku/auth/pikku-auth-types.gen.js +1 -1
  94. package/dist/.pikku/channel/index.d.ts +1 -1
  95. package/dist/.pikku/channel/index.js +1 -1
  96. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  97. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  98. package/dist/.pikku/cli/index.d.ts +1 -1
  99. package/dist/.pikku/cli/index.js +1 -1
  100. package/dist/.pikku/cli/pikku-cli-channel.js +6 -1
  101. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  102. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  103. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  104. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  105. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +7 -0
  106. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  107. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  108. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  109. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +14 -0
  110. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  111. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  112. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  113. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  114. package/dist/.pikku/error/index.d.ts +1 -1
  115. package/dist/.pikku/error/index.js +1 -1
  116. package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
  117. package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
  118. package/dist/.pikku/function/index.d.ts +1 -1
  119. package/dist/.pikku/function/index.js +1 -1
  120. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  121. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  122. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  123. package/dist/.pikku/function/pikku-functions-meta.gen.json +25 -6
  124. package/dist/.pikku/function/pikku-functions.gen.js +1 -1
  125. package/dist/.pikku/gateway/index.d.ts +1 -1
  126. package/dist/.pikku/gateway/index.js +1 -1
  127. package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
  128. package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
  129. package/dist/.pikku/http/index.d.ts +1 -1
  130. package/dist/.pikku/http/index.js +1 -1
  131. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  132. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  133. package/dist/.pikku/mcp/index.d.ts +1 -1
  134. package/dist/.pikku/mcp/index.js +1 -1
  135. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  136. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  137. package/dist/.pikku/middleware/index.d.ts +1 -1
  138. package/dist/.pikku/middleware/index.js +1 -1
  139. package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +2 -2
  140. package/dist/.pikku/middleware/pikku-middleware-types.gen.js +2 -2
  141. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  142. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
  143. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  144. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  145. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  146. package/dist/.pikku/queue/index.d.ts +1 -1
  147. package/dist/.pikku/queue/index.js +1 -1
  148. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  149. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  150. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  151. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  152. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  153. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  154. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -0
  155. package/dist/.pikku/scenarios/index.d.ts +1 -1
  156. package/dist/.pikku/scenarios/index.js +1 -1
  157. package/dist/.pikku/scenarios/pikku-personas.gen.js +1 -1
  158. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  159. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  160. package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +1 -1
  161. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  162. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  163. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  164. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  165. package/dist/.pikku/scheduler/index.d.ts +1 -1
  166. package/dist/.pikku/scheduler/index.js +1 -1
  167. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  168. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  169. package/dist/.pikku/schemas/register.gen.js +5 -1
  170. package/dist/.pikku/schemas/schemas/DeployApplyInput.schema.json +1 -1
  171. package/dist/.pikku/schemas/schemas/FabricProjectsListInput.schema.json +1 -0
  172. package/dist/.pikku/schemas/schemas/FabricProjectsListOutput.schema.json +1 -0
  173. package/dist/.pikku/scopes/index.d.ts +1 -1
  174. package/dist/.pikku/scopes/index.js +1 -1
  175. package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
  176. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
  177. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  178. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  179. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  180. package/dist/.pikku/secrets/index.d.ts +1 -1
  181. package/dist/.pikku/secrets/index.js +1 -1
  182. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  183. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  184. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  185. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  186. package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
  187. package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
  188. package/dist/.pikku/setup/index.d.ts +1 -1
  189. package/dist/.pikku/setup/index.js +1 -1
  190. package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
  191. package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
  192. package/dist/.pikku/trigger/index.d.ts +1 -1
  193. package/dist/.pikku/trigger/index.js +1 -1
  194. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  195. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  196. package/dist/.pikku/variables/index.d.ts +1 -1
  197. package/dist/.pikku/variables/index.js +1 -1
  198. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  199. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  200. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  201. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  202. package/dist/.pikku/workflow/index.d.ts +1 -1
  203. package/dist/.pikku/workflow/index.js +1 -1
  204. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  205. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  206. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  207. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  208. package/dist/bin/pikku-bin.mjs +2 -2
  209. package/dist/src/cli.wiring.js +7 -0
  210. package/dist/src/deploy/build-pipeline.d.ts +10 -0
  211. package/dist/src/deploy/build-pipeline.js +16 -1
  212. package/dist/src/deploy/bundler/bundler.js +1 -15
  213. package/dist/src/deploy/bundler/service-module-map.d.ts +17 -0
  214. package/dist/src/deploy/bundler/service-module-map.js +19 -0
  215. package/dist/src/deploy/frontend-assets.d.ts +50 -0
  216. package/dist/src/deploy/frontend-assets.js +82 -0
  217. package/dist/src/fabric/fabric-commands.d.ts +40 -0
  218. package/dist/src/fabric/fabric-commands.js +6 -0
  219. package/dist/src/fabric/functions/projects-list.function.d.ts +72 -0
  220. package/dist/src/fabric/functions/projects-list.function.js +63 -0
  221. package/dist/src/fabric/functions/validate.function.js +70 -7
  222. package/dist/src/functions/commands/deploy-apply.d.ts +10 -0
  223. package/dist/src/functions/commands/deploy-apply.js +8 -0
  224. package/dist/src/functions/commands/deploy-plan.js +1 -0
  225. package/dist/src/functions/commands/dev.js +13 -2
  226. package/dist/src/functions/commands/environment.d.ts +10 -0
  227. package/dist/src/functions/commands/environment.js +24 -0
  228. package/dist/src/functions/commands/serve-frontend.d.ts +11 -0
  229. package/dist/src/functions/commands/serve-frontend.js +17 -0
  230. package/dist/src/functions/commands/serve.js +16 -3
  231. package/dist/src/functions/db/annotation-parser.d.ts +8 -0
  232. package/dist/src/functions/db/annotation-parser.js +2 -0
  233. package/dist/src/functions/db/db-codegen.js +20 -1
  234. package/dist/src/functions/validate/shared-checks.d.ts +25 -0
  235. package/dist/src/functions/validate/shared-checks.js +86 -2
  236. package/dist/src/functions/wirings/middleware/serialize-middleware-types.js +1 -1
  237. package/dist/src/functions/wirings/variables/serialize-variables-types.js +5 -1
  238. package/dist/src/functions/wirings/virtual-user/serialize-virtual-user-functions.js +6 -3
  239. package/dist/src/server/dev-server-runner.interface.d.ts +9 -0
  240. package/dist/src/server/node-server-runner.js +3 -0
  241. package/dist/src/server/server-ready.d.ts +6 -9
  242. package/dist/src/server/server-ready.js +6 -9
  243. package/dist/src/utils/frontend.d.ts +9 -0
  244. package/dist/src/utils/frontend.js +18 -0
  245. package/dist/src/utils/pikku-cli-config.d.ts +10 -1
  246. package/dist/src/utils/pikku-cli-config.js +27 -0
  247. package/dist/tsconfig.tsbuildinfo +1 -1
  248. package/package.json +9 -9
  249. package/snippets.json +2 -2
  250. package/surface.json +88 -7
  251. package/console-app/assets/architecture-TIHT7OUA-B-7dd9iF.js +0 -1
  252. package/console-app/assets/channel-eaKvL3e5.js +0 -1
  253. package/console-app/assets/chunk-XXDRQBXY-CJZ5XPXr.js +0 -1
  254. package/console-app/assets/classDiagram-JCYQIIEL-CSV3IvA1.js +0 -1
  255. package/console-app/assets/classDiagram-v2-OCEON4UE-CSV3IvA1.js +0 -1
  256. package/console-app/assets/eventmodeling-45OFAUF4-TV8Whj2Z.js +0 -1
  257. package/console-app/assets/flowDiagram-UKHOOZJN-C0682uBh.js +0 -1
  258. package/console-app/assets/railroad-abnf-AHOZXSZD-BbrGgpop.js +0 -1
  259. package/console-app/assets/railroad-ebnf-EBAXGLYW-BMxBKnfa.js +0 -1
  260. package/console-app/assets/railroad-peg-LSFZ7HO6-i24YvrZ1.js +0 -1
  261. package/console-app/assets/stateDiagram-v2-UEYNNEHI-Ds7kyy87.js +0 -1
  262. package/console-app/assets/swimlanesDiagram-ULZ7WXOC-BAuvqF_W.js +0 -8
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod';
2
+ import { pikkuSessionlessFunc } from '../../../.pikku/function/index.js';
3
+ import { findProjectConfig, resolveApiContext } from '../lib/config.js';
4
+ import { getFabricRPC } from '../lib/http.js';
5
+ import { dim } from '../lib/output.js';
6
+ export const FabricProjectsListInput = z.object({
7
+ apiUrl: z.string().optional(),
8
+ });
9
+ export const FabricProjectsListOutput = z.object({
10
+ projects: z.array(z.object({
11
+ projectId: z.string(),
12
+ name: z.string(),
13
+ slug: z.string(),
14
+ status: z.string(),
15
+ productionBranch: z.string(),
16
+ gitRepoUrl: z.string().nullable(),
17
+ createdAt: z.string(),
18
+ linked: z.boolean(),
19
+ })),
20
+ });
21
+ /**
22
+ * The only fabric command that does not need a linked project, which is the
23
+ * point of it: a checkout whose config still holds `__PROJECT_ID__` can run
24
+ * nothing else, and `fabric init` cannot recover the id either — it creates,
25
+ * so against an existing project it fails with a 409 that carries no id.
26
+ */
27
+ export const FabricProjectsList = pikkuSessionlessFunc({
28
+ description: 'List the projects in your organization.',
29
+ input: FabricProjectsListInput,
30
+ output: FabricProjectsListOutput,
31
+ func: async (_services, { apiUrl: apiUrlOverride }) => {
32
+ const ctx = await resolveApiContext({ apiUrlOverride });
33
+ if (!ctx.token)
34
+ throw new Error('Not logged in. Run `pikku fabric login` first.');
35
+ const rpc = getFabricRPC({ apiUrl: ctx.apiUrl, token: ctx.token });
36
+ const { projects } = await rpc.invoke('fabricCliProjects', {});
37
+ const local = await findProjectConfig();
38
+ const linkedId = local?.config.projectId;
39
+ return {
40
+ projects: projects.map((project) => ({
41
+ ...project,
42
+ linked: linkedId !== undefined && project.projectId === linkedId,
43
+ })),
44
+ };
45
+ },
46
+ });
47
+ export const renderProjectsList = (_s, { projects }) => {
48
+ console.log('');
49
+ if (projects.length === 0) {
50
+ console.log(dim(' No projects yet. Run `pikku fabric link` to create one.'));
51
+ console.log('');
52
+ return;
53
+ }
54
+ const width = Math.max(...projects.map((p) => p.name.length));
55
+ for (const project of projects) {
56
+ console.log(` ${project.linked ? '*' : ' '} ${project.name.padEnd(width)} ${project.projectId} ${dim(project.gitRepoUrl ?? 'no repo')}`);
57
+ }
58
+ console.log('');
59
+ if (projects.some((p) => p.linked)) {
60
+ console.log(dim(' * this checkout'));
61
+ console.log('');
62
+ }
63
+ };
@@ -66,6 +66,15 @@ async function listSourceFiles(dir) {
66
66
  return [];
67
67
  }
68
68
  }
69
+ /**
70
+ * Blanks comment bodies so a raw-text scan cannot read prose as code. Prose in a
71
+ * scenario description ("Converted from the Gherkin scenario...") otherwise
72
+ * matches the import scanner's `from "..."` and reports a package that is really
73
+ * a sentence.
74
+ */
75
+ const stripCommentText = (text) => text
76
+ .replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' '))
77
+ .replace(/(^|[^:'"`\\])\/\/[^\n]*/g, (_m, p1) => p1);
69
78
  // Heavy/generated dirs pruned during a source walk.
70
79
  const SKIP_WALK_DIRS = new Set([
71
80
  'node_modules',
@@ -487,11 +496,14 @@ export async function runValidate(startDir = process.cwd(), opts = {}) {
487
496
  const txt = await readTextSafe(file);
488
497
  if (!txt)
489
498
  continue;
499
+ const src = stripCommentText(txt);
490
500
  const re = /(?:from|import|require)\s*\(?\s*['"]([^'".#][^'"]*)['"]/g;
491
501
  let m;
492
- while ((m = re.exec(txt))) {
502
+ while ((m = re.exec(src))) {
493
503
  const spec = m[1];
494
- if (spec.startsWith('node:') ||
504
+ if (/\s/.test(spec) ||
505
+ spec.startsWith('node:') ||
506
+ spec.startsWith('bun:') ||
495
507
  spec.startsWith('@/') ||
496
508
  spec.startsWith('~') ||
497
509
  spec.startsWith('virtual:') ||
@@ -1180,23 +1192,74 @@ export async function runValidate(startDir = process.cwd(), opts = {}) {
1180
1192
  }
1181
1193
  if (keysByValue.size > 0) {
1182
1194
  const STRING_LITERAL = /(?<![A-Za-z0-9_$])(['"])((?:[^\\\n]|\\.){2,200}?)\1/g;
1195
+ // A literal in `someKey: '...'` position is only copy when the key names
1196
+ // something the browser renders. `unit: 'kg'` is an RPC payload field that
1197
+ // happens to read like the catalogue's `unit_kg` label for a form suffix —
1198
+ // rewriting it to a message lookup would bind a stored value to UI copy.
1199
+ const PROPERTY_KEY = /([A-Za-z_$][A-Za-z0-9_$]*)\s*:\s*$/;
1200
+ // The innermost call whose argument list `index` sits in. Walks back
1201
+ // balancing brackets, so in `expect(await page.getByText('Save'), ...)`
1202
+ // the literal belongs to `getByText`, which is a locator and is still
1203
+ // scanned.
1204
+ const enclosingCall = (code, index) => {
1205
+ let depth = 0;
1206
+ for (let i = index - 1; i >= 0; i--) {
1207
+ const ch = code[i];
1208
+ if (ch === ')' || ch === ']' || ch === '}')
1209
+ depth++;
1210
+ else if (ch === '(' || ch === '[' || ch === '{') {
1211
+ if (depth > 0) {
1212
+ depth--;
1213
+ continue;
1214
+ }
1215
+ if (ch !== '(')
1216
+ return null;
1217
+ return (/([A-Za-z_$][A-Za-z0-9_$]*)\s*$/.exec(code.slice(0, i))?.[1] ??
1218
+ null);
1219
+ }
1220
+ }
1221
+ return null;
1222
+ };
1223
+ const UI_TEXT_KEYS = new Set([
1224
+ 'alt',
1225
+ 'button',
1226
+ 'caption',
1227
+ 'heading',
1228
+ 'label',
1229
+ 'link',
1230
+ 'name',
1231
+ 'option',
1232
+ 'placeholder',
1233
+ 'tab',
1234
+ 'text',
1235
+ 'title',
1236
+ 'value',
1237
+ ]);
1183
1238
  for (const file of await walkSourceFiles(root)) {
1184
1239
  if (!/\.(steps|scenario)\.tsx?$/.test(file))
1185
1240
  continue;
1186
1241
  const text = await readTextSafe(file);
1187
1242
  if (!text)
1188
1243
  continue;
1189
- const code = text
1190
- .replace(/\/\*[\s\S]*?\*\//g, '')
1191
- .replace(/(^|[^:'"`\\])\/\/[^\n]*/g, '$1');
1244
+ const code = stripCommentText(text);
1192
1245
  const hits = [];
1193
1246
  for (const match of code.matchAll(STRING_LITERAL)) {
1194
1247
  const literal = match[2];
1195
1248
  if (!literal)
1196
1249
  continue;
1197
1250
  const keys = keysByValue.get(literal);
1198
- if (keys)
1199
- hits.push(`"${literal}" → ${keys.join(' | ')}`);
1251
+ if (!keys)
1252
+ continue;
1253
+ // `expect(item.unit, 'kg')` asserts on a value the RPC returned,
1254
+ // not on anything the browser rendered. Reading it from the
1255
+ // catalogue would bind a stored value to UI copy, and the assertion
1256
+ // would then pass against whatever the label happened to say.
1257
+ if (enclosingCall(code, match.index) === 'expect')
1258
+ continue;
1259
+ const property = PROPERTY_KEY.exec(code.slice(0, match.index));
1260
+ if (property && !UI_TEXT_KEYS.has(property[1]))
1261
+ continue;
1262
+ hits.push(`"${literal}" → ${keys.join(' | ')}`);
1200
1263
  }
1201
1264
  if (hits.length === 0)
1202
1265
  continue;
@@ -8,23 +8,33 @@ export declare function resolveProvider(config?: {
8
8
  };
9
9
  }, providerName?: string, options?: {
10
10
  runtime?: string;
11
+ desktop?: boolean;
12
+ projectDir?: string;
13
+ desktopIdentifier?: string;
14
+ desktopUrl?: string;
11
15
  }): Promise<ProviderAdapter>;
12
16
  export declare const deployApply: import("#pikku/function").PikkuFunctionConfig<{
13
17
  fromPlan?: boolean;
14
18
  provider?: string;
15
19
  runtime?: string;
20
+ desktop?: boolean;
21
+ desktopUrl?: string;
16
22
  resultFile?: string;
17
23
  debugArtifacts?: boolean;
18
24
  }, void, "rpc" | "session", import("#pikku/function").PikkuFunctionSessionless<{
19
25
  fromPlan?: boolean;
20
26
  provider?: string;
21
27
  runtime?: string;
28
+ desktop?: boolean;
29
+ desktopUrl?: string;
22
30
  resultFile?: string;
23
31
  debugArtifacts?: boolean;
24
32
  }, void, "rpc" | "session", import("#pikku/function").WiredServices, unknown> | import("#pikku/function").PikkuFunction<{
25
33
  fromPlan?: boolean;
26
34
  provider?: string;
27
35
  runtime?: string;
36
+ desktop?: boolean;
37
+ desktopUrl?: string;
28
38
  resultFile?: string;
29
39
  debugArtifacts?: boolean;
30
40
  }, void, "rpc" | "session", import("#pikku/function").WiredServices>, undefined, undefined>;
@@ -178,8 +178,15 @@ async function runDeploy(provider, providerDir, logger, resultFile) {
178
178
  export const deployApply = pikkuSessionlessFunc({
179
179
  func: async ({ logger, config, getInspectorState, bundler }, data) => {
180
180
  const projectDir = config.rootDir;
181
+ // A url on its own is a request for a shell — asking for both flags would
182
+ // only leave `--desktop-url` alone as a silent no-op.
183
+ const desktopUrl = data?.desktopUrl ?? config.deploy?.desktop?.url;
181
184
  const provider = await resolveProvider(config, data?.provider, {
182
185
  runtime: data?.runtime,
186
+ desktop: data?.desktop || Boolean(desktopUrl),
187
+ projectDir,
188
+ desktopIdentifier: config.deploy?.desktop?.identifier,
189
+ desktopUrl,
183
190
  });
184
191
  const fromPlan = data?.fromPlan ?? false;
185
192
  const resultFile = data?.resultFile;
@@ -212,6 +219,7 @@ export const deployApply = pikkuSessionlessFunc({
212
219
  defaultTarget: config.deploy?.defaultTarget,
213
220
  globalHTTPPrefix: config.globalHTTPPrefix,
214
221
  getEntryContext,
222
+ frontend: config.frontend,
215
223
  outDir: config.outDir,
216
224
  debugArtifacts: data?.debugArtifacts ?? false,
217
225
  logger,
@@ -59,6 +59,7 @@ export const deployPlan = pikkuSessionlessFunc({
59
59
  defaultTarget: config.deploy?.defaultTarget,
60
60
  globalHTTPPrefix: config.globalHTTPPrefix,
61
61
  getEntryContext,
62
+ frontend: config.frontend,
62
63
  outDir: config.outDir,
63
64
  debugArtifacts: data?.debugArtifacts ?? false,
64
65
  logger,
@@ -18,6 +18,7 @@ import { resolveDb, createKysely, parseDatabaseUrl, } from '../db/local-db.js';
18
18
  import { loadUserBootstrap, loadUserModule } from './load-user-project.js';
19
19
  import { registerScenarioInstrumentation } from '../wirings/scenarios/register-scenario-instrumentation.js';
20
20
  import { startCoverageService } from './start-coverage.js';
21
+ import { resolveDevEnvironmentName } from './environment.js';
21
22
  import { createDevAgentRunner } from './dev-agent-runner.js';
22
23
  import { resolveConsoleMount } from './serve-console.js';
23
24
  import { serverReadyLine } from '../../server/server-ready.js';
@@ -28,6 +29,7 @@ export const dev = pikkuSessionlessFunc({
28
29
  remote: true,
29
30
  func: async ({ logger, config, getInspectorState, invalidateInspectorState, variables, devServerRunner, }, { port, watch, hmr, coverage, test, model }, { rpc }) => {
30
31
  process.env.PIKKU_DEV_QUICK_LOGIN ??= 'true';
32
+ process.env.PIKKU_ENV ??= resolveDevEnvironmentName(config.environments ?? {});
31
33
  enableDevActorSignIn(logger);
32
34
  applyModelAliasOverride(logger, model, config.models);
33
35
  if (test) {
@@ -296,10 +298,19 @@ export const dev = pikkuSessionlessFunc({
296
298
  await lifecycle?.beforeStart?.(resolvedServices);
297
299
  await pikkuServer.start();
298
300
  await lifecycle?.afterStart?.(resolvedServices);
301
+ // Not `resolvedPort`: `--port 0` asks the OS for a free port, and every URL
302
+ // announced from here has to name the one it actually handed out.
303
+ const boundPort = pikkuServer.port;
299
304
  if (consoleMount) {
300
- logger.info(`Pikku Console available at http://${hostname}:${resolvedPort}${consoleMount.urlPrefix}`);
305
+ logger.info(`Pikku Console available at http://${hostname}:${boundPort}${consoleMount.urlPrefix}`);
301
306
  }
302
- logger.info(serverReadyLine(hostname, resolvedPort));
307
+ // Serving the built frontend here would hand you whatever the last build
308
+ // produced, with no HMR and no warning that it is stale. The frontend's own
309
+ // dev server owns dev; it proxies its API calls back to this port.
310
+ if (config.frontend) {
311
+ logger.info(`Frontend not served in dev — run your frontend dev server and proxy its API calls to http://${hostname}:${boundPort}`);
312
+ }
313
+ logger.info(serverReadyLine(hostname, boundPort));
303
314
  let watcher;
304
315
  process.once('SIGINT', async () => {
305
316
  logger.info('Stopping dev server...');
@@ -60,3 +60,13 @@ export declare const parseAppUrls: (value: string) => {
60
60
  base?: string;
61
61
  byApp: Record<string, string>;
62
62
  };
63
+ /**
64
+ * The environment name `pikku dev` is running as, for anything that gates on
65
+ * `PIKKU_ENV` — persona provisioning above all, which fails closed and so
66
+ * provisions nothing at all when nobody says where the process is.
67
+ *
68
+ * Local means a configured, non-production environment whose `apiUrl` is a
69
+ * loopback host; `local` wins when several qualify. A project that configures
70
+ * none gets `undefined` rather than a guess.
71
+ */
72
+ export declare const resolveDevEnvironmentName: (environments: Record<string, PikkuEnvironment>) => string | undefined;
@@ -94,3 +94,27 @@ export const parseAppUrls = (value) => {
94
94
  }
95
95
  return { base, byApp };
96
96
  };
97
+ /**
98
+ * The environment name `pikku dev` is running as, for anything that gates on
99
+ * `PIKKU_ENV` — persona provisioning above all, which fails closed and so
100
+ * provisions nothing at all when nobody says where the process is.
101
+ *
102
+ * Local means a configured, non-production environment whose `apiUrl` is a
103
+ * loopback host; `local` wins when several qualify. A project that configures
104
+ * none gets `undefined` rather than a guess.
105
+ */
106
+ export const resolveDevEnvironmentName = (environments) => {
107
+ const candidates = Object.entries(environments).filter(([, environment]) => {
108
+ if (environment.production) {
109
+ return false;
110
+ }
111
+ try {
112
+ return LOCAL_HOSTNAMES.has(new URL(environment.apiUrl).hostname);
113
+ }
114
+ catch {
115
+ return false;
116
+ }
117
+ });
118
+ const preferred = candidates.find(([name]) => name === 'local');
119
+ return (preferred ?? candidates[0])?.[0];
120
+ };
@@ -0,0 +1,11 @@
1
+ import type { StaticMount } from '@pikku/node-http-server';
2
+ import type { PikkuCLIConfig } from '../../../types/config.js';
3
+ /**
4
+ * Turn a resolved `frontend` config into the mount that serves it.
5
+ *
6
+ * The directory is checked rather than trusted because pikku only ever reads a
7
+ * frontend's output: an unbuilt `dir` means the project's own build has not run,
8
+ * and saying so here is far cheaper than a server that boots fine and answers
9
+ * every page with a 404.
10
+ */
11
+ export declare function resolveFrontendMount(frontend: NonNullable<PikkuCLIConfig['frontend']>): Promise<StaticMount>;
@@ -0,0 +1,17 @@
1
+ import { assertFrontendBuilt } from '../../utils/frontend.js';
2
+ /**
3
+ * Turn a resolved `frontend` config into the mount that serves it.
4
+ *
5
+ * The directory is checked rather than trusted because pikku only ever reads a
6
+ * frontend's output: an unbuilt `dir` means the project's own build has not run,
7
+ * and saying so here is far cheaper than a server that boots fine and answers
8
+ * every page with a 404.
9
+ */
10
+ export async function resolveFrontendMount(frontend) {
11
+ await assertFrontendBuilt(frontend.dir);
12
+ return {
13
+ urlPrefix: frontend.urlPrefix,
14
+ directory: frontend.dir,
15
+ spaFallback: frontend.spaFallback,
16
+ };
17
+ }
@@ -16,6 +16,7 @@ import { loadUserBootstrap, loadUserModule } from './load-user-project.js';
16
16
  import { registerScenarioInstrumentation } from '../wirings/scenarios/register-scenario-instrumentation.js';
17
17
  import { createDevAgentRunner } from './dev-agent-runner.js';
18
18
  import { resolveConsoleMount } from './serve-console.js';
19
+ import { resolveFrontendMount } from './serve-frontend.js';
19
20
  import { serverReadyLine } from '../../server/server-ready.js';
20
21
  import { createEphemeralContentSigningJWT } from '../../server/content-signing-jwt.js';
21
22
  import { disableDevActorSignIn } from '../../server/actor-sign-in.js';
@@ -144,22 +145,34 @@ export const serve = pikkuSessionlessFunc({
144
145
  if (serveConsole && !consoleMount) {
145
146
  logger.warn('Console app not found. Please rebuild @pikku/cli with the console app bundled.');
146
147
  }
148
+ const frontendMount = config.frontend
149
+ ? await resolveFrontendMount(config.frontend)
150
+ : undefined;
151
+ // The console goes first so a frontend mounted at `/` cannot claim
152
+ // `/console` before the console's own mount is offered the request.
153
+ const staticMounts = [consoleMount, frontendMount].filter((mount) => Boolean(mount));
147
154
  const pikkuServer = devServerRunner.createServer({
148
155
  ...userConfig,
149
156
  hostname: bindHostname,
150
157
  port: resolvedPort,
151
158
  content: localContentConfig,
152
- ...(consoleMount ? { staticMounts: [consoleMount] } : {}),
159
+ ...(staticMounts.length ? { staticMounts } : {}),
153
160
  }, logger, { contentSigningJWT });
154
161
  const lifecycle = await loadLifecycle();
155
162
  await pikkuServer.init();
156
163
  await lifecycle?.beforeStart?.(resolvedServices);
157
164
  await pikkuServer.start();
158
165
  await lifecycle?.afterStart?.(resolvedServices);
166
+ // Not `resolvedPort`: `--port 0` asks the OS for a free port, and every URL
167
+ // announced from here has to name the one it actually handed out.
168
+ const boundPort = pikkuServer.port;
159
169
  if (consoleMount) {
160
- logger.info(`Pikku Console available at http://${hostname}:${resolvedPort}${consoleMount.urlPrefix}`);
170
+ logger.info(`Pikku Console available at http://${hostname}:${boundPort}${consoleMount.urlPrefix}`);
171
+ }
172
+ if (frontendMount) {
173
+ logger.info(`Frontend available at http://${hostname}:${boundPort}${frontendMount.urlPrefix}`);
161
174
  }
162
- logger.info(serverReadyLine(hostname, resolvedPort));
175
+ logger.info(serverReadyLine(hostname, boundPort));
163
176
  process.once('SIGINT', async () => {
164
177
  logger.info('Stopping server...');
165
178
  try {
@@ -30,6 +30,14 @@ export interface ColAnnotation {
30
30
  * insert/update brand on the generated column type. Absent means `plain`.
31
31
  */
32
32
  form?: ColumnForm;
33
+ /**
34
+ * Which key protects this column, for an encrypted `form`. Absent means the
35
+ * deployment's default key.
36
+ *
37
+ * Reported as authored rather than defaulted here, so the manifest emitter
38
+ * stays the single place the default is applied.
39
+ */
40
+ keyId?: string;
33
41
  }
34
42
  /** Per-table, per-column annotation map sourced from `db/annotations.ts`. */
35
43
  export type AnnotationMap = Record<string, Record<string, ColAnnotation>>;
@@ -117,6 +117,8 @@ export function loadAnnotations(rootDir) {
117
117
  COLUMN_FORMS.includes(ann.form)) {
118
118
  entry.form = ann.form;
119
119
  }
120
+ if (ann.keyId)
121
+ entry.keyId = ann.keyId;
120
122
  const strategy = parseStrategy(ann.classification);
121
123
  if (strategy !== null)
122
124
  entry.anonymize = strategy;
@@ -340,6 +340,14 @@ function emitInterface(table, camelCase, explicitAnnotations, dialect, enumByNam
340
340
  return `export interface ${ifaceName} {\n${fields}\n}`;
341
341
  }
342
342
  // ─── Manifest emitter ────────────────────────────────────────────────────────
343
+ /**
344
+ * The key an encrypted column is protected by when it names none.
345
+ *
346
+ * Spelled out in every entry rather than left implicit, so the set of keys a
347
+ * deployment has to resolve can be read off the manifest without knowing the
348
+ * rule that produced it.
349
+ */
350
+ const DEFAULT_COLUMN_KEY_ID = 'default';
343
351
  function emitManifest(tables, explicitAnnotations) {
344
352
  const tableEntries = tables
345
353
  .map((table) => {
@@ -354,8 +362,15 @@ function emitManifest(tables, explicitAnnotations) {
354
362
  // tell "held in the clear, stated" from "nobody has said yet", and a
355
363
  // defaulted 'plain' would erase exactly that difference.
356
364
  const formLiteral = ann?.form ? `, form: '${ann.form}'` : '';
365
+ // Only a column that is actually encrypted names a key. Emitting one
366
+ // on a plain or hashed column would claim a protection it has not
367
+ // got — and a hash has no key to be opened with in the first place.
368
+ const encrypted = ann?.form === 'wrapped' || ann?.form === 'sealed';
369
+ const keyIdLiteral = encrypted
370
+ ? `, keyId: '${ann?.keyId ?? DEFAULT_COLUMN_KEY_ID}'`
371
+ : '';
357
372
  return (` ${JSON.stringify(col.name)}: ` +
358
- `{ classification: '${classification}', anonymize_strategy: ${strategyLiteral}${formLiteral} }`);
373
+ `{ classification: '${classification}', anonymize_strategy: ${strategyLiteral}${formLiteral}${keyIdLiteral} }`);
359
374
  })
360
375
  .join(',\n');
361
376
  return ` ${JSON.stringify(table.name)}: {\n${colEntries}\n }`;
@@ -391,6 +406,10 @@ function emitClassificationMap(tables) {
391
406
  ` * real encrypt/seal/hash result can be written to it. Defaults to 'plain',`,
392
407
  ` * which on a \`secret\` column is warned about (PKU483) until stated here. */`,
393
408
  ` form?: 'plain' | 'hashed' | 'wrapped' | 'sealed'`,
409
+ ` /** Which key protects this column, for a 'wrapped' or 'sealed' form. It is a`,
410
+ ` * purpose, not a tenant: naming one here says these columns open together and`,
411
+ ` * separately from the rest. Defaults to 'default'. */`,
412
+ ` keyId?: string`,
394
413
  ` /** Anonymize strategy used by \`pikku db anonymize\`. */`,
395
414
  ` classification?: 'fake:email' | 'fake:name' | 'hash' | 'keep'`,
396
415
  ` /** Column kind override for codegen coercion + typing. */`,
@@ -55,6 +55,31 @@ export declare function readTextSafe(path: string): Promise<string | null>;
55
55
  * optional schema qualifier, since `main.audit` is the same table.
56
56
  */
57
57
  export declare function migrationCreatesTable(sql: string, tableName: string): boolean;
58
+ /**
59
+ * Every table name one better-auth model can be sitting under.
60
+ *
61
+ * `modelName` renames a model's table — `user: { modelName: 'authUser' }` is
62
+ * how an app keeps better-auth's rows out of a `user` table it already owns —
63
+ * and the adapter's CamelCasePlugin then writes that as `auth_user`. An app
64
+ * that renames all four models has none of the default names in its
65
+ * migrations, so a default-names-only check reported it as having no auth
66
+ * schema at all. Accept the default, the override, and the override's
67
+ * snake_case form.
68
+ */
69
+ export declare const betterAuthTableAliases: (model: string, configText: string) => string[];
70
+ /**
71
+ * Static value imports of a package the deploy bundler stubs.
72
+ *
73
+ * A unit that does not require the service gets the package rewritten to
74
+ * `export {}`, so a static named import fails to bundle — once per unit, with
75
+ * an esbuild message that names the stub rather than the mistake. Type-only
76
+ * imports are erased before bundling and a side-effect import binds nothing,
77
+ * so neither is reported.
78
+ */
79
+ export declare const staticStubbedImports: (code: string) => Array<{
80
+ module: string;
81
+ service: string;
82
+ }>;
58
83
  /**
59
84
  * Every check that is true of any pikku project, regardless of where it deploys.
60
85
  *
@@ -8,6 +8,7 @@ import { runAuthPluginChecks } from './auth-plugin-checks.js';
8
8
  import { runScenarioFileChecks } from './scenario-file-checks.js';
9
9
  import { projectWiresChannels, runWebsocketDepsChecks, } from './websocket-deps-checks.js';
10
10
  import { resolveFromProject } from '../../utils/resolve-from-project.js';
11
+ import { SERVICE_MODULE_MAP } from '../../deploy/bundler/service-module-map.js';
11
12
  /**
12
13
  * A malformed config is named, never reported as absent.
13
14
  *
@@ -73,6 +74,81 @@ export function migrationCreatesTable(sql, tableName) {
73
74
  }
74
75
  /** better-auth's core schema — the tables its adapter queries on every request. */
75
76
  const BETTER_AUTH_TABLES = ['user', 'session', 'account', 'verification'];
77
+ /**
78
+ * Every table name one better-auth model can be sitting under.
79
+ *
80
+ * `modelName` renames a model's table — `user: { modelName: 'authUser' }` is
81
+ * how an app keeps better-auth's rows out of a `user` table it already owns —
82
+ * and the adapter's CamelCasePlugin then writes that as `auth_user`. An app
83
+ * that renames all four models has none of the default names in its
84
+ * migrations, so a default-names-only check reported it as having no auth
85
+ * schema at all. Accept the default, the override, and the override's
86
+ * snake_case form.
87
+ */
88
+ export const betterAuthTableAliases = (model, configText) => {
89
+ const override = configText.match(new RegExp(`\\b${model}\\s*:\\s*\\{[^{}]*?\\bmodelName\\s*:\\s*['"\`](\\w+)['"\`]`))?.[1];
90
+ if (!override)
91
+ return [model];
92
+ return [
93
+ model,
94
+ override,
95
+ override.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase(),
96
+ ];
97
+ };
98
+ /**
99
+ * Static value imports of a package the deploy bundler stubs.
100
+ *
101
+ * A unit that does not require the service gets the package rewritten to
102
+ * `export {}`, so a static named import fails to bundle — once per unit, with
103
+ * an esbuild message that names the stub rather than the mistake. Type-only
104
+ * imports are erased before bundling and a side-effect import binds nothing,
105
+ * so neither is reported.
106
+ */
107
+ export const staticStubbedImports = (code) => {
108
+ const found = [];
109
+ const statements = code.matchAll(/^[ \t]*import\s+(?!type\s)([\s\S]*?)\s*from\s*['"`]([^'"`]+)['"`]/gm);
110
+ for (const statement of statements) {
111
+ const clause = statement[1];
112
+ const specifier = statement[2];
113
+ const named = clause.match(/^\{([\s\S]*)\}$/);
114
+ if (named &&
115
+ named[1]
116
+ .split(',')
117
+ .filter((binding) => binding.trim().length > 0)
118
+ .every((binding) => /^type\s/.test(binding.trim()))) {
119
+ continue;
120
+ }
121
+ for (const [service, patterns] of Object.entries(SERVICE_MODULE_MAP)) {
122
+ if (patterns.some((pattern) => pattern.test(specifier))) {
123
+ found.push({ module: specifier, service });
124
+ }
125
+ }
126
+ }
127
+ return found;
128
+ };
129
+ /**
130
+ * The text of every source file that configures better-auth, concatenated.
131
+ *
132
+ * Narrowed to files mentioning `betterAuth` so a `modelName` belonging to some
133
+ * other library cannot rename an auth table out from under the check.
134
+ */
135
+ const readAuthConfigText = async (srcDir) => {
136
+ if (!existsSync(srcDir))
137
+ return '';
138
+ let entries;
139
+ try {
140
+ entries = (await readdir(srcDir, { recursive: true })).filter((f) => typeof f === 'string' &&
141
+ f.endsWith('.ts') &&
142
+ !f.includes('node_modules'));
143
+ }
144
+ catch {
145
+ return '';
146
+ }
147
+ const texts = await Promise.all(entries.map((f) => readTextSafe(join(srcDir, f))));
148
+ return texts
149
+ .filter((t) => Boolean(t) && /betterAuth/.test(t))
150
+ .join('\n');
151
+ };
76
152
  /**
77
153
  * Scaffold flags that gate a generated surface the pikku console calls.
78
154
  *
@@ -223,9 +299,15 @@ export async function runSharedProjectChecks(root) {
223
299
  }
224
300
  }
225
301
  const servicesPath = join(fnDir, 'src', 'services.ts');
226
- if (!(await readTextSafe(servicesPath))) {
302
+ const servicesText = await readTextSafe(servicesPath);
303
+ if (!servicesText) {
227
304
  w('services-missing', 'packages/functions/src/services.ts not found', servicesPath, 'Create services.ts and export your service factory for the workspace');
228
305
  }
306
+ else {
307
+ for (const { module, service } of staticStubbedImports(servicesText)) {
308
+ e('services-static-stubbed-import', `services.ts imports '${module}' statically, but the deploy bundler stubs it in every unit that does not require '${service}'`, servicesPath, lines(`Every unit without '${service}' gets '${module}' rewritten to \`export {}\`, so a static import fails to bundle there.`, 'Import the type only, then load the package when you build the service:', ` const mod = await import('${module}')`, ' if (mod.SomeExport) { ... }', 'See templates/starter-template/packages/functions/src/services.ts for the shape.'));
309
+ }
310
+ }
229
311
  // ── auth schema ──────────────────────────────────────────────────────
230
312
  // The tables are better-auth's own (`user`, `session`, `account`,
231
313
  // `verification`) because those are what the adapter queries. This check
@@ -257,8 +339,10 @@ export async function runSharedProjectChecks(root) {
257
339
  const allSql = (await Promise.all(files.map((f) => readTextSafe(join(migrationsDir, f)))))
258
340
  .filter((sql) => Boolean(sql))
259
341
  .join('\n');
342
+ const authConfigText = await readAuthConfigText(join(fnDir, 'src'));
260
343
  for (const table of BETTER_AUTH_TABLES) {
261
- if (!migrationCreatesTable(allSql, table)) {
344
+ const aliases = betterAuthTableAliases(table, authConfigText);
345
+ if (!aliases.some((name) => migrationCreatesTable(allSql, name))) {
262
346
  missingAuthTables.push(table);
263
347
  }
264
348
  }
@@ -24,7 +24,7 @@ import type { PikkuAgentMiddlewareHooks } from '@pikku/core/agent'
24
24
  import type { Services, SingletonServices } from '${functionTypesImportPath}'
25
25
  ${requiredServicesTypeImport}
26
26
 
27
- export { cors, authAPIKey, authBearer, authCookie } from '@pikku/core/middleware'
27
+ export { cors, requireOrigin, authAPIKey, authBearer, authCookie } from '@pikku/core/middleware'
28
28
  export type { MiddlewarePriority }
29
29
 
30
30
  /**
@@ -24,7 +24,11 @@ export const serializeVariablesTypes = ({ definitions, schemaLookup, variablesFi
24
24
  }
25
25
  schemaImports.get(schemaRef.sourceFile).add(schemaRef.variableName);
26
26
  mapEntries.push(` '${meta.variableId}': z.infer<typeof ${schemaRef.variableName}>`);
27
- metaEntries.push(` '${meta.variableId}': { name: '${name}', displayName: ${literal(meta.displayName)} }`);
27
+ // The schema is emitted behind a thunk because this file and the one
28
+ // declaring the schema import each other: reading it while the modules
29
+ // are still initializing throws `Cannot access '<schema>' before
30
+ // initialization`, and deferring the read to first use avoids the cycle.
31
+ metaEntries.push(` '${meta.variableId}': { name: '${name}', displayName: ${literal(meta.displayName)}, schema: () => ${schemaRef.variableName} }`);
28
32
  }
29
33
  }
30
34
  }