@pikku/cli 0.12.90 → 0.12.92

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 (264) hide show
  1. package/LICENSE +106 -0
  2. package/README.md +25 -2
  3. package/console-app/assets/{index-CSzCJzBb.css → index-D0HG8q0B.css} +1 -1
  4. package/console-app/assets/{index-C5Bd44e4.js → index-DDpIMCpy.js} +149 -149
  5. package/console-app/index.html +2 -2
  6. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  7. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  8. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  9. package/dist/.pikku/cli/pikku-cli-channel.js +6 -1
  10. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  11. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  12. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  13. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  14. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +15 -0
  15. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  16. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  17. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  18. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +43 -0
  19. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  20. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  21. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  22. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  23. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  24. package/dist/.pikku/function/pikku-function-types.gen.d.ts +4 -4
  25. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  26. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  27. package/dist/.pikku/function/pikku-functions-meta.gen.json +39 -44
  28. package/dist/.pikku/function/pikku-functions.gen.js +1 -3
  29. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  30. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  31. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  32. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  33. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +9 -0
  34. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +9 -0
  35. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  36. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  37. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  38. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  39. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  40. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  41. package/dist/.pikku/pikku-types.gen.js +1 -1
  42. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  43. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  44. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  45. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  46. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  47. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  48. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -1
  49. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.d.ts +1 -0
  50. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +10 -0
  51. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.json +1 -0
  52. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +4 -0
  53. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.js +1 -0
  54. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.d.ts +1 -0
  55. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +10 -0
  56. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +4 -0
  57. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.js +1 -0
  58. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +4 -0
  59. package/dist/.pikku/scenarios/schemas/register.gen.js +4 -0
  60. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  61. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  62. package/dist/.pikku/schemas/register.gen.js +5 -3
  63. package/dist/.pikku/schemas/schemas/FabricSecretsListOutput.schema.json +1 -1
  64. package/dist/.pikku/schemas/schemas/FabricSecretsRotateInput.schema.json +1 -0
  65. package/dist/.pikku/schemas/schemas/FabricSecretsRotateOutput.schema.json +1 -0
  66. package/dist/.pikku/schemas/schemas/FabricSecretsSetOutput.schema.json +1 -1
  67. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  68. package/dist/.pikku/schemas/schemas/ScenarioRunInput.schema.json +1 -1
  69. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  70. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  71. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  72. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  73. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  74. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  75. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  76. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  77. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  78. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  79. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  80. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  81. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  82. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +2 -8
  83. package/dist/.pikku/workflow/pikku-scenario-actors.gen.d.ts +19 -0
  84. package/dist/.pikku/workflow/pikku-scenario-actors.gen.js +17 -0
  85. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +152 -3
  86. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +39 -1
  87. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  88. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  89. package/dist/bin/pikku-bin.mjs +2 -2
  90. package/dist/src/cli.wiring.js +28 -0
  91. package/dist/src/deploy/analyzer/analyzer.js +22 -5
  92. package/dist/src/deploy/build-pipeline.js +5 -1
  93. package/dist/src/fabric/fabric-commands.d.ts +37 -9
  94. package/dist/src/fabric/fabric-commands.js +12 -0
  95. package/dist/src/fabric/functions/domains-add.function.d.ts +4 -4
  96. package/dist/src/fabric/functions/secrets-list.function.d.ts +16 -4
  97. package/dist/src/fabric/functions/secrets-list.function.js +12 -10
  98. package/dist/src/fabric/functions/secrets-rotate.function.d.ts +24 -0
  99. package/dist/src/fabric/functions/secrets-rotate.function.js +36 -0
  100. package/dist/src/fabric/functions/secrets-set.function.d.ts +8 -4
  101. package/dist/src/fabric/functions/secrets-set.function.js +18 -7
  102. package/dist/src/fabric/lib/http.d.ts +7 -1
  103. package/dist/src/fabric/lib/sealed-box.d.ts +16 -0
  104. package/dist/src/fabric/lib/sealed-box.js +72 -0
  105. package/dist/src/functions/commands/dev.js +11 -0
  106. package/dist/src/functions/commands/load-user-project.d.ts +7 -0
  107. package/dist/src/functions/commands/load-user-project.js +21 -0
  108. package/dist/src/functions/commands/pikku-command-bootstrap.js +13 -0
  109. package/dist/src/functions/commands/scenario-browser.d.ts +84 -0
  110. package/dist/src/functions/commands/scenario-browser.js +68 -0
  111. package/dist/src/functions/commands/scenario-environment.d.ts +32 -0
  112. package/dist/src/functions/commands/scenario-environment.js +61 -0
  113. package/dist/src/functions/commands/scenario-formatter.d.ts +81 -0
  114. package/dist/src/functions/commands/scenario-formatter.js +120 -0
  115. package/dist/src/functions/commands/scenario-ladder.d.ts +66 -0
  116. package/dist/src/functions/commands/scenario-ladder.js +132 -0
  117. package/dist/src/functions/commands/scenario-plan.d.ts +61 -0
  118. package/dist/src/functions/commands/scenario-plan.js +106 -0
  119. package/dist/src/functions/commands/scenario.d.ts +24 -0
  120. package/dist/src/functions/commands/scenario.js +322 -50
  121. package/dist/src/functions/commands/serve.js +2 -0
  122. package/dist/src/functions/commands/skills.js +27 -61
  123. package/dist/src/functions/db/db-codegen.d.ts +6 -0
  124. package/dist/src/functions/db/db-codegen.js +9 -0
  125. package/dist/src/functions/db/db-migrator.js +14 -0
  126. package/dist/src/functions/db/local-db.d.ts +8 -0
  127. package/dist/src/functions/db/local-db.js +24 -2
  128. package/dist/src/functions/db/migration-identifiers.d.ts +58 -0
  129. package/dist/src/functions/db/migration-identifiers.js +262 -0
  130. package/dist/src/functions/db/migration-provenance.d.ts +35 -0
  131. package/dist/src/functions/db/migration-provenance.js +80 -0
  132. package/dist/src/functions/db/schema-sql.d.ts +43 -0
  133. package/dist/src/functions/db/schema-sql.js +135 -0
  134. package/dist/src/functions/db/sqlite/sqlite-kysely.js +22 -2
  135. package/dist/src/functions/wirings/functions/pikku-command-functions.js +26 -8
  136. package/dist/src/functions/wirings/functions/schemas.js +13 -1
  137. package/dist/src/functions/wirings/functions/serialize-function-types.js +3 -3
  138. package/dist/src/functions/wirings/rpc/pikku-command-rpc.js +6 -1
  139. package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.d.ts +10 -0
  140. package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.js +102 -0
  141. package/dist/src/functions/wirings/scenarios/scenario-partition.d.ts +42 -0
  142. package/dist/src/functions/wirings/scenarios/scenario-partition.js +71 -0
  143. package/dist/src/functions/wirings/scenarios/scenario-schema-partition.d.ts +33 -0
  144. package/dist/src/functions/wirings/scenarios/scenario-schema-partition.js +49 -0
  145. package/dist/src/functions/wirings/scenarios/serialize-feature-meta.d.ts +14 -0
  146. package/dist/src/functions/wirings/scenarios/serialize-feature-meta.js +29 -0
  147. package/dist/src/functions/wirings/scenarios/serialize-scenario-meta.d.ts +12 -0
  148. package/dist/src/functions/wirings/scenarios/serialize-scenario-meta.js +56 -0
  149. package/dist/src/functions/wirings/scenarios/serialize-scenario-registration.d.ts +7 -0
  150. package/dist/src/functions/wirings/scenarios/serialize-scenario-registration.js +40 -0
  151. package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +43 -8
  152. package/dist/src/functions/wirings/workflow/serialize-scenario-actors.d.ts +1 -1
  153. package/dist/src/functions/wirings/workflow/serialize-scenario-actors.js +13 -2
  154. package/dist/src/functions/wirings/workflow/serialize-scenario-step-map.d.ts +4 -0
  155. package/dist/src/functions/wirings/workflow/serialize-scenario-step-map.js +62 -0
  156. package/dist/src/functions/wirings/workflow/serialize-workflow-bootstrap-map.js +10 -3
  157. package/dist/src/functions/wirings/workflow/serialize-workflow-map.js +10 -3
  158. package/dist/src/functions/wirings/workflow/serialize-workflow-meta.js +4 -4
  159. package/dist/src/functions/wirings/workflow/serialize-workflow-types.d.ts +1 -1
  160. package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +232 -3
  161. package/dist/src/functions/workflows/all.workflow.js +2 -7
  162. package/dist/src/server/server-ready.d.ts +12 -0
  163. package/dist/src/server/server-ready.js +12 -0
  164. package/dist/src/server/spawn-dev-server.d.ts +50 -0
  165. package/dist/src/server/spawn-dev-server.js +112 -0
  166. package/dist/src/services.js +12 -1
  167. package/dist/src/utils/file-writer.js +14 -2
  168. package/dist/src/utils/meta-diff.js +5 -2
  169. package/dist/src/utils/pikku-cli-config.d.ts +14 -0
  170. package/dist/src/utils/pikku-cli-config.js +60 -0
  171. package/dist/src/utils/remove-legacy-scaffold-file.d.ts +14 -0
  172. package/dist/src/utils/remove-legacy-scaffold-file.js +23 -0
  173. package/dist/src/utils/resolve-scenario-actors.d.ts +23 -0
  174. package/dist/src/utils/resolve-scenario-actors.js +74 -0
  175. package/dist/src/utils/serialize-schemas.d.ts +3 -1
  176. package/dist/src/utils/serialize-schemas.js +39 -4
  177. package/dist/tsconfig.tsbuildinfo +1 -1
  178. package/package.json +18 -8
  179. package/dist/.pikku/schemas/schemas/PikkuScenarioFunctionsOutput.schema.json +0 -1
  180. package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.d.ts +0 -1
  181. package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.js +0 -31
  182. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.d.ts +0 -10
  183. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.js +0 -104
  184. package/skills/pikku-addon/SKILL.md +0 -243
  185. package/skills/pikku-addon/references/addon-package-manifest.md +0 -63
  186. package/skills/pikku-ai-agent/SKILL.md +0 -231
  187. package/skills/pikku-ai-vercel/SKILL.md +0 -82
  188. package/skills/pikku-ai-voice/SKILL.md +0 -88
  189. package/skills/pikku-audit/SKILL.md +0 -175
  190. package/skills/pikku-aws/SKILL.md +0 -111
  191. package/skills/pikku-backblaze/SKILL.md +0 -71
  192. package/skills/pikku-better-auth/SKILL.md +0 -298
  193. package/skills/pikku-cli/SKILL.md +0 -198
  194. package/skills/pikku-cli/references/complete-example.md +0 -82
  195. package/skills/pikku-concepts/SKILL.md +0 -250
  196. package/skills/pikku-concepts/references/concept-mapping.md +0 -556
  197. package/skills/pikku-concepts/references/packages.md +0 -29
  198. package/skills/pikku-config/SKILL.md +0 -212
  199. package/skills/pikku-cron/SKILL.md +0 -214
  200. package/skills/pikku-deploy-azure/SKILL.md +0 -71
  201. package/skills/pikku-deploy-cloudflare/SKILL.md +0 -82
  202. package/skills/pikku-deploy-express/SKILL.md +0 -86
  203. package/skills/pikku-deploy-fastify/SKILL.md +0 -73
  204. package/skills/pikku-deploy-lambda/SKILL.md +0 -110
  205. package/skills/pikku-deploy-nextjs/SKILL.md +0 -78
  206. package/skills/pikku-deploy-uws/SKILL.md +0 -88
  207. package/skills/pikku-deps/SKILL.md +0 -98
  208. package/skills/pikku-emails/SKILL.md +0 -163
  209. package/skills/pikku-fabric/SKILL.md +0 -318
  210. package/skills/pikku-fabric-debug/SKILL.md +0 -112
  211. package/skills/pikku-feature/SKILL.md +0 -258
  212. package/skills/pikku-gateway-slack/SKILL.md +0 -115
  213. package/skills/pikku-http/SKILL.md +0 -220
  214. package/skills/pikku-http/references/http-options.md +0 -55
  215. package/skills/pikku-i18n/SKILL.md +0 -137
  216. package/skills/pikku-info/SKILL.md +0 -100
  217. package/skills/pikku-jose/SKILL.md +0 -105
  218. package/skills/pikku-kysely/SKILL.md +0 -219
  219. package/skills/pikku-machine-auth/SKILL.md +0 -183
  220. package/skills/pikku-mcp/SKILL.md +0 -241
  221. package/skills/pikku-middleware/SKILL.md +0 -231
  222. package/skills/pikku-middleware/references/middleware-patterns.md +0 -61
  223. package/skills/pikku-mongodb/SKILL.md +0 -105
  224. package/skills/pikku-n8n-import/SKILL.md +0 -109
  225. package/skills/pikku-n8n-import/SPEC.md +0 -84
  226. package/skills/pikku-n8n-import/references/addon-mapping.md +0 -121
  227. package/skills/pikku-n8n-import/references/code-translation.md +0 -121
  228. package/skills/pikku-n8n-import/references/loops-and-control.md +0 -87
  229. package/skills/pikku-paraglide/SKILL.md +0 -117
  230. package/skills/pikku-permissions/SKILL.md +0 -192
  231. package/skills/pikku-pino/SKILL.md +0 -79
  232. package/skills/pikku-product-second-opinion/README.md +0 -43
  233. package/skills/pikku-product-second-opinion/SKILL.md +0 -158
  234. package/skills/pikku-product-second-opinion/example/sample-report.md +0 -90
  235. package/skills/pikku-product-second-opinion/references/report-template.md +0 -73
  236. package/skills/pikku-queue/SKILL.md +0 -240
  237. package/skills/pikku-react/SKILL.md +0 -212
  238. package/skills/pikku-react-query/SKILL.md +0 -242
  239. package/skills/pikku-realtime/SKILL.md +0 -236
  240. package/skills/pikku-realtime/references/other-routes.md +0 -23
  241. package/skills/pikku-redis/SKILL.md +0 -90
  242. package/skills/pikku-rpc/SKILL.md +0 -171
  243. package/skills/pikku-rtl/SKILL.md +0 -219
  244. package/skills/pikku-scenario/SKILL.md +0 -215
  245. package/skills/pikku-schedule/SKILL.md +0 -57
  246. package/skills/pikku-schema-ajv/SKILL.md +0 -62
  247. package/skills/pikku-schema-cfworker/SKILL.md +0 -63
  248. package/skills/pikku-security/SKILL.md +0 -108
  249. package/skills/pikku-services/SKILL.md +0 -248
  250. package/skills/pikku-services/references/audit-wire-service.md +0 -34
  251. package/skills/pikku-software-archaeology/README.md +0 -70
  252. package/skills/pikku-software-archaeology/SKILL.md +0 -186
  253. package/skills/pikku-software-archaeology/references/blueprint.schema.json +0 -625
  254. package/skills/pikku-software-archaeology/references/pikku-mapping.md +0 -49
  255. package/skills/pikku-software-archaeology/scripts/validate.mjs +0 -173
  256. package/skills/pikku-tag-middleware/SKILL.md +0 -13
  257. package/skills/pikku-template-clone/SKILL.md +0 -40
  258. package/skills/pikku-trigger/SKILL.md +0 -181
  259. package/skills/pikku-versioning/SKILL.md +0 -173
  260. package/skills/pikku-websocket/SKILL.md +0 -243
  261. package/skills/pikku-workflow/SKILL.md +0 -172
  262. package/skills/pikku-workflow/references/workflow-reference.md +0 -63
  263. package/skills/pikku-workflows-client/SKILL.md +0 -150
  264. package/skills/pikku-ws/SKILL.md +0 -47
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Reading a schema source's own SQL back out, one table at a time.
3
+ *
4
+ * A source hands `db generate` both what must exist (`tables`) and what creates
5
+ * it (`sql`). The column map is a comparison surface — it answers "is this
6
+ * table there, and does it have these columns" — and it is deliberately lossy:
7
+ * primary keys, foreign keys, uniqueness, check constraints and indexes are all
8
+ * absent from it. So the moment the generator has to *create* something, the
9
+ * only honest source is the SQL, and this is what pulls the relevant part of it
10
+ * out.
11
+ */
12
+ /**
13
+ * Split a SQL script into its top-level statements, semicolons included.
14
+ *
15
+ * A naive `split(';')` is wrong in a way that only shows up later: a semicolon
16
+ * inside a string literal, a quoted identifier or a comment is not a statement
17
+ * boundary, and cutting there yields two fragments that are each valid-looking
18
+ * and neither of which does what the original did.
19
+ */
20
+ export function splitStatements(sql) {
21
+ const statements = [];
22
+ let start = 0;
23
+ let i = 0;
24
+ const closeQuote = (quote) => {
25
+ i++;
26
+ while (i < sql.length) {
27
+ if (sql[i] === '\\' && quote === "'") {
28
+ i += 2;
29
+ continue;
30
+ }
31
+ if (sql[i] === quote) {
32
+ // A doubled quote is an escaped one, not the end of the literal.
33
+ if (sql[i + 1] === quote) {
34
+ i += 2;
35
+ continue;
36
+ }
37
+ i++;
38
+ return;
39
+ }
40
+ i++;
41
+ }
42
+ };
43
+ while (i < sql.length) {
44
+ const ch = sql[i];
45
+ if (ch === "'" || ch === '"' || ch === '`') {
46
+ closeQuote(ch);
47
+ continue;
48
+ }
49
+ if (ch === '-' && sql[i + 1] === '-') {
50
+ const end = sql.indexOf('\n', i);
51
+ i = end === -1 ? sql.length : end + 1;
52
+ continue;
53
+ }
54
+ if (ch === '/' && sql[i + 1] === '*') {
55
+ const end = sql.indexOf('*/', i + 2);
56
+ i = end === -1 ? sql.length : end + 2;
57
+ continue;
58
+ }
59
+ // Postgres dollar quoting: everything between `$tag$` and its twin is a
60
+ // literal, and a function body written that way is full of semicolons.
61
+ if (ch === '$') {
62
+ const tag = /^\$[A-Za-z_0-9]*\$/.exec(sql.slice(i));
63
+ if (tag) {
64
+ const end = sql.indexOf(tag[0], i + tag[0].length);
65
+ i = end === -1 ? sql.length : end + tag[0].length;
66
+ continue;
67
+ }
68
+ }
69
+ if (ch === ';') {
70
+ const statement = sql.slice(start, i + 1).trim();
71
+ if (statement.length > 1)
72
+ statements.push(statement);
73
+ start = i + 1;
74
+ }
75
+ i++;
76
+ }
77
+ // A script whose last statement has no trailing semicolon still ran it.
78
+ const tail = sql.slice(start).trim();
79
+ if (tail.length > 0)
80
+ statements.push(tail);
81
+ return statements;
82
+ }
83
+ /**
84
+ * Reduce a written table name to the form two sources can be compared on.
85
+ *
86
+ * The same table is `two_factor` to one writer, `"two_factor"` to Kysely and
87
+ * `public.two_factor` to Postgres introspection. Dropping the schema qualifier,
88
+ * the quoting and the case is the only shape all three agree on.
89
+ */
90
+ export function bareTableName(name) {
91
+ const last = name.split('.').pop() ?? name;
92
+ return last.replace(/^["'`[]|["'`\]]$/g, '').toLowerCase();
93
+ }
94
+ const CREATE_TABLE = /^CREATE\s+(?:TEMP(?:ORARY)?\s+|UNLOGGED\s+)*TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?([^\s(]+)/i;
95
+ const CREATE_INDEX = /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:CONCURRENTLY\s+)?(?:IF\s+NOT\s+EXISTS\s+)?\S+\s+ON\s+(?:ONLY\s+)?([^\s(]+)/i;
96
+ const ALTER_TABLE = /^ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:ONLY\s+)?([^\s(]+)/i;
97
+ /**
98
+ * Every statement in `sql` that builds `table`, in the order the source wrote
99
+ * them.
100
+ *
101
+ * The `CREATE TABLE` and its indexes, plus any `ALTER TABLE` the source uses to
102
+ * hang a constraint on it afterwards — which together are what the column list
103
+ * cannot express. Source order is preserved because it is load-bearing: an
104
+ * index cannot precede its table, and a table cannot precede one it references.
105
+ *
106
+ * An empty result means the source's SQL does not visibly create the table.
107
+ * That is a real answer rather than a failure — a source may create a table
108
+ * from something other than a literal `CREATE TABLE` — and the caller is
109
+ * expected to fall back rather than emit nothing.
110
+ */
111
+ export function tableCreationSql(sql, table) {
112
+ const wanted = bareTableName(table);
113
+ const statements = [];
114
+ let creates = false;
115
+ for (const statement of splitStatements(sql)) {
116
+ const create = CREATE_TABLE.exec(statement);
117
+ if (create && bareTableName(create[1]) === wanted) {
118
+ creates = true;
119
+ statements.push(statement);
120
+ continue;
121
+ }
122
+ const index = CREATE_INDEX.exec(statement);
123
+ if (index && bareTableName(index[1]) === wanted) {
124
+ statements.push(statement);
125
+ continue;
126
+ }
127
+ const alter = ALTER_TABLE.exec(statement);
128
+ if (alter && bareTableName(alter[1]) === wanted) {
129
+ statements.push(statement);
130
+ }
131
+ }
132
+ // Indexes and alters without the table they belong to would fail on their own,
133
+ // and their presence says the table came from somewhere this cannot read.
134
+ return creates ? statements : [];
135
+ }
@@ -1,4 +1,21 @@
1
1
  import { Kysely, SqliteDialect, CamelCasePlugin, } from 'kysely';
2
+ /**
3
+ * Whether a value is a JSON column's worth of data rather than some other
4
+ * object that merely happens to be one. A `Map`, a `RegExp` or a class
5
+ * instance stringifies to `"{}"` or to a partial view of itself, which would
6
+ * silently persist an empty JSON blob where the caller meant something.
7
+ *
8
+ * Kept in lockstep with `isJsonEncodable` in `@pikku/kysely-sqlite`'s
9
+ * LibsqlWebDialect: whatever binds here under `pikku dev` has to bind the same
10
+ * way once the app is deployed, or the dev and deployed runtimes disagree
11
+ * about what a query may do.
12
+ */
13
+ function isJsonEncodable(v) {
14
+ if (Array.isArray(v))
15
+ return true;
16
+ const proto = Object.getPrototypeOf(v);
17
+ return proto === Object.prototype || proto === null;
18
+ }
2
19
  function coerce(v) {
3
20
  if (v === null || v === undefined)
4
21
  return null;
@@ -8,8 +25,11 @@ function coerce(v) {
8
25
  return v.toISOString();
9
26
  if (v instanceof Uint8Array)
10
27
  return v;
11
- if (typeof v === 'object')
12
- return JSON.stringify(v);
28
+ if (typeof v === 'object') {
29
+ if (isJsonEncodable(v))
30
+ return JSON.stringify(v);
31
+ throw new Error(`sqlite: unsupported argument type ${v.constructor?.name ?? 'object'}`);
32
+ }
13
33
  return v;
14
34
  }
15
35
  // A statement returns rows when it is a SELECT or carries a RETURNING clause.
@@ -4,19 +4,22 @@ import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-
4
4
  import { serializeFunctionImports } from './serialize-function-imports.js';
5
5
  import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
6
6
  import { stripVerboseFields, hasVerboseFields, reattachFunctionServices, } from '../../../utils/strip-verbose-meta.js';
7
+ import { partitionScenarioFunctions, partitionScenarioFunctionsMeta, } from '../scenarios/scenario-partition.js';
8
+ import { serializeScenarioFunctionMeta } from '../scenarios/serialize-scenario-meta.js';
7
9
  export const pikkuFunctions = pikkuSessionlessFunc({
8
10
  func: async ({ logger, config, getInspectorState }) => {
9
11
  const { functions, rpc } = await getInspectorState();
10
- const { functionsMetaFile, functionsMetaJsonFile, functionsFile, packageMappings, schema, } = config;
11
- let minimalMeta = stripVerboseFields(functions.meta);
12
+ const { functionsMetaFile, functionsMetaJsonFile, functionsFile, scenarioStepsFile, scenarioStepsMetaFile, scenarioStepsMetaJsonFile, packageMappings, schema, } = config;
13
+ const { app: appFunctionsMeta, scenario: scenarioFunctionsMeta } = partitionScenarioFunctionsMeta(functions.meta);
14
+ let minimalMeta = stripVerboseFields(appFunctionsMeta);
12
15
  if (config.addonName) {
13
- minimalMeta = reattachFunctionServices(minimalMeta, functions.meta);
16
+ minimalMeta = reattachFunctionServices(minimalMeta, appFunctionsMeta);
14
17
  }
15
18
  await writeFileInDir(logger, functionsMetaJsonFile, JSON.stringify(minimalMeta, null, 2));
16
19
  // Write verbose JSON only if it has additional fields
17
- if (hasVerboseFields(functions.meta)) {
20
+ if (hasVerboseFields(appFunctionsMeta)) {
18
21
  const verbosePath = functionsMetaJsonFile.replace(/\.gen\.json$/, '-verbose.gen.json');
19
- await writeFileInDir(logger, verbosePath, JSON.stringify(functions.meta, null, 2));
22
+ await writeFileInDir(logger, verbosePath, JSON.stringify(appFunctionsMeta, null, 2));
20
23
  }
21
24
  const jsonImportPath = getFileImportRelativePath(functionsMetaFile, functionsMetaJsonFile, packageMappings);
22
25
  const supportsImportAttributes = schema?.supportsImportAttributes ?? false;
@@ -31,11 +34,26 @@ export const pikkuFunctions = pikkuSessionlessFunc({
31
34
  const hasRPCs = rpc.exposedFiles.size > 0 || rpc.internalFiles.size > 0;
32
35
  const hasFunctions = functions.files.size > 0;
33
36
  const shouldGenerateFunctionsFile = isAddon ? hasFunctions : hasRPCs;
37
+ // For addon packages, use all function files; for main packages, use internal RPC files
38
+ const filesToRegister = isAddon ? functions.files : rpc.internalFiles;
39
+ const { app: appFiles, scenario: scenarioFiles } = partitionScenarioFunctions(filesToRegister, functions.meta);
34
40
  if (shouldGenerateFunctionsFile) {
35
- // For addon packages, use all function files; for main packages, use internal RPC files
36
- const filesToRegister = isAddon ? functions.files : rpc.internalFiles;
37
- await writeFileInDir(logger, functionsFile, serializeFunctionImports(functionsFile, filesToRegister, functions.meta, packageMappings, config.addonName));
41
+ await writeFileInDir(logger, functionsFile, serializeFunctionImports(functionsFile, appFiles, appFunctionsMeta, packageMappings, config.addonName));
38
42
  }
43
+ // Always written, even when empty: a project that deletes its last scenario
44
+ // must stop registering the one it had, and the scenario bootstrap imports
45
+ // these unconditionally.
46
+ await writeFileInDir(logger, scenarioStepsMetaJsonFile, JSON.stringify(stripVerboseFields(scenarioFunctionsMeta), null, 2));
47
+ // The bootstrap imports the stripped copy; the verbose one is read off disk
48
+ // by whatever documents a scenario — the console needs `sourceFile` and
49
+ // `exportedName` to show the code a step runs.
50
+ if (hasVerboseFields(scenarioFunctionsMeta)) {
51
+ await writeFileInDir(logger, scenarioStepsMetaJsonFile.replace(/\.gen\.json$/, '-verbose.gen.json'), JSON.stringify(scenarioFunctionsMeta, null, 2));
52
+ }
53
+ await writeFileInDir(logger, scenarioStepsMetaFile, serializeScenarioFunctionMeta(getFileImportRelativePath(scenarioStepsMetaFile, scenarioStepsMetaJsonFile, packageMappings), getFileImportRelativePath(scenarioStepsMetaFile, functionsMetaFile, packageMappings), supportsImportAttributes, config.addonName));
54
+ await writeFileInDir(logger, scenarioStepsFile, scenarioFiles.size === 0
55
+ ? 'export {}'
56
+ : serializeFunctionImports(scenarioStepsFile, scenarioFiles, scenarioFunctionsMeta, packageMappings, config.addonName));
39
57
  return shouldGenerateFunctionsFile;
40
58
  },
41
59
  middleware: [
@@ -1,6 +1,7 @@
1
1
  import { pikkuSessionlessFunc } from '#pikku';
2
2
  import { saveSchemas } from '../../../utils/serialize-schemas.js';
3
3
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
4
+ import { partitionRequiredSchemas } from '../scenarios/scenario-schema-partition.js';
4
5
  export const pikkuSchemas = pikkuSessionlessFunc({
5
6
  func: async ({ logger, config, getInspectorState }) => {
6
7
  const visitState = await getInspectorState();
@@ -16,7 +17,18 @@ export const pikkuSchemas = pikkuSessionlessFunc({
16
17
  Object.keys(visitState.schemas).length === 0) {
17
18
  return undefined;
18
19
  }
19
- await saveSchemas(logger, config.schemaDirectory, visitState.schemas, visitState.requiredSchemas, config.schema?.supportsImportAttributes ?? true, config.addonName || null);
20
+ const supportsImportAttributes = config.schema?.supportsImportAttributes ?? true;
21
+ const { appRequired, scenarioOnly } = partitionRequiredSchemas({
22
+ functionsMeta: visitState.functions.meta,
23
+ requiredSchemas: visitState.requiredSchemas,
24
+ getUniqueName: (name) => visitState.functions.typesMap.getUniqueName(name),
25
+ schemasFromTypes: config.schemasFromTypes,
26
+ });
27
+ await saveSchemas(logger, config.schemaDirectory, visitState.schemas, appRequired, supportsImportAttributes, config.addonName || null);
28
+ // Always written, even when empty — the scenario bootstrap imports it
29
+ // unconditionally, and a project that deleted its last scenario must stop
30
+ // registering the schemas it had.
31
+ await saveSchemas(logger, config.scenarioSchemaDirectory, visitState.schemas, scenarioOnly, supportsImportAttributes, config.addonName || null, 'scenario schemas');
20
32
  return true;
21
33
  },
22
34
  middleware: [
@@ -7,7 +7,7 @@ export const serializeFunctionTypes = (userSessionTypeImport, userSessionTypeNam
7
7
  ? nodeCategories.map((c) => `'${c}'`).join(' | ')
8
8
  : 'string';
9
9
  const workflowImport = workflowTypesImport ||
10
- `import type { TypedWorkflow, TypedScenario } from '../workflow/pikku-workflow-types.gen.js'`;
10
+ `import type { TypedWorkflow, TypedScenario, TypedScenarioActors } from '../workflow/pikku-workflow-types.gen.js'`;
11
11
  // Falls back to `string` when a project has no scopes codegen, so that
12
12
  // `scopes` stays usable rather than resolving to an unbound type name.
13
13
  const scopesImport = scopesTypeImport || `type ScopeId = string`;
@@ -299,7 +299,7 @@ export type PikkuFunctionSessionless<
299
299
  Out,
300
300
  RequiredServices,
301
301
  Session,
302
- PickRequired<PikkuWire<In, Out, false, Session, TypedPikkuRPC, null, any, TypedWorkflow, unknown, TypedScenario>, RequiredWires>
302
+ PickRequired<PikkuWire<In, Out, false, Session, TypedPikkuRPC, null, any, TypedWorkflow, unknown, TypedScenario, TypedScenarioActors>, RequiredWires>
303
303
  >
304
304
 
305
305
  /**
@@ -320,7 +320,7 @@ export type PikkuFunction<
320
320
  Out,
321
321
  RequiredServices,
322
322
  Session,
323
- PickRequired<PikkuWire<In, Out, true, Session, TypedPikkuRPC, null, any, TypedWorkflow, unknown, TypedScenario>, RequiredWires>
323
+ PickRequired<PikkuWire<In, Out, true, Session, TypedPikkuRPC, null, any, TypedWorkflow, unknown, TypedScenario, TypedScenarioActors>, RequiredWires>
324
324
  >
325
325
 
326
326
  /**
@@ -2,6 +2,7 @@ import { pikkuSessionlessFunc } from '#pikku';
2
2
  import { writeFileInDir } from '../../../utils/file-writer.js';
3
3
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
4
4
  import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
5
+ import { withoutScenarios } from '../scenarios/scenario-partition.js';
5
6
  export function filterInternalRPCMeta(internalMeta, functionsMeta) {
6
7
  const filteredInternalMeta = {};
7
8
  for (const [key, value] of Object.entries(internalMeta)) {
@@ -18,7 +19,11 @@ export const pikkuRPC = pikkuSessionlessFunc({
18
19
  // Keep direct function ids and unversioned aliases whose target function
19
20
  // survived filtering. Without the second check, aliases like `listCards`
20
21
  // get dropped even though `listCards@v2` still exists.
21
- const filteredInternalMeta = filterInternalRPCMeta(rpc.internalMeta, functions.meta);
22
+ // Scenarios are excluded along with the steps they call: a scenario name in
23
+ // the app's RPC meta is a name a deployed bundle carries and, in principle,
24
+ // dispatches. Scenario runs don't need the entry — the mapping is the
25
+ // identity, which is also the fallback when a name is absent.
26
+ const filteredInternalMeta = filterInternalRPCMeta(rpc.internalMeta, withoutScenarios(functions.meta));
22
27
  if (Object.keys(filteredInternalMeta).length > 0) {
23
28
  await writeFileInDir(logger, rpcInternalWiringMetaJsonFile, JSON.stringify(filteredInternalMeta, null, 2));
24
29
  const jsonImportPath = getFileImportRelativePath(rpcInternalWiringMetaFile, rpcInternalWiringMetaJsonFile, packageMappings);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Register the instrumentation into the running dev server's state, so the
3
+ * scenario runner can reach it over `/rpc/<name>` exactly as before.
4
+ *
5
+ * They are registered sessionless: `auth` decides whether a session is
6
+ * *required*, and a sessioned function would demand one regardless of the flag —
7
+ * which is what made `scaffold.scenarios: true` (as opposed to `'auth'`) refuse
8
+ * every call and log a warning about it.
9
+ */
10
+ export declare const registerScenarioInstrumentation: (requireAuth: boolean) => void;
@@ -0,0 +1,102 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { addFunction } from '@pikku/core/function';
4
+ import { pikkuState } from '@pikku/core/internal';
5
+ import { getStubTracker, isTestRun, } from '@pikku/core/services';
6
+ /**
7
+ * The instrumentation `pikku scenario run` calls on the server under test:
8
+ * snapshot/reset live coverage, and reset/read recorded stub calls.
9
+ *
10
+ * These are pikku's, not the application's. They were previously scaffolded into
11
+ * the project as ordinary `pikkuFunc`s, which is why they ended up registered in
12
+ * the app bootstrap and shipped — `expose: true` — inside every deployed bundle.
13
+ * Registering them here instead means the only process that has them is the one
14
+ * that should: a development server. Nothing is generated, nothing is written to
15
+ * the user's source tree, and a bundle cannot carry what was never in its
16
+ * bootstrap.
17
+ *
18
+ * The bodies are unchanged from the generated ones, minus their schemas: an
19
+ * instrumentation endpoint with a single optional string argument has nothing to
20
+ * validate, and dropping the schemas drops the `zod` dependency the scaffold
21
+ * silently required of every project.
22
+ */
23
+ const takeLiveCoverage = {
24
+ func: async ({ coverageService, metaService }) => {
25
+ if (!coverageService?.takeReport || !metaService?.basePath)
26
+ return null;
27
+ let functionsMeta;
28
+ try {
29
+ const content = await readFile(join(metaService.basePath, 'function', 'pikku-functions-meta-verbose.gen.json'), 'utf-8');
30
+ functionsMeta = JSON.parse(content);
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ return await coverageService.takeReport(functionsMeta);
36
+ },
37
+ };
38
+ const resetLiveCoverage = {
39
+ func: async ({ coverageService }) => {
40
+ if (!coverageService)
41
+ return { enabled: false };
42
+ await coverageService.reset();
43
+ return { enabled: true };
44
+ },
45
+ };
46
+ const resetStubs = {
47
+ func: async () => {
48
+ getStubTracker().reset();
49
+ return { enabled: isTestRun() };
50
+ },
51
+ };
52
+ const getStubCalls = {
53
+ func: async (_services, data) => getStubTracker().getCalls(data?.service ?? undefined),
54
+ };
55
+ const instrumentation = {
56
+ pikkuScenarioTakeLiveCoverage: {
57
+ ...takeLiveCoverage,
58
+ title: 'Take Live Coverage',
59
+ description: 'Snapshots the live coverage collected since the server started (or since the last reset) — V8 precise coverage on Node, istanbul instrumentation on Bun — and maps it onto function body spans. Returns null unless the server was started with coverage enabled (pikku dev --coverage).',
60
+ },
61
+ pikkuScenarioResetLiveCoverage: {
62
+ ...resetLiveCoverage,
63
+ title: 'Reset Live Coverage',
64
+ description: 'Clears coverage call counts so the next takeLiveCoverage snapshot is attributable to a single scenario run. Reports enabled: false when the server was not started with coverage enabled.',
65
+ },
66
+ pikkuScenarioResetStubs: {
67
+ ...resetStubs,
68
+ title: 'Reset Stubs',
69
+ description: 'Clears recorded stub calls so the next getStubCalls result is attributable to a single scenario run. Reports enabled: false when the server was not started in test mode.',
70
+ },
71
+ pikkuScenarioGetStubCalls: {
72
+ ...getStubCalls,
73
+ title: 'Get Stub Calls',
74
+ description: 'Returns calls recorded against stubbed/spied services (via the stub()/spy() core utils). Empty unless the server records service calls (pikku dev --test).',
75
+ },
76
+ };
77
+ /**
78
+ * Register the instrumentation into the running dev server's state, so the
79
+ * scenario runner can reach it over `/rpc/<name>` exactly as before.
80
+ *
81
+ * They are registered sessionless: `auth` decides whether a session is
82
+ * *required*, and a sessioned function would demand one regardless of the flag —
83
+ * which is what made `scaffold.scenarios: true` (as opposed to `'auth'`) refuse
84
+ * every call and log a warning about it.
85
+ */
86
+ export const registerScenarioInstrumentation = (requireAuth) => {
87
+ const meta = pikkuState(null, 'function', 'meta');
88
+ for (const [name, { func, title, description }] of Object.entries(instrumentation)) {
89
+ addFunction(name, { func, auth: requireAuth });
90
+ meta[name] = {
91
+ pikkuFuncId: name,
92
+ inputSchemaName: null,
93
+ outputSchemaName: null,
94
+ expose: true,
95
+ sessionless: true,
96
+ tags: ['pikku'],
97
+ title,
98
+ description,
99
+ };
100
+ }
101
+ pikkuState(null, 'function', 'meta', meta);
102
+ };
@@ -0,0 +1,42 @@
1
+ import type { FunctionsMeta } from '@pikku/core';
2
+ import type { SerializedWorkflowGraphs } from '@pikku/inspector/workflow-graph';
3
+ export type WiringFileMap = Map<string, {
4
+ path: string;
5
+ exportedName: string;
6
+ }>;
7
+ /**
8
+ * Scenario steps and scenarios are ordinary pikku functions and workflows, so
9
+ * without a split they register into the same bootstrap a deployed server
10
+ * imports — dragging every step body (and whatever it imports: Playwright, test
11
+ * fixtures, assertion helpers) into production. These partitions are what keep
12
+ * them in `.pikku/scenarios/`, reachable only from `pikku scenario run`.
13
+ */
14
+ export declare const isScenarioStep: (meta: FunctionsMeta[string] | undefined) => boolean;
15
+ /**
16
+ * A scenario's own body is a function too, so the app meta used to carry every
17
+ * scenario's name, schemas and hashes — and a deployed bundle imports that meta
18
+ * wholesale. Both kinds belong on the scenario side of the split.
19
+ */
20
+ export declare const isScenarioFunction: (meta: FunctionsMeta[string] | undefined) => boolean;
21
+ export declare const partitionScenarioFunctions: (files: WiringFileMap, functionsMeta: FunctionsMeta) => {
22
+ app: WiringFileMap;
23
+ scenario: WiringFileMap;
24
+ };
25
+ export declare const partitionScenarioFunctionsMeta: (functionsMeta: FunctionsMeta) => {
26
+ app: FunctionsMeta;
27
+ scenario: FunctionsMeta;
28
+ };
29
+ export declare const isScenarioWorkflow: (graphMeta: SerializedWorkflowGraphs, name: string) => boolean;
30
+ export declare const partitionScenarioWorkflows: (workflowNames: string[], workflowFiles: WiringFileMap, graphMeta: SerializedWorkflowGraphs) => {
31
+ appNames: string[];
32
+ scenarioNames: string[];
33
+ appFiles: WiringFileMap;
34
+ scenarioFiles: WiringFileMap;
35
+ };
36
+ /**
37
+ * Drop every scenario and step from a function meta map. The deploy analyzer
38
+ * reads inspector state rather than the partitioned codegen output, so it needs
39
+ * the same split applied before it decides what a deployment contains.
40
+ */
41
+ export declare const withoutScenarios: (functionsMeta: FunctionsMeta) => FunctionsMeta;
42
+ export declare const withoutScenarioWorkflows: (graphMeta: SerializedWorkflowGraphs) => SerializedWorkflowGraphs;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Scenario steps and scenarios are ordinary pikku functions and workflows, so
3
+ * without a split they register into the same bootstrap a deployed server
4
+ * imports — dragging every step body (and whatever it imports: Playwright, test
5
+ * fixtures, assertion helpers) into production. These partitions are what keep
6
+ * them in `.pikku/scenarios/`, reachable only from `pikku scenario run`.
7
+ */
8
+ export const isScenarioStep = (meta) => meta?.scenarioStep === true;
9
+ /**
10
+ * A scenario's own body is a function too, so the app meta used to carry every
11
+ * scenario's name, schemas and hashes — and a deployed bundle imports that meta
12
+ * wholesale. Both kinds belong on the scenario side of the split.
13
+ */
14
+ export const isScenarioFunction = (meta) => isScenarioStep(meta) || meta?.scenario === true;
15
+ export const partitionScenarioFunctions = (files, functionsMeta) => {
16
+ const app = new Map();
17
+ const scenario = new Map();
18
+ for (const [name, entry] of files) {
19
+ if (isScenarioFunction(functionsMeta[name])) {
20
+ scenario.set(name, entry);
21
+ }
22
+ else {
23
+ app.set(name, entry);
24
+ }
25
+ }
26
+ return { app, scenario };
27
+ };
28
+ export const partitionScenarioFunctionsMeta = (functionsMeta) => {
29
+ const app = {};
30
+ const scenario = {};
31
+ for (const [name, meta] of Object.entries(functionsMeta)) {
32
+ if (isScenarioFunction(meta)) {
33
+ scenario[name] = meta;
34
+ }
35
+ else {
36
+ app[name] = meta;
37
+ }
38
+ }
39
+ return { app, scenario };
40
+ };
41
+ export const isScenarioWorkflow = (graphMeta, name) => graphMeta[name]?.source === 'scenario';
42
+ export const partitionScenarioWorkflows = (workflowNames, workflowFiles, graphMeta) => {
43
+ const appNames = [];
44
+ const scenarioNames = [];
45
+ for (const name of workflowNames) {
46
+ if (isScenarioWorkflow(graphMeta, name)) {
47
+ scenarioNames.push(name);
48
+ }
49
+ else {
50
+ appNames.push(name);
51
+ }
52
+ }
53
+ const appFiles = new Map();
54
+ const scenarioFiles = new Map();
55
+ for (const [name, entry] of workflowFiles) {
56
+ if (isScenarioWorkflow(graphMeta, name)) {
57
+ scenarioFiles.set(name, entry);
58
+ }
59
+ else {
60
+ appFiles.set(name, entry);
61
+ }
62
+ }
63
+ return { appNames, scenarioNames, appFiles, scenarioFiles };
64
+ };
65
+ /**
66
+ * Drop every scenario and step from a function meta map. The deploy analyzer
67
+ * reads inspector state rather than the partitioned codegen output, so it needs
68
+ * the same split applied before it decides what a deployment contains.
69
+ */
70
+ export const withoutScenarios = (functionsMeta) => Object.fromEntries(Object.entries(functionsMeta).filter(([, meta]) => !isScenarioFunction(meta)));
71
+ export const withoutScenarioWorkflows = (graphMeta) => Object.fromEntries(Object.entries(graphMeta ?? {}).filter(([name]) => !isScenarioWorkflow(graphMeta, name)));
@@ -0,0 +1,33 @@
1
+ import type { FunctionsMeta } from '@pikku/core';
2
+ /**
3
+ * The schema names a set of functions validates against.
4
+ *
5
+ * `getUniqueName` is the inspector's own type→schema naming, so this resolves a
6
+ * name the same way `computeRequiredSchemas` did when it filled `requiredSchemas`.
7
+ * The two must not disagree: a scenario schema this failed to name would stay on
8
+ * the app side, which is the leak the split exists to close.
9
+ */
10
+ export declare const schemaNamesFor: (meta: FunctionsMeta, getUniqueName: (name: string) => string) => Set<string>;
11
+ /**
12
+ * Split the required schemas into the app's register and the scenario register.
13
+ *
14
+ * A scenario's input/output schemas are as test-only as its body, and the app's
15
+ * `register.gen.ts` is imported by every deployed bundle — on one project half the
16
+ * registered schemas belonged to scenarios and steps. Both sets are derived from
17
+ * `requiredSchemas`, so every name lands in exactly one register and a name the
18
+ * scenario side claims is removed from the app side by construction.
19
+ *
20
+ * Two things deliberately stay on the app side: a schema an application function
21
+ * also needs (registering a shared schema twice is worse than registering it in
22
+ * one place), and a type the project asked for by hand via `schemasFromTypes`,
23
+ * whatever happens to reference it.
24
+ */
25
+ export declare const partitionRequiredSchemas: ({ functionsMeta, requiredSchemas, getUniqueName, schemasFromTypes, }: {
26
+ functionsMeta: FunctionsMeta;
27
+ requiredSchemas: Set<string>;
28
+ getUniqueName: (name: string) => string;
29
+ schemasFromTypes?: string[];
30
+ }) => {
31
+ appRequired: Set<string>;
32
+ scenarioOnly: Set<string>;
33
+ };
@@ -0,0 +1,49 @@
1
+ import { partitionScenarioFunctionsMeta } from './scenario-partition.js';
2
+ /**
3
+ * The schema names a set of functions validates against.
4
+ *
5
+ * `getUniqueName` is the inspector's own type→schema naming, so this resolves a
6
+ * name the same way `computeRequiredSchemas` did when it filled `requiredSchemas`.
7
+ * The two must not disagree: a scenario schema this failed to name would stay on
8
+ * the app side, which is the leak the split exists to close.
9
+ */
10
+ export const schemaNamesFor = (meta, getUniqueName) => {
11
+ const names = new Set();
12
+ for (const { inputs, outputs } of Object.values(meta)) {
13
+ for (const type of [inputs?.[0], outputs?.[0]]) {
14
+ if (!type)
15
+ continue;
16
+ try {
17
+ names.add(getUniqueName(type));
18
+ }
19
+ catch {
20
+ names.add(type);
21
+ }
22
+ }
23
+ }
24
+ return names;
25
+ };
26
+ /**
27
+ * Split the required schemas into the app's register and the scenario register.
28
+ *
29
+ * A scenario's input/output schemas are as test-only as its body, and the app's
30
+ * `register.gen.ts` is imported by every deployed bundle — on one project half the
31
+ * registered schemas belonged to scenarios and steps. Both sets are derived from
32
+ * `requiredSchemas`, so every name lands in exactly one register and a name the
33
+ * scenario side claims is removed from the app side by construction.
34
+ *
35
+ * Two things deliberately stay on the app side: a schema an application function
36
+ * also needs (registering a shared schema twice is worse than registering it in
37
+ * one place), and a type the project asked for by hand via `schemasFromTypes`,
38
+ * whatever happens to reference it.
39
+ */
40
+ export const partitionRequiredSchemas = ({ functionsMeta, requiredSchemas, getUniqueName, schemasFromTypes, }) => {
41
+ const { app: appFunctionsMeta, scenario: scenarioFunctionsMeta } = partitionScenarioFunctionsMeta(functionsMeta);
42
+ const appSchemaNames = schemaNamesFor(appFunctionsMeta, getUniqueName);
43
+ const requestedByHand = new Set(schemasFromTypes ?? []);
44
+ const scenarioOnly = new Set([...schemaNamesFor(scenarioFunctionsMeta, getUniqueName)].filter((name) => requiredSchemas.has(name) &&
45
+ !appSchemaNames.has(name) &&
46
+ !requestedByHand.has(name)));
47
+ const appRequired = new Set([...requiredSchemas].filter((name) => !scenarioOnly.has(name)));
48
+ return { appRequired, scenarioOnly };
49
+ };
@@ -0,0 +1,14 @@
1
+ import type { FeaturesMeta } from '@pikku/core/workflow/types';
2
+ import type { InspectorFeature } from '@pikku/inspector';
3
+ /**
4
+ * The feature meta the console reads, built from what the inspector could read
5
+ * off the source.
6
+ *
7
+ * Entry order is the declared order and is deliberately not sorted — a
8
+ * feature's scenarios read top to bottom the way they were written, the same
9
+ * way a gherkin Feature file does.
10
+ *
11
+ * The source path is dropped: it is absolute to the machine that generated the
12
+ * meta, and nothing rendering a feature needs it.
13
+ */
14
+ export declare const buildFeaturesMeta: (featureFiles: Map<string, InspectorFeature>) => FeaturesMeta;