UNPKG

@daveyplate/better-auth-persistent

Version:

Persistent session management for Better Auth

214 lines (206 loc) 6.32 kB
'use strict'; var nanostores = require('nanostores'); var persistent = require('@nanostores/persistent'); var SuperJSON = require('superjson'); var plugins = require('better-auth/client/plugins'); var react = require('better-auth/react'); var react$1 = require('react'); var react$2 = require('@nanostores/react'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var SuperJSON__default = /*#__PURE__*/_interopDefault(SuperJSON); // src/auth-client-store.ts var $authClient = nanostores.atom(null); var $persistentSession = persistent.persistentAtom( "session", { data: null, isPending: true, isRefetching: true, error: null, refetch: void 0 }, { encode: SuperJSON__default.default.stringify, decode: SuperJSON__default.default.parse } ); var emptyResponse = { data: null, isPending: true, isRefetching: true, error: null, refetch: void 0 }; var $persistentSessions = persistent.persistentAtom( "device-sessions", emptyResponse, { encode: SuperJSON__default.default.stringify, decode: SuperJSON__default.default.parse } ); var $freshSessions = nanostores.computed( [$authClient, $persistentSession], (authClient2, persistentSession) => nanostores.task(async () => { if (!authClient2) return emptyResponse; if (!persistentSession.data) return emptyResponse; try { const deviceSessions = await authClient2.multiSession.listDeviceSessions({ fetchOptions: { throw: true } }); return { data: deviceSessions, isPending: false, isRefetching: false, error: null, refetch: void 0 }; } catch (error) { return { data: null, isPending: false, isRefetching: false, error, refetch: void 0 }; } }) ); var $deviceSessions = nanostores.computed( [$persistentSession, $persistentSessions, $freshSessions], (persistentSession, persistentSessions, freshSessions) => { if (persistentSession.isPending) return emptyResponse; if (!freshSessions) return persistentSessions; if (!persistentSession.data) { $persistentSessions.set(emptyResponse); } else if (freshSessions.data || !persistentSessions.data && freshSessions.error) { $persistentSessions.set(freshSessions); } return $persistentSessions.get(); } ); var authClient = react.createAuthClient({ plugins: [plugins.multiSessionClient()] }); // src/set-active-session.ts async function setActiveSession({ sessionToken }) { const deviceSessions = $deviceSessions.get(); if (!deviceSessions.data) return; const session = deviceSessions.data.find( (session2) => session2.session.token === sessionToken ); if (!session) throw { error: { message: "Invalid session token", code: "INVALID_SESSION_TOKEN" } }; $persistentSession.set({ data: session, isPending: false, isRefetching: false, error: null, refetch: void 0 }); authClient.multiSession.setActive({ sessionToken: session.session.token }).then(({ error }) => { if (error) { $persistentSession.set({ data: session, isPending: false, isRefetching: false, optimistic: true, error: null, refetch: void 0 }); } }); } function subscribePersistSession(authClient2) { if (!$authClient.get()) { $authClient.set(authClient2); } const persistSession = () => { var _a; const value = authClient2.$store.atoms.session.get(); const sessionData = value == null ? void 0 : value.data; const persistentSessionData = (_a = $persistentSession.get()) == null ? void 0 : _a.data; if (!persistentSessionData || !sessionData && !(value == null ? void 0 : value.error) || sessionData && SuperJSON__default.default.stringify(sessionData) !== SuperJSON__default.default.stringify(persistentSessionData)) { $persistentSession.set(value); } }; const restoreSession = () => { var _a; const value = authClient2.$store.atoms.session.get(); const sessionData = value == null ? void 0 : value.data; const persistentValue = $persistentSession.get(); const persistentSessionData = (_a = $persistentSession.get()) == null ? void 0 : _a.data; if (!persistentSessionData) return; if (!sessionData || persistentSessionData.user.id !== sessionData.user.id) { if (sessionData) { console.log("set active session", { sessionToken: persistentSessionData.session.token }); } authClient2.$store.atoms.session.set({ ...persistentValue, refetch: value == null ? void 0 : value.refetch }); } }; const unbindPersistentSessionListener = $persistentSession.subscribe(() => { restoreSession(); }); const unbindSessionListener = authClient2.$store.atoms.session.subscribe( () => { persistSession(); restoreSession(); } ); const checkActiveSession = () => { const persistentSession = $persistentSession.get(); if (persistentSession.optimistic && persistentSession.data) { authClient2.multiSession.setActive({ sessionToken: persistentSession.data.session.token }); } }; window.addEventListener("online", checkActiveSession); return () => { unbindSessionListener(); unbindPersistentSessionListener(); }; } function usePersistSession(authClient2) { react$1.useEffect(() => subscribePersistSession(authClient2), [authClient2]); } function subscribe() { return () => { }; } function useIsHydrated() { return react$1.useSyncExternalStore( subscribe, () => true, () => false ); } // src/use-list-device-sessions.ts function useListDeviceSessions() { const isHydrated = useIsHydrated(); const result = react$2.useStore($deviceSessions); return isHydrated ? result : emptyResponse; } exports.$authClient = $authClient; exports.$deviceSessions = $deviceSessions; exports.$freshSessions = $freshSessions; exports.$persistentSession = $persistentSession; exports.emptyResponse = emptyResponse; exports.setActiveSession = setActiveSession; exports.subscribePersistSession = subscribePersistSession; exports.useListDeviceSessions = useListDeviceSessions; exports.usePersistSession = usePersistSession;