howone 0.1.10 → 0.1.12
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/package.json +1 -1
- package/templates/vite/.howone/skills/howone-sdk/SKILL.md +75 -27
- package/templates/vite/.howone/skills/howone-sdk/references/01-client-setup.md +278 -0
- package/templates/vite/.howone/skills/howone-sdk/references/02-entity-operations.md +379 -0
- package/templates/vite/.howone/skills/howone-sdk/references/03-ai-actions.md +489 -0
- package/templates/vite/.howone/skills/howone-sdk/references/04-auth.md +484 -0
- package/templates/vite/.howone/skills/howone-sdk/references/05-file-upload.md +319 -0
- package/templates/vite/.howone/skills/howone-sdk/references/06-react-integration.md +394 -0
- package/templates/vite/.howone/skills/howone-sdk/references/07-raw-http.md +299 -0
- package/templates/vite/.howone/skills/howone-sdk/references/08-manifest-codegen.md +400 -0
- package/templates/vite/.howone/skills/howone-sdk/references/usage-patterns.md +0 -443
package/package.json
CHANGED
|
@@ -1,45 +1,93 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: howone-sdk
|
|
3
|
-
description: Build application code with @howone/sdk correctly. Use when implementing or reviewing HowOne app SDK wiring, src/lib/sdk.ts, dynamic entity bindings, AI action bindings, zod runtime schemas, Vite environment variables, calls like howone.entities.* and howone.ai.*.run, or code generated from .howone/database and .howone/ai manifests.
|
|
3
|
+
description: Build application code with @howone/sdk correctly. Use when implementing or reviewing HowOne app SDK wiring, src/lib/sdk.ts, dynamic entity bindings, AI action bindings, zod runtime schemas, Vite environment variables, calls like howone.entities.* and howone.ai.*.run, file uploads, auth flows, raw HTTP calls, or code generated from .howone/database and .howone/ai manifests.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# HowOne SDK
|
|
6
|
+
# HowOne SDK Skill
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
This skill is the authoritative source of truth for all `@howone/sdk` usage in generated apps. Every capability is documented in a dedicated reference file — use those files to understand the API. Do not read `node_modules` to rediscover imports or signatures.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## Module Reference Map
|
|
11
|
+
|
|
12
|
+
| Module | File | What It Covers |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| Client Setup | `references/01-client-setup.md` | `createClient`, env vars, `CreateClientOptions`, all client fields |
|
|
15
|
+
| Entity Operations | `references/02-entity-operations.md` | CRUD, `query`, `list`, `aggregate`, `bulkCreate`, typed records |
|
|
16
|
+
| AI Actions | `references/03-ai-actions.md` | `defineAiAction`, `run`/`stream`/`events`, zod schemas, SSE callbacks |
|
|
17
|
+
| Auth | `references/04-auth.md` | Email OTP, Phone OTP, OAuth (Google/GitHub), token management |
|
|
18
|
+
| File Upload | `references/05-file-upload.md` | `upload.file`, `upload.image`, `upload.batch`, progress, abort |
|
|
19
|
+
| React Integration | `references/06-react-integration.md` | `HowOneProvider`, `useHowoneContext`, `FloatingButton`, `Loading` |
|
|
20
|
+
| Raw HTTP | `references/07-raw-http.md` | `client.raw.*`, custom API calls, interceptors |
|
|
21
|
+
| Manifest Codegen | `references/08-manifest-codegen.md` | Reading `.howone/database` and `.howone/ai`, generating `src/lib/sdk.ts` |
|
|
11
22
|
|
|
12
23
|
## Workflow
|
|
13
24
|
|
|
14
|
-
1.
|
|
15
|
-
2. Read
|
|
16
|
-
3. Read the existing
|
|
25
|
+
1. Identify which module(s) the task touches using the map above.
|
|
26
|
+
2. Read the corresponding reference file(s) — they contain complete, runnable code examples.
|
|
27
|
+
3. Read the existing `src/lib/sdk.ts` before editing. Preserve its import style and existing bindings.
|
|
17
28
|
4. For Entity bindings, read `.howone/database/manifest.json`.
|
|
18
29
|
5. For AI action bindings, read `.howone/ai/manifest.json`.
|
|
19
|
-
6. Generate
|
|
20
|
-
7. Keep sync tools
|
|
30
|
+
6. Generate code from the manifests, not from memory.
|
|
31
|
+
7. Keep sync tools (`sync_schema_artifacts`, `sync_ai_artifacts`) separate from app-code edits. Those tools write metadata only; the coding agent edits `src/lib/sdk.ts`.
|
|
32
|
+
|
|
33
|
+
## Minimal App Wiring (Quick Reference)
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
// src/lib/sdk.ts
|
|
37
|
+
import {
|
|
38
|
+
createClient,
|
|
39
|
+
defineAiAction,
|
|
40
|
+
defineAiActions,
|
|
41
|
+
defineEntities,
|
|
42
|
+
type EntityRecord,
|
|
43
|
+
withAiActions,
|
|
44
|
+
withEntities,
|
|
45
|
+
} from '@howone/sdk'
|
|
46
|
+
import { z } from 'zod'
|
|
47
|
+
|
|
48
|
+
// Types
|
|
49
|
+
export type TodoRecord = EntityRecord & {
|
|
50
|
+
title: string
|
|
51
|
+
completed: boolean
|
|
52
|
+
}
|
|
53
|
+
export type TodoCreate = { title: string; completed: boolean }
|
|
54
|
+
export type TodoUpdate = Partial<TodoCreate>
|
|
21
55
|
|
|
22
|
-
|
|
56
|
+
// Schemas
|
|
57
|
+
export const summarizeTodoInputSchema = z.object({ title: z.string().min(1) })
|
|
58
|
+
export type SummarizeTodoInput = z.infer<typeof summarizeTodoInputSchema>
|
|
23
59
|
|
|
24
|
-
|
|
60
|
+
// Client
|
|
61
|
+
const client = createClient({
|
|
62
|
+
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
|
|
63
|
+
env: import.meta.env.VITE_HOWONE_ENV,
|
|
64
|
+
})
|
|
25
65
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
66
|
+
// Entities
|
|
67
|
+
export const entities = defineEntities({
|
|
68
|
+
Todo: client.entity<TodoRecord, TodoCreate, TodoUpdate>('Todo'),
|
|
69
|
+
})
|
|
30
70
|
|
|
31
|
-
|
|
71
|
+
// AI Actions
|
|
72
|
+
export const ai = defineAiActions({
|
|
73
|
+
summarizeTodo: defineAiAction('summarizeTodo', {
|
|
74
|
+
inputSchema: summarizeTodoInputSchema,
|
|
75
|
+
}),
|
|
76
|
+
})
|
|
32
77
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- Do not name AI actions `run`, `stream`, or `events`; those are reserved.
|
|
38
|
-
- Define entity create/update types explicitly. Avoid deriving create types from `Omit<EntityRecord & ...>` when it widens the payload.
|
|
39
|
-
- React integration does not provide entity/query/AI hooks. Use plain async SDK calls from React state/effects or an app-level data-fetching library.
|
|
40
|
-
- Never make generated SDK/type files under `.howone`. `.howone` stores manifests only.
|
|
41
|
-
- If an AI-first feature persists generated results, create/update AI capability schema first, sync `.howone/ai`, then derive Entity fields from the AI `outputSchema`.
|
|
78
|
+
// Composed client
|
|
79
|
+
const howone = withAiActions(withEntities(client, entities), ai)
|
|
80
|
+
export default howone
|
|
81
|
+
```
|
|
42
82
|
|
|
43
|
-
##
|
|
83
|
+
## Hard Rules
|
|
44
84
|
|
|
45
|
-
|
|
85
|
+
- **Vite env only**: use `import.meta.env.VITE_HOWONE_PROJECT_ID` and `import.meta.env.VITE_HOWONE_ENV`. Never hardcode project IDs or add `?? 'prod'` fallbacks.
|
|
86
|
+
- **Explicit types**: define `EntityRecord`, `Create`, and `Update` types explicitly. Never derive create types from `Omit<EntityRecord & ...>` — it widens the payload.
|
|
87
|
+
- **AI action naming**: do not name actions `run`, `stream`, or `events` — those are reserved method names.
|
|
88
|
+
- **Call shape**: `howone.ai.<actionName>.run(input)` / `.stream(input)` / `.events(input)`. Never `howone.ai.run.<actionName>(input)`.
|
|
89
|
+
- **No hooks**: `@howone/sdk/react` provides auth/theme/loading UI only — no entity, query, or AI data hooks.
|
|
90
|
+
- **No codegen under `.howone`**: `.howone/` stores manifests only. Never write generated source files there.
|
|
91
|
+
- **AI-first persistence**: define AI output schema first, sync `.howone/ai`, then derive Entity fields from `outputSchema`.
|
|
92
|
+
- **zod for schemas**: always use zod for AI `inputSchema` / `outputSchema`.
|
|
93
|
+
- **`outputSchema` caution**: `run()` validates the full `ExecutionResult` envelope, not just `finalResult`.
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Client Setup
|
|
2
|
+
|
|
3
|
+
## createClient
|
|
4
|
+
|
|
5
|
+
`createClient(opts: CreateClientOptions)` is the single factory for everything in the HowOne SDK. Call it once at module level and export the result (or the composed `howone` client).
|
|
6
|
+
|
|
7
|
+
### CreateClientOptions — full type
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
type Environment = 'local' | 'dev' | 'prod'
|
|
11
|
+
|
|
12
|
+
type CreateClientOptions = {
|
|
13
|
+
// ── Required ──────────────────────────────────────────────
|
|
14
|
+
projectId?: string // Your HowOne project ID (set via VITE_HOWONE_PROJECT_ID)
|
|
15
|
+
|
|
16
|
+
// ── Environment ───────────────────────────────────────────
|
|
17
|
+
env?: Environment | string // 'local' | 'dev' | 'prod' (set via VITE_HOWONE_ENV)
|
|
18
|
+
apiUrl?: string // Override the REST API base URL
|
|
19
|
+
aiUrl?: string // Override the AI/SSE base URL
|
|
20
|
+
|
|
21
|
+
// ── Behaviour ─────────────────────────────────────────────
|
|
22
|
+
caseStyle?: 'camel' | 'snake' // Default: 'camel'
|
|
23
|
+
mode?: 'auto' | 'standalone' | 'embedded'
|
|
24
|
+
|
|
25
|
+
// ── Auth ──────────────────────────────────────────────────
|
|
26
|
+
auth?: {
|
|
27
|
+
mode?: 'none' | 'managed' | 'headless'
|
|
28
|
+
getToken?: () => Promise<string | null> // Custom token provider (headless)
|
|
29
|
+
tokenCacheMs?: number // How long to cache the token
|
|
30
|
+
tokenInjection?: {
|
|
31
|
+
allowedOrigins?: string[]
|
|
32
|
+
waitMs?: number
|
|
33
|
+
clearUrlParamsAfterInjectionMs?: number
|
|
34
|
+
clearAllUrlParams?: boolean
|
|
35
|
+
sensitiveParams?: string[]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ── Limit-exceeded callbacks ───────────────────────────────
|
|
40
|
+
limitExceeded?: {
|
|
41
|
+
onLimitExceeded?: (context: LimitExceededContext) => void
|
|
42
|
+
showUpgradeToast?: boolean
|
|
43
|
+
upgradeUrl?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── Deprecated — do not use in new code ───────────────────
|
|
47
|
+
appId?: string // Use projectId
|
|
48
|
+
baseUrl?: string // Use apiUrl / aiUrl
|
|
49
|
+
apiBaseUrl?: string // Use apiUrl
|
|
50
|
+
aiBaseUrl?: string // Use aiUrl
|
|
51
|
+
authRequired?: boolean // Use auth.mode
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### What createClient returns
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const client = createClient({ ... })
|
|
59
|
+
|
|
60
|
+
client.projectId // string — resolved project ID
|
|
61
|
+
client.appId // string — alias for projectId
|
|
62
|
+
client.caseStyle // 'camel' | 'snake'
|
|
63
|
+
|
|
64
|
+
// Entity factory
|
|
65
|
+
client.entity<TRecord, TCreate, TUpdate>(entityName: string): EntityClient
|
|
66
|
+
|
|
67
|
+
// Typed entity map (populated via withEntities)
|
|
68
|
+
client.entities: Record<string, EntityClient>
|
|
69
|
+
|
|
70
|
+
// AI action runner (low-level)
|
|
71
|
+
client.ai: AiClient
|
|
72
|
+
|
|
73
|
+
// HTTP utilities (low-level — see 07-raw-http.md)
|
|
74
|
+
client.raw: RawHttpClient
|
|
75
|
+
|
|
76
|
+
// File upload
|
|
77
|
+
client.upload.file(file, options?)
|
|
78
|
+
client.upload.image(file)
|
|
79
|
+
client.upload.batch(options)
|
|
80
|
+
|
|
81
|
+
// User profile
|
|
82
|
+
client.me(options?) // Promise<UserProfile | null>
|
|
83
|
+
client.requireMe(options?) // Promise<UserProfile> throws if unauthenticated
|
|
84
|
+
client.session.user() // alias for client.me()
|
|
85
|
+
|
|
86
|
+
// Auth helpers
|
|
87
|
+
client.auth.setToken(token: string | null)
|
|
88
|
+
client.auth.getToken(): string | null
|
|
89
|
+
client.auth.isAuthenticated(): boolean
|
|
90
|
+
client.auth.login(redirect?: string) // redirects to HowOne login page
|
|
91
|
+
client.auth.logout()
|
|
92
|
+
|
|
93
|
+
// URL utilities
|
|
94
|
+
client.sanitizeUrl(opts?: { clearAll?: boolean; sensitiveParams?: string[] })
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Standard Vite Setup
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
// src/lib/sdk.ts
|
|
103
|
+
import {
|
|
104
|
+
createClient,
|
|
105
|
+
defineAiAction,
|
|
106
|
+
defineAiActions,
|
|
107
|
+
defineEntities,
|
|
108
|
+
type EntityRecord,
|
|
109
|
+
withAiActions,
|
|
110
|
+
withEntities,
|
|
111
|
+
} from '@howone/sdk'
|
|
112
|
+
import { z } from 'zod'
|
|
113
|
+
|
|
114
|
+
// ── 1. Create client ─────────────────────────────────────────
|
|
115
|
+
const client = createClient({
|
|
116
|
+
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
|
|
117
|
+
env: import.meta.env.VITE_HOWONE_ENV,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
// ── 2. Define entity types & bind ────────────────────────────
|
|
121
|
+
// (see 02-entity-operations.md for full details)
|
|
122
|
+
export type NoteRecord = EntityRecord & { title: string; body: string }
|
|
123
|
+
export type NoteCreate = { title: string; body: string }
|
|
124
|
+
export type NoteUpdate = Partial<NoteCreate>
|
|
125
|
+
|
|
126
|
+
export const entities = defineEntities({
|
|
127
|
+
Note: client.entity<NoteRecord, NoteCreate, NoteUpdate>('Note'),
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
// ── 3. Define AI actions ─────────────────────────────────────
|
|
131
|
+
// (see 03-ai-actions.md for full details)
|
|
132
|
+
export const summarizeInputSchema = z.object({ noteId: z.string() })
|
|
133
|
+
export type SummarizeInput = z.infer<typeof summarizeInputSchema>
|
|
134
|
+
|
|
135
|
+
export const ai = defineAiActions({
|
|
136
|
+
summarizeNote: defineAiAction('summarizeNote', {
|
|
137
|
+
inputSchema: summarizeInputSchema,
|
|
138
|
+
}),
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// ── 4. Compose and export ────────────────────────────────────
|
|
142
|
+
const howone = withAiActions(withEntities(client, entities), ai)
|
|
143
|
+
export default howone
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Environment Variables
|
|
149
|
+
|
|
150
|
+
In Vite apps, these two env vars are mandatory:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
VITE_HOWONE_PROJECT_ID=proj_xxxxxxxxxxxxxxxx
|
|
154
|
+
VITE_HOWONE_ENV=prod
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Rules:
|
|
158
|
+
- **Do not** add `?? 'prod'` or `?? ''` fallbacks. Missing env vars should surface as misconfiguration errors.
|
|
159
|
+
- **Do not** hardcode project IDs in source. Use the env var.
|
|
160
|
+
- `env` accepts `'local'`, `'dev'`, or `'prod'`. SDK routes API calls to the correct endpoint automatically.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Auth Modes
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
// Managed (default) — SDK handles login redirect and token storage
|
|
168
|
+
const client = createClient({
|
|
169
|
+
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
|
|
170
|
+
env: import.meta.env.VITE_HOWONE_ENV,
|
|
171
|
+
auth: { mode: 'managed' },
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// Headless — provide your own token (e.g. Supabase, Clerk, etc.)
|
|
175
|
+
const client = createClient({
|
|
176
|
+
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
|
|
177
|
+
env: import.meta.env.VITE_HOWONE_ENV,
|
|
178
|
+
auth: {
|
|
179
|
+
mode: 'headless',
|
|
180
|
+
getToken: async () => {
|
|
181
|
+
// return your JWT or null
|
|
182
|
+
return localStorage.getItem('my_token')
|
|
183
|
+
},
|
|
184
|
+
tokenCacheMs: 60_000, // cache for 1 minute
|
|
185
|
+
},
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
// None — no auth, all requests are unauthenticated
|
|
189
|
+
const client = createClient({
|
|
190
|
+
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
|
|
191
|
+
env: import.meta.env.VITE_HOWONE_ENV,
|
|
192
|
+
auth: { mode: 'none' },
|
|
193
|
+
})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Multi-environment Setup
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
// src/lib/sdk.ts
|
|
202
|
+
const client = createClient({
|
|
203
|
+
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
|
|
204
|
+
env: import.meta.env.VITE_HOWONE_ENV,
|
|
205
|
+
// Override URLs only for special deployments
|
|
206
|
+
// apiUrl: import.meta.env.VITE_HOWONE_API_URL,
|
|
207
|
+
// aiUrl: import.meta.env.VITE_HOWONE_AI_URL,
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## UserProfile Type
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
type UserProfile = {
|
|
217
|
+
id: string
|
|
218
|
+
email?: string
|
|
219
|
+
name?: string
|
|
220
|
+
avatarUrl?: string
|
|
221
|
+
roles?: string[]
|
|
222
|
+
metadata?: Record<string, unknown>
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Usage
|
|
226
|
+
const me = await client.me() // null if not logged in
|
|
227
|
+
const me = await client.requireMe() // throws HowOneAuthError if not logged in
|
|
228
|
+
|
|
229
|
+
// Check auth state programmatically
|
|
230
|
+
const isLoggedIn = client.auth.isAuthenticated()
|
|
231
|
+
const token = client.auth.getToken()
|
|
232
|
+
|
|
233
|
+
// Manually set a token (e.g. after custom login flow)
|
|
234
|
+
client.auth.setToken(jwtToken)
|
|
235
|
+
|
|
236
|
+
// Trigger login redirect
|
|
237
|
+
client.auth.login('/dashboard') // optional return path
|
|
238
|
+
|
|
239
|
+
// Logout
|
|
240
|
+
client.auth.logout()
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## HowOneAuthError
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
import { HowOneAuthError } from '@howone/sdk'
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
const user = await client.requireMe()
|
|
252
|
+
} catch (err) {
|
|
253
|
+
if (err instanceof HowOneAuthError) {
|
|
254
|
+
// err.code === 'UNAUTHENTICATED'
|
|
255
|
+
client.auth.login()
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## LimitExceeded Handling
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
const client = createClient({
|
|
266
|
+
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
|
|
267
|
+
env: import.meta.env.VITE_HOWONE_ENV,
|
|
268
|
+
limitExceeded: {
|
|
269
|
+
showUpgradeToast: true,
|
|
270
|
+
upgradeUrl: 'https://howone.app/upgrade',
|
|
271
|
+
onLimitExceeded: (context) => {
|
|
272
|
+
console.error('Limit exceeded:', context.source, context.message)
|
|
273
|
+
// context.source: 'axios-response' | 'workflow-executor-sse' | ...
|
|
274
|
+
// context.status: HTTP status code (if available)
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
})
|
|
278
|
+
```
|