UNPKG

@nebula.js/stardust

Version:

Product and framework agnostic integration API for Qlik's Associative Engine

2,520 lines 96.1 kB
/*
* @nebula.js/stardust v6.3.0
* Copyright (c) 2025 QlikTech International AB
* Released under the MIT license.
*/

System.register(['./index-DC5bJ1TW.js'], (function (exports) {
	'use strict';
	var sortKeys, isBrowser, cleanFalsyValues, isNode;
	return {
		setters: [function (module) {
			sortKeys = module.s;
			isBrowser = module.i;
			cleanFalsyValues = module.c;
			isNode = module.a;
		}],
		execute: (function () {

			exports({
				determineAuthType: determineAuthType$1,
				getAccessToken: getAccessToken,
				getDefaultHostConfig: getDefaultHostConfig$1,
				getRestCallAuthParams: getRestCallAuthParams,
				getWebResourceAuthParams: getWebResourceAuthParams,
				getWebSocketAuthParams: getWebSocketAuthParams,
				handleAuthenticationError: handleAuthenticationError,
				isHostCrossOrigin: isHostCrossOrigin,
				isWindows: isWindows,
				registerAuthModule: registerAuthModule$1,
				registerHostConfig: registerHostConfig$1,
				serializeHostConfig: serializeHostConfig$1,
				setDefaultHostConfig: setDefaultHostConfig$1,
				toValidLocationUrl: toValidLocationUrl,
				toValidWebsocketLocationUrl: toValidWebsocketLocationUrl,
				unregisterHostConfig: unregisterHostConfig$1
			});

			//#region src/interceptors/interceptors.ts
			let GLOBAL_INTERCEPTORS;
			function createInterceptors() {
				const interceptors$1 = [...GLOBAL_INTERCEPTORS?.getInterceptors() || []];
				return {
					addInterceptor: (interceptor) => {
						interceptors$1.push(interceptor);
						return interceptor;
					},
					removeInterceptor: (interceptor) => {
						const index = interceptors$1.indexOf(interceptor);
						let removed;
						if (index !== -1) removed = interceptors$1.splice(index, 1)[0];
						return removed || null;
					},
					getInterceptors: () => interceptors$1
				};
			}
			/**
			* The global interceptor stack
			*/
			GLOBAL_INTERCEPTORS = createInterceptors();
			/**
			* Gets all registered interceptors
			*/
			function getInterceptors() {
				return GLOBAL_INTERCEPTORS.getInterceptors();
			}

			//#region src/auth/auth-types.ts
			/**
			* These properties are always allowed in the host config, even if they are not defined in the HostConfig interface
			* for the specific auth module.
			*/
			const hostConfigCommonProperties = [
				"authType",
				"autoRedirect",
				"authRedirectUserConfirmation",
				"embedRuntimeUrl",
				"host",
				"onAuthFailed"
			];
			const authTypesThatCanBeOmitted = [
				"apikey",
				"oauth2",
				"cookie",
				"windowscookie",
				"reference",
				"anonymous"
			];

			const urlAlphabet =
			  'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict';

			/* @ts-self-types="./index.d.ts" */
			let random = bytes => crypto.getRandomValues(new Uint8Array(bytes));
			let customRandom = (alphabet, defaultSize, getRandom) => {
			  let mask = (2 << Math.log2(alphabet.length - 1)) - 1;
			  let step = -~((1.6 * mask * defaultSize) / alphabet.length);
			  return (size = defaultSize) => {
			    let id = '';
			    while (true) {
			      let bytes = getRandom(step);
			      let j = step | 0;
			      while (j--) {
			        id += alphabet[bytes[j] & mask] || '';
			        if (id.length >= size) return id
			      }
			    }
			  }
			};
			let customAlphabet = (alphabet, size = 21) =>
			  customRandom(alphabet, size | 0, random);
			let nanoid = (size = 21) => {
			  let id = '';
			  let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)));
			  while (size--) {
			    id += urlAlphabet[bytes[size] & 63];
			  }
			  return id
			};

			//#region src/platform/platform-functions.ts
			const getPlatform = async (options = {}) => {
				const hc = withResolvedHostConfig(options.hostConfig);
				if (hc.authType === "mock-backend-rest-recorder") return hc.recordGetPlatform();
				if (hc?.authType === "mock-backend") return hc.mockGetPlatform();
				if (hc?.authType === "noauth") return result({ isUnknown: true });
				let productInfo = isBrowser() ? window.QlikMain?.productInfo?.() : void 0;
				if (!productInfo) {
					const { data, status } = await getProductInfo(options);
					productInfo = data;
					if (status === 404) return result({ isUnknown: true });
					if (!productInfo || status <= 399 && status >= 300) return result({
						isQSE: true,
						isWindows: true
					});
				}
				const deploymentType = (productInfo.composition?.deploymentType || "").toLowerCase();
				if (deploymentType === "qliksenseserver") return result({
					isQSE: true,
					isWindows: true,
					meta: extractMeta(productInfo)
				});
				if (deploymentType === "qliksensedesktop") return result({
					isQSD: true,
					isWindows: true,
					meta: extractMeta(productInfo)
				});
				if (deploymentType === "qliksensemobile") return result({
					isQSE: true,
					isWindows: true,
					meta: extractMeta(productInfo)
				});
				if (deploymentType === "cloud-console") return result({
					isCloud: true,
					isCloudConsole: true,
					meta: extractMeta(productInfo)
				});
				if (productInfo.composition?.provider === "fedramp") return result({
					isCloud: true,
					isQCG: true,
					meta: extractMeta(productInfo)
				});
				return result({
					isCloud: true,
					isQCS: true,
					meta: extractMeta(productInfo)
				});
			};
			const productInfoPromises = {};
			/**
			* Retrieves the full complete url to the product-info file. The full URL
			* is the cache key. Without templating the URL we'd cache `""` as an entry.
			* @private
			*/
			function templateUrl(baseUrl) {
				return `${baseUrl}/resources/autogenerated/product-info.json`;
			}
			/**
			* Retrieves the product information as a JSON object.
			* It makes an HTTP GET request to fetch the autogenerated product-info.json file.
			*
			* `data` is `undefined` if the file can not be retrieved.
			*/
			const getProductInfo = async ({ hostConfig, noCache } = {}) => {
				const completeUrl = templateUrl(toValidLocationUrl(hostConfig));
				try {
					if (!(completeUrl in productInfoPromises)) {
						const fetchOptions = {};
						if (globalThis.QlikMain?.resourceNeedsCredentials(completeUrl)) fetchOptions.credentials = "include";
						productInfoPromises[completeUrl] = fetch(completeUrl, fetchOptions).then(async (res) => {
							if (res.ok) return {
								data: await res.json(),
								status: res.status
							};
							return {
								data: void 0,
								status: res.status
							};
						});
					}
					const response = await productInfoPromises[completeUrl];
					if (response.status >= 400 || !response.data) delete productInfoPromises[completeUrl];
					return response;
				} catch {
					delete productInfoPromises[completeUrl];
					return {
						data: void 0,
						status: 500
					};
				} finally {
					if (noCache) delete productInfoPromises[completeUrl];
				}
			};
			/** @internal */
			const extractMeta = (data) => {
				const urls = data.externalUrls;
				if (!urls) return;
				const productName = data.composition?.productName ?? "Qlik";
				const releaseLabel = data.composition?.releaseLabel || "-";
				const productLabel = releaseLabel === "-" ? productName : `${productName} (${releaseLabel})`;
				return {
					productId: data.composition?.senseId ?? "qlik",
					productLabel,
					version: data.composition?.version,
					urls: {
						personalHelpBaseUrl: urls.personalHelpBaseUrl,
						personalUpgradeBase: urls.personalUpgradeBase,
						personalUpgradeUrl: urls.personalUpgradeUrl,
						serverHelpBaseUrl: urls.serverHelpBaseUrl,
						qlikWebPageUrl: urls.qlikWebPageUrl
					}
				};
			};
			const result = (data) => ({
				isCloud: false,
				isQCS: false,
				isQCG: false,
				isCloudConsole: false,
				isWindows: false,
				isQSE: false,
				isQSD: false,
				isUnknown: false,
				...data
			});

			//#endregion
			//#region src/utils/random.ts
			/**
			* Method helper for generating a random string [a-zA-Z0-9\-_]{length}
			* @param length - the length of the string
			*/
			function generateRandomString(targetLength) {
				return nanoid(targetLength);
			}
			/**
			* Method helper for generating a random hexadecimal-string [0-9a-f]{length}
			* @param length - the length of the string
			*/
			function generateRandomHexString(targetLength) {
				return customAlphabet("1234567890abcdef", targetLength)();
			}

			//#endregion
			//#region src/utils/expose-internal-test-apis.ts
			const internalApisName = "__QLIK_INTERNAL__DO_NOT_USE_OR_YOU_WILL_BE_FIRED";
			function exposeInternalApiOnWindow(name, fn) {
				if (globalThis.location?.origin.startsWith("https://localhost:") || globalThis.location?.origin?.endsWith("qlik-stage.com")) {
					if (globalThis[internalApisName] === void 0) globalThis[internalApisName] = {};
					globalThis[internalApisName][name] = fn;
				}
			}

			//#endregion
			//#region src/auth/internal/default-auth-modules/oauth/storage-helpers.ts
			const storagePrefix = "qlik-qmfe-api";
			function getTopicFromOauthHostConfig(hostConfig) {
				let topic = hostConfig.clientId + (hostConfig.scope ? `_${hostConfig.scope}` : "_user_default");
				if (hostConfig.subject) topic += `_${hostConfig.subject}`;
				if (hostConfig.userId) topic += `_${hostConfig.userId}`;
				return topic;
			}
			function getTopicFromAnonHostConfig(hostConfig) {
				return `${hostConfig.accessCode}_${hostConfig.clientId}`;
			}
			/**
			* A map from oauth client id to promise of an access token
			*/
			const cachedTokens = {};
			/**
			* Used for testing.
			*/
			function clearAllCachedTokens() {
				for (const key in cachedTokens) delete cachedTokens[key];
			}
			exposeInternalApiOnWindow("clearAllAccessTokens", () => {
				console.log("Clearing tokens", cachedTokens);
				Object.keys(cachedTokens).forEach((key) => {
					console.log("Clearing access tokens for", key);
					deleteFromLocalStorage(key, ["access-token", "refresh-token"]);
					deleteFromSessionStorage(key, ["access-token", "refresh-token"]);
				});
				clearAllCachedTokens();
			});
			function saveInLocalStorage(topic, name, value) {
				localStorage.setItem(`${storagePrefix}-${topic}-${name}`, value);
			}
			function saveInSessionStorage(topic, name, value) {
				sessionStorage.setItem(`${storagePrefix}-${topic}-${name}`, value);
			}
			function loadFromLocalStorage(topic, name) {
				return localStorage.getItem(`${storagePrefix}-${topic}-${name}`) || void 0;
			}
			function loadFromSessionStorage(topic, name) {
				return sessionStorage.getItem(`${storagePrefix}-${topic}-${name}`) || void 0;
			}
			function deleteFromLocalStorage(topic, names) {
				names.forEach((name) => localStorage.removeItem(`${storagePrefix}-${topic}-${name}`));
			}
			function deleteFromSessionStorage(topic, names) {
				names.forEach((name) => sessionStorage.removeItem(`${storagePrefix}-${topic}-${name}`));
			}
			function loadAndDeleteFromSessionStorage(topic, name) {
				const id = `${storagePrefix}-${topic}-${name}`;
				const result$1 = sessionStorage.getItem(id) || void 0;
				sessionStorage.removeItem(id);
				return result$1;
			}
			function loadOauthTokensFromStorage(topic, accessTokenStorage) {
				let accessToken;
				let refreshToken;
				if (accessTokenStorage === "local") {
					accessToken = loadFromLocalStorage(topic, "access-token");
					refreshToken = loadFromLocalStorage(topic, "refresh-token");
				} else if (accessTokenStorage === "session") {
					accessToken = loadFromSessionStorage(topic, "access-token");
					refreshToken = loadFromSessionStorage(topic, "refresh-token");
				}
				if (accessToken) return {
					accessToken,
					refreshToken
				};
			}
			async function loadCachedOauthTokens(hostConfig) {
				return cachedTokens[getTopicFromOauthHostConfig(hostConfig)];
			}
			async function loadOrAcquireAccessTokenOauth(hostConfig, acquireTokens) {
				if (!hostConfig.clientId) throw new InvalidHostConfigError("A host config with authType set to \"oauth2\" has to also provide a clientId");
				return loadOrAcquireAccessToken(getTopicFromOauthHostConfig(hostConfig), acquireTokens, hostConfig.noCache, hostConfig.accessTokenStorage);
			}
			async function loadOrAcquireAccessTokenAnon(hostConfig, acquireTokens) {
				if (!hostConfig.accessCode) throw new InvalidHostConfigError("A host config with authType set to \"anonymous\" has to also provide an accessCode");
				return loadOrAcquireAccessToken(getTopicFromAnonHostConfig(hostConfig), acquireTokens, false, void 0);
			}
			async function loadOrAcquireAccessToken(topic, acquireTokens, noCache, accessTokenStorage) {
				if (noCache) return acquireTokens();
				const mayUseStorage = isBrowser();
				const storedOauthTokens = cachedTokens[topic] || (mayUseStorage ? loadOauthTokensFromStorage(topic, accessTokenStorage) : void 0);
				if (storedOauthTokens) {
					cachedTokens[topic] = storedOauthTokens;
					return Promise.resolve(storedOauthTokens);
				}
				const tokensPromise = acquireTokens();
				cachedTokens[topic] = tokensPromise;
				if (mayUseStorage) {
					const tokens = await tokensPromise;
					if (accessTokenStorage === "local" && tokens) {
						if (tokens.accessToken) saveInLocalStorage(topic, "access-token", tokens.accessToken);
						if (tokens.refreshToken) saveInLocalStorage(topic, "refresh-token", tokens.refreshToken);
					} else if (accessTokenStorage === "session" && tokens) {
						if (tokens.accessToken) saveInSessionStorage(topic, "access-token", tokens.accessToken);
						if (tokens.refreshToken) saveInSessionStorage(topic, "refresh-token", tokens.refreshToken);
					}
				}
				return tokensPromise;
			}
			function clearStoredOauthTokens(hostConfig) {
				const topic = getTopicFromOauthHostConfig(hostConfig);
				delete cachedTokens[topic];
				if (isBrowser()) {
					deleteFromLocalStorage(topic, ["access-token", "refresh-token"]);
					deleteFromSessionStorage(topic, ["access-token", "refresh-token"]);
				}
			}
			function clearStoredAnonymousTokens(hostConfig) {
				const topic = getTopicFromAnonHostConfig(hostConfig);
				delete cachedTokens[topic];
				if (isBrowser()) {
					deleteFromLocalStorage(topic, ["access-token", "refresh-token"]);
					deleteFromSessionStorage(topic, ["access-token", "refresh-token"]);
				}
			}

			//#endregion
			//#region src/auth/internal/default-auth-modules/oauth/oauth-utils.ts
			function toPerformInteractiveLoginFunction(performInteractiveLogin) {
				if (typeof performInteractiveLogin === "string") {
					const fn = lookupInteractiveLoginFn(performInteractiveLogin);
					if (!fn) throw new Error(`No such function: ${performInteractiveLogin}`);
					return fn;
				}
				return performInteractiveLogin;
			}
			function lookupGetAccessFn(getAccessToken$1) {
				return globalThis[getAccessToken$1];
			}
			function lookupInteractiveLoginFn(name) {
				return globalThis[name];
			}
			function handlePossibleErrors(data) {
				if (data.errors) throw new AuthorizationError(data.errors);
			}
			function toQueryString(queryParams) {
				const queryParamsKeys = Object.keys(queryParams);
				queryParamsKeys.sort();
				return queryParamsKeys.map((k) => `${k}=${queryParams[k]}`).join("&");
			}
			function byteArrayToBase64(hashArray) {
				let result$1 = "";
				if (isBrowser()) {
					const byteArrayToString = String.fromCharCode.apply(null, hashArray);
					result$1 = btoa(byteArrayToString);
				} else if (isNode()) result$1 = Buffer.from(hashArray).toString("base64");
				else throw new Error("Environment not supported for oauth2 authentication");
				return result$1;
			}
			/**
			* @param message string to hash
			* @returns sha-256 hashed string
			*/
			async function sha256(message) {
				const msgBuffer = new TextEncoder().encode(message);
				const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", msgBuffer);
				return byteArrayToBase64(Array.from(new Uint8Array(hashBuffer))).replaceAll(/\+/g, "-").replaceAll(/\//g, "_").replace(/=+$/, "");
			}
			async function createInteractiveLoginUrl(hostConfig, redirectUri, state, verifier) {
				const clientId = hostConfig.clientId || "";
				const locationUrl = toValidLocationUrl(hostConfig);
				const codeChallenge = await sha256(verifier);
				return `${locationUrl}/oauth/authorize?${toQueryString({
		response_type: "code",
		client_id: clientId,
		redirect_uri: redirectUri,
		scope: hostConfig.scope || "user_default",
		state,
		code_challenge: codeChallenge,
		code_challenge_method: "S256"
	})}`;
			}
			async function startFullPageLoginFlow(hostConfig) {
				const clientId = hostConfig.clientId || "";
				const locationUrl = toValidLocationUrl(hostConfig);
				const verifier = generateRandomString(128);
				const state = generateRandomString(43);
				const codeChallenge = await sha256(verifier);
				const redirectUri = hostConfig.redirectUri || globalThis.location.href;
				const topic = getTopicFromOauthHostConfig(hostConfig);
				clearStoredOauthTokens(hostConfig);
				saveInSessionStorage(topic, "state", state);
				saveInSessionStorage(topic, "verifier", verifier);
				saveInSessionStorage(topic, "href", globalThis.location.href);
				saveInSessionStorage("", "client-in-progress", topic);
				const url = `${locationUrl}/oauth/authorize?${toQueryString({
		response_type: "code",
		client_id: clientId,
		redirect_uri: redirectUri,
		scope: hostConfig.scope || "user_default",
		state,
		code_challenge: codeChallenge,
		code_challenge_method: "S256"
	})}`;
				globalThis.location.replace(url);
			}
			async function exchangeCodeAndVerifierForAccessTokenData(hostConfig, code, verifier, redirectUri) {
				try {
					const data = await (await fetch(`${toValidLocationUrl(hostConfig)}/oauth/token`, {
						method: "POST",
						credentials: "include",
						mode: "cors",
						headers: { "content-type": "application/json" },
						redirect: "follow",
						body: JSON.stringify({
							grant_type: "authorization_code",
							scope: hostConfig.scope || "user_default",
							...code ? { code } : {},
							redirect_uri: redirectUri || globalThis.location.href,
							...verifier ? { code_verifier: verifier } : {},
							client_id: hostConfig.clientId
						})
					})).json();
					handlePossibleErrors(data);
					return {
						accessToken: data.access_token,
						refreshToken: data.refresh_token,
						errors: data.errors
					};
				} catch (err) {
					console.error(err);
					return new Promise(() => {});
				}
			}
			function createBodyWithCredentialsEtc(clientId, clientSecret, scope, subject, userId) {
				const commonProps = {
					client_id: clientId,
					client_secret: clientSecret,
					scope
				};
				if (subject) return {
					...commonProps,
					grant_type: "urn:qlik:oauth:user-impersonation",
					user_lookup: {
						field: "subject",
						value: subject
					}
				};
				if (userId) return {
					...commonProps,
					grant_type: "urn:qlik:oauth:user-impersonation",
					user_lookup: {
						field: "userId",
						value: userId
					}
				};
				return {
					...commonProps,
					grant_type: "client_credentials"
				};
			}
			async function getOauthTokensWithCredentials(baseUrl, clientId, clientSecret, scope = "user_default", subject, userId) {
				const data = await (await fetch(`${baseUrl}/oauth/token`, {
					method: "POST",
					mode: "cors",
					headers: { "content-type": "application/json" },
					body: JSON.stringify(createBodyWithCredentialsEtc(clientId, clientSecret, scope, subject, userId))
				})).json();
				return {
					accessToken: data.access_token,
					refreshToken: data.refresh_token,
					errors: data.errors
				};
			}
			async function getOauthTokensWithRefreshToken(baseUrl, refreshToken, clientSecret) {
				const data = await (await fetch(`${baseUrl}/oauth/token`, {
					method: "POST",
					mode: "cors",
					headers: { "content-type": "application/json" },
					body: JSON.stringify({
						grant_type: "refresh_token",
						refresh_token: refreshToken,
						client_secret: clientSecret
					})
				})).json();
				return {
					accessToken: data.access_token,
					refreshToken: data.refresh_token,
					errors: data.errors
				};
			}
			async function getAnonymousOauthAccessToken(baseUrl, accessCode, clientId, trackingCode) {
				const data = await (await fetch(`${baseUrl}/oauth/token/anonymous-embed`, {
					method: "POST",
					mode: "cors",
					headers: { "content-type": "application/json" },
					body: JSON.stringify({
						eac: accessCode,
						client_id: clientId,
						grant_type: "urn:qlik:oauth:anonymous-embed",
						tracking_code: trackingCode
					})
				})).json();
				return {
					accessToken: data.access_token,
					refreshToken: data.refresh_token,
					errors: data.errors
				};
			}
			/**
			* Fetches the access token from storage or memory.
			* This code is intended to run in a node environment
			*/
			async function getOAuthTokensForNode(hostConfig) {
				const { clientId, clientSecret } = hostConfig;
				if (!clientId || !clientSecret) throw new InvalidHostConfigError("A host config with authType set to \"oauth2\" has to provide a clientId and a clientSecret");
				return await loadOrAcquireAccessTokenOauth(hostConfig, async () => {
					if (!hostConfig.clientId || !hostConfig.clientSecret) throw new InvalidHostConfigError("A host config with authType set to \"oauth2\" has to provide a clientId and a clientSecret");
					return getOauthTokensWithCredentials(toValidLocationUrl(hostConfig), hostConfig.clientId, hostConfig.clientSecret, hostConfig.scope, hostConfig.subject, hostConfig.userId);
				});
			}
			/**
			* Fetches the access token from storage or memory. If no one is found a code and verifier is expected to be found in the sesion storage
			* This code is intended to run in a browser
			*/
			async function getOAuthTokensForBrowser(hostConfig) {
				const { clientId } = hostConfig;
				if (!clientId) throw new InvalidHostConfigError("A host config with authType set to \"oauth2\" has to also provide a clientId");
				const oauthTokens = await loadOrAcquireAccessTokenOauth(hostConfig, async () => {
					if (hostConfig.getAccessToken) try {
						return {
							accessToken: typeof hostConfig.getAccessToken === "string" ? await lookupGetAccessFn(hostConfig.getAccessToken)() : await hostConfig.getAccessToken(),
							refreshToken: void 0,
							errors: void 0
						};
					} catch {
						return errorMessageToAuthData("Could not fetch access token using custom function");
					}
					if (hostConfig.performInteractiveLogin) {
						let usedRedirectUri;
						try {
							const verifier$1 = generateRandomString(128);
							const originalState = generateRandomString(43);
							const { code: code$1, state } = extractCodeAndState(await toPerformInteractiveLoginFunction(hostConfig.performInteractiveLogin)({ getLoginUrl: async ({ redirectUri }) => {
								usedRedirectUri = redirectUri;
								return createInteractiveLoginUrl(hostConfig, redirectUri, originalState, verifier$1);
							} }));
							if (!usedRedirectUri) return errorMessageToAuthData("No redirect uri provided");
							if (originalState !== state) return errorMessageToAuthData("State returned by custom interactive login function does not match original");
							if (!code$1) return errorMessageToAuthData("No code found in response from custom interactive login function");
							return await exchangeCodeAndVerifierForAccessTokenData(hostConfig, code$1, verifier$1, usedRedirectUri);
						} catch (error) {
							return {
								accessToken: void 0,
								refreshToken: void 0,
								errors: [{
									code: "",
									status: 401,
									title: "Could not perform custom interactive login",
									detail: `${error}`
								}]
							};
						}
					}
					const topic = getTopicFromOauthHostConfig(hostConfig);
					const code = loadAndDeleteFromSessionStorage(topic, "code");
					const verifier = loadAndDeleteFromSessionStorage(topic, "verifier");
					if (code && verifier) {
						const tokenResponse = await exchangeCodeAndVerifierForAccessTokenData(hostConfig, code, verifier, hostConfig.redirectUri);
						if (tokenResponse) return tokenResponse;
					}
				});
				if (oauthTokens) return oauthTokens;
				if (hostConfig.performInteractiveLogin) return new Promise(() => {});
				if (hostConfig.authRedirectUserConfirmation) await hostConfig.authRedirectUserConfirmation();
				startFullPageLoginFlow(hostConfig);
				return new Promise(() => {});
			}
			let lastOauthTokensCall = Promise.resolve("");
			async function getOAuthAccessToken(hostConfig) {
				if (isNode()) {
					const tokens = await getOAuthTokensForNode(hostConfig);
					if (tokens) {
						handlePossibleErrors(tokens);
						return tokens.accessToken || "";
					}
					return "";
				}
				if (isBrowser()) lastOauthTokensCall = lastOauthTokensCall.then(async () => {
					const tokens = await getOAuthTokensForBrowser(hostConfig);
					if (tokens) {
						handlePossibleErrors(tokens);
						return tokens.accessToken || "";
					}
					return "";
				});
				return lastOauthTokensCall;
			}
			async function refreshAccessToken(hostConfig) {
				const tokens = await loadCachedOauthTokens(hostConfig);
				clearStoredOauthTokens(hostConfig);
				if (tokens && tokens.refreshToken && hostConfig.clientSecret) {
					const refreshedTokens = await loadOrAcquireAccessTokenOauth(hostConfig, async () => {
						if (!tokens || !tokens.refreshToken || !hostConfig.clientSecret) throw new Error("Trying to refresh tokens without refreshToken or clientSecret");
						return getOauthTokensWithRefreshToken(toValidLocationUrl(hostConfig), tokens.refreshToken, hostConfig.clientSecret);
					});
					if (refreshedTokens) handlePossibleErrors(refreshedTokens);
				}
			}
			function extractCodeAndState(input) {
				if (typeof input === "string") {
					const queryParams = new URLSearchParams(new URL(input).search);
					return {
						code: queryParams.get("code") || "",
						state: queryParams.get("state") || ""
					};
				}
				return input;
			}
			function errorMessageToAuthData(message) {
				return {
					accessToken: void 0,
					refreshToken: void 0,
					errors: [{
						code: "",
						status: 401,
						title: message,
						detail: ""
					}]
				};
			}

			//#endregion
			//#region src/auth/internal/default-auth-modules/oauth/temporary-token.ts
			async function exchangeAccessTokenForTemporaryToken(hostConfig, accessToken, purpose) {
				const response = await fetch(`${toValidLocationUrl(hostConfig)}/oauth/token`, {
					method: "POST",
					credentials: "include",
					mode: "cors",
					headers: { "content-type": "application/json" },
					redirect: "follow",
					body: JSON.stringify({
						subject_token: accessToken,
						subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
						grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
						purpose,
						redirect_uri: globalThis.location?.href,
						client_id: hostConfig.clientId
					})
				});
				if (response.status !== 200) throw await toError(response);
				return (await response.json()).access_token;
			}
			async function toError(response) {
				const body = await response.text();
				try {
					return new AuthorizationError(JSON.parse(body).errors);
				} catch {
					return new AuthorizationError([{
						code: "unknown",
						status: response.status,
						detail: body,
						title: "Unknown authentication error"
					}]);
				}
			}

			//#endregion
			//#region src/auth/internal/default-auth-modules/anonymous.ts
			/**
			* Retries the passed in function after calling handleAuthenticationError
			*/
			async function handlePotentialAuthenticationErrorAndRetry$1(hostConfig, fn) {
				try {
					return await fn();
				} catch (err) {
					const { retry } = await handleAuthenticationError$9({
						hostConfig});
					if (retry) return fn();
					throw err;
				}
			}
			async function getOrCreateTrackingCode(hostConfig) {
				let trackingCode;
				if (isBrowser()) {
					const topic = getTopicFromAnonHostConfig(hostConfig);
					trackingCode = loadFromLocalStorage(topic, "tracking-code");
					if (!trackingCode) trackingCode = createTrackingCode();
					saveInLocalStorage(topic, "tracking-code", trackingCode);
				} else trackingCode = createTrackingCode();
				return trackingCode;
			}
			function createTrackingCode() {
				const timeStamp = Math.floor(Date.now() / 1e3).toString(16);
				return `${timeStamp}${generateRandomHexString(40 - timeStamp.length)}`;
			}
			async function getAnonymousAccessToken(hostConfig) {
				const { accessCode, clientId } = hostConfig;
				if (!accessCode || !clientId) throw new InvalidHostConfigError("A host config with authType set to \"anonymous\" has to provide both an accessCode and clientId");
				const tokens = await loadOrAcquireAccessTokenAnon(hostConfig, async () => {
					return getAnonymousOauthAccessToken(toValidLocationUrl(hostConfig), accessCode, clientId, await getOrCreateTrackingCode(hostConfig));
				});
				if (!tokens) return "";
				if (tokens.errors) throw new AuthorizationError(tokens.errors);
				if (tokens.accessToken) return tokens.accessToken;
				return "";
			}
			async function getRestCallAuthParams$9({ hostConfig }) {
				return {
					headers: { Authorization: `Bearer ${await getAnonymousAccessToken(hostConfig)}` },
					queryParams: {},
					credentials: "omit"
				};
			}
			async function getWebSocketAuthParams$9({ hostConfig }) {
				if (isNode()) return { headers: { Authorization: `Bearer ${await getAnonymousAccessToken(hostConfig)}` } };
				return { queryParams: { accessToken: await handlePotentialAuthenticationErrorAndRetry$1(hostConfig, async () => {
					return exchangeAccessTokenForTemporaryToken(hostConfig, await getAnonymousAccessToken(hostConfig), "websocket");
				}) } };
			}
			async function getWebResourceAuthParams$2({ hostConfig }) {
				return { queryParams: { accessToken: await handlePotentialAuthenticationErrorAndRetry$1(hostConfig, async () => {
					return exchangeAccessTokenForTemporaryToken(hostConfig, await getAnonymousAccessToken(hostConfig), "websocket");
				}) } };
			}
			async function handleAuthenticationError$9({ hostConfig }) {
				clearStoredAnonymousTokens(hostConfig);
				return {
					preventDefault: false,
					retry: true
				};
			}
			var anonymous_default = {
				requiredProps: ["clientId", "accessCode"],
				optionalProps: [],
				getRestCallAuthParams: getRestCallAuthParams$9,
				getWebSocketAuthParams: getWebSocketAuthParams$9,
				getWebResourceAuthParams: getWebResourceAuthParams$2,
				handleAuthenticationError: handleAuthenticationError$9
			};

			//#endregion
			//#region src/auth/internal/default-auth-modules/apikey.ts
			function getRestCallAuthParams$8({ hostConfig }) {
				return Promise.resolve({
					headers: { Authorization: `Bearer ${hostConfig?.apiKey}` },
					queryParams: {},
					credentials: "omit"
				});
			}
			async function getWebSocketAuthParams$8({ hostConfig }) {
				if (isBrowser()) throw new Error("Not supported in browser environment");
				return Promise.resolve({ headers: { Authorization: `Bearer ${hostConfig?.apiKey}` } });
			}
			function handleAuthenticationError$8() {
				return Promise.resolve({});
			}
			var apikey_default = {
				requiredProps: ["apiKey"],
				optionalProps: [],
				getRestCallAuthParams: getRestCallAuthParams$8,
				getWebSocketAuthParams: getWebSocketAuthParams$8,
				handleAuthenticationError: handleAuthenticationError$8
			};

			//#endregion
			//#region src/invoke-fetch/invoke-fetch-errors.ts
			/**
			* Error thrown by the invokeFetch function
			*/
			var InvokeFetchError = class extends Error {
				status;
				headers;
				data;
				constructor(errorMessage, status, headers, data) {
					super(errorMessage);
					this.status = status;
					this.headers = headers;
					this.data = data;
					this.stack = cleanStack(this.stack);
				}
			};
			/**
			* Error thrown if a request-body cannot be encoded.
			*/
			var EncodingError = class extends Error {
				contentType;
				data;
				constructor(errorMessage, contentType, data) {
					super(errorMessage);
					this.contentType = contentType;
					this.data = data;
					this.stack = cleanStack(this.stack);
				}
			};
			const regex = /^.+\/qmfe-api(?:\.js)?:(\d+)(?::\d+)?$/gim;
			const isFromQmfeApi = (line) => {
				const matches = line.match(regex);
				return Boolean(matches && matches.length > 0);
			};
			/**
			* Function that removes stack entries that are from the qmfe-api module
			*/
			function cleanStack(stack) {
				if (!stack) return stack;
				const newStack = [];
				const lines = stack.split("\n");
				lines.reverse();
				for (const line of lines) {
					if (isFromQmfeApi(line)) break;
					newStack.unshift(line);
				}
				return newStack.join("\n");
			}
			/**
			* Encode the query map into a query string
			* @private
			*/
			function encodeQueryParams(query) {
				if (!query) return "";
				return Object.entries(query).map((kv) => {
					const [, value] = kv;
					if (value === void 0) return;
					return kv.map((val) => encodeURIComponent(decodeURIComponent(val))).join("=");
				}).filter(Boolean).join("&");
			}
			/**
			* Replaces the variable placeholders in the path template with the variable values
			* @param pathTemplate
			* @param pathVariables
			* @private
			*/
			function applyPathVariables(pathTemplate, pathVariables) {
				let result$1 = pathTemplate;
				if (pathVariables) Object.keys(pathVariables).forEach((key) => {
					result$1 = result$1.replace(`{${key}}`, pathVariables[key]);
				});
				return result$1;
			}
			/**
			* Join url and query to a complete url
			* @private
			*/
			function toCompleteUrl(url, query) {
				if (query !== "") return `${url}?${query}`;
				return url;
			}

			//#endregion
			//#region src/invoke-fetch/internal/invoke-xhr.ts
			async function invokeXHR(completeUrl, { method, headers, credentials, keepalive, body, signal, progress }) {
				const xhr = new XMLHttpRequest();
				let resolve;
				let reject;
				const promise = new Promise((res, rej) => {
					resolve = res;
					reject = rej;
				});
				xhr.open(method || "GET", completeUrl);
				if (typeof headers === "object") for (const [key, value] of Object.entries(headers)) xhr.setRequestHeader(key, value);
				else throw Error("malformed headers", headers);
				if (keepalive) xhr.setRequestHeader("Connection", "keep-alive");
				if (signal) signal.addEventListener("abort", () => {
					xhr.abort();
					reject();
				});
				if (credentials === "include") xhr.withCredentials = true;
				else xhr.withCredentials = false;
				if (progress?.onUpload) xhr.upload.onprogress = (event) => {
					const { loaded, total, lengthComputable } = event;
					progress.onUpload?.({
						loaded,
						total: lengthComputable ? total : void 0
					});
				};
				if (progress?.onDownload) xhr.onprogress = (event) => {
					const { loaded, total, lengthComputable } = event;
					progress.onDownload?.({
						loaded,
						total: lengthComputable ? total : void 0
					});
				};
				xhr.onloadend = () => {
					const { status } = xhr;
					const responseHeaders = {};
					for (const line of xhr.getAllResponseHeaders().split("\r\n")) {
						const [key, value] = line.split(":", 2);
						if (key && value) responseHeaders[key] = value;
					}
					if (xhr.response) resolve(new Response(xhr.response, {
						status,
						headers: responseHeaders
					}));
					else resolve(new Response(void 0, {
						status,
						headers: responseHeaders
					}));
				};
				try {
					const bod = body;
					xhr.send(bod);
				} catch (e) {
					return Promise.reject(new InvokeFetchError(getErrorMessage(e), 0, new Headers(), {}));
				}
				return promise;
			}

			//#endregion
			//#region src/invoke-fetch/internal/response-cache.ts
			const responseCaches = {};
			globalThis.__API_CACHE__DO_NOT_USE_OR_YOU_WILL_BE_FIRED = responseCaches;
			let defaultCacheTime = 1e3 * 60 * 10;
			/**
			* The global namespace is used as a fallback if an entry is not found in a specific cache.
			* This cache can be used internally, specifically by BFF interceptors,
			* where we cannot reliably map a response to an API-module.
			*/
			const globalCacheNamespace = ".global";
			/**
			* Gets a cached response-promise from the specified API-namespace (or global cache).
			* A promise is returned if options.noCache is not true, the promise exists and is not too
			* old (based on defaults and cache-options).
			*
			* @param api The cache-namespace of the API.
			* @param props CachingContext
			* @returns The cached response (cloned and ready to use) or undefined.
			*/
			function getFromCache(api, props) {
				if (props?.options?.noCache) return;
				const { method, completeUrl, cacheKey } = props;
				const caches = getPossibleCaches(api);
				if (isModifyingOperation(method, completeUrl)) return;
				let entry;
				for (const cache of caches) if (cacheKey in cache) {
					entry = cache[cacheKey];
					if (entry && typeof entry.value !== "undefined" && shouldUseCachedResult(props?.options, entry, defaultCacheTime)) break;
					entry = void 0;
				}
				if (!entry) return;
				entry.lastHit = /* @__PURE__ */ new Date();
				entry.hitCount += 1;
				entry.accessedPaths.add(globalThis.location ? globalThis.location.pathname : "node");
				const value = entry.value;
				return cloneResultPromise(value);
			}
			/**
			* Updates the cache of 'api' with the response-promise based on the caching-context.
			* If the promise comes from a modifying operation, related cache entries will be removed.
			* The returned promise is either the original promise (if it isn't cached) or a
			* cloned promise that is safe to use.
			*
			* Note: The original response-promise should not be reused directly, use the returned
			* equivalent instead.
			*
			* @private
			* @param api The cache-namespace of the API.
			* @param props CachingContext
			* @param responsePromise Promise<InvokeFetchResponse>, the thing to cache
			* @returns
			*/
			function updateCache(api, props, responsePromise) {
				if (!responseCaches[api]) responseCaches[api] = {};
				const { cacheKey } = props;
				if (isModifyingOperation(props.method, props.completeUrl)) return responsePromise.then((res) => {
					const caches = getPossibleCaches(api);
					for (const cache of caches) clearRelatedCacheEntries(cache, cacheKey);
					return res;
				});
				const cacheNamespace = responseCaches[api];
				const responsePromiseWithCacheClearing = responsePromise.catch((error) => {
					delete cacheNamespace[cacheKey];
					return Promise.reject(error);
				});
				cacheNamespace[cacheKey] = {
					lastPulled: Date.now(),
					value: responsePromiseWithCacheClearing,
					lastHit: null,
					hitCount: 0,
					accessedPaths: /* @__PURE__ */ new Set()
				};
				return cloneResultPromise(responsePromiseWithCacheClearing);
			}
			/**
			* Returns true if it is ok the to use the cached promise from a previous invocation
			* @private
			*/
			function shouldUseCachedResult(options, cacheEntry, defaultMaxCacheTime) {
				if (options?.noCache) return false;
				if (!cacheEntry || typeof cacheEntry.value === "undefined") return false;
				if (options?.useCacheIfAfter) return options.useCacheIfAfter.getTime() <= cacheEntry.lastPulled;
				const age = Date.now() - cacheEntry.lastPulled;
				if (typeof options?.maxCacheAge === "number") return age <= options?.maxCacheAge;
				return age < defaultMaxCacheTime;
			}
			/**
			* Builds a key used for caching based on a CachingContext
			* @private
			* @param cachingContext
			* @returns a key used for caching
			*/
			function toCacheKey({ url, query, headers, serializedHostConfig }) {
				let cacheKey = url;
				if (query) {
					const queryString = encodeQueryParams(query);
					if (url.includes("?")) cacheKey = cacheKey.concat(`&${queryString}`);
					else cacheKey = cacheKey.concat(`?${queryString}`);
				}
				if (headers) cacheKey = cacheKey.concat(`+headers=${JSON.stringify(headers)}`);
				if (serializedHostConfig && serializedHostConfig !== "{}") cacheKey = cacheKey.concat(`+host-config=${serializedHostConfig}`);
				return cacheKey;
			}
			/**
			* Convenience function for getting a list of caches that are OK to use
			* in the context of an API-namespace.
			* @param api
			* @returns
			*/
			function getPossibleCaches(api) {
				const caches = [];
				if (responseCaches[api]) caches.push(responseCaches[api]);
				if (responseCaches[globalCacheNamespace]) caches.push(responseCaches[globalCacheNamespace]);
				return caches;
			}
			/**
			* @private
			* Clears all cache entries where the modifying URL starts with the cached URL
			* @param cache
			* @param cacheKey
			*/
			function clearRelatedCacheEntries(cache, cacheKey) {
				const modifyingUrl = cacheKeyToUrl(cacheKey);
				for (const existingCacheKey in cache) {
					const cleanUrl = cacheKeyToUrl(existingCacheKey);
					if (modifyingUrl.startsWith(cleanUrl) || cleanUrl.startsWith(modifyingUrl)) delete cache[existingCacheKey];
				}
			}
			/**
			* Removes query, headers and host-config from the cache key to get a clean URL.
			*/
			function cacheKeyToUrl(cachedUrl) {
				const queryIdx = cachedUrl.indexOf("?");
				if (queryIdx >= 0) return cachedUrl.substring(0, queryIdx);
				const headersIdx = cachedUrl.indexOf("+headers=");
				if (headersIdx >= 0) return cachedUrl.substring(0, headersIdx);
				const hostConfigIdx = cachedUrl.indexOf("+host-config=");
				if (hostConfigIdx >= 0) return cachedUrl.substring(0, hostConfigIdx);
				return cachedUrl;
			}
			/**
			* Clone mutable-objects using JSON.stringify.
			* Strings, Blobs and ReadableStreams are not cloned.
			* @private
			*/
			function clone(value) {
				if (typeof value === "undefined" || value === null) return value;
				if (value && (value instanceof Blob || value instanceof Object && value.toString() === "[object Blob]")) return value;
				if (value && value instanceof ReadableStream) return value;
				if (typeof value === "string") return value;
				return JSON.parse(JSON.stringify(value));
			}
			/**
			* Clones the result so that no one can tamper with the cache. Note that the headers object is NOT cloned
			* @private
			*/
			function cloneResultPromise(value) {
				return value.then((resp) => {
					const result$1 = {
						data: clone(resp.data),
						headers: resp.headers,
						status: resp.status
					};
					if (resp.next) result$1.next = resp.next;
					if (resp.prev) result$1.prev = resp.prev;
					return result$1;
				});
			}
			/**
			* Returns true if the method might modify things requiring caches to be cleared
			*/
			function isModifyingOperation(method, url) {
				if (method === "get" || method === "GET") return false;
				if (url.endsWith("/api/v1/licenses/allotments") && (method === "post" || method === "POST")) return false;
				return true;
			}

			//#endregion
			//#region src/invoke-fetch/internal/invoke-fetch-methods.ts
			function getErrorMessage(error) {
				if (error instanceof Error) {
					if (error.cause) {
						if (error.message) return `${error.message} - ${getErrorMessage(error.cause)}`;
						return getErrorMessage(error.cause);
					}
					return error.message;
				}
				return String(error);
			}
			async function fetchAndTransformExceptions(input, init) {
				try {
					return await fetch(input, init);
				} catch (e) {
					return Promise.reject(new InvokeFetchError(getErrorMessage(e), 0, new Headers(), {}));
				}
			}
			async function performActualHttpFetch(method, completeUrl, unencodedBody, contentType, options, authHeaders, credentials, userAgent) {
				const { body, contentTypeHeader, requestOptions } = encodeBody(unencodedBody, contentType ?? "");
				const headers = {
					...contentTypeHeader,
					...authHeaders,
					...options?.headers,
					...getServiceOverrideHeaderFromLocalStorage()
				};
				if (!headers["User-Agent"] && userAgent) headers["User-Agent"] = userAgent;
				const request = {
					method,
					credentials,
					mode: "cors",
					headers,
					redirect: await isWindows(options?.hostConfig) ? "manual" : "follow",
					keepalive: options?.keepalive,
					progress: options?.progress,
					body,
					...requestOptions
				};
				let fetchTimeoutId;
				if (options?.signal) request.signal = options.signal;
				else if (options?.timeoutMs && options.timeoutMs > 0) {
					const controller = new AbortController();
					fetchTimeoutId = setTimeout(() => {
						controller.abort();
					}, options.timeoutMs);
					request.signal = controller.signal;
				}
				let response;
				if (options?.progress) response = await invokeXHR(completeUrl, request);
				else response = await fetchAndTransformExceptions(completeUrl, request);
				if (fetchTimeoutId) clearTimeout(fetchTimeoutId);
				return await parseFetchResponse(response, completeUrl);
			}
			function encodeBody(unencodedBody, contentType) {
				if (!unencodedBody) return {
					body: null,
					contentTypeHeader: {},
					requestOptions: {}
				};
				const contentTypeHeader = {};
				const requestOptions = {};
				let body = null;
				switch (contentType) {
					case "":
					case "application/json":
						contentTypeHeader["Content-Type"] = "application/json";
						body = JSON.stringify(unencodedBody);
						break;
					case "multipart/form-data":
						body = encodeMultipartBody(unencodedBody);
						break;
					case "application/octet-stream":
						contentTypeHeader["Content-Type"] = contentType;
						requestOptions["duplex"] = "half";
						body = unencodedBody;
						break;
					case "text/plain":
						if (typeof unencodedBody === "string") {
							contentTypeHeader["Content-Type"] = contentType;
							body = unencodedBody;
						} else throw new EncodingError(`Cannot send ${typeof unencodedBody} as ${contentType}, body should be a string.`, contentType, unencodedBody);
						break;
					default: throw new EncodingError(`Unsupported content-type "${contentType}", supported are: application/json, multipart/form-data, application/octet-stream and text/plain`, contentType, unencodedBody);
				}
				return {
					body,
					contentTypeHeader,
					requestOptions
				};
			}
			function encodeMultipartBody(unencodedBody) {
				const contentType = "multipart/form-data";
				if (typeof unencodedBody !== "object") throw new EncodingError(`Cannot encode ${typeof unencodedBody} as ${contentType}, body should be an object.`, contentType, unencodedBody);
				if (Array.isArray(unencodedBody)) throw new EncodingError(`Cannot encode ${typeof unencodedBody} as ${contentType}, body should be an object.`, contentType, unencodedBody);
				if (unencodedBody instanceof FormData) return unencodedBody;
				const form = new FormData();
				Object.entries(unencodedBody).forEach((entry) => {
					const [key, value] = entry;
					switch (typeof value) {
						case "boolean":
						case "number":
						case "string":
							form.set(key, `${value}`);
							break;
						case "object":
							if (value instanceof Blob) form.set(key, value);
							else if (value instanceof Uint8Array) {
								const data = new Blob([value], { type: "application/octet-stream" });
								form.set(key, data);
							} else {
								const json = JSON.stringify(value);
								const data = new Blob([json], { type: "application/json" });
								form.set(key, data, "");
							}
							break;
						default: throw new EncodingError(`Cannot encode multipart-field "${key}" with value of type ${typeof value}, values must be objects, strings, numbers or boolean.`, contentType, unencodedBody);
					}
				});
				return form;
			}
			async function getInvokeFetchUrlParams({ method, pathTemplate, pathVariables, query, options }) {
				const locationUrl = toValidLocationUrl(options?.hostConfig);
				const { headers: authHeaders, queryParams: authQueryParams, credentials } = await getRestCallAuthParams({
					hostConfig: options?.hostConfig,
					method
				});
				const url = locationUrl + applyPathVariables(pathTemplate, pathVariables);
				const completeUrl = toCompleteUrl(url, encodeQueryParams({
					...query,
					...authQueryParams
				}));
				const serializedHostConfig = serializeHostConfig$1(options?.hostConfig);
				return {
					completeUrl,
					authHeaders,
					credentials,
					cacheKey: toCacheKey({
						url,
						query,
						headers: options?.headers,
						serializedHostConfig
					})
				};
			}
			function invokeFetchWithUrl(api, props) {
				return invokeFetchWithUrlAndRetry(api, props, async () => {
					const { authHeaders, credentials } = await getInvokeFetchUrlParams(props);
					return invokeFetchWithUrlAndRetry(api, {
						...props,
						authHeaders,
						credentials,
						options: {
							...props.options,
							noCache: true
						}
					}, void 0);
				});
			}
			function invokeFetchWithUrlAndRetry(api, props, performRetry) {
				const { method, completeUrl, body, options, authHeaders, credentials, cacheKey, contentType, userAgent } = props;
				const cachingContext = {
					method,
					completeUrl,
					cacheKey,
					options
				};
				const cachedResponse = getFromCache(api, cachingContext);
				if (cachedResponse) return cachedResponse;
				const resultPromiseFromBackend = performActualHttpFetch(method, completeUrl, body, contentType, options, authHeaders, credentials, userAgent);
				const resultAfterAuthenticationCheck = interceptAuthenticationErrors(options?.hostConfig, resultPromiseFromBackend, performRetry);
				return updateCache(api, cachingContext, addPagingFunctions(api, {
					...props,
					value: resultAfterAuthenticationCheck
				}));
			}
			/**
			* Adds paging functions to the response object if there are paging links present in the response body
			*/
			function addPagingFunctions(api, { method, body, options, authHeaders, credentials, value }) {
				return value.then((resp) => {
					const dataWithPotentialLinks = resp.data;
					if (!dataWithPotentialLinks) return resp;
					const serializedHostConfig = serializeHostConfig$1(options?.hostConfig);
					const prevUrl = dataWithPotentialLinks.links?.prev?.href;
					const nextUrl = dataWithPotentialLinks.links?.next?.href;
					if (prevUrl) resp.prev = (prevOptions) => invokeFetchWithUrl(api, {
						method,
						completeUrl: prevUrl,
						body,
						options: prevOptions || options,
						authHeaders,
						credentials,
						cacheKey: toCacheKey({
							url: prevUrl,
							headers: options?.headers,
							serializedHostConfig
						})
					});
					if (nextUrl) resp.next = (nextOptions) => invokeFetchWithUrl(api, {
						method,
						completeUrl: nextUrl,
						body,
						options: nextOptions || options,
						authHeaders,
						credentials,
						cacheKey: toCacheKey({
							url: nextUrl,
							headers: options?.headers,
							serializedHostConfig
						})
					});
					return resp;
				});
			}
			function neverResolvingPromise() {
				return new Promise(() => {});
			}
			async function interceptAuthenticationErrors(hostConfig, resultPromise, performRetry) {
				try {
					return await resultPromise;
				} catch (error) {
					const err = error;
					const errorBody = err.data;
					if (err.status === 401 || err.status === 403 && errorBody?.code === "CSRF-TOKEN-2" || (err.status === 301 || err.status === 302) && await isWindows(hostConfig)) {
						if (globalThis.loggingOut) return neverResolvingPromise();
						const { retry, preventDefault } = await handleAuthenticationError({
							hostConfig,
							status: err.status,
							headers: err.headers,
							errorBody,
							canRetry: !!performRetry
						});
						if (retry && performRetry) return performRetry();
						if (preventDefault) return neverResolvingPromise();
					}
					throw error;
				}
			}
			/** Set the X-Qlik-Overrides header according to what's written to localStorage
			*  by qmfe-devtool */
			function getServiceOverrideHeaderFromLocalStorage() {
				if (!isBrowser()) return {};
				const header = localStorage.getItem("qmfe-api-service-overrides-header");
				if (!header) return {};
				return { "X-Qlik-Overrides": header };
			}
			/** Convert a Blob to a DownloadableBlob */
			function toDownloadableBlob(blob, name) {
				const result$1 = blob;
				if (name) result$1.download = (filename = name) => download(blob, filename);
				else result$1.download = (filename) => download(blob, filename);
				return result$1;
			}
			/** Convenience-function for downloading a blob. */
			async function download(blob, filename) {
				if (isBrowser()) {
					const a = document.createElement("a");
					const blobUrl = window.URL.createObjectURL(blob);
					a.href = blobUrl;
					a.download = filename;
					a.click();
					window.URL.revokeObjectURL(blobUrl);
				} else {
					const { writeFileSync } = await Promise.resolve().then(function () { return _nodeResolve_empty; });
					const arrayBuffer = await blob.arrayBuffer();
					writeFileSync(filename, new Uint8Array(arrayBuffer));
				}
			}

			//#endregion
			//#region src/invoke-fetch/invoke-fetch-functions.ts
			const defaultUserAgent = "qmfe-api/latest";
			/**
			* Makes an HTTP call with the browser's fetch API to the endpoint configured in the call's arguments
			* @param api Name of api to call. Will be used for caching responses.
			* @param invokeFetchProps InvokeFetchProperties
			*/
			async function invokeFetch(api, props, interceptors) {
				const effectiveInterceptors = getInterceptors();
				const invokeFetchFinal = (reqeust) => invokeFetchIntercepted(api, reqeust);
				return (effectiveInterceptors || []).reduce((proceed, interceptor) => (request) => interceptor(request, proceed), invokeFetchFinal)(props);
			}
			/** @private Runs the intercepted version of invoke fetch */
			async function invokeFetchIntercepted(api, props) {
				if (props.options?.hostConfig?.authType === "mock-backend-rest-recorder") return Promise.resolve(props.options?.hostConfig.recordInvokeFetch(props));
				if (props.options?.hostConfig?.authType === "mock-backend") return Promise.resolve(props.options?.hostConfig.mockInvokeFetch(props));
				checkForCrossDomainRequest(props.options?.hostConfig);
				if (props.options?.hostConfig?.authType === "noauth") {
					console.info("API call blocked: noauth auth module is configured, no API calls should be made");
					return {
						status: 204,
						headers: new Headers(),
						data: null
					};
				}
				let userAgent;
				if (props?.userAgent) userAgent = props.userAgent;
				else if (isBrowser()) userAgent = `${window.navigator.userAgent} ${defaultUserAgent}`;
				else userAgent = defaultUserAgent;
				const { completeUrl, authHeaders, credentials, cacheKey } = await getInvokeFetchUrlParams(props);
				return invokeFetchWithUrl(api, {
					...props,
					method: props.method.toUpperCase(),
					completeUrl,
					authHeaders,
					credentials,
					cacheKey,
					userAgent
				});
			}
			/**
			* Function that extracts the payload from the fetch response.
			* @param fetchResponse fetch response
			* @param url to where the request as made
			* @returns parsed fetch response
			*/
			async function parseFetchResponse(fetchResponse, url) {
				let resultData;
				const contentType = fetchResponse.headers.get("content-type")?.split(";")[0];
				const contentDisposition = fetchResponse.headers.get("content-disposition")?.split(";");
				if (contentDisposition && contentDisposition[0] === "attachment") {
					let filename = "";
					for (let i = 1; i < contentDisposition.length; i++) {
						const attr = contentDisposition[i].trim();
						if (attr.indexOf("filename") === 0) {
							const start = attr.indexOf("\"");
							const end = attr.lastIndexOf("\"");
							filename = attr.slice(start + 1, end);
						}
					}
					resultData = toDownloadableBlob(await fetchResponse.blob(), filename);
				} else switch (contentType) {
					case "image/png":
					case "image/jpeg":
					case "image/x-icon":
					case "application/offset+octet-stream":
					case "application/octet-stream":
					case "application/zip":
					case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
					case "application/pdf":
						resultData = toDownloadableBlob(await fetchResponse.blob());
						break;
					case "text/event-stream":
						resultData = fetchResponse.body;
						break;
					default:
						try {
							resultData = await fetchResponse.text();
							resultData = JSON.parse(resultData);
						} catch {}
						break;
				}
				const { status, statusText, headers } = fetchResponse;
				const errorMsg = `request to '${url}' failed with status ${status} ${statusText}.`;
				if (status >= 300) throw new InvokeFetchError(errorMsg, status, headers, resultData);
				if (status === 0) throw new InvokeFetchError(errorMsg, 302, headers, resultData);
				return {
					status,
					headers,
					data: resultData
				};
			}

			//#endregion
			//#region src/http/http-constants.ts
			/**
			* Name of the CSRF Token header.
			*/
			const QLIK_CSRF_TOKEN = "qlik-csrf-token";

			//#endregion
			//#region src/http/http-functions.ts
			/**
			* Clears the CSRF token
			* @param location an optional location that is used to clear the csrf tokens when embedded
			*/
			function clearCsrfToken(hostConfig) {
				const locationUrl = toValidLocationUrl(hostConfig);
				delete csrfTokens[locationUrl];
			}
			/**
			* Fetches the CSRF token from the server. The token is cached and will be reused for subsequent calls.
			* @param hostConfig HostConfig object containing the location url and authentication options
			* @param noCache Set to true to force a new token to be fetched
			* @returns csrf token
			*/
			async function getCsrfToken(hostConfig, noCache) {
				const locationUrl = toValidLocationUrl(hostConfig);
				let pathTemplate;
				if (await isWindows(hostConfig)) pathTemplate = "/qps/csrftoken";
				else pathTemplate = "/api/v1/csrf-token";
				const fetchCsrfToken = async () => {
					try {
						const csrfToken = (await invokeFetch("csrf-token", {
							method: "get",
							pathTemplate,
							options: {
								hostConfig,
								noCache: true
							}
						})).headers.get(QLIK_CSRF_TOKEN);
						if (!csrfToken) return "";
						return csrfToken;
					} catch (e) {
						if (e.status === 404) return "";
						throw e;
					}
				};
				if (noCache) {
					csrfTokens[locationUrl] = fetchCsrfToken();
					return csrfTokens[locationUrl];
				}
				csrfTokens[locationUrl] = csrfTokens[locationUrl] || fetchCsrfToken();
				return csrfTokens[locationUrl];
			}
			const csrfTokens = {};

			//#endregion
			//#region src/auth/internal/internal-auth-functions.ts
			/**
			* Returns the fetch credentials string based on the host config. Note that this
			* assumes cookies are used so it should only be used by the cookie auth modes
			* @param hostConfig the HostConfig containing authentication details
			* @private
			*/
			function internalGetCredentialsForCookieAuth(hostConfig) {
				if (hostConfig.crossSiteCookies === false) return "same-origin";
				if (isHostCrossOrigin(hostConfig)) return "include";
				return "same-origin";
			}
			async function resolveTokenIfPresent(getAccessToken$1) {
				if (typeof getAccessToken$1 === "function") return getAccessToken$1();
				if (typeof getAccessToken$1 === "string") {
					const accessTokenFn = lookupGlobalGetAccessTokenFn(getAccessToken$1);
					if (typeof accessTokenFn !== "function") throw new Error(`getAccessToken function "${getAccessToken$1}" not found on globalThis. Please make sure it is defined.`);
					return accessTokenFn();
				}
			}
			function lookupGlobalGetAccessTokenFn(getAccessToken$1) {
				return globalThis[getAccessToken$1];
			}

			//#endregion
			//#region src/auth/internal/default-auth-modules/cookie.ts
			/**
			* Returns true if the verb might modify things requiring caches to be cleared
			*/
			function isModifyingVerb(verb) {
				return !(verb === "get" || verb === "GET");
			}
			async function getRestCallAuthParams$7({ hostConfig, method }) {
				const headers = {};
				if (isModifyingVerb(method)) headers["qlik-csrf-token"] = await getCsrfToken(hostConfig);
				if (hostConfig.webIntegrationId) headers["qlik-web-integration-id"] = hostConfig.webIntegrationId;
				return {
					headers,
					queryParams: {},
					credentials: internalGetCredentialsForCookieAuth(hostConfig)
				};
			}
			async function getWebSocketAuthParams$7({ hostConfig }) {
				if (isNode()) {
					const headers = {};
					headers["qlik-csrf-token"] = await getCsrfToken(hostConfig);
					if (hostConfig.webIntegrationId) headers["qlik-web-integration-id"] = hostConfig.webIntegrationId;
					return { headers };
				}
				const params = { "qlik-csrf-token": await getCsrfToken(hostConfig, true) };
				if (hostConfig.webIntegrationId) params["qlik-web-integration-id"] = hostConfig.webIntegrationId;
				return { queryParams: params };
			}
			async function handleAuthenticationError$7({ hostConfig, status }) {
				clearCsrfToken(hostConfig);
				if (status === 403) return {
					preventDefault: false,
					retry: true
				};
				const webIntegrationParam = hostConfig.webIntegrationId ? `qlik-web-integration-id=${hostConfig?.webIntegrationId}&` : "";
				const locationUrl = toValidLocationUrl(hostConfig);
				if (hostConfig.authRedirectUserConfirmation) await hostConfig.authRedirectUserConfirmation();
				globalThis.location.replace(`${locationUrl}/login?${webIntegrationParam}returnto=${encodeURIComponent(globalThis.location.href)}`);
				return { preventDefault: true };
			}
			/** @private */
			var cookie_default = {
				requiredProps: [],
				optionalProps: [
					"webIntegrationId",
					"crossSiteCookies",
					"anonymousMode"
				],
				getRestCallAuthParams: getRestCallAuthParams$7,
				getWebSocketAuthParams: getWebSocketAuthParams$7,
				handleAuthenticationError: handleAuthenticationError$7
			};

			//#endregion
			//#region src/auth/internal/default-auth-modules/noauth.ts
			function getRestCallAuthParams$6(_props) {
				return Promise.resolve({
					headers: {},
					queryParams: {},
					credentials: "same-origin"
				});
			}
			function getWebSocketAuthParams$6(_props) {
				return Promise.resolve({ queryParams: {} });
			}
			function handleAuthenticationError$6(_props) {
				return Promise.resolve({});
			}
			/** @private */
			var noauth_default = {
				requiredProps: [],
				optionalProps: [],
				getRestCallAuthParams: getRestCallAuthParams$6,
				getWebSocketAuthParams: getWebSocketAuthParams$6,
				handleAuthenticationError: handleAuthenticationError$6
			};

			//#endregion
			//#region src/auth/internal/default-auth-modules/none.ts
			function getRestCallAuthParams$5() {
				return Promise.resolve({
					headers: {},
					queryParams: {},
					credentials: "same-origin"
				});
			}
			function getWebSocketAuthParams$5() {
				return Promise.resolve({ queryParams: {} });
			}
			function handleAuthenticationError$5() {
				return Promise.resolve({});
			}
			/** @private */
			var none_default = {
				requiredProps: [],
				optionalProps: [],
				getRestCallAuthParams: getRestCallAuthParams$5,
				getWebSocketAuthParams: getWebSocketAuthParams$5,
				handleAuthenticationError: handleAuthenticationError$5
			};

			//#endregion
			//#region src/auth/internal/default-auth-modules/oauth/callback.ts
			/**
			* Receives the oauth code and state callback query parameters,
			* verifies the state, saves the code in session storage, and then
			* redirects the page to the final href if it is set and differes from current uri
			*/
			function handleOAuthCallback() {
				const urlParams = new URLSearchParams(globalThis.location.search);
				const callbackCode = urlParams.get("code") || void 0;
				const callbackState = urlParams.get("state") || void 0;
				if (urlParams.get("error")) {
					const element = document.createElement("pre");
					element.innerText = `<code>${JSON.stringify({
			error: urlParams.get("error"),
			error_code: urlParams.get("error_code"),
			error_description: urlParams.get("error_description"),
			error_detail: urlParams.get("error_detail"),
			error_uri: urlParams.get("error_uri")
		})}</code>`;
					document.body.prepend(element);
				}
				const topic = loadAndDeleteFromSessionStorage("", "client-in-progress");
				if (topic && callbackCode && callbackState) {
					const stateFromLocalStorage = loadAndDeleteFromSessionStorage(topic, "state");
					const finalRedirectUri = loadAndDeleteFromSessionStorage(topic, "href");
					if (stateFromLocalStorage && stateFromLocalStorage === callbackState && finalRedirectUri) {
						saveInSessionStorage(topic, "code", callbackCode);
						if (finalRedirectUri !== globalThis.location.href) globalThis.location.replace(finalRedirectUri);
					}
				}
			}

			//#endregion
			//#region src/auth/internal/default-auth-modules/oauth.ts
			if (isBrowser()) handleOAuthCallback();
			/**
			* Retries the passed in function after calling handleAuthenticationError
			*/
			async function handlePotentialAuthenticationErrorAndRetry(hostConfig, fn) {
				try {
					return await fn();
				} catch (err) {
					const { retry } = await handleAuthenticationError$4({
						hostConfig});
					if (retry) return fn();
					throw err;
				}
			}
			async function getRestCallAuthParams$4({ hostConfig }) {
				return {
					headers: { Authorization: `Bearer ${await getOAuthAccessToken(hostConfig)}` },
					queryParams: {},
					credentials: "omit"
				};
			}
			async function getWebSocketAuthParams$4({ hostConfig }) {
				if (isNode()) return { headers: { Authorization: `Bearer ${await getOAuthAccessToken(hostConfig)}` } };
				return { queryParams: { accessToken: await handlePotentialAuthenticationErrorAndRetry(hostConfig, async () => {
					return exchangeAccessTokenForTemporaryToken(hostConfig, await getOAuthAccessToken(hostConfig), "websocket");
				}) } };
			}
			async function getWebResourceAuthParams$1({ hostConfig }) {
				return { queryParams: { accessToken: await handlePotentialAuthenticationErrorAndRetry(hostConfig, async () => {
					return exchangeAccessTokenForTemporaryToken(hostConfig, await getOAuthAccessToken(hostConfig), "webresource");
				}) } };
			}
			async function handleAuthenticationError$4({ hostConfig }) {
				if (hostConfig.getAccessToken) {
					clearStoredOauthTokens(hostConfig);
					return {
						preventDefault: false,
						retry: true
					};
				}
				if (isBrowser()) {
					if (hostConfig.performInteractiveLogin) {
						clearStoredOauthTokens(hostConfig);
						return { retry: true };
					}
					if (hostConfig.authRedirectUserConfirmation) await hostConfig.authRedirectUserConfirmation();
					startFullPageLoginFlow(hostConfig);
					return { preventDefault: true };
				}
				await refreshAccessToken(hostConfig);
				return {
					preventDefault: false,
					retry: true
				};
			}
			var oauth_default = {
				requiredProps: ["clientId"],
				optionalProps: [
					"clientSecret",
					"redirectUri",
					"accessTokenStorage",
					"scope",
					"subject",
					"userId",
					"noCache",
					"getAccessToken",
					"performInteractiveLogin"
				],
				getRestCallAuthParams: getRestCallAuthParams$4,
				getWebSocketAuthParams: getWebSocketAuthParams$4,
				getWebResourceAuthParams: getWebResourceAuthParams$1,
				handleAuthenticationError: handleAuthenticationError$4
			};

			//#endregion
			//#region src/auth/internal/default-auth-modules/reference.ts
			function getRestCallAuthParams$3() {
				throw new Error("getRestCallAuthParams should never be called for reference auth module");
			}
			function getWebSocketAuthParams$3() {
				throw new Error("getWebSocketAuthParams should never be called for reference auth module");
			}
			function handleAuthenticationError$3() {
				throw new Error("handleAuthenticationError should never be called for reference auth module");
			}
			/**
			* This auth module is only here to provide a reference for the auth type "reference". It should never be used in practice.
			*  @private */
			var reference_default = {
				requiredProps: ["reference"],
				optionalProps: [],
				getRestCallAuthParams: getRestCallAuthParams$3,
				getWebSocketAuthParams: getWebSocketAuthParams$3,
				handleAuthenticationError: handleAuthenticationError$3
			};

			//#endregion
			//#region src/auth/internal/default-auth-modules/windows-cookie/xrf-keys.ts
			const xrfKeys = {};
			/**
			* @returns {string} 16 character long xrf-key
			*/
			function createXrfKey() {
				let result$1 = "";
				for (let i = 0; i < 16; i += 1) {
					const j = Math.floor(Math.random() * 62);
					if (j < 10) result$1 += j;
					else if (j > 9 && j < 36) result$1 += String.fromCharCode(j + 55);
					else result$1 += String.fromCharCode(j + 61);
				}
				return result$1;
			}
			function getXrfKey(hostConfig) {
				const locationUrl = toValidLocationUrl(hostConfig);
				xrfKeys[locationUrl] = xrfKeys[locationUrl] || createXrfKey();
				return xrfKeys[locationUrl];
			}

			//#endregion
			//#region src/auth/internal/default-auth-modules/windows-cookie.ts
			async function getRestCallAuthParams$2({ hostConfig }) {
				return {
					headers: { "X-Qlik-XrfKey": getXrfKey(hostConfig) },
					queryParams: { xrfkey: getXrfKey(hostConfig) },
					credentials: internalGetCredentialsForCookieAuth(hostConfig)
				};
			}
			async function getWebSocketAuthParams$2({ hostConfig }) {
				return { queryParams: {
					xrfkey: getXrfKey(hostConfig),
					"qlik-csrf-token": await getCsrfToken(hostConfig, true)
				} };
			}
			async function handleAuthenticationError$2({ hostConfig }) {
				if (hostConfig.loginUri) {
					if (hostConfig.authRedirectUserConfirmation) await hostConfig.authRedirectUserConfirmation();
					globalThis.location.replace(hostConfig.loginUri.replace("{location}", encodeURIComponent(globalThis.location.href)));
					return { preventDefault: true };
				}
				if (hostConfig.getAccessToken) {
					const token = await resolveTokenIfPresent(hostConfig.getAccessToken);
					if (token) {
						await fetch(`${toValidLocationUrl(hostConfig)}/qlik-embed/main.js`, {
							method: "GET",
							mode: "cors",
							credentials: "include",
							headers: {
								"Content-Type": "application/json",
								Authorization: `Bearer ${token}`
							}
						});
						return { retry: true };
					}
				}
				return {};
			}
			/** @private */
			var windows_cookie_default = {
				requiredProps: [],
				optionalProps: [
					"loginUri",
					"crossSiteCookies",
					"getAccessToken"
				],
				getRestCallAuthParams: getRestCallAuthParams$2,
				getWebSocketAuthParams: getWebSocketAuthParams$2,
				handleAuthenticationError: handleAuthenticationError$2
			};

			//#endregion
			//#region src/auth/internal/default-auth-modules/windows-cookie-node.ts
			const nodeCookieStore = /* @__PURE__ */ new Map();
			function clearCookieInStore(hostConfig) {
				const key = serializeHostConfig$1(hostConfig);
				nodeCookieStore.delete(key);
			}
			function setCookieInStore(hostConfig, cookie) {
				const key = serializeHostConfig$1(hostConfig);
				nodeCookieStore.set(key, cookie);
			}
			function getCookieInStore(hostConfig) {
				const key = serializeHostConfig$1(hostConfig);
				return nodeCookieStore.get(key);
			}
			async function getCookie(hostConfig) {
				let cookie = getCookieInStore(hostConfig);
				if (!cookie) {
					const token = await resolveTokenIfPresent(hostConfig.getAccessToken);
					if (!token) throw new Error("Failed to acquire access token");
					cookie = (await fetch(`${toValidLocationUrl(hostConfig)}/qlik-embed/main.js`, {
						method: "GET",
						mode: "cors",
						credentials: "include",
						headers: {
							"Content-Type": "application/json",
							Authorization: `Bearer ${token}`
						}
					})).headers.get("Set-Cookie") || void 0;
					if (cookie) setCookieInStore(hostConfig, cookie);
				}
				if (!cookie) throw new Error("Failed to exchange access token for cookie");
				return cookie;
			}
			async function getRestCallAuthParams$1({ hostConfig }) {
				return {
					queryParams: { xrfkey: getXrfKey(hostConfig) },
					headers: {
						Cookie: await getCookie(hostConfig),
						"X-Qlik-XrfKey": getXrfKey(hostConfig)
					},
					credentials: internalGetCredentialsForCookieAuth(hostConfig)
				};
			}
			async function getWebSocketAuthParams$1({ hostConfig }) {
				return {
					queryParams: { "qlik-csrf-token": await getCsrfToken(hostConfig, true) },
					headers: { Cookie: await getCookie(hostConfig) }
				};
			}
			async function handleAuthenticationError$1({ hostConfig }) {
				clearCookieInStore(hostConfig);
				return { retry: true };
			}
			/** @private */
			var windows_cookie_node_default = {
				requiredProps: [],
				optionalProps: [
					"loginUri",
					"crossSiteCookies",
					"getAccessToken"
				],
				validateHostConfig: (hostConfig) => {
					if (typeof hostConfig.getAccessToken === "function") return true;
					if (typeof hostConfig.getAccessToken === "string") {
						if (!!!globalThis[hostConfig.getAccessToken]) throw new InvalidHostConfigError("The \"getAccessToken\" function name provided does not exist on globalThis.");
						return true;
					}
					throw new InvalidHostConfigError("The \"getAccessToken\" property must be a function or the name of a globally defined function.");
				},
				getRestCallAuthParams: getRestCallAuthParams$1,
				getWebSocketAuthParams: getWebSocketAuthParams$1,
				handleAuthenticationError: handleAuthenticationError$1
			};

			//#endregion
			//#region src/auth/internal/auth-module-registry.ts
			/**
			* Auth module registry
			*/
			const authModules = {};
			/**
			* A promise that gets recreated when an auth module is currently being loaded
			*/
			let ongoingAuthModuleLoading = Promise.resolve();
			let authModulesRegistered = false;
			(function registerDefaultAuthModules() {
				if (!authModulesRegistered) {
					registerAuthModule("apikey", apikey_default);
					registerAuthModule("cookie", cookie_default);
					registerAuthModule("none", none_default);
					registerAuthModule("noauth", noauth_default);
					registerAuthModule("oauth2", oauth_default);
					registerAuthModule("anonymous", anonymous_default);
					if (isBrowser()) registerAuthModule("windowscookie", windows_cookie_default);
					else registerAuthModule("windowscookie", windows_cookie_node_default);
					registerAuthModule("reference", reference_default);
					authModulesRegistered = true;
				}
			})();
			/**
			* Registers an auth module that can handle authentication. An auth module is used by specifying its name as authType in the HostConfig passed in to api calls.
			* @param name the name of the module
			* @param authModule the implementation of the AuthModule interface
			*/
			function registerAuthModule(name, authModule) {
				authModules[name.toLowerCase()] = authModule;
			}
			/**
			* Get the names of all registered AuthModules.
			* @returns Array of all registered AuthModules
			*/
			function getRegisteredAuthModules() {
				return Object.keys(authModules);
			}
			/**
			*  Gets the auth module with the given name. The name is case insensitive.
			* @param authType The name of the auth module to get. The name is case insensitive.
			* @returns
			*/
			function getRegisteredAuthModule(authType) {
				return authModules[authType.toLowerCase()];
			}
			/**
			* Gets the auth module for a host config. If only clientId is set, then authType is assumed and set to "oauth2".
			* If host config is undefined or empty, then authType is assumed and set to "cookie" or "windowscookie" depending
			*  on which platform the host is running on.
			*
			* @private
			* @param hostConfig
			*/
			async function getAuthModule(hostConfig) {
				const hostConfigToUse = withResolvedHostConfig(hostConfig);
				const authType = await determineAuthType(hostConfigToUse);
				if (ongoingAuthModuleLoading) await ongoingAuthModuleLoading;
				let authModule = getRegisteredAuthModule(authType);
				if (!authModule) {
					/**
					* This is basically poor mans synchronous block so that one globally defined asynchronous auth module factory function
					* is not called more than once if several calls to getAuthModule come in in sequence.
					*/
					ongoingAuthModuleLoading = (async () => {
						authModule = await resolveGloballyDefinedAuthModule(authType);
						if (authModule) registerAuthModule(authType, authModule);
					})();
					await ongoingAuthModuleLoading;
				}
				if (!authModule) throw new InvalidAuthTypeError(authType);
				if (authModule.validateHostConfig) authModule.validateHostConfig({
					authType,
					...hostConfigToUse
				});
				else internalValidateHostConfig({
					authType,
					...hostConfigToUse
				}, {
					requiredProps: authModule.requiredProps || [],
					optionalProps: authModule.optionalProps || []
				});
				return authModule;
			}
			async function resolveGloballyDefinedAuthModule(authType) {
				const globalVariable = globalThis[authType];
				if (globalVariable) {
					let potentialAuthModule;
					if (typeof globalVariable === "function") potentialAuthModule = await globalVariable();
					else potentialAuthModule = globalVariable;
					if (potentialAuthModule && potentialAuthModule.getRestCallAuthParams && potentialAuthModule.getWebSocketAuthParams && potentialAuthModule.handleAuthenticationError) return potentialAuthModule;
					console.error("Not a valid auth module", potentialAuthModule);
					throw new InvalidAuthTypeError(authType);
				}
				return Promise.resolve(void 0);
			}
			/**
			* @private
			* Helper function for validating a host config in runtime
			* @param hostConfig - The host config to validate
			* @param options - Options for validation
			* @param options.requiredProps - The required properties in the host config
			* @param options.optionalProps - The optional properties in the host config
			* @throws {InvalidHostConfigError} If the host config is invalid
			* @returns {boolean} Returns true if the host config is valid
			* */
			function internalValidateHostConfig(hostConfig, { requiredProps, optionalProps }) {
				const missingRequiredProps = [];
				for (const requiredProp of requiredProps) if (!hostConfig[requiredProp]) missingRequiredProps.push(requiredProp);
				if (missingRequiredProps.length > 0) throw new InvalidHostConfigError(`missing required properties in host config; '${missingRequiredProps.join("', '")}'`);
				const validProps = [
					...hostConfigCommonProperties,
					...requiredProps,
					...optionalProps
				];
				const invalidKeys = [];
				Object.keys(hostConfig).forEach((key) => {
					if (!validProps.includes(key)) invalidKeys.push(key);
				});
				if (invalidKeys.length > 0) console.warn(`WARNING: unknown properties in host config; '${invalidKeys.join("', '")}'`);
				return true;
			}
			/**
			* Determines the authType associated with a HostConfig even if it's
			* not explicitly set.
			*/
			async function determineAuthType(hostConfig) {
				if (hostConfig.authType) return hostConfig.authType;
				if (hostConfig.apiKey) return "apikey";
				if (hostConfig.accessCode) return "anonymous";
				if (hostConfig.clientId) return "oauth2";
				if (hostConfig.webIntegrationId) return "cookie";
				if (hostConfig.reference) return "reference";
				if (await isWindows(hostConfig)) return "windowscookie";
				return "cookie";
			}

			//#endregion
			//#region src/auth/auth-errors.ts
			/** Error is thrown when a host config has incorrect or missing settings */
			var InvalidHostConfigError = exports("InvalidHostConfigError", class extends Error {
				constructor(message) {
					super(`Invalid host config: ${message}`);
					this.name = "InvalidHostConfigError";
				}
			});
			/** Error is thrown when a host config has an auth type with incompatible settings */
			var UnexpectedAuthTypeError = exports("UnexpectedAuthTypeError", class extends Error {
				constructor(...expectedAuthTypes) {
					const ors = expectedAuthTypes.map((item, index) => index === 0 ? `"${item}"` : `or "${item}"`).join(" ");
					super(`HostConfig is not properly configured. authType is expected to be ${ors}`);
					this.name = "UnexpectedAuthTypeError";
				}
			});
			/** Error is thrown when a host config uses an auth type that has not been registered */
			var InvalidAuthTypeError = exports("InvalidAuthTypeError", class extends Error {
				constructor(authType) {
					const validAuthModules = getRegisteredAuthModules();
					super(`Not a valid auth type: ${authType}, valid auth types are; '${validAuthModules.filter((name) => name.toLowerCase() !== "qmfeembedframerauthmodule").join("', '")}'`);
					this.name = "InvalidAuthTypeError";
				}
			});
			function errorToString({ title, detail, code, status }) {
				if (detail) return `${title} - ${detail} (Status: ${status}, Code: ${code})`;
				return `${title} (Status: ${status}, Code: ${code})`;
			}
			var AuthorizationError = exports("AuthorizationError", class extends Error {
				errors;
				constructor(errors) {
					if (typeof errors !== "object") {
						super("Unknown error");
						return;
					}
					const errorArray = Array.isArray(errors) ? errors : [errors];
					super(errorArray.map(errorToString).join(", "));
					this.errors = errorArray;
				}
			});

			//#endregion
			//#region src/auth/internal/host-config-functions.ts
			/**
			* Returns a new host config with all default and falsy values removed.
			* @param hostConfig - The host config to fill with defaults
			* @returns
			*/
			function removeDefaults(hostConfig) {
				const cleanedHostConfig = cleanFalsyValues(hostConfig) || {};
				if (cleanedHostConfig.host) cleanedHostConfig.host = toValidLocationUrl(cleanedHostConfig);
				if (isBrowser()) {
					if (toValidLocationUrl(cleanedHostConfig) === window.location.origin) delete cleanedHostConfig.host;
				}
				if (cleanedHostConfig.authType && authTypesThatCanBeOmitted.includes(cleanedHostConfig.authType)) delete cleanedHostConfig.authType;
				return cleanedHostConfig;
			}
			function globalReplacer(key, value) {
				if (typeof value === "function") return;
				return value;
			}
			/**
			* Serializes the provided hostConfig, if present, otherwise the default one.
			*/
			function serializeHostConfig(hostConfig) {
				const sorted = sortKeys(removeDefaults(withResolvedHostConfig(hostConfig)));
				return JSON.stringify(sorted, globalReplacer);
			}
			const registeredHostConfigs = /* @__PURE__ */ new Map();
			/**
			* Registers a host config with the given name.
			* @param name The name of the host config to be used to reference the host config later.
			* @param hostConfig The host config to register.
			*/
			function registerHostConfig(name, hostConfig) {
				const reference = hostConfig?.reference || null;
				if (reference && !registeredHostConfigs.has(reference)) throw new InvalidHostConfigError(`Host config with reference "${reference}" is not registered. Please register it before using it.`);
				if (registeredHostConfigs.has(name)) console.warn(`registerHostConfig: Host config with name "${name}" is already registered. Overwriting.`);
				registeredHostConfigs.set(name, hostConfig);
			}
			/**
			* Unregisters a host config with the given name.
			* @param name The name of the host config to unregister.
			*/
			function unregisterHostConfig(name) {
				if (registeredHostConfigs.has(name)) registeredHostConfigs.delete(name);
				else console.warn(`unregisterHostConfig: Host config with name "${name}" not found.`);
			}
			/**
			* Gets the host config with the given name.
			* @private
			* @param name The name of the host config to get.
			* @returns The host config, or undefined if not found.
			*/
			function getRegisteredHostConfig(name) {
				return registeredHostConfigs.get(name);
			}
			/**
			* Sets the default host config that will be used for all api calls that do not include a HostConfig
			* @private
			* @param hostConfig the default HostConfig to use
			*/
			function setDefaultHostConfig(hostConfig) {
				registerHostConfig("default", hostConfig || {});
			}
			/**
			* Gets the default host config that will be used for all qmfe api calls that do not include a HostConfig.
			* @private
			* @returns The default host config that will be used for all qmfe api calls that do not include a HostConfig
			*/
			function getDefaultHostConfig() {
				return getRegisteredHostConfig("default") || {};
			}

			//#endregion
			//#region src/auth/auth-functions.ts
			/**
			* Set initial loggingOut value to false to make sure it has a value before any auth module is loaded
			*/
			globalThis.loggingOut = false;
			let lastErrorMessage = "";
			/** Default error logger that simply prints the error unless it is identical to the last one (to prevent bloating of the console) */
			function logToConsole({ message }) {
				if (message !== lastErrorMessage) {
					lastErrorMessage = message;
					console.error(message);
				}
			}
			/**
			* Determines the authType associated with a HostConfig even if it's
			* not explicitly set.
			* @param hostConfig the HostConfig containing authentication details
			* @returns promise that resolvs with the determined authentication type
			*/
			function determineAuthType$1(hostConfig) {
				return determineAuthType(hostConfig);
			}
			/**
			* Determines if a host is cross origin or not
			* @param hostConfig the HostConfig containing authentication details
			* @private
			*/
			function isHostCrossOrigin(hostConfig) {
				if (!globalThis.location?.origin) return true;
				const hostConfigToUse = withResolvedHostConfig(hostConfig);
				if (Object.keys(hostConfigToUse).length === 0) return false;
				try {
					return new URL(toValidLocationUrl(hostConfigToUse)).origin !== globalThis.location.origin;
				} catch {}
				return false;
			}
			/**
			* Determines if the host we're connecting to is running on windows or not
			* @param hostConfig the HostConfig containing authentication details
			*/
			async function isWindows(hostConfig) {
				const hostConfigToUse = withResolvedHostConfig(hostConfig);
				if (typeof hostConfigToUse.forceIsWindows === "boolean") return hostConfigToUse.forceIsWindows;
				if (hostConfigToUse.host?.endsWith(".qlik-stage.com") || hostConfigToUse.host?.endsWith(".qlikcloud.com") || hostConfigToUse.host?.endsWith(".qlikcloudgov.com")) return false;
				if (hostConfigToUse.authType === "cookie") return false;
				if (hostConfigToUse.authType === "windowscookie") return true;
				return (await getPlatform({ hostConfig })).isWindows;
			}
			/**
			* Returns a valid location url based on the supplied location string. If location is undefined the default is returned
			* @param hostConfig the HostConfig containing authentication details
			*/
			function toValidLocationUrl(hostConfig) {
				const url = withResolvedHostConfig(hostConfig)?.host?.trim();
				let locationUrl;
				if (!url) locationUrl = "";
				else if (url.toLowerCase().startsWith("https://") || url.toLowerCase().startsWith("http://")) locationUrl = url;
				else locationUrl = `https://${url}`;
				while (locationUrl[locationUrl.length - 1] === "/") locationUrl = locationUrl.substring(0, locationUrl.length - 1);
				return locationUrl;
			}
			/**
			* Returns a valid websocket location url based on the supplied location string. If location is undefined the default is returned
			* @param hostConfig the HostConfig containing authentication details
			*/
			function toValidWebsocketLocationUrl(hostConfig) {
				const url = withResolvedHostConfig(hostConfig)?.host;
				let locationUrl;
				if (!url) locationUrl = globalThis.location.origin;
				else if (url.toLowerCase().startsWith("https://") || url.toLowerCase().startsWith("http://")) locationUrl = url;
				else locationUrl = `https://${url}`;
				while (locationUrl[locationUrl.length - 1] === "/") locationUrl = locationUrl.substring(0, locationUrl.length - 1);
				return locationUrl.replace(leadingHttp, "ws");
			}
			/**
			* Returns a record of query parameters that needs to be added to websockets
			* @param hostConfig the HostConfig containing authentication details
			*/
			async function getWebSocketAuthParams(props) {
				const hostConfigToUse = withResolvedHostConfig(props.hostConfig);
				try {
					return await (await getAuthModule(hostConfigToUse)).getWebSocketAuthParams({
						...props,
						hostConfig: hostConfigToUse
					});
				} catch (err) {
					(hostConfigToUse.onAuthFailed || logToConsole)(normalizeAuthModuleError(err));
					throw err;
				}
			}
			/**
			* Returns a record of query parameters that needs to be added to resources requests, e.g.
			* image tags, etc.
			* @param hostConfig the HostConfig containing authentication details
			*/
			async function getWebResourceAuthParams(props) {
				const hostConfigToUse = withResolvedHostConfig(props.hostConfig);
				try {
					return await (await getAuthModule(hostConfigToUse)).getWebResourceAuthParams?.({
						...props,
						hostConfig: hostConfigToUse
					}) || { queryParams: {} };
				} catch (err) {
					(hostConfigToUse.onAuthFailed || logToConsole)(normalizeAuthModuleError(err));
					throw err;
				}
			}
			/**
			* Calls and return handleAuthenticationError on the authModule a host config is associated with.
			*/
			async function handleAuthenticationError(props) {
				const hostConfigToUse = withResolvedHostConfig(props.hostConfig);
				const result$1 = await (await getAuthModule(hostConfigToUse)).handleAuthenticationError({
					...props,
					hostConfig: hostConfigToUse
				});
				const willRetry = props.canRetry && result$1.retry;
				const willHangUntilANewPageIsLoaded = result$1.preventDefault;
				if (!willRetry && !willHangUntilANewPageIsLoaded) {
					const { status, errorBody } = props;
					(hostConfigToUse.onAuthFailed || logToConsole)(normalizeInbandAuthError({
						status,
						errorBody
					}));
				}
				return result$1;
			}
			/**
			* Returns a record of headers and a record of query params that needs to be added to outgoing rest calls
			* @param props.hostConfig the HostConfig containing authentication details
			* @param props.method the http method, which may affect what authentication are needed.
			*/
			async function getRestCallAuthParams(props) {
				const hostConfigToUse = withResolvedHostConfig(props.hostConfig);
				try {
					return await (await getAuthModule(hostConfigToUse)).getRestCallAuthParams({
						...props,
						hostConfig: hostConfigToUse
					});
				} catch (err) {
					(hostConfigToUse.onAuthFailed || logToConsole)(normalizeAuthModuleError(err));
					throw err;
				}
			}
			/**
			* Returns a record of headers and a record of query params that needs to be added to outgoing rest calls
			* @param props.hostConfig the HostConfig containing authentication details
			* @param props.method the http method, which may affect what authentication are needed.
			*/
			async function getAccessToken(props) {
				const authorizationHeader = (await getRestCallAuthParams({
					method: "GET",
					...props
				})).headers?.Authorization;
				if (authorizationHeader.indexOf("Bearer ") === 0) return authorizationHeader.substring(7);
				throw new Error("Unknown format of authorization header returned by remote auth module");
			}
			/**
			* Registers an auth module that can handle authentication. An auth module is used by specifying its name as authType in the HostConfig passed in to api calls.
			* @param name the name of the module
			* @param authModule the implementation of the AuthModule interface
			*/
			function registerAuthModule$1(name, authModule) {
				registerAuthModule(name, authModule);
			}
			/**
			* Sets the default host config that will be used for all api calls that do not include a HostConfig
			* @param hostConfig the default HostConfig to use
			*/
			function setDefaultHostConfig$1(hostConfig) {
				setDefaultHostConfig(hostConfig);
			}
			/**
			* Registers a host config with the given name.
			* @param name The name of the host config to be used to reference the host config later.
			* @param hostConfig The host config to register.
			*/
			function registerHostConfig$1(name, hostConfig) {
				registerHostConfig(name, hostConfig);
			}
			/**
			* Unregisters a host config with the given name.
			* @param name The name of the host config to unregister.
			*/
			function unregisterHostConfig$1(name) {
				unregisterHostConfig(name);
			}
			/**
			* Serializes the provided hostConfig, if present, otherwise the default one.
			*/
			function serializeHostConfig$1(hostConfig) {
				return serializeHostConfig(hostConfig);
			}
			/**
			* Throws errors if hostConfig is missing or missing properties on cross domain requests
			*/
			function checkForCrossDomainRequest(hostConfig) {
				const hostConfigToUse = withResolvedHostConfig(hostConfig);
				if (isHostCrossOrigin(hostConfigToUse)) {
					if (Object.keys(hostConfigToUse).length === 0) throw new InvalidHostConfigError("a host config must be provided when making a cross domain request");
					if (!hostConfigToUse.host) throw new InvalidHostConfigError("A 'host' property must be set in host config when making a cross domain request");
				}
			}
			/**
			* Logs out the user and sets `global.loggingOut` to true.
			* **NOTE**: Does not abort pending requests.
			*/
			const logout = exports("logout", () => {
				globalThis.loggingOut = true;
				globalThis.location.href = "/logout";
			});
			const leadingHttp = /^http/;
			/**
			* Normalizes the error object returned from the auth module to a FatalAuthError
			* @param errorBody The error body to normalize
			* @param status The status code of the error
			* @returns Either the error message or a default message
			* @private
			*/
			function normalizeInbandAuthError({ errorBody, status }) {
				const authError = errorBody;
				if (typeof authError?.errors === "object") return { message: new AuthorizationError(authError?.errors).message };
				return { message: `HTTP ${status}` };
			}
			/**
			* Normalizes the error object returned from the auth module to a FatalAuthError
			* @param err The error to normalize
			* @returns Either the error message or a default message
			* @private
			*/
			function normalizeAuthModuleError(err) {
				return { message: err.message || "Unknown error" };
			}
			/**
			* Returns a resolved host config. If the host config is a reference to a registered host config, it will be resolved
			* to the actual host config.
			* If the host config is undefined or empty, the default host config will be used.
			* If the host config is not a reference, it will be returned as is.
			* @private
			* @param hostConfig - The host config to resolve
			* @returns
			*/
			function withResolvedHostConfig(hostConfig) {
				if (hostConfig?.reference) {
					const refConfig = getRegisteredHostConfig(hostConfig.reference);
					if (!refConfig) throw new InvalidHostConfigError(`Host config with name "${hostConfig.reference}" not found.`);
					return refConfig;
				}
				if (hostConfig && Object.keys(hostConfig).length > 0) return hostConfig;
				return getDefaultHostConfig();
			}
			/**
			* Returns the default host config that is used for all qmfe api calls that do not include a host config
			*/
			function getDefaultHostConfig$1() {
				return getDefaultHostConfig();
			}

			//#region src/auth/auth.ts
			/**
			* Provides functionalities for authentication with Qlik backends.
			*/
			const auth = {
				determineAuthType: determineAuthType$1,
				getDefaultHostConfig: getDefaultHostConfig$1,
				getRestCallAuthParams,
				getWebResourceAuthParams,
				getWebSocketAuthParams,
				handleAuthenticationError,
				isHostCrossOrigin,
				isWindows,
				logout,
				registerAuthModule: registerAuthModule$1,
				registerHostConfig: registerHostConfig$1,
				serializeHostConfig: serializeHostConfig$1,
				setDefaultHostConfig: setDefaultHostConfig$1,
				toValidLocationUrl,
				toValidWebsocketLocationUrl,
				unregisterHostConfig: unregisterHostConfig$1
			};
			var auth_default = exports("default", auth);

			var _nodeResolve_empty = /*#__PURE__*/Object.freeze({
				__proto__: null
			});

		})
	};
}));
//# sourceMappingURL=auth-sNi1A67--Belh4pQc.js.map