@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.
- package/LICENSE +106 -0
- package/console-app/assets/{index-D0HG8q0B.css → index-BYj7ZbUU.css} +1 -1
- package/console-app/assets/{index-CpkvmAGb.js → index-CmAplHjZ.js} +151 -149
- package/console-app/index.html +2 -2
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +16 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +15 -0
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +48 -0
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +73 -40
- package/dist/.pikku/function/pikku-functions.gen.js +1 -3
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +2 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +2 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +3 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.d.ts +4 -0
- package/dist/.pikku/scenarios/schemas/register.gen.js +4 -0
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +13 -3
- package/dist/.pikku/schemas/schemas/FabricSecretsListOutput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/FabricSecretsRotateInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricSecretsRotateOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricSecretsSetOutput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/KnowledgeIndexInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/KnowledgeIndexOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/KnowledgeValidateInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/KnowledgeValidateOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/ScenarioRunInput.schema.json +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/meta/allWorkflow.gen.json +2 -8
- package/dist/.pikku/workflow/pikku-scenario-actors.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-scenario-actors.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/bin/pikku-bin.mjs +2 -2
- package/dist/src/cli.wiring.js +29 -0
- package/dist/src/deploy/analyzer/analyzer.js +22 -5
- package/dist/src/deploy/build-pipeline.js +5 -1
- package/dist/src/fabric/fabric-commands.d.ts +37 -9
- package/dist/src/fabric/fabric-commands.js +12 -0
- package/dist/src/fabric/functions/domains-add.function.d.ts +4 -4
- package/dist/src/fabric/functions/secrets-list.function.d.ts +16 -4
- package/dist/src/fabric/functions/secrets-list.function.js +12 -10
- package/dist/src/fabric/functions/secrets-rotate.function.d.ts +24 -0
- package/dist/src/fabric/functions/secrets-rotate.function.js +36 -0
- package/dist/src/fabric/functions/secrets-set.function.d.ts +8 -4
- package/dist/src/fabric/functions/secrets-set.function.js +18 -7
- package/dist/src/fabric/functions/validate.function.js +16 -7
- package/dist/src/fabric/lib/http.d.ts +7 -1
- package/dist/src/fabric/lib/sealed-box.d.ts +16 -0
- package/dist/src/fabric/lib/sealed-box.js +72 -0
- package/dist/src/functions/commands/dev.js +9 -0
- package/dist/src/functions/commands/knowledge-index.d.ts +30 -0
- package/dist/src/functions/commands/knowledge-index.js +11 -0
- package/dist/src/functions/commands/knowledge-validate.d.ts +33 -0
- package/dist/src/functions/commands/knowledge-validate.js +11 -0
- package/dist/src/functions/commands/new-addon.d.ts +18 -0
- package/dist/src/functions/commands/new-addon.js +40 -24
- package/dist/src/functions/commands/pikku-command-bootstrap.js +1 -0
- package/dist/src/functions/commands/scenario-browser.d.ts +17 -2
- package/dist/src/functions/commands/scenario-browser.js +16 -8
- package/dist/src/functions/commands/scenario-environment.d.ts +32 -0
- package/dist/src/functions/commands/scenario-environment.js +61 -0
- package/dist/src/functions/commands/scenario.d.ts +6 -0
- package/dist/src/functions/commands/scenario.js +14 -9
- package/dist/src/functions/commands/skills.js +27 -61
- package/dist/src/functions/db/db-codegen.d.ts +6 -0
- package/dist/src/functions/db/db-codegen.js +9 -0
- package/dist/src/functions/db/db-migrator.js +14 -0
- package/dist/src/functions/db/local-db.d.ts +8 -0
- package/dist/src/functions/db/local-db.js +24 -2
- package/dist/src/functions/db/migration-identifiers.d.ts +58 -0
- package/dist/src/functions/db/migration-identifiers.js +262 -0
- package/dist/src/functions/db/migration-provenance.d.ts +35 -0
- package/dist/src/functions/db/migration-provenance.js +80 -0
- package/dist/src/functions/db/schema-sql.d.ts +43 -0
- package/dist/src/functions/db/schema-sql.js +135 -0
- package/dist/src/functions/db/sqlite/sqlite-kysely.js +22 -2
- package/dist/src/functions/knowledge/render.d.ts +3 -0
- package/dist/src/functions/knowledge/render.js +66 -0
- package/dist/src/functions/knowledge/schemas.d.ts +51 -0
- package/dist/src/functions/knowledge/schemas.js +25 -0
- package/dist/src/functions/wirings/credentials/serialize-credentials-types.js +8 -1
- package/dist/src/functions/wirings/functions/schemas.js +13 -1
- package/dist/src/functions/wirings/rpc/pikku-command-rpc.js +6 -1
- package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.d.ts +10 -0
- package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.js +102 -0
- package/dist/src/functions/wirings/scenarios/scenario-partition.d.ts +13 -0
- package/dist/src/functions/wirings/scenarios/scenario-partition.js +15 -2
- package/dist/src/functions/wirings/scenarios/scenario-schema-partition.d.ts +33 -0
- package/dist/src/functions/wirings/scenarios/scenario-schema-partition.js +49 -0
- package/dist/src/functions/wirings/secrets/serialize-secrets-types.js +10 -3
- package/dist/src/functions/wirings/variables/serialize-variables-types.js +8 -1
- package/dist/src/functions/workflows/all.workflow.js +2 -7
- package/dist/src/utils/file-writer.js +14 -2
- package/dist/src/utils/pikku-cli-config.d.ts +14 -0
- package/dist/src/utils/pikku-cli-config.js +34 -0
- package/dist/src/utils/remove-legacy-scaffold-file.d.ts +14 -0
- package/dist/src/utils/remove-legacy-scaffold-file.js +23 -0
- package/dist/src/utils/serialize-schemas.d.ts +3 -1
- package/dist/src/utils/serialize-schemas.js +39 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -9
- package/dist/.pikku/schemas/schemas/PikkuScenarioFunctionsOutput.schema.json +0 -1
- package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.d.ts +0 -1
- package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.js +0 -31
- package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.d.ts +0 -10
- package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.js +0 -104
- package/skills/pikku-addon/SKILL.md +0 -243
- package/skills/pikku-addon/references/addon-package-manifest.md +0 -63
- package/skills/pikku-ai-agent/SKILL.md +0 -231
- package/skills/pikku-ai-vercel/SKILL.md +0 -82
- package/skills/pikku-ai-voice/SKILL.md +0 -88
- package/skills/pikku-audit/SKILL.md +0 -175
- package/skills/pikku-aws/SKILL.md +0 -111
- package/skills/pikku-backblaze/SKILL.md +0 -71
- package/skills/pikku-better-auth/SKILL.md +0 -298
- package/skills/pikku-cli/SKILL.md +0 -198
- package/skills/pikku-cli/references/complete-example.md +0 -82
- package/skills/pikku-concepts/SKILL.md +0 -250
- package/skills/pikku-concepts/references/concept-mapping.md +0 -556
- package/skills/pikku-concepts/references/packages.md +0 -29
- package/skills/pikku-config/SKILL.md +0 -212
- package/skills/pikku-cron/SKILL.md +0 -214
- package/skills/pikku-deploy-azure/SKILL.md +0 -71
- package/skills/pikku-deploy-cloudflare/SKILL.md +0 -82
- package/skills/pikku-deploy-express/SKILL.md +0 -86
- package/skills/pikku-deploy-fastify/SKILL.md +0 -73
- package/skills/pikku-deploy-lambda/SKILL.md +0 -110
- package/skills/pikku-deploy-nextjs/SKILL.md +0 -78
- package/skills/pikku-deploy-uws/SKILL.md +0 -88
- package/skills/pikku-deps/SKILL.md +0 -98
- package/skills/pikku-emails/SKILL.md +0 -163
- package/skills/pikku-fabric/SKILL.md +0 -318
- package/skills/pikku-fabric-debug/SKILL.md +0 -112
- package/skills/pikku-feature/SKILL.md +0 -258
- package/skills/pikku-gateway-slack/SKILL.md +0 -115
- package/skills/pikku-http/SKILL.md +0 -220
- package/skills/pikku-http/references/http-options.md +0 -55
- package/skills/pikku-i18n/SKILL.md +0 -137
- package/skills/pikku-info/SKILL.md +0 -100
- package/skills/pikku-jose/SKILL.md +0 -105
- package/skills/pikku-kysely/SKILL.md +0 -219
- package/skills/pikku-machine-auth/SKILL.md +0 -183
- package/skills/pikku-mcp/SKILL.md +0 -241
- package/skills/pikku-middleware/SKILL.md +0 -231
- package/skills/pikku-middleware/references/middleware-patterns.md +0 -61
- package/skills/pikku-mongodb/SKILL.md +0 -105
- package/skills/pikku-n8n-import/SKILL.md +0 -109
- package/skills/pikku-n8n-import/SPEC.md +0 -84
- package/skills/pikku-n8n-import/references/addon-mapping.md +0 -121
- package/skills/pikku-n8n-import/references/code-translation.md +0 -121
- package/skills/pikku-n8n-import/references/loops-and-control.md +0 -87
- package/skills/pikku-paraglide/SKILL.md +0 -117
- package/skills/pikku-permissions/SKILL.md +0 -192
- package/skills/pikku-pino/SKILL.md +0 -79
- package/skills/pikku-product-second-opinion/README.md +0 -43
- package/skills/pikku-product-second-opinion/SKILL.md +0 -158
- package/skills/pikku-product-second-opinion/example/sample-report.md +0 -90
- package/skills/pikku-product-second-opinion/references/report-template.md +0 -73
- package/skills/pikku-queue/SKILL.md +0 -240
- package/skills/pikku-react/SKILL.md +0 -212
- package/skills/pikku-react-query/SKILL.md +0 -242
- package/skills/pikku-realtime/SKILL.md +0 -236
- package/skills/pikku-realtime/references/other-routes.md +0 -23
- package/skills/pikku-redis/SKILL.md +0 -90
- package/skills/pikku-rpc/SKILL.md +0 -171
- package/skills/pikku-rtl/SKILL.md +0 -219
- package/skills/pikku-scenario/SKILL.md +0 -382
- package/skills/pikku-schedule/SKILL.md +0 -57
- package/skills/pikku-schema-ajv/SKILL.md +0 -62
- package/skills/pikku-schema-cfworker/SKILL.md +0 -63
- package/skills/pikku-security/SKILL.md +0 -108
- package/skills/pikku-services/SKILL.md +0 -248
- package/skills/pikku-services/references/audit-wire-service.md +0 -34
- package/skills/pikku-software-archaeology/README.md +0 -70
- package/skills/pikku-software-archaeology/SKILL.md +0 -186
- package/skills/pikku-software-archaeology/references/blueprint.schema.json +0 -625
- package/skills/pikku-software-archaeology/references/pikku-mapping.md +0 -49
- package/skills/pikku-software-archaeology/scripts/validate.mjs +0 -173
- package/skills/pikku-tag-middleware/SKILL.md +0 -13
- package/skills/pikku-template-clone/SKILL.md +0 -40
- package/skills/pikku-trigger/SKILL.md +0 -181
- package/skills/pikku-versioning/SKILL.md +0 -173
- package/skills/pikku-websocket/SKILL.md +0 -243
- package/skills/pikku-workflow/SKILL.md +0 -172
- package/skills/pikku-workflow/references/workflow-reference.md +0 -63
- package/skills/pikku-workflows-client/SKILL.md +0 -150
- 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.
|