import { Base } from './Base';
import FaableAuthApi from './FaableAuthApi';
import { BroadcastSync } from './lib/broadcast_sync';
import { AuthError } from './lib/errors';
import { Deferred } from './lib/helpers';
import { getSessionFromCookies } from './lib/nextjs';
import { cookieStorageAdapter } from './lib/storage/cookie-storage';
import { AuthFlowType, CallRefreshTokenResult, InitializeResult, OAuthResponse, SignInWithOAuthConnection, Subscription, SupportedStorage } from './lib/types';
import { AuthChangeEvent, AuthResponse, FaableAuthClientConfig } from './lib/types';
import { Session, SignOut } from './lib/types';
import { Lock } from './lock/Lock';
export { cookieStorageAdapter, getSessionFromCookies };
/**
 * The main entry point of the SDK: an isomorphic client bound to a Faable
 * Auth tenant that drives every authentication flow.
 *
 * Prefer creating it through the {@link createClient} factory in app code.
 * Once instantiated it begins loading its session in the background, so you
 * can subscribe to {@link FaableAuthClient.onAuthStateChange | auth-state
 * changes} and trigger sign-ins right away. The most common starting points
 * are the {@link FaableAuthClient.signInWithOauthConnection | sign-in}
 * methods and {@link FaableAuthClient.getSession | getSession}.
 *
 * @category Getting started
 * @see {@link https://faable.com/docs/auth/get-started | Get Started with Faable Auth}
 */
export declare class FaableAuthClient extends Base {
    domainUrl: string;
    tokenIssuer: string;
    redirectUri: string;
    scope?: string;
    audience?: string;
    sessionCheckExpiryDays: number;
    protected initializePromise: Promise<InitializeResult> | null;
    protected _lastInitializeResult: InitializeResult | null;
    protected detectSessionInUrl: boolean;
    protected storageKey: string;
    protected clientId: string;
    protected storage: SupportedStorage;
    protected api: FaableAuthApi;
    protected autoRefreshToken: boolean;
    protected autoRefreshTicker: ReturnType<typeof setInterval> | null;
    protected visibilityChangedCallback: (() => Promise<any>) | null;
    protected refreshingDeferred: Deferred<CallRefreshTokenResult> | null;
    /**
     * Cross-tab broadcaster + local subscriber registry for state changes.
     */
    protected broadcastSync: BroadcastSync;
    protected _session: Session | null;
    /**
     * Initiation flow to use when redirecting to /authorize. Defaults to
     * PKCE in browsers (recommended for SPAs) and implicit otherwise.
     * Distinct from the callback-side flow which is detected from URL params.
     */
    protected flowType: AuthFlowType;
    protected lock: Lock;
    /**
     * Creates a new authentication client bound to a Faable Auth tenant.
     *
     * The constructor kicks off {@link FaableAuthClient.initialize} in the
     * background; you do not need to await anything before calling other methods.
     * Prefer the {@link createClient} factory in app code — it has the same
     * effect with less ceremony.
     *
     * @param config Tenant settings. `domain` and `clientId` are required and
     *   throw synchronously when missing.
     * @example
     * ```ts
     * const auth = new FaableAuthClient({
     *   domain: '<faableauth_domain>',
     *   clientId: '<client_id>',
     *   redirectUri: window.location.origin
     * })
     * ```
     * @see {@link https://faable.com/docs/auth/get-started | Get Started with Faable Auth}
     * @see {@link https://faable.com/docs/auth/clients | Clients}
     * @category Getting started
     */
    constructor(config: FaableAuthClientConfig);
    /**
     * Cached session value last persisted by the client.
     *
     * This is a synchronous accessor that returns whatever the client has
     * already loaded into memory. It can lag behind storage (for example,
     * across tabs before the broadcast event arrives) and never triggers a
     * refresh. Prefer {@link FaableAuthClient.getSession} when you need an
     * authoritative value, especially on the server.
     *
     * @returns The cached {@link Session} or `null` if no user is signed in.
     * @see {@link https://faable.com/docs/auth/oidc/userinfo | UserInfo}
     * @category Sessions
     */
    get session(): Session | null;
    /**
     * Initializes the client session either from the URL or from storage.
     *
     * Automatically called once from the constructor and idempotent — extra
     * calls return the same in-flight promise. Call it explicitly when you
     * need to await an OAuth, magic link, or password-recovery redirect to
     * finish processing so you can surface any returned error.
     *
     * @returns A promise that resolves to `{ error }` — non-null when the URL
     *   carried a failure or storage was corrupt; never throws.
     * @example
     * ```ts
     * const { error } = await auth.initialize()
     * if (error) console.error('Auth redirect failed', error)
     * ```
     * @see {@link https://faable.com/docs/auth/oauth-flows/authorization-code | Authorization Code with PKCE}
     * @category Lifecycle
     */
    initialize(): Promise<InitializeResult>;
    /**
     * The result of the most recent {@link FaableAuthClient.initialize} run, or
     * `null` while the first one is still in flight.
     *
     * The constructor starts `initialize()` in the background and discards the
     * promise; this accessor lets code that cannot await it (for example a
     * React provider effect) read whether the last OAuth/redirect attempt
     * errored, instead of being stuck with a session that silently stays
     * `null`.
     *
     * @category Lifecycle
     */
    get lastInitializeResult(): InitializeResult | null;
    /**
     * Completes an OAuth / magic-link / password-recovery redirect on your
     * callback route and reports the outcome.
     *
     * The SDK already consumes the URL during the `initialize()` it kicks off
     * from the constructor; this is a thin, discoverable wrapper that awaits
     * that same in-flight run (idempotent) so you can:
     *
     * - redirect **only once** the token exchange has finished, and
     * - surface `error` instead of hanging on a "Signing you in…" screen when
     *   the exchange fails (e.g. an expired PKCE verifier).
     *
     * It also returns `returnTo` — the app-side destination you optionally
     * passed to `signInWith*({ returnTo })` — so you don't need a side channel
     * (like `sessionStorage`) to remember where to send the user.
     *
     * @example
     * ```ts
     * // app/callback/page.tsx
     * const { error, returnTo } = await auth.handleRedirectCallback()
     * if (error) showError(error.message)
     * else router.replace(returnTo ?? '/')
     * ```
     * @see {@link FaableAuthClient.initialize}
     * @category Lifecycle
     */
    handleRedirectCallback(): Promise<InitializeResult>;
    /**
     * IMPORTANT:
     * 1. Never throw in this method, as it is called from the constructor
     * 2. Never return a session from this method as it would be cached over
     *    the whole lifetime of the client
     */
    private _initialize;
    /**
     * Gets the session data from a URL string
     */
    private _getSessionFromURL;
    private _exchangeCodeForSession;
    /**
     * Registers callbacks on the browser / platform, which in-turn run
     * algorithms when the browser window/tab are in foreground. On non-browser
     * platforms it assumes always foreground.
     */
    private _handleVisibilityChange;
    /**
     * Callback registered with `window.addEventListener('visibilitychange')`.
     */
    private _onVisibilityChanged;
    /**
     * Recovers the session from LocalStorage and refreshes
     * Note: this method is async to accommodate for AsyncStorage e.g. in React native.
     */
    private _recoverAndRefresh;
    /**
     * Removes any registered visibilitychange callback.
     *
     * @see startAutoRefresh
     * @see stopAutoRefresh
     */
    private _removeVisibilityChangedCallback;
    /**
     * Starts an auto-refresh process in the background. The session is checked
     * every few seconds. Close to the time of expiration a process is started to
     * refresh the session. If refreshing fails it will be retried for as long as
     * necessary.
     *
     * If `autoRefreshToken` is enabled in the client config you don't need to
     * call this function, it will be called for you.
     *
     * On browsers the refresh process works only when the tab/window is in the
     * foreground to conserve resources as well as prevent race conditions and
     * flooding auth with requests. If you call this method any managed
     * visibility change callback will be removed and you must manage visibility
     * changes on your own.
     *
     * On non-browser platforms the refresh process works *continuously* in the
     * background, which may not be desirable. You should hook into your
     * platform's foreground indication mechanism and call these methods
     * appropriately to conserve resources.
     *
     * @example
     * ```ts
     * // React Native / Node: drive the refresh loop on focus events yourself
     * appState.addEventListener('change', state => {
     *   if (state === 'active') auth.startAutoRefresh()
     * })
     * ```
     * @see {@link https://faable.com/docs/auth/oauth-flows/refresh-token | Refresh Token}
     * @category Sessions
     */
    startAutoRefresh(): Promise<void>;
    /**
     * This is the private implementation of {@link #startAutoRefresh}. Use this
     * within the library.
     */
    private _startAutoRefresh;
    /**
     * This is the private implementation of {@link #stopAutoRefresh}. Use this
     * within the library.
     */
    private _stopAutoRefresh;
    /**
     * Runs the auto refresh token tick.
     */
    private _autoRefreshTokenTick;
    private _detectFlowType;
    private _scope;
    private _getUrlForConnection;
    /**
     * Starts an OAuth / social login by redirecting the browser to the tenant's
     * `/authorize` endpoint for the chosen connection.
     *
     * In browsers the SDK redirects the current window unless
     * `skipBrowserRedirect` is `true`. On that redirect success path the returned
     * promise **never resolves** — the pending navigation owns the page, so a
     * loading state you tie to the `await` stays on until unload instead of
     * flashing back. Pass `skipBrowserRedirect: true` to get `{ data: { url } }`
     * back and drive the navigation yourself. PKCE is used by default in
     * browsers, falling back to the implicit flow elsewhere. Prefer
     * `connection_id` when known — the backend resolves it without an extra
     * lookup; `connection` (by name) is kept for legacy tenants.
     *
     * @example
     * ```ts
     * // Redirects the current window; the promise does not resolve on success.
     * await auth.signInWithOauthConnection({
     *   connection: 'google',
     *   redirectTo: 'https://app.example.com/callback'
     * })
     *
     * // Or take over the navigation yourself:
     * const { data } = await auth.signInWithOauthConnection({
     *   connection: 'google',
     *   skipBrowserRedirect: true
     * })
     * window.location.assign(data.url)
     * ```
     * @see {@link https://faable.com/docs/auth/connections | Connections}
     * @see {@link https://faable.com/docs/auth/oauth-flows/authorization-code | Authorization Code with PKCE}
     * @category Sign in
     */
    signInWithOauthConnection(credentials: SignInWithOAuthConnection): Promise<OAuthResponse>;
    /**
     * Signs the user in with a username + password against a database
     * connection on the tenant.
     *
     * The server responds with an HTML form that posts the user back to the
     * tenant's `/login/callback` to complete the OAuth flow; the SDK
     * auto-submits it from the current document. That is why the success path
     * resolves to `{ data: null, error: null }` — the actual session lands on
     * the redirect target, not on this return value. Subscribe with
     * {@link FaableAuthClient.onAuthStateChange} to observe the resulting
     * `SIGNED_IN` event.
     *
     * @example
     * ```ts
     * await auth.signInWithUsernamePassword({
     *   username: 'user@example.com',
     *   password: '••••••••',
     *   redirectTo: 'https://app.example.com/callback'
     * })
     * ```
     * @see {@link https://faable.com/docs/auth/connections | Connections}
     * @category Sign in
     */
    signInWithUsernamePassword(data: {
        username: string;
        password: string;
        redirectTo?: string;
        state?: string;
        audience?: string;
    }): Promise<{
        data: null;
        error: AuthError | null;
    }>;
    /**
     * Registers a new user against the tenant's database connection with an
     * email + password, then signs them in — so an email/password signup form
     * can live entirely in the browser with no backend of your own.
     *
     * This calls the public `POST /dbconnections/signup` endpoint (the Faable
     * analogue of Auth0's `/dbconnections/signup`) which creates the user and
     * its credential in one step, then chains
     * {@link FaableAuthClient.signInWithUsernamePassword} to establish the
     * session.
     *
     * **Auto-login navigates the browser.** Like every interactive
     * username/password login in this SDK, the sign-in step submits a form that
     * round-trips through the auth server, so on success the page redirects to
     * your `redirectTo` and the live session is delivered there by
     * {@link FaableAuthClient.initialize} (and a `SIGNED_IN` event). This method
     * only returns synchronously when signup itself fails, or in non-navigating
     * runtimes (e.g. tests).
     *
     * The user is created with `email_verified: false`; any verification /
     * welcome email is driven by the tenant's account settings.
     *
     * @param data The new user's email, password and optional profile fields.
     * @example
     * ```ts
     * const { error } = await auth.signUp({
     *   email: 'user@example.com',
     *   password: '••••••••',
     *   name: 'Ada Lovelace',
     *   redirectTo: 'https://app.example.com/callback'
     * })
     * if (error) showError(error.message) // e.g. 'email_taken', 'signup_disabled'
     * // otherwise the browser is already navigating to complete the login
     * ```
     * @see {@link https://faable.com/docs/auth/connections | Connections}
     * @category Sign in
     */
    signUp(data: {
        email: string;
        password: string;
        name?: string;
        given_name?: string;
        family_name?: string;
        user_metadata?: Record<string, unknown>;
        connection?: string;
        redirectTo?: string;
        state?: string;
        audience?: string;
    }): Promise<{
        data: null;
        error: AuthError | null;
    }>;
    /**
     * Completes a passwordless login by exchanging an OTP code for a session.
     *
     * Pair this with {@link FaableAuthClient.signInWithPasswordless} called
     * with `type: 'code'`. On success the new session is persisted to storage
     * and a `SIGNED_IN` event is broadcast.
     *
     * @param data The user identifier and the OTP code they received.
     * @example
     * ```ts
     * const { data, error } = await auth.signInWithOtp({
     *   username: 'user@example.com',
     *   otp: '123456'
     * })
     * ```
     * @see {@link https://faable.com/docs/auth/passwordless | Passwordless Authentication}
     * @category Sign in
     */
    signInWithOtp(data: {
        username: string;
        otp: string;
        audience?: string;
    }): Promise<AuthResponse>;
    /**
     * Starts a passwordless login flow by emailing the user either an OTP code
     * or a magic link.
     *
     * - `type: 'code'` sends a short code the user pastes into your UI; finish
     *   the flow with {@link FaableAuthClient.signInWithOtp}.
     * - `type: 'link'` sends a clickable link that lands on your `redirectUri`
     *   with the tokens already attached, processed by
     *   {@link FaableAuthClient.initialize} on page load.
     *
     * @param data The user's email and the delivery mechanism.
     * @example
     * ```ts
     * await auth.signInWithPasswordless({
     *   email: 'user@example.com',
     *   type: 'code'
     * })
     * ```
     * @see {@link https://faable.com/docs/auth/passwordless | Passwordless Authentication}
     * @category Sign in
     */
    signInWithPasswordless(data: {
        email: string;
        type: 'code' | 'link';
        audience?: string;
    }): Promise<{
        data: any;
        error: AuthError | null;
    }>;
    /**
     * Triggers a "change your password" email for a database-connection user.
     *
     * The current session is unaffected — the user clicks the link in the
     * email and completes the reset on the tenant's hosted pages. The promise
     * resolves once the email has been queued.
     *
     * @param params The user's email address.
     * @example
     * ```ts
     * await auth.changePassword({ email: 'user@example.com' })
     * ```
     * @see {@link https://faable.com/docs/auth/connections | Connections}
     * @category Account
     */
    changePassword(params: {
        email: string;
    }): Promise<{
        data: unknown;
        error: AuthError | null;
    }>;
    /**
     * Resolves the signed-in user's id from a session. Prefers the userinfo
     * shape (`user.id`) returned by `/me`, falls back to a `sub` claim on the
     * user object, and finally decodes the access token's `sub` — the id is
     * always present there, so this never reports a live session as missing.
     */
    private _resolveUserId;
    /**
     * Starts a verified email change for the currently signed-in user.
     *
     * The user must be authenticated — the call is made with the session's
     * access token, and the auth server only lets a user change their own
     * email. It creates a verification ticket and emails the user; the change
     * is applied only after they click the link, which the server handles and
     * then redirects to `redirect_uri`. The current session is unaffected until
     * then.
     *
     * @param params The new email plus optional verification policy.
     *   - `verification_mode`: `'new_only'` verifies just the new address;
     *     `'old_and_new'` also requires confirming from the old one. When
     *     omitted the account's default policy applies.
     *   - `redirect_uri`: where the server sends the user after they verify.
     * @example
     * ```ts
     * const { data, error } = await auth.changeEmail({
     *   new_email: 'new@example.com',
     *   redirect_uri: 'https://app.example.com/account'
     * })
     * // data: { status: 'verification_sent', ticket_id, verification_mode }
     * ```
     * @see {@link https://faable.com/docs/auth/change-email | Change Email}
     * @category Account
     */
    changeEmail(params: {
        new_email: string;
        verification_mode?: 'new_only' | 'old_and_new';
        redirect_uri?: string;
    }): Promise<{
        data: unknown;
        error: AuthError | null;
    }>;
    /**
     * Builds the tenant's `/authorize` URL without redirecting the browser.
     *
     * Useful when you need to render a login link, open the page in a popup,
     * or hand the URL to a different runtime (e.g. a webview). For the
     * everyday "click → redirect" flow use {@link FaableAuthClient.authorize}
     * or {@link FaableAuthClient.signInWithOauthConnection}.
     *
     * The redirect target is resolved with this precedence:
     * `options.redirectTo` → `config.redirectUri` → `window.location.origin`.
     *
     * @returns The fully-qualified authorize URL.
     * @example
     * ```ts
     * const url = auth.buildAuthorizeUrl({
     *   connection: 'google',
     *   redirectTo: 'https://app.example.com/callback'
     * })
     * window.open(url, 'login', 'popup')
     * ```
     * @see {@link https://faable.com/docs/auth/oauth-flows/authorization-code | Authorization Code with PKCE}
     * @category Authorize URLs
     */
    buildAuthorizeUrl(options?: {
        connection?: string;
        redirectTo?: string;
        scope?: string;
        response_type?: string;
        audience?: string;
    }): string;
    /**
     * Redirects the current window to the tenant's `/authorize` endpoint.
     *
     * Fire-and-forget: there is no return value because the browser navigates
     * away. The session lands back on your `redirectUri`, where
     * {@link FaableAuthClient.initialize} consumes it on the next page load.
     *
     * @example
     * ```ts
     * auth.authorize({
     *   response_type: 'code',
     *   redirectTo: 'https://app.example.com/callback'
     * })
     * ```
     * @see {@link https://faable.com/docs/auth/oauth-flows/authorization-code | Authorization Code with PKCE}
     * @category Authorize URLs
     */
    authorize(options: {
        redirectTo?: string;
        scope?: string;
        response_type: string;
        audience?: string;
    }): void;
    private _handleConnectionSignIn;
    /**
     * Adopts an externally-provided session into the client.
     *
     * Decodes the access token to find its expiry; refreshes immediately when
     * already expired, otherwise fetches the user info to round-trip the
     * session. Persists the result and broadcasts `SIGNED_IN`. An invalid
     * refresh or access token surfaces as `error` on the returned object.
     *
     * @param currentSession Minimal session shape — an access token and a
     *   refresh token. Other fields are recomputed.
     * @example
     * ```ts
     * // After receiving tokens from a custom server-side handoff
     * await auth.setSession({ access_token, refresh_token })
     * ```
     * @see {@link https://faable.com/docs/auth/oidc/userinfo | UserInfo}
     * @category Sessions
     */
    setSession(currentSession: {
        access_token: string;
        refresh_token: string;
    }): Promise<AuthResponse>;
    /**
     * Returns the session, refreshing it if necessary.
     *
     * The session returned can be `null` if no user is signed in or the last
     * one has logged out.
     *
     * **IMPORTANT:** This method loads values directly from the storage
     * attached to the client. If that storage is based on request cookies (for
     * example, on the server) the values in it may not be authentic and
     * therefore it's strongly advised against using this method and its
     * results in such circumstances — a warning will be emitted when the
     * storage exposes `isServer: true`. Re-fetch the user with a verified
     * call (or verify the access token yourself) before trusting it.
     *
     * @example
     * ```ts
     * const { data, error } = await auth.getSession()
     * if (data.session) console.log(data.session.user)
     * ```
     * @see {@link https://faable.com/docs/auth/oidc/userinfo | UserInfo}
     * @category Sessions
     */
    getSession(): Promise<{
        data: {
            session: Session;
        };
        error: null;
    } | {
        data: {
            session: null;
        };
        error: AuthError;
    } | {
        data: {
            session: null;
        };
        error: null;
    }>;
    /**
     * Use instead of {@link #getSession} inside the library. It is
     * semantically usually what you want, as getting a session involves some
     * processing afterwards that requires only one client operating on the
     * session at once across multiple tabs or processes.
     */
    private _useSession;
    /**
     * NEVER USE DIRECTLY!
     *
     * Always use {@link #_useSession}.
     */
    private __loadSession;
    private _removeSession;
    protected _setSession(currentSession: {
        access_token: string;
        refresh_token: string;
    }): Promise<AuthResponse>;
    /**
     * set currentSession and currentUser
     * process to _startAutoRefreshToken if possible
     */
    private _saveSession;
    private _getUser;
    private _callRefreshToken;
    /**
     * Generates a new JWT.
     * @param refreshToken A valid refresh token that was returned on login.
     */
    private _refreshAccessToken;
    private _notifyAllSubscribers;
    /**
     * Signs the user out and clears the session from storage.
     *
     * In a browser context this removes the persisted session and broadcasts a
     * `SIGNED_OUT` event to every tab listening on the same `storageKey`. The
     * access token JWT itself remains valid until its `exp` — keep that
     * expiry short.
     *
     * Scopes:
     * - `'global'` (default) — invalidate all refresh tokens for the user
     * - `'local'` — only clear this client's storage
     * - `'others'` — invalidate every refresh token except this device's; no
     *   `SIGNED_OUT` event is fired locally
     *
     * @example
     * ```ts
     * await auth.signOut() // global — all sessions for this user
     * await auth.signOut({ scope: 'local' }) // only this device
     * ```
     * @see {@link https://faable.com/docs/auth/oidc/logout | Logout}
     * @category Sign out
     */
    signOut(options?: SignOut): Promise<{
        error: AuthError | null;
    }>;
    protected _signOut({ scope }?: SignOut): Promise<{
        error: AuthError | null;
    }>;
    /**
     * Subscribes to auth-state changes for this client.
     *
     * The callback fires for `INITIAL_SESSION` once shortly after subscribing
     * (so consumers don't have to special-case "no event yet"), and then for
     * every `SIGNED_IN`, `SIGNED_OUT`, `TOKEN_REFRESHED`, `PASSWORD_RECOVERY`,
     * and `USER_UPDATED` event. Events are broadcast across tabs through
     * `BroadcastChannel`, so a sign-in or sign-out in one tab reaches every
     * other tab using the same `storageKey`.
     *
     * @param callback Invoked with the event name and the new session (or
     *   `null` on `SIGNED_OUT`). Can return a promise — the SDK awaits it.
     * @returns `{ data: { subscription } }` — call `subscription.unsubscribe()`
     *   to stop listening.
     * @example
     * ```ts
     * const { data: { subscription } } = auth.onAuthStateChange((event, session) => {
     *   if (event === 'SIGNED_IN') console.log('Welcome', session?.user.email)
     * })
     * // later
     * subscription.unsubscribe()
     * ```
     * @see {@link https://faable.com/docs/auth/get-started | Get Started with Faable Auth}
     * @category Sessions
     */
    onAuthStateChange(callback: (event: AuthChangeEvent, session: Session | null) => void | Promise<void>): {
        data: {
            subscription: Subscription;
        };
    };
    private _emitInitialSession;
    /**
     * Forces a new session by exchanging a refresh token regardless of expiry.
     *
     * Normally the SDK handles refresh transparently via the auto-refresh
     * ticker; call this only when you need to force an immediate refresh —
     * e.g. right after a server-side action that changed the user's claims.
     * Omit `currentSession` to reuse whatever {@link FaableAuthClient.getSession}
     * returns.
     *
     * @param currentSession Optional session shape carrying the refresh token
     *   to exchange. When passed it must include `refresh_token`.
     * @example
     * ```ts
     * const { data, error } = await auth.refreshSession()
     * ```
     * @see {@link https://faable.com/docs/auth/oauth-flows/refresh-token | Refresh Token}
     * @category Sessions
     */
    refreshSession(currentSession?: {
        refresh_token: string;
    }): Promise<AuthResponse>;
    protected _refreshSession(currentSession?: {
        refresh_token: string;
    }): Promise<AuthResponse>;
}
