import { FunctionsClient } from "@supabase/functions-js";
import { PostgrestClient } from "@supabase/postgrest-js";
import { RealtimeClient } from "@supabase/realtime-js";
import { StorageClient as SupabaseStorageClient } from "@supabase/storage-js";
import { DEFAULT_GLOBAL_OPTIONS, DEFAULT_DB_OPTIONS, DEFAULT_AUTH_OPTIONS, DEFAULT_REALTIME_OPTIONS } from "./lib/constants.js";
import { fetchWithAuth } from "./lib/fetch.js";
import { applySettingDefaults, validateSupabaseUrl } from "./lib/helpers.js";
import { SupabaseAuthClient } from "./lib/SupabaseAuthClient.js";
/**
 * Supabase Client.
 *
 * An isomorphic Javascript client for interacting with Postgres.
 */ export default class SupabaseClient {
  supabaseUrl;
  supabaseKey;
  /**
   * Supabase Auth allows you to create and manage user sessions for access to data that is secured by access policies.
   */ auth;
  realtime;
  /**
   * Supabase Storage allows you to manage user-generated content, such as photos or videos.
   */ storage;
  realtimeUrl;
  authUrl;
  storageUrl;
  functionsUrl;
  rest;
  storageKey;
  fetch;
  changedAccessToken;
  accessToken;
  headers;
  /**
   * Create a new client for use in the browser.
   * @param supabaseUrl The unique Supabase URL which is supplied when you create a new project in your project dashboard.
   * @param supabaseKey The unique Supabase Key which is supplied when you create a new project in your project dashboard.
   * @param options.db.schema You can switch in between schemas. The schema needs to be on the list of exposed schemas inside Supabase.
   * @param options.auth.autoRefreshToken Set to "true" if you want to automatically refresh the token before expiring.
   * @param options.auth.persistSession Set to "true" if you want to automatically save the user session into local storage.
   * @param options.auth.detectSessionInUrl Set to "true" if you want to automatically detects OAuth grants in the URL and signs in the user.
   * @param options.realtime Options passed along to realtime-js constructor.
   * @param options.storage Options passed along to the storage-js constructor.
   * @param options.global.fetch A custom fetch implementation.
   * @param options.global.headers Any additional headers to send with each network request.
   */ constructor(supabaseUrl, supabaseKey, options){
    this.supabaseUrl = supabaseUrl;
    this.supabaseKey = supabaseKey;
    const baseUrl = validateSupabaseUrl(supabaseUrl);
    if (!supabaseKey) throw new Error('supabaseKey is required.');
    this.realtimeUrl = new URL('realtime/v1', baseUrl);
    this.realtimeUrl.protocol = this.realtimeUrl.protocol.replace('http', 'ws');
    this.authUrl = new URL('auth/v1', baseUrl);
    this.storageUrl = new URL('storage/v1', baseUrl);
    this.functionsUrl = new URL('functions/v1', baseUrl);
    // default storage key uses the supabase project ref as a namespace
    const defaultStorageKey = `sb-${baseUrl.hostname.split('.')[0]}-auth-token`;
    const DEFAULTS = {
      db: DEFAULT_DB_OPTIONS,
      realtime: DEFAULT_REALTIME_OPTIONS,
      auth: {
        ...DEFAULT_AUTH_OPTIONS,
        storageKey: defaultStorageKey
      },
      global: DEFAULT_GLOBAL_OPTIONS
    };
    const settings = applySettingDefaults(options ?? {}, DEFAULTS);
    this.storageKey = settings.auth.storageKey ?? '';
    this.headers = settings.global.headers ?? {};
    if (!settings.accessToken) {
      this.auth = this._initSupabaseAuthClient(settings.auth ?? {}, this.headers, settings.global.fetch);
    } else {
      this.accessToken = settings.accessToken;
      this.auth = new Proxy({}, {
        get: (_, prop)=>{
          throw new Error(`@supabase/supabase-js: Supabase Client is configured with the accessToken option, accessing supabase.auth.${String(prop)} is not possible`);
        }
      });
    }
    this.fetch = fetchWithAuth(supabaseKey, this._getAccessToken.bind(this), settings.global.fetch);
    this.realtime = this._initRealtimeClient({
      headers: this.headers,
      accessToken: this._getAccessToken.bind(this),
      ...settings.realtime
    });
    this.rest = new PostgrestClient(new URL('rest/v1', baseUrl).href, {
      headers: this.headers,
      schema: settings.db.schema,
      fetch: this.fetch
    });
    this.storage = new SupabaseStorageClient(this.storageUrl.href, this.headers, this.fetch, options?.storage);
    if (!settings.accessToken) {
      this._listenForAuthEvents();
    }
  }
  /**
   * Supabase Functions allows you to deploy and invoke edge functions.
   */ get functions() {
    return new FunctionsClient(this.functionsUrl.href, {
      headers: this.headers,
      customFetch: this.fetch
    });
  }
  /**
   * Perform a query on a table or a view.
   *
   * @param relation - The table or view name to query
   */ from(relation) {
    return this.rest.from(relation);
  }
  // NOTE: signatures must be kept in sync with PostgrestClient.schema
  /**
   * Select a schema to query or perform an function (rpc) call.
   *
   * The schema needs to be on the list of exposed schemas inside Supabase.
   *
   * @param schema - The schema to query
   */ schema(schema) {
    return this.rest.schema(schema);
  }
  // NOTE: signatures must be kept in sync with PostgrestClient.rpc
  /**
   * Perform a function call.
   *
   * @param fn - The function name to call
   * @param args - The arguments to pass to the function call
   * @param options - Named parameters
   * @param options.head - When set to `true`, `data` will not be returned.
   * Useful if you only need the count.
   * @param options.get - When set to `true`, the function will be called with
   * read-only access mode.
   * @param options.count - Count algorithm to use to count rows returned by the
   * function. Only applicable for [set-returning
   * functions](https://www.postgresql.org/docs/current/functions-srf.html).
   *
   * `"exact"`: Exact but slow count algorithm. Performs a `COUNT(*)` under the
   * hood.
   *
   * `"planned"`: Approximated but fast count algorithm. Uses the Postgres
   * statistics under the hood.
   *
   * `"estimated"`: Uses exact count for low numbers and planned count for high
   * numbers.
   */ rpc(fn, args = {}, options = {}) {
    return this.rest.rpc(fn, args, options);
  }
  /**
   * Creates a Realtime channel with Broadcast, Presence, and Postgres Changes.
   *
   * @param {string} name - The name of the Realtime channel.
   * @param {Object} opts - The options to pass to the Realtime channel.
   *
   */ channel(name, opts = {
    config: {}
  }) {
    return this.realtime.channel(name, opts);
  }
  /**
   * Returns all Realtime channels.
   */ getChannels() {
    return this.realtime.getChannels();
  }
  /**
   * Unsubscribes and removes Realtime channel from Realtime client.
   *
   * @param {RealtimeChannel} channel - The name of the Realtime channel.
   *
   */ removeChannel(channel) {
    return this.realtime.removeChannel(channel);
  }
  /**
   * Unsubscribes and removes all Realtime channels from Realtime client.
   */ removeAllChannels() {
    return this.realtime.removeAllChannels();
  }
  async _getAccessToken() {
    if (this.accessToken) {
      return await this.accessToken();
    }
    const { data } = await this.auth.getSession();
    return data.session?.access_token ?? this.supabaseKey;
  }
  _initSupabaseAuthClient({ autoRefreshToken, persistSession, detectSessionInUrl, storage, userStorage, storageKey, flowType, lock, debug }, headers, fetch) {
    const authHeaders = {
      Authorization: `Bearer ${this.supabaseKey}`,
      apikey: `${this.supabaseKey}`
    };
    return new SupabaseAuthClient({
      url: this.authUrl.href,
      headers: {
        ...authHeaders,
        ...headers
      },
      storageKey: storageKey,
      autoRefreshToken,
      persistSession,
      detectSessionInUrl,
      storage,
      userStorage,
      flowType,
      lock,
      debug,
      fetch,
      // auth checks if there is a custom authorizaiton header using this flag
      // so it knows whether to return an error when getUser is called with no session
      hasCustomAuthorizationHeader: Object.keys(this.headers).some((key)=>key.toLowerCase() === 'authorization')
    });
  }
  _initRealtimeClient(options) {
    return new RealtimeClient(this.realtimeUrl.href, {
      ...options,
      params: {
        ...{
          apikey: this.supabaseKey
        },
        ...options?.params
      }
    });
  }
  _listenForAuthEvents() {
    let data = this.auth.onAuthStateChange((event, session)=>{
      this._handleTokenChanged(event, 'CLIENT', session?.access_token);
    });
    return data;
  }
  _handleTokenChanged(event, source, token) {
    if ((event === 'TOKEN_REFRESHED' || event === 'SIGNED_IN') && this.changedAccessToken !== token) {
      this.changedAccessToken = token;
      this.realtime.setAuth(token);
    } else if (event === 'SIGNED_OUT') {
      this.realtime.setAuth();
      if (source == 'STORAGE') this.auth.signOut();
      this.changedAccessToken = undefined;
    }
  }
}
//# sourceMappingURL=SupabaseClient.js.map