@molecule/app-react-native 1.0.3 → 1.0.4

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 +187 -27
  2. package/package.json +5 -5
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-19T22:40:47.803Z
6
+ Generated: 2026-10-23T14:59:33.332Z
7
7
  -->
8
8
 
9
9
  # @molecule/app-react-native
@@ -105,7 +105,7 @@ Provides login/logout/register flows, token management, profile
105
105
  updates, and auth state subscription.
106
106
 
107
107
  ```typescript
108
- interface AuthClient<T = UserProfile, TRegister extends Record<string, unknown> = Record<never, never>> {
108
+ interface AuthClient<T = UserProfile, TRegister extends RegisterExtraShape<TRegister> = Record<never, never>> {
109
109
  /**
110
110
  * Returns the current authentication state snapshot.
111
111
  *
@@ -125,6 +125,15 @@ interface AuthClient<T = UserProfile, TRegister extends Record<string, unknown>
125
125
  * Gets the current user.
126
126
  */
127
127
  getUser(): T | null;
128
+ /**
129
+ * Optional ownership marker for the current session. Compare values only within
130
+ * this client: replacing or ending a session changes it, including re-adopting
131
+ * the same token and user. Profile updates and same-session refreshes keep it.
132
+ * Consumers can use it to discard stale async work without treating editable
133
+ * user fields as an immutable account identifier. Providers without this
134
+ * capability require the consumer's own stable identity check.
135
+ */
136
+ getSessionRevision?(): number;
128
137
  /**
129
138
  * Updates the cached user object (state + persistent storage) without
130
139
  * hitting the network. Intended for local refreshes after a per-app
@@ -199,6 +208,9 @@ interface AuthClient<T = UserProfile, TRegister extends Record<string, unknown>
199
208
  confirmPasswordReset(data: PasswordResetConfirm): Promise<void>;
200
209
  /**
201
210
  * Updates the current user's profile.
211
+ * Returns the saved response. If the session is explicitly replaced or ended
212
+ * while the request is pending, that response does not replace the current
213
+ * session's user or persisted cache.
202
214
  */
203
215
  updateProfile(data: Partial<T>): Promise<T>;
204
216
  /**
@@ -257,16 +269,6 @@ interface AuthClient<T = UserProfile, TRegister extends Record<string, unknown>
257
269
  * Resolves IMMEDIATELY when no restore is in flight: before `initialize()` is ever
258
270
  * called, and after it has finished. A restore that failed resolves too (it settled the
259
271
  * question), so an HTTP client can await this before every request without risking one
260
- * held forever. That is what makes the fetch-before-restore race impossible: a page that
261
- * fetches viewer-dependent data on mount waits here instead of going out anonymously
262
- * (see `setAuthGate` in `@molecule/app-http`).
263
- *
264
- * Optional: a provider that restores no session has nothing to wait for. Without it, an
265
- * HTTP client bonded to that provider sends first-paint requests unauthenticated.
266
- *
267
- * A provider whose restore sends a request THROUGH the gated HTTP client must send that
268
- * request with `skipAuthGate: true` (`RequestConfig` in `@molecule/app-http`): the gate
269
- * waits on this promise, so holding the restore's own request makes the gate wait on
270
272
  // … truncated — the full declaration is in node_modules/@molecule/app-react-native/dist
271
273
  ```
272
274
 
@@ -349,7 +351,13 @@ interface FormController<T extends Record<string, unknown> = Record<string, unkn
349
351
  */
350
352
  validate(): Promise<boolean>
351
353
  /**
352
- * Resets the form to initial values.
354
+ * Resets the form and clears errors, touched fields and submission state.
355
+ *
356
+ * With no argument it restores the current baseline — the initial
357
+ * `defaultValues`, or the values a previous `reset(values)` established.
358
+ * Passing `values` merges them over the current baseline and makes the
359
+ * result the new baseline, so a form reset to freshly loaded or saved data
360
+ * reads clean (`isDirty === false`).
353
361
  */
354
362
  reset(values?: Partial<T>): void
355
363
  /**
@@ -381,7 +389,8 @@ Form creation options.
381
389
  ```typescript
382
390
  interface FormOptions<T extends Record<string, unknown>> {
383
391
  /**
384
- * Default values.
392
+ * Default values, captured as the form's clean baseline when the controller
393
+ * is created. Mutating this object afterwards does not change the baseline.
385
394
  */
386
395
  defaultValues?: Partial<T>
387
396
  /**
@@ -786,12 +795,18 @@ interface UseDeviceResult {
786
795
 
787
796
  Options for useForm hook.
788
797
 
798
+ Every `FormOptions` field is supported. `createForm` is only an override —
799
+ omit it and the hook creates the controller through the bonded forms
800
+ provider (`createForm` from `@molecule/app-forms`), which uses the built-in
801
+ native provider until the app bonds its own.
802
+
789
803
  ```typescript
790
804
  interface UseFormOptions<T extends Record<string, unknown>> extends FormOptions<T> {
791
805
  /**
792
- * Form provider's createForm function.
806
+ * Explicit form factory for this one form. Defaults to the bonded forms
807
+ * provider; pass it only to pin a specific provider instance.
793
808
  */
794
- createForm: (options: FormOptions<T>) => FormController<T>
809
+ createForm?: (options: FormOptions<T>) => FormController<T>
795
810
  }
796
811
  ```
797
812
 
@@ -805,7 +820,12 @@ interface UseFormResult<T extends Record<string, unknown>> {
805
820
  isValid: boolean
806
821
  isDirty: boolean
807
822
  isSubmitting: boolean
808
- register: (name: keyof T, options?: RegisterOptions) => FieldRegistration
823
+ register: (
824
+ name: keyof T,
825
+ options?: Omit<RegisterOptions, 'name'> & {
826
+ name?: string
827
+ },
828
+ ) => FieldRegistration
809
829
  getValue: <K extends keyof T>(name: K) => T[K]
810
830
  setValue: <K extends keyof T>(name: K, value: T[K]) => void
811
831
  getError: (name: keyof T) => string | undefined
@@ -835,6 +855,13 @@ Result of useHttp hook.
835
855
 
836
856
  ```typescript
837
857
  interface UseHttpResult<T> extends UseHttpState<T> {
858
+ /**
859
+ * Runs the request and resolves with its data, or `null` on failure.
860
+ *
861
+ * This function is IDENTITY-STABLE for as long as `method`, `url` and the HTTP
862
+ * client do not change, so it is safe in a `useEffect` dependency array. Its
863
+ * request options are read when it is CALLED, from the latest committed render.
864
+ */
838
865
  execute: () => Promise<T | null>
839
866
  reset: () => void
840
867
  }
@@ -844,6 +871,11 @@ interface UseHttpResult<T> extends UseHttpState<T> {
844
871
 
845
872
  Options for useHttp hook.
846
873
 
874
+ Every request option — headers, params, body `data`, `signal`, `onSuccess`,
875
+ `onError` — is read at the moment `execute` runs, not when the hook rendered,
876
+ so an inline object or callback needs no memoizing. Only `immediate` is read
877
+ on mount.
878
+
847
879
  ```typescript
848
880
  interface UseHttpOptions<T> extends RequestConfig {
849
881
  /**
@@ -928,6 +960,80 @@ interface UsePlatformResult {
928
960
  }
929
961
  ```
930
962
 
963
+ #### `UseProfileFormOptions`
964
+
965
+ Configuration for a profile form; transport stays in the auth bond.
966
+
967
+ ```typescript
968
+ interface UseProfileFormOptions<TUser, TValues extends Record<string, unknown>> extends Omit<
969
+ UseFormOptions<TValues>,
970
+ 'defaultValues'
971
+ > {
972
+ /** Complete controlled-field defaults, including blank write-only fields. */
973
+ defaultValues: TValues
974
+ /**
975
+ * Optional additional account identity check; changing it discards prior edits.
976
+ * By default ownership follows the auth client's session revision, so editable
977
+ * fields can change during a save. Providers without getSessionRevision must
978
+ * supply this callback with an identity that remains stable across profile edits.
979
+ */
980
+ getIdentity?: (user: TUser) => string
981
+ /** Maps the current/saved user into controlled values, e.g. nullable text to ''. */
982
+ toValues: (user: TUser) => TValues
983
+ /**
984
+ * Maps ONLY changed fields into an API patch. Omit blank optional credentials
985
+ * here; preserve undefined/absent fields rather than turning them into null.
986
+ * The second argument is the complete value snapshot taken at submit time.
987
+ */
988
+ toPatch: (changes: Partial<TValues>, values: Readonly<TValues>) => Partial<TUser>
989
+ /**
990
+ * Propagates the server's saved user to other caches, then navigates if needed.
991
+ * Awaited before submission finishes. The auth client already persists and
992
+ * publishes the saved user; never seed caches from the submitted patch.
993
+ */
994
+ onSaved?: (user: TUser) => void | Promise<void>
995
+ }
996
+ ```
997
+
998
+ #### `UseProfileFormResult`
999
+
1000
+ Controlled profile form state and native form bindings.
1001
+
1002
+ ```typescript
1003
+ interface UseProfileFormResult<TUser, TValues extends Record<string, unknown>> extends Omit<
1004
+ UseFormResult<TValues>,
1005
+ 'register' | 'handleSubmit' | 'reset'
1006
+ > {
1007
+ /** The shared auth user's current profile. */
1008
+ user: TUser | null
1009
+ /** A user has been loaded; disable submission until this is true. */
1010
+ ready: boolean
1011
+ /**
1012
+ * The latest save/propagation failure. Rejected saves retain edits; a failed
1013
+ * onSaved callback can be retried by submit without repeating the mutation.
1014
+ */
1015
+ error: unknown
1016
+ /** Native input/textarea bindings with the field's concrete value type. */
1017
+ register: <K extends keyof TValues>(
1018
+ name: K,
1019
+ options?: Parameters<UseFormResult<TValues>['register']>[1],
1020
+ ) => Omit<FieldRegistration, 'value'> & {
1021
+ value: TValues[K]
1022
+ }
1023
+ /** Bind to the native form, with a type="submit" button inside it. */
1024
+ formProps: {
1025
+ onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
1026
+ }
1027
+ /**
1028
+ * Validates and saves one submit-time snapshot. Concurrent submits share the
1029
+ * same promise. Resolves undefined on validation, save or propagation failure.
1030
+ */
1031
+ submit: () => Promise<TUser | undefined>
1032
+ /** Discards edits and restores the newest loaded/saved profile baseline. */
1033
+ reset: () => void
1034
+ }
1035
+ ```
1036
+
931
1037
  #### `UsePromiseState`
932
1038
 
933
1039
  Extended promise state with actions.
@@ -980,11 +1086,14 @@ interface UsePushOptions {
980
1086
  Return type for useSignup hook.
981
1087
 
982
1088
  ```typescript
983
- interface UseSignupReturn<T = unknown> {
1089
+ interface UseSignupReturn<
1090
+ T = unknown,
1091
+ TRegister extends RegisterExtraShape<TRegister> = Record<never, never>,
1092
+ > {
984
1093
  status: UsePromiseState<AuthResult<T>>['status']
985
1094
  value: UsePromiseState<AuthResult<T>>['value']
986
1095
  error: UsePromiseState<AuthResult<T>>['error']
987
- signup: (data: RegisterData) => Promise<AuthResult<T>>
1096
+ signup: (data: RegisterData<TRegister>) => Promise<AuthResult<T>>
988
1097
  reset: () => void
989
1098
  }
990
1099
  ```
@@ -1271,7 +1380,10 @@ interface UseAuthOptions {
1271
1380
  Hook result for useAuth.
1272
1381
 
1273
1382
  ```typescript
1274
- interface UseAuthResult<T = unknown> {
1383
+ interface UseAuthResult<
1384
+ T = unknown,
1385
+ TRegister extends RegisterExtraShape<TRegister> = Record<never, never>,
1386
+ > {
1275
1387
  /**
1276
1388
  * The session state as one value — render from this instead of combining
1277
1389
  * `initialized`/`authenticated`/`loading`. A stored token AND user make it
@@ -1282,7 +1394,7 @@ interface UseAuthResult<T = unknown> {
1282
1394
  state: AuthState<T>
1283
1395
  login: AuthClient<T>['login']
1284
1396
  logout: AuthClient<T>['logout']
1285
- register: AuthClient<T>['register']
1397
+ register: AuthClient<T, TRegister>['register']
1286
1398
  refresh: AuthClient<T>['refresh']
1287
1399
  setUser: AuthClient<T>['setUser']
1288
1400
  /**
@@ -1394,6 +1506,8 @@ Options for {@link UseChatResult.sendMessage}.
1394
1506
 
1395
1507
  ```typescript
1396
1508
  interface SendMessageOptions {
1509
+ /** Reuse a queued message's UUID when delivering it after reconnecting. */
1510
+ messageId?: string
1397
1511
  /**
1398
1512
  * Skip the optimistic local user-message bubble. The text is still sent to
1399
1513
  * the server. Used for ask_user responses: the answer is folded into the
@@ -1774,8 +1888,12 @@ function useAIModels(projectId?: string): UseAIModelsResult
1774
1888
 
1775
1889
  Hook to access the auth client from context.
1776
1890
 
1891
+ Pass the same generics the client was created with —
1892
+ `useAuthClient<MyUser, { username: string }>()` — so `register()` accepts
1893
+ the app's own fields.
1894
+
1777
1895
  ```typescript
1778
- function useAuthClient(): AuthClient<T, Record<never, never>>
1896
+ function useAuthClient(): AuthClient<T, TRegister>
1779
1897
  ```
1780
1898
 
1781
1899
  **Returns:** The auth client from context
@@ -1798,8 +1916,14 @@ To create an account, use `const { register } = useAuth<MyUser>()` and
1798
1916
  `await register({ email, password, name })`. Registration returns the auth
1799
1917
  client's `AuthResult<MyUser>`; there is no separate `useRegister` hook.
1800
1918
 
1919
+ An app whose API takes its OWN registration fields passes the client's
1920
+ second generic here too — `useAuth<MyUser, { username: string }>()` — so
1921
+ `register({ email, password, username })` is typed exactly like the client
1922
+ call (and `requestBody` receives the same payload). Without it, `register`
1923
+ accepts only the base fields.
1924
+
1801
1925
  ```typescript
1802
- function useAuth(options?: UseAuthOptions): UseAuthResult<T>
1926
+ function useAuth(options?: UseAuthOptions): UseAuthResult<T, TRegister>
1803
1927
  ```
1804
1928
 
1805
1929
  - `options` — Hook options
@@ -1968,11 +2092,15 @@ function useEditor(): UseEditorResult
1968
2092
 
1969
2093
  Hook for form state management.
1970
2094
 
2095
+ Creates its controller through the bonded forms provider, so a React app
2096
+ wires a form library once (`setProvider` from `@molecule/app-forms`) and
2097
+ every form picks it up. Passing `createForm` overrides that for one form.
2098
+
1971
2099
  ```typescript
1972
- function useForm(options: UseFormOptions<T>): UseFormResult<T>
2100
+ function useForm(options?: UseFormOptions<T>): UseFormResult<T>
1973
2101
  ```
1974
2102
 
1975
- - `options` — Form options including createForm from a forms provider
2103
+ - `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.
1976
2104
 
1977
2105
  **Returns:** Form state and methods
1978
2106
 
@@ -2016,6 +2144,15 @@ function useHttpClient(): HttpClient
2016
2144
 
2017
2145
  Hook for making HTTP requests with state management.
2018
2146
 
2147
+ `execute` keeps one identity while `method`, `url` and the HTTP client stay the
2148
+ same — across the hook's own loading/data updates, parent rerenders, and inline
2149
+ options or callbacks — so `useEffect(() => { void execute() }, [execute])`
2150
+ runs once per request target instead of looping. The current headers, body,
2151
+ `signal` and callbacks are read when `execute` is called, so a later manual
2152
+ call always sends what the latest committed render passed. Changing `method`, `url` or
2153
+ the client does replace `execute` (it selects a different request) and
2154
+ re-runs such an effect.
2155
+
2019
2156
  ```typescript
2020
2157
  function useHttp(
2021
2158
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
@@ -2026,7 +2163,7 @@ function useHttp(
2026
2163
 
2027
2164
  - `method` — HTTP method
2028
2165
  - `url` — Request URL
2029
- - `options` — Request options
2166
+ - `options` — Request options, read at call time
2030
2167
 
2031
2168
  **Returns:** Request state and execute function
2032
2169
 
@@ -2231,6 +2368,29 @@ function usePreview(): UsePreviewResult
2231
2368
 
2232
2369
  **Returns:** Preview state and controls: state (url, isLoading, device, error, isConnected), setUrl, refresh, setDevice, and openExternal.
2233
2370
 
2371
+ #### `useProfileForm(options)`
2372
+
2373
+ Owns load → edit → submit → publish for the signed-in user's profile.
2374
+
2375
+ Same-account background updates refresh untouched fields but preserve dirty
2376
+ controls. Session ownership comes from the auth client, independently of mutable
2377
+ profile fields; an explicit getIdentity adds the caller's identity check.
2378
+ Submission snapshots the registered values before asynchronous
2379
+ validation, sends only changed fields through `toPatch`, and uses the auth
2380
+ client's configured updateProfile transport. The saved server response becomes
2381
+ the clean baseline; edits made while saving remain dirty. No request is made
2382
+ for a patch with no fields. A failed request retains the edits and shared user.
2383
+
2384
+ ```typescript
2385
+ function useProfileForm(
2386
+ options: UseProfileFormOptions<TUser, TValues>,
2387
+ ): UseProfileFormResult<TUser, TValues>
2388
+ ```
2389
+
2390
+ - `options` — Controlled defaults, identity and wire adapters, optional cache/navigation callback.
2391
+
2392
+ **Returns:** Form state, typed control bindings, native submit binding and save error.
2393
+
2234
2394
  #### `usePush(options)`
2235
2395
 
2236
2396
  Hook for push notification state and actions.
@@ -2323,7 +2483,7 @@ function useIsActive(path: string, exact?: boolean): boolean
2323
2483
  Hook for user registration with async state tracking.
2324
2484
 
2325
2485
  ```typescript
2326
- function useSignup(): UseSignupReturn<T>
2486
+ function useSignup(): UseSignupReturn<T, TRegister>
2327
2487
  ```
2328
2488
 
2329
2489
  **Returns:** Signup state and action
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/app-react-native",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
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,11 +33,11 @@
33
33
  "directory": "packages/app/frameworks/react-native"
34
34
  },
35
35
  "devDependencies": {
36
- "@molecule/app-react": "1.6.7",
37
- "@types/node": "26.1.2",
38
- "react": "19.2.8",
36
+ "@molecule/app-react": "1.10.1",
37
+ "@types/node": "26.6.5",
38
+ "react": "19.3.1",
39
39
  "typescript": "6.0.3",
40
- "vitest": "4.1.11"
40
+ "vitest": "4.1.12"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@molecule/app-keyboard": "^1.0.1",