@molecule/app-react 3.0.2 → 3.2.0

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/README.md CHANGED
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-10-29T06:28:17.772Z
6
+ Generated: 2026-11-03T18:02:49.265Z
7
7
  -->
8
8
 
9
9
  # @molecule/app-react
@@ -24,6 +24,18 @@ server's saved user. Use its `onSaved` callback to update other query caches
24
24
  from that response before navigating. Configure the endpoint, method and JSON
25
25
  envelope once in `AuthClientConfig`; no page-local session store is needed.
26
26
 
27
+ **Session restore:** `useAuth().status === 'loading'` is undecided: wait for
28
+ it and redirect only on `status === 'unauthenticated'`. `'unavailable'` keeps
29
+ the session candidate for retry, but `isAuthenticated` can be false and `user`
30
+ can be null. A restore failure other than 401/403 never signs out.
31
+ `MoleculeProvider auth={authClient} awaitSession` (or `SessionReady` inside
32
+ `AuthProvider`) holds public pages until that initial loading status ends,
33
+ so their first render reads the restored user when restoration succeeds.
34
+ `sessionFallback` remains inside the service providers. Unavailable sessions
35
+ also render and can still have a null user. Synchronous SSR renders the
36
+ fallback for a loading client: initialize a request-scoped client first,
37
+ or leave the opt-in hold disabled for SSR.
38
+
27
39
  > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
28
40
  > It is written to be read by coding agents as much as by people, and is generated from this
29
41
  > package's source — edit `src/index.ts` JSDoc, not this file.
@@ -57,16 +69,21 @@ import { createJWTAuthClient } from '@molecule/app-auth'
57
69
  const authClient = createJWTAuthClient({ baseURL: '/api' })
58
70
 
59
71
  function Dashboard() {
60
- const { user, isAuthenticated, logout } = useAuth<{ name?: string }>()
72
+ const { user, status, logout } = useAuth<{ name?: string }>()
61
73
  const { t } = useTranslation()
62
74
  const { theme, toggleTheme } = useTheme()
63
75
 
64
- if (!isAuthenticated) {
76
+ if (status === 'loading') {
77
+ return <p>{t('auth.restoring', undefined, { defaultValue: 'Restoring session...' })}</p>
78
+ }
79
+ if (status === 'unauthenticated') {
65
80
  return <p>{t('auth.required', undefined, { defaultValue: 'Please log in.' })}</p>
66
81
  }
67
82
  return (
68
83
  <div style={{ background: theme.colors.background }}>
69
- <h1>{t('greeting.welcome', { name: user?.name }, { defaultValue: 'Welcome, {{name}}!' })}</h1>
84
+ <h1>
85
+ {t('greeting.welcome', { name: user?.name ?? '' }, { defaultValue: 'Welcome, {{name}}!' })}
86
+ </h1>
70
87
  <button onClick={toggleTheme}>
71
88
  {t('theme.toggle', undefined, { defaultValue: 'Toggle theme' })}
72
89
  </button>
@@ -114,7 +131,10 @@ Reactive authentication state snapshot (initialized, authenticated, user, loadin
114
131
  interface AuthState<T = UserProfile> {
115
132
  /**
116
133
  * The session's state as one value — render from this instead of combining
117
- * `initialized`/`authenticated`/`loading`. See {@link AuthStatus}.
134
+ * `initialized`/`authenticated`/`loading`. `'loading'` is undecided, so wait
135
+ * and redirect only on `'unauthenticated'`; `'unavailable'` keeps the candidate
136
+ * for retry and can have a null profile. A restore failure other than 401/403
137
+ * never signs out. See {@link AuthStatus}.
118
138
  */
119
139
  status: AuthStatus
120
140
  /**
@@ -122,11 +142,14 @@ interface AuthState<T = UserProfile> {
122
142
  */
123
143
  initialized: boolean
124
144
  /**
125
- * Whether the user is authenticated.
145
+ * Whether the client has an authenticated session. This is not a server
146
+ * confirmation flag: during `'unavailable'` it can be false for a retained
147
+ * token/cookie-only candidate with no cached profile. Use `status` for redirects.
126
148
  */
127
149
  authenticated: boolean
128
150
  /**
129
- * Current user (if authenticated).
151
+ * Current user, or null when none is cached. In particular, an `'unavailable'`
152
+ * token/cookie-only restore has a retained candidate but no profile yet.
130
153
  */
131
154
  user: T | null
132
155
  /**
@@ -180,12 +203,23 @@ interface AuthClient<T = UserProfile, TRegister extends RegisterExtraShape<TRegi
180
203
  * Updates the cached user object (state + persistent storage) without
181
204
  * hitting the network. Intended for local refreshes after a per-app
182
205
  * mutation (e.g., the user just PATCHed their own profile and the
183
- * server returned the canonical row). Does NOT change tokens.
206
+ * server returned the canonical row). A non-null profile does NOT change
207
+ * tokens or sign the client in: `authenticated` and `status` are unchanged,
208
+ * so an anonymous client stays anonymous. Use {@link login} /
209
+ * {@link register} for a credential exchange, or {@link setSession} when
210
+ * the token was issued outside the client.
184
211
  *
185
- * This does not sign the client in: `authenticated` and `status` are
186
- * unchanged, so an anonymous client stays anonymous. Use
187
- * {@link login} / {@link register} for a credential exchange, or
188
- * {@link setSession} when the token was issued outside the client.
212
+ * Passing `null` on a signed-in client, or while any session operation is
213
+ * pending, ends the local session: it clears the access token, refresh token
214
+ * and cached user, stops automatic refresh, and settles `authenticated: false`
215
+ * with `status: 'unauthenticated'` in one notification. Every older operation
216
+ * loses session ownership, and a persistent sign-out marker prevents a reload
217
+ * from restoring its credentials or a late cookie. A late credential exchange
218
+ * is revoked best-effort when a logout endpoint is configured, without blocking
219
+ * the caller. This method does not otherwise call the server for a settled
220
+ * session; use {@link logout} for explicit server revocation. On a settled
221
+ * anonymous client, `null` only clears the cached profile and leaves tokens
222
+ * and auth state unchanged.
189
223
  */
190
224
  setUser(user: T | null): void;
191
225
  /**
@@ -200,10 +234,13 @@ interface AuthClient<T = UserProfile, TRegister extends RegisterExtraShape<TRegi
200
234
  * which would violate the storage contract and make a stored bearer token
201
235
  * JS-readable (XSS-exfiltratable). Pass `null` to clear it.
202
236
  *
203
- * This changes token storage only; it does not sign the client in
204
- * (`authenticated` and `status` are unchanged). Use {@link login} /
205
- * {@link register} for a credential exchange, or call {@link setSession}
206
- * with the token and the user to establish an already-issued session.
237
+ * This changes token storage only: passing `null` does not by itself sign a
238
+ * signed-in client out, and a non-null token does not sign an anonymous
239
+ * client in (`authenticated` and `status` are unchanged). Use {@link logout}
240
+ * to end a session, or {@link setUser} with `null` for local-only sign-out.
241
+ * Use {@link login} / {@link register} for a credential exchange, or call
242
+ * {@link setSession} with the token and user to establish an already-issued
243
+ * session.
207
244
  */
208
245
  setAccessToken(token: string | null): void;
209
246
  /**
@@ -228,7 +265,16 @@ interface AuthClient<T = UserProfile, TRegister extends RegisterExtraShape<TRegi
228
265
  */
229
266
  login(credentials: LoginCredentials): Promise<AuthResult<T>>;
230
267
  /**
231
- * Logs out the current user.
268
+ * Logs out the current user. The server request is best-effort: a missing
269
+ * logout route (404), any other HTTP failure, or a network error never makes
270
+ * this method reject. It always clears the access token, refresh token and
271
+ * cached user, stops automatic refresh, and publishes one settled
272
+ * `authenticated: false`, `status: 'unauthenticated'` transition. Every pending
273
+ * session operation loses ownership; a persistent marker prevents reload from
274
+ * restoring an older session, and late credentials are revoked best-effort.
275
+ *
276
+ * When the API has no logout route, set {@link AuthClientConfigOptions.logoutEndpoint}
277
+ * to `null` (or `''`) to skip the request. Local sign-out still happens.
232
278
  */
233
279
  logout(): Promise<void>;
234
280
  /**
@@ -280,37 +326,6 @@ interface AuthClient<T = UserProfile, TRegister extends RegisterExtraShape<TRegi
280
326
  * until the cookie restore decides.
281
327
  *
282
328
  * Do not put this call (or a `/me` request of your own) in front of the first
283
- * render, and do not render a loading screen over a session it just restored:
284
- * `whenRestored()` already resolves as soon as the restore has settled, and the
285
- * HTTP gate built on it holds requests for exactly that window.
286
- */
287
- initialize(): Promise<void>;
288
- /**
289
- * Re-checks a restored session against the server, in the background — the
290
- * boot-time validation an app with a stored token needs when a revoked or
291
- * expired token must sign the user out. Call it AFTER the first render
292
- * (`initialize()` restores synchronously from storage; this costs one request).
293
- *
294
- * - The server confirms the user: the session is refreshed and {@link AuthState.status}
295
- * stays `'authenticated'`.
296
- * - The server answers 401 or 403: the token is dead — the stored session is
297
- * cleared and the status becomes `'unauthenticated'`.
298
- * - The server cannot confirm the session — a network failure, a 5xx, or a 404
299
- * from a misconfigured {@link AuthClientConfig.currentUserEndpoint}: the
300
- * session (token AND user) is KEPT and the status becomes `'unavailable'`, so
301
- * the app can retry instead of signing the user out over a bad connection or a
302
- * route that does not exist. The 404 error names `currentUserEndpoint`.
303
- *
304
- * Do not hand-write this beside the client: a parallel token/user store for a
305
- * "validating"/"unavailable" flag is the race this method removes.
306
- *
307
- * Optional: a provider with no restorable session has nothing to validate.
308
- */
309
- validateSession?(): Promise<void>;
310
- /**
311
- * Resolves when the session restore started by `initialize()` has settled — the point
312
- * after which the client knows whether this visitor is signed in.
313
- *
314
329
  // … truncated — the full declaration is in node_modules/@molecule/app-react/dist
315
330
  ```
316
331
 
@@ -399,9 +414,20 @@ interface FormController<T extends FormValuesConstraint<T> = Record<string, unkn
399
414
  * `defaultValues`, or the values a previous `reset(values)` established.
400
415
  * Passing `values` merges them over the current baseline and makes the
401
416
  * result the new baseline, so a form reset to freshly loaded or saved data
402
- * reads clean (`isDirty === false`).
417
+ * reads clean (`isDirty === false`). A changed non-null `id` replaces the
418
+ * record and cancels a pending submit.
403
419
  */
404
420
  reset(values?: Partial<T>): void
421
+ /**
422
+ * Applies externally loaded values without clearing submission state.
423
+ *
424
+ * The new clean baseline is the original `defaultValues` plus this latest
425
+ * record, never a merge of earlier records. With `keepDirtyValues`, edited
426
+ * fields retain their values while pristine fields take the loaded values.
427
+ * A changed non-null `id` still replaces the record and cancels a pending
428
+ * submit. Equal-content baselines cause no update.
429
+ */
430
+ updateValues(values: Partial<T>, resetOptions?: FormResetOptions): void
405
431
  /**
406
432
  * Handles form submission.
407
433
  */
@@ -424,6 +450,21 @@ interface FormController<T extends FormValuesConstraint<T> = Record<string, unkn
424
450
  }
425
451
  ```
426
452
 
453
+ #### `FormResetOptions`
454
+
455
+ Options controlling how externally supplied values replace a form's baseline.
456
+
457
+ ```typescript
458
+ interface FormResetOptions {
459
+ /**
460
+ * Keep fields whose current value differs from their baseline when `values`
461
+ * changes. Pristine fields still take the new values. False by default, so a
462
+ * new record can replace edits from the previous record.
463
+ */
464
+ keepDirtyValues?: boolean
465
+ }
466
+ ```
467
+
427
468
  #### `FormOptions`
428
469
 
429
470
  Form creation options.
@@ -435,6 +476,14 @@ interface FormOptions<T extends FormValuesConstraint<T>> {
435
476
  * is created. Mutating this object afterwards does not change the baseline.
436
477
  */
437
478
  defaultValues?: Partial<T>
479
+ /**
480
+ * Externally loaded values, merged over `defaultValues` as the clean
481
+ * baseline. Framework bindings also apply later content changes; use
482
+ * `resetOptions.keepDirtyValues` to preserve edits while pristine fields fill.
483
+ */
484
+ values?: Partial<T>
485
+ /** Controls how framework bindings apply changed `values`. */
486
+ resetOptions?: FormResetOptions
438
487
  /**
439
488
  * Validation mode.
440
489
  */
@@ -881,6 +930,7 @@ interface UseFormResult<T extends FormValuesConstraint<T>> {
881
930
  onSubmit: (values: T) => void | Promise<void>,
882
931
  ) => (event?: React.FormEvent) => Promise<void>
883
932
  reset: (values?: Partial<T>) => void
933
+ updateValues: (values: Partial<T>, resetOptions?: FormResetOptions) => void
884
934
  validate: () => Promise<boolean>
885
935
  }
886
936
  ```
@@ -904,7 +954,8 @@ Result of useHttp hook.
904
954
  ```typescript
905
955
  interface UseHttpResult<T> extends UseHttpState<T> {
906
956
  /**
907
- * Runs the request and resolves with its data, or `null` on failure.
957
+ * Runs the request and resolves with its own data, or `null` on failure,
958
+ * even when a newer request or `reset` has superseded its displayed state.
908
959
  *
909
960
  * This function is IDENTITY-STABLE for as long as `method`, `url` and the HTTP
910
961
  * client do not change, so it is safe in a `useEffect` dependency array. Its
@@ -931,11 +982,14 @@ interface UseHttpOptions<T> extends RequestConfig {
931
982
  */
932
983
  immediate?: boolean
933
984
  /**
934
- * Callback when request succeeds.
985
+ * Callback for each request that succeeds while mounted, including a request
986
+ * superseded by another `execute` or `reset`. Read `data` from the hook when
987
+ * only the newest request's result should drive the UI.
935
988
  */
936
989
  onSuccess?: (data: T) => void
937
990
  /**
938
- * Callback when request fails.
991
+ * Callback for each request that fails while mounted, including a request
992
+ * superseded by another `execute` or `reset`.
939
993
  */
940
994
  onError?: (error: Error) => void
941
995
  }
@@ -1302,6 +1356,19 @@ interface ProviderProps {
1302
1356
  }
1303
1357
  ```
1304
1358
 
1359
+ #### `SessionReadyProps`
1360
+
1361
+ Props for the `SessionReady` component.
1362
+
1363
+ ```typescript
1364
+ interface SessionReadyProps {
1365
+ /** What renders once the initial restore settles — signed in, signed out, or unavailable. */
1366
+ children: ReactNode
1367
+ /** What renders while the session restore is still undecided; default nothing. */
1368
+ fallback?: ReactNode
1369
+ }
1370
+ ```
1371
+
1305
1372
  #### `StateProviderProps`
1306
1373
 
1307
1374
  Props for state provider component.
@@ -1431,6 +1498,20 @@ Properties for molecule provider.
1431
1498
  interface MoleculeProviderProps extends ProviderProps {
1432
1499
  state?: StateProvider
1433
1500
  auth?: AuthClient<unknown>
1501
+ /**
1502
+ * Render the app only once `auth` leaves its initial loading status ({@link SessionReadyProps}).
1503
+ * After a full page load a returning visitor's session is restored by one request, and until
1504
+ * it answers `useAuth().user` is `null` on every page — a public page's signed-in action
1505
+ * (`if (!user) return`, a redirect to `/login`) then treats a signed-in visitor as signed out.
1506
+ * When restoration succeeds, every page's first render reads the restored user. An
1507
+ * `'unavailable'` session also renders, but remains unconfirmed and may have a null user.
1508
+ * A visitor with no session to restore settles in microtasks without a request.
1509
+ * Ignored without `auth`. Synchronous SSR renders the fallback for a loading client:
1510
+ * initialize a request-scoped client first, or leave this disabled for SSR.
1511
+ */
1512
+ awaitSession?: boolean
1513
+ /** Rendered instead of the app while {@link MoleculeProviderProps.awaitSession} waits; default nothing. */
1514
+ sessionFallback?: ReactNode
1434
1515
  theme?: ThemeProvider
1435
1516
  router?: Router
1436
1517
  i18n?: I18nProvider
@@ -1481,7 +1562,9 @@ interface UseAuthResult<
1481
1562
  * The session state as one value — render from this instead of combining
1482
1563
  * `initialized`/`authenticated`/`loading`. A stored token AND user make it
1483
1564
  * `'authenticated'` on the first render; only `'loading'` means "no decision
1484
- * yet", so a route guard waits on that and nothing else.
1565
+ * yet", so a route guard waits on that and nothing else. `'unavailable'` keeps
1566
+ * a session candidate for retry and can have a null profile; redirect only on
1567
+ * `'unauthenticated'`.
1485
1568
  */
1486
1569
  status: AuthState<T>['status']
1487
1570
  state: AuthState<T>
@@ -1501,8 +1584,10 @@ interface UseAuthResult<
1501
1584
  * `authenticated` read signed out.
1502
1585
  */
1503
1586
  setSession: AuthClient<T>['setSession']
1587
+ /** Whether a session is authenticated locally; this can be false while unavailable. */
1504
1588
  isAuthenticated: boolean
1505
1589
  isLoading: boolean
1590
+ /** The cached profile, which can be null while an unavailable candidate is retained. */
1506
1591
  user: T | null
1507
1592
  }
1508
1593
  ```
@@ -1982,6 +2067,12 @@ function useAuthClient<
1982
2067
 
1983
2068
  Hook for authentication state and actions.
1984
2069
 
2070
+ `status === 'loading'` is undecided: wait for it and redirect only on
2071
+ `status === 'unauthenticated'`. `'unavailable'` keeps the session candidate
2072
+ for retry, but `isAuthenticated` can be false and `user` can be null while a
2073
+ token/cookie-only restore has no cached profile. A restore failure other than
2074
+ 401/403 never signs out.
2075
+
1985
2076
  Pass the app's user type — the SAME type used for the auth client and its
1986
2077
  config (`createJWTAuthClient<MyUser>`, `AuthClientConfig<MyUser>`) — so
1987
2078
  `user` and `setSession`/`setUser` are typed for the pages:
@@ -2185,7 +2276,7 @@ function useForm<T extends FormValuesConstraint<T> = Record<string, unknown>>(
2185
2276
  ): UseFormResult<T>
2186
2277
  ```
2187
2278
 
2188
- - `options` — Form options. `defaultValues`, `mode` and the form-level `validate` are forwarded to the provider; omit the options entirely for a form whose fields are registered with their own defaults.
2279
+ - `options` — Form options. `defaultValues`, `mode` and the form-level `validate` are forwarded to the provider. When `values` changes by content, it becomes the new baseline; `resetOptions.keepDirtyValues` retains edited fields while pristine fields take the loaded values. Omit the options entirely for a form whose fields are registered with their own defaults.
2189
2280
 
2190
2281
  **Returns:** Form state and methods
2191
2282
 
@@ -3113,6 +3204,8 @@ function MoleculeProvider({
3113
3204
  workspace,
3114
3205
  editor,
3115
3206
  preview,
3207
+ awaitSession = false,
3208
+ sessionFallback,
3116
3209
  }: MoleculeProviderProps): React.ReactElement
3117
3210
  ```
3118
3211
 
@@ -3120,6 +3213,38 @@ function MoleculeProvider({
3120
3213
 
3121
3214
  **Returns:** The rendered combined provider element.
3122
3215
 
3216
+ #### `SessionReady(props)`
3217
+
3218
+ Renders its children once `useAuth().status` has left `'loading'` (signed in, signed out,
3219
+ or `'unavailable'`), and `fallback` until then. It never redirects. An unavailable session
3220
+ is unconfirmed and may still have `user === null`; handle that status separately from sign-out.
3221
+
3222
+ After a full page load a returning visitor's session is restored by one request. Until it
3223
+ answers, `useAuth().user` is `null` everywhere, so a public page that renders a signed-in action
3224
+ at once lets that action run against "signed out": a Follow button whose handler starts with
3225
+ `if (!user) return` ignores the click, one that redirects to `/login` signs the visitor out of
3226
+ the flow. Wrapping the routes (or passing `awaitSession` to `MoleculeProvider`) makes every
3227
+ page's first render read the restored user when restoration succeeds.
3228
+
3229
+ A visitor with no session to restore settles in microtasks without a request. Must render
3230
+ inside the auth context (`AuthProvider`, or `MoleculeProvider` with `auth`). A client that
3231
+ nobody initialized starts its restore on this first read.
3232
+ The bundled JWT client bounds each auth request (default 10 seconds) and settles a timeout
3233
+ as `'unavailable'`. Custom clients must also settle their initial session status.
3234
+
3235
+ Synchronous SSR renders the fallback while a client is loading. Initialize a request-scoped
3236
+ client before rendering to include its decided content, or leave the hold disabled for SSR.
3237
+
3238
+ _(description truncated — read the full text in `node_modules/@molecule/app-react/dist/session-ready.d.ts`.)_
3239
+
3240
+ ```typescript
3241
+ function SessionReady({ children, fallback = null }: SessionReadyProps): ReactNode
3242
+ ```
3243
+
3244
+ - `props` — {@link SessionReadyProps}.
3245
+
3246
+ **Returns:** The children once the session is decided, otherwise the fallback.
3247
+
3123
3248
  ### Constants
3124
3249
 
3125
3250
  #### `DEFAULT_AGENT_NAME`
@@ -3252,7 +3377,7 @@ Peer dependencies:
3252
3377
 
3253
3378
  - `@molecule/app-auth` ^3.0.0
3254
3379
  - `@molecule/app-ai-models` ^1.0.1
3255
- - `@molecule/app-forms` ^2.0.0
3380
+ - `@molecule/app-forms` ^2.2.0
3256
3381
  - `@molecule/app-utilities` ^1.0.1
3257
3382
  - `@molecule/app-http` ^1.0.1
3258
3383
  - `@molecule/app-i18n` ^1.0.1
@@ -19,6 +19,12 @@ export declare function useAuthClient<T = unknown, TRegister extends RegisterExt
19
19
  /**
20
20
  * Hook for authentication state and actions.
21
21
  *
22
+ * `status === 'loading'` is undecided: wait for it and redirect only on
23
+ * `status === 'unauthenticated'`. `'unavailable'` keeps the session candidate
24
+ * for retry, but `isAuthenticated` can be false and `user` can be null while a
25
+ * token/cookie-only restore has no cached profile. A restore failure other than
26
+ * 401/403 never signs out.
27
+ *
22
28
  * Pass the app's user type — the SAME type used for the auth client and its
23
29
  * config (`createJWTAuthClient<MyUser>`, `AuthClientConfig<MyUser>`) — so
24
30
  * `user` and `setSession`/`setUser` are typed for the pages:
@@ -44,13 +50,13 @@ export declare function useAuthClient<T = unknown, TRegister extends RegisterExt
44
50
  *
45
51
  * @example
46
52
  * ```tsx
47
- * const { user, isAuthenticated, login, logout } = useAuth<{ id: string; name?: string }>()
53
+ * const { user, status, login, logout } = useAuth<{ id: string; name?: string }>()
48
54
  * const { register } = useAuth<MyUser, { username: string }>()
49
55
  * await register({ email, password, username }) // `username` is typed
50
56
  *
51
- * if (isAuthenticated) {
52
- * return <div>Welcome, {user?.name}!</div>
53
- * }
57
+ * if (status === 'loading') return <p>Restoring session...</p>
58
+ * if (status === 'unauthenticated') return <Navigate to="/login" replace />
59
+ * return <div>Welcome, {user?.name}!</div>
54
60
  * ```
55
61
  */
56
62
  export declare function useAuth<T = unknown, TRegister extends RegisterExtraShape<TRegister> = Record<never, never>>(options?: UseAuthOptions): UseAuthResult<T, TRegister>;
@@ -1 +1 @@
1
- {"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../src/hooks/useAuth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EACV,UAAU,EAKV,kBAAkB,EACnB,MAAM,oBAAoB,CAAA;AAI3B,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhE;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,CAAC,GAAG,OAAO,EACX,SAAS,SAAS,kBAAkB,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,KACnE,UAAU,CAAC,CAAC,EAAE,SAAS,CAAC,CAU5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,OAAO,CACrB,CAAC,GAAG,OAAO,EACX,SAAS,SAAS,kBAAkB,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACtE,OAAO,CAAC,EAAE,cAAc,GAAG,aAAa,CAAC,CAAC,EAAE,SAAS,CAAC,CA2EvD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,IAAI,CAG/C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C"}
1
+ {"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../src/hooks/useAuth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EACV,UAAU,EAKV,kBAAkB,EACnB,MAAM,oBAAoB,CAAA;AAI3B,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhE;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,CAAC,GAAG,OAAO,EACX,SAAS,SAAS,kBAAkB,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,KACnE,UAAU,CAAC,CAAC,EAAE,SAAS,CAAC,CAU5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,OAAO,CACrB,CAAC,GAAG,OAAO,EACX,SAAS,SAAS,kBAAkB,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACtE,OAAO,CAAC,EAAE,cAAc,GAAG,aAAa,CAAC,CAAC,EAAE,SAAS,CAAC,CA2EvD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,IAAI,CAG/C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C"}
@@ -28,6 +28,12 @@ export function useAuthClient() {
28
28
  /**
29
29
  * Hook for authentication state and actions.
30
30
  *
31
+ * `status === 'loading'` is undecided: wait for it and redirect only on
32
+ * `status === 'unauthenticated'`. `'unavailable'` keeps the session candidate
33
+ * for retry, but `isAuthenticated` can be false and `user` can be null while a
34
+ * token/cookie-only restore has no cached profile. A restore failure other than
35
+ * 401/403 never signs out.
36
+ *
31
37
  * Pass the app's user type — the SAME type used for the auth client and its
32
38
  * config (`createJWTAuthClient<MyUser>`, `AuthClientConfig<MyUser>`) — so
33
39
  * `user` and `setSession`/`setUser` are typed for the pages:
@@ -53,13 +59,13 @@ export function useAuthClient() {
53
59
  *
54
60
  * @example
55
61
  * ```tsx
56
- * const { user, isAuthenticated, login, logout } = useAuth<{ id: string; name?: string }>()
62
+ * const { user, status, login, logout } = useAuth<{ id: string; name?: string }>()
57
63
  * const { register } = useAuth<MyUser, { username: string }>()
58
64
  * await register({ email, password, username }) // `username` is typed
59
65
  *
60
- * if (isAuthenticated) {
61
- * return <div>Welcome, {user?.name}!</div>
62
- * }
66
+ * if (status === 'loading') return <p>Restoring session...</p>
67
+ * if (status === 'unauthenticated') return <Navigate to="/login" replace />
68
+ * return <div>Welcome, {user?.name}!</div>
63
69
  * ```
64
70
  */
65
71
  export function useAuth(options) {
@@ -1 +1 @@
1
- {"version":3,"file":"useAuth.js","sourceRoot":"","sources":["../../src/hooks/useAuth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAUxF,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAA;AAEtC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAG5C;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa;IAI3B,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,CAAC,CAAC,oCAAoC,EAAE,SAAS,EAAE;YACjD,YAAY,EAAE,mDAAmD;SAClE,CAAC,CACH,CAAA;IACH,CAAC;IACD,OAAO,MAAkC,CAAA;AAC3C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,UAAU,OAAO,CAGrB,OAAwB;IACxB,MAAM,MAAM,GAAG,aAAa,EAAgB,CAAA;IAC5C,MAAM,EAAE,WAAW,GAAG,KAAK,EAAE,GAAG,OAAO,IAAI,EAAE,CAAA;IAE7C,uDAAuD;IACvD,MAAM,cAAc,GAAG,MAAM,CAAsB,IAAI,CAAC,CAAA;IAExD,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAA;QAC9B,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAA;QACnC,IACE,IAAI,KAAK,IAAI;YACb,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;YAC3B,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,WAAW;YACrC,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC,aAAa;YACzC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;YACvB,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO;YAC7B,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EACzB,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;QACD,cAAc,CAAC,OAAO,GAAG,IAAI,CAAA;QAC7B,OAAO,IAAI,CAAA;IACb,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEZ,MAAM,SAAS,GAAG,WAAW,CAC3B,CAAC,aAAyB,EAAE,EAAE;QAC5B,OAAO,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,CAAA;IACnD,CAAC,EACD,CAAC,MAAM,CAAC,CACT,CAAA;IAED,MAAM,KAAK,GAAG,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,WAAW,CAAC,CAAA;IAEvE,qCAAqC;IACrC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,WAAW,IAAI,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YAC1D,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC1B,iDAAiD;YACnD,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAA;IAE7D,2BAA2B;IAC3B,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,WAA6B,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEjG,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAE3D,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,IAA6B,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEhG,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAE7D,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAc,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAE/E,MAAM,UAAU,GAAG,WAAW,CAC5B,CAAC,OAA2B,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAC3D,CAAC,MAAM,CAAC,CACT,CAAA;IAED,OAAO;QACL,mEAAmE;QACnE,6EAA6E;QAC7E,0EAA0E;QAC1E,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK;QACL,KAAK;QACL,MAAM;QACN,QAAQ;QACR,OAAO;QACP,OAAO;QACP,UAAU;QACV,eAAe,EAAE,KAAK,CAAC,aAAa;QACpC,SAAS,EAAE,KAAK,CAAC,OAAO;QACxB,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO;IACrB,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,EAAK,CAAA;IAC7B,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,EAAE,eAAe,EAAE,GAAG,OAAO,EAAE,CAAA;IACrC,OAAO,eAAe,CAAA;AACxB,CAAC"}
1
+ {"version":3,"file":"useAuth.js","sourceRoot":"","sources":["../../src/hooks/useAuth.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/hooks/useChat.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,OAAO,KAAK,EAIV,WAAW,EACX,YAAY,EAGb,MAAM,uBAAuB,CAAA;AAK9B,OAAO,KAAK,EAAsB,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AA8cpF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAc1F;AAiBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAQ5F;AAyDD;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAK9C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,YAAY,CAU9C;AAkDD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CAm4E9D"}
1
+ {"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/hooks/useChat.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,OAAO,KAAK,EAIV,WAAW,EACX,YAAY,EAGb,MAAM,uBAAuB,CAAA;AAK9B,OAAO,KAAK,EAAsB,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AA8cpF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAc1F;AAiBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAQ5F;AAyDD;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAK9C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,YAAY,CAU9C;AAkDD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CAo4E9D"}
@@ -1519,6 +1519,7 @@ export function useChat(options) {
1519
1519
  type: 'verification',
1520
1520
  status: event.status,
1521
1521
  ...(event.output ? { output: event.output } : {}),
1522
+ ...(event.renderCheckUnavailable ? { renderCheckUnavailable: true } : {}),
1522
1523
  workspaces: event.workspaces,
1523
1524
  ...(event.categories ? { categories: event.categories } : {}),
1524
1525
  });