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
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
# HowOne SDK Usage Patterns
|
|
2
|
-
|
|
3
|
-
Working reference for `@howone/sdk` and `@howone/sdk/react`. It covers the SDK surface that generated HowOne apps normally need: client setup, entity bindings, AI action bindings, React provider usage, and the known traps that cause repeated source inspection.
|
|
4
|
-
|
|
5
|
-
## Reading Strategy
|
|
6
|
-
|
|
7
|
-
Use this file as the working SDK manual for normal app generation. It intentionally includes the public imports, client shape, entity methods, AI action bindings, React exports, app SDK entry shape, and known validation caveats.
|
|
8
|
-
|
|
9
|
-
Do not inspect `node_modules/@howone/sdk` for the same information unless:
|
|
10
|
-
|
|
11
|
-
- This file does not cover the exact API needed.
|
|
12
|
-
- TypeScript or runtime verification contradicts this file.
|
|
13
|
-
- You are intentionally updating the SDK package itself, not an app that consumes it.
|
|
14
|
-
|
|
15
|
-
When source fallback is necessary, keep it narrow: inspect the exact exported symbol or declaration that is missing, then return to app code. Do not glob or read the whole SDK package to relearn the basic usage below.
|
|
16
|
-
|
|
17
|
-
## SDK Mental Model
|
|
18
|
-
|
|
19
|
-
- `createClient(...)` creates the base HowOne client with auth, entity, AI, upload, and user helpers.
|
|
20
|
-
- `client.entity<TRecord, TCreate, TUpdate>('EntityName')` creates one typed entity client.
|
|
21
|
-
- `defineEntities(...)` plus `withEntities(client, entities)` makes `howone.entities.<EntityName>` typed in app code.
|
|
22
|
-
- `defineAiAction(...)` plus `defineAiActions(...)` plus `withAiActions(...)` makes `howone.ai.<actionName>.run|stream|events` typed in app code.
|
|
23
|
-
- `@howone/sdk/react` provides auth/theme/loading UI primitives only. It does not provide entity, query, or AI data hooks.
|
|
24
|
-
- `.howone/database/manifest.json` and `.howone/ai/manifest.json` are the generated contracts. App source should be generated from those manifests, not from memory.
|
|
25
|
-
|
|
26
|
-
## Import Reference
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
// @howone/sdk — all public exports
|
|
30
|
-
import {
|
|
31
|
-
createClient,
|
|
32
|
-
defineAiAction,
|
|
33
|
-
defineAiActions,
|
|
34
|
-
defineEntities,
|
|
35
|
-
withAiActions,
|
|
36
|
-
withEntities,
|
|
37
|
-
type CreateClientOptions,
|
|
38
|
-
type EntityRecord,
|
|
39
|
-
type EntityClient,
|
|
40
|
-
type EntityBindings,
|
|
41
|
-
type QueryOptions,
|
|
42
|
-
type QueryResult,
|
|
43
|
-
type PageInfo,
|
|
44
|
-
type DeleteResult,
|
|
45
|
-
type AiClient,
|
|
46
|
-
type AiActionDefinition,
|
|
47
|
-
type AiActionClient,
|
|
48
|
-
type AiActionConfig,
|
|
49
|
-
type AiResult, // = ExecutionResult
|
|
50
|
-
type AiSession, // { result: Promise<AiResult>, cancel(): void, signal: AbortSignal }
|
|
51
|
-
type AiEvent, // SSE event payload
|
|
52
|
-
type AiOptions,
|
|
53
|
-
type UserProfile,
|
|
54
|
-
AiSchemaValidationError,
|
|
55
|
-
} from '@howone/sdk'
|
|
56
|
-
|
|
57
|
-
// @howone/sdk/react — React components and hooks
|
|
58
|
-
import {
|
|
59
|
-
HowOneProvider,
|
|
60
|
-
useHowoneContext,
|
|
61
|
-
FloatingButton,
|
|
62
|
-
Loading,
|
|
63
|
-
LoadingSpinner,
|
|
64
|
-
type HowOneProviderProps,
|
|
65
|
-
type HowOneAuthMode,
|
|
66
|
-
type HowOneThemeMode,
|
|
67
|
-
type HowOneBrandMode,
|
|
68
|
-
} from '@howone/sdk/react'
|
|
69
|
-
|
|
70
|
-
import { z } from 'zod'
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
## createClient
|
|
76
|
-
|
|
77
|
-
```ts
|
|
78
|
-
const client = createClient({
|
|
79
|
-
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID, // required in Vite apps
|
|
80
|
-
env: import.meta.env.VITE_HOWONE_ENV, // 'local' | 'dev' | 'prod'
|
|
81
|
-
// Optional advanced options (rarely needed):
|
|
82
|
-
// apiUrl, aiUrl, caseStyle ('camel'|'snake'), auth: { mode, getToken }
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
// client shape:
|
|
86
|
-
client.entity<TRecord, TCreate, TUpdate>(entityName: string): EntityClient<...>
|
|
87
|
-
client.entities: Record<string, EntityClient>
|
|
88
|
-
client.ai: AiClient // low-level; prefer withAiActions bindings
|
|
89
|
-
client.me(): Promise<UserProfile | null>
|
|
90
|
-
client.requireMe(): Promise<UserProfile>
|
|
91
|
-
client.auth.setToken(t: string | null): void
|
|
92
|
-
client.auth.getToken(): string | null
|
|
93
|
-
client.auth.isAuthenticated(): boolean
|
|
94
|
-
client.auth.login(redirect?: string): void
|
|
95
|
-
client.auth.logout(): void
|
|
96
|
-
client.upload.file(file, options?): Promise<{ url, thumbnailUrl?, id?, size?, mimeType? }>
|
|
97
|
-
client.upload.image(file): Promise<{ url }>
|
|
98
|
-
client.upload.batch({ files, concurrent?, onProgress?, onFileComplete? }): Promise<BatchUploadResponse>
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
## EntityClient
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
type EntityClient<TRecord, TCreate, TUpdate> = {
|
|
107
|
-
name: string
|
|
108
|
-
// Basic CRUD
|
|
109
|
-
list(options?: { page?, limit?, sort?, order?, ...filters }): Promise<TRecord[]>
|
|
110
|
-
get(id: string): Promise<TRecord | null>
|
|
111
|
-
getOrThrow(id: string): Promise<TRecord>
|
|
112
|
-
create(data: TCreate): Promise<TRecord>
|
|
113
|
-
update(id: string, data: TUpdate): Promise<TRecord>
|
|
114
|
-
delete(id: string): Promise<{ deleted: boolean; id: string; message? }>
|
|
115
|
-
// Advanced query with filters/pagination
|
|
116
|
-
query(options?: QueryOptions<TRecord>): Promise<QueryResult<TRecord>>
|
|
117
|
-
query.mine(options?: QueryOptions<TRecord>): Promise<QueryResult<TRecord>>
|
|
118
|
-
bulkCreate(records: TCreate[], options?: { sample?: boolean }): Promise<TRecord[]>
|
|
119
|
-
aggregate(pipeline: unknown[]): Promise<unknown[]>
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// QueryOptions
|
|
123
|
-
type QueryOptions<TRecord> = {
|
|
124
|
-
where?: { [K in keyof TRecord]?: TRecord[K] | FieldOperator }
|
|
125
|
-
// FieldOperator: { eq, ne, gt, gte, lt, lte, contains, like, startsWith, endsWith, in, notIn }
|
|
126
|
-
search?: string
|
|
127
|
-
page?: { number?: number; size?: number }
|
|
128
|
-
orderBy?: Partial<Record<keyof TRecord | string, 'asc' | 'desc'>>
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// QueryResult
|
|
132
|
-
type QueryResult<T> = {
|
|
133
|
-
items: T[]
|
|
134
|
-
page: { number, size, total, totalPages, hasNext, hasPrev }
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// EntityRecord base
|
|
138
|
-
type EntityRecord = {
|
|
139
|
-
id: string
|
|
140
|
-
createdDate?: string
|
|
141
|
-
updatedDate?: string
|
|
142
|
-
createdById?: string
|
|
143
|
-
isSample?: boolean
|
|
144
|
-
[key: string]: unknown
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
|
|
150
|
-
## AI Action
|
|
151
|
-
|
|
152
|
-
```ts
|
|
153
|
-
// Define a typed action
|
|
154
|
-
defineAiAction(id: string, config?: {
|
|
155
|
-
inputSchema?: z.ZodType<TInput>
|
|
156
|
-
outputSchema?: z.ZodType<TOutput>
|
|
157
|
-
mode?: 'run' | 'stream' | 'events'
|
|
158
|
-
}): AiActionDefinition<TInput, TOutput>
|
|
159
|
-
|
|
160
|
-
// Group actions
|
|
161
|
-
defineAiActions({ [name]: AiActionDefinition, ... }): actions
|
|
162
|
-
|
|
163
|
-
// Bind to client
|
|
164
|
-
withAiActions(client, actions): client & { ai: { [name]: AiActionClient } }
|
|
165
|
-
|
|
166
|
-
// AiActionClient methods
|
|
167
|
-
howone.ai.<actionName>.run(inputs?, options?): Promise<TOutput>
|
|
168
|
-
howone.ai.<actionName>.stream(inputs?, options?): AiSession
|
|
169
|
-
howone.ai.<actionName>.events(inputs?, options?): AsyncIterable<AiEvent>
|
|
170
|
-
|
|
171
|
-
// AiSession
|
|
172
|
-
type AiSession = {
|
|
173
|
-
result: Promise<AiResult>
|
|
174
|
-
cancel(): void
|
|
175
|
-
signal: AbortSignal
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Reserved names — do NOT use as action names: run, stream, events
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
---
|
|
182
|
-
|
|
183
|
-
## React: HowOneProvider & hooks
|
|
184
|
-
|
|
185
|
-
`@howone/sdk/react` exports exactly: `HowOneProvider`, `useHowoneContext`, `FloatingButton`, `Loading`, `LoadingSpinner`.
|
|
186
|
-
|
|
187
|
-
**There are no `useEntity`, `useQuery`, `useAi`, or other data-fetching hooks.** Entity and AI calls are plain async functions — call them directly in components with `useEffect`/`useState` or any data-fetching library (SWR, React Query, etc.).
|
|
188
|
-
|
|
189
|
-
```tsx
|
|
190
|
-
// Wrap app root — handles auth, theme, toasts, floating button
|
|
191
|
-
<HowOneProvider
|
|
192
|
-
projectId={...} // optional if client is already configured
|
|
193
|
-
auth="optional" // 'required' | 'optional' | 'none'
|
|
194
|
-
brand="visible" // 'visible' | 'hidden'
|
|
195
|
-
theme="inherit" // 'dark' | 'light' | 'system' | 'inherit'
|
|
196
|
-
showBrandButton={true}
|
|
197
|
-
>
|
|
198
|
-
<App />
|
|
199
|
-
</HowOneProvider>
|
|
200
|
-
|
|
201
|
-
// Auth context hook — the only hook in the package
|
|
202
|
-
const { user, token, isAuthenticated, logout } = useHowoneContext()
|
|
203
|
-
// user shape: { id, email, name, avatar } | null
|
|
204
|
-
|
|
205
|
-
// Loading components
|
|
206
|
-
<Loading size="md" tone="brand" label="..." description="..." fullScreen />
|
|
207
|
-
<LoadingSpinner size="sm" tone="neutral" />
|
|
208
|
-
// size: 'sm' | 'md' | 'lg' tone: 'brand' | 'neutral' | 'inverse'
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## App SDK Entry
|
|
212
|
-
|
|
213
|
-
Preferred Vite app entry shape:
|
|
214
|
-
|
|
215
|
-
```ts
|
|
216
|
-
import {
|
|
217
|
-
createClient,
|
|
218
|
-
defineAiAction,
|
|
219
|
-
defineAiActions,
|
|
220
|
-
defineEntities,
|
|
221
|
-
type EntityRecord,
|
|
222
|
-
withAiActions,
|
|
223
|
-
withEntities,
|
|
224
|
-
} from '@howone/sdk'
|
|
225
|
-
import { z } from 'zod'
|
|
226
|
-
|
|
227
|
-
const client = createClient({
|
|
228
|
-
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
|
|
229
|
-
env: import.meta.env.VITE_HOWONE_ENV,
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
export const entities = defineEntities({
|
|
233
|
-
// Todo: client.entity<Todo, TodoCreate, TodoUpdate>('Todo'),
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
export const ai = defineAiActions({
|
|
237
|
-
// generateImage: defineAiAction('generateImage', {
|
|
238
|
-
// inputSchema: generateImageInputSchema,
|
|
239
|
-
// }),
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
const howone = withAiActions(withEntities(client, entities), ai)
|
|
243
|
-
|
|
244
|
-
export default howone
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
## Manifest-To-Code Recipe
|
|
248
|
-
|
|
249
|
-
When backend metadata has been synced, generate app SDK code in this order:
|
|
250
|
-
|
|
251
|
-
1. Read `src/lib/sdk.ts` to preserve the template's import style and existing bindings.
|
|
252
|
-
2. Read `.howone/database/manifest.json` for entity names and fields. Generate `Record`, `Create`, and `Update` types, then bind each entity with `client.entity<Record, Create, Update>('EntityName')`.
|
|
253
|
-
3. Read `.howone/ai/manifest.json` for AI capability names and JSON schemas. Generate zod input schemas and, only when safe, output schemas.
|
|
254
|
-
4. Export a single default `howone` client built by composing `withEntities` and `withAiActions`.
|
|
255
|
-
5. In UI code, import the default client and call `howone.entities.<Entity>.*` or `howone.ai.<action>.run(...)`.
|
|
256
|
-
|
|
257
|
-
Basic JSON schema mapping for generated zod:
|
|
258
|
-
|
|
259
|
-
```ts
|
|
260
|
-
string -> z.string()
|
|
261
|
-
number -> z.number()
|
|
262
|
-
integer -> z.number().int()
|
|
263
|
-
boolean -> z.boolean()
|
|
264
|
-
array -> z.array(...)
|
|
265
|
-
object -> z.object({ ... })
|
|
266
|
-
enum -> z.enum([...])
|
|
267
|
-
missing from required[] -> .optional()
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
## Entity Types
|
|
271
|
-
|
|
272
|
-
Prefer explicit payload types:
|
|
273
|
-
|
|
274
|
-
```ts
|
|
275
|
-
export type StoryRecord = EntityRecord & {
|
|
276
|
-
title: string
|
|
277
|
-
content: string
|
|
278
|
-
prompt: string
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
export type StoryCreate = {
|
|
282
|
-
title: string
|
|
283
|
-
content: string
|
|
284
|
-
prompt: string
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export type StoryUpdate = Partial<StoryCreate>
|
|
288
|
-
|
|
289
|
-
export const entities = defineEntities({
|
|
290
|
-
Story: client.entity<StoryRecord, StoryCreate, StoryUpdate>('Story'),
|
|
291
|
-
})
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
Generated entity app code usually needs only these calls:
|
|
295
|
-
|
|
296
|
-
```ts
|
|
297
|
-
const result = await howone.entities.Story.query({
|
|
298
|
-
search,
|
|
299
|
-
page: { number: 1, size: 20 },
|
|
300
|
-
orderBy: { createdDate: 'desc' },
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
await howone.entities.Story.create({ title, content, prompt })
|
|
304
|
-
await howone.entities.Story.update(id, { title })
|
|
305
|
-
await howone.entities.Story.delete(id)
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
Prefer `query()` for list UIs that need paging/search/sort metadata. Use `list()` only for simple array reads.
|
|
309
|
-
|
|
310
|
-
Avoid this for generated create payloads:
|
|
311
|
-
|
|
312
|
-
```ts
|
|
313
|
-
type StoryCreate = Omit<StoryRecord, 'id' | 'createdDate' | 'updatedDate'>
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
`EntityRecord` has an index signature, so `Omit` can make generated create types looser than intended.
|
|
317
|
-
|
|
318
|
-
## AI Action Types
|
|
319
|
-
|
|
320
|
-
Generate zod schemas from `.howone/ai/manifest.json`:
|
|
321
|
-
|
|
322
|
-
```ts
|
|
323
|
-
export const generateStoryInputSchema = z.object({
|
|
324
|
-
topic: z.string().min(1),
|
|
325
|
-
ageRange: z.enum(['3-5', '6-8', '9-12']),
|
|
326
|
-
})
|
|
327
|
-
|
|
328
|
-
export const generateStoryOutputSchema = z.object({
|
|
329
|
-
title: z.string(),
|
|
330
|
-
content: z.string(),
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
export type GenerateStoryInput = z.infer<typeof generateStoryInputSchema>
|
|
334
|
-
export type GenerateStoryOutput = z.infer<typeof generateStoryOutputSchema>
|
|
335
|
-
|
|
336
|
-
export const ai = defineAiActions({
|
|
337
|
-
generateStory: defineAiAction('generateStory', {
|
|
338
|
-
inputSchema: generateStoryInputSchema,
|
|
339
|
-
mode: 'run',
|
|
340
|
-
}),
|
|
341
|
-
})
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
Call from app code:
|
|
345
|
-
|
|
346
|
-
```ts
|
|
347
|
-
const result = await howone.ai.generateStory.run({
|
|
348
|
-
topic,
|
|
349
|
-
ageRange,
|
|
350
|
-
})
|
|
351
|
-
const story = result.finalResult as GenerateStoryOutput
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
For streaming:
|
|
355
|
-
|
|
356
|
-
```ts
|
|
357
|
-
const session = howone.ai.generateStory.stream(input)
|
|
358
|
-
session.cancel()
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
For event iteration:
|
|
362
|
-
|
|
363
|
-
```ts
|
|
364
|
-
for await (const event of howone.ai.generateStory.events(input)) {
|
|
365
|
-
console.log(event)
|
|
366
|
-
}
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
## AI Output Validation
|
|
370
|
-
|
|
371
|
-
`run()` validates input with `inputSchema`, calls the workflow, then validates the **raw `ExecutionResult`** (the full SSE envelope) with `outputSchema`. It does **not** automatically unwrap `result.finalResult`.
|
|
372
|
-
|
|
373
|
-
If you want to type the final result only, omit `outputSchema` and unwrap manually:
|
|
374
|
-
|
|
375
|
-
```ts
|
|
376
|
-
const result = await howone.ai.generateStory.run(input) // returns AiResult
|
|
377
|
-
const finalResult = result.finalResult as GenerateStoryOutput
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
Only define `outputSchema` when it matches the full `ExecutionResult` envelope or after verifying the SDK has changed to validate `finalResult` directly.
|
|
381
|
-
|
|
382
|
-
## AI Result Persistence
|
|
383
|
-
|
|
384
|
-
For AI-generated data that should be saved:
|
|
385
|
-
|
|
386
|
-
1. Define AI input/output schemas from `.howone/ai/manifest.json`.
|
|
387
|
-
2. Define entity fields from the AI output schema.
|
|
388
|
-
3. Add only needed request metadata fields, such as prompt, selected options, language, status, or timestamps.
|
|
389
|
-
4. Save with `howone.entities.<Entity>.create(...)`.
|
|
390
|
-
|
|
391
|
-
Example:
|
|
392
|
-
|
|
393
|
-
```ts
|
|
394
|
-
const story = await howone.ai.generateStory.run(input)
|
|
395
|
-
const finalResult = story.finalResult as GenerateStoryOutput
|
|
396
|
-
|
|
397
|
-
await howone.entities.Story.create({
|
|
398
|
-
title: finalResult.title,
|
|
399
|
-
content: finalResult.content,
|
|
400
|
-
prompt: input.topic,
|
|
401
|
-
})
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
## Do Not Generate
|
|
405
|
-
|
|
406
|
-
Avoid these patterns unless the SDK has explicitly added them:
|
|
407
|
-
|
|
408
|
-
```ts
|
|
409
|
-
// No data hooks in @howone/sdk/react
|
|
410
|
-
const todos = useEntity('Todo')
|
|
411
|
-
const query = useQuery(...)
|
|
412
|
-
const result = useAi(...)
|
|
413
|
-
|
|
414
|
-
// Wrong AI binding shape
|
|
415
|
-
await howone.ai.run.generateStory(input)
|
|
416
|
-
|
|
417
|
-
// Too-loose create payload
|
|
418
|
-
type StoryCreate = Omit<StoryRecord, 'id' | 'createdDate' | 'updatedDate'>
|
|
419
|
-
|
|
420
|
-
// Generated files do not belong under .howone
|
|
421
|
-
// .howone stores synced manifests only.
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
## When To Read SDK Source
|
|
425
|
-
|
|
426
|
-
Read SDK source or declaration files only for details not covered above, for example:
|
|
427
|
-
|
|
428
|
-
- A newly added public method not listed here.
|
|
429
|
-
- A TypeScript error proving a signature drifted.
|
|
430
|
-
- A runtime error that points to a specific SDK helper.
|
|
431
|
-
- Work that intentionally modifies `packages/sdk` itself.
|
|
432
|
-
|
|
433
|
-
For app generation, do not inspect SDK source to rediscover `createClient`, entity CRUD, AI action binding shape, React exports, or Vite env setup.
|
|
434
|
-
|
|
435
|
-
## Generated Code Checklist
|
|
436
|
-
|
|
437
|
-
- `src/lib/sdk.ts` imports only APIs it uses.
|
|
438
|
-
- `createClient` uses `import.meta.env.VITE_HOWONE_PROJECT_ID` and `import.meta.env.VITE_HOWONE_ENV`.
|
|
439
|
-
- Entity names match `.howone/database/manifest.json`.
|
|
440
|
-
- AI action names and schemas match `.howone/ai/manifest.json`.
|
|
441
|
-
- zod schemas are strict enough for generated UI inputs and backend contracts.
|
|
442
|
-
- UI code calls `howone.ai.<action>.run(input)` and `howone.entities.<Entity>.*`.
|
|
443
|
-
- No generated source file is written under `.howone`.
|