@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,161 +0,0 @@
1
- ---
2
- name: pikku-aws
3
- description: >-
4
- Use when setting up AWS services (S3, SQS, Secrets Manager) in a Pikku app. Covers S3Content for
5
- file storage, SQSQueueService for queues, and AWSSecrets for secret management. TRIGGER when:
6
- code uses S3Content, SQSQueueService, AWSSecrets, or user asks about AWS integration, S3
7
- uploads, SQS queues, or AWS Secrets Manager with Pikku. DO NOT TRIGGER when: user asks about AWS
8
- Lambda runtime (use pikku-deploy-lambda).
9
- ---
10
-
11
- # Pikku AWS Services
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/aws-services` provides AWS-backed implementations of Pikku's content, queue, and secret service interfaces.
24
-
25
- ## Installation
26
-
27
- ```bash
28
- yarn add @pikku/aws-services
29
- ```
30
-
31
- ## API Reference
32
-
33
- ### `S3Content` (File Storage)
34
-
35
- ```typescript
36
- import { S3Content } from '@pikku/aws-services'
37
-
38
- const content = new S3Content(
39
- config: { bucketName: string; region: string; endpoint?: string },
40
- logger: Logger,
41
- signConfig: { keyPairId: string; privateKey: string }
42
- )
43
- ```
44
-
45
- `endpoint` is what points the client at LocalStack or an S3-compatible store.
46
-
47
- **Methods** — every one takes a single **args object**, matching the shared
48
- `ContentService` interface. None of them are positional:
49
-
50
- - `signURL({ url, dateLessThan, dateGreaterThan? }): Promise<string>` — CloudFront-sign an absolute URL
51
- - `signContentKey({ bucket, contentKey, dateLessThan, dateGreaterThan? }): Promise<string>`
52
- - `getUploadURL({ bucket, fileKey, contentType, visibility? }): Promise<{ uploadUrl, assetKey }>` — `visibility` is ignored
53
- - `readFile({ bucket, key }): Promise<ReadableStream | NodeJS.ReadableStream>`
54
- - `readFileAsBuffer({ bucket, key }): Promise<Buffer>`
55
- - `writeFile({ bucket, key, stream }): Promise<boolean>`
56
- - `copyFile({ bucket, key, fromAbsolutePath }): Promise<boolean>`
57
- - `deleteFile({ bucket, key }): Promise<boolean>`
58
-
59
- ### One real bucket, logical buckets as prefixes
60
-
61
- The `bucket` on every call is a **logical** bucket stored as a path prefix
62
- (`${bucket}/${key}`) inside the single S3 bucket named by `bucketName`. Don't
63
- provision an S3 bucket per logical bucket — the config takes only one.
64
-
65
- ### Behaviours worth knowing before you rely on them
66
-
67
- - **`signURL` fails open.** A signing error is logged and the _unsigned_ URL is
68
- returned rather than thrown. If your CloudFront distribution is private the
69
- client then gets a 403; if it isn't, you have just handed out an unrestricted
70
- link. Check that `signConfig` is a valid CloudFront key pair at boot.
71
- - **`signContentKey` builds `https://<bucketName>/<bucket>/<contentKey>`** — it
72
- uses `bucketName` as the _host_. For signed content the value must therefore be
73
- your CloudFront domain, not a plain bucket name, which also means the same
74
- config field is doing two jobs.
75
- - **Presigned upload URLs expire after a fixed 3600s.** It is not configurable
76
- through the service.
77
- - **Write paths swallow failures.** `writeFile`, `copyFile` and `deleteFile` log
78
- and return `false` rather than throwing; the read paths throw. Check the
79
- boolean.
80
-
81
- ### `SQSQueueService` (Queue)
82
-
83
- ```typescript
84
- import { SQSQueueService } from '@pikku/aws-services'
85
-
86
- const queue = new SQSQueueService({
87
- region: string,
88
- queueUrlPrefix: string, // e.g. 'https://sqs.us-east-1.amazonaws.com/123456789/'
89
- endpoint?: string, // LocalStack or a custom SQS endpoint
90
- })
91
- ```
92
-
93
- Implements `QueueService`. Note: `supportsResults = false` — job status tracking is not supported.
94
-
95
- **Methods:**
96
-
97
- - `add<T>(queueName: string, data: T, options?: JobOptions): Promise<string>` — Enqueue a message; returns SQS's `MessageId`
98
- - `getJob()` — always **throws**. SQS is fire-and-forget; reach for BullMQ or PgBoss when you need the result back.
99
-
100
- The queue URL is `queueUrlPrefix + queueName`, so the queue name in `wireQueueWorker` has to match the SQS queue exactly.
101
-
102
- Constraints inherited from SQS, enforced in `add`:
103
-
104
- - `options.delay` is in **milliseconds** and is floored to whole seconds. Over
105
- 900_000ms (15 minutes) or negative throws before the message is sent.
106
- - Standard queues only — no FIFO, so no `MessageGroupId` and no ordering
107
- guarantee.
108
- - `data` is `JSON.stringify`d, which is where a `Date` or a `Map` quietly
109
- degrades.
110
-
111
- ### `AWSSecrets` (Secrets Manager)
112
-
113
- ```typescript
114
- import { AWSSecrets } from '@pikku/aws-services'
115
-
116
- const secrets = new AWSSecrets({ awsRegion: 'eu-west-2' })
117
- ```
118
-
119
- `AWSConfig` has one field, `awsRegion` — there is no credentials option; the SDK's
120
- default provider chain (instance role, env, profile) supplies those.
121
-
122
- **Methods:**
123
-
124
- - `getSecret<T = string>(SecretId: string): Promise<SecretValue<T>>` — a JSON secret is parsed automatically, so pass a shape as `T` (a non-JSON value comes back as the raw string). The result is a branded `SecretValue`, not a bare value — reveal it where it is used rather than passing it through logs
125
- - `getSecrets<T>(SecretIds: (keyof T & string)[]): Promise<Partial<T>>` — Batch fetch; missing keys are omitted rather than thrown
126
- - `hasSecret(SecretId: string): Promise<boolean>` — Check if secret exists
127
- - `setSecret` / `deleteSecret` — **not implemented** for `AWSSecrets`; it throws. Manage AWS secrets out of band.
128
-
129
- Every `getSecret` failure — missing secret, denied permission, a secret holding
130
- only binary — surfaces as the same `FATAL: Error finding secret: <id>`, with the
131
- real reason on the error's `cause`. Read `cause` before concluding the secret
132
- doesn't exist. `hasSecret` performs a full fetch and returns `false` for any
133
- error, so it can't distinguish "absent" from "not allowed" either.
134
-
135
- ## Usage Patterns
136
-
137
- ### S3 Content Service
138
-
139
- ```typescript
140
- const createSingletonServices = pikkuServices(async (config) => {
141
- const logger = new PinoLogger()
142
- const content = new S3Content(
143
- { bucketName: config.s3Bucket, region: config.awsRegion },
144
- logger,
145
- { keyPairId: config.cfKeyPairId, privateKey: config.cfPrivateKey }
146
- )
147
- return { config, logger, content }
148
- })
149
- ```
150
-
151
- ### SQS Queue
152
-
153
- ```typescript
154
- const createSingletonServices = pikkuServices(async (config) => {
155
- const queue = new SQSQueueService({
156
- region: config.awsRegion,
157
- queueUrlPrefix: config.sqsUrlPrefix,
158
- })
159
- return { config, queue }
160
- })
161
- ```
@@ -1,104 +0,0 @@
1
- ---
2
- name: pikku-backblaze
3
- description: >-
4
- Use when setting up Backblaze B2 file storage in a Pikku app. Covers B2Content for file uploads,
5
- downloads, and signed URLs. TRIGGER when: code uses B2Content, user asks about Backblaze B2, or
6
- @pikku/backblaze. DO NOT TRIGGER when: user asks about S3 storage (use pikku-aws).
7
- ---
8
-
9
- # Pikku Backblaze (B2 Content Storage)
10
-
11
- ## Agent Operating Procedure
12
-
13
- Use this skill as an execution checklist, not reference material.
14
-
15
- 1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
16
- 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.
17
- 3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
18
- 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.
19
- 5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
20
-
21
- `@pikku/backblaze` provides Backblaze B2-backed file storage implementing the `ContentService` interface.
22
-
23
- ## Installation
24
-
25
- ```bash
26
- yarn add @pikku/backblaze
27
- ```
28
-
29
- ## API Reference
30
-
31
- ### `B2Content`
32
-
33
- ```typescript
34
- import { B2Content } from '@pikku/backblaze'
35
-
36
- const content = new B2Content(
37
- config: B2ContentConfig,
38
- logger: Logger
39
- )
40
- ```
41
-
42
- `B2ContentConfig` has exactly three fields — `applicationKeyId`, `applicationKey`
43
- and `bucketId`. There is no `cdnUrl`: downloads are served from the `downloadUrl`
44
- B2 returns at authorization.
45
-
46
- **Methods** — every one takes a single **args object**, matching the shared
47
- `ContentService` interface. None of them are positional:
48
-
49
- - `signContentKey({ bucket, contentKey, dateLessThan }): Promise<string>` — a full download URL with an `Authorization` query param
50
- - `signURL({ url, dateLessThan }): Promise<string>` — re-signs an existing `/file/` URL; a URL with no `/file/` segment is returned untouched
51
- - `getUploadURL({ bucket, fileKey, contentType, visibility? }): Promise<UploadURLResult>` — `visibility` is ignored by this backend
52
- - `writeFile({ bucket, key, stream }): Promise<boolean>`
53
- - `copyFile({ bucket, key, fromAbsolutePath }): Promise<boolean>`
54
- - `readFile({ bucket, key }): Promise<ReadableStream | NodeJS.ReadableStream>`
55
- - `readFileAsBuffer({ bucket, key }): Promise<Buffer>`
56
- - `deleteFile({ bucket, key }): Promise<boolean>`
57
-
58
- ### One real bucket, logical buckets as prefixes
59
-
60
- The `bucket` on every call is a **logical** bucket stored as a path prefix
61
- (`${bucket}/${key}`) inside the single B2 bucket named by `bucketId`. Don't
62
- provision a B2 bucket per logical bucket — the config takes only one.
63
-
64
- ### Behaviours worth knowing before you rely on them
65
-
66
- - **Writes are buffered in memory.** `writeFile` drains the whole stream into a
67
- `Buffer` before uploading, because B2's upload endpoint needs a SHA-1 and a
68
- content length up front. Large uploads should go through `getUploadURL` and be
69
- sent by the client directly.
70
- - **`getUploadURL` sets `X-Bz-Content-Sha1: do_not_verify`**, since the server
71
- can't hash a body it never sees. The client-side upload is unverified.
72
- - **Write paths swallow failures.** `writeFile`, `copyFile` and `deleteFile` log
73
- and return `false` rather than throwing; the read paths and the signing paths
74
- throw. Check the boolean — an ignored return is a silently lost file.
75
- - Authorization and the bucket-name lookup are cached on the instance for its
76
- lifetime, so a rotated application key needs a new `B2Content`.
77
-
78
- ## Usage Patterns
79
-
80
- ```typescript
81
- import { B2Content } from '@pikku/backblaze'
82
-
83
- const createSingletonServices = pikkuServices(async (config) => {
84
- const logger = new PinoLogger()
85
- const content = new B2Content(
86
- {
87
- applicationKeyId: config.b2KeyId,
88
- applicationKey: config.b2AppKey,
89
- bucketId: config.b2BucketId,
90
- },
91
- logger
92
- )
93
- return { config, logger, content }
94
- })
95
- ```
96
-
97
- ```typescript
98
- await content.writeFile({ bucket: 'avatars', key: `${userId}.png`, stream })
99
- const url = await content.signContentKey({
100
- bucket: 'avatars',
101
- contentKey: `${userId}.png`,
102
- dateLessThan: new Date(Date.now() + 60_000),
103
- })
104
- ```
@@ -1,123 +0,0 @@
1
- ---
2
- name: pikku-deploy-cloudflare
3
- description: >-
4
- Use when deploying a Pikku app to Cloudflare Workers. Covers HTTP fetch handler, scheduled
5
- tasks, and WebSocket via Durable Objects. TRIGGER when: code imports @pikku/cloudflare, user
6
- mentions Cloudflare Workers deployment, or worker entry uses ExportedHandler/wrangler.toml. DO
7
- NOT TRIGGER when: just defining functions/wirings without Cloudflare-specific code.
8
- ---
9
-
10
- # Pikku Cloudflare Workers 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.
21
-
22
- ```bash
23
- yarn add @pikku/cloudflare
24
- ```
25
-
26
- ## Worker Entry
27
-
28
- `@pikku/cloudflare` ships the handler factories the deploy codegen emits — use
29
- them rather than hand-rolling an `ExportedHandler`. Each returns a
30
- `WorkerEntrypoint` class that sets services up on every invocation (cached after
31
- the first) and adds an RPC-callable `runRpc(name, args)`:
32
-
33
- ```typescript
34
- import { createCloudflareHandler } from '@pikku/cloudflare'
35
- import { createConfig, createSingletonServices } from './services.js'
36
- import './.pikku/pikku-bootstrap.gen.js'
37
-
38
- export default createCloudflareHandler(
39
- { createConfig, createSingletonServices },
40
- ['fetch', 'scheduled']
41
- )
42
- ```
43
-
44
- | Factory | For |
45
- | -------------------------------------------------- | ------------------------------------------------ |
46
- | `createCloudflareHandler(factories, handlerTypes)` | combined `fetch`/`queue`/`scheduled` |
47
- | `createCloudflareWorkerHandler(factories)` | HTTP / agent / RPC / workflow-orchestrator units |
48
- | `createCloudflareCronHandler(factories)` | cron units |
49
- | `createCloudflareQueueHandler(factories)` | queue-consumer and workflow-step units |
50
- | `createCloudflareMCPHandler(factories)` | MCP units (same HTTP transport) |
51
- | `createCloudflareWebSocketHandler(factories)` | channel units, delegating to the DO |
52
-
53
- `factories` is `{ createConfig, createSingletonServices, createPlatformServices? }`.
54
-
55
- ## Service Setup
56
-
57
- Cloudflare passes env bindings per-request, so services are built from `env`
58
- rather than at module load. `setupServices(env, factories)` is exported from
59
- `@pikku/cloudflare` and is what the factories call:
60
-
61
- ```typescript
62
- import { setupServices } from '@pikku/cloudflare'
63
-
64
- const services = await setupServices(env, {
65
- createConfig,
66
- createSingletonServices,
67
- })
68
- ```
69
-
70
- **Do not hand-roll this.** Beyond building `LocalVariablesService` /
71
- `LocalSecretService` and caching the result, it calls `setSingletonServices()` —
72
- and the core runners (`fetchData`, `runQueueJob`, `runScheduled`) resolve
73
- services through that global slot, _not_ through the value you were returned. A
74
- setup function that only returns the services leaves every request throwing
75
- "Singleton services not initialized" as a CF `1101`. It also stashes the env via
76
- `setCloudflareEnv`, which `getCloudflareEnv()` reads for bindings.
77
-
78
- ## HTTP
79
-
80
- `runFetch(request, websocketHibernationServer?, options?)`:
81
-
82
- - A `GET` with `Upgrade: websocket` is routed to the hibernation server. Without
83
- one passed in it answers **426**, so a channel worker that forgets the second
84
- argument fails every upgrade while plain HTTP keeps working.
85
- - `CF-Ray` becomes the traceId when present, so a Cloudflare trace and a Pikku
86
- trace line up without extra wiring.
87
- - `options.exposeErrors` defaults to **`false`** — error detail is withheld from
88
- responses unless you opt in.
89
-
90
- ## Scheduled Tasks
91
-
92
- `runScheduled(controller)` matches registered tasks against
93
- `controller.cron` and **returns after the first match**. Two tasks sharing one
94
- cron expression means only one of them ever runs — give each its own expression,
95
- or invoke `runScheduledTask({ name })` per task yourself.
96
-
97
- ## WebSocket (Durable Objects)
98
-
99
- The ready-made DO class is exported; re-export it under the binding name and
100
- point the worker at it:
101
-
102
- ```typescript
103
- export { PikkuWebSocketHibernationServer as WebSocketHibernationServer } from '@pikku/cloudflare'
104
- export default createCloudflareWebSocketHandler({
105
- createConfig,
106
- createSingletonServices,
107
- })
108
- ```
109
-
110
- Subclass `CloudflareWebSocketHibernationServer` only when you need something
111
- `getParams()` cannot express — it is abstract with one method returning
112
- `{ singletonServices, createWireServices? }`. The channel store
113
- (`CloudflareWebsocketStore` over the DO's own storage), the event hub and the
114
- channel handler factory are all built by the base class; do not supply them.
115
-
116
- The router looks up the DO through the **`WEBSOCKET_HIBERNATION_SERVER`**
117
- binding and answers `503` naming it if the binding is missing, so declare it in
118
- `wrangler.toml` under exactly that name.
119
-
120
- A throw during `onConnect` closes the socket with `1008` and answers `403
121
- Forbidden` with a deliberately generic body — an auth denial and a genuine fault
122
- look identical to the client. The real reason is on the logger, so read the
123
- worker logs rather than the status code.
@@ -1,122 +0,0 @@
1
- ---
2
- name: pikku-deploy-express
3
- description: >-
4
- Use when deploying a Pikku app with Express. Covers PikkuExpressServer standalone and
5
- pikkuExpressMiddleware for existing Express apps. TRIGGER when: code imports @pikku/express or
6
- @pikku/express-middleware, user mentions Express deployment, or start.ts creates a
7
- PikkuExpressServer. DO NOT TRIGGER when: just defining functions/wirings without
8
- Express-specific code.
9
- ---
10
-
11
- # Pikku Express 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
24
-
25
- ```bash
26
- yarn add @pikku/express
27
- ```
28
-
29
- ```typescript
30
- import { PikkuExpressServer } from '@pikku/express'
31
- import './.pikku/pikku-bootstrap.gen.js'
32
- import { createConfig, createSingletonServices } from './services.js'
33
-
34
- const config = await createConfig()
35
- const singletonServices = await createSingletonServices(config)
36
-
37
- const appServer = new PikkuExpressServer(
38
- { ...config, port: 4002, hostname: 'localhost' },
39
- singletonServices.logger
40
- )
41
- appServer.enableExitOnSigInt()
42
- await appServer.init()
43
- await appServer.start()
44
- ```
45
-
46
- **Constructor:** `new PikkuExpressServer(config, logger)`
47
-
48
- **Config extends CoreConfig with:**
49
-
50
- - `port: number`
51
- - `hostname: string`
52
- - `healthCheckPath?: string`
53
- - `limits?: Partial<Record<string, string>>`
54
- - `content?: LocalContentConfig` (for static assets / file uploads)
55
-
56
- **Methods:**
57
-
58
- - `init(httpOptions?: RunHTTPWiringOptions): Promise<void>` — installs the body parsers, the cookie parser and the Pikku middleware
59
- - `start(): Promise<void>` — Start listening
60
- - `stop(): Promise<void>` — Graceful shutdown; throws if the server was never started
61
- - `enableExitOnSigInt(): Promise<void>` — SIGINT handler: stops the singleton services, then the server, then exits 0
62
- - `enableCors(options): void` — Enable CORS
63
- - `enableStaticAssets(): void` — serve `content.localFileUploadPath` under `content.assetUrlPrefix`
64
- - `enableReaper(): void` — a `PUT /reaper/*path` upload sink for local development, path-traversal checked and bounded by `content.sizeLimit` (default `1mb`)
65
- - `getHttpServer(): Server` — the underlying `http.Server`, e.g. to attach a WebSocket server; throws before `start()`
66
-
67
- `enableStaticAssets` and `enableReaper` both throw when `content` is unset.
68
-
69
- **Property:** `app: Express` — Direct access to Express instance for custom middleware.
70
-
71
- ### Ordering, and what `init` installs for you
72
-
73
- The health check is registered in the **constructor**, so it answers before any
74
- middleware you add and cannot be wrapped in auth. It defaults to
75
- `/health-check`; override with `healthCheckPath`.
76
-
77
- Everything else is installed by `init()`: `express.json`, `express.text` (for
78
- `text/xml`), `express.urlencoded`, `cookie-parser`, then the Pikku middleware.
79
- Call `enableCors` **before** `init` if you want CORS applied to Pikku's routes.
80
-
81
- Express buffers the body before Pikku sees it, so the parser limit is the only
82
- place an oversized request can actually be stopped. `httpOptions.maxBodySize`
83
- therefore feeds those parser limits, with an explicit `config.limits` entry
84
- (`json` / `xml` / `urlencoded`) still winning. Everything defaults to `1mb`.
85
-
86
- `init` passes `logRoutes: true` and `loadSchemas: true` by default; your
87
- `httpOptions` spread over them, so you can turn either off.
88
-
89
- ## Middleware (existing Express app)
90
-
91
- ```bash
92
- yarn add @pikku/express-middleware
93
- ```
94
-
95
- ```typescript
96
- import express from 'express'
97
- import { pikkuExpressMiddleware } from '@pikku/express-middleware'
98
- import './.pikku/pikku-bootstrap.gen.js'
99
-
100
- const app = express()
101
- app.use(express.json())
102
- app.use(cookieParser())
103
- app.use(
104
- pikkuExpressMiddleware({
105
- logger: singletonServices.logger,
106
- logRoutes: true,
107
- loadSchemas: true,
108
- // plus any RunHTTPWiringOptions: maxBodySize, respondWith404, coerceDataFromSchema
109
- })
110
- )
111
- ```
112
-
113
- Options beyond `logger` are all optional: `logRoutes` logs the wiring table once
114
- at startup, `loadSchemas` compiles every schema up front, and the rest are
115
- `RunHTTPWiringOptions` passed through per request.
116
-
117
- On your own app **you** own the parser stack — the middleware reads
118
- `req.body`, so a body parser and `cookie-parser` must be registered before it,
119
- and `maxBodySize` alone will not stop an oversized request that your parser
120
- already accepted. Unmatched requests fall through to `next()` (unless
121
- `respondWith404` is set), so Pikku's routes coexist with your existing ones;
122
- a streaming response is the exception and does not call `next()`.
@@ -1,144 +0,0 @@
1
- ---
2
- name: pikku-deploy-uws
3
- description: >-
4
- Use when deploying a Pikku app with uWebSockets.js. Covers PikkuUWSServer with built-in HTTP and
5
- WebSocket support, and pikkuWebsocketHandler for standalone ws library. TRIGGER when: code
6
- imports @pikku/uws or @pikku/ws, user mentions uWebSockets or high-performance server, or
7
- start.ts creates a PikkuUWSServer. DO NOT TRIGGER when: just defining functions/wirings without
8
- uWS-specific code.
9
- ---
10
-
11
- # Pikku uWebSockets.js 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
- Highest-throughput option among Pikku's runtimes. Handles both HTTP and WebSocket automatically.
24
-
25
- ```bash
26
- yarn add @pikku/uws
27
- ```
28
-
29
- ```typescript
30
- import { PikkuUWSServer } from '@pikku/uws'
31
- import './.pikku/pikku-bootstrap.gen.js'
32
- import { createConfig, createSingletonServices } from './services.js'
33
-
34
- const config = await createConfig()
35
- const singletonServices = await createSingletonServices(config)
36
-
37
- const appServer = new PikkuUWSServer(
38
- { ...config, hostname: 'localhost', port: 4002 },
39
- singletonServices.logger
40
- )
41
- appServer.enableExitOnSigInt()
42
- await appServer.init()
43
- await appServer.start()
44
- ```
45
-
46
- **Constructor:** `new PikkuUWSServer(config, logger)`
47
-
48
- **Config extends CoreConfig with:** `port`, `hostname`, `healthCheckPath?`
49
-
50
- **Methods:** `init(httpOptions?: RunHTTPWiringOptions)`, `start()`, `stop()`, `enableExitOnSigInt()`
51
-
52
- **Property:** `app: uWS.App` — Direct access to uWebSockets app instance.
53
-
54
- ### What the server does and does not give you
55
-
56
- `init()` registers three things in order: the health check (`healthCheckPath`,
57
- default `/health-check`), a catch-all `app.any('/*')` HTTP handler, and a
58
- catch-all `app.ws('/*')` websocket handler. Nothing is registered by the
59
- constructor, so nothing answers before `init` runs.
60
-
61
- **There is no `enableCors`, no static assets and no `content` support** — unlike
62
- the Express server. The class is explicitly a prototyping convenience; for
63
- anything that needs extra handlers, use `@pikku/uws-handler` directly and treat
64
- `pikku-uws-server.ts` as the template (that is what its own JSDoc says).
65
-
66
- `httpOptions` reaches the HTTP handler only. The websocket handler is
67
- constructed with a fixed `{ logger, logRoutes: true }`, so per-request options
68
- do not apply to the upgrade path. `loadSchemas` is also never passed by the
69
- server, so schemas compile lazily on first use rather than at startup — pass
70
- `loadSchemas: true` in `httpOptions` if you want the startup cost paid up front.
71
-
72
- `stop()` closes the listen socket and then waits a fixed 2 seconds for
73
- connections to drain. Called before `start()`, it throws a bare **string**, not
74
- an `Error`, so `catch (e) { e.message }` reads `undefined`.
75
-
76
- ### Body limits
77
-
78
- uWS hands over raw chunks with no limit of its own, so the handler counts the
79
- bytes itself. A request over `maxBodySize` (default `DEFAULT_MAX_BODY_SIZE`) is
80
- answered `413` with a `PayloadTooLargeError` body, and the chunks are dropped
81
- rather than concatenated — an oversized request never accumulates in memory. A
82
- `content-length` header that already exceeds the limit short-circuits before any
83
- data arrives.
84
-
85
- ### Handlers directly (own uWS app)
86
-
87
- ```typescript
88
- import { pikkuHTTPHandler, pikkuWebsocketHandler } from '@pikku/uws-handler'
89
-
90
- app.any('/*', pikkuHTTPHandler({ logger, logRoutes: true, loadSchemas: true }))
91
- app.ws('/*', pikkuWebsocketHandler({ logger, logRoutes: true }))
92
- ```
93
-
94
- Both take `{ logger, logRoutes?, loadSchemas? } & RunHTTPWiringOptions`.
95
-
96
- ## WebSocket Standalone (ws library)
97
-
98
- For WebSocket-only servers using the `ws` library:
99
-
100
- ```bash
101
- yarn add @pikku/ws
102
- ```
103
-
104
- ```typescript
105
- import { DEFAULT_WS_MAX_PAYLOAD, pikkuWebsocketHandler } from '@pikku/ws'
106
- import { stopSingletonServices } from '@pikku/core'
107
- import { Server } from 'http'
108
- import { WebSocketServer } from 'ws'
109
- import './.pikku/pikku-bootstrap.gen.js'
110
-
111
- const server = new Server()
112
- const wss = new WebSocketServer({
113
- noServer: true,
114
- maxPayload: DEFAULT_WS_MAX_PAYLOAD,
115
- })
116
-
117
- pikkuWebsocketHandler({
118
- server,
119
- wss,
120
- logger: singletonServices.logger,
121
- })
122
-
123
- server.listen(4002, 'localhost', () => {
124
- console.log('Server running at http://localhost:4002/')
125
- })
126
-
127
- process.on('SIGINT', async () => {
128
- await stopSingletonServices()
129
- wss.close()
130
- server.close()
131
- process.exit(0)
132
- })
133
- ```
134
-
135
- `pikkuWebsocketHandler` takes `{ server, wss, logger, logRoutes?, loadSchemas? }`
136
- plus `RunHTTPWiringOptions`, and there is no server class in `@pikku/ws` — the
137
- handler attaches to a `Server` you own.
138
-
139
- `noServer: true` is required, not stylistic: the handler listens for the HTTP
140
- server's own `upgrade` event, opens the channel (running middleware and auth
141
- first), and only then calls `wss.handleUpgrade`. A `WebSocketServer` bound to
142
- the server would take the socket before any of that ran. An upgrade the channel
143
- rejects gets the socket destroyed, and an auth failure is written as a real HTTP
144
- response on the raw socket rather than a silent drop.