@pikku/skills 0.12.22 → 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.
Files changed (101) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/dist/skills.gen.js +1 -1
  3. package/package.json +1 -1
  4. package/skills/pikku-addon/SKILL.md +2 -2
  5. package/skills/pikku-agent/SKILL.md +67 -316
  6. package/skills/pikku-agent/references/agents.md +299 -0
  7. package/skills/{pikku-ai-vercel/SKILL.md → pikku-agent/references/runner-vercel.md} +2 -24
  8. package/skills/{pikku-ai-voice/SKILL.md → pikku-agent/references/voice.md} +1 -22
  9. package/skills/pikku-architect/SKILL.md +264 -0
  10. package/skills/pikku-auth/SKILL.md +89 -0
  11. package/skills/{pikku-better-auth/SKILL.md → pikku-auth/references/better-auth.md} +1 -27
  12. package/skills/{pikku-jose/SKILL.md → pikku-auth/references/jose.md} +1 -23
  13. package/skills/{pikku-machine-auth/SKILL.md → pikku-auth/references/machine-auth.md} +0 -23
  14. package/skills/{pikku-permissions/SKILL.md → pikku-auth/references/permissions.md} +1 -20
  15. package/skills/{pikku-security/SKILL.md → pikku-auth/references/sessions.md} +0 -20
  16. package/skills/pikku-build/SKILL.md +87 -0
  17. package/skills/{pikku-build-app/SKILL.md → pikku-build/references/app.md} +75 -23
  18. package/skills/{pikku-feature/SKILL.md → pikku-build/references/feature.md} +1 -8
  19. package/skills/{pikku-build-app → pikku-build}/references/multi-app.md +1 -1
  20. package/skills/{pikku-build-platform/SKILL.md → pikku-build/references/platform.md} +22 -37
  21. package/skills/{pikku-template-clone/SKILL.md → pikku-build/references/post-clone.md} +0 -6
  22. package/skills/{pikku-build-quick/SKILL.md → pikku-build/references/quick.md} +4 -20
  23. package/skills/{pikku-build-app → pikku-build}/references/ship.md +7 -1
  24. package/skills/pikku-concepts/SKILL.md +72 -7
  25. package/skills/pikku-concepts/references/concept-mapping.md +8 -8
  26. package/skills/pikku-deploy/SKILL.md +158 -0
  27. package/skills/{pikku-deploy-azure/SKILL.md → pikku-deploy/references/azure.md} +18 -50
  28. package/skills/pikku-deploy/references/cloudflare.md +104 -0
  29. package/skills/pikku-deploy/references/express.md +92 -0
  30. package/skills/{pikku-deploy-fastify/SKILL.md → pikku-deploy/references/fastify.md} +8 -32
  31. package/skills/{pikku-deploy-lambda/SKILL.md → pikku-deploy/references/lambda.md} +6 -27
  32. package/skills/{pikku-deploy-nextjs/SKILL.md → pikku-deploy/references/nextjs.md} +9 -33
  33. package/skills/pikku-deploy/references/uws.md +72 -0
  34. package/skills/pikku-deploy/references/ws.md +75 -0
  35. package/skills/pikku-emails/SKILL.md +3 -2
  36. package/skills/pikku-fabric/SKILL.md +12 -2
  37. package/skills/{pikku-fabric-debug/SKILL.md → pikku-fabric/references/debugging.md} +0 -6
  38. package/skills/pikku-i18n/SKILL.md +60 -207
  39. package/skills/{pikku-paraglide/SKILL.md → pikku-i18n/references/enum-labels.md} +0 -6
  40. package/skills/pikku-i18n/references/messages.md +218 -0
  41. package/skills/{pikku-rtl/SKILL.md → pikku-i18n/references/rtl.md} +3 -9
  42. package/skills/pikku-knowledge/SKILL.md +14 -0
  43. package/skills/pikku-kysely/SKILL.md +13 -13
  44. package/skills/pikku-meta/SKILL.md +58 -130
  45. package/skills/{pikku-deps/SKILL.md → pikku-meta/references/audit.md} +1 -17
  46. package/skills/pikku-meta/references/meta.md +114 -0
  47. package/skills/{pikku-versioning/SKILL.md → pikku-meta/references/versioning.md} +0 -26
  48. package/skills/pikku-middleware/SKILL.md +5 -5
  49. package/skills/pikku-n8n-import/SKILL.md +0 -1
  50. package/skills/pikku-react/SKILL.md +50 -298
  51. package/skills/pikku-react/references/client.md +293 -0
  52. package/skills/{pikku-react-query/SKILL.md → pikku-react/references/react-query.md} +2 -22
  53. package/skills/{pikku-workflows-client/SKILL.md → pikku-react/references/workflows.md} +1 -22
  54. package/skills/pikku-scenario/SKILL.md +60 -45
  55. package/skills/pikku-scenario/references/persona-run.md +148 -0
  56. package/skills/pikku-service-backends/SKILL.md +154 -0
  57. package/skills/pikku-service-backends/references/aws.md +106 -0
  58. package/skills/pikku-service-backends/references/backblaze.md +57 -0
  59. package/skills/pikku-service-backends/references/mongodb.md +90 -0
  60. package/skills/pikku-service-backends/references/redis.md +75 -0
  61. package/skills/pikku-service-backends/references/schema.md +63 -0
  62. package/skills/pikku-services/SKILL.md +68 -291
  63. package/skills/{pikku-audit/SKILL.md → pikku-services/references/audit.md} +0 -22
  64. package/skills/{pikku-config/SKILL.md → pikku-services/references/config.md} +1 -25
  65. package/skills/{pikku-pino/SKILL.md → pikku-services/references/pino.md} +0 -20
  66. package/skills/pikku-services/references/services.md +272 -0
  67. package/skills/pikku-software-archaeology/README.md +5 -1
  68. package/skills/pikku-software-archaeology/SKILL.md +15 -2
  69. package/skills/{pikku-product-second-opinion/example/sample-report.md → pikku-software-archaeology/example/second-opinion-sample-report.md} +1 -1
  70. package/skills/pikku-software-archaeology/references/blueprint.schema.json +1 -1
  71. package/skills/pikku-software-archaeology/references/pikku-mapping.md +3 -3
  72. package/skills/{pikku-product-second-opinion/SKILL.md → pikku-software-archaeology/references/second-opinion.md} +4 -9
  73. package/skills/pikku-webhook/SKILL.md +199 -0
  74. package/skills/pikku-wiring/SKILL.md +180 -0
  75. package/skills/{pikku-websocket/SKILL.md → pikku-wiring/references/channel.md} +2 -35
  76. package/skills/{pikku-cli/SKILL.md → pikku-wiring/references/cli.md} +1 -33
  77. package/skills/{pikku-gateway-slack/SKILL.md → pikku-wiring/references/gateway-slack.md} +0 -23
  78. package/skills/{pikku-http/SKILL.md → pikku-wiring/references/http.md} +3 -39
  79. package/skills/{pikku-mcp/SKILL.md → pikku-wiring/references/mcp.md} +0 -33
  80. package/skills/{pikku-queue/SKILL.md → pikku-wiring/references/queue.md} +1 -33
  81. package/skills/{pikku-realtime/SKILL.md → pikku-wiring/references/realtime.md} +2 -25
  82. package/skills/{pikku-rpc/SKILL.md → pikku-wiring/references/rpc.md} +0 -32
  83. package/skills/{pikku-schedule/SKILL.md → pikku-wiring/references/scheduler.md} +1 -35
  84. package/skills/{pikku-trigger/SKILL.md → pikku-wiring/references/trigger.md} +0 -43
  85. package/skills/pikku-workflow/SKILL.md +2 -2
  86. package/skills/pikku-aws/SKILL.md +0 -161
  87. package/skills/pikku-backblaze/SKILL.md +0 -104
  88. package/skills/pikku-deploy-cloudflare/SKILL.md +0 -123
  89. package/skills/pikku-deploy-express/SKILL.md +0 -122
  90. package/skills/pikku-deploy-uws/SKILL.md +0 -144
  91. package/skills/pikku-mongodb/SKILL.md +0 -113
  92. package/skills/pikku-product-second-opinion/README.md +0 -43
  93. package/skills/pikku-redis/SKILL.md +0 -99
  94. package/skills/pikku-schema-ajv/SKILL.md +0 -83
  95. package/skills/pikku-schema-cfworker/SKILL.md +0 -82
  96. package/skills/pikku-ws/SKILL.md +0 -87
  97. /package/skills/{pikku-build-app → pikku-build}/references/theming.md +0 -0
  98. /package/skills/{pikku-product-second-opinion/references/report-template.md → pikku-software-archaeology/references/second-opinion-report-template.md} +0 -0
  99. /package/skills/{pikku-cli/references/complete-example.md → pikku-wiring/references/cli-complete-example.md} +0 -0
  100. /package/skills/{pikku-http → pikku-wiring}/references/http-options.md +0 -0
  101. /package/skills/{pikku-realtime/references/other-routes.md → pikku-wiring/references/realtime-other-routes.md} +0 -0
@@ -1,57 +1,25 @@
1
- ---
2
- name: pikku-deploy-azure
3
- description: >-
4
- Use when deploying a Pikku app to Azure Functions. Covers createAzureHandler for HTTP, storage
5
- queue and timer triggers, plus AzInvocationLogger and PikkuAZTimerRequest. TRIGGER when: user
6
- asks about Azure Functions, Azure deployment, or @pikku/azure-functions. DO NOT TRIGGER when:
7
- user asks about AWS Lambda (use pikku-deploy-lambda) or Cloudflare Workers (use
8
- pikku-deploy-cloudflare).
9
- ---
10
-
11
- # Pikku Azure Functions Deployment
12
-
13
- ## Agent Operating Procedure
14
-
15
- Use this skill as an execution checklist, not reference material.
16
-
17
- 1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
18
- 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.
19
- 3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
20
- 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.
21
- 5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
22
-
23
- `@pikku/azure-functions` provides Azure Functions runtime adapters for Pikku.
24
-
25
- ## Installation
1
+ # Azure Functions
26
2
 
27
3
  ```bash
28
4
  yarn add @pikku/azure-functions @azure/functions
29
5
  ```
30
6
 
31
- ## API Reference
32
-
33
- Exported from `@pikku/azure-functions`:
34
-
35
- - `createAzureHandler(factories, handlerTypes)` — the entry point. Returns
36
- `{ http?, queue?, timer? }` for the handler types you ask for.
37
- - `createAzureWorkerHandler(factories)` — `createAzureHandler(factories, ['fetch'])`.
38
- - `createAzureWebSocketHandler(factories)` — **a stub**: its `negotiate` always
39
- answers `501 WebSocket via Azure Web PubSub not yet implemented`. Channels do
40
- not work on Azure yet; do not plan a deployment around it.
41
- - `AzInvocationLogger` — the logger. Note the name: there is no
42
- `PikkuAzFunctionsLogger`.
43
- - `PikkuAZTimerRequest` — `new PikkuAZTimerRequest(context, data)`, a
44
- `PikkuRequest` carrying the data. The context argument is accepted and
45
- ignored.
46
- - `AzureQueueService`, `AzureDeploymentService`.
7
+ `createAzureHandler(factories, handlerTypes)` is the entry point, returning
8
+ `{ http?, queue?, timer? }` for the handler types you ask for.
9
+ `createAzureWorkerHandler(factories)` is `createAzureHandler(factories,
10
+ ['fetch'])`. `factories` is `{ createConfig, createSingletonServices,
11
+ createPlatformServices? }`; services are built from `process.env` and cached in
12
+ module scope across invocations of the same instance.
47
13
 
48
- `factories` is `{ createConfig, createSingletonServices, createPlatformServices? }`.
49
- Services are built from `process.env` and cached in module scope across
50
- invocations of the same instance.
14
+ **Channels do not work on Azure.** `createAzureWebSocketHandler` is a stub whose
15
+ `negotiate` always answers `501 WebSocket via Azure Web PubSub not yet
16
+ implemented` — do not plan a deployment around it.
51
17
 
52
- ## Usage Patterns
18
+ Two naming traps: the logger is `AzInvocationLogger`, not
19
+ `PikkuAzFunctionsLogger`; and `PikkuAZTimerRequest(context, data)` accepts the
20
+ context argument and ignores it.
53
21
 
54
- ### Registering handlers
22
+ ## Registering handlers
55
23
 
56
24
  ```typescript
57
25
  import { app } from '@azure/functions'
@@ -86,7 +54,7 @@ app.timer('scheduler', {
86
54
  Note the key names: `handlerTypes` uses **`scheduled`**, but the handler it
87
55
  returns is **`timer`**.
88
56
 
89
- ### HTTP
57
+ ## HTTP
90
58
 
91
59
  The handler buffers the whole body, converts to a standard `Request`, and
92
60
  returns the response body as **text** — a streaming or binary response is
@@ -95,7 +63,7 @@ flattened. It takes no `RunHTTPWiringOptions`, so there is no `maxBodySize` or
95
63
  is logged to `console.error` and whatever the response already holds is
96
64
  returned.
97
65
 
98
- ### Queue
66
+ ## Queue
99
67
 
100
68
  The queue name comes from the message's own `queueName`, falling back to
101
69
  `context.triggerMetadata.queueTrigger` and then `'unknown'` — a name that does
@@ -112,7 +80,7 @@ base64-encoded (Azure requires it), `delay` is milliseconds mapped to
112
80
  `AZURE_QUEUE_NAME_<SCREAMING_SNAKE>` when that variable exists, otherwise used
113
81
  as-is.
114
82
 
115
- ### Timer
83
+ ## Timer
116
84
 
117
85
  The timer handler runs **every** scheduled task registered in the bundle,
118
86
  ignoring both the `Timer` argument and each task's own cron expression. Unlike
@@ -120,7 +88,7 @@ the Lambda equivalent it does not catch per-task failures, so the first task
120
88
  that throws aborts the ones after it — keep one schedule per function app, or
121
89
  guard the task bodies yourself.
122
90
 
123
- ### Logging
91
+ ## Logging
124
92
 
125
93
  `new AzInvocationLogger(context)` forwards to the invocation context's
126
94
  `info`/`warn`/`error`/`debug`/`trace`. `setLevel()` is a **no-op**: every level
@@ -0,0 +1,104 @@
1
+ # Cloudflare Workers
2
+
3
+ ```bash
4
+ yarn add @pikku/cloudflare
5
+ ```
6
+
7
+ ## Worker entry
8
+
9
+ `@pikku/cloudflare` ships the handler factories the deploy codegen emits — use
10
+ them rather than hand-rolling an `ExportedHandler`. Each returns a
11
+ `WorkerEntrypoint` class that sets services up on every invocation (cached after
12
+ the first) and adds an RPC-callable `runRpc(name, args)`:
13
+
14
+ ```typescript
15
+ import { createCloudflareHandler } from '@pikku/cloudflare'
16
+ import { createConfig, createSingletonServices } from './services.js'
17
+ import './.pikku/pikku-bootstrap.gen.js'
18
+
19
+ export default createCloudflareHandler(
20
+ { createConfig, createSingletonServices },
21
+ ['fetch', 'scheduled']
22
+ )
23
+ ```
24
+
25
+ | Factory | For |
26
+ | --- | --- |
27
+ | `createCloudflareHandler(factories, handlerTypes)` | combined `fetch`/`queue`/`scheduled` |
28
+ | `createCloudflareWorkerHandler(factories)` | HTTP / agent / RPC / workflow-orchestrator units |
29
+ | `createCloudflareCronHandler(factories)` | cron units |
30
+ | `createCloudflareQueueHandler(factories)` | queue-consumer and workflow-step units |
31
+ | `createCloudflareMCPHandler(factories)` | MCP units (same HTTP transport) |
32
+ | `createCloudflareWebSocketHandler(factories)` | channel units, delegating to the DO |
33
+
34
+ `factories` is `{ createConfig, createSingletonServices, createPlatformServices? }`.
35
+
36
+ ## Service setup — do not hand-roll this
37
+
38
+ Cloudflare passes env bindings per-request, so services are built from `env`
39
+ rather than at module load. `setupServices(env, factories)` is exported from
40
+ `@pikku/cloudflare` and is what the factories call:
41
+
42
+ ```typescript
43
+ import { setupServices } from '@pikku/cloudflare'
44
+
45
+ const services = await setupServices(env, {
46
+ createConfig,
47
+ createSingletonServices,
48
+ })
49
+ ```
50
+
51
+ Beyond building `LocalVariablesService` / `LocalSecretService` and caching the
52
+ result, it calls `setSingletonServices()` — and the core runners (`fetchData`,
53
+ `runQueueJob`, `runScheduled`) resolve services through that global slot, _not_
54
+ through the value you were returned. A setup function that only returns the
55
+ services leaves every request throwing "Singleton services not initialized" as a
56
+ CF `1101`. It also stashes the env via `setCloudflareEnv`, which
57
+ `getCloudflareEnv()` reads for bindings.
58
+
59
+ ## HTTP
60
+
61
+ `runFetch(request, websocketHibernationServer?, options?)`:
62
+
63
+ - A `GET` with `Upgrade: websocket` is routed to the hibernation server. Without
64
+ one passed in it answers **426**, so a channel worker that forgets the second
65
+ argument fails every upgrade while plain HTTP keeps working.
66
+ - `CF-Ray` becomes the traceId when present, so a Cloudflare trace and a Pikku
67
+ trace line up without extra wiring.
68
+ - `options.exposeErrors` defaults to **`false`** — error detail is withheld from
69
+ responses unless you opt in.
70
+
71
+ ## Scheduled tasks
72
+
73
+ `runScheduled(controller)` matches registered tasks against `controller.cron`
74
+ and **returns after the first match**. Two tasks sharing one cron expression
75
+ means only one of them ever runs — give each its own expression, or invoke
76
+ `runScheduledTask({ name })` per task yourself.
77
+
78
+ ## WebSocket (Durable Objects)
79
+
80
+ The ready-made DO class is exported; re-export it under the binding name and
81
+ point the worker at it:
82
+
83
+ ```typescript
84
+ export { PikkuWebSocketHibernationServer as WebSocketHibernationServer } from '@pikku/cloudflare'
85
+ export default createCloudflareWebSocketHandler({
86
+ createConfig,
87
+ createSingletonServices,
88
+ })
89
+ ```
90
+
91
+ Subclass `CloudflareWebSocketHibernationServer` only when you need something
92
+ `getParams()` cannot express — it is abstract with one method returning
93
+ `{ singletonServices, createWireServices? }`. The channel store
94
+ (`CloudflareWebsocketStore` over the DO's own storage), the event hub and the
95
+ channel handler factory are all built by the base class; do not supply them.
96
+
97
+ The router looks up the DO through the **`WEBSOCKET_HIBERNATION_SERVER`**
98
+ binding and answers `503` naming it if the binding is missing, so declare it in
99
+ `wrangler.toml` under exactly that name.
100
+
101
+ A throw during `onConnect` closes the socket with `1008` and answers `403
102
+ Forbidden` with a deliberately generic body — an auth denial and a genuine fault
103
+ look identical to the client. The real reason is on the logger, so read the
104
+ worker logs rather than the status code.
@@ -0,0 +1,92 @@
1
+ # Express
2
+
3
+ ```bash
4
+ yarn add @pikku/express
5
+ ```
6
+
7
+ ## Standalone server
8
+
9
+ ```typescript
10
+ import { PikkuExpressServer } from '@pikku/express'
11
+ import './.pikku/pikku-bootstrap.gen.js'
12
+ import { createConfig, createSingletonServices } from './services.js'
13
+
14
+ const config = await createConfig()
15
+ const singletonServices = await createSingletonServices(config)
16
+
17
+ const appServer = new PikkuExpressServer(
18
+ { ...config, port: 4002, hostname: 'localhost' },
19
+ singletonServices.logger
20
+ )
21
+ appServer.enableExitOnSigInt()
22
+ await appServer.init()
23
+ await appServer.start()
24
+ ```
25
+
26
+ The config extends `CoreConfig` with `port`, `hostname`, an optional
27
+ `healthCheckPath`, an optional `limits` map and an optional `content`
28
+ (`LocalContentConfig`) for static assets and file uploads. `app: Express` is
29
+ exposed for custom middleware, and `getHttpServer()` returns the underlying
30
+ `http.Server` — to attach a WebSocket server, say — but throws before `start()`.
31
+
32
+ `enableStaticAssets()` serves `content.localFileUploadPath` under
33
+ `content.assetUrlPrefix`, and `enableReaper()` adds a `PUT /reaper/*path` upload
34
+ sink for local development, path-traversal checked and bounded by
35
+ `content.sizeLimit` (default `1mb`). Both throw when `content` is unset.
36
+
37
+ ## Ordering, and what `init` installs for you
38
+
39
+ The health check is registered in the **constructor**, so it answers before any
40
+ middleware you add and cannot be wrapped in auth. It defaults to
41
+ `/health-check`; override with `healthCheckPath`.
42
+
43
+ Everything else is installed by `init()`: `express.json`, `express.text` (for
44
+ `text/xml`), `express.urlencoded`, `cookie-parser`, then the Pikku middleware.
45
+ Call `enableCors` **before** `init` if you want CORS applied to Pikku's routes.
46
+
47
+ Express buffers the body before Pikku sees it, so the parser limit is the only
48
+ place an oversized request can actually be stopped. `httpOptions.maxBodySize`
49
+ therefore feeds those parser limits, with an explicit `config.limits` entry
50
+ (`json` / `xml` / `urlencoded`) still winning. Everything defaults to `1mb`.
51
+
52
+ `init` passes `logRoutes: true` and `loadSchemas: true` by default; your
53
+ `httpOptions` spread over them, so you can turn either off.
54
+
55
+ `stop()` throws if the server was never started. `enableExitOnSigInt()`
56
+ installs a SIGINT handler that stops the singleton services, then the server,
57
+ then exits 0.
58
+
59
+ ## Middleware (existing Express app)
60
+
61
+ ```bash
62
+ yarn add @pikku/express-middleware
63
+ ```
64
+
65
+ ```typescript
66
+ import express from 'express'
67
+ import { pikkuExpressMiddleware } from '@pikku/express-middleware'
68
+ import './.pikku/pikku-bootstrap.gen.js'
69
+
70
+ const app = express()
71
+ app.use(express.json())
72
+ app.use(cookieParser())
73
+ app.use(
74
+ pikkuExpressMiddleware({
75
+ logger: singletonServices.logger,
76
+ logRoutes: true,
77
+ loadSchemas: true,
78
+ // plus any RunHTTPWiringOptions: maxBodySize, respondWith404, coerceDataFromSchema
79
+ })
80
+ )
81
+ ```
82
+
83
+ Options beyond `logger` are all optional: `logRoutes` logs the wiring table once
84
+ at startup, `loadSchemas` compiles every schema up front, and the rest are
85
+ `RunHTTPWiringOptions` passed through per request.
86
+
87
+ On your own app **you** own the parser stack — the middleware reads `req.body`,
88
+ so a body parser and `cookie-parser` must be registered before it, and
89
+ `maxBodySize` alone will not stop an oversized request that your parser already
90
+ accepted. Unmatched requests fall through to `next()` (unless `respondWith404`
91
+ is set), so Pikku's routes coexist with your existing ones; a streaming response
92
+ is the exception and does not call `next()`.
@@ -1,31 +1,11 @@
1
- ---
2
- name: pikku-deploy-fastify
3
- description: >-
4
- Use when deploying a Pikku app with Fastify. Covers PikkuFastifyServer standalone and
5
- pikkuFastifyPlugin for existing Fastify apps. TRIGGER when: code imports @pikku/fastify or
6
- @pikku/fastify-plugin, user mentions Fastify deployment, or start.ts creates a
7
- PikkuFastifyServer. DO NOT TRIGGER when: just defining functions/wirings without
8
- Fastify-specific code.
9
- ---
10
-
11
- # Pikku Fastify Deployment
12
-
13
- ## Agent Operating Procedure
14
-
15
- Use this skill as an execution checklist, not reference material.
16
-
17
- 1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
18
- 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.
19
- 3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
20
- 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.
21
- 5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
22
-
23
- ## Standalone Server
1
+ # Fastify
24
2
 
25
3
  ```bash
26
4
  yarn add @pikku/fastify
27
5
  ```
28
6
 
7
+ ## Standalone server
8
+
29
9
  ```typescript
30
10
  import { PikkuFastifyServer } from '@pikku/fastify'
31
11
  import './.pikku/pikku-bootstrap.gen.js'
@@ -43,16 +23,12 @@ await appServer.init()
43
23
  await appServer.start()
44
24
  ```
45
25
 
46
- **Constructor:** `new PikkuFastifyServer(config, logger)`
47
-
48
- **Config extends CoreConfig with:** `port`, `hostname`, `healthCheckPath?`
49
-
50
- **Methods:** `init(httpOptions?: RunHTTPWiringOptions)`, `start()`, `stop()`, `enableExitOnSigInt()`
51
-
52
- **Property:** `app: FastifyInstance` — Direct access to Fastify instance.
26
+ The config extends `CoreConfig` with `port`, `hostname` and an optional
27
+ `healthCheckPath`. `app: FastifyInstance` is exposed for direct access.
53
28
 
54
- `enableCors` exists on the class but **throws `Method not implemented.`** — unlike
55
- the Express server. Register `@fastify/cors` on `app` yourself before `init()`.
29
+ `enableCors` exists on the class but **throws `Method not implemented.`** —
30
+ unlike the Express server. Register `@fastify/cors` on `app` yourself before
31
+ `init()`.
56
32
 
57
33
  Unlike the Express server, the health check is registered by `init()`, not the
58
34
  constructor, so nothing answers before `init` runs. `init` also passes
@@ -1,30 +1,10 @@
1
- ---
2
- name: pikku-deploy-lambda
3
- description: >-
4
- Use when deploying a Pikku app to AWS Lambda. Covers HTTP handlers, scheduled tasks, SQS queue
5
- workers, WebSocket via API Gateway, and cold start caching. TRIGGER when: code imports
6
- @pikku/lambda, user mentions Lambda/serverless/AWS deployment, or handler files export
7
- Lambda-typed functions. DO NOT TRIGGER when: just defining functions/wirings without
8
- Lambda-specific code.
9
- ---
10
-
11
- # Pikku AWS Lambda Deployment
12
-
13
- ## Agent Operating Procedure
14
-
15
- Use this skill as an execution checklist, not reference material.
16
-
17
- 1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
18
- 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.
19
- 3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
20
- 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.
21
- 5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
1
+ # AWS Lambda
22
2
 
23
3
  ```bash
24
4
  yarn add @pikku/lambda
25
5
  ```
26
6
 
27
- ## Cold Start Pattern
7
+ ## Cold start pattern
28
8
 
29
9
  Cache singleton services across Lambda invocations:
30
10
 
@@ -53,7 +33,7 @@ named exports (`handler`, `queue`, `scheduled`, or `connect`/`disconnect`/
53
33
  `default`) that `serverless.yml` references. Hand-written handlers are for cases
54
34
  the codegen does not cover.
55
35
 
56
- ## HTTP Handler
36
+ ## HTTP handler
57
37
 
58
38
  Pick the entry point that matches the API Gateway payload version — they take
59
39
  different event types and are not interchangeable:
@@ -91,7 +71,7 @@ carried.
91
71
  Neither takes `RunHTTPWiringOptions` — there is no `maxBodySize` or
92
72
  `respondWith404` knob here; API Gateway's own payload limit is the bound.
93
73
 
94
- ## Scheduled Tasks
74
+ ## Scheduled tasks
95
75
 
96
76
  ```typescript
97
77
  import type { ScheduledHandler } from 'aws-lambda'
@@ -113,7 +93,7 @@ Reach for `runScheduledTask({ name })` from `@pikku/core/scheduler` directly
113
93
  only when one Lambda genuinely bundles several tasks that must fire on separate
114
94
  schedules.
115
95
 
116
- ## SQS Queue Worker
96
+ ## SQS queue worker
117
97
 
118
98
  ```typescript
119
99
  import type { SQSHandler } from 'aws-lambda'
@@ -181,8 +161,7 @@ is a separate invocation, so nothing survives in memory between `$connect` and
181
161
  `LambdaEventHubService` handles cross-connection messaging and takes
182
162
  `(logger, event, channelStore, eventHubStore)` — the `event` is needed to derive
183
163
  the API Gateway Management endpoint, so it is constructed per invocation, not
184
- once at cold start. It also needs an `EventHubStore` alongside the channel
185
- store.
164
+ once at cold start. It also needs an `EventHubStore` alongside the channel store.
186
165
 
187
166
  Two behaviours to design around: **binary payloads throw** (`Binary data is not
188
167
  supported on serverless lambdas`), and any `PostToConnection` failure removes
@@ -1,29 +1,10 @@
1
- ---
2
- name: pikku-deploy-nextjs
3
- description: >-
4
- Use when deploying a Pikku app with Next.js. Covers API route handlers, server-side data
5
- fetching, and RPC calls from Server Components. TRIGGER when: code imports @pikku/next, user
6
- mentions Next.js integration, or app/api route files use pikkuAPIRequest. DO NOT TRIGGER when:
7
- just defining functions/wirings without Next.js-specific code.
8
- ---
9
-
10
- # Pikku Next.js Deployment
11
-
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.
1
+ # Next.js
21
2
 
22
3
  ```bash
23
4
  yarn add @pikku/next
24
5
  ```
25
6
 
26
- ## API Route Handler
7
+ ## API route handler
27
8
 
28
9
  The CLI generates a typed wrapper. Use it in a catch-all route:
29
10
 
@@ -49,7 +30,7 @@ choose. It also passes no `RunHTTPWiringOptions`: to set `maxBodySize` or
49
30
  `respondWith404` you need your own handler over `new PikkuNextJS(...)` calling
50
31
  `apiRequest(req, options)`.
51
32
 
52
- ## Server-Side Data Fetching
33
+ ## Server-side data fetching
53
34
 
54
35
  Use the generated `pikku()` helper in Server Components or Server Actions:
55
36
 
@@ -69,12 +50,9 @@ const config = await staticGet('/config')
69
50
  const result = await rpc('calculateTax', { amount: 100, region: 'US' })
70
51
  ```
71
52
 
72
- **Dynamic vs Static:**
73
-
74
- - `get`, `post`, `patch`, `del`, `rpc` — read `next/headers` cookies and headers,
75
- so they force the component dynamic
76
- - `staticGet`, `staticPost`, `staticRPC` — no request context, safe for
77
- precompile/ISR
53
+ `get`, `post`, `patch`, `del` and `rpc` read `next/headers` cookies and headers,
54
+ so they force the component dynamic. `staticGet`, `staticPost` and `staticRPC`
55
+ have no request context and are safe for precompile/ISR.
78
56
 
79
57
  The static variants pass `skipUserSession: true`, so a wiring that expects a
80
58
  session sees none. That is the real difference — not just where they can run.
@@ -85,7 +63,7 @@ Both paths run with `bubbleErrors: true`, so a failing wiring **throws** in your
85
63
  Server Component rather than resolving to an error status. Wrap the call, or let
86
64
  the Next.js error boundary take it.
87
65
 
88
- ## How It Works
66
+ ## How it works
89
67
 
90
68
  `PikkuNextJS` lazy-initializes on first request:
91
69
 
@@ -95,8 +73,6 @@ import { PikkuNextJS } from '@pikku/next'
95
73
  const pikku = new PikkuNextJS(createConfig, createSingletonServices)
96
74
  ```
97
75
 
98
- **Constructor:** `new PikkuNextJS(createConfig | undefined, createSingletonServices)`
99
-
100
76
  Both arguments are positional and `createConfig` is only optional in the sense
101
77
  that passing `undefined` substitutes an empty config —
102
78
  `createSingletonServices` is required.
@@ -113,8 +89,8 @@ route definitions.
113
89
  - **`PikkuNextJSWorkerRPC({ fetcher })`** — same surface as `PikkuNextJS`, but
114
90
  every call is dispatched through a `Fetcher` (a Cloudflare service binding, a
115
91
  local HTTP client, a fabric dispatcher) instead of loading function code
116
- in-process. Use it to keep functions out of the SSR bundle. A non-2xx
117
- response throws with the status and body text.
92
+ in-process. Use it to keep functions out of the SSR bundle. A non-2xx response
93
+ throws with the status and body text.
118
94
  - **`toNextJsAuthHandler(auth)`** — wraps a better-auth instance or handler
119
95
  function for an auth route. The three-argument form
120
96
  `(pikkuAuthFactory, createConfig, createSingletonServices)` resolves a Pikku
@@ -0,0 +1,72 @@
1
+ # uWebSockets.js
2
+
3
+ Highest-throughput option among Pikku's runtimes. Handles both HTTP and
4
+ WebSocket automatically.
5
+
6
+ ```bash
7
+ yarn add @pikku/uws
8
+ ```
9
+
10
+ ```typescript
11
+ import { PikkuUWSServer } from '@pikku/uws'
12
+ import './.pikku/pikku-bootstrap.gen.js'
13
+ import { createConfig, createSingletonServices } from './services.js'
14
+
15
+ const config = await createConfig()
16
+ const singletonServices = await createSingletonServices(config)
17
+
18
+ const appServer = new PikkuUWSServer(
19
+ { ...config, hostname: 'localhost', port: 4002 },
20
+ singletonServices.logger
21
+ )
22
+ appServer.enableExitOnSigInt()
23
+ await appServer.init()
24
+ await appServer.start()
25
+ ```
26
+
27
+ The config extends `CoreConfig` with `port`, `hostname` and an optional
28
+ `healthCheckPath`. `app: uWS.App` is exposed for direct access.
29
+
30
+ ## What the server does and does not give you
31
+
32
+ `init()` registers three things in order: the health check (`healthCheckPath`,
33
+ default `/health-check`), a catch-all `app.any('/*')` HTTP handler, and a
34
+ catch-all `app.ws('/*')` websocket handler. Nothing is registered by the
35
+ constructor, so nothing answers before `init` runs.
36
+
37
+ **There is no `enableCors`, no static assets and no `content` support** — unlike
38
+ the Express server. The class is explicitly a prototyping convenience; for
39
+ anything that needs extra handlers, use `@pikku/uws-handler` directly and treat
40
+ `pikku-uws-server.ts` as the template (that is what its own JSDoc says).
41
+
42
+ `httpOptions` reaches the HTTP handler only. The websocket handler is
43
+ constructed with a fixed `{ logger, logRoutes: true }`, so per-request options
44
+ do not apply to the upgrade path. `loadSchemas` is also never passed by the
45
+ server, so schemas compile lazily on first use rather than at startup — pass
46
+ `loadSchemas: true` in `httpOptions` if you want the startup cost paid up front.
47
+
48
+ `stop()` closes the listen socket and then waits a fixed 2 seconds for
49
+ connections to drain. Called before `start()`, it throws a bare **string**, not
50
+ an `Error`, so `catch (e) { e.message }` reads `undefined`.
51
+
52
+ ## Body limits
53
+
54
+ uWS hands over raw chunks with no limit of its own, so the handler counts the
55
+ bytes itself. A request over `maxBodySize` (default `DEFAULT_MAX_BODY_SIZE`) is
56
+ answered `413` with a `PayloadTooLargeError` body, and the chunks are dropped
57
+ rather than concatenated — an oversized request never accumulates in memory. A
58
+ `content-length` header that already exceeds the limit short-circuits before any
59
+ data arrives.
60
+
61
+ ## Handlers directly (own uWS app)
62
+
63
+ ```typescript
64
+ import { pikkuHTTPHandler, pikkuWebsocketHandler } from '@pikku/uws-handler'
65
+
66
+ app.any('/*', pikkuHTTPHandler({ logger, logRoutes: true, loadSchemas: true }))
67
+ app.ws('/*', pikkuWebsocketHandler({ logger, logRoutes: true }))
68
+ ```
69
+
70
+ Both take `{ logger, logRoutes?, loadSchemas? } & RunHTTPWiringOptions`.
71
+
72
+ For a WebSocket-only server on the `ws` library instead, see `ws.md`.
@@ -0,0 +1,75 @@
1
+ # ws (WebSocket only)
2
+
3
+ `@pikku/ws` connects Pikku's channel system to a Node.js WebSocket server built
4
+ on the [ws](https://github.com/websockets/ws) library. Use it for a
5
+ WebSocket-only server; for HTTP and WebSocket on one port see `uws.md`, and when
6
+ the WebSocket server shares a port with an existing HTTP app see `express.md` or
7
+ `fastify.md`.
8
+
9
+ ```bash
10
+ yarn add @pikku/ws ws
11
+ ```
12
+
13
+ The package exports one function, `pikkuWebsocketHandler` — there is no server
14
+ class. You own the `http.Server` and the `WebSocketServer`; the handler attaches
15
+ the upgrade and message plumbing to them.
16
+
17
+ ```typescript
18
+ import { DEFAULT_WS_MAX_PAYLOAD, pikkuWebsocketHandler } from '@pikku/ws'
19
+ import { stopSingletonServices } from '@pikku/core'
20
+ import { Server } from 'http'
21
+ import { WebSocketServer } from 'ws'
22
+
23
+ import './.pikku/pikku-bootstrap.gen.js'
24
+ import { createConfig, createSingletonServices } from './services.js'
25
+
26
+ const config = await createConfig()
27
+ const singletonServices = await createSingletonServices(config)
28
+
29
+ const server = new Server()
30
+ const wss = new WebSocketServer({
31
+ noServer: true,
32
+ maxPayload: DEFAULT_WS_MAX_PAYLOAD,
33
+ })
34
+
35
+ pikkuWebsocketHandler({
36
+ server,
37
+ wss,
38
+ logger: singletonServices.logger,
39
+ logRoutes: true, // print the wired channels at startup
40
+ loadSchemas: true, // compile input schemas up front
41
+ })
42
+
43
+ server.listen(4002, 'localhost')
44
+
45
+ process.on('SIGINT', async () => {
46
+ await stopSingletonServices()
47
+ wss.close()
48
+ server.close()
49
+ process.exit(0)
50
+ })
51
+ ```
52
+
53
+ ## `noServer: true` is required, not stylistic
54
+
55
+ The handler listens for the HTTP server's own `upgrade` event, opens the channel
56
+ — running Pikku's middleware chain, auth and CORS against the upgrade request
57
+ first — and only then calls `wss.handleUpgrade`. A `WebSocketServer` bound to the
58
+ server directly would take the socket before any of that ran.
59
+
60
+ An upgrade the channel rejects gets the socket destroyed, and an auth failure is
61
+ written as a real HTTP response on the raw socket rather than a silent drop.
62
+
63
+ ## Services and the event hub
64
+
65
+ Services come from the bootstrap import and the global singleton registry, which
66
+ is why nothing is passed in. The event hub is taken from
67
+ `singletonServices.eventHub` when it is a `LocalEventHubService`, and a local one
68
+ is created otherwise — so a single-process app gets pub/sub for free, while a
69
+ multi-instance deployment must register a distributed hub.
70
+
71
+ The options type also extends `RunHTTPWiringOptions`, so per-request settings
72
+ such as `respondWith404`, `coerceDataFromSchema` and `bubbleErrors` are accepted
73
+ here too.
74
+
75
+ On shutdown, call `stopSingletonServices()` then close `wss` and `server`.