/* eslint-disable @typescript-eslint/no-explicit-any */ import { AuthError } from '@auth/core/errors' import type { BuiltInProviderType, ProviderType } from '@auth/core/providers' import type { LoggerInstance, Session } from '@auth/core/types' import * as React from 'react' /** @todo */ class ClientFetchError extends AuthError {} /** @todo */ export class ClientSessionError extends AuthError {} export interface AuthClientConfig { baseUrl: string basePath: string credentials?: RequestCredentials /** Stores last session response */ _session?: Session | null | undefined /** Used for timestamp since last sycned (in seconds) */ _lastSync: number /** * Stores the `SessionProvider`'s session update method to be able to * trigger session updates from places like `signIn` or `signOut` */ _getSession: (...args: any[]) => any } export interface UseSessionOptions { required: R /** Defaults to `signIn` */ onUnauthenticated?: () => void } // Util type that matches some strings literally, but allows any other string as well. // @source https://github.com/microsoft/TypeScript/issues/29729#issuecomment-832522611 export type LiteralUnion = T | (U & Record) export interface ClientSafeProvider { id: LiteralUnion name: string type: ProviderType signinUrl: string callbackUrl: string } export interface SignInOptions extends Record { /** * Specify to which URL the user will be redirected after signing in. Defaults to the page URL the sign-in is initiated from. * * [Documentation](https://next-auth.js.org/getting-started/client#specifying-a-callbackurl) */ callbackUrl?: string /** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option) */ redirect?: boolean } export interface SignInResponse { error: string | undefined status: number ok: boolean url: string | null } /** * Match `inputType` of `new URLSearchParams(inputType)` * @internal */ export type SignInAuthorizationParams = | string | string[][] | Record | URLSearchParams /** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1) */ export interface SignOutResponse { url: string } export interface SignOutParams { /** [Documentation](https://next-auth.js.org/getting-started/client#specifying-a-callbackurl-1) */ callbackUrl?: string /** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1 */ redirect?: R } /** * If you have session expiry times of 30 days (the default) or more, then you probably don't need to change any of the default options. * * However, if you need to customize the session behavior and/or are using short session expiry times, you can pass options to the provider to customize the behavior of the {@link useSession} hook. */ export interface SessionProviderProps { children: React.ReactNode session?: Session | null baseUrl?: string basePath?: string /** * A time interval (in seconds) after which the session will be re-fetched. * If set to `0` (default), the session is not polled. */ refetchInterval?: number /** * `SessionProvider` automatically refetches the session when the user switches between windows. * This option activates this behaviour if set to `true` (default). */ refetchOnWindowFocus?: boolean /** * Set to `false` to stop polling when the device has no internet access offline (determined by `navigator.onLine`) * * [`navigator.onLine` documentation](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine) */ refetchWhenOffline?: false } export async function fetchData( path: string, config: AuthClientConfig, 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 } } /** @internal */ export function useOnline() { const [isOnline, setIsOnline] = React.useState( typeof navigator !== 'undefined' ? navigator.onLine : false ) const setOnline = () => setIsOnline(true) const setOffline = () => setIsOnline(false) React.useEffect(() => { window.addEventListener('online', setOnline) window.addEventListener('offline', setOffline) return () => { window.removeEventListener('online', setOnline) window.removeEventListener('offline', setOffline) } }, []) return isOnline } /** * Returns the number of seconds elapsed since January 1, 1970 00:00:00 UTC. * @internal */ export function now() { return Math.floor(Date.now() / 1000) } /** * Returns an `URL` like object to make requests/redirects from server-side * @internal */ export function parseUrl(url?: string): { /** @default "http://localhost:3000" */ origin: string /** @default "localhost:3000" */ host: string /** @default "/api/auth" */ path: string /** @default "http://localhost:3000/api/auth" */ base: string /** @default "http://localhost:3000/api/auth" */ toString: () => string } { const defaultUrl = new URL('http://localhost:3000/api/auth') if (url && !url.startsWith('http')) { url = `https://${url}` } const _url = new URL(url ?? defaultUrl) const path = (_url.pathname === '/' ? defaultUrl.pathname : _url.pathname) // Remove trailing slash .replace(/\/$/, '') const base = `${_url.origin}${path}` return { origin: _url.origin, host: _url.host, path, base, toString: () => base, } }