@pikku/cli 0.12.125 → 0.12.127
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +231 -0
- package/console-app/assets/{abnfDiagram-N423BO3Z-B2suyn6i.js → abnfDiagram-N423BO3Z-BY2LM8wi.js} +1 -1
- package/console-app/assets/architecture-TIHT7OUA-CWwmvxZF.js +1 -0
- package/console-app/assets/{architectureDiagram-T3A2C74G-BQ5Dfvpo.js → architectureDiagram-T3A2C74G-CeqjY3Kw.js} +1 -1
- package/console-app/assets/{blockDiagram-VBNYF7ZC-D4pVRDiF.js → blockDiagram-VBNYF7ZC-BW5174xj.js} +1 -1
- package/console-app/assets/{c4Diagram-5PPSVZJV-Da04jGpq.js → c4Diagram-5PPSVZJV-B7zXAFL0.js} +1 -1
- package/console-app/assets/channel-pOkwZ_Ot.js +1 -0
- package/console-app/assets/{chunk-2GRJ4B5K-CwODIPP4.js → chunk-2GRJ4B5K-BfRYzQ4t.js} +1 -1
- package/console-app/assets/{chunk-4I5QYGJK-Bep4akjR.js → chunk-4I5QYGJK-DBA7-Nu4.js} +1 -1
- package/console-app/assets/{chunk-5RXB4S5H-CGyCRU9L.js → chunk-5RXB4S5H-D03lNzwi.js} +1 -1
- package/console-app/assets/{chunk-6Q2QTUOP-D0IK0SMT.js → chunk-6Q2QTUOP-DloGRHJZ.js} +1 -1
- package/console-app/assets/{chunk-7Z6QIM7H-D12vS1jX.js → chunk-7Z6QIM7H-DH3uHtl2.js} +1 -1
- package/console-app/assets/{chunk-GF5L2VYU-kOLwTzIA.js → chunk-GF5L2VYU-Cu252lza.js} +1 -1
- package/console-app/assets/{chunk-I66GZJ75-Cb2nDoa3.js → chunk-I66GZJ75-DH54FcuO.js} +1 -1
- package/console-app/assets/{chunk-JQJVKLGR-B3a_ctfQ.js → chunk-JQJVKLGR-CeFuTL-V.js} +1 -1
- package/console-app/assets/{chunk-KBJHAD2P-DeP2Lqwp.js → chunk-KBJHAD2P-B-prFo0-.js} +1 -1
- package/console-app/assets/{chunk-NSK5VX7P-BO8vlGrO.js → chunk-NSK5VX7P-C10iA4GS.js} +1 -1
- package/console-app/assets/{chunk-QR6OTTB3-sX5V0Lok.js → chunk-QR6OTTB3-Cd8apAsA.js} +1 -1
- package/console-app/assets/{chunk-RYQCIY6F-DoCObEhk.js → chunk-RYQCIY6F-rfPUp2Fj.js} +1 -1
- package/console-app/assets/{chunk-UBXNYLIW-BndMXU5r.js → chunk-UBXNYLIW-D25wqJBc.js} +1 -1
- package/console-app/assets/{chunk-W5SLKNZC--VrZN8EC.js → chunk-W5SLKNZC-CJvxkxuK.js} +1 -1
- package/console-app/assets/{chunk-WRU74C26-DdS0mzXO.js → chunk-WRU74C26-B9Wju92P.js} +1 -1
- package/console-app/assets/chunk-XXDRQBXY-BU_UP6f5.js +1 -0
- package/console-app/assets/classDiagram-JCYQIIEL-CLBKUuxj.js +1 -0
- package/console-app/assets/classDiagram-v2-OCEON4UE-CLBKUuxj.js +1 -0
- package/console-app/assets/{cose-bilkent-JH36ORCC-C5EnHkFn.js → cose-bilkent-JH36ORCC-CwA1VCP5.js} +1 -1
- package/console-app/assets/{cynefin-VYW2F7L2-9irdkTt4.js → cynefin-VYW2F7L2-C_qsD7eR.js} +1 -1
- package/console-app/assets/{cynefinDiagram-MW4NZA55-DY19660c.js → cynefinDiagram-MW4NZA55-CkSLreFW.js} +1 -1
- package/console-app/assets/{dagre-VZM6K2ZE-BYFZSaSb.js → dagre-VZM6K2ZE-0ssqLoHs.js} +1 -1
- package/console-app/assets/{dagre-nXgDYqvf.js → dagre-WqgBlKb7.js} +1 -1
- package/console-app/assets/{diagram-7IWD3JNH-oDftiTp1.js → diagram-7IWD3JNH-DmRL3lmL.js} +1 -1
- package/console-app/assets/{diagram-B4RE2ZJO-Cbj-pPbH.js → diagram-B4RE2ZJO-Bsr9wG4I.js} +1 -1
- package/console-app/assets/{diagram-LBJQPF4R-Ch53oSKw.js → diagram-LBJQPF4R-DdMw1FSY.js} +1 -1
- package/console-app/assets/{diagram-Q27KOJAE-DA7K814v.js → diagram-Q27KOJAE-TPx5TB2B.js} +1 -1
- package/console-app/assets/{diagram-UB23O5K3-B-_C-ctS.js → diagram-UB23O5K3-e_1zSlZR.js} +1 -1
- package/console-app/assets/{ebnfDiagram-BXEA7PRR-DsOdsKXy.js → ebnfDiagram-BXEA7PRR-BACajh2b.js} +1 -1
- package/console-app/assets/{erDiagram-JOGREHBK-XB1mP5_A.js → erDiagram-JOGREHBK-CojomKqL.js} +1 -1
- package/console-app/assets/eventmodeling-45OFAUF4-DkN2KCrT.js +1 -0
- package/console-app/assets/flowDiagram-UKHOOZJN-DL0M9lBc.js +1 -0
- package/console-app/assets/{ganttDiagram-PKOTCBZU-knk0XSHi.js → ganttDiagram-PKOTCBZU-yoOX4Xj2.js} +1 -1
- package/console-app/assets/{gitGraph-TEB2WS4Q-COJnK2tM.js → gitGraph-TEB2WS4Q-BcUY5hIc.js} +1 -1
- package/console-app/assets/{gitGraphDiagram-DS77QQ5N-sF0v605q.js → gitGraphDiagram-DS77QQ5N-BtuIPjuB.js} +1 -1
- package/console-app/assets/{index-DMnbsgzs.js → index-CzWW052c.js} +151 -151
- package/console-app/assets/{info-DKCQHKI2-Cbatr0w6.js → info-DKCQHKI2-B-iR9PUz.js} +1 -1
- package/console-app/assets/{infoDiagram-6WML65LV-93TFPCu2.js → infoDiagram-6WML65LV-CuXKIFH2.js} +1 -1
- package/console-app/assets/{ishikawaDiagram-WSZJBQD7-BBR8_Qms.js → ishikawaDiagram-WSZJBQD7-CSueMFD6.js} +1 -1
- package/console-app/assets/{journeyDiagram-NVQOT4AX-zG55yDoc.js → journeyDiagram-NVQOT4AX-kvnKStZp.js} +1 -1
- package/console-app/assets/{kanban-definition-27J2QSJJ-DonGcEAY.js → kanban-definition-27J2QSJJ-CGGDEz30.js} +1 -1
- package/console-app/assets/{line-D1WdVm1S.js → line-mtMM5-xO.js} +1 -1
- package/console-app/assets/{linear-D2GQL4Cm.js → linear-CLSje0O_.js} +1 -1
- package/console-app/assets/{mermaid-parser.core-b4yKyO56.js → mermaid-parser.core-Cx6yvn3j.js} +3 -3
- package/console-app/assets/{mermaid.core-Cwfec1gr.js → mermaid.core-BebGyklS.js} +4 -4
- package/console-app/assets/{mindmap-definition-FAOFIHXS-D0G4yKgw.js → mindmap-definition-FAOFIHXS-C2_WUUJ9.js} +1 -1
- package/console-app/assets/{packet-7NZHBO7P-DDa4HL2T.js → packet-7NZHBO7P-Dh5yNCA8.js} +1 -1
- package/console-app/assets/{pegDiagram-VL7TDLO6-fPdN5VZs.js → pegDiagram-VL7TDLO6-XgUxesi8.js} +1 -1
- package/console-app/assets/{pie-RZYD4A2V-DRHURUqo.js → pie-RZYD4A2V-D2x2RAXI.js} +1 -1
- package/console-app/assets/{pieDiagram-7S7Q4E2Y-DMYn8ga_.js → pieDiagram-7S7Q4E2Y-Bbor0KbG.js} +1 -1
- package/console-app/assets/{quadrantDiagram-CIZ2JOQS-C190OamC.js → quadrantDiagram-CIZ2JOQS-DZmnMHpM.js} +1 -1
- package/console-app/assets/{radar-I7S5WNFK-Djvqzz9A.js → radar-I7S5WNFK-CPeriDtN.js} +1 -1
- package/console-app/assets/{railroad-3IZDKUUU-C9YvFeFq.js → railroad-3IZDKUUU-2iKAyaie.js} +1 -1
- package/console-app/assets/railroad-abnf-AHOZXSZD-D26xz4MM.js +1 -0
- package/console-app/assets/railroad-ebnf-EBAXGLYW-CCAiMPWc.js +1 -0
- package/console-app/assets/railroad-peg-LSFZ7HO6-BA1zcisq.js +1 -0
- package/console-app/assets/{railroadDiagram-AXF67PYL-IRFg1VrU.js → railroadDiagram-AXF67PYL-DVEqCXoo.js} +1 -1
- package/console-app/assets/{requirementDiagram-LRYGKXZP-L-7ubbcT.js → requirementDiagram-LRYGKXZP-CSgSNL8p.js} +1 -1
- package/console-app/assets/{sankeyDiagram-W5VNT64P-DBttV6Gt.js → sankeyDiagram-W5VNT64P-CFk11odN.js} +1 -1
- package/console-app/assets/{sequenceDiagram-SI44F4Z6-Bg15oKAB.js → sequenceDiagram-SI44F4Z6-Cb5u6FsJ.js} +1 -1
- package/console-app/assets/{src-DUC7tLWb.js → src-LceHEY_Q.js} +1 -1
- package/console-app/assets/{stateDiagram-OKZ733FA-PWl0XGs5.js → stateDiagram-OKZ733FA-DM1jWlkU.js} +1 -1
- package/console-app/assets/stateDiagram-v2-UEYNNEHI-COVnPYic.js +1 -0
- package/console-app/assets/{swimlanes-SLNWSIFB-CyXEtGBV.js → swimlanes-SLNWSIFB-DD6MchlE.js} +1 -1
- package/console-app/assets/swimlanesDiagram-ULZ7WXOC-B5q8iRCQ.js +8 -0
- package/console-app/assets/{timeline-definition-Z64GVDOM-BLA-DhBt.js → timeline-definition-Z64GVDOM-zu5cNnVv.js} +1 -1
- package/console-app/assets/{treeView-QDETBFTQ-Bfnmn2UY.js → treeView-QDETBFTQ-D_4IbvCG.js} +1 -1
- package/console-app/assets/{treemap-6X3UGDF4-DTA0wveO.js → treemap-6X3UGDF4-DNc6bjUc.js} +1 -1
- package/console-app/assets/{vennDiagram-T6HMQDX7-CLXYZln0.js → vennDiagram-T6HMQDX7-DfqoG28f.js} +1 -1
- package/console-app/assets/{wardley-OPB4EBWU-zwxqisG0.js → wardley-OPB4EBWU-BqpI2Lx-.js} +1 -1
- package/console-app/assets/{wardleyDiagram-T6FBY63Y-hKtKvNlv.js → wardleyDiagram-T6FBY63Y-CLIZSvNi.js} +1 -1
- package/console-app/assets/{xychartDiagram-ELKLHX3M-BGf8p4bo.js → xychartDiagram-ELKLHX3M-Cni-XXa6.js} +1 -1
- package/console-app/index.html +1 -1
- package/dist/.pikku/addon/index.d.ts +1 -1
- package/dist/.pikku/addon/index.js +1 -1
- package/dist/.pikku/addon/pikku-addon-types.gen.d.ts +1 -1
- package/dist/.pikku/addon/pikku-addon-types.gen.js +1 -1
- package/dist/.pikku/agent/index.d.ts +1 -1
- package/dist/.pikku/agent/index.js +1 -1
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/agent/pikku-model-aliases.gen.js +1 -1
- package/dist/.pikku/auth/index.d.ts +1 -1
- package/dist/.pikku/auth/index.js +1 -1
- package/dist/.pikku/auth/pikku-auth-types.gen.d.ts +1 -1
- package/dist/.pikku/auth/pikku-auth-types.gen.js +1 -1
- package/dist/.pikku/channel/index.d.ts +1 -1
- package/dist/.pikku/channel/index.js +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/cli/index.d.ts +1 -1
- package/dist/.pikku/cli/index.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +26 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +103 -0
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +116 -0
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/error/index.d.ts +1 -1
- package/dist/.pikku/error/index.js +1 -1
- package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
- package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
- package/dist/.pikku/function/index.d.ts +1 -1
- package/dist/.pikku/function/index.js +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +114 -21
- package/dist/.pikku/function/pikku-functions.gen.js +1 -1
- package/dist/.pikku/gateway/index.d.ts +1 -1
- package/dist/.pikku/gateway/index.js +1 -1
- package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
- package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
- package/dist/.pikku/http/index.d.ts +1 -1
- package/dist/.pikku/http/index.js +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/mcp/index.d.ts +1 -1
- package/dist/.pikku/mcp/index.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/middleware/index.d.ts +1 -1
- package/dist/.pikku/middleware/index.js +1 -1
- package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +1 -1
- package/dist/.pikku/middleware/pikku-middleware-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +1 -1
- package/dist/.pikku/queue/index.d.ts +1 -1
- package/dist/.pikku/queue/index.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +5 -0
- package/dist/.pikku/scenarios/index.d.ts +1 -1
- package/dist/.pikku/scenarios/index.js +1 -1
- package/dist/.pikku/scenarios/pikku-personas.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
- package/dist/.pikku/scheduler/index.d.ts +1 -1
- package/dist/.pikku/scheduler/index.js +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +19 -1
- package/dist/.pikku/schemas/schemas/FabricFindingsClearInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricFindingsClearOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricFindingsFlushInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricFindingsFlushOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricFindingsListInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricFindingsListOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricReportInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricReportOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricSecretsListInput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/FabricSecretsRotateInput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/FabricSecretsSetInput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/FabricVariablesGetInput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/FabricVariablesSetInput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PersonaSecretInput.schema.json +1 -0
- package/dist/.pikku/scopes/index.d.ts +1 -1
- package/dist/.pikku/scopes/index.js +1 -1
- package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
- package/dist/.pikku/secrets/index.d.ts +1 -1
- package/dist/.pikku/secrets/index.js +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/setup/index.d.ts +1 -1
- package/dist/.pikku/setup/index.js +1 -1
- package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
- package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
- package/dist/.pikku/trigger/index.d.ts +1 -1
- package/dist/.pikku/trigger/index.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/index.d.ts +1 -1
- package/dist/.pikku/variables/index.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/index.d.ts +1 -1
- package/dist/.pikku/workflow/index.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/bin/pikku-bin.mjs +2 -2
- package/dist/src/cli.wiring.js +6 -1
- package/dist/src/deploy/bundler/bun-bundler.js +12 -5
- package/dist/src/fabric/fabric-commands.d.ts +153 -15
- package/dist/src/fabric/fabric-commands.js +65 -0
- package/dist/src/fabric/functions/deploy.function.js +12 -2
- package/dist/src/fabric/functions/findings-clear.function.d.ts +12 -0
- package/dist/src/fabric/functions/findings-clear.function.js +17 -0
- package/dist/src/fabric/functions/findings-flush.function.d.ts +33 -0
- package/dist/src/fabric/functions/findings-flush.function.js +36 -0
- package/dist/src/fabric/functions/findings-list.function.d.ts +55 -0
- package/dist/src/fabric/functions/findings-list.function.js +52 -0
- package/dist/src/fabric/functions/report.function.d.ts +121 -0
- package/dist/src/fabric/functions/report.function.js +84 -0
- package/dist/src/fabric/functions/secrets-list.function.d.ts +4 -4
- package/dist/src/fabric/functions/secrets-list.function.js +4 -4
- package/dist/src/fabric/functions/secrets-rotate.function.d.ts +4 -4
- package/dist/src/fabric/functions/secrets-rotate.function.js +8 -5
- package/dist/src/fabric/functions/secrets-set.function.d.ts +4 -4
- package/dist/src/fabric/functions/secrets-set.function.js +4 -4
- package/dist/src/fabric/functions/validate.function.js +49 -24
- package/dist/src/fabric/functions/variables-get.function.d.ts +4 -4
- package/dist/src/fabric/functions/variables-get.function.js +4 -4
- package/dist/src/fabric/functions/variables-set.function.d.ts +4 -4
- package/dist/src/fabric/functions/variables-set.function.js +4 -4
- package/dist/src/fabric/lib/blank-comments.d.ts +21 -0
- package/dist/src/fabric/lib/blank-comments.js +71 -0
- package/dist/src/fabric/lib/blank-scenario-meta.d.ts +1 -0
- package/dist/src/fabric/lib/blank-scenario-meta.js +110 -0
- package/dist/src/fabric/lib/deployment.d.ts +26 -0
- package/dist/src/fabric/lib/deployment.js +35 -0
- package/dist/src/fabric/lib/finding-spool.d.ts +56 -0
- package/dist/src/fabric/lib/finding-spool.js +105 -0
- package/dist/src/fabric/lib/finding.d.ts +92 -0
- package/dist/src/fabric/lib/finding.js +156 -0
- package/dist/src/fabric/lib/report-environment.d.ts +53 -0
- package/dist/src/fabric/lib/report-environment.js +119 -0
- package/dist/src/fabric/lib/stage.d.ts +19 -3
- package/dist/src/fabric/lib/stage.js +29 -6
- package/dist/src/functions/commands/db-reset.js +6 -2
- package/dist/src/functions/commands/dev.js +5 -1
- package/dist/src/functions/commands/environment.d.ts +5 -0
- package/dist/src/functions/commands/persona.d.ts +18 -0
- package/dist/src/functions/commands/persona.js +42 -2
- package/dist/src/functions/commands/scenario-browser.d.ts +13 -5
- package/dist/src/functions/commands/scenario.js +1 -0
- package/dist/src/functions/db/check-enums.d.ts +38 -0
- package/dist/src/functions/db/check-enums.js +103 -0
- package/dist/src/functions/db/db-codegen.js +15 -0
- package/dist/src/functions/db/postgres/postgres-introspector.d.ts +14 -0
- package/dist/src/functions/db/postgres/postgres-introspector.js +48 -0
- package/dist/src/functions/db/sqlite/sqlite-introspector.d.ts +3 -4
- package/dist/src/functions/db/sqlite/sqlite-introspector.js +5 -17
- package/dist/src/functions/validate/auth-plugin-checks.d.ts +1 -1
- package/dist/src/functions/validate/auth-plugin-checks.js +8 -5
- package/dist/src/functions/validate/persona-checks.js +8 -6
- package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.js +10 -5
- package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.d.ts +1 -1
- package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.js +3 -3
- package/dist/src/functions/wirings/virtual-user/serialize-virtual-user-functions.d.ts +9 -6
- package/dist/src/functions/wirings/virtual-user/serialize-virtual-user-functions.js +51 -27
- package/dist/src/server/actor-sign-in.d.ts +24 -1
- package/dist/src/server/actor-sign-in.js +45 -2
- package/dist/src/services.js +9 -0
- package/dist/src/utils/persona-credentials.d.ts +23 -2
- package/dist/src/utils/persona-credentials.js +55 -2
- package/dist/src/utils/pikku-cli-config.d.ts +4 -4
- package/dist/src/utils/pikku-cli-config.js +30 -10
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/snippets.json +1 -1
- package/surface.json +102 -52
- package/console-app/assets/architecture-TIHT7OUA-DmG0bXDT.js +0 -1
- package/console-app/assets/channel-BgCT5gon.js +0 -1
- package/console-app/assets/chunk-XXDRQBXY-EP2C9tTO.js +0 -1
- package/console-app/assets/classDiagram-JCYQIIEL-D9wGqB4J.js +0 -1
- package/console-app/assets/classDiagram-v2-OCEON4UE-D9wGqB4J.js +0 -1
- package/console-app/assets/eventmodeling-45OFAUF4-NKFCZ8Fj.js +0 -1
- package/console-app/assets/flowDiagram-UKHOOZJN-BpdPkmum.js +0 -1
- package/console-app/assets/railroad-abnf-AHOZXSZD-B3CYVW4p.js +0 -1
- package/console-app/assets/railroad-ebnf-EBAXGLYW-DrZ2yF62.js +0 -1
- package/console-app/assets/railroad-peg-LSFZ7HO6-CWAmMyxd.js +0 -1
- package/console-app/assets/stateDiagram-v2-UEYNNEHI-Celbgz4u.js +0 -1
- package/console-app/assets/swimlanesDiagram-ULZ7WXOC-zxpNaB4U.js +0 -8
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { resolve, dirname } from 'node:path';
|
|
2
2
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { pikkuSessionlessFunc } from '#pikku/function';
|
|
4
|
-
import { createHttpPersonas, personaEnvironmentRefusal, roleMismatchMessage, verifyPersonaRoles, } from '@pikku/core/persona';
|
|
4
|
+
import { createHttpPersonas, deriveActorSecret, personaEnvironmentRefusal, roleMismatchMessage, verifyPersonaRoles, } from '@pikku/core/persona';
|
|
5
5
|
import { personaVirtualUserTarget, runVirtualUser, catalogueClassification, DISPOSITIONS, prepareVirtualUserRun, } from '@pikku/core/virtual-user';
|
|
6
6
|
import { getSingletonServices, setSingletonServices } from '@pikku/core/state';
|
|
7
7
|
import { resolvePersonas } from '../../utils/resolve-personas.js';
|
|
8
|
-
import { resolvePersonaCredentials } from '../../utils/persona-credentials.js';
|
|
8
|
+
import { ACTOR_SECRET_VARIABLE, PERSONA_SECRETS_VARIABLE, resolvePersonaCredentials, } from '../../utils/persona-credentials.js';
|
|
9
9
|
import { resolveEnvironment } from './environment.js';
|
|
10
10
|
import { createDevAgentRunner } from './dev-agent-runner.js';
|
|
11
11
|
import { formatVirtualUserReport } from './virtual-user-formatter.js';
|
|
@@ -115,6 +115,7 @@ export const personaRun = pikkuSessionlessFunc({
|
|
|
115
115
|
...credentials,
|
|
116
116
|
personas,
|
|
117
117
|
signInPath: env.signInPath,
|
|
118
|
+
sessionPath: env.sessionPath,
|
|
118
119
|
rpcPath: env.rpcPath,
|
|
119
120
|
model: resolvedModel,
|
|
120
121
|
});
|
|
@@ -191,6 +192,45 @@ export const personaRun = pikkuSessionlessFunc({
|
|
|
191
192
|
},
|
|
192
193
|
});
|
|
193
194
|
/** Who this app is for, and what each of them wants, without running anything. */
|
|
195
|
+
/**
|
|
196
|
+
* Mint the actor credential for one persona, or for every declared persona.
|
|
197
|
+
*
|
|
198
|
+
* The root secret is entitled to all of them, so nothing that only needs to be
|
|
199
|
+
* one person should ever hold it. What comes out is bound to that persona's
|
|
200
|
+
* address by the target — presenting it for anyone else is refused — so a CI
|
|
201
|
+
* job, a container or a virtual user can be handed exactly the people it is
|
|
202
|
+
* meant to be and no more.
|
|
203
|
+
*
|
|
204
|
+
* Printed as `id=secret` lines, which is the shape PIKKU_PERSONA_SECRETS reads.
|
|
205
|
+
*/
|
|
206
|
+
export const personaSecret = pikkuSessionlessFunc({
|
|
207
|
+
func: async ({ logger, config, variables, getInspectorState }, input) => {
|
|
208
|
+
const root = await variables.get(ACTOR_SECRET_VARIABLE);
|
|
209
|
+
if (!root) {
|
|
210
|
+
throw new Error(`${ACTOR_SECRET_VARIABLE} is not set — there is no root to derive from. ` +
|
|
211
|
+
`It lives in the environment of whoever mints credentials, and never in pikku.config.json.`);
|
|
212
|
+
}
|
|
213
|
+
const state = await getInspectorState(true, false, false, true);
|
|
214
|
+
const declared = resolvePersonas(state.personas?.definitions ?? [], config.scenarios?.emailDomain);
|
|
215
|
+
const wanted = input.personas?.length
|
|
216
|
+
? input.personas
|
|
217
|
+
: Object.keys(declared);
|
|
218
|
+
const unknown = wanted.filter((id) => !declared[id]);
|
|
219
|
+
if (unknown.length > 0) {
|
|
220
|
+
throw new Error(`No persona ${unknown.map((id) => `'${id}'`).join(', ')} is declared. ` +
|
|
221
|
+
`Declared: ${Object.keys(declared).sort().join(', ') || 'none'}.`);
|
|
222
|
+
}
|
|
223
|
+
const minted = [];
|
|
224
|
+
for (const id of wanted) {
|
|
225
|
+
minted.push(`${id}=${await deriveActorSecret(String(root), declared[id].email)}`);
|
|
226
|
+
}
|
|
227
|
+
for (const line of minted) {
|
|
228
|
+
logger.info(line);
|
|
229
|
+
}
|
|
230
|
+
logger.info('');
|
|
231
|
+
logger.info(`Hand these to a run as ${PERSONA_SECRETS_VARIABLE}='${minted.join(',')}'`);
|
|
232
|
+
},
|
|
233
|
+
});
|
|
194
234
|
export const personaList = pikkuSessionlessFunc({
|
|
195
235
|
func: async ({ logger, config, getInspectorState }) => {
|
|
196
236
|
const state = await getInspectorState(true, false, false, true);
|
|
@@ -9,12 +9,16 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { ScenarioArtifact, ScenarioBrowserFailure, ScenarioBrowserProvider } from '@pikku/core/scenario';
|
|
11
11
|
import type { ResolvedPersona } from '@pikku/core/services';
|
|
12
|
-
import type { OperatorSignInOptions } from '@pikku/core/persona';
|
|
12
|
+
import type { ActorSecretResolver, OperatorSignInOptions } from '@pikku/core/persona';
|
|
13
13
|
/** The default driver, used when a project names no other. */
|
|
14
14
|
export declare const DEFAULT_BROWSER_DRIVER = "@pikku/playwright";
|
|
15
15
|
export interface ScenarioBrowserDriverOptions {
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* The root actor secret, for a local `pikku dev` target — each persona's
|
|
18
|
+
* credential is derived from it. A resolver instead when this run was handed
|
|
19
|
+
* credentials for only some personas.
|
|
20
|
+
*/
|
|
21
|
+
secret?: string | ActorSecretResolver;
|
|
18
22
|
/** Fabric operator credentials, for a deployed target. */
|
|
19
23
|
operator?: OperatorSignInOptions;
|
|
20
24
|
actors: Record<string, ResolvedPersona>;
|
|
@@ -72,8 +76,12 @@ export interface ResolveScenarioBrowserProviderOptions {
|
|
|
72
76
|
appUrl?: string;
|
|
73
77
|
/** Base url per app, for a product whose personas sign into more than one. */
|
|
74
78
|
appUrls?: Record<string, string>;
|
|
75
|
-
/**
|
|
76
|
-
|
|
79
|
+
/**
|
|
80
|
+
* The root actor secret, for a local `pikku dev` target — each persona's
|
|
81
|
+
* credential is derived from it. A resolver instead when this run was handed
|
|
82
|
+
* credentials for only some personas.
|
|
83
|
+
*/
|
|
84
|
+
secret?: string | ActorSecretResolver;
|
|
77
85
|
/** Fabric operator credentials, for a deployed target. */
|
|
78
86
|
operator?: OperatorSignInOptions;
|
|
79
87
|
actors: Record<string, ResolvedPersona>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `CHECK (col IN ('a','b',…))` constraint is an enum by another name, and
|
|
3
|
+
* both introspectors read one back as SQL text: SQLite stores the `CREATE
|
|
4
|
+
* TABLE` verbatim, Postgres hands back a normalised `pg_get_constraintdef`.
|
|
5
|
+
* The parsing they need differs, but the quoting rules do not, so both go
|
|
6
|
+
* through here.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Blank out `--` line comments and `/* … *\/` block comments, leaving a string
|
|
10
|
+
* of the same shape so offsets and line breaks still line up.
|
|
11
|
+
*
|
|
12
|
+
* Quoted text is skipped rather than scanned: `'a--b'` is a value, not a
|
|
13
|
+
* comment. The reverse — reading a comment as SQL — is what makes this
|
|
14
|
+
* necessary at all: an apostrophe in `-- the clinician's copy` opens a string
|
|
15
|
+
* that runs to the next quote in the real list, and a bracket in `-- (see
|
|
16
|
+
* below)` closes the list early. Either way the CHECK survives as a union that
|
|
17
|
+
* is wrong, which is worse than no union.
|
|
18
|
+
*/
|
|
19
|
+
export declare function stripSqlComments(sql: string): string;
|
|
20
|
+
/** The single-quoted literals in `text`, with doubled quotes unescaped. */
|
|
21
|
+
export declare function quotedValues(text: string): string[];
|
|
22
|
+
/**
|
|
23
|
+
* Map each column constrained by a `CHECK (col IN (…))` in a `CREATE TABLE`
|
|
24
|
+
* statement to its allowed values. Only the positive `col IN (…)` form is
|
|
25
|
+
* recognised (the convention); `NOT IN`, ranges and boolean expressions are
|
|
26
|
+
* left for the column to stay a plain string.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseCheckEnumsFromDdl(ddl: string): Map<string, string[]>;
|
|
29
|
+
/**
|
|
30
|
+
* The allowed values in a Postgres check constraint, or `undefined` when the
|
|
31
|
+
* constraint is not an enumeration.
|
|
32
|
+
*
|
|
33
|
+
* Postgres does not store the `IN (…)` you wrote — `pg_get_constraintdef`
|
|
34
|
+
* rewrites it as `= ANY (ARRAY[…])`, with each element cast to the column's
|
|
35
|
+
* type. Matching that shape is deliberate: `NOT IN` normalises to `<> ALL
|
|
36
|
+
* (ARRAY[…])` and a range to `>`/`<`, and neither is an enum.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseCheckEnumValues(constraintDef: string): string[] | undefined;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `CHECK (col IN ('a','b',…))` constraint is an enum by another name, and
|
|
3
|
+
* both introspectors read one back as SQL text: SQLite stores the `CREATE
|
|
4
|
+
* TABLE` verbatim, Postgres hands back a normalised `pg_get_constraintdef`.
|
|
5
|
+
* The parsing they need differs, but the quoting rules do not, so both go
|
|
6
|
+
* through here.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Blank out `--` line comments and `/* … *\/` block comments, leaving a string
|
|
10
|
+
* of the same shape so offsets and line breaks still line up.
|
|
11
|
+
*
|
|
12
|
+
* Quoted text is skipped rather than scanned: `'a--b'` is a value, not a
|
|
13
|
+
* comment. The reverse — reading a comment as SQL — is what makes this
|
|
14
|
+
* necessary at all: an apostrophe in `-- the clinician's copy` opens a string
|
|
15
|
+
* that runs to the next quote in the real list, and a bracket in `-- (see
|
|
16
|
+
* below)` closes the list early. Either way the CHECK survives as a union that
|
|
17
|
+
* is wrong, which is worse than no union.
|
|
18
|
+
*/
|
|
19
|
+
export function stripSqlComments(sql) {
|
|
20
|
+
let out = '';
|
|
21
|
+
let i = 0;
|
|
22
|
+
while (i < sql.length) {
|
|
23
|
+
const c = sql[i];
|
|
24
|
+
if (c === "'" || c === '"') {
|
|
25
|
+
const end = closingQuote(sql, i);
|
|
26
|
+
out += sql.slice(i, end);
|
|
27
|
+
i = end;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (c === '-' && sql[i + 1] === '-') {
|
|
31
|
+
while (i < sql.length && sql[i] !== '\n') {
|
|
32
|
+
out += ' ';
|
|
33
|
+
i++;
|
|
34
|
+
}
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (c === '/' && sql[i + 1] === '*') {
|
|
38
|
+
const close = sql.indexOf('*/', i + 2);
|
|
39
|
+
const end = close === -1 ? sql.length : close + 2;
|
|
40
|
+
for (let k = i; k < end; k++)
|
|
41
|
+
out += sql[k] === '\n' ? '\n' : ' ';
|
|
42
|
+
i = end;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
out += c;
|
|
46
|
+
i++;
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/** Index just past the quoted run starting at `start`, doubled quotes included. */
|
|
51
|
+
function closingQuote(sql, start) {
|
|
52
|
+
const quote = sql[start];
|
|
53
|
+
let i = start + 1;
|
|
54
|
+
while (i < sql.length) {
|
|
55
|
+
if (sql[i] === quote) {
|
|
56
|
+
if (sql[i + 1] === quote) {
|
|
57
|
+
i += 2;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
return i + 1;
|
|
61
|
+
}
|
|
62
|
+
i++;
|
|
63
|
+
}
|
|
64
|
+
return sql.length;
|
|
65
|
+
}
|
|
66
|
+
/** The single-quoted literals in `text`, with doubled quotes unescaped. */
|
|
67
|
+
export function quotedValues(text) {
|
|
68
|
+
return [...text.matchAll(/'((?:[^']|'')*)'/g)].map((m) => m[1].replace(/''/g, "'"));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Map each column constrained by a `CHECK (col IN (…))` in a `CREATE TABLE`
|
|
72
|
+
* statement to its allowed values. Only the positive `col IN (…)` form is
|
|
73
|
+
* recognised (the convention); `NOT IN`, ranges and boolean expressions are
|
|
74
|
+
* left for the column to stay a plain string.
|
|
75
|
+
*/
|
|
76
|
+
export function parseCheckEnumsFromDdl(ddl) {
|
|
77
|
+
const out = new Map();
|
|
78
|
+
const sql = stripSqlComments(ddl);
|
|
79
|
+
const checkIn = /CHECK\s*\(\s*"?(\w+)"?\s+IN\s*\(([^)]*)\)/gi;
|
|
80
|
+
let m;
|
|
81
|
+
while ((m = checkIn.exec(sql))) {
|
|
82
|
+
const values = quotedValues(m[2]);
|
|
83
|
+
if (values.length > 0)
|
|
84
|
+
out.set(m[1], values);
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The allowed values in a Postgres check constraint, or `undefined` when the
|
|
90
|
+
* constraint is not an enumeration.
|
|
91
|
+
*
|
|
92
|
+
* Postgres does not store the `IN (…)` you wrote — `pg_get_constraintdef`
|
|
93
|
+
* rewrites it as `= ANY (ARRAY[…])`, with each element cast to the column's
|
|
94
|
+
* type. Matching that shape is deliberate: `NOT IN` normalises to `<> ALL
|
|
95
|
+
* (ARRAY[…])` and a range to `>`/`<`, and neither is an enum.
|
|
96
|
+
*/
|
|
97
|
+
export function parseCheckEnumValues(constraintDef) {
|
|
98
|
+
const any = /=\s*ANY\s*\(\s*\(?\s*ARRAY\s*\[([^\]]*)\]/i.exec(constraintDef);
|
|
99
|
+
if (!any)
|
|
100
|
+
return undefined;
|
|
101
|
+
const values = quotedValues(any[1]);
|
|
102
|
+
return values.length > 0 ? values : undefined;
|
|
103
|
+
}
|
|
@@ -641,6 +641,21 @@ export async function generateSchemaTypes(introspector, options) {
|
|
|
641
641
|
const coercionBody = [
|
|
642
642
|
`// Generated by @pikku/cli — do not edit by hand.`,
|
|
643
643
|
`// Run \`pikku db migrate\` to refresh.`,
|
|
644
|
+
`//`,
|
|
645
|
+
`// Nothing applies this for you. Attach it where the kysely singleton is`,
|
|
646
|
+
`// created, in createSingletonServices:`,
|
|
647
|
+
`//`,
|
|
648
|
+
`// import { createCoercionPlugin } from '@pikku/kysely'`,
|
|
649
|
+
`// import { coercionMap } from '#pikku/db/coercion.gen.js'`,
|
|
650
|
+
`//`,
|
|
651
|
+
`// kysely: existingServices.kysely.withPlugin(`,
|
|
652
|
+
`// createCoercionPlugin({ map: coercionMap }),`,
|
|
653
|
+
`// )`,
|
|
654
|
+
`//`,
|
|
655
|
+
`// Unwired, this file is invisible locally and fatal deployed: the dev`,
|
|
656
|
+
`// driver hydrates a TEXT date into a Date by itself, a deployed one`,
|
|
657
|
+
`// returns the raw string, and the generated schema types the column`,
|
|
658
|
+
`// \`Date\` either way — so tsc flags nothing and no local test can fail.`,
|
|
644
659
|
``,
|
|
645
660
|
`export const coercionMap = {`,
|
|
646
661
|
coercionEntries,
|
|
@@ -12,6 +12,20 @@ export declare class PostgresIntrospector implements DbIntrospector {
|
|
|
12
12
|
connect(): Promise<void>;
|
|
13
13
|
listTables(): Promise<string[]>;
|
|
14
14
|
getColumns(table: string): Promise<ColumnInfo[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Columns constrained to a value list by a `CHECK (col IN (…))`, keyed like
|
|
17
|
+
* {@link getAllColumns} then by column name. Postgres has native enums, but
|
|
18
|
+
* a CHECK is the other half of how a schema says "one of these" — and the
|
|
19
|
+
* one an app written against SQLite as well ends up using, since SQLite has
|
|
20
|
+
* no enums at all. Left as plain `string`, every caller narrows it by hand.
|
|
21
|
+
*
|
|
22
|
+
* Only single-column constraints are read: `conkey` naming two columns means
|
|
23
|
+
* neither is restricted to the list on its own.
|
|
24
|
+
*
|
|
25
|
+
* `table` limits the sweep to one table; omitted, it covers the schema. Both
|
|
26
|
+
* are one round-trip — see {@link getAllColumns}.
|
|
27
|
+
*/
|
|
28
|
+
private getCheckEnums;
|
|
15
29
|
getForeignKeys(table: string): Promise<ForeignKeyInfo[]>;
|
|
16
30
|
getAllColumns(): Promise<Map<string, ColumnInfo[]>>;
|
|
17
31
|
getAllForeignKeys(): Promise<Map<string, ForeignKeyInfo[]>>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Pool } from 'pg';
|
|
2
2
|
import { MIGRATION_TRACKING_TABLE } from '../db-migrator.js';
|
|
3
|
+
import { parseCheckEnumValues } from '../check-enums.js';
|
|
3
4
|
/** Table display name matching `listTables` (schema-qualified unless `public`). */
|
|
4
5
|
function tableKey(schema, table) {
|
|
5
6
|
return schema === 'public' ? table : `${schema}.${table}`;
|
|
@@ -85,6 +86,8 @@ export class PostgresIntrospector {
|
|
|
85
86
|
FROM information_schema.columns c
|
|
86
87
|
WHERE c.table_schema = $2 AND c.table_name = $1
|
|
87
88
|
ORDER BY c.ordinal_position`, [tableName, schema]);
|
|
89
|
+
const checkEnums = (await this.getCheckEnums({ schema, name: tableName })).get(table) ??
|
|
90
|
+
new Map();
|
|
88
91
|
return result.rows.map((r) => ({
|
|
89
92
|
name: r.column_name,
|
|
90
93
|
type: resolveColumnType(r.data_type, r.udt_name),
|
|
@@ -93,8 +96,51 @@ export class PostgresIntrospector {
|
|
|
93
96
|
pk: Boolean(r.is_pk),
|
|
94
97
|
defaultValue: r.column_default,
|
|
95
98
|
generated: r.is_generated === 'ALWAYS',
|
|
99
|
+
enumValues: checkEnums.get(r.column_name),
|
|
96
100
|
}));
|
|
97
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Columns constrained to a value list by a `CHECK (col IN (…))`, keyed like
|
|
104
|
+
* {@link getAllColumns} then by column name. Postgres has native enums, but
|
|
105
|
+
* a CHECK is the other half of how a schema says "one of these" — and the
|
|
106
|
+
* one an app written against SQLite as well ends up using, since SQLite has
|
|
107
|
+
* no enums at all. Left as plain `string`, every caller narrows it by hand.
|
|
108
|
+
*
|
|
109
|
+
* Only single-column constraints are read: `conkey` naming two columns means
|
|
110
|
+
* neither is restricted to the list on its own.
|
|
111
|
+
*
|
|
112
|
+
* `table` limits the sweep to one table; omitted, it covers the schema. Both
|
|
113
|
+
* are one round-trip — see {@link getAllColumns}.
|
|
114
|
+
*/
|
|
115
|
+
async getCheckEnums(table) {
|
|
116
|
+
const result = await this.client.query(`SELECT n.nspname AS table_schema,
|
|
117
|
+
cl.relname AS table_name,
|
|
118
|
+
a.attname AS column_name,
|
|
119
|
+
pg_get_constraintdef(co.oid) AS def
|
|
120
|
+
FROM pg_constraint co
|
|
121
|
+
JOIN pg_class cl ON cl.oid = co.conrelid
|
|
122
|
+
JOIN pg_namespace n ON n.oid = cl.relnamespace
|
|
123
|
+
JOIN pg_attribute a ON a.attrelid = co.conrelid AND a.attnum = co.conkey[1]
|
|
124
|
+
WHERE co.contype = 'c'
|
|
125
|
+
AND array_length(co.conkey, 1) = 1
|
|
126
|
+
AND n.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
|
|
127
|
+
AND n.nspname NOT LIKE 'pg_temp_%'
|
|
128
|
+
AND ($1::text IS NULL OR (n.nspname = $1 AND cl.relname = $2))`, [table?.schema ?? null, table?.name ?? null]);
|
|
129
|
+
const byTable = new Map();
|
|
130
|
+
for (const r of result.rows) {
|
|
131
|
+
const values = parseCheckEnumValues(r.def);
|
|
132
|
+
if (!values)
|
|
133
|
+
continue;
|
|
134
|
+
const key = tableKey(r.table_schema, r.table_name);
|
|
135
|
+
let cols = byTable.get(key);
|
|
136
|
+
if (!cols) {
|
|
137
|
+
cols = new Map();
|
|
138
|
+
byTable.set(key, cols);
|
|
139
|
+
}
|
|
140
|
+
cols.set(r.column_name, values);
|
|
141
|
+
}
|
|
142
|
+
return byTable;
|
|
143
|
+
}
|
|
98
144
|
async getForeignKeys(table) {
|
|
99
145
|
const dotIdx = table.indexOf('.');
|
|
100
146
|
const schema = dotIdx >= 0 ? table.slice(0, dotIdx) : 'public';
|
|
@@ -153,6 +199,7 @@ export class PostgresIntrospector {
|
|
|
153
199
|
WHERE c.table_schema NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
|
|
154
200
|
AND c.table_schema NOT LIKE 'pg_temp_%'
|
|
155
201
|
ORDER BY c.table_schema, c.table_name, c.ordinal_position`);
|
|
202
|
+
const checkEnums = await this.getCheckEnums();
|
|
156
203
|
const byTable = new Map();
|
|
157
204
|
for (const r of result.rows) {
|
|
158
205
|
if (isMigrationTracking(r.table_name))
|
|
@@ -171,6 +218,7 @@ export class PostgresIntrospector {
|
|
|
171
218
|
pk: Boolean(r.is_pk),
|
|
172
219
|
defaultValue: r.column_default,
|
|
173
220
|
generated: r.is_generated === 'ALWAYS',
|
|
221
|
+
enumValues: checkEnums.get(key)?.get(r.column_name),
|
|
174
222
|
});
|
|
175
223
|
}
|
|
176
224
|
return byTable;
|
|
@@ -7,10 +7,9 @@ export declare class SqliteIntrospector implements DbIntrospector {
|
|
|
7
7
|
getColumns(table: string): Promise<ColumnInfo[]>;
|
|
8
8
|
/**
|
|
9
9
|
* SQLite has no native enums, but a `CHECK (col IN ('a','b',…))` constraint is
|
|
10
|
-
* an enum by another name.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `NOT IN`, ranges, and boolean expressions are left as plain `string`.
|
|
10
|
+
* an enum by another name. Read back the table's stored `CREATE TABLE` DDL —
|
|
11
|
+
* which SQLite keeps verbatim, comments included — and map each constrained
|
|
12
|
+
* column to its allowed values so codegen can type it as a union.
|
|
14
13
|
*/
|
|
15
14
|
private parseCheckEnums;
|
|
16
15
|
getForeignKeys(table: string): Promise<ForeignKeyInfo[]>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MIGRATION_TRACKING_TABLE } from '../db-migrator.js';
|
|
2
|
+
import { parseCheckEnumsFromDdl } from '../check-enums.js';
|
|
2
3
|
const SKIP_TABLES = new Set(['sqlite_sequence', MIGRATION_TRACKING_TABLE]);
|
|
3
4
|
export class SqliteIntrospector {
|
|
4
5
|
db;
|
|
@@ -34,28 +35,15 @@ export class SqliteIntrospector {
|
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
36
37
|
* SQLite has no native enums, but a `CHECK (col IN ('a','b',…))` constraint is
|
|
37
|
-
* an enum by another name.
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* `NOT IN`, ranges, and boolean expressions are left as plain `string`.
|
|
38
|
+
* an enum by another name. Read back the table's stored `CREATE TABLE` DDL —
|
|
39
|
+
* which SQLite keeps verbatim, comments included — and map each constrained
|
|
40
|
+
* column to its allowed values so codegen can type it as a union.
|
|
41
41
|
*/
|
|
42
42
|
parseCheckEnums(table) {
|
|
43
|
-
const out = new Map();
|
|
44
43
|
const row = this.db
|
|
45
44
|
.prepare(`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?`)
|
|
46
45
|
.get(table);
|
|
47
|
-
|
|
48
|
-
if (!ddl)
|
|
49
|
-
return out;
|
|
50
|
-
const checkIn = /CHECK\s*\(\s*"?(\w+)"?\s+IN\s*\(([^)]*)\)/gi;
|
|
51
|
-
let m;
|
|
52
|
-
while ((m = checkIn.exec(ddl))) {
|
|
53
|
-
const column = m[1];
|
|
54
|
-
const values = [...m[2].matchAll(/'((?:[^']|'')*)'/g)].map((q) => q[1].replace(/''/g, "'"));
|
|
55
|
-
if (values.length > 0)
|
|
56
|
-
out.set(column, values);
|
|
57
|
-
}
|
|
58
|
-
return out;
|
|
46
|
+
return row?.sql ? parseCheckEnumsFromDdl(row.sql) : new Map();
|
|
59
47
|
}
|
|
60
48
|
async getForeignKeys(table) {
|
|
61
49
|
const escaped = `"${table.replace(/"/g, '""')}"`;
|
|
@@ -10,7 +10,7 @@ export type AuthPluginCheckConfig = {
|
|
|
10
10
|
* running two authorization models and projecting one onto the other. The
|
|
11
11
|
* inspector refuses it outright at prebuild; this reports the same thing
|
|
12
12
|
* without one, and additionally catches the quieter half of the migration, an
|
|
13
|
-
* app that dropped `admin()` and never wired `
|
|
13
|
+
* app that dropped `admin()` and never wired `pikkuBan()`, which keeps its ban
|
|
14
14
|
* columns and its ban UI while silently enforcing nothing.
|
|
15
15
|
*/
|
|
16
16
|
export declare const runAuthPluginChecks: (root: string, config: AuthPluginCheckConfig | null) => Promise<ValidateFinding[]>;
|
|
@@ -113,7 +113,7 @@ const configuresPlugin = (text, module, exported) => {
|
|
|
113
113
|
* running two authorization models and projecting one onto the other. The
|
|
114
114
|
* inspector refuses it outright at prebuild; this reports the same thing
|
|
115
115
|
* without one, and additionally catches the quieter half of the migration, an
|
|
116
|
-
* app that dropped `admin()` and never wired `
|
|
116
|
+
* app that dropped `admin()` and never wired `pikkuBan()`, which keeps its ban
|
|
117
117
|
* columns and its ban UI while silently enforcing nothing.
|
|
118
118
|
*/
|
|
119
119
|
export const runAuthPluginChecks = async (root, config) => {
|
|
@@ -131,7 +131,10 @@ export const runAuthPluginChecks = async (root, config) => {
|
|
|
131
131
|
continue;
|
|
132
132
|
if (configuresPlugin(text, 'better-auth/plugins', 'admin'))
|
|
133
133
|
adminFiles.push(file);
|
|
134
|
-
|
|
134
|
+
// `pikkuBan` is the current export; `ban` is the deprecated alias, which
|
|
135
|
+
// configures exactly the same plugin and so must satisfy the check too.
|
|
136
|
+
if (configuresPlugin(text, '@pikku/better-auth', 'pikkuBan') ||
|
|
137
|
+
configuresPlugin(text, '@pikku/better-auth', 'ban'))
|
|
135
138
|
bansAnywhere = true;
|
|
136
139
|
}
|
|
137
140
|
for (const file of adminFiles) {
|
|
@@ -140,7 +143,7 @@ export const runAuthPluginChecks = async (root, config) => {
|
|
|
140
143
|
severity: 'error',
|
|
141
144
|
message: "better-auth's admin() plugin is not supported — it authorizes against a user.role column while pikku authorizes on scopes, so one has to be projected onto the other",
|
|
142
145
|
path: file,
|
|
143
|
-
fixHint: lines('Replace it with
|
|
146
|
+
fixHint: lines('Replace it with pikkuBan(), which keeps the one capability admin() had', 'that pikku cannot supply from outside better-auth — refusing a banned', 'user a session:', " import { pikkuBan } from '@pikku/better-auth'", ' betterAuth({ plugins: [pikkuBan()] })', 'User management is already scoped RPCs in @pikku/addon-admin: list,', 'create, ban, remove, revoke sessions and set password.'),
|
|
144
147
|
});
|
|
145
148
|
}
|
|
146
149
|
if (!bansAnywhere && adminFiles.length === 0) {
|
|
@@ -153,9 +156,9 @@ export const runAuthPluginChecks = async (root, config) => {
|
|
|
153
156
|
findings.push({
|
|
154
157
|
id: 'better-auth-no-ban-plugin',
|
|
155
158
|
severity: 'warn',
|
|
156
|
-
message: 'better-auth is configured without
|
|
159
|
+
message: 'better-auth is configured without pikkuBan() — the banned/banReason/banExpires columns do not exist, so banning a user through @pikku/addon-admin has nothing to write and nothing refuses their next sign-in',
|
|
157
160
|
path: file,
|
|
158
|
-
fixHint: lines(" import {
|
|
161
|
+
fixHint: lines(" import { pikkuBan } from '@pikku/better-auth'", ' betterAuth({ plugins: [pikkuBan()] })', 'Wire it wherever the rest of your plugins are, then run', '`pikku db migrate` to add the columns.'),
|
|
159
162
|
});
|
|
160
163
|
break;
|
|
161
164
|
}
|
|
@@ -81,17 +81,19 @@ const countPersonas = async (root, config, sourceFiles) => {
|
|
|
81
81
|
/**
|
|
82
82
|
* Whether a persona can actually sign in.
|
|
83
83
|
*
|
|
84
|
-
* Two shapes count. `
|
|
85
|
-
* wires, and a hand-rolled `/sign-in/actor` route is the one a project
|
|
86
|
-
* not use better-auth ends up with. Both terminate at the same
|
|
87
|
-
* either satisfies the check.
|
|
84
|
+
* Two shapes count. `pikkuActor()` from `@pikku/better-auth` is the one the
|
|
85
|
+
* scaffold wires, and a hand-rolled `/sign-in/actor` route is the one a project
|
|
86
|
+
* that does not use better-auth ends up with. Both terminate at the same
|
|
87
|
+
* endpoint, so either satisfies the check. The deprecated `actor()` alias is
|
|
88
|
+
* matched too — it is the same plugin under its old name.
|
|
88
89
|
*/
|
|
89
90
|
const hasActorSignIn = async (sourceFiles) => {
|
|
90
91
|
for (const file of sourceFiles) {
|
|
91
92
|
const text = await readTextSafe(file);
|
|
92
93
|
if (!text)
|
|
93
94
|
continue;
|
|
94
|
-
const wiresPlugin = /@pikku\/better-auth/.test(text) &&
|
|
95
|
+
const wiresPlugin = /@pikku\/better-auth/.test(text) &&
|
|
96
|
+
/(^|[^\w$.])(pikkuActor|actor)\s*\(/.test(text);
|
|
95
97
|
if (wiresPlugin || /sign-in\/actor/.test(text))
|
|
96
98
|
return true;
|
|
97
99
|
}
|
|
@@ -123,7 +125,7 @@ export const runPersonaChecks = async (root, config) => {
|
|
|
123
125
|
return findings;
|
|
124
126
|
}
|
|
125
127
|
if (!(await hasActorSignIn(sourceFiles))) {
|
|
126
|
-
w('personas-no-actor-sign-in', `${personas} persona${personas === 1 ? ' is' : 's are'} declared, but nothing wires actor sign-in — a scenario run signs in over POST /auth/sign-in/actor, so every persona fails to acquire a session`, pikkuConfigPath, lines('Wire the actor plugin where better-auth is configured:', " import {
|
|
128
|
+
w('personas-no-actor-sign-in', `${personas} persona${personas === 1 ? ' is' : 's are'} declared, but nothing wires actor sign-in — a scenario run signs in over POST /auth/sign-in/actor, so every persona fails to acquire a session`, pikkuConfigPath, lines('Wire the actor plugin where better-auth is configured:', " import { pikkuActor } from '@pikku/better-auth'", ' plugins: [pikkuActor({ secret: SCENARIO_ACTOR_SECRET })]', '`pikku dev` turns the endpoint on and mints the secret itself, so there', 'is nothing to configure locally. A deployed stage that must run', 'scenarios opts in with', 'PIKKU_ALLOW_ACTOR_SIGN_IN=passwordless-actor-sign-in; everywhere else', 'the endpoint stays off whatever the environment happens to contain.'));
|
|
127
129
|
}
|
|
128
130
|
const environments = config?.environments;
|
|
129
131
|
const hasEnvironment = typeof environments === 'object' &&
|
|
@@ -28,12 +28,17 @@ export const pikkuUserAdminFunctions = pikkuSessionlessFunc({
|
|
|
28
28
|
// Ban is the one capability with a schema requirement of its own, and it is
|
|
29
29
|
// only one of the six — a warning rather than a failure, so an app that
|
|
30
30
|
// never bans anyone is not forced to carry the columns.
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
// The plugin ids here are the callee names read off the `plugins: [...]`
|
|
32
|
+
// array, so both the current `pikkuBan()` and the deprecated `ban()` alias
|
|
33
|
+
// have to count as wired.
|
|
34
|
+
const bansUsers = definition.plugins.includes('pikkuBan') ||
|
|
35
|
+
definition.plugins.includes('ban');
|
|
36
|
+
if (!bansUsers) {
|
|
37
|
+
logger.warn(`"scaffold.userAdmin" is enabled but better-auth is configured without the pikkuBan() plugin, ` +
|
|
33
38
|
`so the banned/banReason/banExpires columns do not exist and setUserBanned will fail.\n` +
|
|
34
|
-
`Fix: add
|
|
35
|
-
` import {
|
|
36
|
-
` betterAuth({ plugins: [
|
|
39
|
+
`Fix: add pikkuBan() to the plugins array in ${definition.sourceFile}:\n` +
|
|
40
|
+
` import { pikkuBan } from '@pikku/better-auth'\n` +
|
|
41
|
+
` betterAuth({ plugins: [pikkuBan()] })`);
|
|
37
42
|
}
|
|
38
43
|
const leaf = (name) => getLeafImportPath(config.userAdminFunctionsFile, name, config);
|
|
39
44
|
const { schemas, functions } = serializeUserAdminFunctions(leaf);
|
|
@@ -13,7 +13,7 @@ export interface UserAdminGenOutput {
|
|
|
13
13
|
* codegen on conflicting declarations.
|
|
14
14
|
*
|
|
15
15
|
* Each function is gated on its own `admin:users:*` scope, and works through
|
|
16
|
-
* better-auth's internal adapter. Banning additionally needs the `
|
|
16
|
+
* better-auth's internal adapter. Banning additionally needs the `pikkuBan()` plugin
|
|
17
17
|
* from `@pikku/better-auth` for the columns it writes to exist.
|
|
18
18
|
*
|
|
19
19
|
* Emitted as two files. The schemas are zod, and the inspector reads a zod
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* codegen on conflicting declarations.
|
|
10
10
|
*
|
|
11
11
|
* Each function is gated on its own `admin:users:*` scope, and works through
|
|
12
|
-
* better-auth's internal adapter. Banning additionally needs the `
|
|
12
|
+
* better-auth's internal adapter. Banning additionally needs the `pikkuBan()` plugin
|
|
13
13
|
* from `@pikku/better-auth` for the columns it writes to exist.
|
|
14
14
|
*
|
|
15
15
|
* Emitted as two files. The schemas are zod, and the inspector reads a zod
|
|
@@ -27,7 +27,7 @@ import { z } from 'zod'
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* A user, as the directory sees one. Ban state is optional because those
|
|
30
|
-
* columns belong to the \`
|
|
30
|
+
* columns belong to the \`pikkuBan()\` plugin: a host without it reports no ban state
|
|
31
31
|
* at all, which a client can render as "unknown" rather than as a misleading
|
|
32
32
|
* "not banned".
|
|
33
33
|
*/
|
|
@@ -224,7 +224,7 @@ export const pikkuAdminSetUserBanned = pikkuFunc({
|
|
|
224
224
|
tags: ['pikku'],
|
|
225
225
|
title: 'Ban or Unban User',
|
|
226
226
|
description:
|
|
227
|
-
'Bans a user — revoking their sessions and blocking sign-in — or lifts an existing ban. An expiry lets the ban lapse on its own; without one it holds until it is lifted. Requires better-auth wired with the \`
|
|
227
|
+
'Bans a user — revoking their sessions and blocking sign-in — or lifts an existing ban. An expiry lets the ban lapse on its own; without one it holds until it is lifted. Requires better-auth wired with the \`pikkuBan()\` plugin.',
|
|
228
228
|
expose: true,
|
|
229
229
|
scopes: ['admin:users:ban'],
|
|
230
230
|
input: SetUserBannedInput,
|
|
@@ -12,12 +12,15 @@ export interface VirtualUserGenOutput {
|
|
|
12
12
|
* `pikku persona run` does the same thing from a terminal; this is the same run
|
|
13
13
|
* started over RPC, from CI or from a console, with the result kept.
|
|
14
14
|
*
|
|
15
|
-
* A virtual user is NOT a workflow
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
15
|
+
* A virtual user is still NOT a workflow: it explores, so no two attempts take
|
|
16
|
+
* the same steps and there is nothing to replay. What it does need is a
|
|
17
|
+
* trigger. `runVirtualUser` writes the record and returns the id; the run
|
|
18
|
+
* itself is dispatched onto `pikku-virtual-user-runs`, at one attempt, because
|
|
19
|
+
* a queue is the only dispatch that survives a deployment which puts each
|
|
20
|
+
* function in its own unit — there is no in-process promise to leave running
|
|
21
|
+
* there, and an RPC to a function nothing triggers has nowhere to land. A
|
|
22
|
+
* project with no queue service keeps the in-process dispatch, which is
|
|
23
|
+
* correct for the single process it runs in.
|
|
21
24
|
*
|
|
22
25
|
* Emitted as two files. The schemas are zod, and the inspector reads a zod
|
|
23
26
|
* schema by importing the module that declares it — which it cannot do for the
|