@better-auth/expo
Version:
Better Auth integration for Expo and React Native applications.
1 lines • 25.1 kB
Source Map (JSON)
{"version":3,"file":"client.mjs","names":["parseSetCookieHeader","toSetCookie: Record<string, StoredCookie>","store: ClientStore | null","Browser: typeof import(\"expo-web-browser\") | undefined","url"],"sources":["../src/focus-manager.ts","../src/online-manager.ts","../src/client.ts"],"sourcesContent":["import type { FocusListener, FocusManager } from \"better-auth/client\";\nimport { kFocusManager } from \"better-auth/client\";\nimport type { AppStateStatus } from \"react-native\";\nimport { AppState } from \"react-native\";\n\nclass ExpoFocusManager implements FocusManager {\n\tlisteners = new Set<FocusListener>();\n\tsubscription?: ReturnType<typeof AppState.addEventListener>;\n\tisFocused: boolean | undefined;\n\n\tsubscribe(listener: FocusListener) {\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t}\n\n\tsetFocused(focused: boolean) {\n\t\tif (this.isFocused === focused) return;\n\t\tthis.isFocused = focused;\n\t\tthis.listeners.forEach((listener) => listener(focused));\n\t}\n\n\tsetup() {\n\t\tthis.subscription = AppState.addEventListener(\n\t\t\t\"change\",\n\t\t\t(state: AppStateStatus) => {\n\t\t\t\tthis.setFocused(state === \"active\");\n\t\t\t},\n\t\t);\n\n\t\treturn () => {\n\t\t\tthis.subscription?.remove();\n\t\t};\n\t}\n}\n\nexport function setupExpoFocusManager() {\n\tif (!(globalThis as any)[kFocusManager]) {\n\t\t(globalThis as any)[kFocusManager] = new ExpoFocusManager();\n\t}\n\treturn (globalThis as any)[kFocusManager] as FocusManager;\n}\n","import type { OnlineListener, OnlineManager } from \"better-auth/client\";\nimport { kOnlineManager } from \"better-auth/client\";\n\nclass ExpoOnlineManager implements OnlineManager {\n\tlisteners = new Set<OnlineListener>();\n\tisOnline = true;\n\tunsubscribe?: () => void;\n\n\tsubscribe(listener: OnlineListener) {\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t}\n\n\tsetOnline(online: boolean) {\n\t\tif (this.isOnline === online) return;\n\t\tthis.isOnline = online;\n\t\tthis.listeners.forEach((listener) => listener(online));\n\t}\n\n\tsetup() {\n\t\timport(\"expo-network\")\n\t\t\t.then(({ addNetworkStateListener }) => {\n\t\t\t\tconst subscription = addNetworkStateListener((state) => {\n\t\t\t\t\tthis.setOnline(!!state.isInternetReachable);\n\t\t\t\t});\n\t\t\t\tthis.unsubscribe = () => subscription.remove();\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\t// fallback to always online\n\t\t\t\tthis.setOnline(true);\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tthis.unsubscribe?.();\n\t\t};\n\t}\n}\n\nexport function setupExpoOnlineManager() {\n\tif (!(globalThis as any)[kOnlineManager]) {\n\t\t(globalThis as any)[kOnlineManager] = new ExpoOnlineManager();\n\t}\n\treturn (globalThis as any)[kOnlineManager] as OnlineManager;\n}\n","import type {\n\tBetterAuthClientPlugin,\n\tClientFetchOption,\n\tClientStore,\n} from \"@better-auth/core\";\nimport { safeJSONParse } from \"@better-auth/core/utils\";\nimport {\n\tparseSetCookieHeader,\n\tSECURE_COOKIE_PREFIX,\n\tstripSecureCookiePrefix,\n} from \"better-auth/cookies\";\nimport Constants from \"expo-constants\";\nimport * as Linking from \"expo-linking\";\nimport { Platform } from \"react-native\";\nimport { setupExpoFocusManager } from \"./focus-manager\";\nimport { setupExpoOnlineManager } from \"./online-manager\";\n\nif (Platform.OS !== \"web\") {\n\tsetupExpoFocusManager();\n\tsetupExpoOnlineManager();\n}\n\ninterface ExpoClientOptions {\n\tscheme?: string | undefined;\n\tstorage: {\n\t\tsetItem: (key: string, value: string) => any;\n\t\tgetItem: (key: string) => string | null;\n\t};\n\t/**\n\t * Prefix for local storage keys (e.g., \"my-app_cookie\", \"my-app_session_data\")\n\t * @default \"better-auth\"\n\t */\n\tstoragePrefix?: string | undefined;\n\t/**\n\t * Prefix(es) for server cookie names to filter (e.g., \"better-auth.session_token\")\n\t * This is used to identify which cookies belong to better-auth to prevent\n\t * infinite refetching when third-party cookies are set.\n\t * Can be a single string or an array of strings to match multiple prefixes.\n\t * @default \"better-auth\"\n\t * @example \"better-auth\"\n\t * @example [\"better-auth\", \"my-app\"]\n\t */\n\tcookiePrefix?: string | string[] | undefined;\n\tdisableCache?: boolean | undefined;\n\t/**\n\t * Options to customize the Expo web browser behavior when opening authentication\n\t * sessions. These are passed directly to `expo-web-browser`'s\n\t * `Browser.openBrowserAsync`.\n\t *\n\t * For example, on iOS you can use `{ preferEphemeralSession: true }` to prevent\n\t * the authentication session from sharing cookies with the user's default\n\t * browser session:\n\t *\n\t * ```ts\n\t * const client = createClient({\n\t * expo: {\n\t * webBrowserOptions: {\n\t * preferEphemeralSession: true,\n\t * },\n\t * },\n\t * });\n\t * ```\n\t */\n\twebBrowserOptions?: import(\"expo-web-browser\").AuthSessionOpenOptions;\n}\n\ninterface StoredCookie {\n\tvalue: string;\n\texpires: string | null;\n}\n\nexport function getSetCookie(header: string, prevCookie?: string | undefined) {\n\tconst parsed = parseSetCookieHeader(header);\n\tlet toSetCookie: Record<string, StoredCookie> = {};\n\tparsed.forEach((cookie, key) => {\n\t\tconst expiresAt = cookie[\"expires\"];\n\t\tconst maxAge = cookie[\"max-age\"];\n\t\tconst expires = maxAge\n\t\t\t? new Date(Date.now() + Number(maxAge) * 1000)\n\t\t\t: expiresAt\n\t\t\t\t? new Date(String(expiresAt))\n\t\t\t\t: null;\n\t\ttoSetCookie[key] = {\n\t\t\tvalue: cookie[\"value\"],\n\t\t\texpires: expires ? expires.toISOString() : null,\n\t\t};\n\t});\n\tif (prevCookie) {\n\t\ttry {\n\t\t\tconst prevCookieParsed = JSON.parse(prevCookie);\n\t\t\ttoSetCookie = {\n\t\t\t\t...prevCookieParsed,\n\t\t\t\t...toSetCookie,\n\t\t\t};\n\t\t} catch {\n\t\t\t//\n\t\t}\n\t}\n\treturn JSON.stringify(toSetCookie);\n}\n\nexport function getCookie(cookie: string) {\n\tlet parsed = {} as Record<string, StoredCookie>;\n\ttry {\n\t\tparsed = JSON.parse(cookie) as Record<string, StoredCookie>;\n\t} catch {}\n\tconst toSend = Object.entries(parsed).reduce((acc, [key, value]) => {\n\t\tif (value.expires && new Date(value.expires) < new Date()) {\n\t\t\treturn acc;\n\t\t}\n\t\treturn `${acc}; ${key}=${value.value}`;\n\t}, \"\");\n\treturn toSend;\n}\n\nfunction getOAuthStateValue(\n\tcookieJson: string | null,\n\tcookiePrefix: string | string[],\n): string | null {\n\tif (!cookieJson) return null;\n\n\tconst parsed = safeJSONParse<Record<string, StoredCookie>>(cookieJson);\n\tif (!parsed) return null;\n\n\tconst prefixes = Array.isArray(cookiePrefix) ? cookiePrefix : [cookiePrefix];\n\n\tfor (const prefix of prefixes) {\n\t\t// cookie strategy uses: <prefix>.oauth_state\n\t\tconst candidates = [\n\t\t\t`${SECURE_COOKIE_PREFIX}${prefix}.oauth_state`,\n\t\t\t`${prefix}.oauth_state`,\n\t\t];\n\n\t\tfor (const name of candidates) {\n\t\t\tconst value = parsed?.[name]?.value;\n\t\t\tif (value) return value;\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction getOrigin(scheme: string) {\n\tconst schemeURI = Linking.createURL(\"\", { scheme });\n\treturn schemeURI;\n}\n\n/**\n * Compare if session cookies have actually changed by comparing their values.\n * Ignores expiry timestamps that naturally change on each request.\n *\n * @param prevCookie - Previous cookie JSON string\n * @param newCookie - New cookie JSON string\n * @returns true if session cookies have changed, false otherwise\n */\nfunction hasSessionCookieChanged(\n\tprevCookie: string | null,\n\tnewCookie: string,\n): boolean {\n\tif (!prevCookie) return true;\n\n\ttry {\n\t\tconst prev = JSON.parse(prevCookie) as Record<string, StoredCookie>;\n\t\tconst next = JSON.parse(newCookie) as Record<string, StoredCookie>;\n\n\t\t// Get all session-related cookie keys (session_token, session_data)\n\t\tconst sessionKeys = new Set<string>();\n\t\tObject.keys(prev).forEach((key) => {\n\t\t\tif (key.includes(\"session_token\") || key.includes(\"session_data\")) {\n\t\t\t\tsessionKeys.add(key);\n\t\t\t}\n\t\t});\n\t\tObject.keys(next).forEach((key) => {\n\t\t\tif (key.includes(\"session_token\") || key.includes(\"session_data\")) {\n\t\t\t\tsessionKeys.add(key);\n\t\t\t}\n\t\t});\n\n\t\t// Compare the values of session cookies (ignore expires timestamps)\n\t\tfor (const key of sessionKeys) {\n\t\t\tconst prevValue = prev[key]?.value;\n\t\t\tconst nextValue = next[key]?.value;\n\t\t\tif (prevValue !== nextValue) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t} catch {\n\t\t// If parsing fails, assume cookie changed\n\t\treturn true;\n\t}\n}\n\n/**\n * Check if the Set-Cookie header contains better-auth cookies.\n * This prevents infinite refetching when non-better-auth cookies (like third-party cookies) change.\n *\n * Supports multiple cookie naming patterns:\n * - Default: \"better-auth.session_token\", \"better-auth-passkey\", \"__Secure-better-auth.session_token\"\n * - Custom prefix: \"myapp.session_token\", \"myapp-passkey\", \"__Secure-myapp.session_token\"\n * - Custom full names: \"my_custom_session_token\", \"custom_session_data\"\n * - No prefix (cookiePrefix=\"\"): matches any cookie with known suffixes\n * - Multiple prefixes: [\"better-auth\", \"my-app\"] matches cookies starting with any of the prefixes\n *\n * @param setCookieHeader - The Set-Cookie header value\n * @param cookiePrefix - The cookie prefix(es) to check for. Can be a string, array of strings, or empty string.\n * @returns true if the header contains better-auth cookies, false otherwise\n */\nexport function hasBetterAuthCookies(\n\tsetCookieHeader: string,\n\tcookiePrefix: string | string[],\n): boolean {\n\tconst cookies = parseSetCookieHeader(setCookieHeader);\n\tconst cookieSuffixes = [\"session_token\", \"session_data\"];\n\tconst prefixes = Array.isArray(cookiePrefix) ? cookiePrefix : [cookiePrefix];\n\n\t// Check if any cookie is a better-auth cookie\n\tfor (const name of cookies.keys()) {\n\t\t// Remove __Secure- prefix if present for comparison\n\t\tconst nameWithoutSecure = stripSecureCookiePrefix(name);\n\n\t\t// Check against all provided prefixes\n\t\tfor (const prefix of prefixes) {\n\t\t\tif (prefix) {\n\t\t\t\t// When prefix is provided, check if cookie starts with the prefix\n\t\t\t\t// This matches all better-auth cookies including session cookies, passkey cookies, etc.\n\t\t\t\tif (nameWithoutSecure.startsWith(prefix)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// When prefix is empty, check for common better-auth cookie patterns\n\t\t\t\tfor (const suffix of cookieSuffixes) {\n\t\t\t\t\tif (nameWithoutSecure.endsWith(suffix)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Expo secure store does not support colons in the keys.\n * This function replaces colons with underscores.\n *\n * @see https://github.com/better-auth/better-auth/issues/5426\n *\n * @param name cookie name to be saved in the storage\n * @returns normalized cookie name\n */\nexport function normalizeCookieName(name: string) {\n\treturn name.replace(/:/g, \"_\");\n}\n\nexport function storageAdapter(storage: {\n\tgetItem: (name: string) => string | null;\n\tsetItem: (name: string, value: string) => void;\n}) {\n\treturn {\n\t\tgetItem: (name: string) => {\n\t\t\treturn storage.getItem(normalizeCookieName(name));\n\t\t},\n\t\tsetItem: (name: string, value: string) => {\n\t\t\treturn storage.setItem(normalizeCookieName(name), value);\n\t\t},\n\t};\n}\n\nexport const expoClient = (opts: ExpoClientOptions) => {\n\tlet store: ClientStore | null = null;\n\tconst storagePrefix = opts?.storagePrefix || \"better-auth\";\n\tconst cookieName = `${storagePrefix}_cookie`;\n\tconst localCacheName = `${storagePrefix}_session_data`;\n\tconst storage = storageAdapter(opts?.storage);\n\tconst isWeb = Platform.OS === \"web\";\n\tconst cookiePrefix = opts?.cookiePrefix || \"better-auth\";\n\n\tconst rawScheme =\n\t\topts?.scheme || Constants.expoConfig?.scheme || Constants.platform?.scheme;\n\tconst scheme = Array.isArray(rawScheme) ? rawScheme[0] : rawScheme;\n\n\tif (!scheme && !isWeb) {\n\t\tthrow new Error(\n\t\t\t\"Scheme not found in app.json. Please provide a scheme in the options.\",\n\t\t);\n\t}\n\treturn {\n\t\tid: \"expo\",\n\t\tgetActions(_, $store) {\n\t\t\tstore = $store;\n\t\t\treturn {\n\t\t\t\t/**\n\t\t\t\t * Get the stored cookie.\n\t\t\t\t *\n\t\t\t\t * You can use this to get the cookie stored in the device and use it in your fetch\n\t\t\t\t * requests.\n\t\t\t\t *\n\t\t\t\t * @example\n\t\t\t\t * ```ts\n\t\t\t\t * const cookie = client.getCookie();\n\t\t\t\t * fetch(\"https://api.example.com\", {\n\t\t\t\t * \theaders: {\n\t\t\t\t * \t\tcookie,\n\t\t\t\t * \t},\n\t\t\t\t * });\n\t\t\t\t */\n\t\t\t\tgetCookie: () => {\n\t\t\t\t\tconst cookie = storage.getItem(cookieName);\n\t\t\t\t\treturn getCookie(cookie || \"{}\");\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\tfetchPlugins: [\n\t\t\t{\n\t\t\t\tid: \"expo\",\n\t\t\t\tname: \"Expo\",\n\t\t\t\thooks: {\n\t\t\t\t\tasync onSuccess(context) {\n\t\t\t\t\t\tif (isWeb) return;\n\t\t\t\t\t\tconst setCookie = context.response.headers.get(\"set-cookie\");\n\t\t\t\t\t\tif (setCookie) {\n\t\t\t\t\t\t\t// Only process and notify if the Set-Cookie header contains better-auth cookies\n\t\t\t\t\t\t\t// This prevents infinite refetching when other cookies (like Cloudflare's __cf_bm) are present\n\t\t\t\t\t\t\tif (hasBetterAuthCookies(setCookie, cookiePrefix)) {\n\t\t\t\t\t\t\t\tconst prevCookie = storage.getItem(cookieName);\n\t\t\t\t\t\t\t\tconst toSetCookie = getSetCookie(\n\t\t\t\t\t\t\t\t\tsetCookie || \"\",\n\t\t\t\t\t\t\t\t\tprevCookie ?? undefined,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t// Only notify $sessionSignal if the session cookie values actually changed\n\t\t\t\t\t\t\t\t// This prevents infinite refetching when the server sends the same cookie with updated expiry\n\t\t\t\t\t\t\t\tif (hasSessionCookieChanged(prevCookie, toSetCookie)) {\n\t\t\t\t\t\t\t\t\tstorage.setItem(cookieName, toSetCookie);\n\t\t\t\t\t\t\t\t\tstore?.notify(\"$sessionSignal\");\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// Still update the storage to refresh expiry times, but don't trigger refetch\n\t\t\t\t\t\t\t\t\tstorage.setItem(cookieName, toSetCookie);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tcontext.request.url.toString().includes(\"/get-session\") &&\n\t\t\t\t\t\t\t!opts?.disableCache\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst data = context.data;\n\t\t\t\t\t\t\tstorage.setItem(localCacheName, JSON.stringify(data));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tcontext.data?.redirect &&\n\t\t\t\t\t\t\t(context.request.url.toString().includes(\"/sign-in\") ||\n\t\t\t\t\t\t\t\tcontext.request.url.toString().includes(\"/link-social\")) &&\n\t\t\t\t\t\t\t!context.request?.body.includes(\"idToken\") // id token is used for silent sign-in\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst callbackURL = JSON.parse(context.request.body)?.callbackURL;\n\t\t\t\t\t\t\tconst to = callbackURL;\n\t\t\t\t\t\t\tconst signInURL = context.data?.url;\n\t\t\t\t\t\t\tlet Browser: typeof import(\"expo-web-browser\") | undefined =\n\t\t\t\t\t\t\t\tundefined;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tBrowser = await import(\"expo-web-browser\");\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t\t'\"expo-web-browser\" is not installed as a dependency!',\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tcause: error,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (Platform.OS === \"android\") {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tBrowser.dismissAuthSession();\n\t\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst storedCookieJson = storage.getItem(cookieName);\n\t\t\t\t\t\t\tconst oauthStateValue = getOAuthStateValue(\n\t\t\t\t\t\t\t\tstoredCookieJson,\n\t\t\t\t\t\t\t\tcookiePrefix,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconst params = new URLSearchParams({\n\t\t\t\t\t\t\t\tauthorizationURL: signInURL,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (oauthStateValue) {\n\t\t\t\t\t\t\t\tparams.append(\"oauthState\", oauthStateValue);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst proxyURL = `${context.request.baseURL}/expo-authorization-proxy?${params.toString()}`;\n\t\t\t\t\t\t\tconst result = await Browser.openAuthSessionAsync(\n\t\t\t\t\t\t\t\tproxyURL,\n\t\t\t\t\t\t\t\tto,\n\t\t\t\t\t\t\t\topts?.webBrowserOptions,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (result.type !== \"success\") return;\n\t\t\t\t\t\t\tconst url = new URL(result.url);\n\t\t\t\t\t\t\tconst cookie = url.searchParams.get(\"cookie\");\n\t\t\t\t\t\t\tif (!cookie) return;\n\t\t\t\t\t\t\tconst prevCookie = storage.getItem(cookieName);\n\t\t\t\t\t\t\tconst toSetCookie = getSetCookie(cookie, prevCookie ?? undefined);\n\t\t\t\t\t\t\tstorage.setItem(cookieName, toSetCookie);\n\t\t\t\t\t\t\tstore?.notify(\"$sessionSignal\");\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync init(url, options) {\n\t\t\t\t\tif (isWeb) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t\toptions: options as ClientFetchOption,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\toptions = options || {};\n\t\t\t\t\tconst storedCookie = storage.getItem(cookieName);\n\t\t\t\t\tconst cookie = getCookie(storedCookie || \"{}\");\n\t\t\t\t\toptions.credentials = \"omit\";\n\t\t\t\t\toptions.headers = {\n\t\t\t\t\t\t...options.headers,\n\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\"expo-origin\": getOrigin(scheme!),\n\t\t\t\t\t\t\"x-skip-oauth-proxy\": \"true\", // skip oauth proxy for expo\n\t\t\t\t\t};\n\t\t\t\t\tif (options.body?.callbackURL) {\n\t\t\t\t\t\tif (options.body.callbackURL.startsWith(\"/\")) {\n\t\t\t\t\t\t\tconst url = Linking.createURL(options.body.callbackURL, {\n\t\t\t\t\t\t\t\tscheme,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\toptions.body.callbackURL = url;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (options.body?.newUserCallbackURL) {\n\t\t\t\t\t\tif (options.body.newUserCallbackURL.startsWith(\"/\")) {\n\t\t\t\t\t\t\tconst url = Linking.createURL(options.body.newUserCallbackURL, {\n\t\t\t\t\t\t\t\tscheme,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\toptions.body.newUserCallbackURL = url;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (options.body?.errorCallbackURL) {\n\t\t\t\t\t\tif (options.body.errorCallbackURL.startsWith(\"/\")) {\n\t\t\t\t\t\t\tconst url = Linking.createURL(options.body.errorCallbackURL, {\n\t\t\t\t\t\t\t\tscheme,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\toptions.body.errorCallbackURL = url;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (url.includes(\"/sign-out\")) {\n\t\t\t\t\t\tstorage.setItem(cookieName, \"{}\");\n\t\t\t\t\t\tstore?.atoms.session?.set({\n\t\t\t\t\t\t\t...store.atoms.session.get(),\n\t\t\t\t\t\t\tdata: null,\n\t\t\t\t\t\t\terror: null,\n\t\t\t\t\t\t\tisPending: false,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tstorage.setItem(localCacheName, \"{}\");\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\turl,\n\t\t\t\t\t\toptions: options as ClientFetchOption,\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t} satisfies BetterAuthClientPlugin;\n};\n\nexport { parseSetCookieHeader } from \"better-auth/cookies\";\nexport * from \"./focus-manager\";\nexport * from \"./online-manager\";\n"],"mappings":"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"}