@pikku/skills 0.12.52 → 0.12.54
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/dist/skills.gen.js +2 -2
- package/package.json +1 -1
- package/skills/pikku-a11y/SKILL.md +22 -15
- package/skills/pikku-addon/SKILL.md +5 -5
- package/skills/pikku-addon/references/addon-package-manifest.md +7 -0
- package/skills/pikku-addon/references/openapi.md +11 -0
- package/skills/pikku-admin-to-fabric/SKILL.md +7 -21
- package/skills/pikku-agent/SKILL.md +6 -6
- package/skills/pikku-agent/references/agents.md +8 -1
- package/skills/pikku-agent/references/runner-vercel.md +2 -2
- package/skills/pikku-agent/references/voice.md +7 -1
- package/skills/pikku-architect/SKILL.md +1 -1
- package/skills/pikku-architect/references/plan-defects.md +2 -0
- package/skills/pikku-auth/SKILL.md +15 -36
- package/skills/pikku-auth/references/better-auth.md +22 -45
- package/skills/pikku-auth/references/jose.md +6 -0
- package/skills/pikku-auth/references/machine-auth.md +6 -0
- package/skills/pikku-auth/references/sessions.md +6 -0
- package/skills/pikku-blueprint-to-fabric/SKILL.md +6 -21
- package/skills/pikku-blueprint-to-fabric/scripts/inventory.mjs +1 -1
- package/skills/pikku-build/SKILL.md +29 -26
- package/skills/pikku-build/references/app.md +60 -41
- package/skills/pikku-build/references/design.md +31 -19
- package/skills/pikku-build/references/feature.md +14 -0
- package/skills/pikku-build/references/mocks.md +23 -0
- package/skills/pikku-build/references/multi-app.md +9 -2
- package/skills/pikku-build/references/native-app.md +9 -0
- package/skills/pikku-build/references/openapi.md +7 -0
- package/skills/pikku-build/references/platform.md +20 -10
- package/skills/pikku-build/references/quick.md +14 -4
- package/skills/pikku-build/references/scenarios.md +12 -1
- package/skills/pikku-build/references/ship.md +6 -1
- package/skills/pikku-build/references/theming.md +74 -125
- package/skills/pikku-changes/SKILL.md +6 -6
- package/skills/pikku-concepts/SKILL.md +8 -17
- package/skills/pikku-concepts/references/concept-mapping.md +18 -0
- package/skills/pikku-convert-screen/SKILL.md +45 -0
- package/skills/pikku-deploy/references/cloudflare.md +8 -0
- package/skills/pikku-deploy/references/lambda.md +8 -0
- package/skills/pikku-deploy/references/uws.md +1 -1
- package/skills/pikku-deploy/references/ws.md +3 -3
- package/skills/pikku-emails/SKILL.md +3 -9
- package/skills/pikku-fabric/SKILL.md +7 -295
- package/skills/pikku-fabric/references/database.md +128 -0
- package/skills/pikku-fabric/references/debugging.md +9 -5
- package/skills/pikku-fabric/references/deploy.md +179 -0
- package/skills/pikku-guide/SKILL.md +10 -10
- package/skills/pikku-i18n/SKILL.md +7 -7
- package/skills/pikku-i18n/references/enum-labels.md +9 -0
- package/skills/pikku-i18n/references/messages.md +16 -3
- package/skills/pikku-i18n/references/rtl.md +18 -38
- package/skills/pikku-knowledge/SKILL.md +9 -10
- package/skills/pikku-kysely/SKILL.md +3 -21
- package/skills/pikku-list-query/SKILL.md +2 -2
- package/skills/pikku-meta/references/audit.md +9 -0
- package/skills/pikku-meta/references/meta.md +6 -0
- package/skills/pikku-meta/references/versioning.md +10 -0
- package/skills/pikku-middleware/SKILL.md +7 -16
- package/skills/pikku-n8n-import/SKILL.md +9 -1
- package/skills/pikku-n8n-import/references/addon-mapping.md +6 -0
- package/skills/pikku-n8n-import/references/code-translation.md +7 -0
- package/skills/pikku-permissions/SKILL.md +41 -10
- package/skills/pikku-permissions/references/scopes.md +59 -0
- package/skills/pikku-react/SKILL.md +2 -2
- package/skills/pikku-react/references/client.md +1 -34
- package/skills/pikku-react/references/react-query.md +11 -0
- package/skills/pikku-react/references/workflows.md +11 -0
- package/skills/pikku-realtime/SKILL.md +9 -3
- package/skills/pikku-report/SKILL.md +10 -9
- package/skills/pikku-scenario/SKILL.md +11 -17
- package/skills/pikku-scenario/references/persona-run.md +11 -1
- package/skills/pikku-scenario/references/personas.md +7 -1
- package/skills/pikku-seo/SKILL.md +12 -16
- package/skills/pikku-service-backends/SKILL.md +0 -10
- package/skills/pikku-service-backends/references/aws.md +6 -0
- package/skills/pikku-service-backends/references/mongodb.md +7 -0
- package/skills/pikku-service-backends/references/redis.md +6 -0
- package/skills/pikku-services/references/audit.md +10 -0
- package/skills/pikku-services/references/config.md +6 -0
- package/skills/pikku-services/references/services.md +8 -1
- package/skills/pikku-software-archaeology/SKILL.md +9 -15
- package/skills/pikku-software-archaeology/references/blueprint.schema.json +7 -7
- package/skills/pikku-software-archaeology/references/pikku-mapping.md +3 -3
- package/skills/pikku-software-archaeology/references/second-opinion.md +13 -1
- package/skills/pikku-tailwind/SKILL.md +111 -0
- package/skills/pikku-theme/SKILL.md +187 -0
- package/skills/pikku-theme/references/from-a-reference.md +44 -0
- package/skills/pikku-theme/references/presets.md +40 -0
- package/skills/pikku-ui/SKILL.md +41 -0
- package/skills/pikku-wiring/SKILL.md +13 -12
- package/skills/pikku-wiring/references/channel.md +7 -1
- package/skills/pikku-wiring/references/cli.md +7 -1
- package/skills/pikku-wiring/references/gateway-slack.md +6 -0
- package/skills/pikku-wiring/references/http.md +10 -3
- package/skills/pikku-wiring/references/mcp.md +8 -0
- package/skills/pikku-wiring/references/queue.md +6 -0
- package/skills/pikku-wiring/references/realtime.md +34 -4
- package/skills/pikku-wiring/references/rpc.md +6 -0
- package/skills/pikku-wiring/references/scheduler.md +6 -0
- package/skills/pikku-wiring/references/trigger.md +8 -0
- package/skills/pikku-workflow/SKILL.md +8 -6
- package/skills/pikku-auth/references/permissions.md +0 -261
- package/skills/pikku-mantine/SKILL.md +0 -80
- package/skills/pikku-wiring/references/realtime-other-routes.md +0 -23
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-permissions
|
|
3
3
|
description: >-
|
|
4
|
-
Use when deciding WHO may call a function
|
|
5
|
-
TRIGGER when: writing or reviewing any function that touches a row a user owns, gating an action on a role, building the permissions half of a contract in build PHASE 2,
|
|
6
|
-
DO NOT TRIGGER when: the question is how
|
|
4
|
+
Use when deciding WHO may call a function: resource ownership, role gates, admin-only actions, or any "only their own rows" rule. Covers the `permissions` field, `pikkuPermission`, `pikkuAuth`, scopes and `defineScope`, `addGlobalPermission`, the three gates, `permissionsInBody`, and where ownership belongs versus where it does not.
|
|
5
|
+
TRIGGER when: writing or reviewing any function that touches a row a user owns, gating an action on a role, building the permissions half of a contract in build PHASE 2, about to write an `if` in a function body that decides whether the caller is allowed, or hitting `MissingScopeError`.
|
|
6
|
+
DO NOT TRIGGER when: the question is how a caller proves who they are, such as sign-in, sessions, tokens, API keys or seeding a persona (use pikku-auth), or how to shape a paginated list (use pikku-list-query).
|
|
7
7
|
installGroups: [core]
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Pikku Permissions
|
|
11
11
|
|
|
12
|
+
## First: is the caller a machine with a token?
|
|
13
|
+
|
|
14
|
+
Then this is NOT a permissions problem. Resolve the token in `addHTTPMiddleware('*')`
|
|
15
|
+
middleware that calls `setSession`, make the function a `pikkuFunc`, and gate it with
|
|
16
|
+
`scopes` (pikku-auth, `../pikku-auth/references/machine-auth.md`). A `permissions` check that verifies a
|
|
17
|
+
bearer token and returns `true` is authentication wearing an authorization hat, and it
|
|
18
|
+
leaves the function sessionless. The only exception is a bootstrap endpoint whose caller
|
|
19
|
+
has no identity yet (a shared-secret registration, a login): it is sessionless and
|
|
20
|
+
declares its gate here.
|
|
21
|
+
|
|
12
22
|
## The rule
|
|
13
23
|
|
|
14
24
|
**Authorization goes in the `permissions` field. Never in the `func` body.**
|
|
@@ -107,11 +117,32 @@ gets straight through if the function itself is open.
|
|
|
107
117
|
## Scopes
|
|
108
118
|
|
|
109
119
|
`scopes: ['admin:invoices:void']` is an AND gate checked BEFORE permissions and before
|
|
110
|
-
input validation.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
`
|
|
114
|
-
|
|
120
|
+
input validation. A function naming an undeclared scope fails codegen rather than gating on
|
|
121
|
+
nothing. A grant satisfies a scope if it is that scope, an ancestor, or a wildcard. Most
|
|
122
|
+
apps need roles, not scopes; reach for these only when the plan asked for granular grants.
|
|
123
|
+
The `defineScope` tree, grant matching and the sessionless restriction are in
|
|
124
|
+
[references/scopes.md](references/scopes.md).
|
|
125
|
+
|
|
126
|
+
## Global permissions
|
|
127
|
+
|
|
128
|
+
`addGlobalPermission([isEmployee])` (from `#pikku/auth`) is an app-wide baseline every
|
|
129
|
+
function must additionally pass. It is an independent AND gate: it can only narrow access,
|
|
130
|
+
never grant what a function's own `permissions` would deny. Multiple calls accumulate and
|
|
131
|
+
are AND'd.
|
|
132
|
+
|
|
133
|
+
## The three gates
|
|
134
|
+
|
|
135
|
+
All must pass, in this order:
|
|
136
|
+
|
|
137
|
+
1. **Scopes** (`scopes`) — AND'd, checked before input validation, fails closed.
|
|
138
|
+
2. **Global permissions** (`addGlobalPermission`) — AND'd together, a baseline that only narrows.
|
|
139
|
+
3. **The function's own `permissions`** — OR'd groups of AND'd entries.
|
|
140
|
+
|
|
141
|
+
They are independent: a broad global (e.g. `isEmployee`) can never satisfy an admin-only
|
|
142
|
+
function's own requirement. Wire-, tag- and HTTP-route-level permissions
|
|
143
|
+
(`addHTTPPermission`, `addTagPermission`, a `permissions` field on a wiring) no longer
|
|
144
|
+
exist; tags are organizational only. Use tag/HTTP middleware for cross-cutting request
|
|
145
|
+
handling, not authorization.
|
|
115
146
|
|
|
116
147
|
## The one sanctioned exception
|
|
117
148
|
|
|
@@ -122,5 +153,5 @@ the auditor the openness is deliberate. Anything expressible as a permission mus
|
|
|
122
153
|
|
|
123
154
|
## After changes
|
|
124
155
|
|
|
125
|
-
`pikku all` — regenerates
|
|
126
|
-
wrong fails here, not at runtime.
|
|
156
|
+
`pikku all --tsc` — regenerates, then verifies the checker types. A permission whose
|
|
157
|
+
signature is wrong fails here, not at runtime.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Scopes
|
|
2
|
+
|
|
3
|
+
Scopes answer "what was this session granted?" before permissions ask "may this user do
|
|
4
|
+
this to this resource?". They are AND-ed: every scope listed must be held. Because they
|
|
5
|
+
are checked first and fail closed, a scope can only ever narrow access.
|
|
6
|
+
|
|
7
|
+
## Declaring the tree
|
|
8
|
+
|
|
9
|
+
Declare the scope tree once with `defineScope`. The body is a no-op that tree-shakes
|
|
10
|
+
away; the CLI reads the call by AST and generates a `ScopeId` union, so a function naming
|
|
11
|
+
an undeclared scope fails the build.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// src/scopes.ts
|
|
15
|
+
import { defineScope } from '#pikku/scopes'
|
|
16
|
+
|
|
17
|
+
defineScope({
|
|
18
|
+
admin: {
|
|
19
|
+
displayName: 'Administration',
|
|
20
|
+
description: 'Administrative access',
|
|
21
|
+
scopes: {
|
|
22
|
+
invoices: {
|
|
23
|
+
description: 'Invoice management',
|
|
24
|
+
scopes: {
|
|
25
|
+
create: { description: 'Create invoices' },
|
|
26
|
+
void: { description: 'Void invoices' },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
billing: {},
|
|
32
|
+
})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Every node is grantable, keyed by segment: the above yields `admin`, `admin:invoices`,
|
|
36
|
+
`admin:invoices:create`, `admin:invoices:void` and `billing`. Scopes may be declared
|
|
37
|
+
across more than one file; the declarations merge.
|
|
38
|
+
|
|
39
|
+
## Using them
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
export const voidInvoice = pikkuFunc({
|
|
43
|
+
scopes: ['admin:invoices:void'],
|
|
44
|
+
permissions: { owner: isInvoiceOwner },
|
|
45
|
+
func: async ({ kysely }, { invoiceId }) => { ... },
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
A grant satisfies a required scope if it is the scope itself, an ancestor of it, or a
|
|
50
|
+
wildcard at any level: a session holding `admin` satisfies `admin:invoices:void`, and so
|
|
51
|
+
does `admin:*`. A missing scope throws `MissingScopeError` naming the first one that
|
|
52
|
+
failed.
|
|
53
|
+
|
|
54
|
+
## Not on sessionless functions
|
|
55
|
+
|
|
56
|
+
`scopes` requires a session and is unavailable on `pikkuSessionlessFunc`: scopes fail
|
|
57
|
+
closed, an anonymous caller holds none, and a sessionless function with scopes would
|
|
58
|
+
reject every caller it exists to serve. Gate those with `permissions`, which receive the
|
|
59
|
+
optional session and may pass anonymous.
|
|
@@ -7,8 +7,8 @@ description: >-
|
|
|
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
9
|
asking about useDevActors / DevActorSwitcher / quick login. DO NOT TRIGGER when: working on the
|
|
10
|
-
backend (use pikku-wiring), defining the workflow itself (use pikku-workflow),
|
|
11
|
-
user-facing copy (use pikku-i18n).
|
|
10
|
+
backend (use pikku-wiring), defining the workflow itself (use pikku-workflow), rendering fetched values such as
|
|
11
|
+
dates (use pikku-tailwind), or writing user-facing copy (use pikku-i18n).
|
|
12
12
|
installGroups: [client]
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -259,9 +259,7 @@ const { actors, signInAs, pendingId, isPending, error } = useDevActors({
|
|
|
259
259
|
```
|
|
260
260
|
|
|
261
261
|
- **It is UI-free**, so render it however you like. For the default rendering use
|
|
262
|
-
`<DevActorSwitcher />`
|
|
263
|
-
`@pikku/mantine/core`, whose contract is "drop-in alias for `@mantine/core`"
|
|
264
|
-
and so must not export components Mantine has no counterpart for.
|
|
262
|
+
the starter template's `<DevActorSwitcher />` (`components/dev/DevActorSwitcher.tsx`).
|
|
265
263
|
- **No credential reaches the bundle.** It lists from `/auth/sign-in/personas`
|
|
266
264
|
and `signInAs(id)` posts only the persona id to `/auth/sign-in/persona`. The
|
|
267
265
|
server decides who is offered and who may sign in, and offers nobody in
|
|
@@ -276,37 +274,6 @@ const { actors, signInAs, pendingId, isPending, error } = useDevActors({
|
|
|
276
274
|
Do not hand-write the list-and-sign-in pair per app; that copy-paste is exactly
|
|
277
275
|
what this replaced.
|
|
278
276
|
|
|
279
|
-
### Linking from a Mantine element: `renderRoot`, not `component`
|
|
280
|
-
|
|
281
|
-
Handing TanStack's `Link` to a Mantine element as `component={Link}` compiles,
|
|
282
|
-
renders, and navigates — and silently unties the type. Mantine's polymorphic
|
|
283
|
-
`component` prop widens the router generic to `AnyRouter`, so `to` and
|
|
284
|
-
`params` stop being checked against your actual routes. Renaming a route then
|
|
285
|
-
breaks the running app instead of the build, which is the one thing the typed
|
|
286
|
-
router exists to prevent.
|
|
287
|
-
|
|
288
|
-
Wrap the typed `Link` once and reach it through `renderRoot`, which passes the
|
|
289
|
-
props through without re-typing the element:
|
|
290
|
-
|
|
291
|
-
```tsx
|
|
292
|
-
// components/links.tsx — one wrapper the whole app links through
|
|
293
|
-
import { Link } from '@tanstack/react-router'
|
|
294
|
-
|
|
295
|
-
export const AssessmentLink = (props: { assessmentId: string; children: React.ReactNode }) => (
|
|
296
|
-
<Link to="/assessments/$assessmentId" params={{ assessmentId: props.assessmentId }}>
|
|
297
|
-
{props.children}
|
|
298
|
-
</Link>
|
|
299
|
-
)
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
```tsx
|
|
303
|
-
<Button renderRoot={(p) => <AssessmentLink assessmentId={id} {...p} />}>
|
|
304
|
-
Open
|
|
305
|
-
</Button>
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
The wrapper is where `to` and `params` are checked, and it is checked once.
|
|
309
|
-
|
|
310
277
|
## What NOT to do
|
|
311
278
|
|
|
312
279
|
- Don't instantiate `PikkuFetch`/`PikkuRPC` inside a component — `createPikku`
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Pikku React Query Hooks
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [Discover what's available on the client](#discover-whats-available-on-the-client)
|
|
6
|
+
- [Setup (once per app)](#setup-once-per-app)
|
|
7
|
+
- [TanStack Start (SSR)](#tanstack-start-ssr)
|
|
8
|
+
- [The hooks](#the-hooks)
|
|
9
|
+
- [Workflow hooks](#workflow-hooks)
|
|
10
|
+
- [Calling RPCs without React Query](#calling-rpcs-without-react-query)
|
|
11
|
+
- [Common patterns](#common-patterns)
|
|
12
|
+
- [What NOT to do](#what-not-to-do)
|
|
13
|
+
|
|
3
14
|
|
|
4
15
|
## Discover what's available on the client
|
|
5
16
|
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Pikku Workflows — Client Hooks
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [Discover what workflows exist](#discover-what-workflows-exist)
|
|
6
|
+
- [Setup](#setup)
|
|
7
|
+
- [useRunWorkflow(name, options?) — run and wait](#userunworkflowname-options--run-and-wait)
|
|
8
|
+
- [useStartWorkflow(name, options?) — fire-and-poll](#usestartworkflowname-options--fire-and-poll)
|
|
9
|
+
- [useWorkflowStatus(workflowName, runId, options?) — observe](#useworkflowstatusworkflowname-runid-options--observe)
|
|
10
|
+
- [Putting it together — start + observe](#putting-it-together--start--observe)
|
|
11
|
+
- [Backend: streaming richer progress](#backend-streaming-richer-progress)
|
|
12
|
+
- [What NOT to do](#what-not-to-do)
|
|
13
|
+
|
|
3
14
|
|
|
4
15
|
## Discover what workflows exist
|
|
5
16
|
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-realtime
|
|
3
3
|
description: >-
|
|
4
|
-
Use when making
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
Use when making a view live in a Pikku app — a board, shared list, dashboard, ticker, bidding
|
|
5
|
+
room, live count — or when adding two-way chat or presence. Covers the default event-hub SSE path
|
|
6
|
+
(`eventHub.publish` on the backend, `PikkuRealtime.subscribeToTopic` on the client) and the
|
|
7
|
+
two-way WebSocket channel. TRIGGER when: the user wants live updates, realtime, "update without
|
|
8
|
+
refresh", a live board/feed/ticker/room, presence or chat, or data that more than one signed-in
|
|
9
|
+
user can change should reflect to others. DO NOT TRIGGER when: a one-shot query/refetch is enough
|
|
10
|
+
(data only one user changes, or a manual refresh is acceptable), for background jobs (use
|
|
11
|
+
pikku-workflow, or pikku-wiring's queue and scheduler references), or for the full wiring
|
|
12
|
+
reference of channels, SSE routes and the `/events` scaffold (use pikku-wiring).
|
|
7
13
|
installGroups: [core, client]
|
|
8
14
|
---
|
|
9
15
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-report
|
|
3
3
|
description: >-
|
|
4
|
-
Use when pikku itself cost you time — wrong generated types, a check that passes when it
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
Use when pikku itself cost you time — wrong generated types, a check that passes when it should
|
|
5
|
+
not, output that is quietly wrong, a skill that misled you — or when the user asks you to report
|
|
6
|
+
a framework bug or file a finding. Owns `pikku fabric report` (a finding is about pikku, not the
|
|
7
|
+
app), the product-vs-harness kinds, the workaround-first ladder, and asking the user at hand-over
|
|
8
|
+
whether to send what was filed. TRIGGER when: the framework fought you, codegen produced
|
|
9
|
+
something broken, a skill told you to run something that does not exist, the user says "report
|
|
10
|
+
this to pikku" / "file a finding", or you are handing over a build. DO NOT TRIGGER when: the bug
|
|
11
|
+
is in the app you are building (fix it there), you are debugging a deployed stage (use
|
|
12
|
+
pikku-fabric), or you are tempted to patch pikku's source (never do that from an app).
|
|
12
13
|
installGroups: [core]
|
|
13
14
|
---
|
|
14
15
|
|
|
@@ -61,7 +62,7 @@ baseline noise that was already failing before you started.
|
|
|
61
62
|
- `--kind harness` — a skill misled you: it told you to run something that does
|
|
62
63
|
not exist, described a flag that is spelled differently, or contradicted what
|
|
63
64
|
the CLI actually did. Pass `--skill <name>` and `--passage "<the line or
|
|
64
|
-
section>"`. This is the most useful kind to file, because it is fixable
|
|
65
|
+
section>"`. This is the most useful kind to file, because it is fixable
|
|
65
66
|
immediately — so file it even when the cost was small.
|
|
66
67
|
|
|
67
68
|
## The command
|
|
@@ -8,7 +8,7 @@ description: >-
|
|
|
8
8
|
expectEventually / expectError / expectService / expectScore, declared steps via
|
|
9
9
|
pikkuScenarioStep (browser steps driven by @pikku/playwright) written as intent rather than
|
|
10
10
|
clicks, personas / actors / environments in pikku.config.json, SCENARIO_ACTOR_SECRET, the
|
|
11
|
-
`pikku scenario
|
|
11
|
+
`pikku scenario` and `pikku persona` commands, and live coverage
|
|
12
12
|
via `pikku dev --coverage`. TRIGGER when: user asks about scenarios, testing a Pikku function,
|
|
13
13
|
coverage, e2e flows, browser/UI e2e, health checks, personas, virtual users, or adversarial
|
|
14
14
|
runs against a stage. DO NOT TRIGGER when: user asks about running an existing suite (use
|
|
@@ -20,19 +20,15 @@ installGroups: [core]
|
|
|
20
20
|
|
|
21
21
|
## Agent Operating Procedure
|
|
22
22
|
|
|
23
|
-
Use this skill as an execution checklist, not reference material.
|
|
24
|
-
|
|
25
23
|
1. Discover before editing: `pikku scenario list` for what exists, `pikku info functions --verbose` for what a scenario can call.
|
|
26
|
-
2.
|
|
27
|
-
3.
|
|
28
|
-
4. Validate with the narrowest relevant command first, then `pikku all --tsc` when functions, wirings or schemas may have changed.
|
|
29
|
-
5. If validation fails, fix the source cause and rerun. Do not paper over generated errors by editing generated files.
|
|
24
|
+
2. Edit source files only; never generated output or `.pikku`.
|
|
25
|
+
3. Validate with the narrowest relevant command first, then `pikku all --tsc` when functions, wirings or schemas may have changed. If it fails, fix the source cause and rerun.
|
|
30
26
|
|
|
31
27
|
**`pikku tests` does not exist.** It was removed in #865 — scenarios own coverage now. Any reference you find to it is stale.
|
|
32
28
|
|
|
33
29
|
## Pick the reference
|
|
34
30
|
|
|
35
|
-
This skill covers writing and running scenarios end to end.
|
|
31
|
+
This skill covers writing and running scenarios end to end. Five topics are one level down, and
|
|
36
32
|
each says when to open it:
|
|
37
33
|
|
|
38
34
|
| Read | For |
|
|
@@ -186,12 +182,6 @@ Hooks are scenario-only. A `before`/`after` on a `pikkuWorkflowFunc` never runs
|
|
|
186
182
|
|
|
187
183
|
### Grouping scenarios (`pikkuFeature`)
|
|
188
184
|
|
|
189
|
-
A feature is also cited by a page of the user guide: `pikku scenario guide`
|
|
190
|
-
fills each cited block with the feature's recordings and screenshots, and
|
|
191
|
-
nothing else (see **pikku-guide**). A scenario's `title` captions its recording
|
|
192
|
-
and a screenshot's `name` captions the still, so write both as copy a user would
|
|
193
|
-
read. Mark plumbing features `document: false`.
|
|
194
|
-
|
|
195
185
|
`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:
|
|
196
186
|
|
|
197
187
|
```typescript
|
|
@@ -229,6 +219,12 @@ export const credentialFeature = pikkuFeature({
|
|
|
229
219
|
|
|
230
220
|
The **feature is the run unit**: `--flows` on a scenario whose every feature entry carries `data` errors and names the features containing it, because the feature is what supplies that data. Use `--features` for those. A scenario referenced bare anywhere, or in no feature at all, still runs standalone.
|
|
231
221
|
|
|
222
|
+
A feature is also cited by a page of the user guide: `pikku scenario guide`
|
|
223
|
+
fills each cited block with the feature's recordings and screenshots, and
|
|
224
|
+
nothing else (see **pikku-guide**). A scenario's `title` captions its recording
|
|
225
|
+
and a screenshot's `name` captions the still, so write both as copy a user would
|
|
226
|
+
read. Mark plumbing features `document: false`.
|
|
227
|
+
|
|
232
228
|
## Steps
|
|
233
229
|
|
|
234
230
|
`scenario.do` can only name an RPC. A **step** — `pikkuScenarioStep` — is a named, typed unit of
|
|
@@ -289,7 +285,7 @@ scenario actor per person, and seeds a user row each:
|
|
|
289
285
|
- `scenarios.emailDomain` is the mail domain actor addresses are built on.
|
|
290
286
|
- `scenarios.browserDriver` is the package driving `browser` bindings.
|
|
291
287
|
- `scenarios.model` is the model a persona thinks with (`actor.converse`, `pikku virtual-user run`).
|
|
292
|
-
- `environments.<name>` are the targets a run can point at. The key is the required positional of `pikku scenario run`; `apiUrl` is required and `
|
|
288
|
+
- `environments.<name>` are the targets a run can point at. The key is the required positional of `pikku scenario run`; `apiUrl` is required; `signInPath` defaults to `/auth/sign-in/actor`, `rpcPath` to `/rpc`, and `sessionPath` has a default too.
|
|
293
289
|
|
|
294
290
|
**`references/personas.md`** covers the parts that bite: one entry is one
|
|
295
291
|
person and one actor, emails are derived and never written, `runnable: false`
|
|
@@ -297,7 +293,6 @@ for someone only acted upon, `definePersonas` being read from source rather
|
|
|
297
293
|
than evaluated, and the same actor list powering a human "Sign in as …"
|
|
298
294
|
switcher.
|
|
299
295
|
|
|
300
|
-
- `environments.<name>.apiUrl` is required. `signInPath` defaults to `/auth/sign-in/actor`, `rpcPath` to `/rpc`.
|
|
301
296
|
- **`SCENARIO_ACTOR_SECRET` is an environment variable and never goes in `pikku.config.json`.** It signs actors in. `pikku scenario run` throws without it; a server auto-building actors warns and runs without them.
|
|
302
297
|
|
|
303
298
|
## Running
|
|
@@ -351,7 +346,6 @@ and where a plain unit test is still the right tool.
|
|
|
351
346
|
|
|
352
347
|
| Smell | Why it's wrong |
|
|
353
348
|
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
354
|
-
| `pikku tests …` | Removed in #865. Use `pikku scenario`. |
|
|
355
349
|
| `.feature` files / Gherkin for function tests | Scenarios are TypeScript, not Gherkin. The in-process cucumber function world was deleted. |
|
|
356
350
|
| `scenario.do(...)` with no `{ actor }` | Throws. Every step runs as somebody. |
|
|
357
351
|
| A scenario per function | Scenarios are user flows. One flow covers many functions; that is the point. |
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Running a persona as a virtual user
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [The shape of a run](#the-shape-of-a-run)
|
|
6
|
+
- [The dispositions](#the-dispositions)
|
|
7
|
+
- [Credentials, and which one wins](#credentials-and-which-one-wins)
|
|
8
|
+
- [Production is opt-in, twice](#production-is-opt-in-twice)
|
|
9
|
+
- [The role check happens before the first step](#the-role-check-happens-before-the-first-step)
|
|
10
|
+
- [The other subcommands](#the-other-subcommands)
|
|
11
|
+
- [What NOT to do](#what-not-to-do)
|
|
12
|
+
|
|
3
13
|
`pikku persona run <environment> <persona>` signs a declared persona in over the
|
|
4
14
|
app's real auth and works the API in character, driven by a model. A persona
|
|
5
15
|
while running **is** the virtual user — there is no second declaration for it.
|
|
@@ -125,7 +135,7 @@ drift.
|
|
|
125
135
|
|
|
126
136
|
`sync` **reports**; it does not provision. The CLI has no connection to a
|
|
127
137
|
deployed environment's database, so the provisioning happens in the deployment —
|
|
128
|
-
pass the generated personas to `
|
|
138
|
+
pass the generated personas to `provisionPersonas` from `@pikku/better-auth`.
|
|
129
139
|
|
|
130
140
|
## What NOT to do
|
|
131
141
|
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Personas and actors
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [Declaring personas in TypeScript](#declaring-personas-in-typescript)
|
|
6
|
+
- [Actors that call a third-party API](#actors-that-call-a-third-party-api)
|
|
7
|
+
- [The same actors sign a human in](#the-same-actors-sign-a-human-in)
|
|
8
|
+
|
|
3
9
|
A **persona** is a person your product is for; an **actor** is one body that signs in as them. Every entry materialises exactly one actor, so `actors.<id>` exists for each declared persona and there is no second way to declare a login.
|
|
4
10
|
|
|
5
11
|
- Two people of the same kind are two entries, not one persona with two logins — "you see yours, not theirs" is only testable with two customers.
|
|
@@ -96,7 +102,7 @@ sign-in opted in **and** its `devSwitcher` feature flag on; production never
|
|
|
96
102
|
has the opt-in, so it lists nobody and refuses every persona sign-in.
|
|
97
103
|
|
|
98
104
|
Do not hand-roll the switcher: `useDevActors()` (`pikku-react`, a separate install) is the logic and
|
|
99
|
-
`<DevActorSwitcher />`
|
|
105
|
+
the starter template's `<DevActorSwitcher />` is a ready rendering of it.
|
|
100
106
|
`pikku fabric validate` **requires** any frontend with a login screen to ship
|
|
101
107
|
one — without it a reviewer is locked out of their own sandbox.
|
|
102
108
|
When the switcher is missing, it is one of three things, and none of them
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-seo
|
|
3
3
|
description: >-
|
|
4
|
-
On-page SEO rules for the app's PUBLIC pages: per-route head() titles and meta descriptions, Open Graph tags,
|
|
4
|
+
On-page SEO rules for the app's PUBLIC pages: per-route head() titles and meta descriptions, Open Graph tags, crawlable links and URLs, JSON-LD on the landing page, and noindex for the logged-in area.
|
|
5
5
|
TRIGGER when: building or reworking any public page (landing, pricing, about, blog/content pages), writing page titles or meta tags, or the user asks about SEO / Google / discoverability / social sharing previews.
|
|
6
|
-
DO NOT TRIGGER when: working on logged-in /app screens (they are noindexed — only the one robots rule below applies), backend functions, database, or deployment.
|
|
6
|
+
DO NOT TRIGGER when: the task is accessibility, including heading order, landmarks or alt text (use pikku-a11y), or working on logged-in /app screens (they are noindexed — only the one robots rule below applies), backend functions, database, or deployment.
|
|
7
7
|
installGroups: [client]
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -38,19 +38,20 @@ export const Route = createFileRoute('/pricing')({
|
|
|
38
38
|
})
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
`head()` strings are plain strings (they do not go through the
|
|
41
|
+
`head()` strings are plain strings (they do not go through the component i18n
|
|
42
42
|
gate) — write real copy for THIS app, in the app's voice.
|
|
43
43
|
|
|
44
44
|
- **Title**: unique per page, 50–60 characters, the page's primary topic first,
|
|
45
|
-
brand at the end (`Topic — AppName`). The template's `__APP_TITLE__`
|
|
46
|
-
|
|
45
|
+
brand at the end (`Topic — AppName`). The template's `__APP_TITLE__` placeholder
|
|
46
|
+
(in `src/app-meta.ts` and `messages/en.json`) is filled with the app title at scaffold time
|
|
47
|
+
by `fabric-new-app`; it must never survive in any page's title.
|
|
47
48
|
- **Description**: unique per page, 150–160 characters, states the concrete
|
|
48
49
|
value of the page in plain language — a reason to click, not a keyword list.
|
|
49
50
|
- **Dynamic public pages** (e.g. a public detail page) build both from loader
|
|
50
51
|
data: `head: ({ loaderData }) => ({ meta: [{ title: `${loaderData.name} — AppName` }, ...] })`.
|
|
51
52
|
- **Never invent URLs**: the deployed domain is unknown at build time, so do
|
|
52
53
|
NOT emit `canonical`, `og:url`, or `og:image` pointing at a made-up domain —
|
|
53
|
-
omit them
|
|
54
|
+
omit them. `og:image` only if a
|
|
54
55
|
real asset exists in the app.
|
|
55
56
|
|
|
56
57
|
## Logged-in area = noindex
|
|
@@ -64,24 +65,19 @@ head: () => ({ meta: [{ name: 'robots', content: 'noindex' }] })
|
|
|
64
65
|
Never noindex a public page, and never put per-page SEO effort into `/app`
|
|
65
66
|
screens — they are invisible to crawlers by design.
|
|
66
67
|
|
|
67
|
-
## Headings
|
|
68
|
+
## Headings, landmarks, alt text
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
- Logical hierarchy below it: h1 → h2 → h3, no skipped levels, headings
|
|
73
|
-
describe the content under them. Never pick a heading level for its font
|
|
74
|
-
size — set the size on the correct level (`<Title order={2} fz="xs">`).
|
|
70
|
+
Owned by `pikku-a11y` and apply app-wide; follow it. SEO addition only: the
|
|
71
|
+
public page's h1 names its primary topic, aligned with the title tag (not
|
|
72
|
+
identical boilerplate).
|
|
75
73
|
|
|
76
74
|
## Crawlable, semantic markup
|
|
77
75
|
|
|
78
|
-
- Landmarks on public pages: `<nav>`, `<main>`, `<footer>` (Mantine: `component="nav"` etc.).
|
|
79
76
|
- Navigation between public pages uses real links (`<Link>`/`<a href>`) with
|
|
80
77
|
descriptive anchor text — crawlers follow hrefs; a `div onClick` navigation
|
|
81
78
|
is invisible to them. No public page may be orphaned: every public page is
|
|
82
79
|
reachable by link from the landing page (directly or via nav/footer).
|
|
83
|
-
-
|
|
84
|
-
get `alt=""`. Prefer descriptive file names for real assets.
|
|
80
|
+
- Prefer descriptive file names for real assets (alt text: see `pikku-a11y`).
|
|
85
81
|
- Readable URLs: public routes are lowercase, hyphen-separated, and named for
|
|
86
82
|
their content (`/pricing`, `/how-it-works`) — never `/page2` or query-param
|
|
87
83
|
navigation.
|
|
@@ -16,16 +16,6 @@ installGroups: [core]
|
|
|
16
16
|
|
|
17
17
|
# Pikku Service Backends
|
|
18
18
|
|
|
19
|
-
## Agent Operating Procedure
|
|
20
|
-
|
|
21
|
-
Use this skill as an execution checklist, not reference material.
|
|
22
|
-
|
|
23
|
-
1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
|
|
24
|
-
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.
|
|
25
|
-
3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
|
|
26
|
-
4. Validate with the narrowest relevant command first, then run `pikku all` when functions, wirings, schemas, or generated clients may have changed.
|
|
27
|
-
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
28
|
-
|
|
29
19
|
Constructor shapes and method signatures come from `pikku doc` — run
|
|
30
20
|
`pikku doc --ai` for the installed surface. This skill is the part the compiler
|
|
31
21
|
cannot tell you: which backend implements which interface, and what changes when
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# AWS (`@pikku/aws-services`)
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [`S3Content` — ContentService](#s3content-contentservice)
|
|
6
|
+
- [`SQSQueueService` — QueueService](#sqsqueueservice-queueservice)
|
|
7
|
+
- [`AWSSecrets` — SecretService](#awssecrets-secretservice)
|
|
8
|
+
|
|
3
9
|
```bash
|
|
4
10
|
yarn add @pikku/aws-services
|
|
5
11
|
```
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# MongoDB (`@pikku/mongodb`)
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [`PikkuMongoDB` — connection wrapper](#pikkumongodb-connection-wrapper)
|
|
6
|
+
- [Available services](#available-services)
|
|
7
|
+
- [`MongoDBSecretService`](#mongodbsecretservice)
|
|
8
|
+
- [Full setup](#full-setup)
|
|
9
|
+
|
|
3
10
|
```bash
|
|
4
11
|
yarn add @pikku/mongodb
|
|
5
12
|
```
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Pikku Audit
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [Mental model — two layers](#mental-model-two-layers)
|
|
6
|
+
- [Wiring (services.ts)](#wiring-servicests)
|
|
7
|
+
- [Recording events — explicit domain events (default)](#recording-events-explicit-domain-events-default)
|
|
8
|
+
- [Recording events — automatic query capture (optional)](#recording-events-automatic-query-capture-optional)
|
|
9
|
+
- [Sinks](#sinks)
|
|
10
|
+
- [AuditEvent shape](#auditevent-shape)
|
|
11
|
+
- [Do / Don't](#do-dont)
|
|
12
|
+
|
|
3
13
|
|
|
4
14
|
## Mental model — two layers
|
|
5
15
|
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Pikku Services (Dependency Injection)
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [Before You Start](#before-you-start)
|
|
6
|
+
- [API Reference](#api-reference)
|
|
7
|
+
- [Usage Patterns](#usage-patterns)
|
|
8
|
+
- [Complete Example](#complete-example)
|
|
9
|
+
|
|
3
10
|
## Before You Start
|
|
4
11
|
|
|
5
12
|
```bash
|
|
@@ -75,7 +82,7 @@ export const lifecycle = pikkuServerLifecycle<SingletonServices>({
|
|
|
75
82
|
})
|
|
76
83
|
```
|
|
77
84
|
|
|
78
|
-
Every hook is optional. Order is `beforeStart` → server starts → `afterStart`, then on SIGINT `beforeStop` → services stopped → server stopped → `afterStop`.
|
|
85
|
+
Every hook is optional. Order is `beforeStart` → server starts → `afterStart`, then on SIGINT or SIGTERM `beforeStop` → services stopped → server stopped → `afterStop`. A repeat signal while this runs is ignored, so the sequence always finishes.
|
|
79
86
|
|
|
80
87
|
**`afterStop` runs after the singleton services have been stopped.** It still receives the services object, but the services inside it are shut down — using one there is a use-after-close bug. Anything that needs a live service goes in `beforeStop`.
|
|
81
88
|
|