@pikku/skills 0.12.21 → 0.12.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +125 -0
- package/dist/skills.gen.js +1 -1
- package/package.json +1 -1
- package/skills/pikku-addon/SKILL.md +10 -9
- package/skills/pikku-agent/SKILL.md +67 -316
- package/skills/pikku-agent/references/agents.md +299 -0
- package/skills/{pikku-ai-vercel/SKILL.md → pikku-agent/references/runner-vercel.md} +2 -24
- package/skills/{pikku-ai-voice/SKILL.md → pikku-agent/references/voice.md} +1 -22
- package/skills/pikku-architect/SKILL.md +264 -0
- package/skills/pikku-auth/SKILL.md +89 -0
- package/skills/{pikku-better-auth/SKILL.md → pikku-auth/references/better-auth.md} +42 -47
- package/skills/{pikku-jose/SKILL.md → pikku-auth/references/jose.md} +1 -23
- package/skills/{pikku-machine-auth/SKILL.md → pikku-auth/references/machine-auth.md} +0 -23
- package/skills/{pikku-permissions/SKILL.md → pikku-auth/references/permissions.md} +5 -24
- package/skills/{pikku-security/SKILL.md → pikku-auth/references/sessions.md} +3 -23
- package/skills/pikku-build/SKILL.md +87 -0
- package/skills/{pikku-build-app/SKILL.md → pikku-build/references/app.md} +77 -25
- package/skills/{pikku-feature/SKILL.md → pikku-build/references/feature.md} +1 -8
- package/skills/{pikku-build-app → pikku-build}/references/multi-app.md +1 -1
- package/skills/{pikku-build-platform/SKILL.md → pikku-build/references/platform.md} +22 -37
- package/skills/{pikku-template-clone/SKILL.md → pikku-build/references/post-clone.md} +0 -6
- package/skills/{pikku-build-quick/SKILL.md → pikku-build/references/quick.md} +6 -22
- package/skills/{pikku-build-app → pikku-build}/references/ship.md +7 -1
- package/skills/pikku-concepts/SKILL.md +75 -8
- package/skills/pikku-concepts/references/concept-mapping.md +8 -8
- package/skills/pikku-deploy/SKILL.md +158 -0
- package/skills/{pikku-deploy-azure/SKILL.md → pikku-deploy/references/azure.md} +18 -50
- package/skills/pikku-deploy/references/cloudflare.md +104 -0
- package/skills/pikku-deploy/references/express.md +92 -0
- package/skills/{pikku-deploy-fastify/SKILL.md → pikku-deploy/references/fastify.md} +8 -32
- package/skills/{pikku-deploy-lambda/SKILL.md → pikku-deploy/references/lambda.md} +6 -27
- package/skills/{pikku-deploy-nextjs/SKILL.md → pikku-deploy/references/nextjs.md} +9 -33
- package/skills/pikku-deploy/references/uws.md +72 -0
- package/skills/pikku-deploy/references/ws.md +75 -0
- package/skills/pikku-emails/SKILL.md +3 -2
- package/skills/pikku-fabric/SKILL.md +20 -10
- package/skills/{pikku-fabric-debug/SKILL.md → pikku-fabric/references/debugging.md} +0 -6
- package/skills/pikku-i18n/SKILL.md +60 -207
- package/skills/{pikku-paraglide/SKILL.md → pikku-i18n/references/enum-labels.md} +0 -6
- package/skills/pikku-i18n/references/messages.md +218 -0
- package/skills/{pikku-rtl/SKILL.md → pikku-i18n/references/rtl.md} +3 -9
- package/skills/pikku-knowledge/SKILL.md +14 -0
- package/skills/pikku-kysely/SKILL.md +13 -13
- package/skills/pikku-meta/SKILL.md +58 -130
- package/skills/{pikku-deps/SKILL.md → pikku-meta/references/audit.md} +1 -17
- package/skills/pikku-meta/references/meta.md +114 -0
- package/skills/{pikku-versioning/SKILL.md → pikku-meta/references/versioning.md} +0 -26
- package/skills/pikku-middleware/SKILL.md +8 -8
- package/skills/pikku-n8n-import/SKILL.md +0 -1
- package/skills/pikku-react/SKILL.md +50 -298
- package/skills/pikku-react/references/client.md +293 -0
- package/skills/{pikku-react-query/SKILL.md → pikku-react/references/react-query.md} +2 -22
- package/skills/{pikku-workflows-client/SKILL.md → pikku-react/references/workflows.md} +1 -22
- package/skills/pikku-scenario/SKILL.md +64 -49
- package/skills/pikku-scenario/references/persona-run.md +148 -0
- package/skills/pikku-service-backends/SKILL.md +154 -0
- package/skills/pikku-service-backends/references/aws.md +106 -0
- package/skills/pikku-service-backends/references/backblaze.md +57 -0
- package/skills/pikku-service-backends/references/mongodb.md +90 -0
- package/skills/pikku-service-backends/references/redis.md +75 -0
- package/skills/pikku-service-backends/references/schema.md +63 -0
- package/skills/pikku-services/SKILL.md +68 -291
- package/skills/{pikku-audit/SKILL.md → pikku-services/references/audit.md} +0 -22
- package/skills/{pikku-config/SKILL.md → pikku-services/references/config.md} +1 -25
- package/skills/{pikku-pino/SKILL.md → pikku-services/references/pino.md} +0 -20
- package/skills/pikku-services/references/services.md +272 -0
- package/skills/pikku-software-archaeology/README.md +5 -1
- package/skills/pikku-software-archaeology/SKILL.md +15 -2
- package/skills/{pikku-product-second-opinion/example/sample-report.md → pikku-software-archaeology/example/second-opinion-sample-report.md} +1 -1
- package/skills/pikku-software-archaeology/references/blueprint.schema.json +1 -1
- package/skills/pikku-software-archaeology/references/pikku-mapping.md +3 -3
- package/skills/{pikku-product-second-opinion/SKILL.md → pikku-software-archaeology/references/second-opinion.md} +4 -9
- package/skills/pikku-webhook/SKILL.md +199 -0
- package/skills/pikku-wiring/SKILL.md +180 -0
- package/skills/{pikku-websocket/SKILL.md → pikku-wiring/references/channel.md} +2 -35
- package/skills/{pikku-cli/SKILL.md → pikku-wiring/references/cli.md} +1 -33
- package/skills/{pikku-gateway-slack/SKILL.md → pikku-wiring/references/gateway-slack.md} +0 -23
- package/skills/{pikku-http/SKILL.md → pikku-wiring/references/http.md} +4 -40
- package/skills/{pikku-mcp/SKILL.md → pikku-wiring/references/mcp.md} +0 -33
- package/skills/{pikku-queue/SKILL.md → pikku-wiring/references/queue.md} +1 -33
- package/skills/{pikku-realtime/SKILL.md → pikku-wiring/references/realtime.md} +2 -25
- package/skills/{pikku-rpc/SKILL.md → pikku-wiring/references/rpc.md} +0 -32
- package/skills/{pikku-schedule/SKILL.md → pikku-wiring/references/scheduler.md} +1 -35
- package/skills/{pikku-trigger/SKILL.md → pikku-wiring/references/trigger.md} +0 -43
- package/skills/pikku-workflow/SKILL.md +3 -3
- package/skills/pikku-aws/SKILL.md +0 -161
- package/skills/pikku-backblaze/SKILL.md +0 -104
- package/skills/pikku-deploy-cloudflare/SKILL.md +0 -123
- package/skills/pikku-deploy-express/SKILL.md +0 -122
- package/skills/pikku-deploy-uws/SKILL.md +0 -144
- package/skills/pikku-mongodb/SKILL.md +0 -113
- package/skills/pikku-product-second-opinion/README.md +0 -43
- package/skills/pikku-redis/SKILL.md +0 -99
- package/skills/pikku-schema-ajv/SKILL.md +0 -83
- package/skills/pikku-schema-cfworker/SKILL.md +0 -82
- package/skills/pikku-ws/SKILL.md +0 -87
- /package/skills/{pikku-build-app → pikku-build}/references/theming.md +0 -0
- /package/skills/{pikku-product-second-opinion/references/report-template.md → pikku-software-archaeology/references/second-opinion-report-template.md} +0 -0
- /package/skills/{pikku-cli/references/complete-example.md → pikku-wiring/references/cli-complete-example.md} +0 -0
- /package/skills/{pikku-http → pikku-wiring}/references/http-options.md +0 -0
- /package/skills/{pikku-realtime/references/other-routes.md → pikku-wiring/references/realtime-other-routes.md} +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Pikku React
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## What ships
|
|
5
|
+
|
|
6
|
+
```tsx
|
|
7
|
+
import {
|
|
8
|
+
PikkuProvider,
|
|
9
|
+
createPikku,
|
|
10
|
+
usePikkuFetch,
|
|
11
|
+
usePikkuRPC,
|
|
12
|
+
usePikkuRealtime,
|
|
13
|
+
usePikkuAgent,
|
|
14
|
+
usePikkuWorkflow,
|
|
15
|
+
asI18n,
|
|
16
|
+
} from '@pikku/react'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`usePikkuRealtime` is only valid when you wired a `PikkuRealtime` class via
|
|
20
|
+
`createPikku` — see the setup section. `usePikkuAgent` and `usePikkuWorkflow`
|
|
21
|
+
are thin bindings over the RPC client that pin one agent/workflow name, so a
|
|
22
|
+
component never repeats it. `asI18n` is the i18n brand (see **pikku-i18n**).
|
|
23
|
+
|
|
24
|
+
## Resolving the server URL
|
|
25
|
+
|
|
26
|
+
Every client (`createPikku`, realtime, the auth client) resolves its base
|
|
27
|
+
through one shared helper in `src/lib/env.ts`. Write this once:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// Endpoints come from env, never hardcoded.
|
|
31
|
+
export function apiUrl(): string {
|
|
32
|
+
// SSR: the client hooks only run in the browser, so a placeholder is fine.
|
|
33
|
+
if (import.meta.env.SSR) {
|
|
34
|
+
return import.meta.env.VITE_API_URL ?? '/__api'
|
|
35
|
+
}
|
|
36
|
+
return import.meta.env.VITE_API_URL ?? `${window.location.origin}/api`
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Never fall back to `http://localhost:3000`.** `import.meta.env.VITE_API_URL`
|
|
41
|
+
is substituted by Vite at _build_ time, so any deploy that supplies the URL as
|
|
42
|
+
a _runtime_ env var or platform binding leaves it `undefined` in the shipped
|
|
43
|
+
bundle — the fallback is then the only branch that ever runs in the browser. A
|
|
44
|
+
localhost fallback means every request from a deployed app goes to the user's
|
|
45
|
+
own machine. `origin + '/api'` is same-origin, needs no build-time knowledge of
|
|
46
|
+
the domain, and is correct wherever the app is served from.
|
|
47
|
+
|
|
48
|
+
For local dev, set `VITE_API_URL`, or proxy `/api` → your backend in
|
|
49
|
+
`vite.config.ts` under `server.proxy`. One `/api` entry also covers
|
|
50
|
+
`/api/auth/*`; only add more entries for root-level routes outside `/api`.
|
|
51
|
+
|
|
52
|
+
## Setup at the app root
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import { createPikku, PikkuProvider } from '@pikku/react'
|
|
56
|
+
import { PikkuFetch } from './pikku/pikku-fetch.gen'
|
|
57
|
+
import { PikkuRPC } from './pikku/pikku-rpc.gen'
|
|
58
|
+
import { apiUrl } from './lib/env'
|
|
59
|
+
|
|
60
|
+
const pikku = createPikku(PikkuFetch, PikkuRPC, {
|
|
61
|
+
serverUrl: apiUrl(),
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
createRoot(document.getElementById('root')!).render(
|
|
65
|
+
<PikkuProvider pikku={pikku}>
|
|
66
|
+
<App />
|
|
67
|
+
</PikkuProvider>
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If the project also exposes realtime events (see **pikku-wiring**), pass
|
|
72
|
+
the `PikkuRealtime` class as the third argument and the instance gets a
|
|
73
|
+
`realtime` field too:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { PikkuRealtime } from './pikku/realtime.gen'
|
|
77
|
+
|
|
78
|
+
const pikku = createPikku(PikkuFetch, PikkuRPC, PikkuRealtime, {
|
|
79
|
+
serverUrl: apiUrl(),
|
|
80
|
+
})
|
|
81
|
+
// pikku.fetch / pikku.rpc / pikku.realtime — all share the same fetch
|
|
82
|
+
// (server URL + auth configured once).
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The generated classes come from your `pikku.config.json`:
|
|
86
|
+
|
|
87
|
+
| config field | generated file |
|
|
88
|
+
| ---------------------------- | ----------------------------------------------------- |
|
|
89
|
+
| `clientFiles.fetchFile` | typed HTTP client (`PikkuFetch` class) |
|
|
90
|
+
| `clientFiles.rpcWiringsFile` | RPC client (`PikkuRPC` class) calling all exposed fns |
|
|
91
|
+
| `clientFiles.realtimeFile` | `PikkuRealtime` (websocket events + SSE + channels) |
|
|
92
|
+
|
|
93
|
+
If a file isn't being generated, that field is missing from the config —
|
|
94
|
+
add it and re-run `pikku all`.
|
|
95
|
+
|
|
96
|
+
`createPikku(...)` accepts the same `CorePikkuFetchOptions` as `PikkuFetch`
|
|
97
|
+
plus `serverUrl`. Auth headers, request interceptors, etc. are configured
|
|
98
|
+
on the fetch instance — RPC and realtime inherit them automatically.
|
|
99
|
+
|
|
100
|
+
## Calling an RPC directly (no React Query)
|
|
101
|
+
|
|
102
|
+
Inside a component:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import { usePikkuRPC } from '@pikku/react'
|
|
106
|
+
|
|
107
|
+
function Logout() {
|
|
108
|
+
const rpc = usePikkuRPC()
|
|
109
|
+
return <button onClick={() => rpc.invoke('logoutUser', {})}>Sign out</button>
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`rpc.invoke(name, data)` is typed against `FlattenedRPCMap` — `name` must
|
|
114
|
+
be an exposed function id, `data` matches the input schema, return value
|
|
115
|
+
matches the output schema.
|
|
116
|
+
|
|
117
|
+
You also have `rpc.<funcName>(data)` if the generated RPC client builds
|
|
118
|
+
direct methods (project-dependent).
|
|
119
|
+
|
|
120
|
+
## Calling fetch directly
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
const fetch = usePikkuFetch()
|
|
124
|
+
const data = await fetch.get('/some-rest-route', { searchParams: {...} })
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Use this only when the function is wired via HTTP (REST shape) and you
|
|
128
|
+
need a path-style call. For RPC calls, `usePikkuRPC()` is cleaner.
|
|
129
|
+
|
|
130
|
+
## Realtime subscriptions
|
|
131
|
+
|
|
132
|
+
If you wired a `PikkuRealtime` class into `createPikku`, use
|
|
133
|
+
`usePikkuRealtime()` to grab the shared instance:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { usePikkuRealtime } from '@pikku/react'
|
|
137
|
+
import type { PikkuRealtime } from './pikku/realtime.gen'
|
|
138
|
+
|
|
139
|
+
function TodoList() {
|
|
140
|
+
const realtime = usePikkuRealtime<PikkuRealtime>()
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
return realtime.subscribe('todo-created', ({ todo }) => {
|
|
143
|
+
/* ... */
|
|
144
|
+
})
|
|
145
|
+
}, [realtime])
|
|
146
|
+
// ...
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The hook throws if no `PikkuRealtime` was wired — that's how you know to
|
|
151
|
+
add it to `createPikku(...)`. Full event-hub setup, publishing, and SSE
|
|
152
|
+
helpers live in **pikku-wiring**.
|
|
153
|
+
|
|
154
|
+
## When to reach for what
|
|
155
|
+
|
|
156
|
+
| Need | Use |
|
|
157
|
+
| ----------------------------------- | -------------------------------------------------- |
|
|
158
|
+
| Render data, dedupe + cache | **usePikkuQuery** (react-query) |
|
|
159
|
+
| Trigger a write, wait for result | **usePikkuMutation** (react-query) |
|
|
160
|
+
| Paginate | **usePikkuInfiniteQuery** (react-query) |
|
|
161
|
+
| One-off call from an event handler | `usePikkuRPC()` direct |
|
|
162
|
+
| Hit a REST endpoint (not RPC) | `usePikkuFetch()` |
|
|
163
|
+
| Run one named workflow | `usePikkuWorkflow('name')` → `.start/.run/.status` |
|
|
164
|
+
| Talk to one named AI agent | `usePikkuAgent('name')` → `.run/.stream/.approve` |
|
|
165
|
+
| Longer-running workflow UX | `references/workflows.md` |
|
|
166
|
+
| Subscribe to events / SSE / channel | `usePikkuRealtime()` (see **pikku-wiring**) |
|
|
167
|
+
|
|
168
|
+
The first three live in your generated `api.gen.ts` (see the
|
|
169
|
+
`references/react-query.md`). This reference covers the rest.
|
|
170
|
+
|
|
171
|
+
`usePikkuAgent` and `usePikkuWorkflow` bind the name once and hand back the
|
|
172
|
+
call methods with it already applied:
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
const agent = usePikkuAgent('todo-agent')
|
|
176
|
+
const { text } = await agent.run({ message, threadId })
|
|
177
|
+
|
|
178
|
+
const workflow = usePikkuWorkflow('onboardUser')
|
|
179
|
+
const { runId } = await workflow.start({ email })
|
|
180
|
+
const state = await workflow.status(runId)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Authentication
|
|
184
|
+
|
|
185
|
+
Auth is handled at the `PikkuFetch` layer, and `createPikku`'s options object
|
|
186
|
+
_is_ `CorePikkuFetchOptions` plus `serverUrl` — flat, not nested under a
|
|
187
|
+
`fetchOptions` key:
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
const pikku = createPikku(PikkuFetch, PikkuRPC, {
|
|
191
|
+
serverUrl: apiUrl(),
|
|
192
|
+
credentials: 'include', // cookie sessions
|
|
193
|
+
authHeaders: { jwt: token }, // or { apiKey }
|
|
194
|
+
transformDate: true,
|
|
195
|
+
})
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
There is no request-interceptor hook. For a token that changes after startup,
|
|
199
|
+
call the setter on the shared instance — RPC and realtime pick it up because
|
|
200
|
+
they hold the same fetch:
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
pikku.fetch.setAuthorizationJWT(token) // null clears it
|
|
204
|
+
pikku.fetch.setAPIKey(key)
|
|
205
|
+
pikku.fetch.setHeader('x-tenant', tenantId)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`authHeaders.jwt` becomes `Authorization: Bearer …` and `authHeaders.apiKey`
|
|
209
|
+
becomes `X-API-KEY`; setting a JWT takes precedence over an API key.
|
|
210
|
+
|
|
211
|
+
### Dev actor sign-in (`useDevActors`)
|
|
212
|
+
|
|
213
|
+
The dev-only "Sign in as …" control: one click signs in as a declared scenario
|
|
214
|
+
persona with no password, so the app can be reviewed as each kind of user.
|
|
215
|
+
`pikku fabric validate` **requires** any frontend with a login screen to ship one
|
|
216
|
+
(`app-missing-actor-quick-login-<app>`) — without it a reviewer is locked out of
|
|
217
|
+
their own sandbox.
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
import { useDevActors } from '@pikku/react'
|
|
221
|
+
|
|
222
|
+
const { actors, signInAs, pendingEmail, isPending, error } = useDevActors({
|
|
223
|
+
// Gate both reads on the bundler's dev flag so no credential can reach a
|
|
224
|
+
// production bundle. The sandbox dev server bakes them from your personas.
|
|
225
|
+
actors: import.meta.env.DEV ? import.meta.env.VITE_DEV_ACTORS : undefined,
|
|
226
|
+
secrets: import.meta.env.DEV
|
|
227
|
+
? import.meta.env.VITE_DEV_ACTOR_SECRETS
|
|
228
|
+
: undefined,
|
|
229
|
+
apiUrl: apiUrl(),
|
|
230
|
+
onSignedIn: () => navigate({ to: '/' }),
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
- **It is UI-free**, so render it however you like. For the default rendering use
|
|
235
|
+
`<DevActorSwitcher />` from `@pikku/mantine/dev` — a separate entry point from
|
|
236
|
+
`@pikku/mantine/core`, whose contract is "drop-in alias for `@mantine/core`"
|
|
237
|
+
and so must not export components Mantine has no counterpart for.
|
|
238
|
+
- **`secrets` is `{ address: credential }`, not one shared value** — a
|
|
239
|
+
credential opens the one persona it was minted for (see
|
|
240
|
+
**pikku-auth**). `actors` is empty unless the host supplied both a list
|
|
241
|
+
and the credentials for it, and an actor with no credential is not offered, so
|
|
242
|
+
a production build renders nothing without you testing for it.
|
|
243
|
+
- **It takes `onSignedIn` rather than a router**, and takes the env values rather
|
|
244
|
+
than reading them, because how env is spelled is a bundler fact
|
|
245
|
+
(`import.meta.env.VITE_*` vs `process.env.NEXT_PUBLIC_*`).
|
|
246
|
+
- The underlying `signInAsActor()` and `parseDevActors()` are exported too, for a
|
|
247
|
+
non-React caller. The endpoint only accepts rows flagged `actor: true`, so it
|
|
248
|
+
can never impersonate a real user — see **pikku-auth**.
|
|
249
|
+
|
|
250
|
+
Do not hand-write the `devActors()` / `signInAsActor()` pair per app; that
|
|
251
|
+
copy-paste, including the `import.meta.env.DEV` gate, is exactly what this
|
|
252
|
+
replaced.
|
|
253
|
+
|
|
254
|
+
### Linking from a Mantine element: `renderRoot`, not `component`
|
|
255
|
+
|
|
256
|
+
Handing TanStack's `Link` to a Mantine element as `component={Link}` compiles,
|
|
257
|
+
renders, and navigates — and silently unties the type. Mantine's polymorphic
|
|
258
|
+
`component` prop widens the router generic to `AnyRouter`, so `to` and
|
|
259
|
+
`params` stop being checked against your actual routes. Renaming a route then
|
|
260
|
+
breaks the running app instead of the build, which is the one thing the typed
|
|
261
|
+
router exists to prevent.
|
|
262
|
+
|
|
263
|
+
Wrap the typed `Link` once and reach it through `renderRoot`, which passes the
|
|
264
|
+
props through without re-typing the element:
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
// components/links.tsx — one wrapper the whole app links through
|
|
268
|
+
import { Link } from '@tanstack/react-router'
|
|
269
|
+
|
|
270
|
+
export const AssessmentLink = (props: { assessmentId: string; children: React.ReactNode }) => (
|
|
271
|
+
<Link to="/assessments/$assessmentId" params={{ assessmentId: props.assessmentId }}>
|
|
272
|
+
{props.children}
|
|
273
|
+
</Link>
|
|
274
|
+
)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
```tsx
|
|
278
|
+
<Button renderRoot={(p) => <AssessmentLink assessmentId={id} {...p} />}>
|
|
279
|
+
Open
|
|
280
|
+
</Button>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
The wrapper is where `to` and `params` are checked, and it is checked once.
|
|
284
|
+
|
|
285
|
+
## What NOT to do
|
|
286
|
+
|
|
287
|
+
- Don't instantiate `PikkuFetch`/`PikkuRPC` inside a component — `createPikku`
|
|
288
|
+
goes once at the app root, the instance flows through Context.
|
|
289
|
+
- Don't call `usePikkuRPC()` outside a `<PikkuProvider>` — it throws.
|
|
290
|
+
- Don't write a custom RPC client. The generated one already covers every
|
|
291
|
+
exposed function with full types.
|
|
292
|
+
- Don't hardcode user-facing strings. Every display string goes through an
|
|
293
|
+
i18n token — see **pikku-i18n** for the setup (it's English-only by default).
|
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-react-query
|
|
3
|
-
description: 'Use the Pikku auto-generated React Query hooks (`usePikkuQuery`, `usePikkuMutation`, `usePikkuInfiniteQuery`) to call backend RPC functions from a React frontend with full type safety. TRIGGER when: writing React components that need to call a Pikku function, fetch data, mutate data, or paginate; user mentions React Query, useQuery, useMutation, or building a frontend that talks to a Pikku backend. DO NOT TRIGGER when: working on the backend (use pikku-rpc / pikku-feature) or wiring a non-React frontend.'
|
|
4
|
-
installGroups: [client, fabric]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
1
|
# Pikku React Query Hooks
|
|
8
2
|
|
|
9
|
-
## Agent Operating Procedure
|
|
10
|
-
|
|
11
|
-
Use this skill as an execution checklist, not reference material.
|
|
12
|
-
|
|
13
|
-
1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
|
|
14
|
-
2. Identify the source files that own the behavior. Do not start by reading generated output, `.pikku`, `node_modules`, vendored packages, or broad build artifacts.
|
|
15
|
-
3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
|
|
16
|
-
4. Validate with the narrowest relevant command first, then run `pikku-verify` or `pikku all` when functions, wirings, schemas, or generated clients may have changed.
|
|
17
|
-
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
18
|
-
|
|
19
|
-
Pikku generates a typed React Query layer from your backend `expose: true`
|
|
20
|
-
functions. You don't write `useQuery`/`useMutation` against `fetch`
|
|
21
|
-
yourself — you call hooks named after RPCs and get full type inference for
|
|
22
|
-
input + output.
|
|
23
3
|
|
|
24
4
|
## Discover what's available on the client
|
|
25
5
|
|
|
@@ -77,7 +57,7 @@ The two generated files come from `pikku.config.json`'s
|
|
|
77
57
|
`clientFiles.fetchFile` and `clientFiles.rpcWiringsFile`. Hooks live in
|
|
78
58
|
the file at `clientFiles.reactQueryFile` (typically `api.gen.ts`).
|
|
79
59
|
|
|
80
|
-
`apiUrl()` is the shared server-URL helper — see
|
|
60
|
+
`apiUrl()` is the shared server-URL helper — see `references/client.md`. Never
|
|
81
61
|
inline `?? 'http://localhost:3000'`: a deploy that supplies the URL as a
|
|
82
62
|
runtime binding leaves `import.meta.env.VITE_API_URL` undefined in the
|
|
83
63
|
bundle, so the fallback is the branch that actually runs.
|
|
@@ -206,7 +186,7 @@ instead.
|
|
|
206
186
|
When the project defines any workflow, the same file also gains
|
|
207
187
|
`useStartWorkflow(name)` (mutation → `{ runId }`), `useRunWorkflow(name)`
|
|
208
188
|
(mutation → the workflow's output) and `useWorkflowStatus(name, runId?)` (query,
|
|
209
|
-
disabled until `runId` is set). See
|
|
189
|
+
disabled until `runId` is set). See `references/workflows.md`.
|
|
210
190
|
|
|
211
191
|
## Calling RPCs without React Query
|
|
212
192
|
|
|
@@ -1,26 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-workflows-client
|
|
3
|
-
description: 'Run Pikku workflows from a React frontend and track their progress. Covers `useRunWorkflow` (run-and-wait), `useStartWorkflow` (fire-and-poll), and `useWorkflowStatus` (live status). TRIGGER when: a React component needs to invoke or display the status of a Pikku workflow, the user mentions long-running tasks / background jobs / progress UI tied to a workflow, or asks how to start/track a workflow from the client. DO NOT TRIGGER when: the user is wiring the workflow itself (use pikku-workflow) or only making regular RPC calls (use pikku-react-query).'
|
|
4
|
-
installGroups: [client]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
1
|
# Pikku Workflows — Client Hooks
|
|
8
2
|
|
|
9
|
-
## Agent Operating Procedure
|
|
10
|
-
|
|
11
|
-
Use this skill as an execution checklist, not reference material.
|
|
12
|
-
|
|
13
|
-
1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
|
|
14
|
-
2. Identify the source files that own the behavior. Do not start by reading generated output, `.pikku`, `node_modules`, vendored packages, or broad build artifacts.
|
|
15
|
-
3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
|
|
16
|
-
4. Validate with the narrowest relevant command first, then run `pikku-verify` or `pikku all` when functions, wirings, schemas, or generated clients may have changed.
|
|
17
|
-
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
18
|
-
|
|
19
|
-
When a project defines any workflow — DSL or `pikkuWorkflowGraph` — three
|
|
20
|
-
React Query hooks are auto-generated alongside the standard RPC hooks. They handle
|
|
21
|
-
the two common shapes: **run-and-wait** (short workflows where the
|
|
22
|
-
client waits for the result) and **fire-and-poll** (long workflows where
|
|
23
|
-
the client gets a `runId` and polls status).
|
|
24
3
|
|
|
25
4
|
## Discover what workflows exist
|
|
26
5
|
|
|
@@ -36,7 +15,7 @@ below.
|
|
|
36
15
|
|
|
37
16
|
These hooks are generated into the same `api.gen.ts` as `usePikkuQuery` —
|
|
38
17
|
no extra setup beyond `PikkuProvider` + `QueryClientProvider` (see the
|
|
39
|
-
|
|
18
|
+
`references/client.md` and `references/react-query.md`).
|
|
40
19
|
|
|
41
20
|
## `useRunWorkflow(name, options?)` — run and wait
|
|
42
21
|
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-scenario
|
|
3
3
|
description: >-
|
|
4
|
-
Use when writing or running Pikku scenarios,
|
|
5
|
-
test coverage. A scenario (pikkuScenario) drives the app
|
|
6
|
-
over the real transport against a running server — so a
|
|
7
|
-
staged/production health check. Covers scenario.do /
|
|
8
|
-
expectService / expectScore, declared steps via
|
|
9
|
-
@pikku/playwright) written as intent rather than
|
|
10
|
-
|
|
11
|
-
`pikku scenario list|run`
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
TRIGGER when: user asks about running an existing
|
|
4
|
+
Use when writing or running Pikku scenarios, running a persona as a virtual user, or when
|
|
5
|
+
asked to test Pikku functions or improve coverage. A scenario (pikkuScenario) drives the app
|
|
6
|
+
the way users do — steps run as actors over the real transport against a running server — so a
|
|
7
|
+
flow doubles as an e2e test and a staged/production health check. Covers scenario.do /
|
|
8
|
+
expectEventually / expectError / expectService / expectScore, declared steps via
|
|
9
|
+
pikkuScenarioStep (browser steps driven by @pikku/playwright) written as intent rather than
|
|
10
|
+
clicks, personas / actors / environments in pikku.config.json, SCENARIO_ACTOR_SECRET, the
|
|
11
|
+
`pikku scenario list|run` and `pikku persona run|list|sync|secret` commands, and live coverage
|
|
12
|
+
via `pikku dev --coverage`. TRIGGER when: user asks about scenarios, testing a Pikku function,
|
|
13
|
+
coverage, e2e flows, browser/UI e2e, health checks, personas, virtual users, or adversarial
|
|
14
|
+
runs against a stage. DO NOT TRIGGER when: user asks about running an existing suite (use
|
|
15
|
+
Bash) or CI config.
|
|
15
16
|
installGroups: [core]
|
|
16
17
|
---
|
|
17
18
|
|
|
@@ -29,6 +30,13 @@ Use this skill as an execution checklist, not reference material.
|
|
|
29
30
|
|
|
30
31
|
**`pikku tests` does not exist.** It was removed in #865 — scenarios own coverage now. Any reference you find to it is stale.
|
|
31
32
|
|
|
33
|
+
## Pick the reference
|
|
34
|
+
|
|
35
|
+
| You are… | Read |
|
|
36
|
+
| ---------------------------------------------------------------- | --------------------------- |
|
|
37
|
+
| Writing or running scenarios | this skill |
|
|
38
|
+
| Running a persona as a model-driven virtual user against a stage | `references/persona-run.md` |
|
|
39
|
+
|
|
32
40
|
## What a scenario is
|
|
33
41
|
|
|
34
42
|
A scenario is a `pikkuScenario` export that drives the app **as real actors over the real transport**, against a running server. That is what lets one artifact serve as both an e2e test and a staged/production health check.
|
|
@@ -46,7 +54,7 @@ Scenarios live in `srcDirectories` like any other function — by convention `*.
|
|
|
46
54
|
`pikkuScenario` comes from the **generated** workflow types, not `@pikku/core`:
|
|
47
55
|
|
|
48
56
|
```typescript
|
|
49
|
-
import { pikkuScenario } from '#pikku/
|
|
57
|
+
import { pikkuScenario } from '#pikku/scenarios'
|
|
50
58
|
|
|
51
59
|
export const orderSupportScenario = pikkuScenario<
|
|
52
60
|
{ value?: number },
|
|
@@ -175,7 +183,7 @@ Hooks are scenario-only. A `before`/`after` on a `pikkuWorkflowFunc` never runs
|
|
|
175
183
|
`pikkuFeature` groups scenarios the way gherkin's `Feature:` groups `Scenario:`. Scenarios are referenced by **imported identifier**, so a renamed or deleted scenario is a compile error rather than a silent skip:
|
|
176
184
|
|
|
177
185
|
```typescript
|
|
178
|
-
import { pikkuFeature } from '#pikku/
|
|
186
|
+
import { pikkuFeature } from '#pikku/scenarios'
|
|
179
187
|
import {
|
|
180
188
|
credentialLazyLoadScenario,
|
|
181
189
|
credentialRoundTripScenario,
|
|
@@ -240,7 +248,7 @@ Utilities are **not steps**. They are plain exported functions, they take the br
|
|
|
240
248
|
|
|
241
249
|
```typescript
|
|
242
250
|
// shop.browser.ts — shared actions. Not steps: nothing here is an intent.
|
|
243
|
-
import type { PikkuBrowserWire } from '#pikku/
|
|
251
|
+
import type { PikkuBrowserWire } from '#pikku/scenarios'
|
|
244
252
|
import type {} from '@pikku/playwright'
|
|
245
253
|
|
|
246
254
|
/** Arrive on the shop, from wherever the browser happens to be. */
|
|
@@ -379,10 +387,13 @@ identifier is an API, and it is read by the toolchain.
|
|
|
379
387
|
|
|
380
388
|
```typescript
|
|
381
389
|
// pikku.config.json: { "metaLocale": "de" }
|
|
382
|
-
export const buysAnApple = pikkuScenarioStep<
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
390
|
+
export const buysAnApple = pikkuScenarioStep<
|
|
391
|
+
{ qty: number },
|
|
392
|
+
{ orderId: string }
|
|
393
|
+
>({
|
|
394
|
+
name: 'buysAnApple', // identifier — English, always
|
|
395
|
+
description: 'kauft einen Apfel', // prose — follows locale
|
|
396
|
+
template: 'kauft {qty} Äpfel', // prose — follows locale
|
|
386
397
|
actor: true,
|
|
387
398
|
default: async (_services, { qty }, { actor }) =>
|
|
388
399
|
await actor.invoke('placeOrder', { qty }),
|
|
@@ -396,11 +407,10 @@ A product with a non-English UI is not on its own a reason to set `metaLocale`
|
|
|
396
407
|
is the app's language, not the team's. Ask, or leave it `en`.
|
|
397
408
|
|
|
398
409
|
**Where a non-`en` `metaLocale` still shows English, today.** The reporter composes a
|
|
399
|
-
sentence as `<Keyword>
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
English frame — `Given the shopper kauft 1 Äpfel`. Write templates that read
|
|
410
|
+
sentence as `<Keyword> <actor> <template>` (`composeStepProse`), and the keyword is
|
|
411
|
+
an English literal. The Console translates the Given/When/Then keywords into its own
|
|
412
|
+
UI language; the CLI reporter does not, so `metaLocale: "de"` gives you German step
|
|
413
|
+
prose inside an English frame — `Given shopper kauft 1 Äpfel`. Write templates that read
|
|
404
414
|
acceptably in that frame rather than trying to defeat it. A second gap: where a
|
|
405
415
|
function or scenario declares no `title`, the Console falls back to splitting the
|
|
406
416
|
**identifier** into an English-looking label (`toEnglishName`), so under a
|
|
@@ -462,7 +472,7 @@ Assertions with no possible browser witness are a different thing and should not
|
|
|
462
472
|
`scenario.do` can only name an RPC. A **step** is a named, typed unit of scenario behaviour whose body is an ordinary pikku function — so it can call several RPCs as its actor, assert, or drive a browser.
|
|
463
473
|
|
|
464
474
|
```typescript
|
|
465
|
-
import { pikkuScenarioStep } from '#pikku/
|
|
475
|
+
import { pikkuScenarioStep } from '#pikku/scenarios'
|
|
466
476
|
|
|
467
477
|
export const buysAnApple = pikkuScenarioStep<
|
|
468
478
|
{ qty: number },
|
|
@@ -502,6 +512,7 @@ Rules that bite:
|
|
|
502
512
|
- **Steps default to `retries: 0`**, unlike ordinary workflow steps. Retrying a failed assertion is wrong; pass `retries` explicitly if a step is genuinely flaky-by-nature.
|
|
503
513
|
- **Step results are persisted**, so return JSON-serialisable data — never a `Locator` or a client object.
|
|
504
514
|
- **`description` documents the step; `template` is what the report renders.** `template`'s `{placeholders}` are filled from the input the step was called with, so one step reads differently for each call — `sees {state} addon {packageName}` reports as "sees available addon @pikku/addon-stripe". Reflect every input field in the template, and type the values so they read as words (`state?: 'installed' | 'available'`, not `installed?: boolean`). A placeholder with no value renders as nothing and the whitespace collapses.
|
|
515
|
+
- **Never write the actor into the prose.** The reporter renders the actor as the sentence's subject, so a step authored as `` `'sam' creates the client` `` run as `{ actor: actors.sam }` reads "Given sam 'sam' creates the client" — and the hardcoded name desyncs the moment the call site changes actor. Write a bare third-person predicate (`creates the {name} client`) and let the actor supply the subject. Prose that opens with its own actor's key — quoted, capitalised or possessive — is `PKU681`; naming someone **else** mid-sentence ("sends nadia an invite") is ordinary prose and is left alone, as is an actor keyed after a role noun used as a noun ("creates the admin client" as `actors.admin`).
|
|
505
516
|
- Prose precedence is `options.description` → the step's `template` → the step's own `description` → the positional step name. Repeated names get `#1`, `#2` ordinals, so a `for` loop over a data set is how you write a Scenario Outline. A loop-generated step name is not statically known, so it is matched back to its declaration by step function instead — which works as long as that function's call sites agree on their phase, actor and prose. Two call sites that disagree make the loop step report under its bare runtime name.
|
|
506
517
|
|
|
507
518
|
#### What a step is given
|
|
@@ -548,7 +559,7 @@ Two consequences follow, and both shape how steps get written:
|
|
|
548
559
|
as an RPC — which usually improves the product, since a client debugging the
|
|
549
560
|
same problem needed it too.
|
|
550
561
|
- **`agentRunner` is conditional.** It is built only when the project declares
|
|
551
|
-
agents, and `createDevAgentRunner` needs a base URL
|
|
562
|
+
agents, and `createDevAgentRunner` needs a base URL _and_ a key together
|
|
552
563
|
(`OPENAI_BASE_URL` + `OPENAI_API_KEY`, or the LiteLLM pair). With a key alone
|
|
553
564
|
it returns nothing and `agentRunner` is `undefined`, so `actor.converse`
|
|
554
565
|
fails before the persona says anything. A suite that would rather own its own
|
|
@@ -596,12 +607,16 @@ import type messages from '../../../../apps/web/messages/en.json'
|
|
|
596
607
|
|
|
597
608
|
export type MessageKey = keyof typeof messages
|
|
598
609
|
|
|
599
|
-
export const t = (key: MessageKey, locale = baseLocale): string => {
|
|
610
|
+
export const t = (key: MessageKey, locale = baseLocale): string => {
|
|
611
|
+
/* … */
|
|
612
|
+
}
|
|
600
613
|
```
|
|
601
614
|
|
|
602
615
|
```typescript
|
|
603
616
|
await page.getByLabel(t('jobs_apply_fullname')).fill(identity.name)
|
|
604
|
-
await page
|
|
617
|
+
await page
|
|
618
|
+
.getByRole('button', { name: t('jobs_apply_submit'), exact: true })
|
|
619
|
+
.click()
|
|
605
620
|
```
|
|
606
621
|
|
|
607
622
|
- Type off `messages/<baseLocale>.json`, **not** the generated Paraglide output — `i18n/paraglide/` is build output, so typing against it makes the tests unbuildable until the app has been built. The JSON is the tracked source.
|
|
@@ -676,11 +691,11 @@ Generated files are exempt and never claim the slot.
|
|
|
676
691
|
What that admits and what it does not:
|
|
677
692
|
|
|
678
693
|
```typescript
|
|
679
|
-
personality: 'Wound up and short with it.'
|
|
694
|
+
personality: 'Wound up and short with it.' // read
|
|
680
695
|
personality: `Wound up and short with it.
|
|
681
|
-
Says what she wants in a few blunt words.`
|
|
682
|
-
personality: 'Wound up. ' + 'Short with it.'
|
|
683
|
-
personality: TEMPERAMENTS.impatient
|
|
696
|
+
Says what she wants in a few blunt words.` // read — no ${} in it
|
|
697
|
+
personality: 'Wound up. ' + 'Short with it.' // dropped, silently
|
|
698
|
+
personality: TEMPERAMENTS.impatient // dropped, silently
|
|
684
699
|
```
|
|
685
700
|
|
|
686
701
|
A no-substitution template literal is a string literal as far as the reader is
|
|
@@ -704,14 +719,14 @@ An actor with no `persona` is its own persona, so a project that never declares
|
|
|
704
719
|
### The same actors sign a human in
|
|
705
720
|
|
|
706
721
|
Declared actors are not only for automated runs. `signInPath` is Better Auth's
|
|
707
|
-
`actor` plugin (see `pikku-
|
|
722
|
+
`actor` plugin (see `pikku-auth`, a separate install), which any caller can post to — so the
|
|
708
723
|
frontend gets a one-click "Sign in as …" switcher over the **same** list, and an
|
|
709
724
|
app can be reviewed as each kind of user without anyone knowing a seed password.
|
|
710
725
|
|
|
711
726
|
The sandbox dev server bakes both halves into the frontend from the declared
|
|
712
727
|
personas: `VITE_DEV_ACTORS` (the JSON actor list) and `VITE_DEV_ACTOR_SECRETS`
|
|
713
728
|
(`{ email: credential }`, one per persona — `SCENARIO_ACTOR_SECRET` itself never
|
|
714
|
-
goes in a bundle; see **pikku-
|
|
729
|
+
goes in a bundle; see **pikku-auth**). Neither var is set in a production
|
|
715
730
|
build, so the control renders nothing there — but gate the reads on your
|
|
716
731
|
bundler's dev flag anyway (`import.meta.env.DEV ? … : undefined`) so no
|
|
717
732
|
credential reaches a production bundle in the first place.
|
|
@@ -825,22 +840,22 @@ Services are plain objects — a Pikku function is pure business logic, so a moc
|
|
|
825
840
|
|
|
826
841
|
## Red flags
|
|
827
842
|
|
|
828
|
-
| Smell | Why it's wrong
|
|
829
|
-
| --------------------------------------------------- |
|
|
830
|
-
| `pikku tests …` | Removed in #865. Use `pikku scenario`.
|
|
831
|
-
| `.feature` files / Gherkin for function tests | Scenarios are TypeScript, not Gherkin. The in-process cucumber function world was deleted.
|
|
832
|
-
| `scenario.do(...)` with no `{ actor }` | Throws. Every step runs as somebody.
|
|
833
|
-
| A scenario per function | Scenarios are user flows. One flow covers many functions; that is the point.
|
|
834
|
-
| Assuming a clean database | There is no state reset — it may be a staging server. Scope what you create.
|
|
835
|
-
| `sleep()` before asserting | Use `expectEventually`.
|
|
836
|
-
| A step named `clicksAddToBasket` / `opensThePage` | That is an action, not an intent. Name the step for what the actor wanted; put the clicking in a utility.
|
|
837
|
-
| A step named `kauftEinenApfel` / a `vorgang` table
|
|
838
|
-
| A browser step that assumes it is already on a page | It can then only run mid-flow. Arrive first — check the URL, navigate if needed.
|
|
839
|
-
| `getByLabel('Full Name')` in a translated app
|
|
840
|
-
| A `browser` binding guarding `if (!browser)` | The binding guarantees it. The guard hides the real error, which is a missing actor (`PKU677`).
|
|
841
|
-
| A step with a `func:` instead of a surface binding | There is no `func` on a step. Bodies live under `default` / `browser` / `cli`; a step with none throws at load.
|
|
842
|
-
| `expectEventually` in a `pikkuWorkflowFunc` | `PKU675` — scenario-only.
|
|
843
|
-
| Coverage silently 0 | Server not run with `--coverage`, verbose functions meta not deployed, `scaffold.scenarios` unset, or no actors configured.
|
|
843
|
+
| Smell | Why it's wrong |
|
|
844
|
+
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
845
|
+
| `pikku tests …` | Removed in #865. Use `pikku scenario`. |
|
|
846
|
+
| `.feature` files / Gherkin for function tests | Scenarios are TypeScript, not Gherkin. The in-process cucumber function world was deleted. |
|
|
847
|
+
| `scenario.do(...)` with no `{ actor }` | Throws. Every step runs as somebody. |
|
|
848
|
+
| A scenario per function | Scenarios are user flows. One flow covers many functions; that is the point. |
|
|
849
|
+
| Assuming a clean database | There is no state reset — it may be a staging server. Scope what you create. |
|
|
850
|
+
| `sleep()` before asserting | Use `expectEventually`. |
|
|
851
|
+
| A step named `clicksAddToBasket` / `opensThePage` | That is an action, not an intent. Name the step for what the actor wanted; put the clicking in a utility. |
|
|
852
|
+
| A step named `kauftEinenApfel` / a `vorgang` table | Identifiers are English in every project. The German belongs in `description` / `template`, and only when `pikku.config.json` sets `metaLocale`. |
|
|
853
|
+
| A browser step that assumes it is already on a page | It can then only run mid-flow. Arrive first — check the URL, navigate if needed. |
|
|
854
|
+
| `getByLabel('Full Name')` in a translated app | Passes only in the base locale, and a copy edit breaks it as an unexplained timeout. Locate by message key. |
|
|
855
|
+
| A `browser` binding guarding `if (!browser)` | The binding guarantees it. The guard hides the real error, which is a missing actor (`PKU677`). |
|
|
856
|
+
| A step with a `func:` instead of a surface binding | There is no `func` on a step. Bodies live under `default` / `browser` / `cli`; a step with none throws at load. |
|
|
857
|
+
| `expectEventually` in a `pikkuWorkflowFunc` | `PKU675` — scenario-only. |
|
|
858
|
+
| Coverage silently 0 | Server not run with `--coverage`, verbose functions meta not deployed, `scaffold.scenarios` unset, or no actors configured. |
|
|
844
859
|
|
|
845
860
|
`@pikku/cucumber` is a **browser/e2e** harness (`Actor`, `BrowserWorld`, `PersonaData`, `DbUtils`) — out of scope here.
|
|
846
861
|
|