@pikku/skills 0.12.43 → 0.12.44
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/README.md +3 -2
- package/dist/skills.gen.js +2 -2
- package/package.json +1 -1
- package/skills/pikku-auth/references/better-auth.md +8 -7
- package/skills/pikku-build/references/app.md +22 -11
- package/skills/pikku-fabric/SKILL.md +6 -7
- package/skills/pikku-react/SKILL.md +1 -1
- package/skills/pikku-react/references/client.md +15 -22
- package/skills/pikku-scenario/references/personas.md +20 -52
package/package.json
CHANGED
|
@@ -500,10 +500,9 @@ value for the address being signed in as and compares, so a credential minted
|
|
|
500
500
|
for one persona is refused for every other, and the root itself is never a valid
|
|
501
501
|
credential. A root under 32 characters refuses the endpoint outright rather than
|
|
502
502
|
deriving weak credentials from it (the server log names the problem; the client
|
|
503
|
-
is not told which). Callers rarely derive by hand — `pikku
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
derive on the fly.
|
|
503
|
+
is not told which). Callers rarely derive by hand — `pikku persona secret <id>`
|
|
504
|
+
mints them for a run, and the two `PersonaSignIn` implementations derive on the
|
|
505
|
+
fly. The browser switcher holds none: it signs in through `/sign-in/persona`.
|
|
507
506
|
|
|
508
507
|
**Which command is running decides whether it works, not whether a secret is
|
|
509
508
|
set.** `pikku dev` sets `PIKKU_DEV_ACTOR_SIGN_IN` and mints an ephemeral
|
|
@@ -559,9 +558,11 @@ actor`. So the secret cannot take over a **real user's** account — the blast
|
|
|
559
558
|
paragraph above. The comparison is constant-time and length-hiding, so a wrong
|
|
560
559
|
credential leaks neither the length nor a prefix of the right one.
|
|
561
560
|
|
|
562
|
-
This is the endpoint `pikku scenario` signs its actors in through
|
|
563
|
-
|
|
564
|
-
|
|
561
|
+
This is the endpoint `pikku scenario` signs its actors in through. The frontend
|
|
562
|
+
switcher does not use it: it lists from `/sign-in/personas` and posts a persona
|
|
563
|
+
id to `/sign-in/persona`, both served by
|
|
564
|
+
`pikkuActor({ personaSignIn: { personas, featureFlags } })` with no credential — see
|
|
565
|
+
`pikku-scenario` for the setup and `pikku-react` for `useDevActors()`.
|
|
565
566
|
|
|
566
567
|
### Provisioning personas
|
|
567
568
|
|
|
@@ -573,17 +573,13 @@ frontend running against a dead API looks exactly like an app bug, so if every
|
|
|
573
573
|
request fails, check that both halves came up.
|
|
574
574
|
|
|
575
575
|
**Start the stack through `bun run dev`, not by launching `vite` or `pikku dev`
|
|
576
|
-
yourself.** The
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
and `VITE_API_PROXY` pointing at your API — the dev proxy defaults to
|
|
584
|
-
`http://localhost:3000`, so beside another project's server your sign-ins go to
|
|
585
|
-
_its_ API and come back `401 Invalid actor secret`, which reads like a bad
|
|
586
|
-
credential rather than the wrong server.
|
|
576
|
+
yourself.** The "Sign in as …" switcher asks the API for its personas at
|
|
577
|
+
runtime, so the frontend needs nothing baked in — but it does need to reach
|
|
578
|
+
_your_ API. If you start the frontend on its own (say :3000 is taken by another
|
|
579
|
+
project), point `VITE_API_PROXY` at your API: the dev proxy defaults to
|
|
580
|
+
`http://localhost:3000`, so beside another project's server the switcher lists
|
|
581
|
+
_its_ personas, or none, which reads like a missing switcher rather than the
|
|
582
|
+
wrong server.
|
|
587
583
|
|
|
588
584
|
The `--bun` in `bunx --bun pikku …` is load-bearing — keep it. Without it the
|
|
589
585
|
CLI's `#!/usr/bin/env node` shebang hands the process to whatever Node is on
|
|
@@ -709,6 +705,21 @@ new RPC and does not re-run `afterStart`, so a fresh function answers 404 and
|
|
|
709
705
|
anything provisioned at boot is missing — failures that read like a wiring bug
|
|
710
706
|
and are nothing but a stale process.
|
|
711
707
|
|
|
708
|
+
**Run the whole suite, not the milestone's own scenarios.** The milestone's
|
|
709
|
+
scenarios are the ones you wrote to pass; the regression lives in someone
|
|
710
|
+
else's. Tightening what "archived" means is a one-function change that reads as
|
|
711
|
+
local and quietly breaks the milestone-01 scenario nobody re-ran.
|
|
712
|
+
|
|
713
|
+
**Restart the server after adding a function, and never edit one while a run is
|
|
714
|
+
in flight.** Hot reload does not register a new RPC and does not re-run
|
|
715
|
+
`afterStart`, so a fresh function answers 404 and anything provisioned at boot
|
|
716
|
+
is missing — failures that read like a wiring bug and are nothing but a stale
|
|
717
|
+
process. The same reload is what makes a run unrepeatable if you edit during
|
|
718
|
+
it: a browser pass is long enough to feel like free time, and a schema touched
|
|
719
|
+
at minute four hot-reloads into a half-generated contract, so every scenario
|
|
720
|
+
after that point fails on something you have already fixed. Wait for the run or
|
|
721
|
+
kill it — a run you edited under is not a result.
|
|
722
|
+
|
|
712
723
|
### 7a. Coverage — which functions have actually been run
|
|
713
724
|
|
|
714
725
|
Green scenarios tell you the journeys you wrote still work. They say nothing
|
|
@@ -477,17 +477,16 @@ reviewer has no seed password, so without the control they are locked out of the
|
|
|
477
477
|
app they were asked to look at.
|
|
478
478
|
|
|
479
479
|
Satisfy it with `<DevActorSwitcher />` from `@pikku/mantine/dev`, or with your
|
|
480
|
-
own UI built on `useDevActors()` from `@pikku/react` —
|
|
481
|
-
call
|
|
482
|
-
|
|
480
|
+
own UI built on `useDevActors()` or `signInAsPersona()` from `@pikku/react` —
|
|
481
|
+
validate accepts any of those call sites as evidence, so custom rendering
|
|
482
|
+
passes. Either way the server needs `personaSignIn` on `pikkuActor`; see
|
|
483
|
+
**pikku-scenario** for it and **pikku-react** for the props.
|
|
483
484
|
|
|
484
485
|
The validator also accepts the shapes that predate the package — a hand-rolled
|
|
485
486
|
`signInAsActor()` or a literal `POST /auth/sign-in/actor` — so an older app does
|
|
486
487
|
not fail the build. **Treat that as a grace period, not the target: migrate those
|
|
487
|
-
to `<DevActorSwitcher />`.**
|
|
488
|
-
|
|
489
|
-
had already diverged on the `import.meta.env.DEV` gate that keeps the shared
|
|
490
|
-
secret out of production bundles.
|
|
488
|
+
to `<DevActorSwitcher />`.** They put a per-persona credential in the frontend
|
|
489
|
+
bundle, which the persona endpoint exists to avoid.
|
|
491
490
|
|
|
492
491
|
Do **not** satisfy it with Better Auth's `/dev/quick-login`. That is a different
|
|
493
492
|
endpoint with a different purpose — one fixed admin, not the declared personas —
|
|
@@ -6,7 +6,7 @@ description: >-
|
|
|
6
6
|
direct usePikkuRPC / usePikkuFetch calls, realtime subscriptions, agent and workflow hooks, and
|
|
7
7
|
the dev actor switcher. TRIGGER when: writing a React component that fetches or mutates backend
|
|
8
8
|
data, wiring PikkuProvider, paginating, running or tracking a workflow from the client, or
|
|
9
|
-
asking about useDevActors /
|
|
9
|
+
asking about useDevActors / DevActorSwitcher / quick login. DO NOT TRIGGER when: working on the
|
|
10
10
|
backend (use pikku-wiring), defining the workflow itself (use pikku-workflow), or writing
|
|
11
11
|
user-facing copy (use pikku-i18n).
|
|
12
12
|
installGroups: [client]
|
|
@@ -251,14 +251,9 @@ their own sandbox.
|
|
|
251
251
|
```tsx
|
|
252
252
|
import { useDevActors } from '@pikku/react'
|
|
253
253
|
|
|
254
|
-
const { actors, signInAs,
|
|
255
|
-
// Gate both reads on the bundler's dev flag so no credential can reach a
|
|
256
|
-
// production bundle. The sandbox dev server bakes them from your personas.
|
|
257
|
-
actors: import.meta.env.DEV ? import.meta.env.VITE_DEV_ACTORS : undefined,
|
|
258
|
-
secrets: import.meta.env.DEV
|
|
259
|
-
? import.meta.env.VITE_DEV_ACTOR_SECRETS
|
|
260
|
-
: undefined,
|
|
254
|
+
const { actors, signInAs, pendingId, isPending, error } = useDevActors({
|
|
261
255
|
apiUrl: apiUrl(),
|
|
256
|
+
app: appSlug,
|
|
262
257
|
onSignedIn: () => navigate({ to: '/' }),
|
|
263
258
|
})
|
|
264
259
|
```
|
|
@@ -267,21 +262,19 @@ const { actors, signInAs, pendingEmail, isPending, error } = useDevActors({
|
|
|
267
262
|
`<DevActorSwitcher />` from `@pikku/mantine/dev` — a separate entry point from
|
|
268
263
|
`@pikku/mantine/core`, whose contract is "drop-in alias for `@mantine/core`"
|
|
269
264
|
and so must not export components Mantine has no counterpart for.
|
|
270
|
-
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
copy-paste, including the `import.meta.env.DEV` gate, is exactly what this
|
|
284
|
-
replaced.
|
|
265
|
+
- **No credential reaches the bundle.** It lists from `/auth/sign-in/personas`
|
|
266
|
+
and `signInAs(id)` posts only the persona id to `/auth/sign-in/persona`. The
|
|
267
|
+
server decides who is offered and who may sign in, and offers nobody in
|
|
268
|
+
production — see **pikku-scenario** for `personaSignIn`.
|
|
269
|
+
- **It takes `onSignedIn` rather than a router**, since every app lands
|
|
270
|
+
somewhere different.
|
|
271
|
+
- `listDevActors()` and `signInAsPersona()` are exported too, for a non-React
|
|
272
|
+
caller. The endpoint only
|
|
273
|
+
signs in rows flagged `actor: true`, so it can never impersonate a real user —
|
|
274
|
+
see **pikku-auth**.
|
|
275
|
+
|
|
276
|
+
Do not hand-write the list-and-sign-in pair per app; that copy-paste is exactly
|
|
277
|
+
what this replaced.
|
|
285
278
|
|
|
286
279
|
### Linking from a Mantine element: `renderRoot`, not `component`
|
|
287
280
|
|
|
@@ -83,13 +83,17 @@ Declared actors are not only for automated runs. `signInPath` is Better Auth's
|
|
|
83
83
|
frontend gets a one-click "Sign in as …" switcher over the **same** list, and an
|
|
84
84
|
app can be reviewed as each kind of user without anyone knowing a seed password.
|
|
85
85
|
|
|
86
|
-
The
|
|
87
|
-
personas
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
The switcher holds no credential. It lists personas from
|
|
87
|
+
`/auth/sign-in/personas` and signs in by posting only a persona id to
|
|
88
|
+
`/auth/sign-in/persona`; the server resolves the address. One server piece
|
|
89
|
+
serves both, in the auth config:
|
|
90
|
+
|
|
91
|
+
```ts snippet:personaSignIn
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
It is always open under `pikku dev`. A deployed stage needs actor
|
|
95
|
+
sign-in opted in **and** its `devSwitcher` feature flag on; production never
|
|
96
|
+
has the opt-in, so it lists nobody and refuses every persona sign-in.
|
|
93
97
|
|
|
94
98
|
Do not hand-roll the switcher: `useDevActors()` (`pikku-react`, a separate install) is the logic and
|
|
95
99
|
`<DevActorSwitcher />` from `@pikku/mantine/dev` is a ready rendering of it.
|
|
@@ -98,52 +102,16 @@ one — without it a reviewer is locked out of their own sandbox.
|
|
|
98
102
|
When the switcher is missing, it is one of three things, and none of them
|
|
99
103
|
errors:
|
|
100
104
|
|
|
101
|
-
- **The
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
- **The list is empty.** On a deployed stage that is the gate
|
|
106
|
+
doing its job — check the opt-in and the `devSwitcher` flag. Locally, check
|
|
107
|
+
the personas declare an `email` (via `scenarios.emailDomain`) and are not
|
|
108
|
+
`runnable: false`.
|
|
109
|
+
- **`personaSignIn` is missing, or the frontend calls another API.** A dev
|
|
110
|
+
proxy (`VITE_API_PROXY`, default `http://localhost:3000`) that points at
|
|
111
|
+
another project's API lists that project's personas, or none.
|
|
106
112
|
- **It is not mounted on the page you are looking at.** The template mounts it
|
|
107
113
|
on the login screen. A public homepage that replaces the `/` → `/app`
|
|
108
114
|
redirect needs its own `<DevActorSwitcher />` in the public layout.
|
|
109
115
|
|
|
110
|
-
When the switcher
|
|
111
|
-
|
|
112
|
-
whose dev proxy (`VITE_API_PROXY`, default `http://localhost:3000`) points at
|
|
113
|
-
another project's API sends the sign-in there.
|
|
114
|
-
|
|
115
|
-
**A runner of your own that starts vite has to bake them itself**, from the
|
|
116
|
-
generated persona meta (`<outDir>/workflow/personas.gen.json`, which already
|
|
117
|
-
carries the derived `email`):
|
|
118
|
-
|
|
119
|
-
```js
|
|
120
|
-
const personas = Object.values(JSON.parse(readFileSync(personasPath, 'utf8')))
|
|
121
|
-
|
|
122
|
-
env.VITE_DEV_ACTORS = JSON.stringify(
|
|
123
|
-
personas.map(({ id, email, name, jobTitle }) => ({
|
|
124
|
-
key: id,
|
|
125
|
-
email,
|
|
126
|
-
name,
|
|
127
|
-
jobTitle: jobTitle ?? '',
|
|
128
|
-
}))
|
|
129
|
-
)
|
|
130
|
-
env.VITE_DEV_ACTOR_SECRETS = JSON.stringify(
|
|
131
|
-
Object.fromEntries(
|
|
132
|
-
await Promise.all(
|
|
133
|
-
personas.map(async ({ email }) => [
|
|
134
|
-
email,
|
|
135
|
-
await deriveActorSecret(env.SCENARIO_ACTOR_SECRET, email),
|
|
136
|
-
])
|
|
137
|
-
)
|
|
138
|
-
)
|
|
139
|
-
)
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
**Set `SCENARIO_ACTOR_SECRET` yourself**, at least 32 characters, in the
|
|
143
|
-
environment both processes read. Left unset, `pikku dev` mints an ephemeral root
|
|
144
|
-
for its own run that a separately spawned vite cannot see, so the two derive
|
|
145
|
-
from different roots: the switcher renders every persona and each click is
|
|
146
|
-
refused, which reads as a broken login rather than missing configuration. On a
|
|
147
|
-
brand-new project the persona file does not exist until the first `pikku dev`
|
|
148
|
-
codegen, after vite has baked an empty list — watch it and restart the frontend
|
|
149
|
-
when it changes.
|
|
116
|
+
When the switcher lists personas but a click 404s, that persona has no `email`
|
|
117
|
+
or is `runnable: false`.
|