howone 0.1.22 → 0.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/nextjs/lib/sdk.ts +3 -0
- package/templates/vite/.howone/skills/howone-sdk/01-architect/01-app-generation.md +183 -69
- package/templates/vite/.howone/skills/howone-sdk/01-architect/02-manifest-codegen.md +98 -23
- package/templates/vite/.howone/skills/howone-sdk/02-database/01-schema-design.md +463 -69
- package/templates/vite/.howone/skills/howone-sdk/02-database/02-schema-operations.md +366 -64
- package/templates/vite/.howone/skills/howone-sdk/02-database/03-data-access-patterns.md +204 -67
- package/templates/vite/.howone/skills/howone-sdk/02-database/04-query-dsl-and-responses.md +237 -0
- package/templates/vite/.howone/skills/howone-sdk/02-database/05-ai-persistence-patterns.md +372 -0
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/01-client-setup.md +58 -36
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/02-entity-operations.md +67 -0
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/03-auth.md +267 -469
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/04-react-integration.md +113 -322
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/07-ai-action-calls.md +95 -48
- package/templates/vite/.howone/skills/howone-sdk/03-sdk/08-extension-boundaries.md +226 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/01-ai-capability-architecture.md +205 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/02-workflow-contract-rules.md +426 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/03-ai-sdk-handoff.md +219 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/04-service-capability-catalog.md +281 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/05-workflow-operations.md +256 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/06-ai-feature-playbooks.md +296 -0
- package/templates/vite/.howone/skills/howone-sdk/SKILL.md +83 -15
- package/templates/vite/.howone/skills/howone-sdk/agents/openai.yaml +2 -2
- package/templates/vite/package.json +1 -1
- package/templates/vite/src/lib/sdk.ts +3 -0
- package/templates/vite/.howone/skills/howone-sdk/04-ai/.gitkeep +0 -1
|
@@ -2,399 +2,190 @@
|
|
|
2
2
|
|
|
3
3
|
## What `@howone/sdk/react` Provides
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Thin integration layer: auth context plus the HowOne floating brand button. **No** entity hooks,
|
|
6
|
+
AI hooks, toast system, redirect overlay, or app-owned UI.
|
|
6
7
|
|
|
7
8
|
Exports:
|
|
8
|
-
- `HowOneProvider` — all-in-one app provider (auth, theme, toasts, floating button)
|
|
9
|
-
- `useHowoneContext` — access auth state (user, token, isAuthenticated, logout)
|
|
10
|
-
- `FloatingButton` — a floating action button (shows Login when unauthenticated)
|
|
11
|
-
- `Loading` — full-page or inline loading component
|
|
12
|
-
- `LoadingSpinner` — headless spinner for composition
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
import {
|
|
20
|
-
HowOneProvider,
|
|
21
|
-
useHowoneContext,
|
|
22
|
-
FloatingButton,
|
|
23
|
-
Loading,
|
|
24
|
-
LoadingSpinner,
|
|
25
|
-
} from '@howone/sdk/react'
|
|
26
|
-
```
|
|
10
|
+
- `HowOneProvider`
|
|
11
|
+
- `useHowoneContext`
|
|
12
|
+
- `FloatingButton`
|
|
27
13
|
|
|
28
14
|
---
|
|
29
15
|
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
Wrap your entire app. Provides auth, theme, and toast context to all children.
|
|
16
|
+
## Auth: one SDK config + one Provider flag
|
|
33
17
|
|
|
34
|
-
|
|
35
|
-
`projectId` and `env` once in `src/lib/sdk.ts`; do not pass a separate env to the provider.
|
|
36
|
-
|
|
37
|
-
```tsx
|
|
38
|
-
// main.tsx or app/layout.tsx
|
|
39
|
-
import React from 'react'
|
|
40
|
-
import ReactDOM from 'react-dom/client'
|
|
41
|
-
import { HowOneProvider } from '@howone/sdk/react'
|
|
42
|
-
import './lib/sdk'
|
|
43
|
-
import App from './App'
|
|
44
|
-
|
|
45
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
46
|
-
<React.StrictMode>
|
|
47
|
-
<HowOneProvider
|
|
48
|
-
auth="required"
|
|
49
|
-
brand="visible"
|
|
50
|
-
theme="system"
|
|
51
|
-
>
|
|
52
|
-
<App />
|
|
53
|
-
</HowOneProvider>
|
|
54
|
-
</React.StrictMode>
|
|
55
|
-
)
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### HowOneProviderProps
|
|
18
|
+
**Step 1 — `src/lib/sdk.ts` (required):**
|
|
59
19
|
|
|
60
20
|
```ts
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
interface HowOneProviderProps {
|
|
66
|
-
children: React.ReactNode
|
|
67
|
-
|
|
68
|
-
// Optional override. Prefer configuring projectId once in createClient.
|
|
69
|
-
projectId?: string
|
|
70
|
-
|
|
71
|
-
// Auth behaviour
|
|
72
|
-
auth?: HowOneAuthMode
|
|
73
|
-
// 'required' — redirects unauthenticated users to login page
|
|
74
|
-
// 'optional' — allows both authenticated and unauthenticated users
|
|
75
|
-
// 'none' — disables auth entirely
|
|
76
|
-
|
|
77
|
-
// Brand button (HowOne branding)
|
|
78
|
-
brand?: HowOneBrandMode // default: 'visible'
|
|
79
|
-
showBrandButton?: boolean // alias for brand
|
|
80
|
-
|
|
81
|
-
// Theme
|
|
82
|
-
theme?: HowOneThemeMode // default: 'system'
|
|
83
|
-
themeStorageKey?: string // localStorage key for theme preference
|
|
84
|
-
forceTheme?: boolean // ignore user preference and force theme
|
|
85
|
-
}
|
|
21
|
+
const client = createClient({
|
|
22
|
+
projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
|
|
23
|
+
env: import.meta.env.VITE_HOWONE_ENV,
|
|
24
|
+
})
|
|
86
25
|
```
|
|
87
26
|
|
|
88
|
-
|
|
27
|
+
**Step 2 — Provider:**
|
|
89
28
|
|
|
90
29
|
```tsx
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
<App />
|
|
94
|
-
</HowOneProvider>
|
|
95
|
-
|
|
96
|
-
// Auth-gated app — redirect to login if not authenticated
|
|
97
|
-
<HowOneProvider auth="required" theme="system">
|
|
98
|
-
<App />
|
|
99
|
-
</HowOneProvider>
|
|
100
|
-
|
|
101
|
-
// Optional auth — user can browse without logging in
|
|
102
|
-
<HowOneProvider auth="optional" theme="inherit" brand="visible">
|
|
103
|
-
<App />
|
|
104
|
-
</HowOneProvider>
|
|
30
|
+
import { HowOneProvider } from '@howone/sdk/react'
|
|
31
|
+
import './lib/sdk' // must import before Provider so auth config is registered
|
|
105
32
|
|
|
106
|
-
|
|
107
|
-
<HowOneProvider auth="required" theme="dark" forceTheme>
|
|
33
|
+
<HowOneProvider auth="none" brand="visible">
|
|
108
34
|
<App />
|
|
109
35
|
</HowOneProvider>
|
|
110
36
|
```
|
|
111
37
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
```ts
|
|
119
|
-
type HowoneContextValue = {
|
|
120
|
-
user: {
|
|
121
|
-
id: string // backend owner id; same as userId when JWT has userId
|
|
122
|
-
userId?: string // authenticated backend user id when present in JWT
|
|
123
|
-
puid?: string // public UUID; do not use as public ownerId scope
|
|
124
|
-
email: string
|
|
125
|
-
name: string
|
|
126
|
-
avatar: string
|
|
127
|
-
appId?: string
|
|
128
|
-
} | null
|
|
129
|
-
token: string | null
|
|
130
|
-
isAuthenticated: boolean
|
|
131
|
-
logout: () => void
|
|
132
|
-
}
|
|
133
|
-
```
|
|
38
|
+
| Layer | Setting | Meaning |
|
|
39
|
+
|-------|---------|---------|
|
|
40
|
+
| `createClient` | `{ projectId, env }` (default **hosted**) | Login/logout → HowOne `/auth` |
|
|
41
|
+
| `createClient` | `auth: 'custom'` | Login/logout → your `loginPath`; APIs still HowOne |
|
|
42
|
+
| `HowOneProvider` | `auth="required"` | Default with hosted — redirect to HowOne login |
|
|
43
|
+
| `HowOneProvider` | `auth="none"` | Use with `auth: 'custom'`; guard routes yourself |
|
|
134
44
|
|
|
135
|
-
|
|
45
|
+
Default (HowOne hosted login):
|
|
136
46
|
|
|
137
47
|
```tsx
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
function UserMenu() {
|
|
141
|
-
const { user, isAuthenticated, logout } = useHowoneContext()
|
|
142
|
-
|
|
143
|
-
if (!isAuthenticated || !user) {
|
|
144
|
-
return <a href="/login">Login</a>
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return (
|
|
148
|
-
<div>
|
|
149
|
-
<img src={user.avatar} alt={user.name} />
|
|
150
|
-
<span>{user.name}</span>
|
|
151
|
-
<button onClick={logout}>Logout</button>
|
|
152
|
-
</div>
|
|
153
|
-
)
|
|
154
|
-
}
|
|
48
|
+
createClient({ projectId, env })
|
|
49
|
+
<HowOneProvider auth="required" />
|
|
155
50
|
```
|
|
156
51
|
|
|
157
|
-
|
|
52
|
+
Custom login page (your UI, HowOne auth APIs, keep HowOne logo unless product asks to hide it):
|
|
158
53
|
|
|
159
54
|
```tsx
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (!isAuthenticated) {
|
|
164
|
-
return <div>Please log in to continue.</div>
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return <div>Welcome, {user?.name}!</div>
|
|
168
|
-
}
|
|
55
|
+
createClient({ projectId, env, auth: 'custom', loginPath: '/login' })
|
|
56
|
+
<HowOneProvider auth="none" brand="visible" />
|
|
169
57
|
```
|
|
170
58
|
|
|
171
|
-
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## HowOneProvider
|
|
172
62
|
|
|
173
63
|
```tsx
|
|
174
|
-
|
|
64
|
+
<HowOneProvider
|
|
65
|
+
auth="none"
|
|
66
|
+
brand="visible"
|
|
67
|
+
onAuthRedirect={({ mode, returnUrl }) => {
|
|
68
|
+
// App may set its own loading/redirect state here.
|
|
69
|
+
}}
|
|
70
|
+
onAuthStateChange={(state) => {
|
|
71
|
+
// App may update analytics or local UI state here.
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
<App />
|
|
75
|
+
</HowOneProvider>
|
|
76
|
+
```
|
|
175
77
|
|
|
176
|
-
|
|
177
|
-
const { token } = useHowoneContext()
|
|
78
|
+
### HowOneProviderProps
|
|
178
79
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
80
|
+
```ts
|
|
81
|
+
type HowOneProviderAuth = 'required' | 'optional' | 'none'
|
|
182
82
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
83
|
+
interface HowOneProviderProps {
|
|
84
|
+
children: React.ReactNode
|
|
85
|
+
projectId?: string // prefer createClient projectId
|
|
86
|
+
auth?: HowOneProviderAuth
|
|
87
|
+
brand?: 'visible' | 'hidden'
|
|
88
|
+
showBrandButton?: boolean
|
|
89
|
+
theme?: 'dark' | 'light' | 'system' | 'inherit'
|
|
90
|
+
onAuthStateChange?: (state: AuthState) => void
|
|
91
|
+
onAuthRedirect?: (info: { mode: 'hosted' | 'custom'; returnUrl: string }) => void
|
|
188
92
|
}
|
|
189
93
|
```
|
|
190
94
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
## FloatingButton
|
|
95
|
+
**Important:** Provider `auth` is only a **route guard**. Login/logout URLs come from `createClient({ auth: 'custom' })`.
|
|
194
96
|
|
|
195
|
-
|
|
97
|
+
The provider must not render app-owned UI. It does not own toasts, dialogs, pages, custom login UI,
|
|
98
|
+
or redirect overlays. It does keep the bottom-right HowOne logo by default through `FloatingButton`.
|
|
99
|
+
Use `brand="hidden"` or `showBrandButton={false}` only when the product explicitly asks to hide it.
|
|
196
100
|
|
|
197
|
-
|
|
198
|
-
import { FloatingButton } from '@howone/sdk/react'
|
|
199
|
-
|
|
200
|
-
// Default — shows Login/brand button
|
|
201
|
-
<FloatingButton />
|
|
202
|
-
|
|
203
|
-
// Custom text and handler
|
|
204
|
-
<FloatingButton
|
|
205
|
-
text="Get Started"
|
|
206
|
-
onClick={() => router.push('/signup')}
|
|
207
|
-
/>
|
|
208
|
-
|
|
209
|
-
// Custom styling
|
|
210
|
-
<FloatingButton
|
|
211
|
-
text="Support"
|
|
212
|
-
onClick={() => setShowChat(true)}
|
|
213
|
-
className="bg-blue-600 hover:bg-blue-700"
|
|
214
|
-
/>
|
|
215
|
-
```
|
|
101
|
+
---
|
|
216
102
|
|
|
217
|
-
|
|
103
|
+
## useHowoneContext
|
|
218
104
|
|
|
219
105
|
```ts
|
|
220
|
-
|
|
221
|
-
text?: string // button label (default: 'Login' or brand text)
|
|
222
|
-
onClick?: () => void
|
|
223
|
-
className?: string // additional CSS classes
|
|
224
|
-
}
|
|
106
|
+
const { user, token, isAuthenticated, logout } = useHowoneContext()
|
|
225
107
|
```
|
|
226
108
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
## Loading Components
|
|
230
|
-
|
|
231
|
-
### Loading — full-page or inline loading state
|
|
109
|
+
### Logout
|
|
232
110
|
|
|
233
111
|
```tsx
|
|
234
|
-
|
|
112
|
+
<button onClick={() => void logout()}>Sign out</button>
|
|
113
|
+
```
|
|
235
114
|
|
|
236
|
-
|
|
237
|
-
<Loading fullScreen label="Loading your workspace..." />
|
|
115
|
+
With `auth: 'custom'`, `logout()` clears session and navigates to `loginPath` — **not** howone.dev.
|
|
238
116
|
|
|
239
|
-
|
|
240
|
-
<Loading
|
|
241
|
-
label="Generating story..."
|
|
242
|
-
description="This may take a moment"
|
|
243
|
-
size="lg"
|
|
244
|
-
tone="brand"
|
|
245
|
-
/>
|
|
117
|
+
Equivalent:
|
|
246
118
|
|
|
247
|
-
|
|
248
|
-
|
|
119
|
+
```ts
|
|
120
|
+
await howone.auth.logout()
|
|
249
121
|
```
|
|
250
122
|
|
|
251
|
-
###
|
|
123
|
+
### Custom login page link
|
|
252
124
|
|
|
253
125
|
```tsx
|
|
254
|
-
import {
|
|
255
|
-
|
|
256
|
-
// Sizes: 'sm' | 'md' | 'lg'
|
|
257
|
-
// Tones: 'brand' | 'neutral' | 'inverse'
|
|
258
|
-
|
|
259
|
-
<LoadingSpinner size="md" tone="brand" />
|
|
260
|
-
<LoadingSpinner size="sm" tone="neutral" className="mr-2" />
|
|
261
|
-
```
|
|
126
|
+
import { useNavigate } from 'react-router-dom'
|
|
127
|
+
import howone from '@/lib/sdk'
|
|
262
128
|
|
|
263
|
-
|
|
129
|
+
function Header() {
|
|
130
|
+
const navigate = useNavigate()
|
|
131
|
+
const { isAuthenticated, logout } = useHowoneContext()
|
|
264
132
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
interface LoadingProps {
|
|
270
|
-
size?: LoadingSize // default: 'md'
|
|
271
|
-
tone?: LoadingTone // default: 'brand'
|
|
272
|
-
label?: string // primary text
|
|
273
|
-
description?: string // secondary text
|
|
274
|
-
className?: string
|
|
275
|
-
fullScreen?: boolean // renders over full viewport
|
|
276
|
-
}
|
|
133
|
+
if (!isAuthenticated) {
|
|
134
|
+
return <button onClick={() => navigate(howone.auth.loginPath)}>Sign in</button>
|
|
135
|
+
}
|
|
277
136
|
|
|
278
|
-
|
|
279
|
-
size?: LoadingSize
|
|
280
|
-
tone?: LoadingTone
|
|
281
|
-
className?: string
|
|
137
|
+
return <button onClick={() => void logout()}>Sign out</button>
|
|
282
138
|
}
|
|
283
139
|
```
|
|
284
140
|
|
|
285
141
|
---
|
|
286
142
|
|
|
287
|
-
##
|
|
143
|
+
## FloatingButton
|
|
144
|
+
|
|
145
|
+
The bottom-right HowOne logo is part of the SDK React integration and should remain visible by
|
|
146
|
+
default. It does not replace your login page and does not perform app auth. Hide it only with
|
|
147
|
+
`brand="hidden"` or `showBrandButton={false}`.
|
|
288
148
|
|
|
289
|
-
|
|
149
|
+
---
|
|
290
150
|
|
|
291
|
-
|
|
151
|
+
## Protected route pattern
|
|
292
152
|
|
|
293
153
|
```tsx
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
import howone, { type StoryRecord } from '@/lib/sdk'
|
|
298
|
-
|
|
299
|
-
function MyStories() {
|
|
300
|
-
const { isAuthenticated } = useHowoneContext()
|
|
301
|
-
const [stories, setStories] = useState<StoryRecord[]>([])
|
|
302
|
-
const [loading, setLoading] = useState(false)
|
|
154
|
+
function ProtectedPage() {
|
|
155
|
+
const [user, setUser] = useState(null)
|
|
156
|
+
const navigate = useNavigate()
|
|
303
157
|
|
|
304
158
|
useEffect(() => {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
.then(result => setStories(result.items))
|
|
310
|
-
.finally(() => setLoading(false))
|
|
311
|
-
}, [isAuthenticated])
|
|
312
|
-
|
|
313
|
-
if (!isAuthenticated) return <div>Please log in.</div>
|
|
314
|
-
if (loading) return <Loading label="Loading your stories..." />
|
|
315
|
-
|
|
316
|
-
return (
|
|
317
|
-
<ul>
|
|
318
|
-
{stories.map(s => <li key={s.id}>{s.title}</li>)}
|
|
319
|
-
</ul>
|
|
320
|
-
)
|
|
321
|
-
}
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
### Pattern: Full app layout
|
|
159
|
+
howone.me()
|
|
160
|
+
.then(setUser)
|
|
161
|
+
.catch(() => navigate(howone.auth.loginPath, { replace: true }))
|
|
162
|
+
}, [navigate])
|
|
325
163
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
import ReactDOM from 'react-dom/client'
|
|
330
|
-
import { HowOneProvider } from '@howone/sdk/react'
|
|
331
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
332
|
-
import './lib/sdk'
|
|
333
|
-
import App from './App'
|
|
334
|
-
|
|
335
|
-
const queryClient = new QueryClient()
|
|
336
|
-
|
|
337
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
338
|
-
<React.StrictMode>
|
|
339
|
-
<HowOneProvider
|
|
340
|
-
auth="required"
|
|
341
|
-
theme="system"
|
|
342
|
-
brand="visible"
|
|
343
|
-
>
|
|
344
|
-
<QueryClientProvider client={queryClient}>
|
|
345
|
-
<App />
|
|
346
|
-
</QueryClientProvider>
|
|
347
|
-
</HowOneProvider>
|
|
348
|
-
</React.StrictMode>
|
|
349
|
-
)
|
|
164
|
+
if (!user) return null
|
|
165
|
+
return <div>Welcome {user.name}</div>
|
|
166
|
+
}
|
|
350
167
|
```
|
|
351
168
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
```tsx
|
|
355
|
-
import { useState } from 'react'
|
|
356
|
-
import { useHowoneContext } from '@howone/sdk/react'
|
|
357
|
-
import howone from '@/lib/sdk'
|
|
169
|
+
---
|
|
358
170
|
|
|
359
|
-
|
|
360
|
-
const { isAuthenticated } = useHowoneContext()
|
|
361
|
-
const [loading, setLoading] = useState(false)
|
|
362
|
-
const [result, setResult] = useState<string | null>(null)
|
|
363
|
-
|
|
364
|
-
async function handleGenerate() {
|
|
365
|
-
if (!isAuthenticated) {
|
|
366
|
-
howone.auth.login()
|
|
367
|
-
return
|
|
368
|
-
}
|
|
369
|
-
setLoading(true)
|
|
370
|
-
try {
|
|
371
|
-
const res = await howone.ai.generateStory.run({ topic, language: 'en' })
|
|
372
|
-
if (res.success && res.finalResult) {
|
|
373
|
-
setResult((res.finalResult as any).content)
|
|
374
|
-
}
|
|
375
|
-
} finally {
|
|
376
|
-
setLoading(false)
|
|
377
|
-
}
|
|
378
|
-
}
|
|
171
|
+
## Common mistakes
|
|
379
172
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
```
|
|
173
|
+
| Mistake | Fix |
|
|
174
|
+
|---------|-----|
|
|
175
|
+
| Custom UI but no `auth: 'custom'` | Add to `createClient` |
|
|
176
|
+
| `HowOneProvider auth="required"` without custom SDK auth | Hosted redirect to howone.ai |
|
|
177
|
+
| `useHowoneContext` without Provider | Wrap app in `HowOneProvider` |
|
|
178
|
+
| Import Provider before `./lib/sdk` | Import sdk module first |
|
|
179
|
+
| Manual redirect to howone.dev on logout | Use `howone.auth.logout()` |
|
|
180
|
+
| Deleting the bottom-right HowOne logo by default | Keep `brand="visible"` unless explicitly asked to hide it |
|
|
181
|
+
| Expecting SDK toast APIs | Implement visible feedback in the frontend app from callbacks/results |
|
|
390
182
|
|
|
391
183
|
---
|
|
392
184
|
|
|
393
|
-
##
|
|
185
|
+
## Import map
|
|
394
186
|
|
|
395
|
-
|
|
|
396
|
-
|
|
397
|
-
|
|
|
398
|
-
|
|
|
399
|
-
|
|
|
400
|
-
| Using `user.id` without null-checking `user` | Guard: `if (!user) return` or use optional chaining `user?.id` |
|
|
187
|
+
| Need | Import |
|
|
188
|
+
|------|--------|
|
|
189
|
+
| Provider, context | `@howone/sdk/react` |
|
|
190
|
+
| Client, OTP, OAuth | `@howone/sdk` |
|
|
191
|
+
| App singleton | `@/lib/sdk` default export |
|