@molecule/app-react-native 3.0.5 → 3.0.7

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.
Files changed (2) hide show
  1. package/README.md +201 -104
  2. package/package.json +2 -2
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-11-06T02:49:48.362Z
6
+ Generated: 2026-11-09T15:44:47.787Z
7
7
  -->
8
8
 
9
9
  # @molecule/app-react-native
@@ -78,25 +78,30 @@ interface AuthState<T = UserProfile> {
78
78
  * never signs out. See {@link AuthStatus}.
79
79
  */
80
80
  status: AuthStatus
81
+
81
82
  /**
82
83
  * Whether auth state has been initialized.
83
84
  */
84
85
  initialized: boolean
86
+
85
87
  /**
86
88
  * Whether the client has an authenticated session. This is not a server
87
89
  * confirmation flag: during `'unavailable'` it can be false for a retained
88
90
  * token/cookie-only candidate with no cached profile. Use `status` for redirects.
89
91
  */
90
92
  authenticated: boolean
93
+
91
94
  /**
92
95
  * Current user, or null when none is cached. In particular, an `'unavailable'`
93
96
  * token/cookie-only restore has a retained candidate but no profile yet.
94
97
  */
95
98
  user: T | null
99
+
96
100
  /**
97
101
  * Whether an auth operation is in progress.
98
102
  */
99
103
  loading: boolean
104
+
100
105
  /**
101
106
  * Last auth error (if any).
102
107
  */
@@ -117,58 +122,82 @@ interface AuthClient<
117
122
  > {
118
123
  /** Returns the current authentication state snapshot. */
119
124
  getState(): AuthState<T>
125
+
120
126
  /** Returns whether the user is currently authenticated. */
121
127
  isAuthenticated(): boolean
128
+
122
129
  /** Gets the current user. */
123
130
  getUser(): T | null
131
+
124
132
  /** Optional ownership marker for the current session. */
125
133
  getSessionRevision?(): number
134
+
126
135
  /** Updates the cached user object (state + persistent storage) without hitting the network. */
127
136
  setUser(user: T | null): void
137
+
128
138
  /** Gets the current access token. */
129
139
  getAccessToken(): string | null
140
+
130
141
  /** Stores the access token in the configured token storage adapter (in a browser, Web Storage; `'memory'` where no browser store exists). */
131
142
  setAccessToken(token: string | null): void
143
+
132
144
  /** Adopts a session an out-of-band exchange already established: stores the access token (and the refresh token when given), caches the user, and sets the COMPLET… */
133
145
  setSession(session: AuthSessionSeed<T>): void
146
+
134
147
  /** Gets the refresh token. */
135
148
  getRefreshToken(): string | null
149
+
136
150
  /** Logs in with credentials. */
137
151
  login(credentials: LoginCredentials): Promise<AuthResult<T>>
152
+
138
153
  /** Logs out the current user. */
139
154
  logout(): Promise<void>
155
+
140
156
  /** Registers a new user. */
141
157
  register(data: RegisterData<TRegister>): Promise<AuthResult<T>>
142
- /** Refreshes the access token. */
158
+
159
+ /** Exchanges a stored refresh token, or re-reads `currentUserEndpoint` with the existing access token/session cookie when the API has no refresh credential. */
143
160
  refresh(): Promise<AuthResult<T>>
161
+
144
162
  /** Requests a password reset. */
145
163
  requestPasswordReset(data: PasswordResetRequest): Promise<void>
164
+
146
165
  /** Confirms a password reset. */
147
166
  confirmPasswordReset(data: PasswordResetConfirm): Promise<void>
167
+
148
168
  /** Updates the current user's profile. */
149
169
  updateProfile(data: Partial<T>): Promise<T>
170
+
150
171
  /** Changes the current user's password. */
151
172
  changePassword(oldPassword: string, newPassword: string): Promise<void>
173
+
152
174
  /** Initializes auth state (e.g., from stored tokens). */
153
175
  initialize(): Promise<void>
176
+
154
177
  /** Re-checks a restored session against the server, in the background — the boot-time validation an app with a stored token needs when a revoked or expired token… */
155
178
  validateSession?(): Promise<void>
179
+
156
180
  /** Resolves when the session restore started by `initialize()` has settled — the point after which the client knows whether this visitor is signed in. */
157
181
  whenRestored?(): Promise<void>
182
+
158
183
  /** Subscribes to auth state changes. */
159
184
  subscribe(callback: (state: AuthState<T>) => void): () => void
185
+
160
186
  /** Subscribes to auth state changes (alias for subscribe). */
161
187
  onAuthChange(callback: (state: AuthState<T>) => void): () => void
188
+
162
189
  /** Gets the current access token (alias for getAccessToken). */
163
190
  getToken?(): string | null
191
+
164
192
  /** Adds an auth event listener. */
165
193
  addEventListener(listener: AuthEventListener<T>): () => void
194
+
166
195
  /** Destroys the auth client. */
167
196
  destroy(): void
168
197
  }
169
198
  ```
170
199
 
171
- _(Member comments are shortened to their first sentence to fit; the full declaration is in `node_modules/@molecule/app-react-native/dist`.)_
200
+ _(Member comments are shortened to their first sentence to fit; the full declaration is in `node_modules/@molecule/app-auth/dist/types.d.ts`.)_
172
201
 
173
202
  #### `FormController`
174
203
 
@@ -182,72 +211,77 @@ interface FormController<T extends FormValuesConstraint<T> = Record<string, unkn
182
211
  * Gets the current form state.
183
212
  */
184
213
  getState(): FormState<T>
214
+
185
215
  /**
186
216
  * Gets the value of a specific field.
187
217
  */
188
218
  getValue(name: string): unknown
189
219
  getValue<K extends keyof T>(name: K): T[K]
220
+
190
221
  /**
191
222
  * Gets all form values.
192
223
  */
193
224
  getValues(): T
225
+
194
226
  /**
195
227
  * Sets the value of a specific field.
196
228
  */
197
229
  setValue(
198
230
  name: string,
199
231
  value: unknown,
200
- options?: {
201
- shouldValidate?: boolean
202
- shouldDirty?: boolean
203
- shouldTouch?: boolean
204
- },
232
+ options?: { shouldValidate?: boolean; shouldDirty?: boolean; shouldTouch?: boolean },
205
233
  ): void
234
+
206
235
  /**
207
236
  * Sets multiple values at once.
208
237
  */
209
- setValues(
210
- values: Partial<T>,
211
- options?: {
212
- shouldValidate?: boolean
213
- },
214
- ): void
238
+ setValues(values: Partial<T>, options?: { shouldValidate?: boolean }): void
239
+
215
240
  /**
216
241
  * Gets the error for a specific field.
217
242
  */
218
243
  getError(name: string): string | undefined
244
+
219
245
  /**
220
246
  * Sets the error for a specific field.
221
247
  */
222
248
  setError(name: string, error: string | undefined): void
249
+
223
250
  /**
224
251
  * Clears the error for a specific field.
225
252
  */
226
253
  clearError<K extends keyof T>(name: K): void
254
+
227
255
  /**
228
256
  * Clears all errors.
229
257
  */
230
258
  clearErrors(): void
259
+
231
260
  /**
232
261
  * Gets the field state for a specific field.
233
262
  */
234
263
  getFieldState<K extends keyof T>(name: K): FieldState<T[K]>
264
+
235
265
  /**
236
266
  * Registers a field for form management.
237
267
  */
238
268
  register(nameOrOptions: string | RegisterOptions, options?: RegisterOptions): FieldRegistration
269
+
239
270
  /**
240
271
  * Unregisters a field.
241
272
  */
242
273
  unregister(name: string): void
274
+
243
275
  /**
244
276
  * Validates a specific field.
245
277
  */
246
278
  validateField<K extends keyof T>(name: K): Promise<boolean>
279
+
247
280
  /**
248
281
  * Validates all fields.
249
282
  */
250
283
  validate(): Promise<boolean>
284
+
251
285
  /**
252
286
  * Resets the form and clears errors, touched fields and submission state.
253
287
  *
@@ -259,6 +293,7 @@ interface FormController<T extends FormValuesConstraint<T> = Record<string, unkn
259
293
  * record and cancels a pending submit.
260
294
  */
261
295
  reset(values?: Partial<T>): void
296
+
262
297
  /**
263
298
  * Applies externally loaded values without clearing submission state.
264
299
  *
@@ -269,6 +304,7 @@ interface FormController<T extends FormValuesConstraint<T> = Record<string, unkn
269
304
  * submit. Equal-content baselines cause no update.
270
305
  */
271
306
  updateValues(values: Partial<T>, resetOptions?: FormResetOptions): void
307
+
272
308
  /**
273
309
  * Handles form submission.
274
310
  */
@@ -276,14 +312,17 @@ interface FormController<T extends FormValuesConstraint<T> = Record<string, unkn
276
312
  onSubmit: (values: T) => void | Promise<void>,
277
313
  onError?: (errors: Partial<Record<keyof T, string>>) => void,
278
314
  ): (event?: { preventDefault?: () => void }) => Promise<void>
315
+
279
316
  /**
280
317
  * Sets focus to a field.
281
318
  */
282
319
  setFocus(name: keyof T): void
320
+
283
321
  /**
284
322
  * Subscribes to form state changes.
285
323
  */
286
324
  subscribe(callback: (state: FormState<T>) => void): () => void
325
+
287
326
  /**
288
327
  * Destroys the form controller.
289
328
  */
@@ -317,26 +356,32 @@ interface FormOptions<T extends FormValuesConstraint<T>> {
317
356
  * is created. Mutating this object afterwards does not change the baseline.
318
357
  */
319
358
  defaultValues?: Partial<T>
359
+
320
360
  /**
321
361
  * Externally loaded values, merged over `defaultValues` as the clean
322
362
  * baseline. Framework bindings also apply later content changes; use
323
363
  * `resetOptions.keepDirtyValues` to preserve edits while pristine fields fill.
324
364
  */
325
365
  values?: Partial<T>
366
+
326
367
  /** Controls how framework bindings apply changed `values`. */
327
368
  resetOptions?: FormResetOptions
369
+
328
370
  /**
329
371
  * Validation mode.
330
372
  */
331
373
  mode?: 'onSubmit' | 'onChange' | 'onBlur' | 'all'
374
+
332
375
  /**
333
376
  * Revalidation mode.
334
377
  */
335
378
  reValidateMode?: 'onChange' | 'onBlur' | 'onSubmit'
379
+
336
380
  /**
337
381
  * Whether to focus the first error field on submit.
338
382
  */
339
383
  shouldFocusError?: boolean
384
+
340
385
  /**
341
386
  * Form-level validation function.
342
387
  */
@@ -358,54 +403,65 @@ interface HttpClient {
358
403
  * Base URL for all requests.
359
404
  */
360
405
  baseURL: string
406
+
361
407
  /**
362
408
  * Default headers for all requests.
363
409
  */
364
410
  defaultHeaders: Record<string, string>
411
+
365
412
  /**
366
413
  * Makes a generic HTTP request. `data` is a field of THIS config object — the same body
367
414
  * `post(url, data, config)` takes as its second argument.
368
415
  */
369
416
  request<T = unknown>(config: FullRequestConfig): Promise<HttpResponse<T>>
417
+
370
418
  /**
371
419
  * Makes a GET request.
372
420
  */
373
421
  get<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>>
422
+
374
423
  /**
375
424
  * Makes a POST request. `data` is the request BODY, not a config object: the wire body is
376
425
  * `data` itself, so `post(url, { data: body })` sends `{"data":body}` — pass `body` directly.
377
426
  * A config (headers, params) is the THIRD argument.
378
427
  */
379
428
  post<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>
429
+
380
430
  /**
381
431
  * Makes a PUT request. `data` is the request BODY (second argument) — never `{ data: body }`,
382
432
  * which sends `{"data":body}`; a config is the THIRD argument.
383
433
  */
384
434
  put<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>
435
+
385
436
  /**
386
437
  * Makes a PATCH request. `data` is the request BODY (second argument) — never `{ data: body }`,
387
438
  * which sends `{"data":body}`; a config is the THIRD argument.
388
439
  */
389
440
  patch<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>
441
+
390
442
  /**
391
443
  * Makes a DELETE request.
392
444
  */
393
445
  delete<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>>
446
+
394
447
  /**
395
448
  * Adds a request interceptor.
396
449
  * Returns a function to remove the interceptor.
397
450
  */
398
451
  addRequestInterceptor(interceptor: RequestInterceptor): () => void
452
+
399
453
  /**
400
454
  * Adds a response interceptor.
401
455
  * Returns a function to remove the interceptor.
402
456
  */
403
457
  addResponseInterceptor(interceptor: ResponseInterceptor): () => void
458
+
404
459
  /**
405
460
  * Adds an error interceptor.
406
461
  * Returns a function to remove the interceptor.
407
462
  */
408
463
  addErrorInterceptor(interceptor: ErrorInterceptor): () => void
464
+
409
465
  /**
410
466
  * Sets the authorization token.
411
467
  *
@@ -417,11 +473,13 @@ interface HttpClient {
417
473
  * the resolver is removed, the static token is sent again.
418
474
  */
419
475
  setAuthToken(token: string | null): void
476
+
420
477
  /**
421
478
  * Returns the token the client applies to its next request: the resolver's answer when one
422
479
  * is registered, else the static token from `setAuthToken`.
423
480
  */
424
481
  getAuthToken(): string | null
482
+
425
483
  /**
426
484
  * Registers the LIVE token resolver every request of this client reads, or clears it with
427
485
  * `null`.
@@ -452,6 +510,7 @@ interface HttpClient {
452
510
  * @param resolver - Returns the current access token (or null/undefined), or `null` to clear.
453
511
  */
454
512
  setAuthTokenResolver?(resolver: AuthTokenResolver | null): void
513
+
455
514
  /**
456
515
  * Registers a handler for authentication errors (401).
457
516
  *
@@ -473,26 +532,32 @@ interface Router {
473
532
  * Returns the current route location (pathname, search, hash, state).
474
533
  */
475
534
  getLocation(): RouteLocation
535
+
476
536
  /**
477
537
  * Gets the current route params.
478
538
  */
479
539
  getParams<T extends RouteParams = RouteParams>(): T
540
+
480
541
  /**
481
542
  * Gets the current query params.
482
543
  */
483
544
  getQuery(): QueryParams
545
+
484
546
  /**
485
547
  * Gets a specific query parameter.
486
548
  */
487
549
  getQueryParam(key: string): string | undefined
550
+
488
551
  /**
489
552
  * Gets the current hash.
490
553
  */
491
554
  getHash(): string
555
+
492
556
  /**
493
557
  * Navigates to a path.
494
558
  */
495
559
  navigate(path: string, options?: NavigateOptions): void
560
+
496
561
  /**
497
562
  * Navigates to a named route.
498
563
  */
@@ -502,36 +567,44 @@ interface Router {
502
567
  query?: QueryParams,
503
568
  options?: NavigateOptions,
504
569
  ): void
570
+
505
571
  /**
506
572
  * Goes back in history.
507
573
  */
508
574
  back(): void
575
+
509
576
  /**
510
577
  * Goes forward in history.
511
578
  */
512
579
  forward(): void
580
+
513
581
  /**
514
582
  * Goes to a specific point in history.
515
583
  */
516
584
  go(delta: number): void
585
+
517
586
  /**
518
587
  * Updates the current query params.
519
588
  */
520
589
  setQuery(params: QueryParams, options?: NavigateOptions): void
590
+
521
591
  /**
522
592
  * Updates a specific query parameter.
523
593
  */
524
594
  setQueryParam(key: string, value: string | undefined, options?: NavigateOptions): void
595
+
525
596
  /**
526
597
  * Updates the current hash.
527
598
  */
528
599
  setHash(hash: string, options?: NavigateOptions): void
600
+
529
601
  /**
530
602
  * Checks if a path matches the current location.
531
603
  *
532
604
  * @returns `true` if the path matches the current route.
533
605
  */
534
606
  isActive(path: string, exact?: boolean): boolean
607
+
535
608
  /**
536
609
  * Matches a path pattern against a pathname.
537
610
  */
@@ -539,26 +612,32 @@ interface Router {
539
612
  pattern: string,
540
613
  pathname: string,
541
614
  ): RouteMatch<Params> | null
615
+
542
616
  /**
543
617
  * Generates a URL from a named route.
544
618
  */
545
619
  generatePath(name: string, params?: RouteParams, query?: QueryParams): string
620
+
546
621
  /**
547
622
  * Subscribes to route changes.
548
623
  */
549
624
  subscribe(listener: RouteChangeListener): () => void
625
+
550
626
  /**
551
627
  * Adds a navigation guard.
552
628
  */
553
629
  addGuard(guard: NavigationGuard): () => void
630
+
554
631
  /**
555
632
  * Registers route definitions.
556
633
  */
557
634
  registerRoutes(routes: RouteDefinition[]): void
635
+
558
636
  /**
559
637
  * Gets all registered routes.
560
638
  */
561
639
  getRoutes(): RouteDefinition[]
640
+
562
641
  /**
563
642
  * Destroys the router.
564
643
  */
@@ -576,10 +655,12 @@ interface RouterConfig {
576
655
  * Router mode.
577
656
  */
578
657
  mode?: 'history' | 'hash' | 'memory'
658
+
579
659
  /**
580
660
  * Base path.
581
661
  */
582
662
  basePath?: string
663
+
583
664
  /**
584
665
  * Initial routes.
585
666
  */
@@ -597,10 +678,12 @@ interface StoreConfig<T> {
597
678
  * Initial state value.
598
679
  */
599
680
  initialState: T
681
+
600
682
  /**
601
683
  * Optional name for debugging.
602
684
  */
603
685
  name?: string
686
+
604
687
  /**
605
688
  * Optional middleware functions.
606
689
  */
@@ -620,15 +703,18 @@ interface Store<T> {
620
703
  * Gets the current state.
621
704
  */
622
705
  getState(): T
706
+
623
707
  /**
624
708
  * Sets the state (partial or via updater function).
625
709
  */
626
710
  setState(partial: Partial<T> | ((state: T) => Partial<T>)): void
711
+
627
712
  /**
628
713
  * Subscribes to state changes.
629
714
  * Returns an unsubscribe function.
630
715
  */
631
716
  subscribe(listener: StateListener<T>): () => void
717
+
632
718
  /**
633
719
  * Destroys the store and cleans up subscriptions.
634
720
  */
@@ -786,21 +872,24 @@ Result of useForm hook.
786
872
 
787
873
  ```typescript
788
874
  interface UseFormResult<T extends FormValuesConstraint<T>> {
875
+ // State
789
876
  formState: FormState<T>
790
877
  isValid: boolean
791
878
  isDirty: boolean
792
879
  isSubmitting: boolean
880
+
881
+ // Field methods
793
882
  register: (
794
883
  name: keyof T,
795
- options?: Omit<RegisterOptions, 'name'> & {
796
- name?: string
797
- },
884
+ options?: Omit<RegisterOptions, 'name'> & { name?: string },
798
885
  ) => FieldRegistration
799
886
  getValue: <K extends keyof T>(name: K) => T[K]
800
887
  setValue: <K extends keyof T>(name: K, value: T[K]) => void
801
888
  getError: (name: keyof T) => string | undefined
802
889
  setError: (name: keyof T, error: string | undefined) => void
803
890
  clearErrors: () => void
891
+
892
+ // Form methods
804
893
  handleSubmit: (
805
894
  onSubmit: (values: T) => void | Promise<void>,
806
895
  ) => (event?: React.FormEvent) => Promise<void>
@@ -916,6 +1005,7 @@ interface UseOptimisticValueResult<T> extends OptimisticValueState<T> {
916
1005
  * @param next - The value the person chose.
917
1006
  */
918
1007
  set: (next: T) => void
1008
+
919
1009
  /**
920
1010
  * Like `set`, computed from the value shown now (`update((on) => !on)`).
921
1011
  *
@@ -1018,13 +1108,9 @@ interface UseProfileFormResult<TUser, TValues extends FormValuesConstraint<TValu
1018
1108
  register: <K extends keyof TValues>(
1019
1109
  name: K,
1020
1110
  options?: Parameters<UseFormResult<TValues>['register']>[1],
1021
- ) => Omit<FieldRegistration, 'value'> & {
1022
- value: TValues[K]
1023
- }
1111
+ ) => Omit<FieldRegistration, 'value'> & { value: TValues[K] }
1024
1112
  /** Bind to the native form, with a type="submit" button inside it. */
1025
- formProps: {
1026
- onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
1027
- }
1113
+ formProps: { onSubmit: (event: React.FormEvent<HTMLFormElement>) => void }
1028
1114
  /**
1029
1115
  * Validates and saves one submit-time snapshot. Concurrent submits share the
1030
1116
  * same promise. Resolves undefined on validation, save or propagation failure.
@@ -1178,16 +1264,20 @@ interface UseVerifyPaymentReturnOptions {
1178
1264
  * query parameter. Leave unset to verify only what the URL names.
1179
1265
  */
1180
1266
  provider?: string
1267
+
1181
1268
  /** Set `false` to skip verification entirely (e.g. behind a feature flag). */
1182
1269
  enabled?: boolean
1270
+
1183
1271
  /**
1184
1272
  * Remove the provider's query parameters from the address bar once the
1185
1273
  * purchase is verified, so a reload (or a shared link) cannot replay the
1186
1274
  * verification. Defaults to `true`.
1187
1275
  */
1188
1276
  cleanUrl?: boolean
1277
+
1189
1278
  /** Called once, after the purchase verifies. */
1190
1279
  onVerified?: () => void
1280
+
1191
1281
  /** Called when verification fails. */
1192
1282
  onError?: (error: Error) => void
1193
1283
  }
@@ -1205,14 +1295,19 @@ interface UseVerifyPaymentReturnResult {
1205
1295
  * flight; then `verified` or `failed`.
1206
1296
  */
1207
1297
  status: VerifyPaymentReturnStatus
1298
+
1208
1299
  /** `true` when the URL identifies a purchase to verify. */
1209
1300
  isReturn: boolean
1301
+
1210
1302
  /** The provider named by the URL (or the `provider` option). */
1211
1303
  provider: string | null
1304
+
1212
1305
  /** The provider transaction/session id read from the URL. */
1213
1306
  transactionId: string | null
1307
+
1214
1308
  /** Why verification failed, when it did. */
1215
1309
  error: Error | null
1310
+
1216
1311
  /** Re-run the verification — wire this to a retry button. */
1217
1312
  retry: () => void
1218
1313
  }
@@ -1463,6 +1558,8 @@ interface UseAuthResult<
1463
1558
  logout: AuthClient<T>['logout']
1464
1559
  register: AuthClient<T, TRegister>['register']
1465
1560
  refresh: AuthClient<T>['refresh']
1561
+ /** Save through the configured auth transport and persist/publish its canonical profile. */
1562
+ updateProfile: AuthClient<T>['updateProfile']
1466
1563
  setUser: AuthClient<T>['setUser']
1467
1564
  /**
1468
1565
  * Adopt a session an exchange outside the client already established (an
@@ -1700,10 +1797,7 @@ interface UseChatResult {
1700
1797
  * once per second; `attempt` is the 1-based retry number (capped at 3). 4XX,
1701
1798
  * limit/quota, and signup-required errors never auto-retry.
1702
1799
  */
1703
- retryCountdown: {
1704
- secondsRemaining: number
1705
- attempt: number
1706
- } | null
1800
+ retryCountdown: { secondsRemaining: number; attempt: number } | null
1707
1801
  /** Update the local mode state (for instant mode toggle without an AI turn). */
1708
1802
  setMode: (mode: 'plan' | 'execute') => void
1709
1803
  /** Update the local fast-mode state (for instant toggle without an AI turn). */
@@ -1981,6 +2075,18 @@ function useAIModels(projectId?: string): UseAIModelsResult
1981
2075
 
1982
2076
  **Returns:** Models, free-tier model, loading flag, and error.
1983
2077
 
2078
+ #### `useAsyncState(initialState)`
2079
+
2080
+ Hook like useState but accepts Promises and supports partial state extension.
2081
+
2082
+ ```typescript
2083
+ function useAsyncState<T>(initialState: T): [T, AsyncSetState<T>, AsyncExtendState<T>]
2084
+ ```
2085
+
2086
+ - `initialState` — Initial state value
2087
+
2088
+ **Returns:** Tuple of [state, asyncSetState, asyncExtendState]
2089
+
1984
2090
  #### `useAuthClient()`
1985
2091
 
1986
2092
  Hook to access the auth client from context.
@@ -2012,6 +2118,9 @@ Pass the app's user type — the SAME type used for the auth client and its
2012
2118
  config (`createJWTAuthClient<MyUser>`, `AuthClientConfig<MyUser>`) — so
2013
2119
  `user` and `setSession`/`setUser` are typed for the pages:
2014
2120
  `useAuth<MyUser>()`.
2121
+ Save profile edits with `updateProfile(patch)`: it uses the configured auth
2122
+ transport and publishes the saved user to every subscriber before resolving.
2123
+ An app-owned mutation instead passes its canonical response to `setUser`.
2015
2124
 
2016
2125
  For a sign-in the app runs itself (an OAuth redirect, a custom endpoint that
2017
2126
  already issued the token), call `setSession({ accessToken, user })`: it
@@ -2025,7 +2134,7 @@ client's `AuthResult<MyUser>`; there is no separate `useRegister` hook.
2025
2134
  An app whose API takes its OWN registration fields passes the client's
2026
2135
  second generic here too — `useAuth<MyUser, { username: string }>()` — so
2027
2136
  `register({ email, password, username })` is typed exactly like the client
2028
- call (and `requestBody` receives the same payload). Without it, `register`
2137
+ call (and `mapRequestBody` receives that payload as its first argument). Without it, `register`
2029
2138
  accepts only the base fields.
2030
2139
 
2031
2140
  ```typescript
@@ -2440,6 +2549,40 @@ function useOAuth(config?: {
2440
2549
 
2441
2550
  **Returns:** OAuth helpers: providers, getOAuthUrl, redirect, and loginViaPopup.
2442
2551
 
2552
+ #### `useOptimisticValue(options)`
2553
+
2554
+ A server-backed value — a favorite, follow, like or bookmark button, a rating, a quantity, a
2555
+ setting switch — that changes on the first render after a tap and is saved one change at a
2556
+ time, in order. Taps during a save update the desired value; the newest waiting value is
2557
+ saved after it (intermediate waiting values coalesce). Only a failure of the newest
2558
+ change puts the server value back. See `createOptimisticValue` in `@molecule/app-state`.
2559
+
2560
+ `value` is the server's value from props or a query. A new `value` is taken only when it
2561
+ differs (by `equals`) from the one the hook last received, and only while nothing is being
2562
+ saved, so a parent re-render or a refetch of older data never flips the button mid-save. The
2563
+ latest `commit` and `onError` are always used. Give the component a `key` per record, so one
2564
+ record's pending change never shows on another.
2565
+ Keep related fields, such as a favorite flag and its count, in the SAME value. Return both
2566
+ from the server response in `commit`, and publish that response to parent/query caches there
2567
+ if needed. Let commit reject on failure: a catch that returns old data hides the failure.
2568
+ Replace display-state setters and prop-copy effects with this hook's value and `update`.
2569
+ `pending` is for feedback, never a reason to disable the toggle or drop a tap.
2570
+
2571
+ Render/enable the keyed control only once its initial record exists. Keep that control
2572
+ mounted through refetches of the same record. This hook does not fix a click handler that
2573
+ returns on a still-null profile, or a remount that throws its controller away. Refetches
2574
+ during a save cannot replace pending intent; changed server snapshots arriving AFTER saving
2575
+ finishes are accepted and need query-layer ordering if they can be stale. See the module
2576
+ quickstart for a complete favorite button with a server-confirmed count.
2577
+
2578
+ ```typescript
2579
+ function useOptimisticValue<T>(options: OptimisticValueOptions<T>): UseOptimisticValueResult<T>
2580
+ ```
2581
+
2582
+ - `options` — The server's current value and how to save a change.
2583
+
2584
+ **Returns:** The value to render, whether a save is pending, the newest failure, and `set`/`update`.
2585
+
2443
2586
  #### `usePasswordReset()`
2444
2587
 
2445
2588
  Hook for password reset flow with async state tracking.
@@ -2508,6 +2651,34 @@ function useProfileForm<TUser, TValues extends FormValuesConstraint<TValues>>(
2508
2651
 
2509
2652
  **Returns:** Form state, typed control bindings, native submit binding and save error.
2510
2653
 
2654
+ #### `usePromise(asyncFn)`
2655
+
2656
+ Hook that wraps an async function with state tracking.
2657
+
2658
+ For one UI action, pass `state.status === 'pending'` to `Button loading`
2659
+ from `@molecule/app-ui-react` (or to a native control's `disabled` prop).
2660
+ The control then refuses input while the request is pending and re-enables
2661
+ when the tracked call settles. Handle the returned promise's rejection;
2662
+ the hook also records the failure in `state.error`.
2663
+
2664
+ This hook tracks the latest call: it does not serialize invocations or
2665
+ cancel their network requests. An explicit reset/cancel changes the tracked
2666
+ state, so it is not proof that an outstanding request has stopped. Use an
2667
+ app-owned queue when every click must be accepted in order.
2668
+
2669
+ ```typescript
2670
+ function usePromise<T extends (...args: any[]) => Promise<any>>(
2671
+ asyncFn: T,
2672
+ ): [
2673
+ UsePromiseState<Awaited<ReturnType<T>>>,
2674
+ (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>>,
2675
+ ]
2676
+ ```
2677
+
2678
+ - `asyncFn` — The async function to wrap
2679
+
2680
+ **Returns:** Tuple of [state, wrappedFunction]
2681
+
2511
2682
  #### `usePush(options)`
2512
2683
 
2513
2684
  Hook for push notification state and actions.
@@ -3111,7 +3282,7 @@ function MoleculeProvider({
3111
3282
  workspace,
3112
3283
  editor,
3113
3284
  preview,
3114
- awaitSession,
3285
+ awaitSession = false,
3115
3286
  sessionFallback,
3116
3287
  }: MoleculeProviderProps): React.ReactElement
3117
3288
  ```
@@ -3145,7 +3316,7 @@ client before rendering to include its decided content, or leave the hold disabl
3145
3316
  Route-level redirects for signed-in-only pages stay with `AuthGuard` (`@molecule/app-ui-react`).
3146
3317
 
3147
3318
  ```typescript
3148
- function SessionReady({ children, fallback }: SessionReadyProps): ReactNode
3319
+ function SessionReady({ children, fallback = null }: SessionReadyProps): ReactNode
3149
3320
  ```
3150
3321
 
3151
3322
  - `props` — {@link SessionReadyProps}.
@@ -3276,80 +3447,6 @@ Context for live preview provider.
3276
3447
  const PreviewContext: Context<PreviewProvider | null>
3277
3448
  ```
3278
3449
 
3279
- #### `useAsyncState`
3280
-
3281
- Hook like useState but accepts Promises and supports partial state extension.
3282
-
3283
- ```typescript
3284
- const useAsyncState: <T>(initialState: T) => [T, AsyncSetState<T>, AsyncExtendState<T>]
3285
- ```
3286
-
3287
- - `initialState` — Initial state value
3288
-
3289
- **Returns:** Tuple of [state, asyncSetState, asyncExtendState]
3290
-
3291
- #### `useOptimisticValue`
3292
-
3293
- A server-backed value — a favorite, follow, like or bookmark button, a rating, a quantity, a
3294
- setting switch — that changes on the first render after a tap and is saved one change at a
3295
- time, in order. Taps during a save update the desired value; the newest waiting value is
3296
- saved after it (intermediate waiting values coalesce). Only a failure of the newest
3297
- change puts the server value back. See `createOptimisticValue` in `@molecule/app-state`.
3298
-
3299
- `value` is the server's value from props or a query. A new `value` is taken only when it
3300
- differs (by `equals`) from the one the hook last received, and only while nothing is being
3301
- saved, so a parent re-render or a refetch of older data never flips the button mid-save. The
3302
- latest `commit` and `onError` are always used. Give the component a `key` per record, so one
3303
- record's pending change never shows on another.
3304
- Keep related fields, such as a favorite flag and its count, in the SAME value. Return both
3305
- from the server response in `commit`, and publish that response to parent/query caches there
3306
- if needed. Let commit reject on failure: a catch that returns old data hides the failure.
3307
- Replace display-state setters and prop-copy effects with this hook's value and `update`.
3308
- `pending` is for feedback, never a reason to disable the toggle or drop a tap.
3309
-
3310
- Render/enable the keyed control only once its initial record exists. Keep that control
3311
- mounted through refetches of the same record. This hook does not fix a click handler that
3312
- returns on a still-null profile, or a remount that throws its controller away. Refetches
3313
- during a save cannot replace pending intent; changed server snapshots arriving AFTER saving
3314
- finishes are accepted and need query-layer ordering if they can be stale. See the module
3315
- quickstart for a complete favorite button with a server-confirmed count.
3316
-
3317
- ```typescript
3318
- const useOptimisticValue: <T>(options: OptimisticValueOptions<T>) => UseOptimisticValueResult<T>
3319
- ```
3320
-
3321
- - `options` — The server's current value and how to save a change.
3322
-
3323
- **Returns:** The value to render, whether a save is pending, the newest failure, and `set`/`update`.
3324
-
3325
- #### `usePromise`
3326
-
3327
- Hook that wraps an async function with state tracking.
3328
-
3329
- For one UI action, pass `state.status === 'pending'` to `Button loading`
3330
- from `@molecule/app-ui-react` (or to a native control's `disabled` prop).
3331
- The control then refuses input while the request is pending and re-enables
3332
- when the tracked call settles. Handle the returned promise's rejection;
3333
- the hook also records the failure in `state.error`.
3334
-
3335
- This hook tracks the latest call: it does not serialize invocations or
3336
- cancel their network requests. An explicit reset/cancel changes the tracked
3337
- state, so it is not proof that an outstanding request has stopped. Use an
3338
- app-owned queue when every click must be accepted in order.
3339
-
3340
- ```typescript
3341
- const usePromise: <T extends (...args: any[]) => Promise<any>>(
3342
- asyncFn: T,
3343
- ) => [
3344
- UsePromiseState<Awaited<ReturnType<T>>>,
3345
- (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>>,
3346
- ]
3347
- ```
3348
-
3349
- - `asyncFn` — The async function to wrap
3350
-
3351
- **Returns:** Tuple of [state, wrappedFunction]
3352
-
3353
3450
  ## Injection Notes
3354
3451
 
3355
3452
  ### Requirements
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/app-react-native",
3
- "version": "3.0.5",
3
+ "version": "3.0.7",
4
4
  "description": "React Native framework bindings for molecule.dev",
5
5
  "homepage": "https://www.molecule.dev/packages/app-react-native",
6
6
  "type": "module",
@@ -33,7 +33,7 @@
33
33
  "directory": "packages/app/frameworks/react-native"
34
34
  },
35
35
  "devDependencies": {
36
- "@molecule/app-react": "3.4.1",
36
+ "@molecule/app-react": "3.5.0",
37
37
  "@types/node": "26.6.5",
38
38
  "react": "19.3.1",
39
39
  "typescript": "6.0.3",