@pikku/cli 0.12.91 → 0.12.93

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 (241) hide show
  1. package/LICENSE +106 -0
  2. package/console-app/assets/{index-D0HG8q0B.css → index-BYj7ZbUU.css} +1 -1
  3. package/console-app/assets/{index-CpkvmAGb.js → index-CmAplHjZ.js} +151 -149
  4. package/console-app/index.html +2 -2
  5. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  6. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  7. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  8. package/dist/.pikku/cli/pikku-cli-channel.js +16 -1
  9. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  10. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  11. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  12. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  13. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +15 -0
  14. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  15. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  16. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  17. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +48 -0
  18. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  19. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  20. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  21. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  22. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  23. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  24. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  25. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  26. package/dist/.pikku/function/pikku-functions-meta.gen.json +73 -40
  27. package/dist/.pikku/function/pikku-functions.gen.js +1 -3
  28. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  29. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  30. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  31. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  32. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +2 -1
  33. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +2 -1
  34. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  35. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  36. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  37. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  38. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  39. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  40. package/dist/.pikku/pikku-types.gen.js +1 -1
  41. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  42. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  43. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  44. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  45. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  46. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  47. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +3 -1
  48. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  49. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  50. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  51. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  52. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +4 -0
  53. package/dist/.pikku/scenarios/schemas/register.gen.js +4 -0
  54. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  55. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  56. package/dist/.pikku/schemas/register.gen.js +13 -3
  57. package/dist/.pikku/schemas/schemas/FabricSecretsListOutput.schema.json +1 -1
  58. package/dist/.pikku/schemas/schemas/FabricSecretsRotateInput.schema.json +1 -0
  59. package/dist/.pikku/schemas/schemas/FabricSecretsRotateOutput.schema.json +1 -0
  60. package/dist/.pikku/schemas/schemas/FabricSecretsSetOutput.schema.json +1 -1
  61. package/dist/.pikku/schemas/schemas/KnowledgeIndexInput.schema.json +1 -0
  62. package/dist/.pikku/schemas/schemas/KnowledgeIndexOutput.schema.json +1 -0
  63. package/dist/.pikku/schemas/schemas/KnowledgeValidateInput.schema.json +1 -0
  64. package/dist/.pikku/schemas/schemas/KnowledgeValidateOutput.schema.json +1 -0
  65. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  66. package/dist/.pikku/schemas/schemas/ScenarioRunInput.schema.json +1 -1
  67. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  68. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  69. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  70. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  71. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  72. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  73. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  74. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  75. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  76. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  77. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  78. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  79. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  80. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +2 -8
  81. package/dist/.pikku/workflow/pikku-scenario-actors.gen.d.ts +1 -1
  82. package/dist/.pikku/workflow/pikku-scenario-actors.gen.js +1 -1
  83. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  84. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  85. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  86. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  87. package/dist/bin/pikku-bin.mjs +2 -2
  88. package/dist/src/cli.wiring.js +29 -0
  89. package/dist/src/deploy/analyzer/analyzer.js +22 -5
  90. package/dist/src/deploy/build-pipeline.js +5 -1
  91. package/dist/src/fabric/fabric-commands.d.ts +37 -9
  92. package/dist/src/fabric/fabric-commands.js +12 -0
  93. package/dist/src/fabric/functions/domains-add.function.d.ts +4 -4
  94. package/dist/src/fabric/functions/secrets-list.function.d.ts +16 -4
  95. package/dist/src/fabric/functions/secrets-list.function.js +12 -10
  96. package/dist/src/fabric/functions/secrets-rotate.function.d.ts +24 -0
  97. package/dist/src/fabric/functions/secrets-rotate.function.js +36 -0
  98. package/dist/src/fabric/functions/secrets-set.function.d.ts +8 -4
  99. package/dist/src/fabric/functions/secrets-set.function.js +18 -7
  100. package/dist/src/fabric/functions/validate.function.js +16 -7
  101. package/dist/src/fabric/lib/http.d.ts +7 -1
  102. package/dist/src/fabric/lib/sealed-box.d.ts +16 -0
  103. package/dist/src/fabric/lib/sealed-box.js +72 -0
  104. package/dist/src/functions/commands/dev.js +9 -0
  105. package/dist/src/functions/commands/knowledge-index.d.ts +30 -0
  106. package/dist/src/functions/commands/knowledge-index.js +11 -0
  107. package/dist/src/functions/commands/knowledge-validate.d.ts +33 -0
  108. package/dist/src/functions/commands/knowledge-validate.js +11 -0
  109. package/dist/src/functions/commands/new-addon.d.ts +18 -0
  110. package/dist/src/functions/commands/new-addon.js +40 -24
  111. package/dist/src/functions/commands/pikku-command-bootstrap.js +1 -0
  112. package/dist/src/functions/commands/scenario-browser.d.ts +17 -2
  113. package/dist/src/functions/commands/scenario-browser.js +16 -8
  114. package/dist/src/functions/commands/scenario-environment.d.ts +32 -0
  115. package/dist/src/functions/commands/scenario-environment.js +61 -0
  116. package/dist/src/functions/commands/scenario.d.ts +6 -0
  117. package/dist/src/functions/commands/scenario.js +14 -9
  118. package/dist/src/functions/commands/skills.js +27 -61
  119. package/dist/src/functions/db/db-codegen.d.ts +6 -0
  120. package/dist/src/functions/db/db-codegen.js +9 -0
  121. package/dist/src/functions/db/db-migrator.js +14 -0
  122. package/dist/src/functions/db/local-db.d.ts +8 -0
  123. package/dist/src/functions/db/local-db.js +24 -2
  124. package/dist/src/functions/db/migration-identifiers.d.ts +58 -0
  125. package/dist/src/functions/db/migration-identifiers.js +262 -0
  126. package/dist/src/functions/db/migration-provenance.d.ts +35 -0
  127. package/dist/src/functions/db/migration-provenance.js +80 -0
  128. package/dist/src/functions/db/schema-sql.d.ts +43 -0
  129. package/dist/src/functions/db/schema-sql.js +135 -0
  130. package/dist/src/functions/db/sqlite/sqlite-kysely.js +22 -2
  131. package/dist/src/functions/knowledge/render.d.ts +3 -0
  132. package/dist/src/functions/knowledge/render.js +66 -0
  133. package/dist/src/functions/knowledge/schemas.d.ts +51 -0
  134. package/dist/src/functions/knowledge/schemas.js +25 -0
  135. package/dist/src/functions/wirings/credentials/serialize-credentials-types.js +8 -1
  136. package/dist/src/functions/wirings/functions/schemas.js +13 -1
  137. package/dist/src/functions/wirings/rpc/pikku-command-rpc.js +6 -1
  138. package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.d.ts +10 -0
  139. package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.js +102 -0
  140. package/dist/src/functions/wirings/scenarios/scenario-partition.d.ts +13 -0
  141. package/dist/src/functions/wirings/scenarios/scenario-partition.js +15 -2
  142. package/dist/src/functions/wirings/scenarios/scenario-schema-partition.d.ts +33 -0
  143. package/dist/src/functions/wirings/scenarios/scenario-schema-partition.js +49 -0
  144. package/dist/src/functions/wirings/secrets/serialize-secrets-types.js +10 -3
  145. package/dist/src/functions/wirings/variables/serialize-variables-types.js +8 -1
  146. package/dist/src/functions/workflows/all.workflow.js +2 -7
  147. package/dist/src/utils/file-writer.js +14 -2
  148. package/dist/src/utils/pikku-cli-config.d.ts +14 -0
  149. package/dist/src/utils/pikku-cli-config.js +34 -0
  150. package/dist/src/utils/remove-legacy-scaffold-file.d.ts +14 -0
  151. package/dist/src/utils/remove-legacy-scaffold-file.js +23 -0
  152. package/dist/src/utils/serialize-schemas.d.ts +3 -1
  153. package/dist/src/utils/serialize-schemas.js +39 -4
  154. package/dist/tsconfig.tsbuildinfo +1 -1
  155. package/package.json +10 -9
  156. package/dist/.pikku/schemas/schemas/PikkuScenarioFunctionsOutput.schema.json +0 -1
  157. package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.d.ts +0 -1
  158. package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.js +0 -31
  159. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.d.ts +0 -10
  160. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.js +0 -104
  161. package/skills/pikku-addon/SKILL.md +0 -243
  162. package/skills/pikku-addon/references/addon-package-manifest.md +0 -63
  163. package/skills/pikku-ai-agent/SKILL.md +0 -231
  164. package/skills/pikku-ai-vercel/SKILL.md +0 -82
  165. package/skills/pikku-ai-voice/SKILL.md +0 -88
  166. package/skills/pikku-audit/SKILL.md +0 -175
  167. package/skills/pikku-aws/SKILL.md +0 -111
  168. package/skills/pikku-backblaze/SKILL.md +0 -71
  169. package/skills/pikku-better-auth/SKILL.md +0 -298
  170. package/skills/pikku-cli/SKILL.md +0 -198
  171. package/skills/pikku-cli/references/complete-example.md +0 -82
  172. package/skills/pikku-concepts/SKILL.md +0 -250
  173. package/skills/pikku-concepts/references/concept-mapping.md +0 -556
  174. package/skills/pikku-concepts/references/packages.md +0 -29
  175. package/skills/pikku-config/SKILL.md +0 -212
  176. package/skills/pikku-cron/SKILL.md +0 -214
  177. package/skills/pikku-deploy-azure/SKILL.md +0 -71
  178. package/skills/pikku-deploy-cloudflare/SKILL.md +0 -82
  179. package/skills/pikku-deploy-express/SKILL.md +0 -86
  180. package/skills/pikku-deploy-fastify/SKILL.md +0 -73
  181. package/skills/pikku-deploy-lambda/SKILL.md +0 -110
  182. package/skills/pikku-deploy-nextjs/SKILL.md +0 -78
  183. package/skills/pikku-deploy-uws/SKILL.md +0 -88
  184. package/skills/pikku-deps/SKILL.md +0 -98
  185. package/skills/pikku-emails/SKILL.md +0 -163
  186. package/skills/pikku-fabric/SKILL.md +0 -318
  187. package/skills/pikku-fabric-debug/SKILL.md +0 -112
  188. package/skills/pikku-feature/SKILL.md +0 -258
  189. package/skills/pikku-gateway-slack/SKILL.md +0 -115
  190. package/skills/pikku-http/SKILL.md +0 -220
  191. package/skills/pikku-http/references/http-options.md +0 -55
  192. package/skills/pikku-i18n/SKILL.md +0 -137
  193. package/skills/pikku-info/SKILL.md +0 -100
  194. package/skills/pikku-jose/SKILL.md +0 -105
  195. package/skills/pikku-kysely/SKILL.md +0 -219
  196. package/skills/pikku-machine-auth/SKILL.md +0 -183
  197. package/skills/pikku-mcp/SKILL.md +0 -241
  198. package/skills/pikku-middleware/SKILL.md +0 -231
  199. package/skills/pikku-middleware/references/middleware-patterns.md +0 -61
  200. package/skills/pikku-mongodb/SKILL.md +0 -105
  201. package/skills/pikku-n8n-import/SKILL.md +0 -109
  202. package/skills/pikku-n8n-import/SPEC.md +0 -84
  203. package/skills/pikku-n8n-import/references/addon-mapping.md +0 -121
  204. package/skills/pikku-n8n-import/references/code-translation.md +0 -121
  205. package/skills/pikku-n8n-import/references/loops-and-control.md +0 -87
  206. package/skills/pikku-paraglide/SKILL.md +0 -117
  207. package/skills/pikku-permissions/SKILL.md +0 -192
  208. package/skills/pikku-pino/SKILL.md +0 -79
  209. package/skills/pikku-product-second-opinion/README.md +0 -43
  210. package/skills/pikku-product-second-opinion/SKILL.md +0 -158
  211. package/skills/pikku-product-second-opinion/example/sample-report.md +0 -90
  212. package/skills/pikku-product-second-opinion/references/report-template.md +0 -73
  213. package/skills/pikku-queue/SKILL.md +0 -240
  214. package/skills/pikku-react/SKILL.md +0 -212
  215. package/skills/pikku-react-query/SKILL.md +0 -242
  216. package/skills/pikku-realtime/SKILL.md +0 -236
  217. package/skills/pikku-realtime/references/other-routes.md +0 -23
  218. package/skills/pikku-redis/SKILL.md +0 -90
  219. package/skills/pikku-rpc/SKILL.md +0 -171
  220. package/skills/pikku-rtl/SKILL.md +0 -219
  221. package/skills/pikku-scenario/SKILL.md +0 -382
  222. package/skills/pikku-schedule/SKILL.md +0 -57
  223. package/skills/pikku-schema-ajv/SKILL.md +0 -62
  224. package/skills/pikku-schema-cfworker/SKILL.md +0 -63
  225. package/skills/pikku-security/SKILL.md +0 -108
  226. package/skills/pikku-services/SKILL.md +0 -248
  227. package/skills/pikku-services/references/audit-wire-service.md +0 -34
  228. package/skills/pikku-software-archaeology/README.md +0 -70
  229. package/skills/pikku-software-archaeology/SKILL.md +0 -186
  230. package/skills/pikku-software-archaeology/references/blueprint.schema.json +0 -625
  231. package/skills/pikku-software-archaeology/references/pikku-mapping.md +0 -49
  232. package/skills/pikku-software-archaeology/scripts/validate.mjs +0 -173
  233. package/skills/pikku-tag-middleware/SKILL.md +0 -13
  234. package/skills/pikku-template-clone/SKILL.md +0 -40
  235. package/skills/pikku-trigger/SKILL.md +0 -181
  236. package/skills/pikku-versioning/SKILL.md +0 -173
  237. package/skills/pikku-websocket/SKILL.md +0 -243
  238. package/skills/pikku-workflow/SKILL.md +0 -172
  239. package/skills/pikku-workflow/references/workflow-reference.md +0 -63
  240. package/skills/pikku-workflows-client/SKILL.md +0 -150
  241. package/skills/pikku-ws/SKILL.md +0 -47
@@ -1,109 +0,0 @@
1
- ---
2
- name: pikku-n8n-import
3
- description: 'Use to import an n8n workflow JSON export into a runnable Pikku workflow. Triggers when the user says "import this n8n workflow", "convert this n8n export to pikku", points at an n8n `.json` export or a directory of them, or picks up after `pikku import n8n` left throwing stub functions (`STUB — generated from n8n …`, `— implement me`) or a `<workflow>.integrations.json` manifest. Owns the whole flow: run the importer, triage what it could not map, fill each stub, report any missing `@pikku/addon-*` integrations, and verify the result compiles and runs with no surviving stubs. DO NOT TRIGGER for hand-written addon wiring unrelated to an n8n import (use pikku-addon), or for authoring workflows from scratch (use pikku-workflow).'
4
- metadata:
5
- version: 1.0.0
6
- ---
7
-
8
- # n8n → Pikku Import
9
-
10
- Take an n8n export all the way to a compiling, stub-free Pikku workflow. The
11
- `@pikku/n8n-import` package (invoked by `pikku import n8n`) is **frozen**: it does
12
- the provable, mechanical conversion and leaves everything it cannot prove as a
13
- typed stub that throws at runtime. This skill runs that package, then fills the
14
- remainder with judgment, reports what needs a human decision, and verifies.
15
-
16
- Never re-do what the importer already did, and never hand-edit generated files to
17
- paper over a stub — fix the source cause (the stub function, the graph node, or a
18
- missing dependency).
19
-
20
- ## Agent Operating Procedure
21
-
22
- 1. Discover before editing. Prefer `pikku-meta`/`pikku meta ... --json` when
23
- available; inspect only the focused output you need.
24
- 2. Identify the source file that owns the behavior. Do not start from generated
25
- output, `.pikku`, `node_modules`, or vendored packages.
26
- 3. Make the smallest source change that satisfies the task. Keep generated files
27
- generated.
28
- 4. Validate with the narrowest relevant command first, then `pikku all` /
29
- `pikku-verify` when functions, wirings, or schemas changed.
30
- 5. If validation fails, fix the source cause and rerun. Never edit generated
31
- files to hide an error.
32
-
33
- ## Workflow
34
-
35
- ### 1 — Run the importer (do as much as possible, cheaply)
36
-
37
- ```bash
38
- pikku import n8n <export.json> [outDir]
39
- ```
40
-
41
- It writes `<slug>.graph.ts` (+ `.agent.ts` for AI workflows), `<slug>.addons.gen.ts`,
42
- a `<slug>.integrations.json` manifest, and one stub function per node it could not
43
- map. It **exits 1** on an un-importable input (a cross-workflow sub-workflow
44
- reference, a dynamic workflow target, a mid-flow `respondToWebhook`) with a
45
- `[reason] message` — relay that to the user; do not fake a partial scaffold.
46
-
47
- For a directory of exports, run it per file.
48
-
49
- ### 2 — Triage what it left
50
-
51
- Every unmapped node is a stub that throws `… — implement me`. Classify each by its
52
- JSDoc marker and route to the matching reference:
53
-
54
- | Stub marker / signal | Handle via |
55
- |---|---|
56
- | `STUB — generated from n8n node "…" (type "n8n-nodes-base.<svc>…")` | `references/addon-mapping.md` |
57
- | `STUB — generated from n8n Code node "…"` | `references/code-translation.md` |
58
- | A `control` stub — Loop Over Items / **splitInBatches**, Switch expr-mode | `references/loops-and-control.md` |
59
- | `STUB — … vector-store … #902` | rare now (RAG ships as `<store>:query`/`:ingest`); a residual one = an unmapped store → report it, don't guess |
60
- | Importer `diagnostics` (already exited 1) | explain the reason; the workflow is un-importable as-is |
61
-
62
- Read a reference file only when you actually hit that stub class.
63
-
64
- ### 3 — Fill each stub
65
-
66
- Work the manifest + stub files per the routed reference. The mechanical classes
67
- (addon, code) are near-deterministic; the loop/control class needs judgment
68
- (map vs reduce, done-branch semantics) — reference `loops-and-control.md` tells you
69
- when to decide vs ask.
70
-
71
- ### 4 — Report missing integrations (first-class output)
72
-
73
- An addon stub can only be wired to an **installed** `@pikku/addon-*`. When the
74
- package for an n8n service is not in `dependencies`, do not guess a lookalike —
75
- collect it. Give the user one upfront list:
76
-
77
- ```
78
- Missing integrations — install these or the nodes stay stubs:
79
- • slackTool "Post to channel" → npm i @pikku/addon-chat-slack
80
- • hubspot "Create contact" → no @pikku/addon-hubspot exists yet
81
- ```
82
-
83
- ### 5 — Verify it works
84
-
85
- 1. `pikku all` (regenerate) → `yarn tsc` from the package root; fix the source
86
- cause of any error and rerun.
87
- 2. Grep the emitted functions for any surviving `— implement me`
88
- / `throw new Error('Stub:`. **Any survivor means the import is not done** —
89
- list them by node name.
90
- 3. Green tsc **and** zero surviving stubs = success.
91
-
92
- ## References
93
-
94
- | Open when you need to… | Read |
95
- |---|---|
96
- | map an integration stub (gmailTool, slackTool, googleSheets, plain action nodes) to an installed addon `ref(...)` | `references/addon-mapping.md` |
97
- | translate an n8n Code node body into a Pikku function body | `references/code-translation.md` |
98
- | lower a Loop Over Items / splitInBatches loop, or a Switch that stayed a stub | `references/loops-and-control.md` |
99
-
100
- ## Final summary
101
-
102
- Report, terse:
103
-
104
- - Files written and workflow shape (`pure-graph` / `agent`).
105
- - Stubs filled, by class.
106
- - **Missing integrations** (the step-4 list) — the thing the user must act on.
107
- - Anything left as a `// TODO:` and why (credentials to wire, a loop deferred, an
108
- unmapped store).
109
- - Verification: `tsc` status + surviving-stub count (must be 0).
@@ -1,84 +0,0 @@
1
- # n8n → Pikku Import Specification
2
-
3
- ## Intent
4
-
5
- Take an n8n workflow JSON export all the way to a compiling, stub-free, runnable
6
- Pikku workflow. The `@pikku/n8n-import` package is treated as **frozen**: it does
7
- the provable mechanical conversion and leaves everything it cannot prove as a typed,
8
- throwing stub. This skill owns the end-to-end flow around it — run it, fill the
9
- remainder with judgment, report gaps that need a human decision, and verify.
10
-
11
- ## Scope
12
-
13
- In scope:
14
- - Running `pikku import n8n` and triaging its output.
15
- - Filling integration stubs (→ addon refs), Code stubs (→ function bodies), and
16
- loop/control stubs (→ `graph:map`/reduce/branch).
17
- - Reporting missing `@pikku/addon-*` integrations as a first-class output.
18
- - Verifying via `pikku all` + `tsc` + a zero-surviving-stub check.
19
-
20
- Out of scope:
21
- - Extending `@pikku/n8n-import` itself (it is frozen; do not add per-service tables
22
- or new compiler rules to it).
23
- - Authoring workflows from scratch (`pikku-workflow`) or hand-written addon wiring
24
- unrelated to an import (`pikku-addon`).
25
- - Inventing batching primitives or guessing ambiguous loop semantics — surface them.
26
-
27
- ## Users And Trigger Context
28
-
29
- - Primary users: developers importing their own n8n workflows into a Pikku app,
30
- usually inside an agentic session.
31
- - Common requests: "import this n8n workflow", "convert this n8n export to pikku",
32
- finishing `— implement me` stubs, wiring a `*.integrations.json` manifest.
33
- - Should not trigger for: from-scratch workflow authoring, or addon wiring with no
34
- n8n import involved.
35
-
36
- ## Runtime Contract
37
-
38
- - Required first action: run `pikku import n8n <export.json> [outDir]` (per file for
39
- a directory); relay any exit-1 diagnostic instead of scaffolding a partial.
40
- - Required outputs: filled stubs, a missing-integrations list, verification status.
41
- - Non-negotiable: never hand-edit generated files to hide a stub; map only to
42
- installed addons; zero surviving `— implement me` stubs at success.
43
- - Bundled files loaded at runtime: `references/addon-mapping.md`,
44
- `references/code-translation.md`, `references/loops-and-control.md` — each only
45
- when its stub class appears.
46
-
47
- ## Source And Evidence Model
48
-
49
- Authoritative sources:
50
- - `@pikku/n8n-import` codegen (stub markers, manifest shape, `import-n8n` command).
51
- - `@pikku/addon-graph` function contracts (`graph:map`/`fanout`, `branch`).
52
- - Installed `@pikku/addon-*` source (function names verified by grep, never guessed).
53
-
54
- Useful improvement sources: real imported workflows, harness coverage deltas,
55
- addon catalogue changes.
56
-
57
- Data that must not be stored: credential secrets, customer data, private ids beyond
58
- what a manifest already records for reproduction.
59
-
60
- ## Reference Architecture
61
-
62
- - `SKILL.md`: the run → triage → fill → report → verify workflow + router.
63
- - `references/`: per-stub-class depth (addon mapping, code translation, loops/control).
64
-
65
- ## Validation
66
-
67
- - Lightweight: `yarn tsc` from the package root after each fill.
68
- - Deeper: `pikku all` regeneration; grep emitted functions for surviving stub throws.
69
- - Acceptance gates: green tsc **and** zero surviving `— implement me` stubs.
70
-
71
- ## Known Limitations
72
-
73
- - `splitInBatches` with `batchSize > 1` and reduce-style accumulators have no direct
74
- primitive — surfaced, not auto-converted.
75
- - Missing addons block their nodes; the skill reports, it does not install.
76
- - Cross-workflow sub-workflow references fail import at the package level.
77
-
78
- ## Maintenance Notes
79
-
80
- - Update `SKILL.md` when the import command, stub taxonomy, or verify gates change.
81
- - Update a reference when an addon convention, the `graph:map` contract, or a rubric
82
- changes.
83
- - This skill supersedes the former `pikku-n8n-addon-map` and `pikku-n8n-code-translate`
84
- skills (folded into `references/addon-mapping.md` and `references/code-translation.md`).
@@ -1,121 +0,0 @@
1
- # Integration stub → Pikku addon
2
-
3
- Translate n8n integration nodes (`gmailTool`, `slackTool`, `googleSheetsTool`, plain
4
- `gmail` / `slack` action nodes, etc.) that the importer left as throwing stubs into
5
- real `ref('<addonRpc>')` references pointing at functions in **installed**
6
- `@pikku/addon-*` packages.
7
-
8
- This is **per-stub mechanical**. Do not invent business logic, chain calls, or
9
- "improve" the workflow. Read one manifest entry, find the matching addon function,
10
- rewrite the stub.
11
-
12
- ## Inputs
13
-
14
- 1. **Manifest** — `<workflow>.integrations.json` next to the `.graph.ts`. Each entry:
15
- ```jsonc
16
- {
17
- "rpcName": "agentGmailtool__sendAMessageInGmail",
18
- "n8nType": "n8n-nodes-base.gmailTool",
19
- "n8nName": "Send a message in Gmail",
20
- "parameters": { "sendTo": "...", "message": "...", "subject": "..." },
21
- "credentials": { "gmailOAuth2": { "id": "...", "name": "Personal Gmail" } },
22
- "isAgentTool": true,
23
- "agentName": "Inbox Assistant"
24
- }
25
- ```
26
- 2. **Installed addons** — `@pikku/addon-*` in the project's `package.json`
27
- `dependencies`. Map **only** against installed packages. If the addon for an n8n
28
- type is not installed, surface it (see SKILL step 4) — never silently skip and
29
- never pick a vaguely-named function from another addon.
30
-
31
- ## Per entry, in order
32
-
33
- ### Step 1 — identify the target addon
34
-
35
- Map `n8nType` to a package by reading its source. Common shapes (**guesses, not
36
- authoritative** — always verify against installed source):
37
-
38
- | n8n type prefix | typical addon candidate |
39
- |---|---|
40
- | `n8n-nodes-base.gmail` / `gmailTool` | `@pikku/addon-email-gmail` |
41
- | `n8n-nodes-base.slack` / `slackTool` | `@pikku/addon-chat-slack` |
42
- | `n8n-nodes-base.googleSheets` / `…Tool` | `@pikku/addon-sheets-google` |
43
- | `n8n-nodes-base.notion` / `notionTool` | `@pikku/addon-docs-notion` |
44
- | `n8n-nodes-base.telegram` / `telegramTool` | `@pikku/addon-chat-telegram` |
45
-
46
- If no installed addon plausibly covers the n8n type, stop and report it — do not
47
- pick a wrong addon.
48
-
49
- ### Step 2 — pick the function (resource + operation → fn name)
50
-
51
- n8n nodes use a `(resource, operation)` pair. Rubric:
52
-
53
- - `resource` defaults to the integration's primary noun if absent (gmail →
54
- `message`, slack → `message`, sheets → `spreadsheet`). Read the addon's folder
55
- structure (`messages/`, `drafts/`, `channels/`) to see what nouns exist.
56
- - `operation` is usually a verb (`get`, `getAll`, `send`, `delete`, `addLabels`).
57
- - The pikku function name is almost always `<resource><Verb>` in camelCase, matching
58
- the file's `export const` (`messageList` for `messages/list.function.ts`).
59
-
60
- Verify: `grep -h "^export const" <addonPkg>/src/functions/**/*.ts` and match by name.
61
- Conventions in `@pikku/addon-email-gmail` (a **sanity reference**, not a fallback):
62
-
63
- - `getAll → <resource>List`, `get → <resource>Get`, `send → <resource>Send`,
64
- `delete → <resource>Delete`, `reply → <resource>Reply`
65
- - `addLabels → <resource>AddLabel` (singular!), `removeLabels → <resource>RemoveLabel`
66
- - `markAsRead / markAsUnread → <resource>MarkRead / <resource>MarkUnread`
67
- - `create → <resource>Create`
68
-
69
- If the function has a `node:` block, prefer matching its `category`/`displayName`
70
- over guessing.
71
-
72
- ### Step 3 — rewrite the stub
73
-
74
- Two outcomes, by `isAgentTool`:
75
-
76
- **A) `isAgentTool: true`** — the stub is an agent tool referenced via `ref()`:
77
- 1. Delete the stub file.
78
- 2. In the agent file, replace `ref('agentGmailtool__sendAMessageInGmail')` in
79
- `tools: [...]` with `ref('messageSend')` (the resolved addon function).
80
- 3. Ensure the addon is where pikku scans functions (usually automatic via
81
- `node_modules/@pikku/addon-*`).
82
-
83
- If you can't delete safely, leave a one-line re-export instead of a stub:
84
- ```ts
85
- import { messageSend } from '@pikku/addon-email-gmail'
86
- export const agentGmailtool__sendAMessageInGmail = messageSend
87
- ```
88
- Default is delete + retarget; wrappers add maintenance burden.
89
-
90
- **B) `isAgentTool: false`** — the stub is a graph node:
91
- 1. Open `<workflow>.graph.ts`.
92
- 2. In `nodes: { … }` find the entry whose value is the stub rpc name.
93
- 3. Replace it with the addon function name (`'messageSend'`).
94
- 4. If `config: { <id>: { input } }` produces an `{ items }` envelope, rewrite it to
95
- the addon function's real input schema (read its `input: z.object({...})`).
96
- 5. Delete the stub file.
97
-
98
- ### Step 4 — port hard-coded parameters
99
-
100
- - **Hardcoded values** (`"limit": 20`, `"labelIds": ["INBOX"]`) were user choices.
101
- Preserve them in the graph node's `input` (case B). **Agent tools cannot carry
102
- hardcoded params** (the LLM fills args at call time) — surface the trade-off; if
103
- the user needs a value pinned, they keep a thin wrapper.
104
- - **`$fromAI('Name', '', 'string')`** placeholders are LLM-filled — the addon's Zod
105
- schema becomes the tool schema. Just drop the placeholder string; no other action.
106
-
107
- ### Step 5 — credentials
108
-
109
- `credentials: { gmailOAuth2: { id, name } }` is the n8n credential ref. Pikku addons
110
- expect a wired service (`services.gmail`). Do **not** auto-wire — leave a TODO:
111
-
112
- > `// TODO: wire services.gmail using credential "Personal Gmail" (n8n id: gmail_cred_1) — see @pikku/addon-email-gmail/README.md`
113
-
114
- ## Never
115
-
116
- - Invent functions that don't exist — grep first.
117
- - Pick a wrong addon because the right one isn't installed — report `npm i @pikku/addon-<x>`.
118
- - Bake per-mapping tables into `@pikku/n8n-import` — it is addon-agnostic; mapping lives here.
119
- - Modify the manifest — it's an audit artifact.
120
- - Chain calls — each entry maps to exactly one addon function.
121
- - Silently drop a hardcoded param — surface it.
@@ -1,121 +0,0 @@
1
- # n8n Code node → Pikku function
2
-
3
- Replace a Code-node stub's `throw new Error(...)` body with a faithful TypeScript
4
- reimplementation. The original JS is preserved verbatim in the JSDoc above the
5
- function. Keep the signature and JSDoc intact; only widen the Zod input/output if
6
- the code's data shape demands it.
7
-
8
- **Narrow, mechanical translation** — behavioral parity, not better code. Do not
9
- refactor, add error handling, or invent fields.
10
-
11
- ## Process
12
-
13
- 1. **Read the file.** Identify the input schema, output schema, the verbatim JS in
14
- the JSDoc, and the `pikkuSessionlessFunc` shape.
15
- 2. **Determine the n8n mode** from the original JSON if available (importer leaves it
16
- in `fixtures/`, or ask). Two modes:
17
- - `runOnceForAllItems` (default) — code runs once with `items: Array<{ json, binary, pairedItem }>`, returns an array of envelopes.
18
- - `runOnceForEachItem` — runs once per item, `$json` / `$input.item.json` in scope, returns a single envelope.
19
- - If unknown, infer: bare `items.X` → all-items; bare `$json.X` / `$input.item.X` → each-item.
20
- 3. **Apply the rubric.**
21
- 4. **Edit only the function body.** Leave imports, schemas, JSDoc, name, description, refs untouched unless step 5 forces it.
22
- 5. **If the schemas are wrong** (code reads `$json.userId: string` but input is `items: z.array(z.unknown())`), tighten with the smallest change. Prefer `z.unknown()` over `z.any()`. Never widen output to `z.any()`.
23
- 6. **Add one comment** at the top of the body noting the mode: `// translated from n8n Code node, mode: runOnceForAllItems`. This is the *only* comment you may add.
24
- 7. **Typecheck** (`yarn tsc` from the package root); fix errors with the smallest change.
25
-
26
- ## Rubric
27
-
28
- ### Envelope unwrapping — all-items mode
29
-
30
- | n8n | Pikku |
31
- |---|---|
32
- | `items` | `(data.items ?? []) as any[]` (or typed if known) |
33
- | `items[i].json.X` | `items[i].X` |
34
- | `items[i].json` | `items[i]` |
35
- | `items[i].binary` | **NOT supported** — leave a TODO and explain |
36
- | `items.length` | `items.length` |
37
- | `items.map(i => i.json.X)` | `items.map((i: any) => i.X)` |
38
-
39
- ### Envelope unwrapping — each-item mode
40
-
41
- | n8n | Pikku |
42
- |---|---|
43
- | `$json.X` / `$input.item.json.X` | `data.X` (input is the item itself) |
44
- | `$input.item.json` | `data` |
45
- | `$input.all()` | not available per-item — change to all-items mode |
46
-
47
- ### Return statement
48
-
49
- | n8n | Pikku |
50
- |---|---|
51
- | `return [{ json: X }]` | `return { items: [X] }` |
52
- | `return items.map(i => ({ json: ... }))` | `return { items: items.map(...) }` |
53
- | `return [{ json: X }, { json: Y }]` | `return { items: [X, Y] }` |
54
- | `return { json: X }` (each-item) | `return X` |
55
- | `return [...]` (already plain) | wrap in `{ items: [...] }` only if the output schema expects it |
56
-
57
- ### Built-ins — do NOT auto-translate
58
-
59
- If the code references any of these, **stop**, leave the body a stub, and annotate
60
- `// TODO:` + explain:
61
-
62
- - `this.helpers.*` (binary buffers, HTTP requests, prepareBinaryData)
63
- - `$node['Some Node'].json` (cross-node refs — resolve via Pikku `ref()` upstream, not in the body)
64
- - `$workflow`, `$execution`, `$item()`, `$items('Other Node')`
65
- - `getBinaryDataBuffer` / `getStaticData` — no equivalent, TODO
66
- - `require()` / dynamic `import()` — flag and stop
67
-
68
- Translate these only when reachable: `$now`/`$today` → `new Date()`; `$env.X` →
69
- `services.variables.get('X')` (never `process.env` — Pikku house rule; `services` is
70
- the first param).
71
-
72
- ### Async / types
73
-
74
- - Original uses `await` → the body is already `async`; keep every `await`.
75
- - `this.helpers.httpRequest(...)` → do NOT inline; the user should use a separate
76
- `httpRequest` rpc node. Leave a `// TODO:` and explain.
77
- - Cast `items` as `any[]` only if the schema is `z.array(z.unknown())`; use the
78
- inferred type if tightened. Never `as any` on the return — fix the schema instead.
79
-
80
- ## Example
81
-
82
- Before (stub):
83
- ```ts
84
- /**
85
- * STUB — generated from n8n Code node "Custom Code".
86
- * const total = items.reduce((acc, i) => acc + i.json.amount, 0);
87
- * return [{ json: { total } }];
88
- */
89
- export const codeStubCustomCode = pikkuSessionlessFunc({
90
- input: CodeStubCustomCodeInput,
91
- output: CodeStubCustomCodeOutput,
92
- func: async (_services, _data) => {
93
- throw new Error('Stub: ported from n8n Code node "Custom Code" — implement me')
94
- },
95
- })
96
- ```
97
-
98
- After:
99
- ```ts
100
- export const codeStubCustomCode = pikkuSessionlessFunc({
101
- description: 'Ported from n8n Code node "Custom Code"',
102
- input: CodeStubCustomCodeInput,
103
- output: CodeStubCustomCodeOutput,
104
- func: async (_services, data) => {
105
- // translated from n8n Code node, mode: runOnceForAllItems
106
- const items = (data.items ?? []) as any[]
107
- const total = items.reduce((acc, i) => acc + i.amount, 0)
108
- return { items: [{ total }] }
109
- },
110
- })
111
- ```
112
-
113
- ## Report
114
-
115
- Terse: the mode you inferred (one sentence), the literal rubric translations
116
- applied, anything flagged TODO and why, any schema tightening (before → after).
117
-
118
- Do not add tests, refactor, edit other files, "improve" the logic, or add
119
- try/catch unless the original did. If the code is empty, comment-only, or so
120
- dependent on n8n internals that no honest translation is possible, leave the stub
121
- and tell the user which n8n features block it.
@@ -1,87 +0,0 @@
1
- # Loops & control stubs
2
-
3
- The importer maps the mechanical control flow (IF/Filter/Switch it can normalize →
4
- `graph:branch`) but leaves the **semantic** cases as `control` stubs — chiefly
5
- **Loop Over Items / splitInBatches** and Switch in expression mode. These need
6
- judgment, which is why they are not compiled. Read the loop body and the workflow
7
- around it; decide, or ask.
8
-
9
- ## Loop Over Items / splitInBatches
10
-
11
- n8n's loop node has two outputs: **loop** (output 1, fires per batch) and **done**
12
- (output 0, fires once when iteration finishes). The loop body flows from the loop
13
- output back into the node — a cycle. Pikku graphs are a DAG, so **the loop becomes a
14
- `graph:map`** (`@pikku/addon-graph`) and the back-edge disappears:
15
-
16
- ```ts
17
- theLoop: "graph:map", // (graph:fanout) — one child invocation per item
18
- // config:
19
- theLoop: {
20
- input: (ref) => ({
21
- items: ref("<predecessor>"), // what fed the loop
22
- child: "<childRpc-or-subGraph>", // the loop body
23
- childInput: { /* $item-rebound body input */ },
24
- stepPrefix: "theLoop",
25
- }),
26
- next: "<done-branch target>", // output 0
27
- }
28
- ```
29
-
30
- Inside `childInput`, references rebind to the current element: the body's `$json` /
31
- predecessor and any `$('<loop node>')` become `$item`.
32
-
33
- ### Decide the shape first
34
-
35
- | Loop body does… | Emit |
36
- |---|---|
37
- | transform each item independently (enrich, format, call one thing) | `graph:map` — child = the body |
38
- | accumulate across items (running total, build one object/array) | a **reduce**: a single generated function over the whole array, not a map — `graph:map` collects per-item results and *loses* the accumulator |
39
- | pure side-effect per item, nothing downstream consumes results | `graph:map` with **no `next`** (done branch empty) — the safest, unambiguous case |
40
-
41
- ### Child arity
42
-
43
- - **Single-node body** → `child: "<that node's rpc>"`, its input as `childInput`.
44
- - **Multi-node body** → the child must be a per-item **sub-graph**. Lift the body
45
- into its own `pikkuWorkflowGraph` (see `pikku-workflow`) and set `child` to that
46
- workflow's registered name. If the body references nodes **outside** the loop
47
- (not just the item), that value has to be threaded in as `childInput` — if you
48
- can't do it cleanly, stop and ask rather than emit something subtly wrong.
49
-
50
- ### Done-branch semantics (ask if it matters)
51
-
52
- n8n's done output is version-dependent: it may carry the *original* items or the
53
- *accumulated* results. `graph:map`'s `next` receives the array of child results.
54
- If a downstream node reads that array's shape and the distinction matters, add:
55
-
56
- ```ts
57
- // TODO(n8n): done branch receives collected loop results (not original items) — confirm this matches intent
58
- ```
59
-
60
- and call it out in your summary. When the done branch is empty, there's nothing to
61
- decide.
62
-
63
- ### batchSize > 1
64
-
65
- `graph:map` is one-item-at-a-time. A real numeric `batchSize` (chunk into groups,
66
- run the body per chunk) has no direct primitive — leave the stub, and tell the user
67
- this loop batches N-at-a-time and needs a manual pass (or a `graph:chunk` +
68
- `graph:map` composition if the body is chunk-shaped).
69
-
70
- ## Switch / control stubs the importer couldn't normalize
71
-
72
- A Switch in **expression mode** (routing by an arbitrary JS expression rather than
73
- comparable conditions) stays a `control` stub. Options, in order of preference:
74
-
75
- 1. If the expression is really a set of value comparisons, rewrite the node as a
76
- `graph:branch` by hand (see `pikku-workflow` for the `branch` shape) and wire the
77
- emitted `next` keys to the branch targets.
78
- 2. If it's genuinely computed routing, translate the stub into a small function that
79
- returns the branch key, then feed it a `graph:branch`.
80
- 3. If neither is faithful, leave the stub and explain what the Switch does.
81
-
82
- ## Never
83
-
84
- - Emit a `graph:map` for an accumulator loop — you'll silently drop the running state.
85
- - Guess the done-branch semantics when a downstream node depends on the shape — mark
86
- it and surface it.
87
- - Invent a batching primitive — say what's unsupported instead.
@@ -1,117 +0,0 @@
1
- ---
2
- name: pikku-paraglide
3
- description: 'Generate typed, static enum-label maps for a Paraglide i18n frontend with `@pikku/paraglide`, and reconcile them against the database enum columns so a label can never silently drift from a DB value. Enum-valued labels live under a reserved `enum__<group>__<member>` message namespace; the generator emits `i18n-enum.gen.ts` typed `satisfies EnumLabel<DbEnum>`. TRIGGER when: labelling an enum/status/kind/role value in a Paraglide app, replacing a dynamic `mKey(...)`/`m[...]` lookup with a static map, wiring `@pikku/paraglide` into Vite, or reconciling i18n against `CHECK (col IN (...))` / Postgres enum columns. DO NOT TRIGGER for plain free-text UI copy (that is a normal `m.some_key()` message), backend errors, or logs.'
4
- installGroups: [core]
5
- ---
6
-
7
- # Pikku Paraglide enum labels
8
-
9
- ## Agent Operating Procedure
10
-
11
- Use this as an execution checklist, not reference material.
12
-
13
- 1. **Is the value an enum (a closed set — a status/kind/role/tag from a DB column or a fixed union)?** Then its label is a static map entry, never a dynamic lookup. Add `enum__<group>__<member>` keys to the catalog (`messages/en.json`) and read the value through the generated map: `group[value]()`.
14
- 2. **Is the value free text or a one-off literal** (a heading, a button, a never-indexed label)? Then it is a normal Paraglide message — call `m.<key>()` directly. Do **not** invent an enum group for something that is always a literal.
15
- 3. **Wire the generator** (`@pikku/paraglide/vite` or the CLI) so `i18n-enum.gen.ts` is regenerated from the catalog, and point it at the DB enums module (`enums.gen.ts`) so the maps are typed against the database.
16
- 4. **Validate with the app's own `tsc`.** Reconciliation is enforced purely by types: a missing key or a dropped DB member is a compile error, and the deploy gate runs `tsc` before building. A clean `vite build` alone does not type-check.
17
-
18
- ## The rules that don't change
19
-
20
- - **Never resolve an enum key dynamically.** No `mKey('status.' + value)`, no `m['enum__status__' + value]()`, no `mExists`/`mList` helpers. Dynamic keys can't be type-checked or tree-shaken. Everything is a static `m.<literal>()` reference, generated into the map.
21
- - **The `enum__<group>__<member>` namespace.** `__` separates the prefix / group / member segments; a single `_` joins words *within* a segment (`enum__booking_status__form_received`). The prefix (`enum`) and separator (`__`) are configurable but leave them at the defaults.
22
- - **Members must be valid JS identifiers.** Spell out leading digits — `two_guests`, not `2_guests`. The generator quotes an invalid member as a fallback but warns you to rename it.
23
- - **`asI18n(...)` is only for opaque server data** (names, slugs, ids returned from the API). Never `asI18n()` a hardcoded English string or an enum value — an enum value goes through its label map.
24
-
25
- ## The generated module
26
-
27
- `i18n-enum.gen.ts` is **AUTO-GENERATED — do not edit.** It exports, per enum group:
28
-
29
- ```ts
30
- import { m } from './messages.js'
31
- import type { I18nString } from '@pikku/react'
32
- import type { BookingStatus } from '#pikku/db/enums.gen' // when reconciled
33
-
34
- export type I18nMessage = () => I18nString
35
- export type EnumLabel<E extends string> = Record<E, I18nMessage>
36
-
37
- export const bookingStatus = {
38
- enquiry: m.enum__booking_status__enquiry,
39
- reserved: m.enum__booking_status__reserved,
40
- confirmed: m.enum__booking_status__confirmed,
41
- } satisfies EnumLabel<BookingStatus>
42
- export type BookingStatusKey = keyof typeof bookingStatus
43
- ```
44
-
45
- - Each value is an `I18nMessage` — a `() => I18nString` accessor. **Call it at render time** so the label tracks the active locale.
46
- - App code: `import { bookingStatus } from './i18n/i18n-enum.gen'` then `bookingStatus[value]()`.
47
- - For an open server value, gate it: `value in bookingStatus ? bookingStatus[value as BookingStatusKey]() : asI18n(value)`.
48
-
49
- ### Module-scope hazard — store the accessor, don't call it
50
-
51
- A label used in a config built at module load (nav items, column defs) must hold the **accessor**, not the result — calling `m.foo()` at module scope freezes the label to the locale that was active at import:
52
-
53
- ```ts
54
- // nav.config.ts
55
- const items = [{ label: m.common__nav__items__dashboard /* ← reference */ }]
56
- // at render: <span>{item.label()}</span> // ← call here
57
- ```
58
-
59
- ## Reconciliation against the database
60
-
61
- The DB column is the real source of truth for what an enum can be. The pikku CLI's db codegen emits a bare unions module — `.pikku/db/enums.gen.ts` — covering **both** Postgres native enums and SQLite `CHECK (col IN ('a','b',…))` constraints:
62
-
63
- ```ts
64
- export type BookingStatus = 'enquiry' | 'reserved' | 'confirmed' | 'ended' | 'cancelled'
65
- ```
66
-
67
- Point `@pikku/paraglide` at that file (`enumsFile`) and each catalog group whose member set **exactly matches** a DB enum is typed `satisfies EnumLabel<DbEnum>`. The label map then **is** the reconciliation — no separate assertion:
68
-
69
- - catalog drops a DB member, or `en.json` is missing the key → `m.enum__…` doesn't exist / `Record<DbEnum,…>` isn't exhaustive → **`tsc` error naming the gap**.
70
- - a DB enum with **no** catalog group → `unmatchedDbEnums: 'emit'` (default) generates a label map referencing `enum__<table>_<column>__<member>` keys, so `tsc` tells you exactly which keys to add; `'warn'` only reports it.
71
- - a group with a member the DB lacks (a *derived* UI state, e.g. a `waitlisted` view of a `pending` row) → a drift warning. Make that a **standalone `m.<key>()` message**, not an enum member — the enum group must mirror the DB column exactly.
72
-
73
- Labelling an enum that's never rendered costs nothing: Paraglide compiles only the messages actually referenced, so unused labels are tree-shaken away. So label every DB enum; don't add an opt-out.
74
-
75
- **To make a column an enum**, give it a closed domain in the migration so codegen can see it:
76
- - SQLite: `status TEXT NOT NULL CHECK (status IN ('enquiry','reserved','confirmed'))`
77
- - Postgres: a native `CREATE TYPE … AS ENUM (…)` column.
78
-
79
- ## Wiring
80
-
81
- ### Vite (dev + build)
82
-
83
- Place `paraglideEnums` **after** `paraglideVitePlugin` (the generated file imports the compiled `m`). It regenerates on catalog/enums edits and only writes on change, so it never loops HMR.
84
-
85
- ```ts
86
- import { paraglideVitePlugin } from '@inlang/paraglide-js'
87
- import { paraglideEnums } from '@pikku/paraglide/vite'
88
-
89
- export default defineConfig({
90
- plugins: [
91
- paraglideVitePlugin({ project: './project.inlang', outdir: './src/paraglide' }),
92
- paraglideEnums({
93
- catalog: './messages/en.json',
94
- outFile: './src/i18n/i18n-enum.gen.ts',
95
- enumsFile: './packages/functions/.pikku/db/enums.gen.ts', // reconcile against the DB
96
- // enumsImport: '#pikku/db/enums.gen', // explicit specifier; defaults to a relative path
97
- }),
98
- ],
99
- })
100
- ```
101
-
102
- ### CLI (CI / non-Vite), run right after `paraglide-js compile`
103
-
104
- ```sh
105
- # paraglide-enums <catalog.json> <out.gen.ts> [messagesImport] [enums.gen.ts]
106
- paraglide-enums ./messages/en.json ./src/i18n/i18n-enum.gen.ts ./messages.js ./packages/functions/.pikku/db/enums.gen.ts
107
- ```
108
-
109
- `i18n-enum.gen.ts` is generated — gitignore it once the plugin/CLI runs in the build.
110
-
111
- ## What NOT to do
112
-
113
- - Don't write `mKey`/`mList`/`mExists` or any `m[expr]()` dynamic lookup — every enum label is a static generated reference.
114
- - Don't introduce a literal-key indirection helper (`k('approve_enquiry')`); a literal is `m.approve_enquiry()` directly.
115
- - Don't call `m.foo()` at module scope for config built at import time — store `m.foo` and call it at render.
116
- - Don't put an extra UI-only member into an enum group to match a derived state — make it a standalone message and keep the group an exact mirror of the DB column.
117
- - Don't hand-edit `i18n-enum.gen.ts` or `enums.gen.ts` — fix the catalog / the migration and regenerate.