@take-out/better-auth-utils 0.0.28

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 (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/dist/cjs/createAuthClient.cjs +172 -0
  4. package/dist/cjs/createAuthClient.js +153 -0
  5. package/dist/cjs/createAuthClient.js.map +6 -0
  6. package/dist/cjs/createAuthClient.native.js +161 -0
  7. package/dist/cjs/createAuthClient.native.js.map +6 -0
  8. package/dist/cjs/index.cjs +18 -0
  9. package/dist/cjs/index.js +15 -0
  10. package/dist/cjs/index.js.map +6 -0
  11. package/dist/cjs/index.native.js +20 -0
  12. package/dist/cjs/index.native.js.map +6 -0
  13. package/dist/esm/createAuthClient.js +143 -0
  14. package/dist/esm/createAuthClient.js.map +6 -0
  15. package/dist/esm/createAuthClient.mjs +149 -0
  16. package/dist/esm/createAuthClient.mjs.map +1 -0
  17. package/dist/esm/createAuthClient.native.js +164 -0
  18. package/dist/esm/createAuthClient.native.js.map +1 -0
  19. package/dist/esm/index.js +2 -0
  20. package/dist/esm/index.js.map +6 -0
  21. package/dist/esm/index.mjs +2 -0
  22. package/dist/esm/index.mjs.map +1 -0
  23. package/dist/esm/index.native.js +2 -0
  24. package/dist/esm/index.native.js.map +1 -0
  25. package/package.json +64 -0
  26. package/src/createAuthClient.ts +324 -0
  27. package/src/index.ts +1 -0
  28. package/types/client.d.ts +10 -0
  29. package/types/client.d.ts.map +11 -0
  30. package/types/createAuthClient.d.ts +81 -0
  31. package/types/createAuthClient.d.ts.map +18 -0
  32. package/types/hooks/useAuthSession.d.ts +8 -0
  33. package/types/hooks/useAuthSession.d.ts.map +13 -0
  34. package/types/hooks/useAuthState.d.ts +7 -0
  35. package/types/hooks/useAuthState.d.ts.map +13 -0
  36. package/types/hooks/useAuthUser.d.ts +8 -0
  37. package/types/hooks/useAuthUser.d.ts.map +13 -0
  38. package/types/index.d.ts +3 -0
  39. package/types/index.d.ts.map +11 -0
@@ -0,0 +1,324 @@
1
+ /**
2
+ * Better-auth helpers for React / React Native applications
3
+ *
4
+ * Features:
5
+ * - JWT support (for Zero, Tauri, React Native)
6
+ * - Session persistence in local storage
7
+ * - Token validation and refresh
8
+ * - State management with emitters
9
+ * - Automatic retry on errors
10
+ */
11
+
12
+ import {
13
+ createEmitter,
14
+ createLocalStorageValue,
15
+ type Emitter,
16
+ isEqualDeepLite,
17
+ useEmitterValue,
18
+ } from '@take-out/helpers'
19
+ import type { Session, User } from 'better-auth'
20
+ import { type BetterAuthClientOptions, createAuthClient } from 'better-auth/client'
21
+
22
+ export interface StorageKeys {
23
+ token: string
24
+ session: string
25
+ }
26
+
27
+ export type AuthState<U extends User = User> = {
28
+ state: 'loading' | 'logged-in' | 'logged-out'
29
+ session: Session | null
30
+ user: U | null
31
+ token: string | null
32
+ }
33
+
34
+ export interface BetterAuthClientProps<TUser extends User = User>
35
+ extends BetterAuthClientOptions {
36
+ /**
37
+ * Callback to transform and type the user object
38
+ * This allows you to add app-specific fields and ensure proper typing
39
+ * @default (user) => user
40
+ */
41
+ createUser?: (user: User) => TUser
42
+ /**
43
+ * Optional callback when authentication state changes
44
+ */
45
+ onAuthStateChange?: (state: AuthState<TUser>) => void
46
+ /**
47
+ * Optional callback for handling auth errors
48
+ */
49
+ onAuthError?: (error: any) => void
50
+ /**
51
+ * Storage key prefix for local storage
52
+ * @default 'auth'
53
+ */
54
+ storagePrefix?: string
55
+ /**
56
+ * Retry delay in milliseconds after auth errors
57
+ * @default 4000
58
+ */
59
+ retryDelay?: number
60
+ /**
61
+ * Custom token validation endpoint
62
+ * @default '/api/auth/validateToken'
63
+ */
64
+ tokenValidationEndpoint?: string
65
+ }
66
+
67
+ export interface BetterAuthClientReturn<U extends User = User, TClient = any> {
68
+ clearState: () => void
69
+ authState: ReturnType<typeof createEmitter<AuthState<U>>>
70
+ authClient: TClient
71
+ setAuthClientToken: (props: { token: string; session: string }) => Promise<void>
72
+ clearAuthClientToken: () => void
73
+ useAuth: () => AuthState<U>
74
+ getAuth: () => AuthState<U> & { loggedIn: boolean }
75
+ getValidToken: () => Promise<string | undefined>
76
+ updateAuthClient: (session: string) => void
77
+ authClientVersion: Emitter<number>
78
+ }
79
+
80
+ type InferUser<T> = T extends { createUser?: (user: User) => infer R }
81
+ ? R extends User
82
+ ? R
83
+ : User
84
+ : User
85
+
86
+ export function createBetterAuthClient<const Opts extends BetterAuthClientProps<any>>(
87
+ options: Opts
88
+ ): BetterAuthClientReturn<InferUser<Opts>, ReturnType<typeof createAuthClient<Opts>>> {
89
+ type TUser = InferUser<Opts>
90
+ const {
91
+ onAuthStateChange,
92
+ onAuthError,
93
+ createUser,
94
+ storagePrefix = 'auth',
95
+ retryDelay = 4000,
96
+ tokenValidationEndpoint = '/api/auth/validateToken',
97
+ ...authClientOptions
98
+ } = options
99
+
100
+ const empty: AuthState<TUser> = {
101
+ state: 'logged-out',
102
+ session: null,
103
+ user: null,
104
+ token: null,
105
+ }
106
+
107
+ const createAuthClientWithSession = (session: string) => {
108
+ return createAuthClient({
109
+ ...authClientOptions,
110
+ fetchOptions: {
111
+ headers: {
112
+ Authorization: `Bearer ${session}`,
113
+ },
114
+ },
115
+ })
116
+ }
117
+
118
+ const keysStorage = createLocalStorageValue<StorageKeys>(`${storagePrefix}-keys`)
119
+ const stateStorage = createLocalStorageValue<AuthState<TUser>>(`${storagePrefix}-state`)
120
+
121
+ let authClient = (() => {
122
+ const existingSession = keysStorage.get()?.session
123
+ return existingSession
124
+ ? createAuthClientWithSession(existingSession)
125
+ : createAuthClient(authClientOptions as Opts)
126
+ })()
127
+
128
+ const authState = createEmitter<AuthState<TUser>>(
129
+ 'authState',
130
+ stateStorage.get() || empty,
131
+ {
132
+ comparator: isEqualDeepLite,
133
+ }
134
+ )
135
+
136
+ const authClientVersion = createEmitter<number>('authClientVersion', 0)
137
+
138
+ const setState = (update: Partial<AuthState<TUser>>) => {
139
+ const current = authState.value!
140
+ const next = { ...current, ...update }
141
+ stateStorage.set(next)
142
+ authState.emit(next)
143
+
144
+ // update storage keys
145
+ if (next.token && next.session) {
146
+ keysStorage.set({
147
+ token: next.token,
148
+ session: next.session.token,
149
+ })
150
+ } else {
151
+ keysStorage.set({
152
+ token: '',
153
+ session: '',
154
+ })
155
+ }
156
+
157
+ // call optional callback
158
+ onAuthStateChange?.(next)
159
+ }
160
+
161
+ const setAuthClientToken = async (props: { token: string; session: string }) => {
162
+ keysStorage.set(props)
163
+ updateAuthClient(props.session)
164
+ }
165
+
166
+ function updateAuthClient(session: string) {
167
+ authClient = createAuthClientWithSession(session)
168
+ authClientVersion.emit(Math.random())
169
+ subscribeToAuthEffect()
170
+ }
171
+
172
+ let dispose: Function | null = null
173
+ let retryTimer: ReturnType<typeof setTimeout> | null = null
174
+
175
+ function subscribeToAuthEffect() {
176
+ dispose?.()
177
+
178
+ dispose = authClient.useSession.subscribe(async (props) => {
179
+ const { data: dataGeneric, isPending, error } = props
180
+
181
+ if (error) {
182
+ onAuthError?.(error)
183
+ // retry by re-subscribing after a delay to recover from transient errors
184
+ scheduleAuthRetry(retryDelay)
185
+ return
186
+ }
187
+
188
+ const data = dataGeneric as
189
+ | undefined
190
+ | {
191
+ session?: AuthState<TUser>['session']
192
+ user?: AuthState<TUser>['user']
193
+ }
194
+
195
+ setState({
196
+ state: isPending ? 'loading' : data?.session ? 'logged-in' : 'logged-out',
197
+ session: data?.session,
198
+ user: data?.user
199
+ ? createUser
200
+ ? createUser(data.user as User)
201
+ : (data.user as any)
202
+ : null,
203
+ })
204
+
205
+ // get token on creating a new session (for native + tauri)
206
+ if (data?.session && !authState.value.token) {
207
+ getValidToken().then((token) => {
208
+ if (token) {
209
+ setState({ token })
210
+ }
211
+ })
212
+ }
213
+ })
214
+ }
215
+
216
+ function scheduleAuthRetry(delayMs: number) {
217
+ if (retryTimer) {
218
+ clearTimeout(retryTimer)
219
+ }
220
+ retryTimer = setTimeout(() => {
221
+ retryTimer = null
222
+ subscribeToAuthEffect()
223
+ }, delayMs)
224
+ }
225
+
226
+ async function getValidToken(): Promise<string | undefined> {
227
+ const existing = keysStorage.get()?.token
228
+
229
+ if (existing) {
230
+ try {
231
+ const response = await fetch(tokenValidationEndpoint, {
232
+ method: 'POST',
233
+ headers: {
234
+ 'Content-Type': 'application/json',
235
+ },
236
+ body: JSON.stringify({
237
+ token: existing,
238
+ }),
239
+ }).then((res) => res.json())
240
+
241
+ if (response?.valid) {
242
+ return existing
243
+ }
244
+ } catch (error) {
245
+ console.error('Error validating token:', error)
246
+ }
247
+ }
248
+
249
+ const res = await authClient.$fetch('/token')
250
+ if (res.error) {
251
+ console.error(`Error fetching token: ${res.error.statusText}`)
252
+ return undefined
253
+ }
254
+ const data = res.data as any
255
+ return data?.token as string | undefined
256
+ }
257
+
258
+ const clearAuthClientToken = () => {
259
+ keysStorage.remove()
260
+ }
261
+
262
+ const getAuth = () => {
263
+ const state = authState?.value || empty
264
+ return {
265
+ ...state,
266
+ loggedIn: !!state.session,
267
+ }
268
+ }
269
+
270
+ const useAuth = () => {
271
+ return useEmitterValue(authState) || empty
272
+ }
273
+
274
+ function clearState() {
275
+ keysStorage.clear()
276
+ stateStorage.clear()
277
+ setState(empty)
278
+ }
279
+
280
+ // initialize subscription
281
+ subscribeToAuthEffect()
282
+
283
+ // cleanup on unmount
284
+ if (typeof window !== 'undefined' && window.addEventListener) {
285
+ const cleanup = () => {
286
+ dispose?.()
287
+ if (retryTimer) {
288
+ clearTimeout(retryTimer)
289
+ }
290
+ }
291
+ window.addEventListener('beforeunload', cleanup)
292
+ }
293
+
294
+ const proxiedAuthClient = new Proxy(authClient, {
295
+ get(_target, key) {
296
+ if (key === 'signOut') {
297
+ return async () => {
298
+ clearState()
299
+ // @ts-expect-error better-auth type issue, signOut does exist
300
+ await authClient.signOut?.()
301
+ // force refresh to clear any remaining state
302
+ if (typeof window !== 'undefined') {
303
+ window.location?.reload?.()
304
+ }
305
+ }
306
+ }
307
+ // ensure always latest authClient after login
308
+ return Reflect.get(authClient, key)
309
+ },
310
+ }) as ReturnType<typeof createAuthClient<Opts>>
311
+
312
+ return {
313
+ authClientVersion,
314
+ clearState,
315
+ authState,
316
+ authClient: proxiedAuthClient,
317
+ setAuthClientToken,
318
+ clearAuthClientToken,
319
+ useAuth,
320
+ getAuth,
321
+ getValidToken,
322
+ updateAuthClient,
323
+ }
324
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './createAuthClient'
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Client-specific exports for better-auth
3
+ */
4
+ export { createBetterAuthClient } from "./createAuthClient";
5
+ export type { AuthState, BetterAuthClientConfig, BetterAuthClientReturn, StorageKeys } from "./createAuthClient";
6
+ // re-export useful better-auth types
7
+ export type { BetterAuthClientOptions, BetterAuthClientPlugin } from "better-auth/client";
8
+ export type { Session, User } from "better-auth";
9
+
10
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "mappings": ";;;AAIA,SAAS,8BAA8B;AACvC,cACE,WACA,wBACA,wBACA,mBACK;;AAGP,cAAc,yBAAyB,8BAA8B;AACrE,cAAc,SAAS,YAAY",
3
+ "names": [],
4
+ "sources": [
5
+ "src/client.ts"
6
+ ],
7
+ "sourcesContent": [
8
+ "/**\n * Client-specific exports for better-auth\n */\n\nexport { createBetterAuthClient } from './createAuthClient'\nexport type {\n AuthState,\n BetterAuthClientConfig,\n BetterAuthClientReturn,\n StorageKeys,\n} from './createAuthClient'\n\n// re-export useful better-auth types\nexport type { BetterAuthClientOptions, BetterAuthClientPlugin } from 'better-auth/client'\nexport type { Session, User } from 'better-auth'\n"
9
+ ],
10
+ "version": 3
11
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Better-auth helpers for React / React Native applications
3
+ *
4
+ * Features:
5
+ * - JWT support (for Zero, Tauri, React Native)
6
+ * - Session persistence in local storage
7
+ * - Token validation and refresh
8
+ * - State management with emitters
9
+ * - Automatic retry on errors
10
+ */
11
+ import { createEmitter, type Emitter } from "@take-out/helpers";
12
+ import type { Session, User } from "better-auth";
13
+ import { type BetterAuthClientOptions, createAuthClient } from "better-auth/client";
14
+ export interface StorageKeys {
15
+ token: string;
16
+ session: string;
17
+ }
18
+ export type AuthState<U extends User = User> = {
19
+ state: "loading" | "logged-in" | "logged-out";
20
+ session: Session | null;
21
+ user: U | null;
22
+ token: string | null;
23
+ };
24
+ export interface BetterAuthClientProps<TUser extends User = User> extends BetterAuthClientOptions {
25
+ /**
26
+ * Callback to transform and type the user object
27
+ * This allows you to add app-specific fields and ensure proper typing
28
+ * @default (user) => user
29
+ */
30
+ createUser?: (user: User) => TUser;
31
+ /**
32
+ * Optional callback when authentication state changes
33
+ */
34
+ onAuthStateChange?: (state: AuthState<TUser>) => void;
35
+ /**
36
+ * Optional callback for handling auth errors
37
+ */
38
+ onAuthError?: (error: any) => void;
39
+ /**
40
+ * Storage key prefix for local storage
41
+ * @default 'auth'
42
+ */
43
+ storagePrefix?: string;
44
+ /**
45
+ * Retry delay in milliseconds after auth errors
46
+ * @default 4000
47
+ */
48
+ retryDelay?: number;
49
+ /**
50
+ * Custom token validation endpoint
51
+ * @default '/api/auth/validateToken'
52
+ */
53
+ tokenValidationEndpoint?: string;
54
+ }
55
+ export interface BetterAuthClientReturn<
56
+ U extends User = User,
57
+ TClient = any
58
+ > {
59
+ clearState: () => void;
60
+ authState: ReturnType<typeof createEmitter<AuthState<U>>>;
61
+ authClient: TClient;
62
+ setAuthClientToken: (props: {
63
+ token: string;
64
+ session: string;
65
+ }) => Promise<void>;
66
+ clearAuthClientToken: () => void;
67
+ useAuth: () => AuthState<U>;
68
+ getAuth: () => AuthState<U> & {
69
+ loggedIn: boolean;
70
+ };
71
+ getValidToken: () => Promise<string | undefined>;
72
+ updateAuthClient: (session: string) => void;
73
+ authClientVersion: Emitter<number>;
74
+ }
75
+ type InferUser<T> = T extends {
76
+ createUser?: (user: User) => infer R;
77
+ } ? R extends User ? R : User : User;
78
+ export declare function createBetterAuthClient<const Opts extends BetterAuthClientProps<any>>(options: Opts): BetterAuthClientReturn<InferUser<Opts>, ReturnType<typeof createAuthClient<Opts>>>;
79
+ export {};
80
+
81
+ //# sourceMappingURL=createAuthClient.d.ts.map
@@ -0,0 +1,18 @@
1
+ {
2
+ "mappings": ";;;;;;;;;;AAWA,SACE,oBAEK,eAGA,mBAAmB;AAC1B,cAAc,SAAS,YAAY,aAAa;AAChD,cAAc,yBAAyB,wBAAwB,oBAAoB;AAEnF,iBAAiB,YAAY;CAC3B;CACA;AACD;AAED,YAAY,UAAU,UAAU,OAAO,QAAQ;CAC7C,OAAO,YAAY,cAAc;CACjC,SAAS;CACT,MAAM;CACN;AACD;AAED,iBAAiB,sBAAsB,cAAc,OAAO,cAClD,wBAAwB;;;;;;CAMhC,cAAcA,MAAM,SAAS;;;;CAI7B,qBAAqBC,OAAO,UAAU;;;;CAItC,eAAeC;;;;;CAKf;;;;;CAKA;;;;;CAKA;AACD;AAED,iBAAiB;CAAuB,UAAU,OAAO;CAAM;EAAe;CAC5E;CACA,WAAW,kBAAkB,cAAc,UAAU;CACrD,YAAY;CACZ,qBAAqBC,OAAO;EAAE;EAAe;CAAiB,MAAK;CACnE;CACA,eAAe,UAAU;CACzB,eAAe,UAAU,KAAK;EAAE;CAAmB;CACnD,qBAAqB;CACrB,mBAAmBC;CACnB,mBAAmB;AACpB;KAEI,UAAU,KAAK,UAAU;CAAE,cAAcJ,MAAM,eAAe;AAAG,IAClE,UAAU,OACR,IACA,OACF;AAEJ,OAAO,iBAAS,6BAA6B,aAAa,4BACxDK,SAAS,OACR,uBAAuB,UAAU,OAAO,kBAAkB,iBAAiB",
3
+ "names": [
4
+ "user: User",
5
+ "state: AuthState<TUser>",
6
+ "error: any",
7
+ "props: { token: string; session: string }",
8
+ "session: string",
9
+ "options: Opts"
10
+ ],
11
+ "sources": [
12
+ "src/createAuthClient.ts"
13
+ ],
14
+ "sourcesContent": [
15
+ "/**\n * Better-auth helpers for React / React Native applications\n *\n * Features:\n * - JWT support (for Zero, Tauri, React Native)\n * - Session persistence in local storage\n * - Token validation and refresh\n * - State management with emitters\n * - Automatic retry on errors\n */\n\nimport {\n createEmitter,\n createLocalStorageValue,\n type Emitter,\n isEqualDeepLite,\n useEmitterValue,\n} from '@take-out/helpers'\nimport type { Session, User } from 'better-auth'\nimport { type BetterAuthClientOptions, createAuthClient } from 'better-auth/client'\n\nexport interface StorageKeys {\n token: string\n session: string\n}\n\nexport type AuthState<U extends User = User> = {\n state: 'loading' | 'logged-in' | 'logged-out'\n session: Session | null\n user: U | null\n token: string | null\n}\n\nexport interface BetterAuthClientProps<TUser extends User = User>\n extends BetterAuthClientOptions {\n /**\n * Callback to transform and type the user object\n * This allows you to add app-specific fields and ensure proper typing\n * @default (user) => user\n */\n createUser?: (user: User) => TUser\n /**\n * Optional callback when authentication state changes\n */\n onAuthStateChange?: (state: AuthState<TUser>) => void\n /**\n * Optional callback for handling auth errors\n */\n onAuthError?: (error: any) => void\n /**\n * Storage key prefix for local storage\n * @default 'auth'\n */\n storagePrefix?: string\n /**\n * Retry delay in milliseconds after auth errors\n * @default 4000\n */\n retryDelay?: number\n /**\n * Custom token validation endpoint\n * @default '/api/auth/validateToken'\n */\n tokenValidationEndpoint?: string\n}\n\nexport interface BetterAuthClientReturn<U extends User = User, TClient = any> {\n clearState: () => void\n authState: ReturnType<typeof createEmitter<AuthState<U>>>\n authClient: TClient\n setAuthClientToken: (props: { token: string; session: string }) => Promise<void>\n clearAuthClientToken: () => void\n useAuth: () => AuthState<U>\n getAuth: () => AuthState<U> & { loggedIn: boolean }\n getValidToken: () => Promise<string | undefined>\n updateAuthClient: (session: string) => void\n authClientVersion: Emitter<number>\n}\n\ntype InferUser<T> = T extends { createUser?: (user: User) => infer R }\n ? R extends User\n ? R\n : User\n : User\n\nexport function createBetterAuthClient<const Opts extends BetterAuthClientProps<any>>(\n options: Opts\n): BetterAuthClientReturn<InferUser<Opts>, ReturnType<typeof createAuthClient<Opts>>> {\n type TUser = InferUser<Opts>\n const {\n onAuthStateChange,\n onAuthError,\n createUser,\n storagePrefix = 'auth',\n retryDelay = 4000,\n tokenValidationEndpoint = '/api/auth/validateToken',\n ...authClientOptions\n } = options\n\n const empty: AuthState<TUser> = {\n state: 'logged-out',\n session: null,\n user: null,\n token: null,\n }\n\n const createAuthClientWithSession = (session: string) => {\n return createAuthClient({\n ...authClientOptions,\n fetchOptions: {\n headers: {\n Authorization: `Bearer ${session}`,\n },\n },\n })\n }\n\n const keysStorage = createLocalStorageValue<StorageKeys>(`${storagePrefix}-keys`)\n const stateStorage = createLocalStorageValue<AuthState<TUser>>(`${storagePrefix}-state`)\n\n let authClient = (() => {\n const existingSession = keysStorage.get()?.session\n return existingSession\n ? createAuthClientWithSession(existingSession)\n : createAuthClient(authClientOptions as Opts)\n })()\n\n const authState = createEmitter<AuthState<TUser>>(\n 'authState',\n stateStorage.get() || empty,\n {\n comparator: isEqualDeepLite,\n }\n )\n\n const authClientVersion = createEmitter<number>('authClientVersion', 0)\n\n const setState = (update: Partial<AuthState<TUser>>) => {\n const current = authState.value!\n const next = { ...current, ...update }\n stateStorage.set(next)\n authState.emit(next)\n\n // update storage keys\n if (next.token && next.session) {\n keysStorage.set({\n token: next.token,\n session: next.session.token,\n })\n } else {\n keysStorage.set({\n token: '',\n session: '',\n })\n }\n\n // call optional callback\n onAuthStateChange?.(next)\n }\n\n const setAuthClientToken = async (props: { token: string; session: string }) => {\n keysStorage.set(props)\n updateAuthClient(props.session)\n }\n\n function updateAuthClient(session: string) {\n authClient = createAuthClientWithSession(session)\n authClientVersion.emit(Math.random())\n subscribeToAuthEffect()\n }\n\n let dispose: Function | null = null\n let retryTimer: ReturnType<typeof setTimeout> | null = null\n\n function subscribeToAuthEffect() {\n dispose?.()\n\n dispose = authClient.useSession.subscribe(async (props) => {\n const { data: dataGeneric, isPending, error } = props\n\n if (error) {\n onAuthError?.(error)\n // retry by re-subscribing after a delay to recover from transient errors\n scheduleAuthRetry(retryDelay)\n return\n }\n\n const data = dataGeneric as\n | undefined\n | {\n session?: AuthState<TUser>['session']\n user?: AuthState<TUser>['user']\n }\n\n setState({\n state: isPending ? 'loading' : data?.session ? 'logged-in' : 'logged-out',\n session: data?.session,\n user: data?.user\n ? createUser\n ? createUser(data.user as User)\n : (data.user as any)\n : null,\n })\n\n // get token on creating a new session (for native + tauri)\n if (data?.session && !authState.value.token) {\n getValidToken().then((token) => {\n if (token) {\n setState({ token })\n }\n })\n }\n })\n }\n\n function scheduleAuthRetry(delayMs: number) {\n if (retryTimer) {\n clearTimeout(retryTimer)\n }\n retryTimer = setTimeout(() => {\n retryTimer = null\n subscribeToAuthEffect()\n }, delayMs)\n }\n\n async function getValidToken(): Promise<string | undefined> {\n const existing = keysStorage.get()?.token\n\n if (existing) {\n try {\n const response = await fetch(tokenValidationEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n token: existing,\n }),\n }).then((res) => res.json())\n\n if (response?.valid) {\n return existing\n }\n } catch (error) {\n console.error('Error validating token:', error)\n }\n }\n\n const res = await authClient.$fetch('/token')\n if (res.error) {\n console.error(`Error fetching token: ${res.error.statusText}`)\n return undefined\n }\n const data = res.data as any\n return data?.token as string | undefined\n }\n\n const clearAuthClientToken = () => {\n keysStorage.remove()\n }\n\n const getAuth = () => {\n const state = authState?.value || empty\n return {\n ...state,\n loggedIn: !!state.session,\n }\n }\n\n const useAuth = () => {\n return useEmitterValue(authState) || empty\n }\n\n function clearState() {\n keysStorage.clear()\n stateStorage.clear()\n setState(empty)\n }\n\n // initialize subscription\n subscribeToAuthEffect()\n\n // cleanup on unmount\n if (typeof window !== 'undefined' && window.addEventListener) {\n const cleanup = () => {\n dispose?.()\n if (retryTimer) {\n clearTimeout(retryTimer)\n }\n }\n window.addEventListener('beforeunload', cleanup)\n }\n\n const proxiedAuthClient = new Proxy(authClient, {\n get(_target, key) {\n if (key === 'signOut') {\n return async () => {\n clearState()\n // @ts-expect-error better-auth type issue, signOut does exist\n await authClient.signOut?.()\n // force refresh to clear any remaining state\n if (typeof window !== 'undefined') {\n window.location?.reload?.()\n }\n }\n }\n // ensure always latest authClient after login\n return Reflect.get(authClient, key)\n },\n }) as ReturnType<typeof createAuthClient<Opts>>\n\n return {\n authClientVersion,\n clearState,\n authState,\n authClient: proxiedAuthClient,\n setAuthClientToken,\n clearAuthClientToken,\n useAuth,\n getAuth,\n getValidToken,\n updateAuthClient,\n }\n}\n"
16
+ ],
17
+ "version": 3
18
+ }
@@ -0,0 +1,8 @@
1
+ import type { Session } from "better-auth";
2
+ import type { BetterAuthClientReturn } from "../createAuthClient";
3
+ /**
4
+ * Hook to access just the auth session
5
+ */
6
+ export declare function useAuthSession<U = any>(client: BetterAuthClientReturn<U>): Session | null;
7
+
8
+ //# sourceMappingURL=useAuthSession.d.ts.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "mappings": "AAAA,cAAc,eAAe,aAAa;AAC1C,cAAc,8BAA8B,qBAAqB;;;;AAKjE,OAAO,iBAAS,eAAe,SAC7BA,QAAQ,uBAAuB,KAC9B",
3
+ "names": [
4
+ "client: BetterAuthClientReturn<U>"
5
+ ],
6
+ "sources": [
7
+ "src/hooks/useAuthSession.ts"
8
+ ],
9
+ "sourcesContent": [
10
+ "import type { Session } from 'better-auth'\nimport type { BetterAuthClientReturn } from '../createAuthClient'\n\n/**\n * Hook to access just the auth session\n */\nexport function useAuthSession<U = any>(\n client: BetterAuthClientReturn<U>\n): Session | null {\n const state = client.useAuth()\n return state.session\n}"
11
+ ],
12
+ "version": 3
13
+ }
@@ -0,0 +1,7 @@
1
+ import type { AuthState, BetterAuthClientReturn } from "../createAuthClient";
2
+ /**
3
+ * Hook to access the full auth state
4
+ */
5
+ export declare function useAuthState<U = any>(client: BetterAuthClientReturn<U>): AuthState<U>;
6
+
7
+ //# sourceMappingURL=useAuthState.d.ts.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "mappings": "AACA,cAAc,WAAW,8BAA8B,qBAAqB;;;;AAK5E,OAAO,iBAAS,aAAa,SAC3BA,QAAQ,uBAAuB,KAC9B,UAAU",
3
+ "names": [
4
+ "client: BetterAuthClientReturn<U>"
5
+ ],
6
+ "sources": [
7
+ "src/hooks/useAuthState.ts"
8
+ ],
9
+ "sourcesContent": [
10
+ "import { useEmitterValue } from '@vxrn/helpers'\nimport type { AuthState, BetterAuthClientReturn } from '../createAuthClient'\n\n/**\n * Hook to access the full auth state\n */\nexport function useAuthState<U = any>(\n client: BetterAuthClientReturn<U>\n): AuthState<U> {\n return client.useAuth()\n}"
11
+ ],
12
+ "version": 3
13
+ }
@@ -0,0 +1,8 @@
1
+ import type { User } from "better-auth";
2
+ import type { BetterAuthClientReturn } from "../createAuthClient";
3
+ /**
4
+ * Hook to access just the authenticated user
5
+ */
6
+ export declare function useAuthUser<U extends User = User>(client: BetterAuthClientReturn<U>): U | null;
7
+
8
+ //# sourceMappingURL=useAuthUser.d.ts.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "mappings": "AAAA,cAAc,YAAY,aAAa;AACvC,cAAc,8BAA8B,qBAAqB;;;;AAKjE,OAAO,iBAAS,YAAY,UAAU,OAAO,MAC3CA,QAAQ,uBAAuB,KAC9B",
3
+ "names": [
4
+ "client: BetterAuthClientReturn<U>"
5
+ ],
6
+ "sources": [
7
+ "src/hooks/useAuthUser.ts"
8
+ ],
9
+ "sourcesContent": [
10
+ "import type { User } from 'better-auth'\nimport type { BetterAuthClientReturn } from '../createAuthClient'\n\n/**\n * Hook to access just the authenticated user\n */\nexport function useAuthUser<U extends User = User>(\n client: BetterAuthClientReturn<U>\n): U | null {\n const state = client.useAuth()\n return state.user\n}"
11
+ ],
12
+ "version": 3
13
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./createAuthClient";
2
+
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "mappings": "AAAA,cAAc",
3
+ "names": [],
4
+ "sources": [
5
+ "src/index.ts"
6
+ ],
7
+ "sourcesContent": [
8
+ "export * from './createAuthClient'\n"
9
+ ],
10
+ "version": 3
11
+ }