@pikku/cli 0.12.55 → 0.12.56

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 (125) hide show
  1. package/cli.schema.json +1 -1
  2. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  3. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  4. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  5. package/dist/.pikku/cli/pikku-cli-channel.js +6 -1
  6. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  7. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  8. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  9. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  10. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +14 -0
  11. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  12. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  13. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  14. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +14 -0
  15. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  16. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  17. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  18. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  19. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  20. package/dist/.pikku/function/pikku-function-types.gen.d.ts +7 -30
  21. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  22. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  23. package/dist/.pikku/function/pikku-functions-meta.gen.json +24 -5
  24. package/dist/.pikku/function/pikku-functions.gen.js +3 -1
  25. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  26. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  27. package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
  28. package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
  29. package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
  30. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  31. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  32. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  33. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  34. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  35. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  36. package/dist/.pikku/pikku-services.gen.d.ts +4 -2
  37. package/dist/.pikku/pikku-services.gen.js +2 -0
  38. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  39. package/dist/.pikku/pikku-types.gen.js +1 -1
  40. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  41. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  42. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  43. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.json +0 -248
  44. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  45. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  46. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  47. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -0
  48. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  49. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  50. package/dist/.pikku/schemas/register.gen.js +5 -1
  51. package/dist/.pikku/schemas/schemas/FabricAddonVerifyInput.schema.json +1 -0
  52. package/dist/.pikku/schemas/schemas/FabricAddonVerifyOutput.schema.json +1 -0
  53. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  54. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  55. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  56. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  57. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  58. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  59. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  60. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  61. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  62. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  63. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  64. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  65. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  66. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  67. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  68. package/dist/bin/pikku-bin.mjs +2 -2
  69. package/dist/src/deploy/analyzer/analyzer.d.ts +6 -0
  70. package/dist/src/deploy/analyzer/analyzer.js +5 -4
  71. package/dist/src/deploy/build-pipeline.d.ts +5 -1
  72. package/dist/src/deploy/build-pipeline.js +5 -5
  73. package/dist/src/deploy/bundler/bun-bundler.d.ts +14 -0
  74. package/dist/src/deploy/bundler/bun-bundler.js +121 -0
  75. package/dist/src/deploy/bundler/bundler.d.ts +25 -30
  76. package/dist/src/deploy/bundler/bundler.interface.d.ts +54 -0
  77. package/dist/src/deploy/bundler/bundler.interface.js +11 -0
  78. package/dist/src/deploy/bundler/bundler.js +120 -190
  79. package/dist/src/deploy/bundler/dep-extractor.d.ts +11 -3
  80. package/dist/src/deploy/bundler/dep-extractor.js +12 -6
  81. package/dist/src/deploy/bundler/index.d.ts +5 -2
  82. package/dist/src/deploy/bundler/index.js +4 -2
  83. package/dist/src/deploy/bundler/node-bundler.d.ts +13 -0
  84. package/dist/src/deploy/bundler/node-bundler.js +80 -0
  85. package/dist/src/fabric/fabric-commands.d.ts +37 -0
  86. package/dist/src/fabric/fabric-commands.js +8 -0
  87. package/dist/src/fabric/functions/addon-verify.function.d.ts +54 -0
  88. package/dist/src/fabric/functions/addon-verify.function.js +153 -0
  89. package/dist/src/fabric/functions/llm-key.function.js +1 -1
  90. package/dist/src/fabric/functions/publish.function.js +8 -3
  91. package/dist/src/functions/commands/deploy-apply.js +3 -1
  92. package/dist/src/functions/commands/deploy-plan.js +3 -1
  93. package/dist/src/functions/commands/dev.js +11 -45
  94. package/dist/src/functions/db/db-codegen.js +14 -0
  95. package/dist/src/functions/wirings/functions/serialize-function-types.js +6 -29
  96. package/dist/src/server/bun-server-runner.d.ts +17 -0
  97. package/dist/src/server/bun-server-runner.js +25 -0
  98. package/dist/src/server/dev-server-runner.interface.d.ts +31 -0
  99. package/dist/src/server/dev-server-runner.interface.js +11 -0
  100. package/dist/src/server/node-server-runner.d.ts +12 -0
  101. package/dist/src/server/node-server-runner.js +30 -0
  102. package/dist/src/services.js +10 -0
  103. package/dist/src/utils/parse-cli-filters.d.ts +1 -0
  104. package/dist/src/utils/parse-cli-filters.js +1 -0
  105. package/dist/tsconfig.tsbuildinfo +1 -1
  106. package/package.json +3 -3
  107. package/skills/pikku-addon/SKILL.md +25 -117
  108. package/skills/pikku-addon/references/addon-package-manifest.md +63 -0
  109. package/skills/pikku-cli/SKILL.md +7 -93
  110. package/skills/pikku-cli/references/complete-example.md +82 -0
  111. package/skills/pikku-concepts/SKILL.md +17 -69
  112. package/skills/pikku-concepts/references/concept-mapping.md +37 -13
  113. package/skills/pikku-concepts/references/packages.md +29 -0
  114. package/skills/pikku-http/SKILL.md +14 -105
  115. package/skills/pikku-http/references/http-options.md +57 -0
  116. package/skills/pikku-middleware/SKILL.md +11 -68
  117. package/skills/pikku-middleware/references/middleware-patterns.md +61 -0
  118. package/skills/pikku-realtime/SKILL.md +56 -105
  119. package/skills/pikku-realtime/references/other-routes.md +23 -0
  120. package/skills/pikku-services/SKILL.md +25 -108
  121. package/skills/pikku-services/references/audit-wire-service.md +34 -0
  122. package/skills/pikku-testing/SKILL.md +51 -359
  123. package/skills/pikku-testing/references/cucumber-bdd-testing.md +176 -0
  124. package/skills/pikku-workflow/SKILL.md +93 -259
  125. package/skills/pikku-workflow/references/workflow-reference.md +63 -0
@@ -53,42 +53,32 @@ The function never imports Express, never reads `req.body`, never touches `ws.se
53
53
 
54
54
  ## Concept Mapping: Generic Backend → Pikku
55
55
 
56
- | Generic Backend Concept | Pikku Equivalent | Skill |
57
- | --------------------------------------- | --------------------------------------------------------------- | ----------------- |
58
- | **Controller / Route Handler** | `pikkuFunc` / `pikkuSessionlessFunc` | (this skill) |
59
- | **Route definition** (`GET /users/:id`) | `wireHTTP({ route, method, func })` | `pikku-http` |
60
- | **Middleware** (Express/Koa-style) | `pikkuMiddleware` | `pikku-security` |
61
- | **Auth Guard / Auth Middleware** | `authBearer()` / `authCookie()` / `authApiKey()` | `pikku-security` |
62
- | **Authorization / Permissions** | `pikkuPermission` / `pikkuAuth` | `pikku-security` |
63
- | **DTO / Request Validation** | Standard Schema (Zod, Valibot, ArkType) | (this skill) |
64
- | **Dependency Injection** | `pikkuServices` (singleton) + `pikkuWireServices` (per-request) | `pikku-services` |
65
- | **WebSocket handlers** | `wireChannel` | `pikku-websocket` |
66
- | **Job Queue workers** | `wireQueueWorker` | `pikku-queue` |
67
- | **Cron / Scheduled tasks** | `wireScheduler` | `pikku-cron` |
68
- | **Module / Feature grouping** | Tags + wiring files | (this skill) |
69
- | **Error handling** | Throw typed errors (`NotFoundError`, `ForbiddenError`) | (this skill) |
70
- | **Type-safe API client** | `npx pikku prebuild` generates clients | (this skill) |
71
- | **Secrets / Config** | `wireSecret`, `wireVariable`, `services.variables` | `pikku-config` |
56
+ Controllers/routes → `pikkuFunc`; middleware/auth/permissions → `pikku-security`; DI → `pikku-services`; transports (HTTP/WS/queue/cron) → their `wire*` + skill. For the full Generic Backend Pikku mapping table (with side-by-side code examples), read `references/concept-mapping.md`.
72
57
 
73
58
  ## Functions
74
59
 
75
60
  Three main function types:
76
61
 
77
62
  ```typescript
78
- // Requires authentication — receives session in wire context
79
- const updateTodo = pikkuFunc<UpdateInput, TodoOutput>(
80
- async (services, data, wire) => {
63
+ // Requires authentication — receives session in wire context.
64
+ // input/output are Zod schemas; the data + return types are inferred from them.
65
+ const updateTodo = pikkuFunc({
66
+ input: UpdateTodoInput,
67
+ output: TodoOutput,
68
+ func: async (services, data, wire) => {
81
69
  const session = await wire.session.get()
82
70
  return services.todoStore.update(data.id, data)
83
- }
84
- )
71
+ },
72
+ })
85
73
 
86
74
  // No authentication required
87
- const listTodos = pikkuSessionlessFunc<ListInput, TodoListOutput>(
88
- async (services, data) => {
75
+ const listTodos = pikkuSessionlessFunc({
76
+ input: ListTodosInput,
77
+ output: TodoListOutput,
78
+ func: async (services, data) => {
89
79
  return { todos: services.todoStore.list(data.filters) }
90
- }
91
- )
80
+ },
81
+ })
92
82
 
93
83
  // No input or output (for scheduled tasks, lifecycle hooks)
94
84
  const cleanup = pikkuVoidFunc(async (services) => {
@@ -96,23 +86,7 @@ const cleanup = pikkuVoidFunc(async (services) => {
96
86
  })
97
87
  ```
98
88
 
99
- Config object form (recommended):
100
-
101
- ```typescript
102
- const createTodo = pikkuSessionlessFunc({
103
- title: 'Create Todo',
104
- description: 'Create a new todo item',
105
- input: CreateTodoInputSchema,
106
- output: CreateTodoOutputSchema,
107
- func: async ({ logger, todoStore }, { title, priority }) => {
108
- const todo = todoStore.createTodo(title, priority)
109
- logger.info(`Created todo: ${todo.id}`)
110
- return { todo }
111
- },
112
- })
113
- ```
114
-
115
- Full config options:
89
+ Services can be destructured inline in the `func` signature (e.g. `async ({ logger, todoStore }, { title }) => ...`). Full config options:
116
90
 
117
91
  ```typescript
118
92
  pikkuFunc({
@@ -243,33 +217,7 @@ expect(result.todos).toHaveLength(3)
243
217
 
244
218
  ## Available Packages
245
219
 
246
- ### Runtime Adapters
247
-
248
- | Package | Use Case |
249
- | ----------------------------- | ------------------------------------- |
250
- | `@pikku/express-server` | Express standalone server |
251
- | `@pikku/express-middleware` | Express as middleware in existing app |
252
- | `@pikku/fastify-server` | Fastify standalone |
253
- | `@pikku/fastify-plugin` | Fastify plugin |
254
- | `@pikku/next` | Next.js API routes |
255
- | `@pikku/aws-lambda` | AWS Lambda handlers |
256
- | `@pikku/cloudflare` | Cloudflare Workers |
257
- | `@pikku/uws-server` | uWebSockets.js (high perf) |
258
- | `@pikku/modelcontextprotocol` | MCP server |
259
-
260
- ### Service Packages
261
-
262
- | Package | Provides |
263
- | ------------------------ | ---------------------------------------------------- |
264
- | `@pikku/jose` | JWT (sign/verify) via jose library |
265
- | `@pikku/schema-ajv` | Schema validation via AJV |
266
- | `@pikku/schema-cfworker` | Schema validation for Cloudflare |
267
- | `@pikku/pino` | Structured logging via Pino |
268
- | `@pikku/kysely` | Type-safe SQL via Kysely (PostgreSQL, SQLite, MySQL) |
269
- | `@pikku/redis` | Redis client |
270
- | `@pikku/queue-bullmq` | Job queues via BullMQ |
271
- | `@pikku/queue-pg-boss` | Job queues via PgBoss |
272
- | `@pikku/aws-services` | AWS SDK (SQS, DynamoDB, etc.) |
220
+ Pikku ships runtime adapters (`@pikku/express-server`, `@pikku/fastify-server`, `@pikku/next`, `@pikku/aws-lambda`, `@pikku/cloudflare`, `@pikku/uws-server`, `@pikku/modelcontextprotocol`, ...) and service packages (`@pikku/jose`, `@pikku/schema-ajv`, `@pikku/pino`, `@pikku/kysely`, `@pikku/redis`, `@pikku/queue-bullmq`, `@pikku/queue-pg-boss`, ...). For the full list with use cases, read `references/packages.md`.
273
221
 
274
222
  ## Key Differences from Traditional Frameworks
275
223
 
@@ -1,6 +1,25 @@
1
1
  # Concept Mapping: Generic Backend → Pikku
2
2
 
3
- Side-by-side code examples showing how common backend patterns translate to Pikku.
3
+ Authoritative mapping table plus side-by-side code examples showing how common backend patterns translate to Pikku.
4
+
5
+ ## Quick Reference Table
6
+
7
+ | Generic Backend Concept | Pikku Equivalent | Skill |
8
+ | --------------------------------------- | --------------------------------------------------------------- | ----------------- |
9
+ | **Controller / Route Handler** | `pikkuFunc` / `pikkuSessionlessFunc` | `pikku-concepts` |
10
+ | **Route definition** (`GET /users/:id`) | `wireHTTP({ route, method, func })` | `pikku-http` |
11
+ | **Middleware** (Express/Koa-style) | `pikkuMiddleware` | `pikku-security` |
12
+ | **Auth Guard / Auth Middleware** | `authBearer()` / `authCookie()` / `authApiKey()` | `pikku-security` |
13
+ | **Authorization / Permissions** | `pikkuPermission` / `pikkuAuth` | `pikku-security` |
14
+ | **DTO / Request Validation** | Standard Schema (Zod, Valibot, ArkType) | `pikku-concepts` |
15
+ | **Dependency Injection** | `pikkuServices` (singleton) + `pikkuWireServices` (per-request) | `pikku-services` |
16
+ | **WebSocket handlers** | `wireChannel` | `pikku-websocket` |
17
+ | **Job Queue workers** | `wireQueueWorker` | `pikku-queue` |
18
+ | **Cron / Scheduled tasks** | `wireScheduler` | `pikku-cron` |
19
+ | **Module / Feature grouping** | Tags + wiring files | `pikku-concepts` |
20
+ | **Error handling** | Throw typed errors (`NotFoundError`, `ForbiddenError`) | `pikku-concepts` |
21
+ | **Type-safe API client** | `npx pikku prebuild` generates clients | `pikku-concepts` |
22
+ | **Secrets / Config** | `wireSecret`, `wireVariable`, `services.variables` | `pikku-config` |
4
23
 
5
24
  ## Route Handler / Controller → pikkuFunc
6
25
 
@@ -21,14 +40,17 @@ class TodoController {
21
40
  **Pikku:**
22
41
 
23
42
  ```typescript
24
- // Function knows nothing about HTTP
25
- const createTodo = pikkuSessionlessFunc<CreateTodoInput, TodoOutput>(
26
- async ({ todoStore, logger }, { title, priority }) => {
43
+ // Function knows nothing about HTTP.
44
+ // input/output are Zod schemas; the data + return types are inferred from them.
45
+ const createTodo = pikkuSessionlessFunc({
46
+ input: CreateTodoInput,
47
+ output: TodoOutput,
48
+ func: async ({ todoStore, logger }, { title, priority }) => {
27
49
  const todo = todoStore.createTodo(title, priority)
28
50
  logger.info(`Created todo: ${todo.id}`)
29
51
  return { todo }
30
- }
31
- )
52
+ },
53
+ })
32
54
 
33
55
  // Wiring (separate file) - grouped with defineHTTPRoutes
34
56
  export const todoRoutes = defineHTTPRoutes({
@@ -68,13 +90,15 @@ async getUser(req: Request, res: Response) {
68
90
  **Pikku:**
69
91
 
70
92
  ```typescript
71
- // All sources merged into a single typed `data` object
72
- const getUser = pikkuSessionlessFunc<
73
- { id: string; fields?: string },
74
- UserOutput
75
- >(async (services, { id, fields }) => {
76
- // id comes from route param, fields from query - function doesn't care
77
- return { user: await services.db.getUser(id, fields) }
93
+ // All sources merged into a single typed `data` object.
94
+ // input/output are Zod schemas; the data + return types are inferred from them.
95
+ const getUser = pikkuSessionlessFunc({
96
+ input: z.object({ id: z.string(), fields: z.string().optional() }),
97
+ output: UserOutput,
98
+ func: async (services, { id, fields }) => {
99
+ // id comes from route param, fields from query - function doesn't care
100
+ return { user: await services.db.getUser(id, fields) }
101
+ },
78
102
  })
79
103
 
80
104
  wireHTTP({ method: 'get', route: '/users/:id', func: getUser, auth: false })
@@ -0,0 +1,29 @@
1
+ # Available Pikku Packages
2
+
3
+ ## Runtime Adapters
4
+
5
+ | Package | Use Case |
6
+ | ----------------------------- | ------------------------------------- |
7
+ | `@pikku/express-server` | Express standalone server |
8
+ | `@pikku/express-middleware` | Express as middleware in existing app |
9
+ | `@pikku/fastify-server` | Fastify standalone |
10
+ | `@pikku/fastify-plugin` | Fastify plugin |
11
+ | `@pikku/next` | Next.js API routes |
12
+ | `@pikku/aws-lambda` | AWS Lambda handlers |
13
+ | `@pikku/cloudflare` | Cloudflare Workers |
14
+ | `@pikku/uws-server` | uWebSockets.js (high perf) |
15
+ | `@pikku/modelcontextprotocol` | MCP server |
16
+
17
+ ## Service Packages
18
+
19
+ | Package | Provides |
20
+ | ------------------------ | ---------------------------------------------------- |
21
+ | `@pikku/jose` | JWT (sign/verify) via jose library |
22
+ | `@pikku/schema-ajv` | Schema validation via AJV |
23
+ | `@pikku/schema-cfworker` | Schema validation for Cloudflare |
24
+ | `@pikku/pino` | Structured logging via Pino |
25
+ | `@pikku/kysely` | Type-safe SQL via Kysely (PostgreSQL, SQLite, MySQL) |
26
+ | `@pikku/redis` | Redis client |
27
+ | `@pikku/queue-bullmq` | Job queues via BullMQ |
28
+ | `@pikku/queue-pg-boss` | Job queues via PgBoss |
29
+ | `@pikku/aws-services` | AWS SDK (SQS, DynamoDB, etc.) |
@@ -34,67 +34,14 @@ Follow existing patterns you find (naming, tag usage, file organization). See `p
34
34
 
35
35
  ## API Reference
36
36
 
37
- ### `wireHTTP(config)`
37
+ - `wireHTTP(config)` (from `@pikku/core/http`) — wire one function to one endpoint.
38
+ - `defineHTTPRoutes(config)` + `wireHTTPRoutes(config)` (from `.pikku/pikku-types.gen.js`) — group routes with shared config; composable/nestable.
38
39
 
39
- Wire a single function to an HTTP endpoint.
40
+ Function input/output types come from the function's own `input:`/`output:` zod schemas — never declared in the wiring. Route `:params`, query params, and body are merged into the function's `data` arg (see Data Flow).
40
41
 
41
- ```typescript
42
- import { wireHTTP } from '@pikku/core/http'
43
-
44
- wireHTTP({
45
- method: 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head',
46
- route: string, // e.g. '/books/:bookId' — :params become data fields
47
- func: PikkuFunc, // The function to call
48
- auth?: boolean, // Override default auth (true = require session)
49
- tags?: string[], // For grouping, middleware targeting
50
- permissions?: Record<string, PikkuPermission | PikkuPermission[]>,
51
- middleware?: PikkuMiddleware[],
52
- sse?: boolean, // Enable Server-Sent Events
53
- contentType?: 'xml' | 'json', // Response content type
54
- timeout?: number, // Request timeout in ms
55
- headers?: HTTPHeadersSchema, // Expected headers schema
56
- docs?: HTTPRouteDocsConfig, // OpenAPI docs config
57
- })
58
- ```
59
-
60
- ### `defineHTTPRoutes(config)` + `wireHTTPRoutes(config)`
42
+ Config cascading across groups: `basePath` concatenates down the chain, `tags` merge (union), `auth` child overrides parent.
61
43
 
62
- Group routes with shared configuration. Groups are composable and nestable.
63
-
64
- ```typescript
65
- import { defineHTTPRoutes, wireHTTPRoutes } from '.pikku/pikku-types.gen.js'
66
-
67
- const routes = defineHTTPRoutes({
68
- basePath?: string, // Prepended to all route paths
69
- tags?: string[], // Applied to all routes in group
70
- auth?: boolean, // Default auth for all routes (overridable per-route)
71
- middleware?: PikkuMiddleware[],
72
- routes: {
73
- [key: string]: {
74
- method: string,
75
- route: string,
76
- func: PikkuFunc,
77
- auth?: boolean, // Override group auth
78
- permissions?: Record<string, PikkuPermission | PikkuPermission[]>,
79
- middleware?: PikkuMiddleware[],
80
- }
81
- }
82
- })
83
-
84
- wireHTTPRoutes({
85
- basePath?: string, // Top-level prefix (e.g. '/api/v1')
86
- middleware?: PikkuMiddleware[],
87
- routes: {
88
- [key: string]: ReturnType<typeof defineHTTPRoutes>,
89
- }
90
- })
91
- ```
92
-
93
- Config cascading rules:
94
-
95
- - `basePath` — concatenates down the chain
96
- - `tags` — merge (union)
97
- - `auth` — child overrides parent
44
+ For the full option tables (every `wireHTTP` field, the `defineHTTPRoutes`/`wireHTTPRoutes` config shape), read `references/http-options.md`.
98
45
 
99
46
  ### `addHTTPMiddleware(pattern, middlewares)`
100
47
 
@@ -137,7 +84,7 @@ wireHTTP({
137
84
  const booksRoutes = defineHTTPRoutes({
138
85
  tags: ['books'],
139
86
  routes: {
140
- list: { method: 'get', route: '/books', func: listBooks, auth: false },
87
+ list: { method: 'get', route: '/books', func: listBooks, auth: false }, // per-route override
141
88
  get: { method: 'get', route: '/books/:bookId', func: getBook },
142
89
  create: { method: 'post', route: '/books', func: createBook },
143
90
  delete: { method: 'delete', route: '/books/:bookId', func: deleteBook },
@@ -145,24 +92,19 @@ const booksRoutes = defineHTTPRoutes({
145
92
  })
146
93
 
147
94
  const todosRoutes = defineHTTPRoutes({
148
- auth: false,
95
+ auth: false, // group-level default, overridable per-route
149
96
  tags: ['todos'],
150
97
  routes: {
151
98
  list: { method: 'get', route: '/todos', func: listTodos },
152
- create: { method: 'post', route: '/todos', func: createTodo },
153
- get: { method: 'get', route: '/todos/:id', func: getTodo },
154
99
  },
155
100
  })
156
101
 
157
102
  wireHTTPRoutes({
158
103
  basePath: '/api/v1',
159
104
  middleware: [cors()],
160
- routes: {
161
- books: booksRoutes,
162
- todos: todosRoutes,
163
- },
105
+ routes: { books: booksRoutes, todos: todosRoutes },
164
106
  })
165
- // Results in: GET /api/v1/books, POST /api/v1/books, etc.
107
+ // Results in: GET /api/v1/books, POST /api/v1/books, GET /api/v1/todos, etc.
166
108
  ```
167
109
 
168
110
  ### Auth & Permissions
@@ -258,60 +200,27 @@ const deleted = await pikkuFetch.delete('/api/v1/books/:bookId', {
258
200
 
259
201
  ## Complete Example
260
202
 
203
+ Functions live in their own files (one per file) and supply behavior + `permissions`; the wiring file imports them and wires routes. Sessionless funcs need no session; `pikkuFunc` does.
204
+
261
205
  ```typescript
262
206
  // functions/books.functions.ts
263
207
  import { pikkuFunc, pikkuSessionlessFunc } from '#pikku'
264
208
 
265
209
  export const listBooks = pikkuSessionlessFunc({
266
210
  title: 'List Books',
267
- func: async ({ db }, { limit }) => {
268
- return { books: await db.listBooks(limit) }
269
- },
211
+ func: async ({ db }, { limit }) => ({ books: await db.listBooks(limit) }),
270
212
  })
271
213
 
272
214
  export const getBook = pikkuFunc({
273
215
  title: 'Get Book',
274
216
  description: 'Retrieve a book by ID',
275
- func: async ({ db }, { bookId }) => {
276
- return await db.getBook(bookId)
277
- },
217
+ func: async ({ db }, { bookId }) => await db.getBook(bookId),
278
218
  permissions: { user: isAuthenticated },
279
219
  })
280
220
 
281
- export const createBook = pikkuFunc({
282
- title: 'Create Book',
283
- func: async ({ db }, { title, author }) => {
284
- return await db.createBook({ title, author })
285
- },
286
- })
287
-
288
- export const deleteBook = pikkuFunc({
289
- title: 'Delete Book',
290
- func: async ({ db }, { bookId }) => {
291
- await db.deleteBook(bookId)
292
- return { deleted: true }
293
- },
294
- })
295
-
296
- // wirings/books.http.ts
297
- import { defineHTTPRoutes, wireHTTPRoutes } from '.pikku/pikku-types.gen.js'
221
+ // wirings/books.http.ts same defineHTTPRoutes/wireHTTPRoutes shape as the Route Groups example above
298
222
  import { addHTTPMiddleware } from '@pikku/core/http'
299
223
  import { cors, authBearer } from '@pikku/core/middleware'
300
224
 
301
- const booksRoutes = defineHTTPRoutes({
302
- tags: ['books'],
303
- routes: {
304
- list: { method: 'get', route: '/books', func: listBooks, auth: false },
305
- get: { method: 'get', route: '/books/:bookId', func: getBook },
306
- create: { method: 'post', route: '/books', func: createBook },
307
- delete: { method: 'delete', route: '/books/:bookId', func: deleteBook },
308
- },
309
- })
310
-
311
- wireHTTPRoutes({
312
- basePath: '/api',
313
- routes: { books: booksRoutes },
314
- })
315
-
316
225
  addHTTPMiddleware('*', [cors(), authBearer()])
317
226
  ```
@@ -0,0 +1,57 @@
1
+ # wireHTTP / defineHTTPRoutes / wireHTTPRoutes — full option reference
2
+
3
+ ## `wireHTTP(config)`
4
+
5
+ Wire a single function to an HTTP endpoint. Import from `@pikku/core/http`.
6
+
7
+ | Option | Type | Notes |
8
+ | --- | --- | --- |
9
+ | `method` | `'get' \| 'post' \| 'put' \| 'patch' \| 'delete' \| 'head'` | HTTP verb |
10
+ | `route` | `string` | e.g. `/books/:bookId` — `:params` become `data` fields |
11
+ | `func` | `PikkuFunc` | The function to call |
12
+ | `auth?` | `boolean` | Override default auth (`true` = require session) |
13
+ | `tags?` | `string[]` | For grouping, middleware targeting |
14
+ | `permissions?` | `Record<string, PikkuPermission \| PikkuPermission[]>` | Permission checks |
15
+ | `middleware?` | `PikkuMiddleware[]` | Per-route middleware |
16
+ | `sse?` | `boolean` | Enable Server-Sent Events |
17
+ | `contentType?` | `'xml' \| 'json'` | Response content type |
18
+ | `timeout?` | `number` | Request timeout in ms |
19
+ | `headers?` | `HTTPHeadersSchema` | Expected headers schema |
20
+ | `docs?` | `HTTPRouteDocsConfig` | OpenAPI docs config |
21
+
22
+ ## `defineHTTPRoutes(config)` + `wireHTTPRoutes(config)`
23
+
24
+ Group routes with shared configuration. Groups are composable and nestable. Import from `.pikku/pikku-types.gen.js`.
25
+
26
+ ```typescript
27
+ const routes = defineHTTPRoutes({
28
+ basePath?: string, // Prepended to all route paths
29
+ tags?: string[], // Applied to all routes in group
30
+ auth?: boolean, // Default auth for all routes (overridable per-route)
31
+ middleware?: PikkuMiddleware[],
32
+ routes: {
33
+ [key: string]: {
34
+ method: string,
35
+ route: string,
36
+ func: PikkuFunc,
37
+ auth?: boolean, // Override group auth
38
+ permissions?: Record<string, PikkuPermission | PikkuPermission[]>,
39
+ middleware?: PikkuMiddleware[],
40
+ }
41
+ }
42
+ })
43
+
44
+ wireHTTPRoutes({
45
+ basePath?: string, // Top-level prefix (e.g. '/api/v1')
46
+ middleware?: PikkuMiddleware[],
47
+ routes: {
48
+ [key: string]: ReturnType<typeof defineHTTPRoutes>,
49
+ }
50
+ })
51
+ ```
52
+
53
+ Config cascading rules:
54
+
55
+ - `basePath` — concatenates down the chain
56
+ - `tags` — merge (union)
57
+ - `auth` — child overrides parent
@@ -77,20 +77,17 @@ wireHTTP({
77
77
 
78
78
  ## Global Middleware (`addGlobalMiddleware`)
79
79
 
80
- `addGlobalMiddleware` registers middleware that runs before everything else across every wire type: HTTP, Queue, Channel, Trigger, Scheduler, Workflow, Agent, CLI, MCP. Use it for cross-cutting concerns like telemetry that must wrap every invocation regardless of transport.
80
+ Runs before everything else, across every wire type: HTTP, Queue, Channel, Trigger, Scheduler, Workflow, Agent, CLI, MCP. Use it for cross-cutting concerns (e.g. telemetry) that must wrap every invocation regardless of transport.
81
81
 
82
82
  ```typescript
83
83
  import { addGlobalMiddleware } from '@pikku/core'
84
84
  import { telemetryOuter, telemetryInner } from '@pikku/core/middleware'
85
85
 
86
- // Outer telemetry: wraps the full call (highest priority)
87
- addGlobalMiddleware([telemetryOuter({ environmentId: env.STAGE_ID })])
88
-
89
- // Inner telemetry: closest to the function body (lowest priority)
90
- addGlobalMiddleware([telemetryInner({ environmentId: env.STAGE_ID })])
86
+ addGlobalMiddleware([telemetryOuter({ environmentId: env.STAGE_ID })]) // wraps the full call
87
+ addGlobalMiddleware([telemetryInner({ environmentId: env.STAGE_ID })]) // closest to the function body
91
88
  ```
92
89
 
93
- `telemetryOuter` ships with `priority: 'highest'` and `telemetryInner` with `priority: 'lowest'` — so even if both are added in the same call, priority sorting places outer first regardless of array order.
90
+ `telemetryOuter` ships with `priority: 'highest'`, `telemetryInner` with `priority: 'lowest'` — so priority sorting places outer first regardless of array/call order.
94
91
 
95
92
  ## HTTP & Prefix Middleware (`addHTTPMiddleware`)
96
93
 
@@ -165,15 +162,13 @@ const earlyMiddleware = pikkuMiddleware({
165
162
  })
166
163
  ```
167
164
 
168
- Within the same priority level, registration order is preserved. Priority is the primary sort key — use it when a middleware must run before or after others regardless of registration order (e.g. telemetry wrapping everything, session extraction before auth checks).
169
-
170
- ## Common Patterns
165
+ Priority is the primary sort key; within the same level, registration order is preserved. Use priority when a middleware must run before/after others regardless of registration order (e.g. telemetry wrapping everything, session extraction before auth checks).
171
166
 
172
- ### Service-to-Service Bearer Auth
167
+ ## Service-to-Service Bearer Auth (canonical pattern)
173
168
 
174
- The canonical pattern for a server that exposes RPCs only to a trusted caller (e.g. an API calling a machine-agent):
169
+ A server that exposes RPCs only to a trusted caller (e.g. an API calling a machine-agent). Auth lives in a tag middleware — NOT in the function body. Authorization/permission checks belong in the `permissions` field (see `pikku-permissions`), never inside `func`.
175
170
 
176
- **On the server (the service being called):**
171
+ **On the server (the service being called):** tag the function, register a `pikkuMiddleware` that reads the `Authorization` header on that tag.
177
172
 
178
173
  ```typescript
179
174
  // lib/host-token.ts
@@ -217,63 +212,11 @@ export const myFunc = pikkuSessionlessFunc({
217
212
  })
218
213
  ```
219
214
 
220
- **On the client (the caller):**
221
-
222
- Use the generated `RPCInvoke` type from `.pikku/rpc/pikku-rpc-wirings-map.gen.d.ts` — never hand-write the input/output types:
223
-
224
- ```typescript
225
- import type { RPCInvoke } from '../../backends/my-service/.pikku/rpc/pikku-rpc-wirings-map.gen.d.js'
226
-
227
- export function getServiceRPC(baseUrl: string, token: string): RPCInvoke {
228
- return async (name: string, data?: unknown) => {
229
- const res = await fetch(`${baseUrl}/rpc/${String(name)}`, {
230
- method: 'POST',
231
- headers: {
232
- 'Content-Type': 'application/json',
233
- Authorization: `Bearer ${token}`,
234
- },
235
- body: JSON.stringify({ data: data ?? {} }),
236
- })
237
- if (!res.ok) {
238
- const text = await res.text().catch(() => '')
239
- throw new Error(`rpc ${String(name)} failed: ${res.status} ${text}`)
240
- }
241
- return res.json()
242
- } as RPCInvoke
243
- }
244
- ```
245
-
246
- ### Session-Setting Middleware
247
-
248
- ```typescript
249
- const apiKeyAuth = pikkuMiddleware(async ({ kysely }, { http, setSession, session }, next) => {
250
- if (session) return next() // already authenticated
251
-
252
- const header = http?.request?.header?.('x-api-key')
253
- if (!header) return next()
254
-
255
- const row = await kysely.selectFrom('apiKey').select('userId').where('key', '=', header).executeTakeFirst()
256
- if (row) setSession?.({ userId: row.userId })
257
-
258
- return next()
259
- })
260
-
261
- addTagMiddleware('api-key-auth', [apiKeyAuth])
262
- ```
263
-
264
- Functions tagged `'api-key-auth'` with `auth: true` reject requests without a valid key; those with `auth: false` can inspect the session but won't reject.
215
+ **On the client (the caller):** use the generated `RPCInvoke` type — never hand-write a `fetch` wrapper's types. See `references/middleware-patterns.md`.
265
216
 
266
- ### Request Logging / Audit
217
+ ## More patterns
267
218
 
268
- ```typescript
269
- const auditLog = pikkuMiddleware(async ({ logger, db }, wire, next) => {
270
- const start = Date.now()
271
- await next()
272
- await db.createAuditLog({ duration: Date.now() - start })
273
- })
274
-
275
- addHTTPMiddleware('/admin/*', [auditLog])
276
- ```
219
+ `references/middleware-patterns.md` covers the client-side `RPCInvoke` caller, session-setting middleware (set a session from an API key), and request logging / audit middleware.
277
220
 
278
221
  ## After Changes
279
222
 
@@ -0,0 +1,61 @@
1
+ # Middleware Patterns (extended)
2
+
3
+ Detailed, less-common middleware recipes. The common-path bearer-auth pattern lives inline in SKILL.md; this file holds the client-side caller, session-setting, and audit recipes.
4
+
5
+ ## Service-to-Service: the client (caller) side
6
+
7
+ Use the generated `RPCInvoke` type from `.pikku/rpc/pikku-rpc-wirings-map.gen.d.ts` — never hand-write the input/output types:
8
+
9
+ ```typescript
10
+ import type { RPCInvoke } from '../../backends/my-service/.pikku/rpc/pikku-rpc-wirings-map.gen.d.js'
11
+
12
+ export function getServiceRPC(baseUrl: string, token: string): RPCInvoke {
13
+ return async (name: string, data?: unknown) => {
14
+ const res = await fetch(`${baseUrl}/rpc/${String(name)}`, {
15
+ method: 'POST',
16
+ headers: {
17
+ 'Content-Type': 'application/json',
18
+ Authorization: `Bearer ${token}`,
19
+ },
20
+ body: JSON.stringify({ data: data ?? {} }),
21
+ })
22
+ if (!res.ok) {
23
+ const text = await res.text().catch(() => '')
24
+ throw new Error(`rpc ${String(name)} failed: ${res.status} ${text}`)
25
+ }
26
+ return res.json()
27
+ } as RPCInvoke
28
+ }
29
+ ```
30
+
31
+ ## Session-Setting Middleware
32
+
33
+ ```typescript
34
+ const apiKeyAuth = pikkuMiddleware(async ({ kysely }, { http, setSession, session }, next) => {
35
+ if (session) return next() // already authenticated
36
+
37
+ const header = http?.request?.header?.('x-api-key')
38
+ if (!header) return next()
39
+
40
+ const row = await kysely.selectFrom('apiKey').select('userId').where('key', '=', header).executeTakeFirst()
41
+ if (row) setSession?.({ userId: row.userId })
42
+
43
+ return next()
44
+ })
45
+
46
+ addTagMiddleware('api-key-auth', [apiKeyAuth])
47
+ ```
48
+
49
+ Functions tagged `'api-key-auth'` with `auth: true` reject requests without a valid key; those with `auth: false` can inspect the session but won't reject.
50
+
51
+ ## Request Logging / Audit
52
+
53
+ ```typescript
54
+ const auditLog = pikkuMiddleware(async ({ logger, db }, wire, next) => {
55
+ const start = Date.now()
56
+ await next()
57
+ await db.createAuditLog({ duration: Date.now() - start })
58
+ })
59
+
60
+ addHTTPMiddleware('/admin/*', [auditLog])
61
+ ```