@molecule/app-auth 3.2.3 → 3.2.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 +116 -303
  2. package/package.json +1 -1
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-03T17:06:00.004Z
6
+ Generated: 2026-11-04T14:20:03.859Z
7
7
  -->
8
8
 
9
9
  # @molecule/app-auth
@@ -706,189 +706,85 @@ interface AuthClient<
706
706
  T = UserProfile,
707
707
  TRegister extends RegisterExtraShape<TRegister> = Record<never, never>,
708
708
  > {
709
- /**
710
- * Returns the current authentication state snapshot.
711
- *
712
- * READING the state starts the client's one session restore when nobody has called
713
- * {@link initialize} yet — lazily and exactly once, sharing the promise an explicit
714
- * `initialize()` returns. A hook (`useAuth()`), a subscriber, `isAuthenticated()` and
715
- * `getUser()` are reads too, so a client whose entry file dropped the template's
716
- * `authClient.initialize()` line still restores instead of reporting `'loading'`
717
- * forever.
718
- */
709
+ /** Returns the current authentication state snapshot. */
719
710
  getState(): AuthState<T>
720
711
 
721
- /**
722
- * Returns whether the user is currently authenticated.
723
- */
712
+ /** Returns whether the user is currently authenticated. */
724
713
  isAuthenticated(): boolean
725
714
 
726
- /**
727
- * Gets the current user.
728
- */
715
+ /** Gets the current user. */
729
716
  getUser(): T | null
730
717
 
731
- /**
732
- * Optional ownership marker for the current session. Compare values only within
733
- * this client: replacing or ending a session changes it, including re-adopting
734
- * the same token and user. Profile updates and same-session refreshes keep it.
735
- * Consumers can use it to discard stale async work without treating editable
736
- * user fields as an immutable account identifier. Providers without this
737
- * capability require the consumer's own stable identity check.
738
- */
718
+ /** Optional ownership marker for the current session. */
739
719
  getSessionRevision?(): number
740
720
 
741
- /**
742
- * Updates the cached user object (state + persistent storage) without
743
- * hitting the network. Intended for local refreshes after a per-app
744
- * mutation (e.g., the user just PATCHed their own profile and the
745
- * server returned the canonical row). A non-null profile does NOT change
746
- * tokens or sign the client in: `authenticated` and `status` are unchanged,
747
- * so an anonymous client stays anonymous. Use {@link login} /
748
- * {@link register} for a credential exchange, or {@link setSession} when
749
- * the token was issued outside the client.
750
- *
751
- * Passing `null` on a signed-in client, or while any session operation is
752
- * pending, ends the local session: it clears the access token, refresh token
753
- * and cached user, stops automatic refresh, and settles `authenticated: false`
754
- * with `status: 'unauthenticated'` in one notification. Every older operation
755
- * loses session ownership, and a persistent sign-out marker prevents a reload
756
- * from restoring its credentials or a late cookie. A late credential exchange
757
- * is revoked best-effort when a logout endpoint is configured, without blocking
758
- * the caller. This method does not otherwise call the server for a settled
759
- * session; use {@link logout} for explicit server revocation. On a settled
760
- * anonymous client, `null` only clears the cached profile and leaves tokens
761
- * and auth state unchanged.
762
- */
721
+ /** Updates the cached user object (state + persistent storage) without hitting the network. */
763
722
  setUser(user: T | null): void
764
723
 
765
- /**
766
- * Gets the current access token.
767
- */
724
+ /** Gets the current access token. */
768
725
  getAccessToken(): string | null
769
726
 
770
- /**
771
- * Stores the access token in the configured token storage adapter (in a
772
- * browser, Web Storage; `'memory'` where no browser store exists). Use this
773
- * to seed the token after an out-of-band exchange (e.g. the OAuth
774
- * code→token redirect) instead of writing to `localStorage` directly,
775
- * which would violate the storage contract and make a stored bearer token
776
- * JS-readable (XSS-exfiltratable). Pass `null` to clear it.
777
- *
778
- * This changes token storage only: passing `null` does not by itself sign a
779
- * signed-in client out, and a non-null token does not sign an anonymous
780
- * client in (`authenticated` and `status` are unchanged). Use {@link logout}
781
- * to end a session, or {@link setUser} with `null` for local-only sign-out.
782
- * Use {@link login} / {@link register} for a credential exchange, or call
783
- * {@link setSession} with the token and user to establish an already-issued
784
- * session.
785
- */
727
+ /** Stores the access token in the configured token storage adapter (in a browser, Web Storage; `'memory'` where no browser store exists). */
786
728
  setAccessToken(token: string | null): void
787
729
 
788
- /**
789
- * Adopts a session an out-of-band exchange already established: stores the
790
- * access token (and the refresh token when given), caches the user, and
791
- * sets the COMPLETE state — `initialized`, `authenticated`, `status:
792
- * 'authenticated'`, no error — in one update, with one subscriber
793
- * notification. Every hook reading the shared client sees the user as
794
- * signed in immediately.
795
- *
796
- * This is the way to seed an OAuth/custom sign-in. Calling
797
- * {@link setAccessToken} and {@link setUser} instead does NOT sign the
798
- * client in.
799
- */
730
+ /** 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… */
800
731
  setSession(session: AuthSessionSeed<T>): void
801
732
 
802
- /**
803
- * Gets the refresh token.
804
- */
733
+ /** Gets the refresh token. */
805
734
  getRefreshToken(): string | null
806
735
 
807
- /**
808
- * Logs in with credentials.
809
- */
736
+ /** Logs in with credentials. */
810
737
  login(credentials: LoginCredentials): Promise<AuthResult<T>>
811
738
 
812
- /**
813
- * Logs out the current user. The server request is best-effort: a missing
814
- * logout route (404), any other HTTP failure, or a network error never makes
815
- * this method reject. It always clears the access token, refresh token and
816
- * cached user, stops automatic refresh, and publishes one settled
817
- * `authenticated: false`, `status: 'unauthenticated'` transition. Every pending
818
- * session operation loses ownership; a persistent marker prevents reload from
819
- * restoring an older session, and late credentials are revoked best-effort.
820
- *
821
- * When the API has no logout route, set {@link AuthClientConfigOptions.logoutEndpoint}
822
- * to `null` (or `''`) to skip the request. Local sign-out still happens.
823
- */
739
+ /** Logs out the current user. */
824
740
  logout(): Promise<void>
825
741
 
826
- /**
827
- * Registers a new user. Accepts the base fields plus the app's own fields
828
- * (`TRegister`) at the top level — see {@link RegisterData}.
829
- */
742
+ /** Registers a new user. */
830
743
  register(data: RegisterData<TRegister>): Promise<AuthResult<T>>
831
744
 
832
- /**
833
- * Refreshes the access token.
834
- */
745
+ /** Refreshes the access token. */
835
746
  refresh(): Promise<AuthResult<T>>
836
747
 
837
- /**
838
- * Requests a password reset.
839
- */
748
+ /** Requests a password reset. */
840
749
  requestPasswordReset(data: PasswordResetRequest): Promise<void>
841
750
 
842
- /**
843
- * Confirms a password reset.
844
- */
751
+ /** Confirms a password reset. */
845
752
  confirmPasswordReset(data: PasswordResetConfirm): Promise<void>
846
753
 
847
- /**
848
- * Updates the current user's profile.
849
- * Returns the saved profile: the answer applied onto the signed-in user field
850
- * by field, with or without an `id` — a field it carries replaces the value
851
- * (`null` clears it), one it omits or leaves `undefined` is kept, so a field
852
- * cleared by omission must arrive as `null`. A non-object answer is returned
853
- * as it came. An answer for a replaced or ended session leaves the current one
854
- * untouched.
855
- */
754
+ /** Updates the current user's profile. */
856
755
  updateProfile(data: Partial<T>): Promise<T>
857
756
 
858
- /**
859
- * Changes the current user's password.
860
- */
757
+ /** Changes the current user's password. */
861
758
  changePassword(oldPassword: string, newPassword: string): Promise<void>
862
759
 
863
- /**
864
- * Initializes auth state (e.g., from stored tokens).
865
- *
866
- * LAZY and IDEMPOTENT: the first read of the state ({@link getState},
867
- * {@link subscribe}, `useAuth()`) starts this same restore when nobody has called
868
- * `initialize()`, and every later call shares the one promise instead of sending a
869
- * second request. An app that keeps the template's explicit call behaves exactly as
870
- * before; an app that loses that line still restores its session.
871
- *
872
- * With a stored token AND a stored user it marks the session `'authenticated'`
873
- * SYNCHRONOUSLY — before its first `await` — so the first render is the
874
- * signed-in UI and no page waits on a verification round trip. Any check
875
- * (a refresh, the cookie-restore probe) settles behind that render, and only an
876
- * explicit 401/403 ends the session; a network/5xx/404 failure keeps it and
877
- * reports `'unavailable'`. With no stored user the client stays `'loading'`
878
- * until the cookie restore decides.
879
- *
880
- * Do not put this call (or a `/me` request of your own) in front of the first
881
- * render, and do not render a loading screen over a session it just restored:
882
- * `whenRestored()` already resolves as soon as the restore has settled, and the
883
- * HTTP gate built on it holds requests for exactly that window.
884
- */
760
+ /** Initializes auth state (e.g., from stored tokens). */
885
761
  initialize(): Promise<void>
886
762
 
887
- /**
888
- * Re-checks a restored session against the server, in the background — the
889
- // … truncated — the full declaration is in node_modules/@molecule/app-auth/dist/types.d.ts
763
+ /** 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… */
764
+ validateSession?(): Promise<void>
765
+
766
+ /** Resolves when the session restore started by `initialize()` has settled — the point after which the client knows whether this visitor is signed in. */
767
+ whenRestored?(): Promise<void>
768
+
769
+ /** Subscribes to auth state changes. */
770
+ subscribe(callback: (state: AuthState<T>) => void): () => void
771
+
772
+ /** Subscribes to auth state changes (alias for subscribe). */
773
+ onAuthChange(callback: (state: AuthState<T>) => void): () => void
774
+
775
+ /** Gets the current access token (alias for getAccessToken). */
776
+ getToken?(): string | null
777
+
778
+ /** Adds an auth event listener. */
779
+ addEventListener(listener: AuthEventListener<T>): () => void
780
+
781
+ /** Destroys the auth client. */
782
+ destroy(): void
783
+ }
890
784
  ```
891
785
 
786
+ _(Member comments are shortened to their first sentence to fit; the full declaration is in `node_modules/@molecule/app-auth/dist/types.d.ts`.)_
787
+
892
788
  #### `StorageAdapter`
893
789
 
894
790
  Minimal key-value storage adapter used by the auth client for
@@ -909,43 +805,24 @@ Every option {@link AuthClientConfig} accepts, typed by the profile.
909
805
  Use `AuthClientConfig<T>` rather than this type: it adds the profile-shape
910
806
  check. This interface exists so each member's documentation is addressable.
911
807
 
912
- ````typescript
808
+ ```typescript
913
809
  interface AuthClientConfigOptions<T extends object = UserProfile> {
914
- /**
915
- * API base URL.
916
- */
810
+ /** API base URL. */
917
811
  baseURL?: string
918
812
 
919
- /**
920
- * Login endpoint.
921
- */
813
+ /** Login endpoint. */
922
814
  loginEndpoint?: string
923
815
 
924
- /**
925
- * Logout endpoint. Defaults to `/users/logout`. Set to `null` (or `''`) when
926
- * the API has no logout route: {@link AuthClient.logout} skips the request
927
- * and still clears the local session.
928
- */
816
+ /** Logout endpoint. */
929
817
  logoutEndpoint?: string | null
930
818
 
931
- /**
932
- * Register endpoint.
933
- */
819
+ /** Register endpoint. */
934
820
  registerEndpoint?: string
935
821
 
936
- /**
937
- * Refresh endpoint.
938
- */
822
+ /** Refresh endpoint. */
939
823
  refreshEndpoint?: string
940
824
 
941
- /**
942
- * The write route used by `updateProfile`, independently of `currentUserEndpoint`.
943
- * Defaults to `/auth/profile`. A resource route with an account id can resolve
944
- * from the signed-in user, e.g. `(user) => '/accounts/' + encodeURIComponent(user.id)`.
945
- * A profile with no `id` resolves from a field it does have, e.g.
946
- * `(user) => '/people/' + encodeURIComponent(user.username)`.
947
- * The resolver sees the current user before the patch, never caller-submitted identity.
948
- */
825
+ /** The write route used by `updateProfile`, independently of `currentUserEndpoint`. */
949
826
  profileEndpoint?: string | ((user: Readonly<T>) => string)
950
827
 
951
828
  /** HTTP method for `updateProfile()` (default `'PATCH'`; `'PUT'` for a `PUT /api/user` route). */
@@ -954,149 +831,63 @@ interface AuthClientConfigOptions<T extends object = UserProfile> {
954
831
  /** Request-body adapter for `updateProfile()` — default the `Partial<T>` as given, e.g. `(data) => ({ user: data })`; see the module docs. */
955
832
  profileRequestBody?: (data: Partial<T>) => unknown
956
833
 
957
- /**
958
- * The API route that returns the signed-in user (default `/users/me`). Called
959
- * with credentials by `initialize()` to re-establish the session after a full
960
- * page load when the token did not survive the reload itself — i.e. when
961
- * `storage` is `'memory'` and the API sets an httpOnly session cookie — and by
962
- * {@link AuthClient.validateSession} on every reload to confirm a stored token
963
- * is still valid. Must return the user (as `user` or `props`).
964
- *
965
- * Set it whenever the app serves its own current-user route: a wrong value
966
- * answers 404, which `validateSession` reports as `'unavailable'` and keeps the
967
- * stored token instead of signing the user out — but the session then stays
968
- * unconfirmed.
969
- */
834
+ /** The API route that returns the signed-in user (default `/users/me`). */
970
835
  currentUserEndpoint?: string
971
836
 
972
- /**
973
- * When to probe {@link currentUserEndpoint} for an HttpOnly cookie session.
974
- * The default `'hint'` probes only when the readable `mol_auth=1` cookie is
975
- * present or this client already holds a cookie-session candidate. The API
976
- * must set that presence hint alongside its HttpOnly cookie for automatic
977
- * restoration after a full page load.
978
- *
979
- * Choose `'always'` for an external cookie API without that hint. Pair it with
980
- * `storage: 'memory'` and the API's current-user endpoint; the request includes
981
- * credentials. An anonymous load then makes a probe too. A local sign-out
982
- * suppresses probes until a successful sign-in establishes another session.
983
- * Network/5xx/404 failures leave a retryable candidate; 401/403 ends it.
984
- */
837
+ /** When to probe {@link currentUserEndpoint} for an HttpOnly cookie session. */
985
838
  cookieRestore?: 'hint' | 'always'
986
839
 
987
- /**
988
- * Maximum duration of an auth request, including reading its response body, in
989
- * milliseconds (default `10000`). Must be finite, positive, and below `2147483648`.
990
- * A restore timeout settles as `'unavailable'` and preserves the session for retry,
991
- * so initial-session render holds cannot wait forever on an unresponsive server.
992
- */
840
+ /** Maximum duration of an auth request, including reading its response body, in milliseconds (default `10000`). */
993
841
  requestTimeout?: number
994
842
 
995
- /**
996
- * Storage key prefix.
997
- */
843
+ /** Storage key prefix. */
998
844
  storagePrefix?: string
999
845
 
1000
- /**
1001
- * The EXACT `StorageAdapter` key the access token is written under (default
1002
- * `${storagePrefix}accessToken`).
1003
- *
1004
- * Set it when something outside this package reads the token at its own key —
1005
- * an e2e suite's contract (for example, `localStorage.getItem('app.jwt')`), a
1006
- * second library, a host page — instead of mirroring the token into
1007
- * `localStorage` beside the client. The refresh token and the cached user keep
1008
- * the prefix.
1009
- *
1010
- * In a browser the client's storage default is {@link createBrowserStorageAdapter},
1011
- * so `tokenKey` alone writes the token to exactly that key and a reload keeps
1012
- * the session. An EXPLICIT `storage: 'memory'` beside it warns at construction:
1013
- * the value then lives in a process-local Map nothing outside this client can
1014
- * read, and a reload signs the user out.
1015
- */
846
+ /** The EXACT `StorageAdapter` key the access token is written under (default `${storagePrefix}accessToken`). */
1016
847
  tokenKey?: string
1017
848
 
1018
- /**
1019
- * Scheme of the `Authorization` header the access token is sent with:
1020
- * `Bearer` (default), or e.g. `Token` for an API that reads
1021
- * `Authorization: Token <jwt>`.
1022
- */
849
+ /** Scheme of the `Authorization` header the access token is sent with: `Bearer` (default), or e.g. `Token` for an API that reads `Authorization: Token <jwt>`. */
1023
850
  authScheme?: string
1024
851
 
1025
- /**
1026
- * Shape the login/register request body for an API that expects another
1027
- * envelope (default: the payload as given). `register()` hands this callback
1028
- * exactly what the caller passed — including the app's own fields, typed
1029
- * through `createJWTAuthClient<UserProfile, { username: string }>` — so the
1030
- * body can be sent as-is under the envelope the API wants.
1031
- *
1032
- * A nested-account API, for example, nests the payload under `user` and registers
1033
- * with `{ user: { username, email, password } }`:
1034
- *
1035
- * ```typescript
1036
- * const client = createJWTAuthClient<UserProfile, { username: string }>({
1037
- * requestBody: (kind, payload) => ({ user: payload }),
1038
- * // …
1039
- * })
1040
- * await client.register({ username, email, password })
1041
- * ```
1042
- *
1043
- * A caller that keeps the untyped payload can map the base fields instead:
1044
- * `(kind, p) => ({ user: { username: p.name, email: p.email, password: p.password } })`
1045
- * with `register({ email, password, name: username })`.
1046
- *
1047
- * @param kind - Which request.
1048
- * @param payload - The credentials or registration data.
1049
- * @returns The JSON body to send.
1050
- */
852
+ /** Shape the login/register request body for an API that expects another envelope (default: the payload as given). */
1051
853
  requestBody?: (kind: 'login' | 'register', payload: Record<string, unknown>) => unknown
1052
854
 
1053
- /**
1054
- * Turn an answer that carries a user into the client's
1055
- * `{ accessToken, refreshToken?, user }` (default: the answer as given).
1056
- * For a nested user answer, keep the wire bearer OUT of the profile, because
1057
- * the client caches `user` in Web Storage where a token copy is readable by
1058
- * any script on the origin:
1059
- *
1060
- * ```typescript
1061
- * (data) => {
1062
- * const { token, ...user } = data.user
1063
- * return { accessToken: token, user }
1064
- * }
1065
- * ```
1066
- *
1067
- * It is the ONE user-shape adapter, and the client applies it on EVERY path
1068
- * that adopts a user from an API answer — `login()`, `register()`,
1069
- * `validateSession()`, the cookie restore `initialize()`/`refresh()` run, the
1070
- * token-refresh answer, and `updateProfile()`. Those answers carry the raw wire
1071
- * user (a current-user route typically answers a bare `{ user }`), so applying
1072
- * the same mapping is what keeps the adapted profile after a reload instead of
1073
- * replacing it with the unadapted one. A mapper that
1074
- * cannot read a given answer (it throws, or returns no `user`) leaves that
1075
- * answer's user as it came.
1076
- *
1077
- * It is NOT applied to `setSession()` or `setUser()`: those receive a profile
1078
- * the caller already built in this client's shape, not a wire answer.
1079
- *
1080
- * The return type does NOT infer the profile: an untyped
1081
- * `createJWTAuthClient({ mapAuthResponse })` stays `UserProfile`, so a mapper
1082
- * that returns a no-id profile fails to compile there instead of silently
1083
- * replacing the default with whatever the mapper happened to return. The same
1084
- * rule holds for every config member below (`profileEndpoint`,
1085
- * `profileRequestBody`, `shouldRestoreUser`): none of them supplies the
1086
- * profile. Name the profile at the call
1087
- * (`createJWTAuthClient<MyUser>({ mapAuthResponse })`) to use a custom one.
1088
- *
1089
- * @param data - The parsed response body.
1090
- * @returns The auth result.
1091
- */
855
+ /** Turn an answer that carries a user into the client's `{ accessToken, refreshToken?, user }` (default: the answer as given). */
1092
856
  mapAuthResponse?: (data: unknown) => AuthResult<NoInfer<T>> | AuthChallenge
1093
857
 
1094
- /**
1095
- * Where the access/refresh token and the cached user are kept — the field that
1096
- * decides whether a page reload keeps the user signed in.
1097
- *
1098
- // … truncated — the full declaration is in node_modules/@molecule/app-auth/dist/types.d.ts
1099
- ````
858
+ /** Where the access/refresh token and the cached user are kept — the field that decides whether a page reload keeps the user signed in. **In a browser the default… */
859
+ storage?: 'memory' | StorageAdapter
860
+
861
+ /** Decide whether a user returned by the cookie-restore probe (`currentUserEndpoint`) constitutes a signed-in CLIENT session. */
862
+ shouldRestoreUser?: (user: T) => boolean
863
+
864
+ /** Refresh the access token before it expires. */
865
+ autoRefresh?: boolean
866
+
867
+ /** Refresh tokens this many seconds before expiry. */
868
+ refreshBuffer?: number
869
+
870
+ /** Forgot password / password reset request endpoint. */
871
+ forgotPasswordEndpoint?: string
872
+
873
+ /** Password reset confirmation endpoint. */
874
+ resetPasswordEndpoint?: string
875
+
876
+ /** Change password endpoint. */
877
+ changePasswordEndpoint?: string | ((user: Readonly<T>) => string)
878
+
879
+ /** HTTP method for `changePassword()` (default `'POST'`). */
880
+ changePasswordMethod?: 'POST' | 'PATCH'
881
+
882
+ /** Available OAuth providers (e.g., ['github', 'google', 'gitlab']). */
883
+ oauthProviders?: string[]
884
+
885
+ /** OAuth endpoint path (default: '/oauth'). */
886
+ oauthEndpoint?: string
887
+ }
888
+ ```
889
+
890
+ _(Member comments are shortened to their first sentence to fit; the full declaration is in `node_modules/@molecule/app-auth/dist/types.d.ts`.)_
1100
891
 
1101
892
  #### `TokenStorage`
1102
893
 
@@ -1385,8 +1176,30 @@ rejected; see {@link AuthProfileShape} for the persistence contract behind
1385
1176
  that list.
1386
1177
 
1387
1178
  The type argument is NEVER inferred from the config: not from
1388
-
1389
- _(description truncated — read the full text in `node_modules/@molecule/app-auth/dist/types.d.ts`.)_
1179
+ `mapAuthResponse`, `profileEndpoint`, `profileRequestBody` or
1180
+ `shouldRestoreUser`, and not from a config variable the caller annotated as
1181
+ `AuthClientConfig<MyUser>`. An untyped call stays typed `UserProfile`, so the
1182
+ default configuration keeps requiring `id: string`. Name the profile at the
1183
+ call (`createJWTAuthClient<MyUser>(config)`) — which also checks this config
1184
+ against `MyUser`.
1185
+
1186
+ **Callers upgrading from 1.8 — three shapes now need the profile at the call.**
1187
+ Each was accepted by 1.8 and is a type error here; each has a one-line fix:
1188
+
1189
+ 1. A mapper that supplied the profile
1190
+ (`createJWTAuthClient({ mapAuthResponse: (d): AuthResult<MyUser> => … })`)
1191
+ is now typed `UserProfile`. Name it on the factory:
1192
+ `createJWTAuthClient<MyUser>({ mapAuthResponse })`.
1193
+ 2. A config variable annotated `AuthClientConfig<MyUser>` with a profile that
1194
+ extends `UserProfile` used to supply the type argument. Pass it:
1195
+ `createJWTAuthClient<MyUser>(config)`. (A no-id profile was already
1196
+ rejected by 1.8's `T extends UserProfile`.)
1197
+ 3. Generic forwarding written `function make<T extends UserProfile>(config: AuthClientConfig<T>)`
1198
+ no longer satisfies the factory's constraint. Copy the named constraint:
1199
+ `function make<T extends AuthProfileConstraint<T>>(config: AuthClientConfig<T>)`.
1200
+
1201
+ `any` is accepted at every entrypoint (`createJWTAuthClient<any>()`,
1202
+ `getClient<any>()`), so a caller that deliberately opted out keeps compiling.
1390
1203
 
1391
1204
  ```typescript
1392
1205
  type AuthClientConfig<T extends object = UserProfile> = AuthClientConfigOptions<T> &
@@ -1426,10 +1239,10 @@ reload out of the box; each of these replaces that default:
1426
1239
 
1427
1240
  ```ts
1428
1241
  storage: 'memory' // memory only: pair it with an httpOnly cookie session
1242
+ storage: createBrowserStorageAdapter({ kind: 'session' }) // survives a reload, dies with the tab
1243
+ storage: { getItem: (k) => …, setItem: (k, v) => …, removeItem: (k) => … } // your own
1429
1244
  ```
1430
1245
 
1431
- _(description truncated — read the full text in `node_modules/@molecule/app-auth/dist/browser-storage.d.ts`.)_
1432
-
1433
1246
  ```typescript
1434
1247
  function createBrowserStorageAdapter(options?: BrowserStorageAdapterOptions): StorageAdapter
1435
1248
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/app-auth",
3
- "version": "3.2.3",
3
+ "version": "3.2.4",
4
4
  "description": "Client-side authentication interface for molecule.dev",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",