better-auth
Version:
The most comprehensive authentication framework for TypeScript.
1 lines • 8.32 kB
Source Map (JSON)
{"version":3,"file":"session-refresh.mjs","names":["state: SessionRefreshState"],"sources":["../../src/client/session-refresh.ts"],"sourcesContent":["import type { BetterAuthClientOptions } from \"@better-auth/core\";\nimport type { Session, User } from \"@better-auth/core/db\";\nimport type { BetterFetch, BetterFetchError } from \"@better-fetch/fetch\";\nimport type { WritableAtom } from \"nanostores\";\nimport { getGlobalBroadcastChannel } from \"./broadcast-channel\";\nimport { getGlobalFocusManager } from \"./focus-manager\";\nimport { getGlobalOnlineManager } from \"./online-manager\";\nimport type { AuthQueryAtom } from \"./query\";\n\nconst now = () => Math.floor(Date.now() / 1000);\n\n/**\n * Rate limit: don't refetch on focus if a session request was made within this many seconds\n */\nconst FOCUS_REFETCH_RATE_LIMIT_SECONDS = 5;\n\nexport interface SessionRefreshOptions {\n\tsessionAtom: AuthQueryAtom<{\n\t\tuser: User;\n\t\tsession: Session;\n\t}>;\n\tsessionSignal: WritableAtom<boolean>;\n\t$fetch: BetterFetch;\n\toptions?: BetterAuthClientOptions | undefined;\n}\n\ninterface SessionRefreshState {\n\tlastSync: number;\n\tlastSessionRequest: number;\n\tcachedSession: any;\n\tpollInterval?: ReturnType<typeof setInterval> | undefined;\n\tunsubscribeBroadcast?: (() => void) | undefined;\n\tunsubscribeFocus?: (() => void) | undefined;\n\tunsubscribeOnline?: (() => void) | undefined;\n}\n\nexport function createSessionRefreshManager(opts: SessionRefreshOptions) {\n\tconst { sessionAtom, sessionSignal, $fetch, options = {} } = opts;\n\n\tconst refetchInterval = options.sessionOptions?.refetchInterval ?? 0;\n\tconst refetchOnWindowFocus =\n\t\toptions.sessionOptions?.refetchOnWindowFocus ?? true;\n\tconst refetchWhenOffline =\n\t\toptions.sessionOptions?.refetchWhenOffline ?? false;\n\n\tconst state: SessionRefreshState = {\n\t\tlastSync: 0,\n\t\tlastSessionRequest: 0,\n\t\tcachedSession: undefined,\n\t};\n\n\tconst shouldRefetch = (): boolean => {\n\t\treturn refetchWhenOffline || getGlobalOnlineManager().isOnline;\n\t};\n\n\tconst triggerRefetch = (\n\t\tevent?:\n\t\t\t| {\n\t\t\t\t\tevent?: \"poll\" | \"visibilitychange\" | \"storage\";\n\t\t\t }\n\t\t\t| undefined,\n\t) => {\n\t\tif (!shouldRefetch()) return;\n\n\t\tif (event?.event === \"storage\") {\n\t\t\tstate.lastSync = now();\n\t\t\tsessionSignal.set(!sessionSignal.get());\n\t\t\treturn;\n\t\t}\n\n\t\tconst currentSession = sessionAtom.get();\n\n\t\tif (event?.event === \"poll\") {\n\t\t\tstate.lastSessionRequest = now();\n\t\t\t$fetch<{\n\t\t\t\tuser: User;\n\t\t\t\tsession: Session;\n\t\t\t}>(\"/get-session\")\n\t\t\t\t.then((res) => {\n\t\t\t\t\tif (res.error) {\n\t\t\t\t\t\tsessionAtom.set({\n\t\t\t\t\t\t\t...currentSession,\n\t\t\t\t\t\t\tdata: null,\n\t\t\t\t\t\t\terror: res.error as BetterFetchError | null,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsessionAtom.set({\n\t\t\t\t\t\t\t...currentSession,\n\t\t\t\t\t\t\tdata: res.data,\n\t\t\t\t\t\t\terror: null,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tstate.lastSync = now();\n\t\t\t\t\tsessionSignal.set(!sessionSignal.get());\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t\treturn;\n\t\t}\n\n\t\t// Rate limit: don't refetch on focus if a session request was made recently\n\t\tif (event?.event === \"visibilitychange\") {\n\t\t\tconst timeSinceLastRequest = now() - state.lastSessionRequest;\n\t\t\tif (\n\t\t\t\ttimeSinceLastRequest < FOCUS_REFETCH_RATE_LIMIT_SECONDS &&\n\t\t\t\tcurrentSession?.data !== null &&\n\t\t\t\tcurrentSession?.data !== undefined\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\tcurrentSession?.data === null ||\n\t\t\tcurrentSession?.data === undefined ||\n\t\t\tevent?.event === \"visibilitychange\"\n\t\t) {\n\t\t\tif (event?.event === \"visibilitychange\") {\n\t\t\t\tstate.lastSessionRequest = now();\n\t\t\t}\n\t\t\tstate.lastSync = now();\n\t\t\tsessionSignal.set(!sessionSignal.get());\n\t\t}\n\t};\n\n\tconst broadcastSessionUpdate = (\n\t\ttrigger: \"signout\" | \"getSession\" | \"updateUser\",\n\t) => {\n\t\tgetGlobalBroadcastChannel().post({\n\t\t\tevent: \"session\",\n\t\t\tdata: { trigger },\n\t\t\tclientId: Math.random().toString(36).substring(7),\n\t\t});\n\t};\n\n\tconst setupPolling = () => {\n\t\tif (refetchInterval && refetchInterval > 0) {\n\t\t\tstate.pollInterval = setInterval(() => {\n\t\t\t\tconst currentSession = sessionAtom.get();\n\t\t\t\tif (currentSession?.data) {\n\t\t\t\t\ttriggerRefetch({ event: \"poll\" });\n\t\t\t\t}\n\t\t\t}, refetchInterval * 1000);\n\t\t}\n\t};\n\n\tconst setupBroadcast = () => {\n\t\tstate.unsubscribeBroadcast = getGlobalBroadcastChannel().subscribe(() => {\n\t\t\ttriggerRefetch({ event: \"storage\" });\n\t\t});\n\t};\n\n\tconst setupFocusRefetch = () => {\n\t\tif (!refetchOnWindowFocus) return;\n\n\t\tstate.unsubscribeFocus = getGlobalFocusManager().subscribe(() => {\n\t\t\ttriggerRefetch({ event: \"visibilitychange\" });\n\t\t});\n\t};\n\n\tconst setupOnlineRefetch = () => {\n\t\tstate.unsubscribeOnline = getGlobalOnlineManager().subscribe((online) => {\n\t\t\tif (online) {\n\t\t\t\ttriggerRefetch({ event: \"visibilitychange\" });\n\t\t\t}\n\t\t});\n\t};\n\n\tconst init = () => {\n\t\tsetupPolling();\n\t\tsetupBroadcast();\n\t\tsetupFocusRefetch();\n\t\tsetupOnlineRefetch();\n\n\t\tgetGlobalBroadcastChannel().setup();\n\t\tgetGlobalFocusManager().setup();\n\t\tgetGlobalOnlineManager().setup();\n\t};\n\n\tconst cleanup = () => {\n\t\tif (state.pollInterval) {\n\t\t\tclearInterval(state.pollInterval);\n\t\t\tstate.pollInterval = undefined;\n\t\t}\n\t\tif (state.unsubscribeBroadcast) {\n\t\t\tstate.unsubscribeBroadcast();\n\t\t\tstate.unsubscribeBroadcast = undefined;\n\t\t}\n\t\tif (state.unsubscribeFocus) {\n\t\t\tstate.unsubscribeFocus();\n\t\t\tstate.unsubscribeFocus = undefined;\n\t\t}\n\t\tif (state.unsubscribeOnline) {\n\t\t\tstate.unsubscribeOnline();\n\t\t\tstate.unsubscribeOnline = undefined;\n\t\t}\n\t\tstate.lastSync = 0;\n\t\tstate.lastSessionRequest = 0;\n\t\tstate.cachedSession = undefined;\n\t};\n\n\treturn {\n\t\tinit,\n\t\tcleanup,\n\t\ttriggerRefetch,\n\t\tbroadcastSessionUpdate,\n\t};\n}\n"],"mappings":"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"}