'use strict';

var idpHelpers = require('./idpHelpers-uqs0nkKd.js');
var StytchSSRProxy = require('./StytchSSRProxy-9SPT89Z8.js');

class HeadlessIDPClient {
    _networkClient;
    constructor(_networkClient){
        this._networkClient = _networkClient;
    }
    oauthAuthorizeStart = async (data)=>this._networkClient.fetchSDK({
            url: '/idp/oauth/authorize/start',
            method: 'POST',
            body: data
        });
    oauthAuthorizeSubmit = async (data)=>this._networkClient.fetchSDK({
            url: '/idp/oauth/authorize/submit',
            method: 'POST',
            body: data
        });
    oauthLogoutStart = async (data)=>this._networkClient.fetchSDK({
            url: `/oauth/logout/start`,
            method: 'POST',
            body: data
        });
}

class HeadlessImpersonationClient {
    _networkClient;
    _subscriptionService;
    dfpProtectedAuth;
    authenticate;
    constructor(_networkClient, _subscriptionService, dfpProtectedAuth){
        this._networkClient = _networkClient;
        this._subscriptionService = _subscriptionService;
        this.dfpProtectedAuth = dfpProtectedAuth;
        this.authenticate = this._subscriptionService.withUpdateSession(async (data)=>{
            const { dfp_telemetry_id, captcha_token } = await this.dfpProtectedAuth.getDFPTelemetryIDAndCaptcha();
            return this._networkClient.retriableFetchSDK({
                url: '/impersonation/authenticate',
                body: {
                    ...data,
                    dfp_telemetry_id,
                    captcha_token
                },
                method: 'POST',
                retryCallback: this.dfpProtectedAuth.retryWithCaptchaAndDFP
            });
        });
    }
}

let HeadlessOAuthClient$1 = class HeadlessOAuthClient {
    _networkClient;
    _subscriptionService;
    _pkceManager;
    _dynamicConfig;
    _config;
    authenticate;
    constructor(_networkClient, _subscriptionService, _pkceManager, _dynamicConfig, _config){
        this._networkClient = _networkClient;
        this._subscriptionService = _subscriptionService;
        this._pkceManager = _pkceManager;
        this._dynamicConfig = _dynamicConfig;
        this._config = _config;
        this.authenticate = this._subscriptionService.withUpdateSession(async (token, options)=>{
            const keyPair = await this._pkceManager.getPKPair();
            if (!keyPair) {
                idpHelpers.logger.warn('No code verifier found in local storage for OAuth flow.\n' + 'Consider using stytch.oauth.$provider.start() to add PKCE to your OAuth flows for added security.\n' + 'See https://stytch.com/docs/oauth#guides_pkce for more information.');
            }
            const resp = await this._networkClient.fetchSDK({
                url: '/oauth/authenticate',
                method: 'POST',
                body: {
                    token,
                    code_verifier: keyPair?.code_verifier,
                    ...options
                }
            });
            this._pkceManager.clearPKPair();
            return idpHelpers.omitUser(resp);
        });
    }
    google = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Google)
    };
    apple = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Apple)
    };
    microsoft = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Microsoft)
    };
    github = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Github)
    };
    gitlab = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.GitLab)
    };
    facebook = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Facebook)
    };
    discord = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Discord)
    };
    salesforce = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Salesforce)
    };
    slack = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Slack)
    };
    amazon = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Amazon)
    };
    bitbucket = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Bitbucket)
    };
    linkedin = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.LinkedIn)
    };
    coinbase = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Coinbase)
    };
    twitch = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Twitch)
    };
    twitter = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Twitter)
    };
    tiktok = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.TikTok)
    };
    snapchat = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Snapchat)
    };
    figma = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Figma)
    };
    yahoo = {
        start: this.startOAuthFlow(StytchSSRProxy.OAuthProviders.Yahoo)
    };
    async attach(provider) {
        return await this._networkClient.fetchSDK({
            url: '/oauth/attach',
            method: 'POST',
            body: {
                provider
            }
        });
    }
    async getBaseApiUrl() {
        const { cnameDomain } = await this._dynamicConfig;
        if (cnameDomain) {
            // The API returns cname domains without a protocol - assume HTTPS
            return `https://${cnameDomain}`;
        }
        // Our TestAPIURL and LiveAPIURL should have the HTTPS protocol already attached
        // Don't add it twice!
        if (idpHelpers.isTestPublicToken(this._config.publicToken)) {
            return this._config.testAPIURL;
        }
        return this._config.liveAPIURL;
    }
    startOAuthFlow(providerType) {
        return async ({ login_redirect_url, signup_redirect_url, custom_scopes, provider_params, oauth_attach_token } = {})=>{
            const { cnameDomain, pkceRequiredForOAuth } = await this._dynamicConfig;
            const baseURL = await this.getBaseApiUrl();
            this._networkClient.logEvent({
                name: 'start_oauth_flow',
                details: {
                    provider_type: providerType,
                    custom_scopes,
                    cname_domain: cnameDomain,
                    pkce: pkceRequiredForOAuth,
                    provider_params
                }
            });
            const oauthUrl = new URL(`${baseURL}/v1/public/oauth/${providerType}/start`);
            oauthUrl.searchParams.set('public_token', this._config.publicToken);
            if (pkceRequiredForOAuth) {
                const keyPair = await this._pkceManager.startPKCETransaction();
                oauthUrl.searchParams.set('code_challenge', keyPair.code_challenge);
            } else {
                this._pkceManager.clearPKPair();
            }
            if (custom_scopes) {
                oauthUrl.searchParams.set('custom_scopes', custom_scopes.join(' '));
            }
            if (provider_params) {
                for(const key in provider_params){
                    oauthUrl.searchParams.set('provider_' + key, provider_params[key]);
                }
            }
            if (login_redirect_url) oauthUrl.searchParams.set('login_redirect_url', login_redirect_url);
            if (signup_redirect_url) oauthUrl.searchParams.set('signup_redirect_url', signup_redirect_url);
            if (oauth_attach_token) oauthUrl.searchParams.set('oauth_attach_token', oauth_attach_token);
            this.navigate(oauthUrl);
        };
    }
    // Public so it can be mocked in tests
    navigate(url) {
        window.location.href = url.toString();
    }
};

class HeadlessOTPClient {
    _networkClient;
    _subscriptionService;
    executeRecaptcha;
    dfpProtectedAuth;
    authenticate;
    constructor(_networkClient, _subscriptionService, executeRecaptcha = ()=>Promise.resolve(undefined), dfpProtectedAuth){
        this._networkClient = _networkClient;
        this._subscriptionService = _subscriptionService;
        this.executeRecaptcha = executeRecaptcha;
        this.dfpProtectedAuth = dfpProtectedAuth;
        this.authenticate = this._subscriptionService.withUpdateSession(async (code, method_id, options)=>{
            const { dfp_telemetry_id, captcha_token } = await this.dfpProtectedAuth.getDFPTelemetryIDAndCaptcha();
            const requestBody = {
                token: code,
                method_id,
                dfp_telemetry_id,
                captcha_token,
                ...options
            };
            const resp = await this._networkClient.retriableFetchSDK({
                url: '/otps/authenticate',
                body: requestBody,
                method: 'POST',
                retryCallback: this.dfpProtectedAuth.retryWithCaptchaAndDFP
            });
            return idpHelpers.omitUser(resp);
        });
    }
    sms = {
        loginOrCreate: async (phone_number, options)=>{
            const { dfp_telemetry_id, captcha_token } = await this.dfpProtectedAuth.getDFPTelemetryIDAndCaptcha();
            const requestBody = {
                ...options,
                phone_number,
                captcha_token,
                dfp_telemetry_id
            };
            return this._networkClient.retriableFetchSDK({
                url: '/otps/sms/login_or_create',
                body: requestBody,
                method: 'POST',
                retryCallback: this.dfpProtectedAuth.retryWithCaptchaAndDFP
            });
        },
        send: async (phone_number, options)=>{
            const { dfp_telemetry_id, captcha_token } = await this.dfpProtectedAuth.getDFPTelemetryIDAndCaptcha();
            const requestBody = {
                ...options,
                phone_number,
                captcha_token,
                dfp_telemetry_id
            };
            const isLoggedIn = !!this._subscriptionService.getSession();
            const endpoint = isLoggedIn ? '/otps/sms/send/secondary' : '/otps/sms/send/primary';
            return this._networkClient.retriableFetchSDK({
                url: endpoint,
                body: requestBody,
                method: 'POST',
                retryCallback: this.dfpProtectedAuth.retryWithCaptchaAndDFP
            });
        }
    };
    whatsapp = {
        loginOrCreate: async (phone_number, options)=>{
            const { dfp_telemetry_id, captcha_token } = await this.dfpProtectedAuth.getDFPTelemetryIDAndCaptcha();
            const requestBody = {
                ...options,
                phone_number,
                dfp_telemetry_id,
                captcha_token
            };
            return this._networkClient.retriableFetchSDK({
                url: '/otps/whatsapp/login_or_create',
                body: requestBody,
                method: 'POST',
                retryCallback: this.dfpProtectedAuth.retryWithCaptchaAndDFP
            });
        },
        send: async (phone_number, options)=>{
            const { dfp_telemetry_id, captcha_token } = await this.dfpProtectedAuth.getDFPTelemetryIDAndCaptcha();
            const requestBody = {
                ...options,
                phone_number,
                captcha_token,
                dfp_telemetry_id
            };
            const isLoggedIn = !!this._subscriptionService.getSession();
            const endpoint = isLoggedIn ? '/otps/whatsapp/send/secondary' : '/otps/whatsapp/send/primary';
            return this._networkClient.retriableFetchSDK({
                url: endpoint,
                body: requestBody,
                method: 'POST',
                retryCallback: this.dfpProtectedAuth.retryWithCaptchaAndDFP
            });
        }
    };
    email = {
        loginOrCreate: async (email, options)=>{
            const { dfp_telemetry_id, captcha_token } = await this.dfpProtectedAuth.getDFPTelemetryIDAndCaptcha();
            const requestBody = {
                ...options,
                email,
                captcha_token,
                dfp_telemetry_id
            };
            return this._networkClient.retriableFetchSDK({
                url: '/otps/email/login_or_create',
                body: requestBody,
                method: 'POST',
                retryCallback: this.dfpProtectedAuth.retryWithCaptchaAndDFP
            });
        },
        send: async (email, options)=>{
            const captcha_token = await this.executeRecaptcha();
            const requestBody = {
                ...options,
                email,
                captcha_token
            };
            const isLoggedIn = !!this._subscriptionService.getSession();
            const endpoint = isLoggedIn ? '/otps/email/send/secondary' : '/otps/email/send/primary';
            return this._networkClient.fetchSDK({
                url: endpoint,
                body: requestBody,
                method: 'POST'
            });
        }
    };
}

class HeadlessRBACClient {
    _subscriptionService;
    cachedPolicy;
    policyPromise;
    constructor(cachedConfig, dynamicConfig, _subscriptionService){
        this._subscriptionService = _subscriptionService;
        this.cachedPolicy = cachedConfig.rbacPolicy ? idpHelpers.RBACPolicy.fromJSON(cachedConfig.rbacPolicy) : null;
        this.policyPromise = dynamicConfig.then((data)=>{
            if (!data.rbacPolicy) {
                idpHelpers.logger.error('Unable to retrieve RBAC policy from servers. Assuming caller has no permissions.');
                return new idpHelpers.RBACPolicy([], []);
            }
            // Update the existing policy too, so isAuthorizedSync will be up-to-date
            this.cachedPolicy = idpHelpers.RBACPolicy.fromJSON(data.rbacPolicy);
            return this.cachedPolicy;
        });
    }
    allPermissions() {
        return this.policyPromise.then((policy)=>policy.allPermissionsForCaller(this.roleIds()));
    }
    isAuthorizedSync = (resourceId, action)=>{
        return !!this.cachedPolicy?.callerIsAuthorized(this.roleIds(), resourceId, action);
    };
    isAuthorized = (resourceId, action)=>{
        return this.policyPromise.then((policy)=>policy.callerIsAuthorized(this.roleIds(), resourceId, action));
    };
    roleIds() {
        const user = this._subscriptionService.getUser();
        if (!user) {
            return [];
        }
        // Although user.roles is guaranteed to exist for fresh data, there is a chance
        // that the user stored in localstorage clientside comes from before roles were added to
        // the API response - in which case user.roles will be undefined and this will crash
        // TODO: [AUTH-2294] We can safely remove this ~3mos after RBAC is released
        return user.roles ?? [];
    }
}

class HeadlessSessionClient {
    _networkClient;
    _subscriptionService;
    authenticate;
    exchangeAccessToken;
    attest;
    constructor(_networkClient, _subscriptionService){
        this._networkClient = _networkClient;
        this._subscriptionService = _subscriptionService;
        this.authenticate = this._subscriptionService.withUpdateSession(this._authenticate);
        this.exchangeAccessToken = this._subscriptionService.withUpdateSession(async (data)=>{
            const resp = await this._networkClient.fetchSDK({
                url: '/sessions/exchange_access_token',
                body: data,
                method: 'POST'
            });
            return idpHelpers.omitUser(resp);
        });
        this.attest = this._subscriptionService.withUpdateSession(async (data)=>{
            return this._networkClient.fetchSDK({
                url: '/sessions/attest',
                body: data,
                method: 'POST'
            });
        });
    }
    getSync = ()=>{
        return this._subscriptionService.getSession();
    };
    getInfo = ()=>{
        const session = this.getSync();
        const fromCache = this._subscriptionService.getFromCache();
        return {
            session,
            fromCache
        };
    };
    onChange = (callback)=>{
        let lastVal = this._subscriptionService.getSession();
        const listener = (state)=>{
            if (state?.session !== lastVal) {
                lastVal = state?.session ?? null;
                callback(lastVal);
            }
        };
        return this._subscriptionService.subscribeToState(listener);
    };
    revoke = async (options)=>{
        /**
     * Revoke destroys the local state if the API request is successful
     * or if we return an unrecoverable error (user is unauthenticated)
     * or if the developer passses in a forceClear option.
     * If the API request returns a recoverable error (the user is offline),
     * we do not destroy the local state to let the developer manually add retry
     * logic to call revoke again.
     */ try {
            const resp = await this._networkClient.fetchSDK({
                url: `/sessions/revoke`,
                method: 'POST'
            });
            this._subscriptionService.destroyState();
            return resp;
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        } catch (error) {
            if (options?.forceClear) {
                this._subscriptionService.destroyState();
            } else if (StytchSSRProxy.UNRECOVERABLE_ERROR_TYPES.includes(error.error_type)) {
                this._subscriptionService.destroyState();
            }
            throw error;
        }
    };
    _authenticate = async (options)=>{
        const initialSession = this._subscriptionService.getSession();
        const isSessionStale = ()=>initialSession?.session_id !== this._subscriptionService.getSession()?.session_id;
        try {
            const requestBody = {
                session_duration_minutes: options?.session_duration_minutes
            };
            const resp = await this._networkClient.fetchSDK({
                url: '/sessions/authenticate',
                body: requestBody,
                method: 'POST'
            });
            if (isSessionStale()) {
                // [SDK-1336] The session was updated out from under us while the
                // request was in flight; discard the response and retry
                return this._authenticate(options);
            }
            return idpHelpers.omitUser(resp);
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        } catch (error) {
            if (isSessionStale()) {
                // [SDK-1336] The session was updated out from under us while the
                // request was in flight; discard the response and retry
                return this._authenticate(options);
            }
            if (StytchSSRProxy.UNRECOVERABLE_ERROR_TYPES.includes(error.error_type)) {
                this._subscriptionService.destroySession();
            }
            throw error;
        }
    };
    getTokens() {
        return this._subscriptionService.getTokens();
    }
    updateSession(tokens) {
        this._subscriptionService.updateTokens(tokens);
    }
}

class HeadlessTOTPClient {
    _networkClient;
    _subscriptionService;
    dfpProtectedAuth;
    authenticate;
    recover;
    constructor(_networkClient, _subscriptionService, dfpProtectedAuth){
        this._networkClient = _networkClient;
        this._subscriptionService = _subscriptionService;
        this.dfpProtectedAuth = dfpProtectedAuth;
        this.authenticate = this._subscriptionService.withUpdateSession(async (options)=>{
            const { dfp_telemetry_id, captcha_token } = await this.dfpProtectedAuth.getDFPTelemetryIDAndCaptcha();
            const resp = await this._networkClient.retriableFetchSDK({
                url: '/totps/authenticate',
                method: 'POST',
                body: {
                    session_duration_minutes: options.session_duration_minutes,
                    totp_code: options.totp_code,
                    captcha_token,
                    dfp_telemetry_id
                },
                retryCallback: this.dfpProtectedAuth.retryWithCaptchaAndDFP
            });
            return idpHelpers.omitUser(resp);
        });
        this.recover = this._subscriptionService.withUpdateSession(async (options)=>{
            const { dfp_telemetry_id, captcha_token } = await this.dfpProtectedAuth.getDFPTelemetryIDAndCaptcha();
            const resp = await this._networkClient.retriableFetchSDK({
                url: '/totps/recover',
                method: 'POST',
                body: {
                    session_duration_minutes: options.session_duration_minutes,
                    recovery_code: options.recovery_code,
                    captcha_token,
                    dfp_telemetry_id
                },
                retryCallback: this.dfpProtectedAuth.retryWithCaptchaAndDFP
            });
            return idpHelpers.omitUser(resp);
        });
    }
    async create(options) {
        const resp = await this._networkClient.fetchSDK({
            url: '/totps',
            method: 'POST',
            body: {
                expiration_minutes: options.expiration_minutes
            }
        });
        this._subscriptionService.updateUser(resp.__user);
        return idpHelpers.omitUser(resp);
    }
    async recoveryCodes() {
        return this._networkClient.fetchSDK({
            url: '/totps/recovery_codes',
            method: 'POST'
        });
    }
}

class HeadlessUserClient {
    _networkClient;
    _subscriptionService;
    constructor(_networkClient, _subscriptionService){
        this._networkClient = _networkClient;
        this._subscriptionService = _subscriptionService;
    }
    get = async ()=>{
        const resp = await this._networkClient.fetchSDK({
            url: '/users/me',
            method: 'GET'
        });
        const user = idpHelpers.removeResponseCommon(resp);
        this._subscriptionService.updateUser(user);
        return user;
    };
    getSync = ()=>{
        return this._subscriptionService.getUser();
    };
    getInfo = ()=>({
            user: this.getSync(),
            fromCache: this._subscriptionService.getFromCache()
        });
    update = async (options)=>{
        const resp = await this._networkClient.fetchSDK({
            url: '/users/me',
            body: options,
            method: 'PUT'
        });
        const user = idpHelpers.removeResponseCommon(resp.__user);
        this._subscriptionService.updateUser(user);
        return idpHelpers.omitUser(resp);
    };
    deleteEmail = async (emailId)=>{
        const resp = await this._networkClient.fetchSDK({
            url: `/users/emails/${emailId}`,
            method: 'DELETE'
        });
        const user = idpHelpers.removeResponseCommon(resp.__user);
        this._subscriptionService.updateUser(user);
        return idpHelpers.omitUser(resp);
    };
    deletePhoneNumber = async (phoneId)=>{
        const resp = await this._networkClient.fetchSDK({
            url: `/users/phone_numbers/${phoneId}`,
            method: 'DELETE'
        });
        const user = idpHelpers.removeResponseCommon(resp.__user);
        this._subscriptionService.updateUser(user);
        return idpHelpers.omitUser(resp);
    };
    deleteTOTP = async (totpId)=>{
        const resp = await this._networkClient.fetchSDK({
            url: `/users/totps/${totpId}`,
            method: 'DELETE'
        });
        const user = idpHelpers.removeResponseCommon(resp.__user);
        this._subscriptionService.updateUser(user);
        return idpHelpers.omitUser(resp);
    };
    deleteCryptoWallet = async (cryptoWalletId)=>{
        const resp = await this._networkClient.fetchSDK({
            url: `/users/crypto_wallets/${cryptoWalletId}`,
            method: 'DELETE'
        });
        const user = idpHelpers.removeResponseCommon(resp.__user);
        this._subscriptionService.updateUser(user);
        return idpHelpers.omitUser(resp);
    };
    deleteOAuthRegistration = async (oauthUserRegistrationId)=>{
        const resp = await this._networkClient.fetchSDK({
            url: `/users/oauth/${oauthUserRegistrationId}`,
            method: 'DELETE'
        });
        const user = idpHelpers.removeResponseCommon(resp.__user);
        this._subscriptionService.updateUser(user);
        return idpHelpers.omitUser(resp);
    };
    deleteWebauthnRegistration = async (webAuthnId)=>{
        const resp = await this._networkClient.fetchSDK({
            url: `/users/webauthn_registrations/${webAuthnId}`,
            method: 'DELETE'
        });
        const user = idpHelpers.removeResponseCommon(resp.__user);
        this._subscriptionService.updateUser(user);
        return idpHelpers.omitUser(resp);
    };
    deleteBiometricRegistration = async (biometricRegistrationId)=>{
        const resp = await this._networkClient.fetchSDK({
            url: `/users/biometric_registrations/${biometricRegistrationId}`,
            method: 'DELETE'
        });
        const user = idpHelpers.removeResponseCommon(resp.__user);
        this._subscriptionService.updateUser(user);
        return idpHelpers.omitUser(resp);
    };
    onChange = (callback)=>{
        let lastVal = this._subscriptionService.getUser();
        const listener = (state)=>{
            if (state?.user !== lastVal) {
                lastVal = state?.user ?? null;
                callback(lastVal);
            }
        };
        return this._subscriptionService.subscribeToState(listener);
    };
    getConnectedApps = async ()=>{
        return await this._networkClient.fetchSDK({
            url: '/users/connected_apps',
            method: 'GET'
        });
    };
    revokedConnectedApp = async (connectedAppId)=>{
        return await this._networkClient.fetchSDK({
            url: `/users/connected_apps/${connectedAppId}/revoke`,
            method: 'POST'
        });
    };
}

// src/webauthn-json/base64url.ts
function base64urlToBuffer(baseurl64String) {
  const padding = "==".slice(0, (4 - baseurl64String.length % 4) % 4);
  const base64String = baseurl64String.replace(/-/g, "+").replace(/_/g, "/") + padding;
  const str = atob(base64String);
  const buffer = new ArrayBuffer(str.length);
  const byteView = new Uint8Array(buffer);
  for (let i = 0; i < str.length; i++) {
    byteView[i] = str.charCodeAt(i);
  }
  return buffer;
}
function bufferToBase64url(buffer) {
  const byteView = new Uint8Array(buffer);
  let str = "";
  for (const charCode of byteView) {
    str += String.fromCharCode(charCode);
  }
  const base64String = btoa(str);
  const base64urlString = base64String.replace(/\+/g, "-").replace(
    /\//g,
    "_"
  ).replace(/=/g, "");
  return base64urlString;
}

// src/webauthn-json/convert.ts
var copyValue = "copy";
var convertValue = "convert";
function convert(conversionFn, schema2, input) {
  if (schema2 === copyValue) {
    return input;
  }
  if (schema2 === convertValue) {
    return conversionFn(input);
  }
  if (schema2 instanceof Array) {
    return input.map((v) => convert(conversionFn, schema2[0], v));
  }
  if (schema2 instanceof Object) {
    const output = {};
    for (const [key, schemaField] of Object.entries(schema2)) {
      if (schemaField.derive) {
        const v = schemaField.derive(input);
        if (v !== void 0) {
          input[key] = v;
        }
      }
      if (!(key in input)) {
        if (schemaField.required) {
          throw new Error(`Missing key: ${key}`);
        }
        continue;
      }
      if (input[key] == null) {
        output[key] = null;
        continue;
      }
      output[key] = convert(
        conversionFn,
        schemaField.schema,
        input[key]
      );
    }
    return output;
  }
}
function derived(schema2, derive) {
  return {
    required: true,
    schema: schema2,
    derive
  };
}
function required(schema2) {
  return {
    required: true,
    schema: schema2
  };
}
function optional(schema2) {
  return {
    required: false,
    schema: schema2
  };
}

// src/webauthn-json/basic/schema.ts
var publicKeyCredentialDescriptorSchema = {
  type: required(copyValue),
  id: required(convertValue),
  transports: optional(copyValue)
};
var simplifiedExtensionsSchema = {
  appid: optional(copyValue),
  appidExclude: optional(copyValue),
  credProps: optional(copyValue)
};
var simplifiedClientExtensionResultsSchema = {
  appid: optional(copyValue),
  appidExclude: optional(copyValue),
  credProps: optional(copyValue)
};
var credentialCreationOptions = {
  publicKey: required({
    rp: required(copyValue),
    user: required({
      id: required(convertValue),
      name: required(copyValue),
      displayName: required(copyValue)
    }),
    challenge: required(convertValue),
    pubKeyCredParams: required(copyValue),
    timeout: optional(copyValue),
    excludeCredentials: optional([publicKeyCredentialDescriptorSchema]),
    authenticatorSelection: optional(copyValue),
    attestation: optional(copyValue),
    extensions: optional(simplifiedExtensionsSchema)
  }),
  signal: optional(copyValue)
};
var publicKeyCredentialWithAttestation = {
  type: required(copyValue),
  id: required(copyValue),
  rawId: required(convertValue),
  authenticatorAttachment: optional(copyValue),
  response: required({
    clientDataJSON: required(convertValue),
    attestationObject: required(convertValue),
    transports: derived(
      copyValue,
      (response) => {
        var _a;
        return ((_a = response.getTransports) == null ? void 0 : _a.call(response)) || [];
      }
    )
  }),
  clientExtensionResults: derived(
    simplifiedClientExtensionResultsSchema,
    (pkc) => pkc.getClientExtensionResults()
  )
};
var credentialRequestOptions = {
  mediation: optional(copyValue),
  publicKey: required({
    challenge: required(convertValue),
    timeout: optional(copyValue),
    rpId: optional(copyValue),
    allowCredentials: optional([publicKeyCredentialDescriptorSchema]),
    userVerification: optional(copyValue),
    extensions: optional(simplifiedExtensionsSchema)
  }),
  signal: optional(copyValue)
};
var publicKeyCredentialWithAssertion = {
  type: required(copyValue),
  id: required(copyValue),
  rawId: required(convertValue),
  authenticatorAttachment: optional(copyValue),
  response: required({
    clientDataJSON: required(convertValue),
    authenticatorData: required(convertValue),
    signature: required(convertValue),
    userHandle: required(convertValue)
  }),
  clientExtensionResults: derived(
    simplifiedClientExtensionResultsSchema,
    (pkc) => pkc.getClientExtensionResults()
  )
};

// src/webauthn-json/basic/api.ts
function createRequestFromJSON(requestJSON) {
  return convert(base64urlToBuffer, credentialCreationOptions, requestJSON);
}
function createResponseToJSON(credential) {
  return convert(
    bufferToBase64url,
    publicKeyCredentialWithAttestation,
    credential
  );
}
async function create(requestJSON) {
  const credential = await navigator.credentials.create(
    createRequestFromJSON(requestJSON)
  );
  return createResponseToJSON(credential);
}
function getRequestFromJSON(requestJSON) {
  return convert(base64urlToBuffer, credentialRequestOptions, requestJSON);
}
function getResponseToJSON(credential) {
  return convert(
    bufferToBase64url,
    publicKeyCredentialWithAssertion,
    credential
  );
}
async function get(requestJSON) {
  const credential = await navigator.credentials.get(
    getRequestFromJSON(requestJSON)
  );
  return getResponseToJSON(credential);
}

class HeadlessWebAuthnClient {
    _networkClient;
    _subscriptionService;
    dfpProtectedAuth;
    register;
    authenticate;
    constructor(_networkClient, _subscriptionService, dfpProtectedAuth){
        this._networkClient = _networkClient;
        this._subscriptionService = _subscriptionService;
        this.dfpProtectedAuth = dfpProtectedAuth;
        this.register = this._subscriptionService.withUpdateSession(async (options)=>{
            const startResp = await this._networkClient.fetchSDK({
                url: '/webauthn/register/start',
                method: 'POST',
                body: {
                    domain: options?.domain ?? window.location.hostname,
                    authenticator_type: options?.authenticator_type ?? undefined,
                    return_passkey_credential_options: options?.is_passkey,
                    override_id: options?.override_id,
                    override_name: options?.override_name,
                    override_display_name: options?.override_display_name,
                    user_agent: navigator.userAgent,
                    use_base64_url_encoding: options?.use_base64_url_encoding
                }
            });
            const publicKeyCredentialCreationOptions = startResp.public_key_credential_creation_options;
            const publicKey = JSON.parse(publicKeyCredentialCreationOptions);
            const credential = await create({
                publicKey: publicKey
            });
            const resp = await this._networkClient.fetchSDK({
                url: '/webauthn/register',
                method: 'POST',
                body: {
                    public_key_credential: JSON.stringify(credential),
                    session_duration_minutes: options?.session_duration_minutes
                }
            });
            return idpHelpers.omitUser(resp);
        });
        this.authenticate = this._subscriptionService.withUpdateSession(async (options)=>{
            const { dfp_telemetry_id, captcha_token } = await this.dfpProtectedAuth.getDFPTelemetryIDAndCaptcha();
            if (options.conditional_mediation) {
                if (!await this.browserSupportsAutofill()) {
                    idpHelpers.logger.error('Browser does not support WebAuthn autofill');
                    return null;
                }
                if (!options.disable_input_check && !this.checkEligibleInputs()) {
                    return null;
                }
            }
            const isLoggedIn = !!this._subscriptionService.getSession();
            const endpoint = isLoggedIn ? '/webauthn/authenticate/start/secondary' : '/webauthn/authenticate/start/primary';
            const startResp = await this._networkClient.fetchSDK({
                url: endpoint,
                method: 'POST',
                body: {
                    domain: options.domain ?? window.location.hostname,
                    return_passkey_credential_options: options?.is_passkey
                }
            });
            const publicKeyCredentialRequestOptions = startResp.public_key_credential_request_options;
            const abortController = new AbortController();
            const credReqOptions = {
                publicKey: JSON.parse(publicKeyCredentialRequestOptions),
                signal: options.signal ?? abortController.signal
            };
            const conditionalMediationCredReqOption = {
                ...credReqOptions,
                mediation: 'conditional'
            };
            const credential = await get(options.conditional_mediation ? conditionalMediationCredReqOption : credReqOptions);
            const authenticationData = await this._networkClient.retriableFetchSDK({
                url: '/webauthn/authenticate',
                method: 'POST',
                body: {
                    public_key_credential: JSON.stringify(credential),
                    session_duration_minutes: options.session_duration_minutes,
                    dfp_telemetry_id,
                    captcha_token
                },
                retryCallback: this.dfpProtectedAuth.retryWithCaptchaAndDFP
            });
            return idpHelpers.omitUser(authenticationData);
        });
    }
    async update(options) {
        const url = '/webauthn/update/' + options.webauthn_registration_id;
        return await this._networkClient.fetchSDK({
            url: url,
            method: 'PUT',
            body: {
                name: options.name
            }
        });
    }
    async browserSupportsAutofill() {
        return await window.PublicKeyCredential?.isConditionalMediationAvailable?.() ?? false;
    }
    checkEligibleInputs = ()=>{
        // Check for an <input> with "webauthn" in its `autocomplete` attribute
        const eligibleInputs = document.querySelectorAll("input[autocomplete*='webauthn']");
        // WebAuthn autofill requires at least one valid input
        if (eligibleInputs.length < 1) {
            idpHelpers.logger.error('No <input> with `"webauthn"` in its `autocomplete` attribute was detected');
            return false;
        }
        return true;
    };
}

class HeadlessOAuthClient extends HeadlessOAuthClient$1 {
    _oneTap;
    constructor(_networkClient, _subscriptionService, _pkceManager, _dynamicConfig, _config, _oneTap){
        super(_networkClient, _subscriptionService, _pkceManager, _dynamicConfig, _config), this._oneTap = _oneTap;
    }
    startOneTap = async (options)=>{
        const clientResult = await this._oneTap.createOneTapClient();
        if (!clientResult.success) {
            throw new Error(`One Tap could not load: ${clientResult.reason}`);
        }
        const { client } = clientResult;
        const onSuccessCallback = this._oneTap.createOnSuccessHandler({
            signupRedirectUrl: options.signup_redirect_url,
            loginRedirectUrl: options.login_redirect_url,
            onSuccess: this._oneTap.redirectOnSuccess
        });
        return client.render({
            style: {
                position: StytchSSRProxy.OneTapPositions.floating
            },
            onOneTapCancelled: options.onOneTapCancelled,
            callback: onSuccessCallback,
            cancelOnTapOutside: options.cancel_on_tap_outside
        });
    };
    googleOneTap = {
        start: this.startOneTap
    };
}

/**
 * A headless client used for invoking the Stytch API.
 * The Stytch Headless Client can be used as a drop-in solution for authentication and session management.
 * Full documentation can be found {@link https://stytch.com/docs/sdks/javascript-sdk online}.
 * @example
 * const stytch = new StytchClient('public-token-<find yours in the stytch dashboard>');
 * stytch.magicLinks.email.loginOrCreate('sandbox@stytch.com', {
 *   login_magic_link_url: 'https://example.com/authenticate',
 *   login_expiration_minutes: 60,
 *   signup_magic_link_url: 'https://example.com/authenticate',
 *   signup_expiration_minutes: 60,
 * });
 */ class StytchClient {
    _subscriptionService;
    _sessionManager;
    _networkClient;
    _dataLayer;
    _stateChangeClient;
    // External API Clients
    user;
    magicLinks;
    session;
    otps;
    oauth;
    cryptoWallets;
    totps;
    webauthn;
    passwords;
    impersonation;
    rbac;
    idp;
    // External methods
    /**
   * Register a callback function to be invoked whenever certain state changes
   * occur, like a user or session object being updated.
   *
   * This is an alternative to more specific methods like `user.onChange` and
   * `session.onChange`. It can be helpful if you want to be notified of related
   * changes to different parts of state at once.
   *
   * If you are only interested in specific state changes, consider using more
   * specific methods like `user.onChange` and `session.onChange` instead.
   */ onStateChange;
    /**
   * Extracts token and token type from the current page URL's query parameters.
   * If the current URL do not have the required query params, this will return null.
   * Otherwise, returns an object { handled: boolean, tokenType: string, token: string }
   */ parseAuthenticateUrl;
    /**
   * Call this method to authenticate the user when the user has been redirected
   * to this page with a token in the query parameters, such as after OAuth
   * or through an email magic link. This method currently supports
   *
   * - Magic links
   * - OAuth
   * - Impersonation
   *
   * Returns null if token or stytch_token_type query parameters are not present,
   * or returns an unhandled result if the token type is not recognized.
   */ authenticateByUrl;
    constructor(publicTokenRaw, options){
        idpHelpers.checkNotSSR();
        // Intentionally check the raw public token so that we can log a correct error message,
        // but then coalesce to an empty string so that the UI components don't fail to render.
        idpHelpers.checkPublicToken(publicTokenRaw);
        const _PUBLIC_TOKEN = publicTokenRaw ?? '';
        // Not casting to InternalStytchClientOptions since InternalStytchClientOptions is currently a superset
        const config = idpHelpers.buildFinalConfig(this.constructor.name, options);
        const usingCustomApiEndpoint = idpHelpers.hasCustomApiEndpoint(_PUBLIC_TOKEN, options);
        this._dataLayer = idpHelpers.getConsumerDataLayer(_PUBLIC_TOKEN, config);
        this._subscriptionService = new idpHelpers.ConsumerSubscriptionService(_PUBLIC_TOKEN, this._dataLayer, {
            usingCustomApiEndpoint
        });
        this._stateChangeClient = new idpHelpers.StateChangeClient(this._subscriptionService, {});
        this.onStateChange = (...args)=>this._stateChangeClient.onStateChange(...args);
        const additionalTelemetryDataFn = ()=>({
                stytch_user_id: this._dataLayer.state?.user?.user_id,
                stytch_session_id: this._dataLayer.state?.session?.session_id
            });
        const passwordsPKCEManager = new idpHelpers.PKCEManager(this._dataLayer, 'passwords');
        const networkClient = new idpHelpers.NetworkClient(_PUBLIC_TOKEN, this._dataLayer, config.endpoints.liveAPIURL, config.endpoints.testAPIURL, additionalTelemetryDataFn);
        this._networkClient = networkClient;
        const bootstrap = new idpHelpers.BootstrapDataManager(_PUBLIC_TOKEN, this._networkClient, this._dataLayer);
        const captcha = new idpHelpers.CaptchaProvider(bootstrap.getAsync());
        const dfpProtectedAuth = new idpHelpers.DFPProtectedAuthProvider(_PUBLIC_TOKEN, config.endpoints.dfpBackendURL, config.endpoints.dfpCdnURL, bootstrap.getAsync(), captcha.executeRecaptcha);
        const clientsideServices = new idpHelpers.ClientsideServicesProvider(config.endpoints.clientsideServicesIframeURL);
        const oneTap = new idpHelpers.OneTapProvider(_PUBLIC_TOKEN, clientsideServices);
        const searchManager = new idpHelpers.SearchDataManager(this._networkClient, dfpProtectedAuth);
        this.user = new HeadlessUserClient(this._networkClient, this._subscriptionService);
        this.session = new HeadlessSessionClient(this._networkClient, this._subscriptionService);
        this.magicLinks = new idpHelpers.HeadlessMagicLinksClient(this._networkClient, this._subscriptionService, new idpHelpers.PKCEManager(this._dataLayer, 'magic_links'), passwordsPKCEManager, bootstrap.getAsync(), dfpProtectedAuth);
        this.otps = new HeadlessOTPClient(this._networkClient, this._subscriptionService, captcha.executeRecaptcha, dfpProtectedAuth);
        this.oauth = new HeadlessOAuthClient(this._networkClient, this._subscriptionService, new idpHelpers.PKCEManager(this._dataLayer, 'oauth'), bootstrap.getAsync(), {
            publicToken: _PUBLIC_TOKEN,
            testAPIURL: config.endpoints.testAPIURL,
            liveAPIURL: config.endpoints.liveAPIURL
        }, oneTap);
        this.cryptoWallets = new idpHelpers.HeadlessCryptoWalletClient(this._networkClient, this._networkClient, this._subscriptionService, captcha.executeRecaptcha, dfpProtectedAuth, bootstrap.getAsync());
        this.totps = new HeadlessTOTPClient(this._networkClient, this._subscriptionService, dfpProtectedAuth);
        this.webauthn = new HeadlessWebAuthnClient(this._networkClient, this._subscriptionService, dfpProtectedAuth);
        this.passwords = new idpHelpers.HeadlessPasswordClient(this._networkClient, this._subscriptionService, passwordsPKCEManager, bootstrap.getAsync(), dfpProtectedAuth);
        this.impersonation = new HeadlessImpersonationClient(this._networkClient, this._subscriptionService, dfpProtectedAuth);
        this.rbac = new HeadlessRBACClient(bootstrap.getSync(), bootstrap.getAsync(), this._subscriptionService);
        this.idp = new HeadlessIDPClient(this._networkClient);
        this._sessionManager = new idpHelpers.SessionManager(this._subscriptionService, this.session, _PUBLIC_TOKEN, {
            keepSessionAlive: config.keepSessionAlive
        });
        // If the session does not exist in localstorage (like if we are in an iframe)
        // then the cookie might still be set and we can retrieve the session via an API call.
        // If a custom API endpoint is being used, the session may be being managed by
        // HttpOnly cookies, which we can't detect.
        if (usingCustomApiEndpoint || this._dataLayer.readSessionCookie().session_token) {
            this._sessionManager.performBackgroundRefresh();
        }
        this._networkClient.logEvent({
            name: 'sdk_instance_instantiated',
            details: {
                event_callback_registered: false,
                error_callback_registered: false,
                success_callback_registered: false
            }
        });
        bootstrap.getAsync().then((bootstrapData)=>{
            if (bootstrapData.vertical === idpHelpers.VERTICAL_B2B) {
                idpHelpers.logger.error('This application is using a Stytch client for Consumer projects, but the public token is for a Stytch B2B project. Use a B2B Stytch client instead, or verify that the public token is correct.');
            }
        });
        const { authenticateByUrl, parseAuthenticateUrl } = idpHelpers.createAuthUrlHandler({
            magic_links: (token, options)=>this.magicLinks.authenticate(token, options),
            oauth: (token, options)=>this.oauth.authenticate(token, options),
            impersonation: (token)=>this.impersonation.authenticate({
                    impersonation_token: token
                })
        });
        this.authenticateByUrl = authenticateByUrl;
        this.parseAuthenticateUrl = parseAuthenticateUrl;
        StytchSSRProxy.writeB2CInternals(this, {
            bootstrap,
            clientsideServices,
            captcha,
            oneTap,
            searchManager,
            publicToken: _PUBLIC_TOKEN,
            dataLayer: this._dataLayer,
            networkClient: this._networkClient
        });
    }
}

/**
 * Creates a Headless Stytch client object to call the stytch APIs.
 * The Stytch client is not available serverside.
 * @example
 * const stytch = createStytchClient('public-token-<find yours in the stytch dashboard>')
 *
 * return (
 *   <StytchProvider stytch={stytch}>
 *     <App />
 *   </StytchProvider>
 * )
 * @returns A {@link StytchClient}
 */ const createStytchClient = StytchSSRProxy.ssrSafeClientFactory(StytchClient);
/**
 * @deprecated Use createStytchClient instead
 */ const createStytchHeadlessClient = createStytchClient;
/**
 * @deprecated Use StytchClient instead
 */ const StytchHeadlessClient = StytchClient;

exports.parseOAuthAuthorizeParams = idpHelpers.parseOAuthAuthorizeParams;
exports.parseOAuthLogoutParams = idpHelpers.parseOAuthLogoutParams;
exports.AuthFlowType = StytchSSRProxy.AuthFlowType;
exports.B2BMFAProducts = StytchSSRProxy.B2BMFAProducts;
exports.B2BOAuthProviders = StytchSSRProxy.B2BOAuthProviders;
exports.BiometricRegistrationIdIsNullOrBlank = StytchSSRProxy.BiometricRegistrationIdIsNullOrBlank;
exports.BiometricsAlreadyEnrolledError = StytchSSRProxy.BiometricsAlreadyEnrolledError;
exports.BiometricsFailedError = StytchSSRProxy.BiometricsFailedError;
exports.BiometricsUnavailableError = StytchSSRProxy.BiometricsUnavailableError;
exports.ChallengeSigningFailedError = StytchSSRProxy.ChallengeSigningFailedError;
exports.DFPNotConfigured = StytchSSRProxy.DFPNotConfigured;
exports.DeviceCredentialsNotAllowedError = StytchSSRProxy.DeviceCredentialsNotAllowedError;
exports.FailedCodeChallengeError = StytchSSRProxy.FailedCodeChallengeError;
exports.FailedToDecryptDataError = StytchSSRProxy.FailedToDecryptDataError;
exports.IDPOAuthFlowMissingParamError = StytchSSRProxy.IDPOAuthFlowMissingParamError;
exports.InternalError = StytchSSRProxy.InternalError;
exports.InvalidAuthorizationCredentialError = StytchSSRProxy.InvalidAuthorizationCredentialError;
exports.InvalidCredentialTypeError = StytchSSRProxy.InvalidCredentialTypeError;
exports.InvalidPrivateKeyLength = StytchSSRProxy.InvalidPrivateKeyLength;
exports.InvalidRedirectSchemeError = StytchSSRProxy.InvalidRedirectSchemeError;
exports.InvalidStartUrlError = StytchSSRProxy.InvalidStartUrlError;
exports.JSONDataNotConvertibleToStringError = StytchSSRProxy.JSONDataNotConvertibleToStringError;
exports.KeyInvalidatedError = StytchSSRProxy.KeyInvalidatedError;
exports.KeystoreUnavailableError = StytchSSRProxy.KeystoreUnavailableError;
exports.MissingAttestationObjectError = StytchSSRProxy.MissingAttestationObjectError;
exports.MissingAuthorizationCredentialIDTokenError = StytchSSRProxy.MissingAuthorizationCredentialIDTokenError;
exports.MissingCipherIv = StytchSSRProxy.MissingCipherIv;
exports.MissingGoogleClientIDError = StytchSSRProxy.MissingGoogleClientIDError;
exports.MissingPKCEError = StytchSSRProxy.MissingPKCEError;
exports.MissingPublicKeyError = StytchSSRProxy.MissingPublicKeyError;
exports.MissingUrlError = StytchSSRProxy.MissingUrlError;
exports.NoBiometricsEnrolledError = StytchSSRProxy.NoBiometricsEnrolledError;
exports.NoBiometricsRegistrationError = StytchSSRProxy.NoBiometricsRegistrationError;
exports.NoCredentialsPresentError = StytchSSRProxy.NoCredentialsPresentError;
exports.NoCurrentSessionError = StytchSSRProxy.NoCurrentSessionError;
exports.OAuthProviders = StytchSSRProxy.OAuthProviders;
exports.OTPMethods = StytchSSRProxy.OTPMethods;
exports.OneTapPositions = StytchSSRProxy.OneTapPositions;
exports.PasskeysInvalidEncoding = StytchSSRProxy.PasskeysInvalidEncoding;
exports.PasskeysMisconfigured = StytchSSRProxy.PasskeysMisconfigured;
exports.PasskeysUnsupportedError = StytchSSRProxy.PasskeysUnsupportedError;
exports.RNUIProducts = StytchSSRProxy.RNUIProducts;
exports.RandomNumberGenerationFailed = StytchSSRProxy.RandomNumberGenerationFailed;
exports.RedirectURLType = StytchSSRProxy.RedirectURLType;
exports.SDKAPIUnreachableError = StytchSSRProxy.SDKAPIUnreachableError;
exports.SDKNotConfiguredError = StytchSSRProxy.SDKNotConfiguredError;
exports.SignInWithAppleMisconfigured = StytchSSRProxy.SignInWithAppleMisconfigured;
exports.StytchAPIError = StytchSSRProxy.StytchAPIError;
exports.StytchAPISchemaError = StytchSSRProxy.StytchAPISchemaError;
exports.StytchAPIUnreachableError = StytchSSRProxy.StytchAPIUnreachableError;
exports.StytchError = StytchSSRProxy.StytchError;
exports.StytchEventType = StytchSSRProxy.StytchEventType;
exports.StytchSDKAPIError = StytchSSRProxy.StytchSDKAPIError;
exports.StytchSDKError = StytchSSRProxy.StytchSDKError;
exports.StytchSDKSchemaError = StytchSSRProxy.StytchSDKSchemaError;
exports.StytchSDKUsageError = StytchSSRProxy.StytchSDKUsageError;
exports.UNRECOVERABLE_ERROR_TYPES = StytchSSRProxy.UNRECOVERABLE_ERROR_TYPES;
exports.UserCancellationError = StytchSSRProxy.UserCancellationError;
exports.UserLockedOutError = StytchSSRProxy.UserLockedOutError;
exports.Wallets = StytchSSRProxy.Wallets;
exports.errorToStytchError = StytchSSRProxy.errorToStytchError;
exports.StytchClient = StytchClient;
exports.StytchHeadlessClient = StytchHeadlessClient;
exports.createStytchClient = createStytchClient;
exports.createStytchHeadlessClient = createStytchHeadlessClient;
//# sourceMappingURL=index.headless.cjs.map
