@pikku/skills 0.12.22 → 0.12.26
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 +134 -0
- package/dist/skills.gen.js +1 -1
- package/package.json +1 -1
- package/skills/pikku-a11y/SKILL.md +59 -0
- package/skills/pikku-addon/SKILL.md +2 -2
- 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 +265 -0
- package/skills/pikku-auth/SKILL.md +89 -0
- package/skills/{pikku-better-auth/SKILL.md → pikku-auth/references/better-auth.md} +126 -34
- 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-auth/references/permissions.md +261 -0
- package/skills/{pikku-security/SKILL.md → pikku-auth/references/sessions.md} +0 -20
- package/skills/pikku-build/SKILL.md +88 -0
- package/skills/{pikku-build-app/SKILL.md → pikku-build/references/app.md} +76 -24
- package/skills/{pikku-feature/SKILL.md → pikku-build/references/feature.md} +1 -8
- package/skills/{pikku-build-app → pikku-build}/references/multi-app.md +56 -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} +4 -20
- package/skills/{pikku-build-app → pikku-build}/references/ship.md +9 -3
- package/skills/pikku-concepts/SKILL.md +72 -7
- 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 +47 -20
- package/skills/{pikku-fabric-debug/SKILL.md → pikku-fabric/references/debugging.md} +0 -6
- package/skills/pikku-i18n/SKILL.md +62 -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 +15 -0
- package/skills/pikku-kysely/SKILL.md +13 -13
- package/skills/pikku-list-query/SKILL.md +163 -0
- 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 +5 -5
- package/skills/pikku-n8n-import/SKILL.md +0 -1
- package/skills/pikku-permissions/SKILL.md +75 -229
- package/skills/pikku-react/SKILL.md +50 -298
- package/skills/pikku-react/references/client.md +313 -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-realtime/SKILL.md +110 -251
- package/skills/pikku-scenario/SKILL.md +60 -45
- package/skills/pikku-scenario/references/persona-run.md +148 -0
- package/skills/pikku-seo/SKILL.md +133 -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 +16 -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 +224 -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} +3 -39
- 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-wiring/references/realtime.md +265 -0
- 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 +39 -2
- 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
|
@@ -1,297 +1,74 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-services
|
|
3
3
|
description: >-
|
|
4
|
-
Use
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
asks about services.ts, lifecycle.ts, dependency
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
permissions (use pikku-
|
|
4
|
+
Use for the service layer of a Pikku app — dependency injection with pikkuServices and
|
|
5
|
+
pikkuWireServices, startup/shutdown work with pikkuServerLifecycle, configuration through the
|
|
6
|
+
secrets, variables and credentials services, the audit sink and buffer, and structured logging.
|
|
7
|
+
TRIGGER when: code uses pikkuServices/pikkuWireServices/pikkuServerLifecycle, defineSecret,
|
|
8
|
+
defineVariable or defineCredential, user asks about services.ts, lifecycle.ts, dependency
|
|
9
|
+
injection, env vars, secrets, API credentials, audit logging, AuditService, PinoLogger, or a
|
|
10
|
+
built-in service. DO NOT TRIGGER when: user asks about middleware (use pikku-middleware),
|
|
11
|
+
authentication or permissions (use pikku-auth), or a third-party backend such as Redis, S3 or
|
|
12
|
+
MongoDB (use pikku-service-backends).
|
|
12
13
|
installGroups: [core]
|
|
13
14
|
---
|
|
14
15
|
|
|
15
|
-
# Pikku Services
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
)
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### `pikkuServerLifecycle(hooks)` — startup and shutdown work
|
|
80
|
-
|
|
81
|
-
A service factory should **construct** services, not run startup side effects. Seeding a database, warming a cache, starting a background consumer or draining a queue belongs in lifecycle hooks, which receive the singleton services after they are built:
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
// src/lifecycle.ts
|
|
85
|
-
import { pikkuServerLifecycle } from '@pikku/core'
|
|
86
|
-
import type { SingletonServices } from '../types/application-types.js'
|
|
87
|
-
|
|
88
|
-
export const lifecycle = pikkuServerLifecycle<SingletonServices>({
|
|
89
|
-
beforeStart: async ({ kysely }) => {
|
|
90
|
-
await runMigrations(kysely) // before the port opens
|
|
91
|
-
},
|
|
92
|
-
afterStart: async (services) => {
|
|
93
|
-
await seedDevData(services) // server is accepting traffic
|
|
94
|
-
},
|
|
95
|
-
beforeStop: async ({ queueService }) => {
|
|
96
|
-
await queueService.drain() // services are still alive here
|
|
97
|
-
},
|
|
98
|
-
afterStop: async () => {
|
|
99
|
-
await releaseExternalLock() // services are ALREADY stopped
|
|
100
|
-
},
|
|
101
|
-
})
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Every hook is optional. Order is `beforeStart` → server starts → `afterStart`, then on SIGINT `beforeStop` → services stopped → server stopped → `afterStop`.
|
|
105
|
-
|
|
106
|
-
**`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`.
|
|
107
|
-
|
|
108
|
-
Export **exactly one** `pikkuServerLifecycle` from anywhere in `srcDirectories`; the inspector finds it by the wrapper call, so the filename is free (`src/lifecycle.ts` by convention). It must be an exported `const` initialized with a direct call to `pikkuServerLifecycle` — a re-export or a conditional wrapper is invisible to the inspector.
|
|
109
|
-
|
|
110
|
-
**Only `pikku dev` and `pikku serve` run these hooks.** If you bootstrap your own server (Express, Fastify, uWS, Lambda, Cloudflare, Next.js), no runtime adapter invokes them — put the work in your entrypoint instead.
|
|
111
|
-
|
|
112
|
-
### Auto-Generated Service Manifest
|
|
113
|
-
|
|
114
|
-
After `npx pikku all`, Pikku generates `.pikku/pikku-services.gen.ts`, a manifest of which services are actually used by wired functions:
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
export const requiredSingletonServices = {
|
|
118
|
-
database: true, // used by getUser, deleteUser
|
|
119
|
-
audit: true, // used by deleteUser
|
|
120
|
-
cache: false, // not used by any wired function
|
|
121
|
-
jwt: true, // used by auth middleware
|
|
122
|
-
} as const
|
|
123
|
-
|
|
124
|
-
export type RequiredSingletonServices = Pick<
|
|
125
|
-
SingletonServices,
|
|
126
|
-
'database' | 'audit' | 'jwt'
|
|
127
|
-
> &
|
|
128
|
-
Partial<Omit<SingletonServices, 'database' | 'audit' | 'jwt'>>
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## Usage Patterns
|
|
132
|
-
|
|
133
|
-
### Using Services in Functions
|
|
134
|
-
|
|
135
|
-
**Every service must be declared in `SingletonServices` (or `Services`) in `application-types.d.ts`.** Never access a service via a body-level cast (`services as typeof services & { myService: MyService }`) — that means the type is missing. Add the import and the field to `SingletonServices`, then destructure inline in the function signature. The inspector emits `SERVICES_NOT_DESTRUCTURED` (`PKU410`) and tree-shaking breaks when the first param is a plain identifier rather than an object pattern. Never `new` a service inside a function — services arrive only via injection.
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
// ✅ Correct — inline destructure, no cast
|
|
139
|
-
const getUser = pikkuFunc({
|
|
140
|
-
title: 'Get User',
|
|
141
|
-
func: async ({ db, logger, jwt }, { userId }) => {
|
|
142
|
-
logger.info('Fetching user', { userId })
|
|
143
|
-
return { user: await db.getUser(userId) }
|
|
144
|
-
},
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
// ❌ Wrong — named param + body cast; inspector warns + tree-shaking breaks
|
|
148
|
-
const getUser = pikkuFunc({
|
|
149
|
-
func: async (services, { userId }) => {
|
|
150
|
-
const { db } = services as typeof services & { db: DbService }
|
|
151
|
-
// ...
|
|
152
|
-
},
|
|
153
|
-
})
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### Services Are Never Optional Inside a Function
|
|
157
|
-
|
|
158
|
-
**Never write a `if (!service) throw ...` existence guard in a function body.** It is dead code, and it defeats the platform.
|
|
159
|
-
|
|
160
|
-
Optionality lives in exactly one place — `services.ts` / the `SingletonServices` declaration — and it means _"this may not be created"_, not _"this may be missing at call time"_. A service is optional precisely because **nothing destructures it**, and the generated `requiredSingletonServices` manifest therefore never marks it for creation. The moment any wired function destructures it, Pikku creates it and guarantees it is there.
|
|
161
|
-
|
|
162
|
-
The types enforce this rather than merely documenting it. The inspector records the services destructured by every wired `func`, `permissions` **and** `middleware`, and emits them as `RequiredSingletonServices`. The generated function types then default their service parameter to:
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
export type WiredSingletonServices = RequiredSingletonServices &
|
|
166
|
-
SingletonServices
|
|
167
|
-
export type WiredServices = SecretlessServices<
|
|
168
|
-
RequiredSingletonServices & Services
|
|
169
|
-
>
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
The `SecretlessServices<...>` wrapper is why `secrets` never appears in a
|
|
173
|
-
function's services: it is stripped at the type level, not merely omitted by
|
|
174
|
-
convention. Read secrets in a service factory or middleware and hand the value
|
|
175
|
-
to a service instead.
|
|
176
|
-
|
|
177
|
-
so a service that is `foo?: Foo` in `SingletonServices` arrives as a non-optional `Foo` in every function, permission and middleware that uses it. There is nothing to guard against.
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
// ✅ Correct — destructure and use; creation is guaranteed by the manifest
|
|
181
|
-
const listThreads = pikkuFunc({
|
|
182
|
-
func: async ({ agentRunService }, { threadId }) => {
|
|
183
|
-
return await agentRunService.getThreadMessages(threadId)
|
|
184
|
-
},
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
// ❌ Wrong — unreachable guard; signals a misunderstanding of service wiring
|
|
188
|
-
const listThreads = pikkuFunc({
|
|
189
|
-
func: async ({ agentRunService }, { threadId }) => {
|
|
190
|
-
if (!agentRunService) throw new MissingServiceError('agentRunService')
|
|
191
|
-
return await agentRunService.getThreadMessages(threadId)
|
|
192
|
-
},
|
|
193
|
-
})
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
If a service really is conditional at runtime (e.g. an optional integration a deployment may not configure), that is a **configuration** concern: branch on config, or fail fast at startup in `services.ts` — not per-request in every function.
|
|
197
|
-
|
|
198
|
-
### Dynamic Import Optimization
|
|
199
|
-
|
|
200
|
-
Use the generated manifest to conditionally import heavy dependencies — only the services actually wired get instantiated:
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
import { requiredSingletonServices } from '.pikku/pikku-services.gen.js'
|
|
204
|
-
|
|
205
|
-
const createSingletonServices = pikkuServices(async (config) => {
|
|
206
|
-
const logger = new ConsoleLogger()
|
|
207
|
-
|
|
208
|
-
let jwt: JWTService | undefined
|
|
209
|
-
if (requiredSingletonServices.jwt) {
|
|
210
|
-
const { JoseJWTService } = await import('@pikku/jose')
|
|
211
|
-
jwt = new JoseJWTService(keys, logger)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
let database: Database | undefined
|
|
215
|
-
if (requiredSingletonServices.database) {
|
|
216
|
-
database = await createDatabase(config.databaseUrl)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return { config, logger, jwt, database }
|
|
220
|
-
})
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### Audit Wire Service
|
|
224
|
-
|
|
225
|
-
`createInvocationAudit` + `createAuditedKysely` add per-request audit buffering that flushes on request close (no-op if `audit` is unconfigured). For the full pattern, no-op behavior, custom-event usage, and Fabric notes, read `references/audit-wire-service.md`.
|
|
226
|
-
|
|
227
|
-
### Built-in Services
|
|
228
|
-
|
|
229
|
-
| Service | Package | Purpose |
|
|
230
|
-
| ----------------------- | ---------------------- | --------------------------------------- |
|
|
231
|
-
| `ConsoleLogger` | `@pikku/core/services` | Console-based logging |
|
|
232
|
-
| `JoseJWTService` | `@pikku/jose` | JWT sign/verify via jose |
|
|
233
|
-
| `LocalSecretService` | `@pikku/core/services` | Local development secrets |
|
|
234
|
-
| `LocalVariablesService` | `@pikku/core/services` | Local environment variables |
|
|
235
|
-
| `PinoLogger` | `@pikku/pino` | Structured logging via Pino |
|
|
236
|
-
| `createInvocationAudit` | `@pikku/core/services` | Per-request audit buffer |
|
|
237
|
-
| `createAuditedKysely` | `@pikku/kysely` | Auto-capture DB queries as audit events |
|
|
238
|
-
|
|
239
|
-
## Complete Example
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
// services.ts
|
|
243
|
-
import { pikkuServices, pikkuWireServices } from '#pikku/setup'
|
|
244
|
-
import { ConsoleLogger } from '@pikku/core/services'
|
|
245
|
-
import { JoseJWTService } from '@pikku/jose'
|
|
246
|
-
|
|
247
|
-
// Custom service
|
|
248
|
-
class TodoStore {
|
|
249
|
-
private todos: Map<string, Todo> = new Map()
|
|
250
|
-
async create(title: string, priority: string) {
|
|
251
|
-
const todo = { id: crypto.randomUUID(), title, priority, completed: false }
|
|
252
|
-
this.todos.set(todo.id, todo)
|
|
253
|
-
return todo
|
|
254
|
-
}
|
|
255
|
-
async get(id: string) {
|
|
256
|
-
return this.todos.get(id)
|
|
257
|
-
}
|
|
258
|
-
async list() {
|
|
259
|
-
return [...this.todos.values()]
|
|
260
|
-
}
|
|
261
|
-
async delete(id: string) {
|
|
262
|
-
this.todos.delete(id)
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
export const createSingletonServices = pikkuServices(async (config) => {
|
|
267
|
-
const logger = new ConsoleLogger()
|
|
268
|
-
const jwt = new JoseJWTService(
|
|
269
|
-
async () => [{ id: 'my-key', value: config.jwtSecret }],
|
|
270
|
-
logger
|
|
271
|
-
)
|
|
272
|
-
return {
|
|
273
|
-
config,
|
|
274
|
-
logger,
|
|
275
|
-
jwt,
|
|
276
|
-
secrets: new LocalSecretService(),
|
|
277
|
-
variables: new LocalVariablesService(),
|
|
278
|
-
todoStore: new TodoStore(),
|
|
279
|
-
}
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
export const createWireServices = pikkuWireServices(
|
|
283
|
-
async (singletonServices, wire) => ({
|
|
284
|
-
scopedLogger: new ScopedLogger(wire.session?.userId),
|
|
285
|
-
})
|
|
286
|
-
)
|
|
287
|
-
|
|
288
|
-
// functions/todos.functions.ts — services are auto-injected
|
|
289
|
-
export const createTodo = pikkuFunc({
|
|
290
|
-
title: 'Create Todo',
|
|
291
|
-
func: async ({ todoStore, logger }, { title, priority }) => {
|
|
292
|
-
const todo = await todoStore.create(title, priority)
|
|
293
|
-
logger.info('Created todo', { id: todo.id })
|
|
294
|
-
return { todo }
|
|
295
|
-
},
|
|
296
|
-
})
|
|
297
|
-
```
|
|
16
|
+
# Pikku Services
|
|
17
|
+
|
|
18
|
+
Signatures and option keys come from `pikku doc` — run `pikku doc --ai` for the
|
|
19
|
+
installed surface. This skill is the part the compiler cannot tell you: where a
|
|
20
|
+
value is allowed to be read, and what a service's lifetime commits you to.
|
|
21
|
+
|
|
22
|
+
## Two lifetimes, and that is the whole model
|
|
23
|
+
|
|
24
|
+
**Singleton services** (`pikkuServices`) are built once at startup and live for
|
|
25
|
+
the process. **Wire services** (`pikkuWireServices`) are built fresh per HTTP
|
|
26
|
+
request, queue job, channel message or CLI command, and receive the wire.
|
|
27
|
+
|
|
28
|
+
Everything else follows from that split: a database pool is a singleton, a
|
|
29
|
+
request-scoped logger or audit buffer is a wire service, and startup work that
|
|
30
|
+
needs the singletons goes in `pikkuServerLifecycle` rather than in a module's
|
|
31
|
+
top level.
|
|
32
|
+
|
|
33
|
+
## Pick the reference
|
|
34
|
+
|
|
35
|
+
| You are… | Read |
|
|
36
|
+
| ---------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
37
|
+
| Writing or wiring `services.ts` / `lifecycle.ts`, or adding a custom service | `references/services.md` |
|
|
38
|
+
| Reading config — secrets, env vars, or a per-user API credential | `references/config.md` |
|
|
39
|
+
| Recording audit events, or choosing a sink | `references/audit.md` and `references/audit-wire-service.md` |
|
|
40
|
+
| Setting up structured logging | `references/pino.md` |
|
|
41
|
+
| Reaching for Redis, S3, SQS, MongoDB or a schema backend | `pikku-service-backends` |
|
|
42
|
+
| Sending outgoing webhooks to a customer's endpoint | `pikku-webhook` |
|
|
43
|
+
|
|
44
|
+
## Where a value may be read
|
|
45
|
+
|
|
46
|
+
- **Never `process.env` inside a Pikku function.** Use `services.variables.get()`
|
|
47
|
+
or `services.secrets`. `process.env` belongs to server bootstrap (`start.ts`)
|
|
48
|
+
— and under `pikku dev` / `pikku serve` there is no `start.ts` at all, so
|
|
49
|
+
startup work goes in a `pikkuServerLifecycle` hook, which receives the
|
|
50
|
+
singletons and reads through `variables` too.
|
|
51
|
+
- **`secrets` never reaches a function.** `WiredServices` is wrapped in
|
|
52
|
+
`SecretlessServices<…>`, so it is stripped at the type level rather than merely
|
|
53
|
+
omitted by convention. Read a secret in a service factory or middleware and
|
|
54
|
+
hand the resulting service the value.
|
|
55
|
+
|
|
56
|
+
## What NOT to do
|
|
57
|
+
|
|
58
|
+
- **Do not guard a service's existence in a function body.** `if (!db) throw …`
|
|
59
|
+
is dead code. Optionality lives only in the `SingletonServices` declaration and
|
|
60
|
+
means "may not be created" — a service is optional precisely because nothing
|
|
61
|
+
destructures it. The inspector records every service destructured by a wired
|
|
62
|
+
`func`, `permissions` or `middleware` and marks it required, so inside the
|
|
63
|
+
function it is a non-optional value. A genuinely conditional integration is a
|
|
64
|
+
configuration concern: branch in `services.ts` or fail fast at startup.
|
|
65
|
+
- **Do not take `services` as a named parameter and cast in the body.** The
|
|
66
|
+
inspector reads the destructuring pattern to build the manifest; a cast makes
|
|
67
|
+
the service invisible to it, so tree-shaking drops what the function needs.
|
|
68
|
+
- **Do not hand-roll an audit table.** `auditLog.write()` on an `audit: true`
|
|
69
|
+
function enriches the event with the function id, wire type, trace id and user
|
|
70
|
+
identity; an `insertInto('audit_log')` of your own gets none of that, and a
|
|
71
|
+
write inside a transaction that later rolls back records nothing.
|
|
72
|
+
- **Do not log an unrevealed secret.** Every logger argument is `Safe<>`-guarded,
|
|
73
|
+
so a `SecretValue` nested anywhere in the call collapses it to `never` and it
|
|
74
|
+
stops compiling. That is deliberate — it would have printed `[secret]` anyway.
|
|
@@ -1,27 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-audit
|
|
3
|
-
description: >-
|
|
4
|
-
Use when adding audit / activity-history / change-tracking to a Pikku app, or when a function
|
|
5
|
-
needs to record who changed what. Covers the built-in AuditService sink, the per-invocation
|
|
6
|
-
auditLog buffer (createInvocationAudit / pikkuWireServices), the `audit: true` function flag,
|
|
7
|
-
explicit `auditLog.write()` domain events, automatic query-level capture via
|
|
8
|
-
createAuditedKysely, and the durable KyselyAuditService sink. TRIGGER when: user asks for an
|
|
9
|
-
audit log, change history, activity feed, "who did this", or a custom audit/history table; code
|
|
10
|
-
uses auditLog, createInvocationAudit, createAuditedKysely, or AuditService. DO NOT TRIGGER when:
|
|
11
|
-
user wants app logging/telemetry (use the logger) or DB migrations in general (use
|
|
12
|
-
pikku-kysely).
|
|
13
|
-
---
|
|
14
|
-
|
|
15
1
|
# Pikku Audit
|
|
16
2
|
|
|
17
|
-
## Agent Operating Procedure
|
|
18
|
-
|
|
19
|
-
Use this skill as an execution checklist, not reference material.
|
|
20
|
-
|
|
21
|
-
1. Discover before editing. Check how services are wired (`services.ts`) and whether an `audit` table migration exists before adding audit calls.
|
|
22
|
-
2. NEVER hand-roll a custom `audit_log` / history table with direct `insertInto('audit_log')` calls. The framework owns audit. A bespoke table drifts from the runtime (missing user/trace/wire context, hand-written CHECK constraints that reject valid events, no prod sink). Use the built-in path below.
|
|
23
|
-
3. Make the smallest source change: mark the function `audit: true`, inject `auditLog`, call `auditLog.write(...)`. Do not invent a new service.
|
|
24
|
-
4. Validate with `pikku all` (regenerates the service flags) then run the app / e2e.
|
|
25
3
|
|
|
26
4
|
## Mental model — two layers
|
|
27
5
|
|
|
@@ -1,29 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-config
|
|
3
|
-
description: >-
|
|
4
|
-
Use when managing secrets, environment variables, config, or OAuth2 credentials in a Pikku app.
|
|
5
|
-
Covers defineSecret, defineVariable, defineCredential, and typed config access. TRIGGER when:
|
|
6
|
-
code uses defineSecret/defineVariable/defineCredential, user asks about env vars, secrets,
|
|
7
|
-
config, OAuth2, SecretValue/.reveal(), SecretCoercionError, or "how do I access environment
|
|
8
|
-
variables". DO NOT TRIGGER when: user asks about API versioning/breaking changes (use
|
|
9
|
-
pikku-versioning), service factories (use pikku-services), middleware (use pikku-middleware), or
|
|
10
|
-
auth strategies and sessions (use pikku-security).
|
|
11
|
-
installGroups: [core]
|
|
12
|
-
---
|
|
13
|
-
|
|
14
1
|
# Pikku Config, Secrets & OAuth2
|
|
15
2
|
|
|
16
|
-
## Agent Operating Procedure
|
|
17
|
-
|
|
18
|
-
Use this skill as an execution checklist, not reference material.
|
|
19
|
-
|
|
20
|
-
1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
|
|
21
|
-
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.
|
|
22
|
-
3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
|
|
23
|
-
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.
|
|
24
|
-
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
25
|
-
|
|
26
|
-
Manage secrets, variables, and OAuth2 credentials. Never use `process.env` in Pikku functions — use typed services instead.
|
|
27
3
|
|
|
28
4
|
## Before You Start
|
|
29
5
|
|
|
@@ -228,7 +204,7 @@ const apiKey = process.env.API_KEY
|
|
|
228
204
|
const apiKey = services.variables.get('API_KEY')
|
|
229
205
|
```
|
|
230
206
|
|
|
231
|
-
`process.env` belongs only in server bootstrap code (`start.ts`). Under `pikku dev` / `pikku serve` there is no `start.ts` — startup work goes in a `pikkuServerLifecycle` export, and the hooks receive the singleton services, so read configuration through `variables` there too (see
|
|
207
|
+
`process.env` belongs only in server bootstrap code (`start.ts`). Under `pikku dev` / `pikku serve` there is no `start.ts` — startup work goes in a `pikkuServerLifecycle` export, and the hooks receive the singleton services, so read configuration through `variables` there too (see `references/services.md`).
|
|
232
208
|
|
|
233
209
|
### Lint rules
|
|
234
210
|
|
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-pino
|
|
3
|
-
description: >-
|
|
4
|
-
Use when setting up structured logging with Pino in a Pikku app. Covers PinoLogger setup and log
|
|
5
|
-
levels. TRIGGER when: code uses PinoLogger, user asks about structured logging, Pino, or
|
|
6
|
-
@pikku/pino. DO NOT TRIGGER when: user asks about ConsoleLogger (use pikku-services) or general
|
|
7
|
-
service setup.
|
|
8
|
-
---
|
|
9
|
-
|
|
10
1
|
# Pikku Pino (Structured Logging)
|
|
11
2
|
|
|
12
|
-
## Agent Operating Procedure
|
|
13
|
-
|
|
14
|
-
Use this skill as an execution checklist, not reference material.
|
|
15
|
-
|
|
16
|
-
1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
|
|
17
|
-
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.
|
|
18
|
-
3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
|
|
19
|
-
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.
|
|
20
|
-
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
21
|
-
|
|
22
|
-
`@pikku/pino` provides structured JSON logging via [Pino](https://getpino.io/). Implements the `Logger` interface from `@pikku/core`.
|
|
23
3
|
|
|
24
4
|
## Installation
|
|
25
5
|
|