import { AuthError } from '@auth/core/errors' import type { BuiltInProviderType, ProviderType } from '@auth/core/providers' import type { LoggerInstance, Session } from '@auth/core/types' import { useEffect, useState } from 'react' class ClientFetchError extends AuthError {} export class ClientSessionError extends AuthError {} export interface GetSessionParams { event?: 'storage' | 'timer' | 'hidden' | string triggerEvent?: boolean } export interface AuthClientConfig { baseUrl: string basePath: string credentials: RequestCredentials lastSync: number session: Session | null fetchSession: (params?: GetSessionParams) => Promise } export interface UseSessionOptions { required: R onUnauthenticated?: () => void } export type LiteralUnion = T | (U & Record) export interface ClientSafeProvider { id: LiteralUnion name: string type: ProviderType signinUrl: string callbackUrl: string } export interface SignInOptions extends Record { callbackUrl?: string redirect?: boolean } export interface SignInResponse { error: string | undefined status: number ok: boolean url: string | null } export type SignInAuthorizationParams = | string | string[][] | Record | URLSearchParams export interface SignOutResponse { url: string } export interface SignOutParams { callbackUrl?: string redirect?: R } export interface SessionProviderProps { children: React.ReactNode session?: Session | null refetchInterval?: number refetchOnWindowFocus?: boolean refetchWhenOffline?: false } export type UpdateSession = (data?: any) => Promise export type SessionContextValue = R extends true ? | { update: UpdateSession; data: Session; status: 'authenticated' } | { update: UpdateSession; data: null; status: 'loading' } : | { update: UpdateSession; data: Session; status: 'authenticated' } | { update: UpdateSession data: null status: 'unauthenticated' | 'loading' } export type WindowProps = { url: string title: string width: number height: number } export type AuthState = { status: 'loading' | 'success' | 'errored' error?: string } export async function fetchData( path: string, config: { baseUrl: string basePath: string credentials: RequestCredentials }, logger: LoggerInstance, req: any = {} ): Promise { const url = `${config.baseUrl}${config.basePath}/${path}` try { const options: RequestInit = { headers: { 'Content-Type': 'application/json', ...(req?.headers?.cookie ? { cookie: req.headers.cookie } : {}), }, credentials: config.credentials, } if (req?.body) { options.body = JSON.stringify(req.body) options.method = 'POST' } const res = await fetch(url, options) const data = await res.json() if (!res.ok) { throw data } return data as T } catch (error) { logger.error(new ClientFetchError((error as Error).message, error as any)) return null } } export function useOnline(): boolean { const [isOnline, setIsOnline] = useState( typeof navigator !== 'undefined' ? navigator.onLine : false ) useEffect(() => { const abortController = new AbortController() const { signal } = abortController const setOnline = () => setIsOnline(true) const setOffline = () => setIsOnline(false) window.addEventListener('online', setOnline, { signal }) window.addEventListener('offline', setOffline, { signal }) return () => { abortController.abort() } }, []) return isOnline } export function now(): number { return Math.floor(Date.now() / 1000) } interface ParsedUrl { origin: string host: string path: string base: string toString: () => string } export function parseUrl(url?: string): ParsedUrl { const defaultUrl = 'http://localhost:3000/api/auth' const parsedUrl = new URL(url ? (url.startsWith('http') ? url : `https://${url}`) : defaultUrl) const path = parsedUrl.pathname === '/' ? '/api/auth' : parsedUrl.pathname.replace(/\/$/, '') const base = `${parsedUrl.origin}${path}` return { origin: parsedUrl.origin, host: parsedUrl.host, path, base, toString: () => base, } }