@pikku/cli 0.12.91 → 0.12.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +106 -0
- package/console-app/assets/{index-CpkvmAGb.js → index-DDpIMCpy.js} +149 -149
- package/console-app/index.html +1 -1
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +6 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +15 -0
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +21 -0
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +35 -40
- package/dist/.pikku/function/pikku-functions.gen.js +1 -3
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +2 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +2 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.d.ts +4 -0
- package/dist/.pikku/scenarios/schemas/register.gen.js +4 -0
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +5 -3
- package/dist/.pikku/schemas/schemas/FabricSecretsListOutput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/FabricSecretsRotateInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricSecretsRotateOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricSecretsSetOutput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/ScenarioRunInput.schema.json +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/meta/allWorkflow.gen.json +2 -8
- package/dist/.pikku/workflow/pikku-scenario-actors.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-scenario-actors.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/bin/pikku-bin.mjs +2 -2
- package/dist/src/cli.wiring.js +6 -0
- package/dist/src/deploy/analyzer/analyzer.js +22 -5
- package/dist/src/deploy/build-pipeline.js +5 -1
- package/dist/src/fabric/fabric-commands.d.ts +37 -9
- package/dist/src/fabric/fabric-commands.js +12 -0
- package/dist/src/fabric/functions/domains-add.function.d.ts +4 -4
- package/dist/src/fabric/functions/secrets-list.function.d.ts +16 -4
- package/dist/src/fabric/functions/secrets-list.function.js +12 -10
- package/dist/src/fabric/functions/secrets-rotate.function.d.ts +24 -0
- package/dist/src/fabric/functions/secrets-rotate.function.js +36 -0
- package/dist/src/fabric/functions/secrets-set.function.d.ts +8 -4
- package/dist/src/fabric/functions/secrets-set.function.js +18 -7
- package/dist/src/fabric/lib/http.d.ts +7 -1
- package/dist/src/fabric/lib/sealed-box.d.ts +16 -0
- package/dist/src/fabric/lib/sealed-box.js +72 -0
- package/dist/src/functions/commands/dev.js +9 -0
- package/dist/src/functions/commands/pikku-command-bootstrap.js +1 -0
- package/dist/src/functions/commands/scenario-browser.d.ts +17 -2
- package/dist/src/functions/commands/scenario-browser.js +16 -8
- package/dist/src/functions/commands/scenario-environment.d.ts +32 -0
- package/dist/src/functions/commands/scenario-environment.js +61 -0
- package/dist/src/functions/commands/scenario.d.ts +6 -0
- package/dist/src/functions/commands/scenario.js +14 -9
- package/dist/src/functions/commands/skills.js +27 -61
- package/dist/src/functions/db/db-codegen.d.ts +6 -0
- package/dist/src/functions/db/db-codegen.js +9 -0
- package/dist/src/functions/db/db-migrator.js +14 -0
- package/dist/src/functions/db/local-db.d.ts +8 -0
- package/dist/src/functions/db/local-db.js +24 -2
- package/dist/src/functions/db/migration-identifiers.d.ts +58 -0
- package/dist/src/functions/db/migration-identifiers.js +262 -0
- package/dist/src/functions/db/migration-provenance.d.ts +35 -0
- package/dist/src/functions/db/migration-provenance.js +80 -0
- package/dist/src/functions/db/schema-sql.d.ts +43 -0
- package/dist/src/functions/db/schema-sql.js +135 -0
- package/dist/src/functions/db/sqlite/sqlite-kysely.js +22 -2
- package/dist/src/functions/wirings/functions/schemas.js +13 -1
- package/dist/src/functions/wirings/rpc/pikku-command-rpc.js +6 -1
- package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.d.ts +10 -0
- package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.js +102 -0
- package/dist/src/functions/wirings/scenarios/scenario-partition.d.ts +13 -0
- package/dist/src/functions/wirings/scenarios/scenario-partition.js +15 -2
- package/dist/src/functions/wirings/scenarios/scenario-schema-partition.d.ts +33 -0
- package/dist/src/functions/wirings/scenarios/scenario-schema-partition.js +49 -0
- package/dist/src/functions/workflows/all.workflow.js +2 -7
- package/dist/src/utils/file-writer.js +14 -2
- package/dist/src/utils/pikku-cli-config.d.ts +14 -0
- package/dist/src/utils/pikku-cli-config.js +34 -0
- package/dist/src/utils/remove-legacy-scaffold-file.d.ts +14 -0
- package/dist/src/utils/remove-legacy-scaffold-file.js +23 -0
- package/dist/src/utils/serialize-schemas.d.ts +3 -1
- package/dist/src/utils/serialize-schemas.js +39 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/dist/.pikku/schemas/schemas/PikkuScenarioFunctionsOutput.schema.json +0 -1
- package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.d.ts +0 -1
- package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.js +0 -31
- package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.d.ts +0 -10
- package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.js +0 -104
- package/skills/pikku-addon/SKILL.md +0 -243
- package/skills/pikku-addon/references/addon-package-manifest.md +0 -63
- package/skills/pikku-ai-agent/SKILL.md +0 -231
- package/skills/pikku-ai-vercel/SKILL.md +0 -82
- package/skills/pikku-ai-voice/SKILL.md +0 -88
- package/skills/pikku-audit/SKILL.md +0 -175
- package/skills/pikku-aws/SKILL.md +0 -111
- package/skills/pikku-backblaze/SKILL.md +0 -71
- package/skills/pikku-better-auth/SKILL.md +0 -298
- package/skills/pikku-cli/SKILL.md +0 -198
- package/skills/pikku-cli/references/complete-example.md +0 -82
- package/skills/pikku-concepts/SKILL.md +0 -250
- package/skills/pikku-concepts/references/concept-mapping.md +0 -556
- package/skills/pikku-concepts/references/packages.md +0 -29
- package/skills/pikku-config/SKILL.md +0 -212
- package/skills/pikku-cron/SKILL.md +0 -214
- package/skills/pikku-deploy-azure/SKILL.md +0 -71
- package/skills/pikku-deploy-cloudflare/SKILL.md +0 -82
- package/skills/pikku-deploy-express/SKILL.md +0 -86
- package/skills/pikku-deploy-fastify/SKILL.md +0 -73
- package/skills/pikku-deploy-lambda/SKILL.md +0 -110
- package/skills/pikku-deploy-nextjs/SKILL.md +0 -78
- package/skills/pikku-deploy-uws/SKILL.md +0 -88
- package/skills/pikku-deps/SKILL.md +0 -98
- package/skills/pikku-emails/SKILL.md +0 -163
- package/skills/pikku-fabric/SKILL.md +0 -318
- package/skills/pikku-fabric-debug/SKILL.md +0 -112
- package/skills/pikku-feature/SKILL.md +0 -258
- package/skills/pikku-gateway-slack/SKILL.md +0 -115
- package/skills/pikku-http/SKILL.md +0 -220
- package/skills/pikku-http/references/http-options.md +0 -55
- package/skills/pikku-i18n/SKILL.md +0 -137
- package/skills/pikku-info/SKILL.md +0 -100
- package/skills/pikku-jose/SKILL.md +0 -105
- package/skills/pikku-kysely/SKILL.md +0 -219
- package/skills/pikku-machine-auth/SKILL.md +0 -183
- package/skills/pikku-mcp/SKILL.md +0 -241
- package/skills/pikku-middleware/SKILL.md +0 -231
- package/skills/pikku-middleware/references/middleware-patterns.md +0 -61
- package/skills/pikku-mongodb/SKILL.md +0 -105
- package/skills/pikku-n8n-import/SKILL.md +0 -109
- package/skills/pikku-n8n-import/SPEC.md +0 -84
- package/skills/pikku-n8n-import/references/addon-mapping.md +0 -121
- package/skills/pikku-n8n-import/references/code-translation.md +0 -121
- package/skills/pikku-n8n-import/references/loops-and-control.md +0 -87
- package/skills/pikku-paraglide/SKILL.md +0 -117
- package/skills/pikku-permissions/SKILL.md +0 -192
- package/skills/pikku-pino/SKILL.md +0 -79
- package/skills/pikku-product-second-opinion/README.md +0 -43
- package/skills/pikku-product-second-opinion/SKILL.md +0 -158
- package/skills/pikku-product-second-opinion/example/sample-report.md +0 -90
- package/skills/pikku-product-second-opinion/references/report-template.md +0 -73
- package/skills/pikku-queue/SKILL.md +0 -240
- package/skills/pikku-react/SKILL.md +0 -212
- package/skills/pikku-react-query/SKILL.md +0 -242
- package/skills/pikku-realtime/SKILL.md +0 -236
- package/skills/pikku-realtime/references/other-routes.md +0 -23
- package/skills/pikku-redis/SKILL.md +0 -90
- package/skills/pikku-rpc/SKILL.md +0 -171
- package/skills/pikku-rtl/SKILL.md +0 -219
- package/skills/pikku-scenario/SKILL.md +0 -382
- package/skills/pikku-schedule/SKILL.md +0 -57
- package/skills/pikku-schema-ajv/SKILL.md +0 -62
- package/skills/pikku-schema-cfworker/SKILL.md +0 -63
- package/skills/pikku-security/SKILL.md +0 -108
- package/skills/pikku-services/SKILL.md +0 -248
- package/skills/pikku-services/references/audit-wire-service.md +0 -34
- package/skills/pikku-software-archaeology/README.md +0 -70
- package/skills/pikku-software-archaeology/SKILL.md +0 -186
- package/skills/pikku-software-archaeology/references/blueprint.schema.json +0 -625
- package/skills/pikku-software-archaeology/references/pikku-mapping.md +0 -49
- package/skills/pikku-software-archaeology/scripts/validate.mjs +0 -173
- package/skills/pikku-tag-middleware/SKILL.md +0 -13
- package/skills/pikku-template-clone/SKILL.md +0 -40
- package/skills/pikku-trigger/SKILL.md +0 -181
- package/skills/pikku-versioning/SKILL.md +0 -173
- package/skills/pikku-websocket/SKILL.md +0 -243
- package/skills/pikku-workflow/SKILL.md +0 -172
- package/skills/pikku-workflow/references/workflow-reference.md +0 -63
- package/skills/pikku-workflows-client/SKILL.md +0 -150
- package/skills/pikku-ws/SKILL.md +0 -47
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-queue
|
|
3
|
-
description: >-
|
|
4
|
-
Use when adding background job processing, async task queues, or distributed workers to a Pikku
|
|
5
|
-
app. Covers wireQueueWorker, job enqueuing, progress tracking, retries, BullMQ and PgBoss
|
|
6
|
-
adapters. TRIGGER when: code uses wireQueueWorker, user asks about background jobs, task queues,
|
|
7
|
-
async processing, BullMQ, PgBoss, or job retries. DO NOT TRIGGER when: user asks about scheduled
|
|
8
|
-
cron tasks (use pikku-cron) or event-driven triggers (use pikku-trigger).
|
|
9
|
-
installGroups: [core]
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# Pikku Queue Wiring
|
|
13
|
-
|
|
14
|
-
## Agent Operating Procedure
|
|
15
|
-
|
|
16
|
-
Use this skill as an execution checklist, not reference material.
|
|
17
|
-
|
|
18
|
-
1. Discover before editing. Prefer OpenCode tools such as `pikku-meta` when available; otherwise run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
|
|
19
|
-
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.
|
|
20
|
-
3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
|
|
21
|
-
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.
|
|
22
|
-
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
23
|
-
|
|
24
|
-
Wire Pikku functions as background queue workers. Supports job control (progress, retry, discard), configurable concurrency, and type-safe job publishing.
|
|
25
|
-
|
|
26
|
-
## Before You Start
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
pikku info functions --verbose # See existing functions and their types
|
|
30
|
-
pikku info tags --verbose # Understand project organization
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
See `pikku-concepts` for the core mental model.
|
|
34
|
-
|
|
35
|
-
## API Reference
|
|
36
|
-
|
|
37
|
-
### `wireQueueWorker(config)`
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
import { wireQueueWorker } from '@pikku/core/queue'
|
|
41
|
-
|
|
42
|
-
wireQueueWorker({
|
|
43
|
-
name: string, // Queue name (unique identifier)
|
|
44
|
-
func: PikkuFunc, // Worker function
|
|
45
|
-
config?: {
|
|
46
|
-
batchSize?: number, // Process N jobs at once
|
|
47
|
-
removeOnComplete?: number | boolean, // Clean up completed jobs
|
|
48
|
-
},
|
|
49
|
-
})
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Wire Object (`wire.queue`)
|
|
53
|
-
|
|
54
|
-
Inside queue worker functions:
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
wire.queue.updateProgress(percent: number) // Report progress (0-100)
|
|
58
|
-
wire.queue.discard(reason: string) // Silently discard job
|
|
59
|
-
wire.queue.fail(reason: string) // Mark job as failed
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Job Publishing
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
const jobId = await queue.add(queueName, data, options?)
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Options:
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
{
|
|
72
|
-
priority?: number, // Higher = processed first
|
|
73
|
-
delay?: number, // Delay in ms before processing
|
|
74
|
-
attempts?: number, // Max retry attempts
|
|
75
|
-
backoff?: {
|
|
76
|
-
type: 'exponential' | 'fixed',
|
|
77
|
-
delay: number, // Base delay in ms
|
|
78
|
-
},
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Usage Patterns
|
|
83
|
-
|
|
84
|
-
### Basic Queue Worker
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
const processReminder = pikkuSessionlessFunc({
|
|
88
|
-
title: 'Process Reminder',
|
|
89
|
-
func: async ({ db, emailService }, { todoId, userId }) => {
|
|
90
|
-
const todo = await db.getTodo(todoId)
|
|
91
|
-
await emailService.sendReminder(userId, todo)
|
|
92
|
-
return { sent: true }
|
|
93
|
-
},
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
wireQueueWorker({
|
|
97
|
-
name: 'todo-reminders',
|
|
98
|
-
func: processReminder,
|
|
99
|
-
})
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Job Control (Progress, Discard, Fail)
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
const processReminder = pikkuSessionlessFunc({
|
|
106
|
-
title: 'Process Reminder',
|
|
107
|
-
func: async ({ db }, { todoId }, wire) => {
|
|
108
|
-
await wire.queue.updateProgress(25)
|
|
109
|
-
|
|
110
|
-
const todo = await db.getTodo(todoId)
|
|
111
|
-
if (!todo) {
|
|
112
|
-
await wire.queue.discard('Todo not found')
|
|
113
|
-
return
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (todo.completed) {
|
|
117
|
-
await wire.queue.fail('Todo already completed')
|
|
118
|
-
return
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
await wire.queue.updateProgress(100)
|
|
122
|
-
return { sent: true }
|
|
123
|
-
},
|
|
124
|
-
})
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Retries & Configuration
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
wireQueueWorker({
|
|
131
|
-
name: 'todo-reminders',
|
|
132
|
-
func: processReminder,
|
|
133
|
-
config: {
|
|
134
|
-
batchSize: 5,
|
|
135
|
-
removeOnComplete: 100,
|
|
136
|
-
},
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
// Enqueue with retry options
|
|
140
|
-
const jobId = await queue.add(
|
|
141
|
-
'todo-reminders',
|
|
142
|
-
{
|
|
143
|
-
todoId: 'abc-123',
|
|
144
|
-
userId: 'user-456',
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
priority: 10,
|
|
148
|
-
delay: 5000,
|
|
149
|
-
attempts: 3,
|
|
150
|
-
backoff: { type: 'exponential', delay: 1000 },
|
|
151
|
-
}
|
|
152
|
-
)
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Type-Safe Queue Publishing
|
|
156
|
-
|
|
157
|
-
After `npx pikku all`:
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
import { PikkuQueue } from '.pikku/pikku-queue.gen.js'
|
|
161
|
-
|
|
162
|
-
const queue = new PikkuQueue(queueService)
|
|
163
|
-
|
|
164
|
-
const jobId = await queue.add('todo-reminders', {
|
|
165
|
-
todoId: 'abc-123',
|
|
166
|
-
userId: 'user-456',
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
const job = await queue.getJob('todo-reminders', jobId)
|
|
170
|
-
const status = await job.status() // 'waiting' | 'active' | 'completed' | 'failed'
|
|
171
|
-
const result = await job.waitForCompletion(30_000)
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### Queue Adapters
|
|
175
|
-
|
|
176
|
-
**BullMQ** (Redis-based):
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
import { BullMQQueueService } from '@pikku/queue-bullmq'
|
|
180
|
-
|
|
181
|
-
const queueService = new BullMQQueueService({
|
|
182
|
-
connection: { host: 'localhost', port: 6379 },
|
|
183
|
-
})
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
**PgBoss** (PostgreSQL-based):
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
import { PgBossQueueService } from '@pikku/queue-pg-boss'
|
|
190
|
-
|
|
191
|
-
const queueService = new PgBossQueueService({
|
|
192
|
-
connectionString: 'postgres://...',
|
|
193
|
-
})
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
## Complete Example
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
// functions/email.functions.ts
|
|
200
|
-
export const sendWelcomeEmail = pikkuSessionlessFunc({
|
|
201
|
-
title: 'Send Welcome Email',
|
|
202
|
-
func: async ({ emailService, db }, { userId }, wire) => {
|
|
203
|
-
await wire.queue.updateProgress(10)
|
|
204
|
-
|
|
205
|
-
const user = await db.getUser(userId)
|
|
206
|
-
if (!user) {
|
|
207
|
-
await wire.queue.discard('User not found')
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
await wire.queue.updateProgress(50)
|
|
212
|
-
await emailService.send({
|
|
213
|
-
to: user.email,
|
|
214
|
-
subject: 'Welcome!',
|
|
215
|
-
template: 'welcome',
|
|
216
|
-
data: { name: user.name },
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
await wire.queue.updateProgress(100)
|
|
220
|
-
return { sent: true, email: user.email }
|
|
221
|
-
},
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
// wirings/queue.wiring.ts
|
|
225
|
-
wireQueueWorker({
|
|
226
|
-
name: 'welcome-emails',
|
|
227
|
-
func: sendWelcomeEmail,
|
|
228
|
-
config: { removeOnComplete: 100 },
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
// Enqueue from another function
|
|
232
|
-
export const registerUser = pikkuSessionlessFunc({
|
|
233
|
-
title: 'Register User',
|
|
234
|
-
func: async ({ db, queue }, { email, name }) => {
|
|
235
|
-
const user = await db.createUser({ email, name })
|
|
236
|
-
await queue.add('welcome-emails', { userId: user.id })
|
|
237
|
-
return { user }
|
|
238
|
-
},
|
|
239
|
-
})
|
|
240
|
-
```
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-react
|
|
3
|
-
description: 'Set up @pikku/react in a React app: PikkuProvider context, createPikku factory, and the usePikkuRPC / usePikkuFetch hooks for direct (non-React-Query) calls. TRIGGER when: the user is bootstrapping a React frontend that talks to a Pikku backend, asks how to wire `PikkuProvider`, or needs to make one-off RPC calls outside of useQuery/useMutation. DO NOT TRIGGER when: the user is asking about useQuery/useMutation hooks (use pikku-react-query) or about workflows (use pikku-workflows-client).'
|
|
4
|
-
installGroups: [core]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Pikku React
|
|
8
|
-
|
|
9
|
-
## Agent Operating Procedure
|
|
10
|
-
|
|
11
|
-
Use this skill as an execution checklist, not reference material.
|
|
12
|
-
|
|
13
|
-
1. Discover before editing. Prefer OpenCode tools such as `pikku-meta` when available; otherwise run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
|
|
14
|
-
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.
|
|
15
|
-
3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
|
|
16
|
-
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.
|
|
17
|
-
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
18
|
-
|
|
19
|
-
`@pikku/react` is the smallest possible binding: a Context provider plus
|
|
20
|
-
two hooks. It does **not** depend on React Query — that's a separate
|
|
21
|
-
opt-in via the generated `api.gen.ts`. Use this skill when setting up the
|
|
22
|
-
provider or making direct RPC calls.
|
|
23
|
-
|
|
24
|
-
## What ships
|
|
25
|
-
|
|
26
|
-
```tsx
|
|
27
|
-
import {
|
|
28
|
-
PikkuProvider,
|
|
29
|
-
createPikku,
|
|
30
|
-
usePikkuFetch,
|
|
31
|
-
usePikkuRPC,
|
|
32
|
-
usePikkuRealtime,
|
|
33
|
-
} from '@pikku/react'
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Five exports. `usePikkuRealtime` is only valid when you wired a
|
|
37
|
-
`PikkuRealtime` class via `createPikku` — see step 3 below.
|
|
38
|
-
|
|
39
|
-
## Resolving the server URL
|
|
40
|
-
|
|
41
|
-
Every client (`createPikku`, realtime, the auth client) resolves its base
|
|
42
|
-
through one shared helper in `src/lib/env.ts`. Write this once:
|
|
43
|
-
|
|
44
|
-
```ts
|
|
45
|
-
// Endpoints come from env, never hardcoded.
|
|
46
|
-
export function apiUrl(): string {
|
|
47
|
-
// SSR: the client hooks only run in the browser, so a placeholder is fine.
|
|
48
|
-
if (import.meta.env.SSR) {
|
|
49
|
-
return import.meta.env.VITE_API_URL ?? '/__api'
|
|
50
|
-
}
|
|
51
|
-
return import.meta.env.VITE_API_URL ?? `${window.location.origin}/api`
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
**Never fall back to `http://localhost:3000`.** `import.meta.env.VITE_API_URL`
|
|
56
|
-
is substituted by Vite at *build* time, so any deploy that supplies the URL as
|
|
57
|
-
a *runtime* env var or platform binding leaves it `undefined` in the shipped
|
|
58
|
-
bundle — the fallback is then the only branch that ever runs in the browser. A
|
|
59
|
-
localhost fallback means every request from a deployed app goes to the user's
|
|
60
|
-
own machine. `origin + '/api'` is same-origin, needs no build-time knowledge of
|
|
61
|
-
the domain, and is correct wherever the app is served from.
|
|
62
|
-
|
|
63
|
-
For local dev, set `VITE_API_URL`, or proxy `/api` → your backend in
|
|
64
|
-
`vite.config.ts` under `server.proxy`. One `/api` entry also covers
|
|
65
|
-
`/api/auth/*`; only add more entries for root-level routes outside `/api`.
|
|
66
|
-
|
|
67
|
-
## Setup at the app root
|
|
68
|
-
|
|
69
|
-
```tsx
|
|
70
|
-
import { createPikku, PikkuProvider } from '@pikku/react'
|
|
71
|
-
import { PikkuFetch } from './pikku/pikku-fetch.gen'
|
|
72
|
-
import { PikkuRPC } from './pikku/pikku-rpc.gen'
|
|
73
|
-
import { apiUrl } from './lib/env'
|
|
74
|
-
|
|
75
|
-
const pikku = createPikku(PikkuFetch, PikkuRPC, {
|
|
76
|
-
serverUrl: apiUrl(),
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
createRoot(document.getElementById('root')!).render(
|
|
80
|
-
<PikkuProvider pikku={pikku}>
|
|
81
|
-
<App />
|
|
82
|
-
</PikkuProvider>
|
|
83
|
-
)
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
If the project also exposes realtime events (see **pikku-realtime**), pass
|
|
87
|
-
the `PikkuRealtime` class as the third argument and the instance gets a
|
|
88
|
-
`realtime` field too:
|
|
89
|
-
|
|
90
|
-
```tsx
|
|
91
|
-
import { PikkuRealtime } from './pikku/realtime.gen'
|
|
92
|
-
|
|
93
|
-
const pikku = createPikku(PikkuFetch, PikkuRPC, PikkuRealtime, {
|
|
94
|
-
serverUrl: apiUrl(),
|
|
95
|
-
})
|
|
96
|
-
// pikku.fetch / pikku.rpc / pikku.realtime — all share the same fetch
|
|
97
|
-
// (server URL + auth configured once).
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
The generated classes come from your `pikku.config.json`:
|
|
101
|
-
|
|
102
|
-
| config field | generated file |
|
|
103
|
-
| ---------------------------- | ----------------------------------------------------- |
|
|
104
|
-
| `clientFiles.fetchFile` | typed HTTP client (`PikkuFetch` class) |
|
|
105
|
-
| `clientFiles.rpcWiringsFile` | RPC client (`PikkuRPC` class) calling all exposed fns |
|
|
106
|
-
| `clientFiles.realtimeFile` | `PikkuRealtime` (websocket events + SSE + channels) |
|
|
107
|
-
|
|
108
|
-
If a file isn't being generated, that field is missing from the config —
|
|
109
|
-
add it and re-run `pikku all`.
|
|
110
|
-
|
|
111
|
-
`createPikku(...)` accepts the same `CorePikkuFetchOptions` as `PikkuFetch`
|
|
112
|
-
plus `serverUrl`. Auth headers, request interceptors, etc. are configured
|
|
113
|
-
on the fetch instance — RPC and realtime inherit them automatically.
|
|
114
|
-
|
|
115
|
-
## Calling an RPC directly (no React Query)
|
|
116
|
-
|
|
117
|
-
Inside a component:
|
|
118
|
-
|
|
119
|
-
```tsx
|
|
120
|
-
import { usePikkuRPC } from '@pikku/react'
|
|
121
|
-
|
|
122
|
-
function Logout() {
|
|
123
|
-
const rpc = usePikkuRPC()
|
|
124
|
-
return <button onClick={() => rpc.invoke('logoutUser', {})}>Sign out</button>
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
`rpc.invoke(name, data)` is typed against `FlattenedRPCMap` — `name` must
|
|
129
|
-
be an exposed function id, `data` matches the input schema, return value
|
|
130
|
-
matches the output schema.
|
|
131
|
-
|
|
132
|
-
You also have `rpc.<funcName>(data)` if the generated RPC client builds
|
|
133
|
-
direct methods (project-dependent).
|
|
134
|
-
|
|
135
|
-
## Calling fetch directly
|
|
136
|
-
|
|
137
|
-
```tsx
|
|
138
|
-
const fetch = usePikkuFetch()
|
|
139
|
-
const data = await fetch.get('/some-rest-route', { searchParams: {...} })
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Use this only when the function is wired via HTTP (REST shape) and you
|
|
143
|
-
need a path-style call. For RPC calls, `usePikkuRPC()` is cleaner.
|
|
144
|
-
|
|
145
|
-
## Realtime subscriptions
|
|
146
|
-
|
|
147
|
-
If you wired a `PikkuRealtime` class into `createPikku`, use
|
|
148
|
-
`usePikkuRealtime()` to grab the shared instance:
|
|
149
|
-
|
|
150
|
-
```tsx
|
|
151
|
-
import { usePikkuRealtime } from '@pikku/react'
|
|
152
|
-
import type { PikkuRealtime } from './pikku/realtime.gen'
|
|
153
|
-
|
|
154
|
-
function TodoList() {
|
|
155
|
-
const realtime = usePikkuRealtime<PikkuRealtime>()
|
|
156
|
-
useEffect(() => {
|
|
157
|
-
return realtime.subscribe('todo-created', ({ todo }) => {
|
|
158
|
-
/* ... */
|
|
159
|
-
})
|
|
160
|
-
}, [realtime])
|
|
161
|
-
// ...
|
|
162
|
-
}
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
The hook throws if no `PikkuRealtime` was wired — that's how you know to
|
|
166
|
-
add it to `createPikku(...)`. Full event-hub setup, publishing, and SSE
|
|
167
|
-
helpers live in **pikku-realtime**.
|
|
168
|
-
|
|
169
|
-
## When to reach for what
|
|
170
|
-
|
|
171
|
-
| Need | Use |
|
|
172
|
-
| ----------------------------------- | --------------------------------------------- |
|
|
173
|
-
| Render data, dedupe + cache | **usePikkuQuery** (react-query) |
|
|
174
|
-
| Trigger a write, wait for result | **usePikkuMutation** (react-query) |
|
|
175
|
-
| Paginate | **usePikkuInfiniteQuery** (react-query) |
|
|
176
|
-
| One-off call from an event handler | `usePikkuRPC()` direct |
|
|
177
|
-
| Hit a REST endpoint (not RPC) | `usePikkuFetch()` |
|
|
178
|
-
| Run a workflow | **pikku-workflows-client** |
|
|
179
|
-
| Subscribe to events / SSE / channel | `usePikkuRealtime()` (see **pikku-realtime**) |
|
|
180
|
-
|
|
181
|
-
The first three live in your generated `api.gen.ts` (see the
|
|
182
|
-
**pikku-react-query** skill). This skill covers the bottom four rows.
|
|
183
|
-
|
|
184
|
-
## Authentication
|
|
185
|
-
|
|
186
|
-
Auth is handled at the `PikkuFetch` layer — pass options to `createPikku`
|
|
187
|
-
or set headers on the fetch instance after creation. Common pattern:
|
|
188
|
-
|
|
189
|
-
```tsx
|
|
190
|
-
const pikku = createPikku(PikkuFetch, PikkuRPC, {
|
|
191
|
-
serverUrl: apiUrl(),
|
|
192
|
-
fetchOptions: {
|
|
193
|
-
onRequest: (req) => {
|
|
194
|
-
const token = localStorage.getItem('token')
|
|
195
|
-
if (token) req.headers.set('Authorization', `Bearer ${token}`)
|
|
196
|
-
},
|
|
197
|
-
},
|
|
198
|
-
})
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
Exact option names depend on the `@pikku/fetch` version — read
|
|
202
|
-
`PikkuFetch`'s constructor type if unsure.
|
|
203
|
-
|
|
204
|
-
## What NOT to do
|
|
205
|
-
|
|
206
|
-
- Don't instantiate `PikkuFetch`/`PikkuRPC` inside a component — `createPikku`
|
|
207
|
-
goes once at the app root, the instance flows through Context.
|
|
208
|
-
- Don't call `usePikkuRPC()` outside a `<PikkuProvider>` — it throws.
|
|
209
|
-
- Don't write a custom RPC client. The generated one already covers every
|
|
210
|
-
exposed function with full types.
|
|
211
|
-
- Don't hardcode user-facing strings. Every display string goes through an
|
|
212
|
-
i18n token — see **pikku-i18n** for the setup (it's English-only by default).
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-react-query
|
|
3
|
-
description: 'Use the Pikku auto-generated React Query hooks (`usePikkuQuery`, `usePikkuMutation`, `usePikkuInfiniteQuery`) to call backend RPC functions from a React frontend with full type safety. TRIGGER when: writing React components that need to call a Pikku function, fetch data, mutate data, or paginate; user mentions React Query, useQuery, useMutation, or building a frontend that talks to a Pikku backend. DO NOT TRIGGER when: working on the backend (use pikku-rpc / pikku-feature) or wiring a non-React frontend.'
|
|
4
|
-
installGroups: [core]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Pikku React Query Hooks
|
|
8
|
-
|
|
9
|
-
## Agent Operating Procedure
|
|
10
|
-
|
|
11
|
-
Use this skill as an execution checklist, not reference material.
|
|
12
|
-
|
|
13
|
-
1. Discover before editing. Prefer OpenCode tools such as `pikku-meta` when available; otherwise run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
|
|
14
|
-
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.
|
|
15
|
-
3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
|
|
16
|
-
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.
|
|
17
|
-
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
18
|
-
|
|
19
|
-
Pikku generates a typed React Query layer from your backend `expose: true`
|
|
20
|
-
functions. You don''t write `useQuery`/`useMutation` against `fetch`
|
|
21
|
-
yourself — you call hooks named after RPCs and get full type inference for
|
|
22
|
-
input + output.
|
|
23
|
-
|
|
24
|
-
## Discover what's available on the client
|
|
25
|
-
|
|
26
|
-
Before writing a hook, get the full client surface in one call:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
yarn pikku meta clients --json
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Returns RPCs, workflows, and channels with descriptions and type names:
|
|
33
|
-
|
|
34
|
-
```json
|
|
35
|
-
{
|
|
36
|
-
"rpcs": [
|
|
37
|
-
{ "name": "createTodo", "description": "Create a todo",
|
|
38
|
-
"readonly": false, "input": "CreateTodoInput", "output": "CreateTodoOutput" },
|
|
39
|
-
{ "name": "listTodos", "description": "List all todos",
|
|
40
|
-
"readonly": true, "input": null, "output": "ListTodosOutput" }
|
|
41
|
-
],
|
|
42
|
-
"workflows": [...],
|
|
43
|
-
"channels": [...]
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
The `name` is the RPC identifier; pass it to the hooks below. Input/output
|
|
48
|
-
shapes are inferred automatically — the hook is typed against
|
|
49
|
-
`FlattenedRPCMap[name]['input' | 'output']`. Use `description` to pick the
|
|
50
|
-
right RPC; use `readonly` to choose `usePikkuQuery` vs `usePikkuMutation`.
|
|
51
|
-
|
|
52
|
-
## Setup (once per app)
|
|
53
|
-
|
|
54
|
-
In your app entry (e.g. `main.tsx`):
|
|
55
|
-
|
|
56
|
-
```tsx
|
|
57
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
58
|
-
import { PikkuProvider, createPikku } from '@pikku/react'
|
|
59
|
-
import { PikkuFetch } from './pikku/pikku-fetch.gen'
|
|
60
|
-
import { PikkuRPC } from './pikku/pikku-rpc.gen'
|
|
61
|
-
|
|
62
|
-
import { apiUrl } from './lib/env'
|
|
63
|
-
|
|
64
|
-
const queryClient = new QueryClient()
|
|
65
|
-
const pikku = createPikku(PikkuFetch, PikkuRPC, {
|
|
66
|
-
serverUrl: apiUrl(),
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
<QueryClientProvider client={queryClient}>
|
|
70
|
-
<PikkuProvider pikku={pikku}>
|
|
71
|
-
<App />
|
|
72
|
-
</PikkuProvider>
|
|
73
|
-
</QueryClientProvider>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
The two generated files come from `pikku.config.json`'s
|
|
77
|
-
`clientFiles.fetchFile` and `clientFiles.rpcWiringsFile`. Hooks live in
|
|
78
|
-
the file at `clientFiles.reactQueryFile` (typically `api.gen.ts`).
|
|
79
|
-
|
|
80
|
-
`apiUrl()` is the shared server-URL helper — see **pikku-react**. Never
|
|
81
|
-
inline `?? 'http://localhost:3000'`: a deploy that supplies the URL as a
|
|
82
|
-
runtime binding leaves `import.meta.env.VITE_API_URL` undefined in the
|
|
83
|
-
bundle, so the fallback is the branch that actually runs.
|
|
84
|
-
|
|
85
|
-
## TanStack Start (SSR)
|
|
86
|
-
|
|
87
|
-
Under Start the provider mounts in `routes/__root.tsx` rather than
|
|
88
|
-
`main.tsx`, and the same module is evaluated on the server. Three things
|
|
89
|
-
differ:
|
|
90
|
-
|
|
91
|
-
1. **`apiUrl()` must have an SSR branch.** `window` is undefined during
|
|
92
|
-
render; return the build-time var or a placeholder (the client hooks
|
|
93
|
-
only fire in the browser).
|
|
94
|
-
2. **Build auth clients lazily.** Better Auth validates its baseURL with
|
|
95
|
-
`new URL(...)` at construction, so a module-scope `createAuthClient`
|
|
96
|
-
crashes SSR on the placeholder. Memoize it behind a getter:
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
let _authClient: ReturnType<typeof createAuthClient> | undefined
|
|
100
|
-
export const authClient = () =>
|
|
101
|
-
(_authClient ??= createAuthClient({ baseURL: `${apiUrl()}/auth` }))
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
3. **The auth baseURL needs the `/auth` suffix.** Better Auth only
|
|
105
|
-
appends its default `/api/auth` when the baseURL carries no path.
|
|
106
|
-
`apiUrl()` already ends in `/api`, so a bare `apiUrl()` leaves the
|
|
107
|
-
client calling `/api/get-session` and 404ing.
|
|
108
|
-
|
|
109
|
-
Server functions that need typed RPC access use the generated shim:
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
pikku tanstack-start # emits the makeApi server-function shim
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## The hooks
|
|
116
|
-
|
|
117
|
-
All hooks are imported from your generated `api.gen.ts`:
|
|
118
|
-
|
|
119
|
-
```tsx
|
|
120
|
-
import {
|
|
121
|
-
usePikkuQuery,
|
|
122
|
-
usePikkuMutation,
|
|
123
|
-
usePikkuInfiniteQuery,
|
|
124
|
-
} from './pikku/api.gen'
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### `usePikkuQuery(name, data, options?)`
|
|
128
|
-
|
|
129
|
-
For RPCs that **read** data. Cacheable. The hook is typed against the RPC's
|
|
130
|
-
input + output.
|
|
131
|
-
|
|
132
|
-
```tsx
|
|
133
|
-
export function TodoList() {
|
|
134
|
-
const { data, isLoading, error } = usePikkuQuery('listTodos', {})
|
|
135
|
-
|
|
136
|
-
if (isLoading) return <p>Loading…</p>
|
|
137
|
-
if (error) return <p>{error.message}</p>
|
|
138
|
-
return (
|
|
139
|
-
<ul>
|
|
140
|
-
{data?.todos.map((t) => (
|
|
141
|
-
<li key={t.id}>{t.title}</li>
|
|
142
|
-
))}
|
|
143
|
-
</ul>
|
|
144
|
-
)
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
The query key is `[name, data]` automatically — no manual key wrangling.
|
|
149
|
-
Pass standard `useQuery` options through (`staleTime`, `enabled`, etc.).
|
|
150
|
-
|
|
151
|
-
### `usePikkuMutation(name, options?)`
|
|
152
|
-
|
|
153
|
-
For RPCs that **write**. Returns a React Query mutation object.
|
|
154
|
-
|
|
155
|
-
```tsx
|
|
156
|
-
export function CreateTodoForm() {
|
|
157
|
-
const queryClient = useQueryClient()
|
|
158
|
-
const mutation = usePikkuMutation('createTodo', {
|
|
159
|
-
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['listTodos'] }),
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
163
|
-
e.preventDefault()
|
|
164
|
-
const title = (
|
|
165
|
-
e.currentTarget.elements.namedItem('title') as HTMLInputElement
|
|
166
|
-
).value
|
|
167
|
-
mutation.mutate({ title })
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return (
|
|
171
|
-
<form onSubmit={onSubmit}>
|
|
172
|
-
<input name="title" />
|
|
173
|
-
<button type="submit" disabled={mutation.isPending}>
|
|
174
|
-
{mutation.isPending ? 'Adding…' : 'Add'}
|
|
175
|
-
</button>
|
|
176
|
-
</form>
|
|
177
|
-
)
|
|
178
|
-
}
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
The input passed to `mutation.mutate(...)` is type-checked against the RPC's
|
|
182
|
-
input schema. After success, **invalidate** any list/get queries that should
|
|
183
|
-
refetch.
|
|
184
|
-
|
|
185
|
-
### `usePikkuInfiniteQuery(name, data, options?)`
|
|
186
|
-
|
|
187
|
-
Only available for RPCs whose output has a `nextCursor?: string | null`
|
|
188
|
-
field — typically a list endpoint with pagination. The hook auto-feeds
|
|
189
|
-
`nextCursor` into the next page's request.
|
|
190
|
-
|
|
191
|
-
```tsx
|
|
192
|
-
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
|
|
193
|
-
usePikkuInfiniteQuery('listTodos', { limit: 20 })
|
|
194
|
-
|
|
195
|
-
const todos = data?.pages.flatMap((p) => p.rows) ?? []
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
If the hook isn't generated for an RPC, the RPC's output doesn't include
|
|
199
|
-
`nextCursor` — paginate it on the backend or use `usePikkuQuery` with
|
|
200
|
-
manual cursor state.
|
|
201
|
-
|
|
202
|
-
## Workflow hooks
|
|
203
|
-
|
|
204
|
-
When the project has workflows (`capabilities.workflow: true`), three
|
|
205
|
-
extra hooks are generated. See the **pikku-workflows-client** skill.
|
|
206
|
-
|
|
207
|
-
## Calling RPCs without React Query
|
|
208
|
-
|
|
209
|
-
For one-off calls (event handlers outside of state, side effects), use
|
|
210
|
-
`usePikkuRPC()` from `@pikku/react`:
|
|
211
|
-
|
|
212
|
-
```tsx
|
|
213
|
-
const rpc = usePikkuRPC()
|
|
214
|
-
const handleClick = async () => {
|
|
215
|
-
const result = await rpc.invoke('createTodo', { title: 'inline' })
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
But prefer the React Query hooks for anything that touches render state —
|
|
220
|
-
caching, retries, dedup, and dev-tools come for free.
|
|
221
|
-
|
|
222
|
-
## Common patterns
|
|
223
|
-
|
|
224
|
-
- **Optimistic updates**: pass `onMutate` to `usePikkuMutation` to update
|
|
225
|
-
the cache before the server responds. Standard React Query pattern;
|
|
226
|
-
Pikku doesn't add anything special.
|
|
227
|
-
- **Conditional fetching**: pass `enabled: !!someValue` to skip a query
|
|
228
|
-
until you have the input.
|
|
229
|
-
- **Refetch on focus**: enabled by default in React Query; disable with
|
|
230
|
-
`refetchOnWindowFocus: false` in options.
|
|
231
|
-
|
|
232
|
-
## What NOT to do
|
|
233
|
-
|
|
234
|
-
- Don't import the RPC client directly and call it inside `useEffect` —
|
|
235
|
-
use the hooks. They handle dedup, caching, and unmount safely.
|
|
236
|
-
- Don't hand-write `useQuery({ queryKey: ['listTodos'], queryFn: ... })`
|
|
237
|
-
— `usePikkuQuery('listTodos', {})` does it correctly with one line.
|
|
238
|
-
- Don't construct hook names dynamically. Hook names = RPC names known at
|
|
239
|
-
generation time.
|
|
240
|
-
- Don't bypass the type system with `as any` — if a hook's types don't
|
|
241
|
-
match what you expect, the backend's input/output schemas are wrong;
|
|
242
|
-
fix those first.
|