@pikku/cli 0.12.85 → 0.12.88
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/console-app/assets/{index-CEv-kj3J.js → index-C3FPwdl7.js} +161 -147
- package/console-app/index.html +1 -1
- 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 +1 -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 +4 -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 +4 -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 +41 -17
- package/dist/.pikku/function/pikku-functions.gen.js +3 -1
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +3 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +3 -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 +2 -1
- package/dist/.pikku/pikku-services.gen.js +1 -0
- 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.d.ts +1 -0
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +6 -0
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.json +6 -0
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +4 -0
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +5 -0
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -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 +3 -1
- package/dist/.pikku/schemas/schemas/FabricLoginInput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuUserAdminFunctionsOutput.schema.json +1 -0
- 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 +8 -2
- 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/bin/pikku.js +22 -0
- package/dist/src/fabric/fabric-commands.d.ts +6 -3
- package/dist/src/fabric/fabric-commands.js +4 -0
- package/dist/src/fabric/functions/deploy.function.js +6 -0
- package/dist/src/fabric/functions/init.function.js +2 -2
- package/dist/src/fabric/functions/llm-key.function.d.ts +4 -4
- package/dist/src/fabric/functions/login.function.d.ts +4 -0
- package/dist/src/fabric/functions/login.function.js +45 -4
- package/dist/src/fabric/functions/validate.function.js +12 -2
- package/dist/src/fabric/lib/config.d.ts +8 -0
- package/dist/src/fabric/lib/config.js +15 -1
- package/dist/src/fabric/lib/git.js +2 -2
- package/dist/src/functions/wirings/ai-agent/pikku-command-public-agent.js +6 -2
- package/dist/src/functions/wirings/ai-agent/serialize-public-agent.d.ts +5 -1
- package/dist/src/functions/wirings/ai-agent/serialize-public-agent.js +81 -48
- package/dist/src/functions/wirings/auth/pikku-command-auth.js +4 -0
- package/dist/src/functions/wirings/console/pikku-command-console-functions.js +6 -2
- package/dist/src/functions/wirings/console/serialize-console-functions.d.ts +5 -1
- package/dist/src/functions/wirings/console/serialize-console-functions.js +55 -23
- package/dist/src/functions/wirings/functions/schemas.js +10 -1
- package/dist/src/functions/wirings/graph/pikku-command-graph-wirings.js +2 -0
- package/dist/src/functions/wirings/queue/pikku-command-queue.js +10 -2
- package/dist/src/functions/wirings/realtime/pikku-command-events-scaffold.js +8 -2
- package/dist/src/functions/wirings/realtime/serialize-events-scaffold.d.ts +5 -4
- package/dist/src/functions/wirings/realtime/serialize-events-scaffold.js +17 -7
- package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.js +6 -2
- package/dist/src/functions/wirings/rpc/pikku-command-remote-rpc.js +6 -2
- package/dist/src/functions/wirings/rpc/serialize-public-rpc.d.ts +5 -1
- package/dist/src/functions/wirings/rpc/serialize-public-rpc.js +16 -5
- package/dist/src/functions/wirings/rpc/serialize-remote-rpc.d.ts +5 -1
- package/dist/src/functions/wirings/rpc/serialize-remote-rpc.js +16 -5
- package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.js +8 -2
- package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.d.ts +5 -1
- package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.js +22 -16
- package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.d.ts +1 -0
- package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.js +49 -0
- package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.d.ts +24 -0
- package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.js +283 -0
- package/dist/src/functions/wirings/webhook/pikku-command-webhook.js +6 -2
- package/dist/src/functions/wirings/webhook/serialize-webhook.d.ts +5 -8
- package/dist/src/functions/wirings/webhook/serialize-webhook.js +19 -12
- package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.js +8 -2
- package/dist/src/functions/wirings/workflow/serialize-workflow-routes.d.ts +5 -1
- package/dist/src/functions/wirings/workflow/serialize-workflow-routes.js +61 -31
- package/dist/src/functions/workflows/all.workflow.js +8 -0
- package/dist/src/utils/pikku-cli-config.js +40 -10
- package/dist/src/utils/remove-legacy-scaffold-file.d.ts +3 -0
- package/dist/src/utils/remove-legacy-scaffold-file.js +43 -0
- package/dist/src/utils/serialize-schemas.js +57 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/skills/pikku-addon/SKILL.md +1 -0
- package/skills/pikku-ai-agent/SKILL.md +2 -1
- package/skills/pikku-ai-vercel/SKILL.md +1 -0
- package/skills/pikku-better-auth/SKILL.md +57 -0
- package/skills/pikku-concepts/SKILL.md +17 -1
- package/skills/pikku-concepts/references/concept-mapping.md +11 -9
- package/skills/pikku-emails/SKILL.md +1 -0
- package/skills/pikku-fabric/SKILL.md +92 -19
- package/skills/pikku-fabric-debug/SKILL.md +112 -0
- package/skills/pikku-permissions/SKILL.md +28 -12
- package/skills/pikku-react/SKILL.md +32 -3
- package/skills/pikku-react-query/SKILL.md +38 -1
- package/skills/pikku-realtime/SKILL.md +1 -1
- package/skills/pikku-security/SKILL.md +3 -3
- package/skills/pikku-template-clone/SKILL.md +1 -0
- package/cli.schema.json +0 -1
|
@@ -55,7 +55,7 @@ Fabric apps use SQLite, accessed via Kysely with the libSQL HTTP adapter. NOT Po
|
|
|
55
55
|
```typescript
|
|
56
56
|
import { Kysely, CamelCasePlugin } from 'kysely'
|
|
57
57
|
import { LibsqlWebDialect } from '@pikku/kysely-sqlite'
|
|
58
|
-
import type { DB } from '
|
|
58
|
+
import type { DB } from '#pikku/db/schema.gen.js'
|
|
59
59
|
|
|
60
60
|
const databaseUrl = await variables.get('DATABASE_URL')
|
|
61
61
|
let kysely: Kysely<DB>
|
|
@@ -75,18 +75,31 @@ Fabric injects `DATABASE_URL` as a variable binding when the stage starts. In lo
|
|
|
75
75
|
|
|
76
76
|
### Migrations
|
|
77
77
|
|
|
78
|
-
Migrations are plain `.sql` files in
|
|
78
|
+
Migrations are plain `.sql` files at the **project root**, in a directory named
|
|
79
|
+
for the engine — `db/sqlite/` for SQLite/libSQL stages, `db/postgres/` for
|
|
80
|
+
Postgres ones. Never `db/migrations/`, and never under `packages/functions/`:
|
|
81
|
+
the deploy pipeline stages `db/<engine>/*.sql` from the root and applies them
|
|
82
|
+
after upload, so a migration anywhere else is silently never run.
|
|
79
83
|
|
|
80
84
|
```
|
|
81
|
-
db/
|
|
85
|
+
db/sqlite/
|
|
82
86
|
0001-init.sql
|
|
83
87
|
0002-add-users.sql
|
|
84
88
|
```
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
Numbers must be consecutive and gap-free, and an applied migration is frozen —
|
|
91
|
+
correct a mistake with a new forward migration, never by editing or renaming one
|
|
92
|
+
that has already run (the recorded hash will no longer match).
|
|
88
93
|
|
|
89
|
-
|
|
94
|
+
Run migrations: `pikku db migrate`. It also regenerates `.pikku/db/schema.gen.ts`
|
|
95
|
+
(Kysely types) and `.pikku/db/zod.gen.ts` — there is no separate types step.
|
|
96
|
+
|
|
97
|
+
**NEVER hand-edit the generated schema** — write a migration and re-run.
|
|
98
|
+
|
|
99
|
+
A Better Auth app has a second constraint: the plugins you enable (`admin()`,
|
|
100
|
+
`actor()`, …) each declare columns, and `pikku db migrate` refuses to run while
|
|
101
|
+
the applied schema is missing any of them. `pikku db generate` writes the
|
|
102
|
+
migration that closes the gap.
|
|
90
103
|
|
|
91
104
|
### Column conventions
|
|
92
105
|
|
|
@@ -135,28 +148,27 @@ packages/functions/
|
|
|
135
148
|
services.ts # pikkuServices factory (singleton)
|
|
136
149
|
middleware.ts # Shared middleware
|
|
137
150
|
permissions.ts # Shared permissions
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
.pikku/ # Generated by pikku all — gitignored
|
|
151
|
+
.pikku/
|
|
152
|
+
db/schema.gen.ts # Kysely types, written by `pikku db migrate` — NEVER hand-edit
|
|
153
|
+
apps/app/ # Frontend(s)
|
|
154
|
+
db/sqlite/ # Plain .sql migrations, numbered, gap-free (project root)
|
|
143
155
|
pikku.config.json # Pikku + deploy config (project root)
|
|
144
|
-
|
|
156
|
+
pikkufabric.config.json # Fabric project link + frontends (project root)
|
|
145
157
|
```
|
|
146
158
|
|
|
147
|
-
## `
|
|
159
|
+
## `pikkufabric.config.json`
|
|
148
160
|
|
|
149
|
-
|
|
161
|
+
Links the repo to a Fabric project and declares its frontends:
|
|
150
162
|
|
|
151
163
|
```json
|
|
152
164
|
{
|
|
153
165
|
"projectId": "my-project-id",
|
|
154
166
|
"production": {
|
|
155
|
-
"
|
|
167
|
+
"domain": "example.com"
|
|
156
168
|
},
|
|
157
169
|
"frontends": {
|
|
158
170
|
"app": {
|
|
159
|
-
"cwd": "apps/
|
|
171
|
+
"cwd": "apps/app",
|
|
160
172
|
"primary": true,
|
|
161
173
|
"deploy": true,
|
|
162
174
|
"kind": "ssr",
|
|
@@ -170,7 +182,11 @@ Declares environments and frontend dev servers:
|
|
|
170
182
|
}
|
|
171
183
|
```
|
|
172
184
|
|
|
173
|
-
- `
|
|
185
|
+
- `projectId`: written by `pikku fabric init` / `link`. Templates ship the
|
|
186
|
+
`__PROJECT_ID__` placeholder — that is *not* a link, and the CLI treats it as
|
|
187
|
+
unlinked.
|
|
188
|
+
- `production.domain`: optional custom domain. Production always maps to `main`;
|
|
189
|
+
without a domain it lives on the platform `*.pikkufabric.app` hostnames.
|
|
174
190
|
- `frontends`: each entry declares a frontend app with its dev command and port
|
|
175
191
|
|
|
176
192
|
## RPC is the default transport
|
|
@@ -198,6 +214,63 @@ Add `wireHTTP` only when you need a specific REST shape (webhooks, third-party c
|
|
|
198
214
|
- Every new or changed function must have a real description.
|
|
199
215
|
- If function metadata would show `missing description`, the work is not finished yet.
|
|
200
216
|
|
|
217
|
+
## Run it locally
|
|
218
|
+
|
|
219
|
+
A Fabric app is two processes: the pikku API server (`:3000`) and the frontend
|
|
220
|
+
(vite). The starter template's `bun run dev` starts **both** and takes the whole
|
|
221
|
+
session down if either dies — a frontend running against a dead API looks like an
|
|
222
|
+
app bug and is the single most common way to waste an hour here.
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
bun run prebuild # pikku all — codegen must be current before the server boots
|
|
226
|
+
bun run dev
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Then open the app, sign up as a real user, and click through what you built.
|
|
230
|
+
**HTTP 200 is not evidence.** These are client-rendered pages: the server returns
|
|
231
|
+
200 with an empty shell, so a page whose component throws still looks fine to
|
|
232
|
+
curl. Either open it in a browser or drive it headlessly and assert on rendered
|
|
233
|
+
text.
|
|
234
|
+
|
|
235
|
+
Secrets come from `process.env`, which the CLI populates from a `.env` in the
|
|
236
|
+
working directory. `BETTER_AUTH_SECRET` is required — without it the first
|
|
237
|
+
sign-up fails with `Requested secret not found`, which names no key and points at
|
|
238
|
+
no file. The starter template generates one on first `bun run dev`.
|
|
239
|
+
|
|
240
|
+
If you are running the two processes yourself rather than through the template's
|
|
241
|
+
script, run `pikku dev` from the **project root** (it resolves `srcDirectories`
|
|
242
|
+
relative to the config, so a nested cwd yields a doubled watch path and no hot
|
|
243
|
+
reload).
|
|
244
|
+
|
|
245
|
+
## Deploy
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
pikku fabric login # opens a browser; needs a human, wait for it
|
|
249
|
+
pikku fabric init https://github.com/<owner>/<repo>
|
|
250
|
+
pikku fabric validate # must pass clean
|
|
251
|
+
pikku fabric deploy plan --production
|
|
252
|
+
pikku fabric deploy apply --production --auto-apply
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`apply` confirms before deploying, and with no TTY to ask — CI, an agent shell —
|
|
256
|
+
it refuses rather than hangs. `--auto-apply` supplies that confirmation; drop it
|
|
257
|
+
only when a human is at a real terminal.
|
|
258
|
+
|
|
259
|
+
`init` adopts a **GitHub** repo, and adoption goes through the Pikku Fabric
|
|
260
|
+
GitHub App — the app has to be installed on the account or org that owns the
|
|
261
|
+
repo, and if it is installed with "selected repositories" this one must be in
|
|
262
|
+
the selection. There is no CLI flag that works around a missing installation:
|
|
263
|
+
`init` returns "Connect the GitHub account '<owner>'". Send the user to install
|
|
264
|
+
it, or create the project in the console instead (which provisions a Fabric-hosted
|
|
265
|
+
git repo you push to) and write the returned `projectId` into
|
|
266
|
+
`pikkufabric.config.json` yourself.
|
|
267
|
+
|
|
268
|
+
Deploy refuses to run unless the target branch equals its upstream — the guard
|
|
269
|
+
compares `main` against `main@{upstream}`. So the remote you pushed to must be
|
|
270
|
+
the one the branch tracks; a stale `origin` left over from scaffolding blocks
|
|
271
|
+
the deploy with "local HEAD … ≠ remote …" even though your code is pushed.
|
|
272
|
+
`git branch --set-upstream-to=<remote>/main main` before deploying.
|
|
273
|
+
|
|
201
274
|
## Versioning
|
|
202
275
|
|
|
203
276
|
Functions with `expose: true` are versioned via `versions.pikku.json`. When you change a function's input or output schema, you must bump its version number — otherwise `pikku all` will report a breaking change and callers' generated clients become stale.
|
|
@@ -221,7 +294,7 @@ These apply in every Fabric app:
|
|
|
221
294
|
- **No `as any`** — fix types properly.
|
|
222
295
|
- **No generic `Error`** — throw `NotFoundError`, `ConflictError`, `BadRequestError`, `UnauthorizedError` from `@pikku/core/errors`.
|
|
223
296
|
- **No auth checks in function bodies** — use `permissions:` field on the function config with a `pikkuPermission` factory.
|
|
224
|
-
- **No hand-editing
|
|
297
|
+
- **No hand-editing `.pikku/db/schema.gen.ts`** — write a migration and re-run `pikku db migrate`.
|
|
225
298
|
- **One runtime unit per file** — never define multiple functions/workflows in a single source file.
|
|
226
299
|
- **Workflow steps don't need manual wiring** — `pikkuSessionlessFunc` step functions in `*.steps.ts` files are auto-discovered by codegen.
|
|
227
300
|
|
|
@@ -235,7 +308,7 @@ pikku fabric validate --json
|
|
|
235
308
|
|
|
236
309
|
Fix every `error` and `warn` in the output before continuing. Then:
|
|
237
310
|
|
|
238
|
-
1. **Replace the database layer**: swap PostgreSQL/MySQL queries for Kysely + libSQL. Convert schema to SQLite-compatible SQL migrations in `db/
|
|
311
|
+
1. **Replace the database layer**: swap PostgreSQL/MySQL queries for Kysely + libSQL. Convert schema to SQLite-compatible SQL migrations in `db/sqlite/`.
|
|
239
312
|
2. **Replace route handlers with pikkuFuncs**: extract business logic into `pikkuFunc`/`pikkuSessionlessFunc`, add `wireHTTP` or `expose: true` for transport.
|
|
240
313
|
3. **Replace DI/IoC with pikkuServices**: move service construction to `createSingletonServices` in `services.ts`.
|
|
241
314
|
4. **Replace `process.env` calls** with `wireVariable`/`wireSecret` + `variables.get()`.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pikku-fabric-debug
|
|
3
|
+
description: 'Debug a deployed Fabric stage from the CLI — read logs, find recent errors, follow a single request end-to-end by traceId, and check request/error/latency metrics. TRIGGER when: a deployed Fabric app is erroring, timing out, or behaving differently than local; the user asks "why is prod failing", "check the logs", "what happened to this request"; or a deploy succeeded but the app misbehaves. DO NOT TRIGGER when: the failure reproduces locally (debug it locally), the deploy itself failed (use pikku-fabric — that is a build/config problem, not a runtime one), or the project is not deployed to Fabric.'
|
|
4
|
+
installGroups: [fabric]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Debugging a deployed Fabric stage
|
|
8
|
+
|
|
9
|
+
## Agent Operating Procedure
|
|
10
|
+
|
|
11
|
+
Use this skill as an execution checklist, not reference material.
|
|
12
|
+
|
|
13
|
+
1. Reproduce locally first. If it fails locally too, debug it there — the
|
|
14
|
+
deployed stage adds cost and latency to every iteration.
|
|
15
|
+
2. Start from `errors`, not `logs`. Errors are already filtered and carry the
|
|
16
|
+
traceId that unlocks the rest.
|
|
17
|
+
3. Follow one trace end-to-end before forming a theory. A single failing request
|
|
18
|
+
tells you more than a hundred unrelated log lines.
|
|
19
|
+
4. Fix the source cause and redeploy. Never leave the diagnosis at "it is flaky".
|
|
20
|
+
5. Confirm the fix against the same stage — recheck `errors` for the function.
|
|
21
|
+
|
|
22
|
+
Every command below requires a logged-in CLI and a linked project. Both fail
|
|
23
|
+
with the exact remediation if not:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Not logged in. Run `pikku fabric login` first.
|
|
27
|
+
No fabric project linked. Run `pikku fabric link` first.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## The loop
|
|
31
|
+
|
|
32
|
+
**1 — What is broken?**
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pikku fabric errors -b main # branch defaults to main
|
|
36
|
+
pikku fabric errors -b main --function createOrder
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Prints a `WHEN | FUNCTION | TRACE | MESSAGE` table. The message is **truncated
|
|
40
|
+
to 100 characters** — treat it as a label, not the full error. The TRACE column
|
|
41
|
+
is the input to the next step.
|
|
42
|
+
|
|
43
|
+
**2 — What happened in that one request?**
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pikku fabric trace <traceId> -b main
|
|
47
|
+
pikku fabric trace <traceId> -b main --json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`--branch` is **required** here (no default). Each event prints as:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
<timestamp> <scriptName> <wireType>:<wireId> <duration>ms — <error|message|outcome>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This is the whole request across the stage — every unit it touched, in order,
|
|
57
|
+
with per-event durations. The last event before the failure is where to look.
|
|
58
|
+
|
|
59
|
+
**3 — Is it one request or the whole stage?**
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pikku fabric metrics -b main # last 24h
|
|
63
|
+
pikku fabric metrics -b main --hours 2 --function createOrder
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Rows are `reqs= err= (rate%) avg= min= max=` per bucket. A single bad request
|
|
67
|
+
with a healthy error rate is a data problem; a climbing error rate is a
|
|
68
|
+
deployment or dependency problem. `--json` additionally returns a `wireTypes`
|
|
69
|
+
breakdown (requests per http/queue/scheduler/…) that the table output omits.
|
|
70
|
+
|
|
71
|
+
**4 — Wider context around the failure**
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pikku fabric logs -b main
|
|
75
|
+
pikku fabric logs -b main --level warn
|
|
76
|
+
pikku fabric logs -b main -f # follow
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`--branch` is **required** — `logs` throws `Specify --branch <branch-name>.`
|
|
80
|
+
without it, even though the flag reads as optional.
|
|
81
|
+
|
|
82
|
+
**5 — Is the running code the code you think it is?**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pikku fabric status # active + in-flight deployment, per stage, with gitSha
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Check this *before* deep-diving. A stage still serving an older `gitSha`, or a
|
|
89
|
+
deploy stuck in flight, explains a whole class of "my fix did nothing".
|
|
90
|
+
|
|
91
|
+
## Known gaps — do not misread these as bugs in your app
|
|
92
|
+
|
|
93
|
+
- **`pikku fabric logs --since` and `--deployment` are accepted and ignored.**
|
|
94
|
+
They are declared as options but the command never reads them, so
|
|
95
|
+
`--since 15m` silently returns the same default window as no flag at all. Do
|
|
96
|
+
not conclude "nothing happened in the last 15 minutes" from it. Narrow by
|
|
97
|
+
`--level`, or by `--function` via `errors`, instead.
|
|
98
|
+
- **`--follow` is a 2-second client-side poll, not a server stream.** It
|
|
99
|
+
dedups against what it already printed, so it behaves like `tail -f`, but new
|
|
100
|
+
entries can appear up to ~2s late and it holds the process open until killed.
|
|
101
|
+
|
|
102
|
+
## What NOT to do
|
|
103
|
+
|
|
104
|
+
- **Do not SSH anywhere or query the telemetry backend directly.** These
|
|
105
|
+
commands are the supported surface; anything lower-level is Fabric-internal
|
|
106
|
+
and will not exist for your project.
|
|
107
|
+
- **Do not debug by redeploying with added `console.log`s.** Get the traceId,
|
|
108
|
+
read the trace. A deploy cycle per hypothesis is the slow path.
|
|
109
|
+
- **Do not read the truncated `errors` message as the full error.** Always
|
|
110
|
+
confirm against `trace` before changing code.
|
|
111
|
+
- **Do not treat an empty `errors` table as "the app is fine"** — a request that
|
|
112
|
+
returns a wrong 200 logs nothing. Check `metrics` for the outcome mix.
|
|
@@ -49,20 +49,36 @@ export const deleteBook = pikkuFunc({
|
|
|
49
49
|
|
|
50
50
|
### `pikkuAuth(fn)` — Session-Only Checks
|
|
51
51
|
|
|
52
|
-
Use for checks that
|
|
52
|
+
Use for checks that read the session but need no request data — and that assert
|
|
53
|
+
something **beyond** merely having a session (a flag, a tier, a claim).
|
|
53
54
|
|
|
54
55
|
```typescript
|
|
55
56
|
import { pikkuAuth } from '#pikku'
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
// Good: a real gate on the session's contents, not just its existence.
|
|
59
|
+
export const isVerified = pikkuAuth(
|
|
60
|
+
async (_services, session) => !!session?.emailVerified
|
|
59
61
|
)
|
|
62
|
+
```
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
**Do NOT write an "is signed in" permission.** A checker that just returns
|
|
65
|
+
`!!session` is not authorization — it re-checks authentication, which the
|
|
66
|
+
function already enforces. A function that needs a signed-in user sets
|
|
67
|
+
`auth: true` (the default for `pikkuFunc`); it does not also carry a
|
|
68
|
+
`permissions: { signedIn }`.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// WRONG — redundant with auth: true; adds a permission that gates nothing.
|
|
72
|
+
export const isSignedIn = pikkuAuth(async (_s, session) => !!session)
|
|
73
|
+
pikkuFunc({ auth: true, permissions: { signedIn: isSignedIn }, /* ... */ })
|
|
74
|
+
|
|
75
|
+
// RIGHT — auth: true already requires the session; permissions are for capability.
|
|
76
|
+
pikkuFunc({ auth: true, /* ... */ })
|
|
64
77
|
```
|
|
65
78
|
|
|
79
|
+
A permission answers "*may this user do this?*" (role, ownership, tier) — never
|
|
80
|
+
"*is there a session?*".
|
|
81
|
+
|
|
66
82
|
### `pikkuPermission(fn)` — Data-Aware Checks
|
|
67
83
|
|
|
68
84
|
Use when authorization depends on the actual request data (e.g., resource ownership).
|
|
@@ -88,11 +104,11 @@ export const hasBookAccess = pikkuPermission(
|
|
|
88
104
|
|
|
89
105
|
```typescript
|
|
90
106
|
permissions: {
|
|
91
|
-
|
|
107
|
+
verified: isVerified, // OR: verified users can access
|
|
92
108
|
owner: isBookOwner, // OR: owners can access
|
|
93
109
|
reviewer: [isAuthenticated, hasBookAccess], // AND: both must pass
|
|
94
110
|
}
|
|
95
|
-
// Logic:
|
|
111
|
+
// Logic: verified OR owner OR (isAuthenticated AND hasBookAccess)
|
|
96
112
|
```
|
|
97
113
|
|
|
98
114
|
Groups are OR'd. Entries within a group array are AND'd.
|
|
@@ -107,7 +123,7 @@ export const deleteBook = pikkuFunc({
|
|
|
107
123
|
await db.deleteBook(bookId)
|
|
108
124
|
},
|
|
109
125
|
permissions: {
|
|
110
|
-
|
|
126
|
+
verified: isVerified,
|
|
111
127
|
owner: isBookOwner,
|
|
112
128
|
},
|
|
113
129
|
})
|
|
@@ -146,8 +162,8 @@ export const isAuthenticated = pikkuAuth(
|
|
|
146
162
|
async (_services, session) => !!session
|
|
147
163
|
)
|
|
148
164
|
|
|
149
|
-
export const
|
|
150
|
-
async (_services, session) => session?.
|
|
165
|
+
export const isVerified = pikkuAuth(
|
|
166
|
+
async (_services, session) => !!session?.emailVerified
|
|
151
167
|
)
|
|
152
168
|
|
|
153
169
|
export const isOrgMember = pikkuPermission(
|
|
@@ -162,7 +178,7 @@ export const deleteOrg = pikkuFunc({
|
|
|
162
178
|
await db.deleteOrg(orgId)
|
|
163
179
|
},
|
|
164
180
|
permissions: {
|
|
165
|
-
|
|
181
|
+
verified: isVerified,
|
|
166
182
|
owner: [isAuthenticated, isOrgMember],
|
|
167
183
|
},
|
|
168
184
|
})
|
|
@@ -36,15 +36,44 @@ import {
|
|
|
36
36
|
Five exports. `usePikkuRealtime` is only valid when you wired a
|
|
37
37
|
`PikkuRealtime` class via `createPikku` — see step 3 below.
|
|
38
38
|
|
|
39
|
+
## Resolving the server URL
|
|
40
|
+
|
|
41
|
+
Every client (`createPikku`, realtime, the auth client) resolves its base
|
|
42
|
+
through one shared helper in `src/lib/env.ts`. Write this once:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// Endpoints come from env, never hardcoded.
|
|
46
|
+
export function apiUrl(): string {
|
|
47
|
+
// SSR: the client hooks only run in the browser, so a placeholder is fine.
|
|
48
|
+
if (import.meta.env.SSR) {
|
|
49
|
+
return import.meta.env.VITE_API_URL ?? '/__api'
|
|
50
|
+
}
|
|
51
|
+
return import.meta.env.VITE_API_URL ?? `${window.location.origin}/api`
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Never fall back to `http://localhost:3000`.** `import.meta.env.VITE_API_URL`
|
|
56
|
+
is substituted by Vite at *build* time, so any deploy that supplies the URL as
|
|
57
|
+
a *runtime* env var or platform binding leaves it `undefined` in the shipped
|
|
58
|
+
bundle — the fallback is then the only branch that ever runs in the browser. A
|
|
59
|
+
localhost fallback means every request from a deployed app goes to the user's
|
|
60
|
+
own machine. `origin + '/api'` is same-origin, needs no build-time knowledge of
|
|
61
|
+
the domain, and is correct wherever the app is served from.
|
|
62
|
+
|
|
63
|
+
For local dev, set `VITE_API_URL`, or proxy `/api` → your backend in
|
|
64
|
+
`vite.config.ts` under `server.proxy`. One `/api` entry also covers
|
|
65
|
+
`/api/auth/*`; only add more entries for root-level routes outside `/api`.
|
|
66
|
+
|
|
39
67
|
## Setup at the app root
|
|
40
68
|
|
|
41
69
|
```tsx
|
|
42
70
|
import { createPikku, PikkuProvider } from '@pikku/react'
|
|
43
71
|
import { PikkuFetch } from './pikku/pikku-fetch.gen'
|
|
44
72
|
import { PikkuRPC } from './pikku/pikku-rpc.gen'
|
|
73
|
+
import { apiUrl } from './lib/env'
|
|
45
74
|
|
|
46
75
|
const pikku = createPikku(PikkuFetch, PikkuRPC, {
|
|
47
|
-
serverUrl:
|
|
76
|
+
serverUrl: apiUrl(),
|
|
48
77
|
})
|
|
49
78
|
|
|
50
79
|
createRoot(document.getElementById('root')!).render(
|
|
@@ -62,7 +91,7 @@ the `PikkuRealtime` class as the third argument and the instance gets a
|
|
|
62
91
|
import { PikkuRealtime } from './pikku/realtime.gen'
|
|
63
92
|
|
|
64
93
|
const pikku = createPikku(PikkuFetch, PikkuRPC, PikkuRealtime, {
|
|
65
|
-
serverUrl:
|
|
94
|
+
serverUrl: apiUrl(),
|
|
66
95
|
})
|
|
67
96
|
// pikku.fetch / pikku.rpc / pikku.realtime — all share the same fetch
|
|
68
97
|
// (server URL + auth configured once).
|
|
@@ -159,7 +188,7 @@ or set headers on the fetch instance after creation. Common pattern:
|
|
|
159
188
|
|
|
160
189
|
```tsx
|
|
161
190
|
const pikku = createPikku(PikkuFetch, PikkuRPC, {
|
|
162
|
-
serverUrl:
|
|
191
|
+
serverUrl: apiUrl(),
|
|
163
192
|
fetchOptions: {
|
|
164
193
|
onRequest: (req) => {
|
|
165
194
|
const token = localStorage.getItem('token')
|
|
@@ -59,9 +59,11 @@ import { PikkuProvider, createPikku } from '@pikku/react'
|
|
|
59
59
|
import { PikkuFetch } from './pikku/pikku-fetch.gen'
|
|
60
60
|
import { PikkuRPC } from './pikku/pikku-rpc.gen'
|
|
61
61
|
|
|
62
|
+
import { apiUrl } from './lib/env'
|
|
63
|
+
|
|
62
64
|
const queryClient = new QueryClient()
|
|
63
65
|
const pikku = createPikku(PikkuFetch, PikkuRPC, {
|
|
64
|
-
serverUrl:
|
|
66
|
+
serverUrl: apiUrl(),
|
|
65
67
|
})
|
|
66
68
|
|
|
67
69
|
<QueryClientProvider client={queryClient}>
|
|
@@ -75,6 +77,41 @@ The two generated files come from `pikku.config.json`'s
|
|
|
75
77
|
`clientFiles.fetchFile` and `clientFiles.rpcWiringsFile`. Hooks live in
|
|
76
78
|
the file at `clientFiles.reactQueryFile` (typically `api.gen.ts`).
|
|
77
79
|
|
|
80
|
+
`apiUrl()` is the shared server-URL helper — see **pikku-react**. Never
|
|
81
|
+
inline `?? 'http://localhost:3000'`: a deploy that supplies the URL as a
|
|
82
|
+
runtime binding leaves `import.meta.env.VITE_API_URL` undefined in the
|
|
83
|
+
bundle, so the fallback is the branch that actually runs.
|
|
84
|
+
|
|
85
|
+
## TanStack Start (SSR)
|
|
86
|
+
|
|
87
|
+
Under Start the provider mounts in `routes/__root.tsx` rather than
|
|
88
|
+
`main.tsx`, and the same module is evaluated on the server. Three things
|
|
89
|
+
differ:
|
|
90
|
+
|
|
91
|
+
1. **`apiUrl()` must have an SSR branch.** `window` is undefined during
|
|
92
|
+
render; return the build-time var or a placeholder (the client hooks
|
|
93
|
+
only fire in the browser).
|
|
94
|
+
2. **Build auth clients lazily.** Better Auth validates its baseURL with
|
|
95
|
+
`new URL(...)` at construction, so a module-scope `createAuthClient`
|
|
96
|
+
crashes SSR on the placeholder. Memoize it behind a getter:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
let _authClient: ReturnType<typeof createAuthClient> | undefined
|
|
100
|
+
export const authClient = () =>
|
|
101
|
+
(_authClient ??= createAuthClient({ baseURL: `${apiUrl()}/auth` }))
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
3. **The auth baseURL needs the `/auth` suffix.** Better Auth only
|
|
105
|
+
appends its default `/api/auth` when the baseURL carries no path.
|
|
106
|
+
`apiUrl()` already ends in `/api`, so a bare `apiUrl()` leaves the
|
|
107
|
+
client calling `/api/get-session` and 404ing.
|
|
108
|
+
|
|
109
|
+
Server functions that need typed RPC access use the generated shim:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pikku tanstack-start # emits the makeApi server-function shim
|
|
113
|
+
```
|
|
114
|
+
|
|
78
115
|
## The hooks
|
|
79
116
|
|
|
80
117
|
All hooks are imported from your generated `api.gen.ts`:
|
|
@@ -160,7 +160,7 @@ const pikku = createPikku(
|
|
|
160
160
|
PikkuFetch,
|
|
161
161
|
PikkuRPC,
|
|
162
162
|
PikkuRealtime, // pass the realtime class as the third arg
|
|
163
|
-
{ serverUrl:
|
|
163
|
+
{ serverUrl: apiUrl() } // shared env helper — see pikku-react
|
|
164
164
|
)
|
|
165
165
|
// pikku.fetch / pikku.rpc / pikku.realtime — all share the same fetch.
|
|
166
166
|
|
|
@@ -36,7 +36,7 @@ const login = pikkuFunc({
|
|
|
36
36
|
auth: false,
|
|
37
37
|
func: async ({ jwt, db }, { email, password }, { setSession }) => {
|
|
38
38
|
const user = await db.verifyCredentials(email, password)
|
|
39
|
-
setSession({ userId: user.id
|
|
39
|
+
setSession({ userId: user.id })
|
|
40
40
|
return { token: jwt.sign({ userId: user.id }) }
|
|
41
41
|
},
|
|
42
42
|
})
|
|
@@ -80,7 +80,7 @@ addHTTPMiddleware('*', [authAPIKey({ source: 'all' })])
|
|
|
80
80
|
import { pikkuAuth, pikkuPermission } from '#pikku'
|
|
81
81
|
|
|
82
82
|
export const isAuthenticated = pikkuAuth(async (_services, session) => !!session)
|
|
83
|
-
export const
|
|
83
|
+
export const isVerified = pikkuAuth(async (_services, session) => !!session?.emailVerified)
|
|
84
84
|
|
|
85
85
|
// wirings/auth.wiring.ts
|
|
86
86
|
import { authCookie } from '@pikku/core/middleware'
|
|
@@ -95,7 +95,7 @@ export const login = pikkuFunc({
|
|
|
95
95
|
auth: false,
|
|
96
96
|
func: async ({ jwt, db }, { email, password }, { setSession }) => {
|
|
97
97
|
const user = await db.verifyCredentials(email, password)
|
|
98
|
-
setSession({ userId: user.id
|
|
98
|
+
setSession({ userId: user.id })
|
|
99
99
|
return { token: jwt.sign({ userId: user.id }) }
|
|
100
100
|
},
|
|
101
101
|
})
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: pikku-template-clone
|
|
3
3
|
description: 'Standard cleanup to run right after a Pikku template is cloned or scaffolded into a new project. TRIGGER when: a Pikku template was just cloned/scaffolded (via `npm create pikku`, `git clone <template>`, or the user says "I cloned the kanban template / starter / template"), or the working tree still looks like an untouched template (template README, placeholder `@project/*` name in package.json). DO NOT TRIGGER when: working in an established project mid-feature, or editing the template repo itself.'
|
|
4
4
|
allowed-tools: Bash(git status *), Bash(git add *), Bash(git commit *), Bash(git rm *), Bash(git mv *), Bash(git log *)
|
|
5
|
+
installGroups: [core]
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
# Pikku Template Post-Clone Cleanup
|
package/cli.schema.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","additionalProperties":false,"properties":{"configFile":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"wires":{"type":"array","items":{"type":"string"}},"excludeWires":{"type":"array","items":{"type":"string"}},"userSessionType":{"type":"string"},"singletonServicesFactoryType":{"type":"string"},"wireServicesFactoryType":{"type":"string"},"outDir":{"type":"string"},"schemaDirectory":{"type":"string"},"typesDeclarationFile":{"type":"string"},"functionsFile":{"type":"string"},"functionsMetaFile":{"type":"string"},"functionsMetaJsonFile":{"type":"string"},"functionTypesFile":{"type":"string"},"httpWiringsFile":{"type":"string"},"httpWiringMetaFile":{"type":"string"},"httpWiringMetaJsonFile":{"type":"string"},"httpContractsMetaJsonFile":{"type":"string"},"httpContractsMetaFile":{"type":"string"},"httpMapDeclarationFile":{"type":"string"},"httpTypesFile":{"type":"string"},"gatewaysWiringFile":{"type":"string"},"gatewaysWiringMetaFile":{"type":"string"},"gatewaysWiringMetaJsonFile":{"type":"string"},"channelsWiringFile":{"type":"string"},"channelsWiringMetaFile":{"type":"string"},"channelsWiringMetaJsonFile":{"type":"string"},"channelContractsMetaJsonFile":{"type":"string"},"channelContractsMetaFile":{"type":"string"},"channelsMapDeclarationFile":{"type":"string"},"channelsTypesFile":{"type":"string"},"rpcInternalWiringMetaFile":{"type":"string"},"rpcInternalWiringMetaJsonFile":{"type":"string"},"rpcInternalMapDeclarationFile":{"type":"string"},"rpcMapDeclarationFile":{"type":"string"},"rpcRemoteMapDeclarationFile":{"type":"string"},"remoteRpcWorkersFile":{"type":"string"},"graphWiringsFile":{"type":"string"},"webhookWorkersFile":{"type":"string"},"publicRpcFile":{"type":"string"},"publicAgentFile":{"type":"string"},"consoleFunctionsFile":{"type":"string"},"scenariosFunctionsFile":{"type":"string"},"workflowRoutesFile":{"type":"string"},"eventsChannelFile":{"type":"string"},"triggersTypesFile":{"type":"string"},"triggersWiringFile":{"type":"string"},"triggersWiringMetaFile":{"type":"string"},"triggersWiringMetaJsonFile":{"type":"string"},"triggerSourcesMetaFile":{"type":"string"},"triggerSourcesMetaJsonFile":{"type":"string"},"schedulersWiringFile":{"type":"string"},"schedulersWiringMetaFile":{"type":"string"},"schedulersWiringMetaJsonFile":{"type":"string"},"schedulersTypesFile":{"type":"string"},"queueWorkersWiringFile":{"type":"string"},"queueWorkersWiringMetaFile":{"type":"string"},"queueWorkersWiringMetaJsonFile":{"type":"string"},"queueMapDeclarationFile":{"type":"string"},"queueTypesFile":{"type":"string"},"workflowsWiringFile":{"type":"string"},"workflowsWiringMetaFile":{"type":"string"},"workflowsWorkersFile":{"type":"string"},"workflowMapDeclarationFile":{"type":"string"},"workflowTypesFile":{"type":"string"},"workflowMetaDir":{"type":"string"},"scenarioActorsFile":{"type":"string"},"mcpWiringsFile":{"type":"string"},"mcpWiringsMetaFile":{"type":"string"},"mcpWiringsMetaJsonFile":{"type":"string"},"mcpTypesFile":{"type":"string"},"mcpJsonFile":{"type":"string"},"agentWiringsFile":{"type":"string"},"agentWiringMetaFile":{"type":"string"},"agentWiringMetaJsonFile":{"type":"string"},"agentTypesFile":{"type":"string"},"agentMapDeclarationFile":{"type":"string"},"cliWiringsFile":{"type":"string"},"cliWiringMetaFile":{"type":"string"},"cliWiringMetaJsonFile":{"type":"string"},"cliContractsMetaJsonFile":{"type":"string"},"cliContractsMetaFile":{"type":"string"},"cliBootstrapFile":{"type":"string"},"cliTypesFile":{"type":"string"},"servicesFile":{"type":"string"},"middlewareFile":{"type":"string"},"middlewareGroupsMetaJsonFile":{"type":"string"},"permissionsFile":{"type":"string"},"permissionsGroupsMetaJsonFile":{"type":"string"},"bootstrapFile":{"type":"string"},"packageFile":{"type":"string"},"addonTypesFile":{"type":"string"},"nodeTypesFile":{"type":"string"},"secretTypesFile":{"type":"string"},"secretsFile":{"type":"string"},"secretsMetaJsonFile":{"type":"string"},"credentialsFile":{"type":"string"},"credentialsMetaJsonFile":{"type":"string"},"scopeTypesFile":{"type":"string"},"scopesFile":{"type":"string"},"scopesMetaJsonFile":{"type":"string"},"variableTypesFile":{"type":"string"},"variablesFile":{"type":"string"},"variablesMetaJsonFile":{"type":"string"},"$schema":{"type":"string"},"extends":{"type":"string"},"rootDir":{"type":"string"},"runtimeDir":{"type":"string","description":"Runtime artifacts directory (dev.db, content, tmp). Resolved relative to rootDir. Defaults to <rootDir>/.pikku-runtime."},"srcDirectories":{"type":"array","items":{"type":"string"}},"ignoreFiles":{"type":"array","items":{"type":"string"}},"packageMappings":{"type":"object","additionalProperties":{"type":"string"}},"addon":{"anyOf":[{"type":"boolean"},{"type":"object","properties":{"categories":{"type":"array","items":{"type":"string"}},"icon":{"type":"string"},"displayName":{"type":"string"},"description":{"type":"string"},"serverlessIncompatible":{"type":"array","items":{"type":"string"}},"openapi":{"type":"object","properties":{"version":{"type":"string"},"hash":{"type":"string"}},"required":["version","hash"],"additionalProperties":false}},"additionalProperties":false}]},"addonName":{"type":"string"},"configDir":{"type":"string"},"tsconfig":{"type":"string"},"clientFiles":{"type":"object","properties":{"fetchFile":{"type":"string"},"websocketFile":{"type":"string"},"rpcWiringsFile":{"type":"string"},"reactQueryFile":{"type":"string"},"realtimeFile":{"type":"string"},"realtimeEventHubTopicsImport":{"type":"string","description":"Optional import for the EventHubTopics type so the realtime client is fully typed. Format: `<path>#<TypeName>` resolved relative to `realtimeFile`. Example: `../types/eventhub-topics.js#EventHubTopics`. If unset, the generated client treats topics as `Record<string, unknown>`."},"queueWiringsFile":{"type":"string"},"mcpJsonFile":{"type":"string"},"nextBackendFile":{"type":"string"},"nextHTTPFile":{"type":"string"},"nextBackendTransport":{"type":"string","enum":["local","worker-rpc","http"],"description":"Transport used by the generated nextBackendFile wrapper.\n- `'local'` (default): function code is loaded in-process via bootstrap + createSingletonServices. Required for Node/dev runs.\n- `'worker-rpc'`: SSR dispatches every call through an injected `Fetcher` ({ fetch(req): Promise<Response> }). Function code is NOT bundled into the SSR worker. Pair with `nextBackendFetcherImport` to point at your resolver module.\n- `'http'`: SSR dispatches every call through the generated `PikkuFetch` client. Use this when your Next app should call a separately running local/server API instead of importing function code in-process."},"nextBackendFetcherImport":{"type":"string","description":"Module that exports a `fetcher: Fetcher` (or default export) used by the worker-RPC variant of the next backend wrapper. Resolved relative to `nextBackendFile`. Required when `nextBackendTransport === 'worker-rpc'`."},"startServerFnsFile":{"type":"string","description":"Emit a TanStack Start server-function shim into this file. The shim exports `makeApi(): PikkuRPC` — a typed caller over the generated RPC map for use in Start loaders, actions and components. It reads the API base URL from `import.meta.env.VITE_API_URL` (throws if unset). Requires `rpcWiringsFile` (where the `PikkuRPC` class is generated)."}},"additionalProperties":false},"emailTemplatesDir":{"type":"string","description":"Directory containing email templates, locales, partials, and theme.json."},"authFile":{"type":"string","description":"Path to write the generated Better Auth wiring file (auth.gen.ts). Must be within srcDirectories so wireSecret calls are picked up by the inspector. Example: \"src/auth.gen.ts\""},"authTypesFile":{"type":"string","description":"Path to write the generated typed `pikkuBetterAuth` re-export (auth.types.ts). Defaults to `{outDir}/auth/auth.types.ts`. Re-exported from `#pikku` so user code can `import { pikkuBetterAuth } from '#pikku'` with project-typed services."},"authMetaJsonFile":{"type":"string","description":"Path to write the generated Better Auth metadata (auth-meta.gen.json) — the enabled social providers and plugins the console SSO page reads via getAuthProviders. Defaults to `{outDir}/auth/pikku-auth-meta.gen.json`."},"openAPI":{"type":"object","properties":{"outputFile":{"type":"string"},"additionalInfo":{"$ref":"#/definitions/OpenAPISpecInfo"}},"required":["outputFile","additionalInfo"],"additionalProperties":false},"schema":{"type":"object","properties":{"additionalProperties":{"type":"boolean"},"supportsImportAttributes":{"type":"boolean"}},"additionalProperties":false},"db":{"type":"object","properties":{"engine":{"type":"string","enum":["sqlite","postgres"]},"pgVersion":{"type":"number"}},"additionalProperties":false},"cli":{"type":"object","properties":{"entrypoints":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"type":{"type":"string","const":"local"},"path":{"type":"string"}},"required":["type","path"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"channel"},"name":{"type":"string"},"route":{"type":"string"},"wirePath":{"type":"string"},"path":{"type":"string"}},"required":["type","wirePath"],"additionalProperties":false},{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"object","properties":{"type":{"type":"string","const":"local"},"path":{"type":"string"}},"required":["type","path"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"channel"},"name":{"type":"string"},"route":{"type":"string"},"wirePath":{"type":"string"},"path":{"type":"string"}},"required":["type","wirePath"],"additionalProperties":false}]}}]}}},"additionalProperties":false},"workflows":{"type":"object","properties":{"orchestratorQueue":{"type":"string"},"workerQueue":{"type":"string"}},"additionalProperties":false},"scenarios":{"type":"object","properties":{"actors":{"type":"object","additionalProperties":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string"},"jobTitle":{"type":"string"},"personality":{"type":"string"}},"required":["email"],"additionalProperties":false},"description":"Global scenario actor registry — any scenario can impersonate any actor"},"environments":{"type":"object","additionalProperties":{"type":"object","properties":{"apiUrl":{"type":"string"},"signInPath":{"type":"string","description":"Actor sign-in path under apiUrl. Default: /auth/sign-in/actor"},"rpcPath":{"type":"string","description":"Exposed-RPC prefix under apiUrl. Default: /rpc"}},"required":["apiUrl"],"additionalProperties":false},"description":"Environments `pikku scenario run <environment>` can target; the actor secret comes from SCENARIO_ACTOR_SECRET, never config"}},"additionalProperties":false},"scaffold":{"type":"object","properties":{"addonDir":{"type":"string"},"functionDir":{"type":"string"},"wiringDir":{"type":"string"},"middlewareDir":{"type":"string"},"permissionDir":{"type":"string"},"pikkuDir":{"type":"string"},"graph":{"type":"boolean","description":"Wire the pikku addon-graph package so pikkuWorkflowGraph can reference its native transforms like graph:editFields."},"rpc":{"$ref":"#/definitions/PikkuScaffoldFeature"},"console":{"$ref":"#/definitions/PikkuScaffoldFeature"},"scenarios":{"$ref":"#/definitions/PikkuScaffoldFeature"},"agent":{"$ref":"#/definitions/PikkuScaffoldFeature"},"workflow":{"$ref":"#/definitions/PikkuScaffoldFeature"},"events":{"$ref":"#/definitions/PikkuScaffoldFeature"},"remoteRpc":{"$ref":"#/definitions/PikkuScaffoldFeature"},"webhook":{"type":"boolean","description":"The outgoing webhook delivery worker exposes no endpoint of its own, so it has no auth dimension — it is on or off."}},"additionalProperties":false},"addons":{"type":"object","properties":{"addonDir":{"type":"string"}},"additionalProperties":false,"description":"Community-registry addons installed via `pikku fabric addon add`. The source is copied into the project shadcn-style; each lands in `<addonDir>/<name>/` and the dir is registered as a yarn workspace so `wireAddon({ package })` resolves it by name. `addonDir` defaults to `addons` (top-level, outside the app's TS scan). Install provenance is tracked in pikku-addons.json."},"tests":{"type":"object","properties":{"outputDir":{"type":"string"}},"additionalProperties":false},"forceRequiredServices":{"type":"array","items":{"type":"string"}},"schemasFromTypes":{"type":"array","items":{"type":"string"}},"stateOutput":{"type":"string"},"stateInput":{"type":"string"},"verboseMeta":{"type":"boolean"},"security":{"type":"boolean","description":"Run the data-classification security lint (scans function return types for Private/Pii/Secret leaks). Off by default — it forces expensive return-type inference on every function and is not part of codegen. Enable here to always run it, or per-invocation via `pikku all --security`. Pair with `failOnError` to gate a build/CI on leaks."},"tsc":{"type":"boolean","description":"After codegen, run a real `tsc --noEmit` over the project's tsconfig and fail on type errors. `tsc` prints full diagnostics with code frames; `tscSummary` prints a compact one-line-per-error render (no code frames, capped) that's cheap for AI agents / CI logs. Off by default; enable per invocation via `pikku all --tsc` / `--tsc-summary`."},"tscSummary":{"type":"boolean"},"diff":{"type":"boolean","description":"After a successful codegen run, emit a structural diff of the generated `.pikku` meta (functions/wirings/workflows/emails added/removed/changed vs the state before this run) as a `PIKKU_DIFF <json>` line on stdout. Only emitted on exit 0. Off by default; enable per invocation via `pikku all --diff`. Consumed by the sandbox \"what changed\" build card."},"lint":{"type":"object","properties":{"servicesNotDestructured":{"type":"string","enum":["off","warn","error"]},"wiresNotDestructured":{"type":"string","enum":["off","warn","error"]},"functionDynamicImport":{"type":"string","enum":["off","warn","error"]}},"additionalProperties":false},"addonMetaJsonFile":{"type":"string"},"globalHTTPPrefix":{"type":"string"},"binary":{"type":"object","properties":{"entrypoint":{"type":"string"},"output":{"type":"string"},"targets":{"type":"array","items":{"type":"string"}}},"required":["entrypoint","output"],"additionalProperties":false},"deploy":{"type":"object","properties":{"providers":{"type":"object","additionalProperties":{"type":"string"}},"defaultProvider":{"type":"string"},"serverlessIncompatible":{"type":"array","items":{"type":"string"}},"defaultTarget":{"type":"string","enum":["serverless","server"],"description":"Default deploy target for functions that don't declare an explicit `deploy` flag and don't use a serverless-incompatible service. Defaults to 'serverless'."}},"required":["providers"],"additionalProperties":false},"namedFilters":{"type":"object","additionalProperties":{"$ref":"#/definitions/InspectorFilters"},"description":"Named filter presets keyed by name, used via CLI --filter <name>."},"filters":{"$ref":"#/definitions/InspectorFilters"}},"required":["addonTypesFile","agentMapDeclarationFile","agentTypesFile","agentWiringMetaFile","agentWiringMetaJsonFile","agentWiringsFile","bootstrapFile","channelContractsMetaFile","channelContractsMetaJsonFile","channelsMapDeclarationFile","channelsTypesFile","channelsWiringFile","channelsWiringMetaFile","channelsWiringMetaJsonFile","cliBootstrapFile","cliContractsMetaFile","cliContractsMetaJsonFile","cliTypesFile","cliWiringMetaFile","cliWiringMetaJsonFile","cliWiringsFile","configDir","consoleFunctionsFile","credentialsFile","credentialsMetaJsonFile","eventsChannelFile","filters","functionTypesFile","functionsFile","functionsMetaFile","functionsMetaJsonFile","gatewaysWiringFile","gatewaysWiringMetaFile","gatewaysWiringMetaJsonFile","httpContractsMetaFile","httpContractsMetaJsonFile","httpMapDeclarationFile","httpTypesFile","httpWiringMetaFile","httpWiringMetaJsonFile","httpWiringsFile","mcpJsonFile","mcpTypesFile","mcpWiringsFile","mcpWiringsMetaFile","mcpWiringsMetaJsonFile","middlewareFile","middlewareGroupsMetaJsonFile","nodeTypesFile","outDir","packageFile","packageMappings","permissionsFile","permissionsGroupsMetaJsonFile","publicAgentFile","publicRpcFile","queueMapDeclarationFile","queueTypesFile","queueWorkersWiringFile","queueWorkersWiringMetaFile","queueWorkersWiringMetaJsonFile","rootDir","rpcInternalMapDeclarationFile","rpcInternalWiringMetaFile","rpcInternalWiringMetaJsonFile","rpcMapDeclarationFile","rpcRemoteMapDeclarationFile","scenarioActorsFile","scenariosFunctionsFile","schedulersTypesFile","schedulersWiringFile","schedulersWiringMetaFile","schedulersWiringMetaJsonFile","schemaDirectory","scopeTypesFile","scopesFile","scopesMetaJsonFile","secretTypesFile","secretsFile","secretsMetaJsonFile","servicesFile","srcDirectories","triggerSourcesMetaFile","triggerSourcesMetaJsonFile","triggersTypesFile","triggersWiringFile","triggersWiringMetaFile","triggersWiringMetaJsonFile","tsconfig","typesDeclarationFile","variableTypesFile","variablesFile","variablesMetaJsonFile","workflowMapDeclarationFile","workflowMetaDir","workflowRoutesFile","workflowTypesFile","workflowsWiringFile","workflowsWiringMetaFile","workflowsWorkersFile"],"definitions":{"OpenAPISpecInfo":{"type":"object","properties":{"info":{"type":"object","properties":{"title":{"type":"string"},"version":{"type":"string"},"description":{"type":"string"},"termsOfService":{"type":"string"},"contact":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"},"email":{"type":"string"}},"additionalProperties":false},"license":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"}},"required":["name"],"additionalProperties":false}},"required":["title","version","description"],"additionalProperties":false},"servers":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"description":{"type":"string"}},"required":["url"],"additionalProperties":false}},"tags":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}},"required":["name"],"additionalProperties":false}},"externalDocs":{"type":"object","properties":{"description":{"type":"string"},"url":{"type":"string"}},"required":["url"],"additionalProperties":false},"securitySchemes":{"type":"object"},"security":{"type":"array","items":{"type":"object","additionalProperties":{"type":"array","items":{}}}}},"required":["info","servers"],"additionalProperties":false},"PikkuScaffoldFeature":{"type":["string","boolean"],"enum":["auth","no-auth",false]},"InspectorFilters":{"type":"object","properties":{"names":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"}},"wires":{"type":"array","items":{"type":"string"}},"directories":{"type":"array","items":{"type":"string"}},"httpRoutes":{"type":"array","items":{"type":"string"}},"httpMethods":{"type":"array","items":{"type":"string"}},"excludeNames":{"type":"array","items":{"type":"string"}},"excludeTags":{"type":"array","items":{"type":"string"}},"excludeWires":{"type":"array","items":{"type":"string"}},"excludeDirectories":{"type":"array","items":{"type":"string"}},"excludeHttpRoutes":{"type":"array","items":{"type":"string"}},"excludeHttpMethods":{"type":"array","items":{"type":"string"}},"target":{"type":"array","items":{"type":"string","enum":["serverless","server"]}},"excludeTarget":{"type":"array","items":{"type":"string","enum":["serverless","server"]}},"serverlessIncompatible":{"type":"array","items":{"type":"string"}},"defaultTarget":{"type":"string","enum":["serverless","server"]}},"additionalProperties":false}}}
|