UNPKG

@daveyplate/better-auth-tanstack

Version:

Tanstack hooks for better-auth

374 lines (363 loc) 12.6 kB
'use strict'; var react = require('react'); var jsxRuntime = require('react/jsx-runtime'); var reactQuery = require('@tanstack/react-query'); var queryCore = require('@tanstack/query-core'); // src/lib/auth-query-provider.tsx var defaultAuthQueryOptions = { sessionKey: ["session"], tokenKey: ["token"], listAccountsKey: ["list-accounts"], listSessionsKey: ["list-sessions"], listDeviceSessionsKey: ["list-device-sessions"], listPasskeysKey: ["list-passkeys"], optimistic: true, refetchOnMutate: true }; var AuthQueryContext = react.createContext(defaultAuthQueryOptions); var AuthQueryProvider = ({ children, sessionQueryOptions, tokenQueryOptions, ...props }) => { return /* @__PURE__ */ jsxRuntime.jsx( AuthQueryContext.Provider, { value: { sessionQueryOptions: { staleTime: 60 * 1e3, ...sessionQueryOptions }, tokenQueryOptions: { staleTime: 600 * 1e3, ...tokenQueryOptions }, ...defaultAuthQueryOptions, ...props }, children } ); }; function useSession(authClient, options) { var _a, _b; const { sessionQueryOptions, sessionKey: queryKey, queryOptions } = react.useContext(AuthQueryContext); const mergedOptions = { ...queryOptions, ...sessionQueryOptions, ...options }; const result = reactQuery.useQuery({ queryKey, queryFn: () => authClient.getSession({ fetchOptions: { throw: true } }), ...mergedOptions }); return { ...result, session: (_a = result.data) == null ? void 0 : _a.session, user: (_b = result.data) == null ? void 0 : _b.user }; } // src/hooks/shared/use-auth-query.ts function useAuthQuery({ authClient, queryKey, queryFn, options }) { const { data: sessionData } = useSession(authClient); const { queryOptions } = react.useContext(AuthQueryContext); const mergedOptions = { ...queryOptions, ...options }; return reactQuery.useQuery({ queryKey, queryFn: sessionData ? () => queryFn({ fetchOptions: { throw: true } }) : queryCore.skipToken, ...mergedOptions }); } // src/hooks/accounts/use-list-accounts.ts function useListAccounts(authClient, options) { const { listAccountsKey: queryKey } = react.useContext(AuthQueryContext); return useAuthQuery({ authClient, queryKey, queryFn: authClient.listAccounts, options }); } var useOnMutateError = () => { const queryClient = reactQuery.useQueryClient(); const { optimistic } = react.useContext(AuthQueryContext); const onMutateError = (error, queryKey, context) => { var _a, _b; if (error) { console.error(error); (_b = (_a = queryClient.getQueryCache().config).onError) == null ? void 0 : _b.call(_a, error, { queryKey }); } if (!optimistic || !(context == null ? void 0 : context.previousData)) return; queryClient.setQueryData(queryKey, context.previousData); }; return { onMutateError }; }; // src/hooks/shared/use-auth-mutation.ts function useAuthMutation({ queryKey, mutationFn, optimisticData, options }) { const queryClient = reactQuery.useQueryClient(); const context = react.useContext(AuthQueryContext); const { optimistic } = { ...context, ...options }; const { onMutateError } = useOnMutateError(); const mutation = reactQuery.useMutation({ mutationFn: ({ fetchOptions = { throw: true }, ...params }) => mutationFn({ fetchOptions, ...params }), onMutate: async (params) => { if (!optimistic || !optimisticData) return; await queryClient.cancelQueries({ queryKey }); const previousData = queryClient.getQueryData(queryKey); if (!previousData) return; queryClient.setQueryData(queryKey, () => optimisticData(params, previousData)); return { previousData }; }, onError: (error2, _, context2) => onMutateError(error2, queryKey, context2), onSettled: () => queryClient.invalidateQueries({ queryKey }) }); const { mutate, isPending, error } = mutation; async function mutateAsync(params) { return await mutation.mutateAsync(params); } return { ...mutation, mutate, mutateAsync, isPending, error }; } // src/hooks/accounts/use-unlink-account.ts function useUnlinkAccount(authClient, options) { const { listAccountsKey: queryKey } = react.useContext(AuthQueryContext); return useAuthMutation({ queryKey, mutationFn: authClient.unlinkAccount, options }); } function useListDeviceSessions(authClient, options) { const { listDeviceSessionsKey: queryKey } = react.useContext(AuthQueryContext); return useAuthQuery({ authClient, queryKey, queryFn: authClient.multiSession.listDeviceSessions, options }); } function useRevokeDeviceSession(authClient, options) { const { listDeviceSessionsKey: queryKey } = react.useContext(AuthQueryContext); return useAuthMutation({ queryKey, mutationFn: authClient.multiSession.revoke, options }); } function useSetActiveSession(authClient, options) { const queryClient = reactQuery.useQueryClient(); const { onMutateError } = useOnMutateError(); const context = react.useContext(AuthQueryContext); const { listDeviceSessionsKey: queryKey } = { ...context, ...options }; const mutation = reactQuery.useMutation({ mutationFn: ({ fetchOptions = { throw: true }, ...params }) => authClient.multiSession.setActive({ fetchOptions, ...params }), onError: (error) => onMutateError(error, queryKey), onSettled: () => queryClient.clear() }); const { mutate: setActiveSession, mutateAsync: setActiveSessionAsync, isPending: setActiveSessionPending, error: setActiveSessionError } = mutation; return { ...mutation, setActiveSession, setActiveSessionAsync, setActiveSessionPending, setActiveSessionError }; } function useDeletePasskey(authClient, options) { const { listPasskeysKey: queryKey } = react.useContext(AuthQueryContext); return useAuthMutation({ queryKey, mutationFn: authClient.passkey.deletePasskey, options }); } function useListPasskeys(authClient, options) { const { listPasskeysKey: queryKey } = react.useContext(AuthQueryContext); return useAuthQuery({ authClient, queryKey, queryFn: authClient.passkey.listUserPasskeys, options }); } function useUpdateUser(authClient, options) { const { sessionKey: queryKey } = react.useContext(AuthQueryContext); return useAuthMutation({ queryKey, mutationFn: authClient.updateUser, optimisticData: (params, previousSession) => ({ ...previousSession, user: { ...previousSession.user, ...params } }), options }); } function useListSessions(authClient, options) { const { listSessionsKey: queryKey } = react.useContext(AuthQueryContext); return useAuthQuery({ authClient, queryKey, queryFn: authClient.listSessions, options }); } function useRevokeOtherSessions(authClient, options) { const { listSessionsKey: queryKey } = react.useContext(AuthQueryContext); const { data: sessionData } = useSession(authClient); return useAuthMutation({ queryKey, mutationFn: authClient.revokeOtherSessions, options }); } function useRevokeSession(authClient, options) { const { listSessionsKey: queryKey } = react.useContext(AuthQueryContext); return useAuthMutation({ queryKey, mutationFn: authClient.revokeSession, options }); } function useRevokeSessions(authClient, options) { const { listSessionsKey: queryKey } = react.useContext(AuthQueryContext); return useAuthMutation({ queryKey, mutationFn: authClient.revokeSessions, options }); } var decodeJwt = (token) => { const decode = (data) => { if (typeof Buffer === "undefined") { return atob(data); } return Buffer.from(data, "base64").toString(); }; const parts = token.split(".").map((part) => decode(part.replace(/-/g, "+").replace(/_/g, "/"))); return JSON.parse(parts[1]); }; function useToken(authClient, options) { const { data: sessionData } = useSession(authClient, options); const { tokenKey, tokenQueryOptions, queryOptions } = react.useContext(AuthQueryContext); const mergedOptions = { ...queryOptions, ...tokenQueryOptions, ...options }; const queryResult = useAuthQuery({ authClient, queryKey: tokenKey, queryFn: ({ fetchOptions }) => authClient.$fetch("/token", fetchOptions), options: { enabled: !!sessionData && (mergedOptions.enabled ?? true) } }); const { data, refetch, ...rest } = queryResult; const payload = react.useMemo(() => data ? decodeJwt(data.token) : null, [data]); react.useEffect(() => { if (!(data == null ? void 0 : data.token)) return; const payload2 = decodeJwt(data.token); if (!(payload2 == null ? void 0 : payload2.exp)) return; const expiresAt = payload2.exp * 1e3; const expiresIn = expiresAt - Date.now(); const timeout = setTimeout(() => refetch(), expiresIn); return () => clearTimeout(timeout); }, [data, refetch]); const isTokenExpired = react.useCallback(() => { if (!(data == null ? void 0 : data.token)) return true; const payload2 = decodeJwt(data.token); if (!(payload2 == null ? void 0 : payload2.exp)) return true; return payload2.exp < Date.now() / 1e3; }, [data]); react.useEffect(() => { if (!sessionData) return; if ((payload == null ? void 0 : payload.sub) !== sessionData.user.id) { refetch(); } }, [payload, sessionData, refetch]); const tokenData = react.useMemo( () => !sessionData || isTokenExpired() || (sessionData == null ? void 0 : sessionData.user.id) !== (payload == null ? void 0 : payload.sub) ? void 0 : data, [sessionData, isTokenExpired, payload, data] ); return { ...rest, data: tokenData, token: tokenData == null ? void 0 : tokenData.token, payload }; } // src/lib/prefetch-session.ts async function prefetchSession(authClient, queryClient, queryOptions, options) { const { error, data } = await authClient.getSession(); const mergedOptions = { ...queryOptions == null ? void 0 : queryOptions.queryOptions, ...queryOptions == null ? void 0 : queryOptions.sessionQueryOptions, ...options }; await queryClient.prefetchQuery({ ...mergedOptions, queryKey: queryOptions == null ? void 0 : queryOptions.sessionKey, queryFn: () => data }); return { error, data, session: data == null ? void 0 : data.session, user: data == null ? void 0 : data.user }; } // src/lib/create-auth-hooks.ts function createAuthHooks(authClient) { return { useSession: (options) => useSession(authClient, options), usePrefetchSession: (options) => { const queryClient = reactQuery.useQueryClient(); const queryOptions = react.useContext(AuthQueryContext); return { prefetch: () => prefetchSession(authClient, queryClient, queryOptions, options) }; }, useUpdateUser: (options) => useUpdateUser(authClient, options), useToken: (options) => useToken(authClient, options), useAuthQuery: ({ queryKey, queryFn, options }) => useAuthQuery({ authClient, queryKey, queryFn, options }), useListAccounts: (options) => useListAccounts(authClient, options), useUnlinkAccount: () => useUnlinkAccount(authClient), useListSessions: (options) => useListSessions(authClient, options), useRevokeSession: (options) => useRevokeSession(authClient, options), useRevokeSessions: (options) => useRevokeSessions(authClient, options), useRevokeOtherSessions: (options) => useRevokeOtherSessions(authClient, options), useListDeviceSessions: (options) => useListDeviceSessions(authClient, options), useRevokeDeviceSession: (options) => useRevokeDeviceSession(authClient, options), useSetActiveSession: (options) => useSetActiveSession(authClient, options), useListPasskeys: (options) => useListPasskeys(authClient, options), useDeletePasskey: (options) => useDeletePasskey(authClient, options), useAuthMutation }; } // src/lib/create-auth-prefetches.ts function createAuthPrefetches(authClient, queryOptions) { return { prefetchSession: (queryClient, options) => { return prefetchSession( authClient, queryClient, { ...defaultAuthQueryOptions, ...queryOptions }, options ); } }; } exports.AuthQueryContext = AuthQueryContext; exports.AuthQueryProvider = AuthQueryProvider; exports.createAuthHooks = createAuthHooks; exports.createAuthPrefetches = createAuthPrefetches; exports.defaultAuthQueryOptions = defaultAuthQueryOptions; exports.prefetchSession = prefetchSession;