import React, { useContext, createContext, useEffect, useState, Children, isValidElement, useMemo, useRef, Suspense } from 'react';
import PropTypes from 'prop-types';
import { createVersionedContext, createVersionedValueMap, getOrCreateGlobalSingleton } from '@backstage/version-bridge';
import ObservableImpl from 'zen-observable';
import { SessionState, FeatureFlagState, AnalyticsContext, useAnalytics, attachComponentData, useApp, useApi, configApiRef, getComponentData, featureFlagsApiRef, appThemeApiRef, identityApiRef, useElementFilter } from '@backstage/core-plugin-api';
import { z } from 'zod';
import { ConfigReader } from '@backstage/config';
export { ConfigReader } from '@backstage/config';
import { createRoutesFromChildren, Route, useLocation, matchRoutes, Routes, generatePath, useRoutes } from 'react-router-dom';
import useAsync from 'react-use/lib/useAsync';
import { appLanguageApiRef, translationApiRef } from '@backstage/core-plugin-api/alpha';
import mapValues from 'lodash/mapValues';
import useObservable from 'react-use/lib/useObservable';
import { createInstance } from 'i18next';

var __defProp$l = Object.defineProperty;
var __defNormalProp$l = (obj, key, value) => key in obj ? __defProp$l(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$l = (obj, key, value) => {
  __defNormalProp$l(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class ApiAggregator {
  constructor(...holders) {
    __publicField$l(this, "holders");
    this.holders = holders;
  }
  get(apiRef) {
    for (const holder of this.holders) {
      const api = holder.get(apiRef);
      if (api) {
        return api;
      }
    }
    return void 0;
  }
}

const ApiContext = createVersionedContext("api-context");
const ApiProvider = (props) => {
  var _a;
  const { apis, children } = props;
  const parentHolder = (_a = useContext(ApiContext)) == null ? void 0 : _a.atVersion(1);
  const holder = parentHolder ? new ApiAggregator(apis, parentHolder) : apis;
  return /* @__PURE__ */ React.createElement(
    ApiContext.Provider,
    {
      value: createVersionedValueMap({ 1: holder }),
      children
    }
  );
};
ApiProvider.propTypes = {
  apis: PropTypes.shape({ get: PropTypes.func.isRequired }).isRequired,
  children: PropTypes.node
};

var __defProp$k = Object.defineProperty;
var __defNormalProp$k = (obj, key, value) => key in obj ? __defProp$k(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$k = (obj, key, value) => {
  __defNormalProp$k(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class ApiResolver {
  constructor(factories) {
    this.factories = factories;
    __publicField$k(this, "apis", /* @__PURE__ */ new Map());
  }
  /**
   * Validate factories by making sure that each of the apis can be created
   * without hitting any circular dependencies.
   */
  static validateFactories(factories, apis) {
    for (const api of apis) {
      const heap = [api];
      const allDeps = /* @__PURE__ */ new Set();
      while (heap.length) {
        const apiRef = heap.shift();
        const factory = factories.get(apiRef);
        if (!factory) {
          continue;
        }
        for (const dep of Object.values(factory.deps)) {
          if (dep.id === api.id) {
            throw new Error(`Circular dependency of api factory for ${api}`);
          }
          if (!allDeps.has(dep)) {
            allDeps.add(dep);
            heap.push(dep);
          }
        }
      }
    }
  }
  get(ref) {
    return this.load(ref);
  }
  load(ref, loading = []) {
    const impl = this.apis.get(ref.id);
    if (impl) {
      return impl;
    }
    const factory = this.factories.get(ref);
    if (!factory) {
      return void 0;
    }
    if (loading.includes(factory.api)) {
      throw new Error(`Circular dependency of api factory for ${factory.api}`);
    }
    const deps = this.loadDeps(ref, factory.deps, [...loading, factory.api]);
    const api = factory.factory(deps);
    this.apis.set(ref.id, api);
    return api;
  }
  loadDeps(dependent, apis, loading) {
    const impls = {};
    for (const key in apis) {
      if (apis.hasOwnProperty(key)) {
        const ref = apis[key];
        const api = this.load(ref, loading);
        if (!api) {
          throw new Error(
            `No API factory available for dependency ${ref} of dependent ${dependent}`
          );
        }
        impls[key] = api;
      }
    }
    return impls;
  }
}

var __defProp$j = Object.defineProperty;
var __defNormalProp$j = (obj, key, value) => key in obj ? __defProp$j(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$j = (obj, key, value) => {
  __defNormalProp$j(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
var ScopePriority = /* @__PURE__ */ ((ScopePriority2) => {
  ScopePriority2[ScopePriority2["default"] = 10] = "default";
  ScopePriority2[ScopePriority2["app"] = 50] = "app";
  ScopePriority2[ScopePriority2["static"] = 100] = "static";
  return ScopePriority2;
})(ScopePriority || {});
class ApiFactoryRegistry {
  constructor() {
    __publicField$j(this, "factories", /* @__PURE__ */ new Map());
  }
  /**
   * Register a new API factory. Returns true if the factory was added
   * to the registry.
   *
   * A factory will not be added to the registry if there is already
   * an existing factory with the same or higher priority.
   */
  register(scope, factory) {
    const priority = ScopePriority[scope];
    const existing = this.factories.get(factory.api.id);
    if (existing && existing.priority >= priority) {
      return false;
    }
    this.factories.set(factory.api.id, { priority, factory });
    return true;
  }
  get(api) {
    const tuple = this.factories.get(api.id);
    if (!tuple) {
      return void 0;
    }
    return tuple.factory;
  }
  getAllApis() {
    const refs = /* @__PURE__ */ new Set();
    for (const { factory } of this.factories.values()) {
      refs.add(factory.api);
    }
    return refs;
  }
}

function showLoginPopup(options) {
  return new Promise((resolve, reject) => {
    const width = options.width || 500;
    const height = options.height || 700;
    const left = window.screen.width / 2 - width / 2;
    const top = window.screen.height / 2 - height / 2;
    const popup = window.open(
      options.url,
      options.name,
      `menubar=no,location=no,resizable=no,scrollbars=no,status=no,width=${width},height=${height},top=${top},left=${left}`
    );
    let targetOrigin = "";
    if (!popup || typeof popup.closed === "undefined" || popup.closed) {
      const error = new Error("Failed to open auth popup.");
      error.name = "PopupRejectedError";
      reject(error);
      return;
    }
    const messageListener = (event) => {
      if (event.source !== popup) {
        return;
      }
      if (event.origin !== options.origin) {
        return;
      }
      const { data } = event;
      if (data.type === "config_info") {
        targetOrigin = data.targetOrigin;
        return;
      }
      if (data.type !== "authorization_response") {
        return;
      }
      const authResult = data;
      if ("error" in authResult) {
        const error = new Error(authResult.error.message);
        error.name = authResult.error.name;
        reject(error);
      } else {
        resolve(authResult.response);
      }
      done();
    };
    const intervalId = setInterval(() => {
      if (popup.closed) {
        const errMessage = `Login failed, ${targetOrigin && targetOrigin !== window.location.origin ? `Incorrect app origin, expected ${targetOrigin}` : "popup was closed"}`;
        const error = new Error(errMessage);
        error.name = "PopupClosedError";
        reject(error);
        done();
      }
    }, 100);
    function done() {
      window.removeEventListener("message", messageListener);
      clearInterval(intervalId);
    }
    window.addEventListener("message", messageListener);
  });
}

var __defProp$i = Object.defineProperty;
var __defNormalProp$i = (obj, key, value) => key in obj ? __defProp$i(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$i = (obj, key, value) => {
  __defNormalProp$i(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
let warned$1 = false;
function defaultJoinScopes(scopes) {
  return [...scopes].join(" ");
}
class DefaultAuthConnector {
  constructor(options) {
    __publicField$i(this, "discoveryApi");
    __publicField$i(this, "environment");
    __publicField$i(this, "provider");
    __publicField$i(this, "joinScopesFunc");
    __publicField$i(this, "authRequester");
    __publicField$i(this, "sessionTransform");
    __publicField$i(this, "enableExperimentalRedirectFlow");
    __publicField$i(this, "popupOptions");
    var _a;
    const {
      configApi,
      discoveryApi,
      environment,
      provider,
      joinScopes = defaultJoinScopes,
      oauthRequestApi,
      sessionTransform = (id) => id,
      popupOptions
    } = options;
    if (!warned$1 && !configApi) {
      console.warn(
        "DEPRECATION WARNING: Authentication providers require a configApi instance to configure the authentication flow. Please provide one to the authentication provider constructor."
      );
      warned$1 = true;
    }
    this.enableExperimentalRedirectFlow = configApi ? (_a = configApi.getOptionalBoolean("enableExperimentalRedirectFlow")) != null ? _a : false : false;
    this.authRequester = oauthRequestApi.createAuthRequester({
      provider,
      onAuthRequest: async (scopes) => {
        if (!this.enableExperimentalRedirectFlow) {
          return this.showPopup(scopes);
        }
        return this.executeRedirect(scopes);
      }
    });
    this.discoveryApi = discoveryApi;
    this.environment = environment;
    this.provider = provider;
    this.joinScopesFunc = joinScopes;
    this.sessionTransform = sessionTransform;
    this.popupOptions = popupOptions;
  }
  async createSession(options) {
    if (options.instantPopup) {
      if (this.enableExperimentalRedirectFlow) {
        return this.executeRedirect(options.scopes);
      }
      return this.showPopup(options.scopes);
    }
    return this.authRequester(options.scopes);
  }
  async refreshSession(scopes) {
    const res = await fetch(
      await this.buildUrl("/refresh", {
        optional: true,
        ...scopes && { scope: this.joinScopesFunc(scopes) }
      }),
      {
        headers: {
          "x-requested-with": "XMLHttpRequest"
        },
        credentials: "include"
      }
    ).catch((error) => {
      throw new Error(`Auth refresh request failed, ${error}`);
    });
    if (!res.ok) {
      const error = new Error(
        `Auth refresh request failed, ${res.statusText}`
      );
      error.status = res.status;
      throw error;
    }
    const authInfo = await res.json();
    if (authInfo.error) {
      const error = new Error(authInfo.error.message);
      if (authInfo.error.name) {
        error.name = authInfo.error.name;
      }
      throw error;
    }
    return await this.sessionTransform(authInfo);
  }
  async removeSession() {
    const res = await fetch(await this.buildUrl("/logout"), {
      method: "POST",
      headers: {
        "x-requested-with": "XMLHttpRequest"
      },
      credentials: "include"
    }).catch((error) => {
      throw new Error(`Logout request failed, ${error}`);
    });
    if (!res.ok) {
      const error = new Error(`Logout request failed, ${res.statusText}`);
      error.status = res.status;
      throw error;
    }
  }
  async showPopup(scopes) {
    var _a, _b, _c, _d, _e, _f, _g, _h;
    const scope = this.joinScopesFunc(scopes);
    const popupUrl = await this.buildUrl("/start", {
      scope,
      origin: window.location.origin,
      flow: "popup"
    });
    const width = ((_b = (_a = this.popupOptions) == null ? void 0 : _a.size) == null ? void 0 : _b.fullscreen) ? window.screen.width : ((_d = (_c = this.popupOptions) == null ? void 0 : _c.size) == null ? void 0 : _d.width) || 450;
    const height = ((_f = (_e = this.popupOptions) == null ? void 0 : _e.size) == null ? void 0 : _f.fullscreen) ? window.screen.height : ((_h = (_g = this.popupOptions) == null ? void 0 : _g.size) == null ? void 0 : _h.height) || 730;
    const payload = await showLoginPopup({
      url: popupUrl,
      name: `${this.provider.title} Login`,
      origin: new URL(popupUrl).origin,
      width,
      height
    });
    return await this.sessionTransform(payload);
  }
  async executeRedirect(scopes) {
    const scope = this.joinScopesFunc(scopes);
    window.location.href = await this.buildUrl("/start", {
      scope,
      origin: window.location.origin,
      redirectUrl: window.location.href,
      flow: "redirect"
    });
    return new Promise(() => {
    });
  }
  async buildUrl(path, query) {
    const baseUrl = await this.discoveryApi.getBaseUrl("auth");
    const queryString = this.buildQueryString({
      ...query,
      env: this.environment
    });
    return `${baseUrl}/${this.provider.id}${path}${queryString}`;
  }
  buildQueryString(query) {
    if (!query) {
      return "";
    }
    const queryString = Object.entries(query).map(([key, value]) => {
      if (typeof value === "string") {
        return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
      } else if (value) {
        return encodeURIComponent(key);
      }
      return void 0;
    }).filter(Boolean).join("&");
    if (!queryString) {
      return "";
    }
    return `?${queryString}`;
  }
}

var __defProp$h = Object.defineProperty;
var __defNormalProp$h = (obj, key, value) => key in obj ? __defProp$h(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$h = (obj, key, value) => {
  __defNormalProp$h(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class DirectAuthConnector {
  constructor(options) {
    __publicField$h(this, "discoveryApi");
    __publicField$h(this, "environment");
    __publicField$h(this, "provider");
    const { discoveryApi, environment, provider } = options;
    this.discoveryApi = discoveryApi;
    this.environment = environment;
    this.provider = provider;
  }
  async createSession() {
    const popupUrl = await this.buildUrl("/start");
    const payload = await showLoginPopup({
      url: popupUrl,
      name: `${this.provider.title} Login`,
      origin: new URL(popupUrl).origin,
      width: 450,
      height: 730
    });
    return {
      ...payload,
      id: payload.profile.email
    };
  }
  async refreshSession() {
  }
  async removeSession() {
    const res = await fetch(await this.buildUrl("/logout"), {
      method: "POST",
      headers: {
        "x-requested-with": "XMLHttpRequest"
      },
      credentials: "include"
    }).catch((error) => {
      throw new Error(`Logout request failed, ${error}`);
    });
    if (!res.ok) {
      const error = new Error(`Logout request failed, ${res.statusText}`);
      error.status = res.status;
      throw error;
    }
  }
  async buildUrl(path) {
    const baseUrl = await this.discoveryApi.getBaseUrl("auth");
    return `${baseUrl}/${this.provider.id}${path}?env=${this.environment}`;
  }
}

function hasScopes$1(searched, searchFor) {
  for (const scope of searchFor) {
    if (!searched.has(scope)) {
      return false;
    }
  }
  return true;
}
class SessionScopeHelper {
  constructor(options) {
    this.options = options;
  }
  sessionExistsAndHasScope(session, scopes) {
    if (!session) {
      return false;
    }
    if (!scopes) {
      return true;
    }
    if (this.options.sessionScopes === void 0) {
      return true;
    }
    const sessionScopes = this.options.sessionScopes(session);
    return hasScopes$1(sessionScopes, scopes);
  }
  getExtendedScope(session, scopes) {
    const newScope = new Set(this.options.defaultScopes);
    if (session && this.options.sessionScopes !== void 0) {
      const sessionScopes = this.options.sessionScopes(session);
      for (const scope of sessionScopes) {
        newScope.add(scope);
      }
    }
    if (scopes) {
      for (const scope of scopes) {
        newScope.add(scope);
      }
    }
    return newScope;
  }
}

var __defProp$g = Object.defineProperty;
var __defNormalProp$g = (obj, key, value) => key in obj ? __defProp$g(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$g = (obj, key, value) => {
  __defNormalProp$g(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class PublishSubject {
  constructor() {
    __publicField$g(this, "isClosed", false);
    __publicField$g(this, "terminatingError");
    __publicField$g(this, "observable", new ObservableImpl((subscriber) => {
      if (this.isClosed) {
        if (this.terminatingError) {
          subscriber.error(this.terminatingError);
        } else {
          subscriber.complete();
        }
        return () => {
        };
      }
      this.subscribers.add(subscriber);
      return () => {
        this.subscribers.delete(subscriber);
      };
    }));
    __publicField$g(this, "subscribers", /* @__PURE__ */ new Set());
  }
  [Symbol.observable]() {
    return this;
  }
  get closed() {
    return this.isClosed;
  }
  next(value) {
    if (this.isClosed) {
      throw new Error("PublishSubject is closed");
    }
    this.subscribers.forEach((subscriber) => subscriber.next(value));
  }
  error(error) {
    if (this.isClosed) {
      throw new Error("PublishSubject is closed");
    }
    this.isClosed = true;
    this.terminatingError = error;
    this.subscribers.forEach((subscriber) => subscriber.error(error));
  }
  complete() {
    if (this.isClosed) {
      throw new Error("PublishSubject is closed");
    }
    this.isClosed = true;
    this.subscribers.forEach((subscriber) => subscriber.complete());
  }
  subscribe(onNext, onError, onComplete) {
    const observer = typeof onNext === "function" ? {
      next: onNext,
      error: onError,
      complete: onComplete
    } : onNext;
    return this.observable.subscribe(observer);
  }
}
class BehaviorSubject {
  constructor(value) {
    __publicField$g(this, "isClosed");
    __publicField$g(this, "currentValue");
    __publicField$g(this, "terminatingError");
    __publicField$g(this, "observable");
    __publicField$g(this, "subscribers", /* @__PURE__ */ new Set());
    this.isClosed = false;
    this.currentValue = value;
    this.terminatingError = void 0;
    this.observable = new ObservableImpl((subscriber) => {
      if (this.isClosed) {
        if (this.terminatingError) {
          subscriber.error(this.terminatingError);
        } else {
          subscriber.complete();
        }
        return () => {
        };
      }
      subscriber.next(this.currentValue);
      this.subscribers.add(subscriber);
      return () => {
        this.subscribers.delete(subscriber);
      };
    });
  }
  [Symbol.observable]() {
    return this;
  }
  get closed() {
    return this.isClosed;
  }
  next(value) {
    if (this.isClosed) {
      throw new Error("BehaviorSubject is closed");
    }
    this.currentValue = value;
    this.subscribers.forEach((subscriber) => subscriber.next(value));
  }
  error(error) {
    if (this.isClosed) {
      throw new Error("BehaviorSubject is closed");
    }
    this.isClosed = true;
    this.terminatingError = error;
    this.subscribers.forEach((subscriber) => subscriber.error(error));
  }
  complete() {
    if (this.isClosed) {
      throw new Error("BehaviorSubject is closed");
    }
    this.isClosed = true;
    this.subscribers.forEach((subscriber) => subscriber.complete());
  }
  subscribe(onNext, onError, onComplete) {
    const observer = typeof onNext === "function" ? {
      next: onNext,
      error: onError,
      complete: onComplete
    } : onNext;
    return this.observable.subscribe(observer);
  }
}

var __defProp$f = Object.defineProperty;
var __defNormalProp$f = (obj, key, value) => key in obj ? __defProp$f(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$f = (obj, key, value) => {
  __defNormalProp$f(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class SessionStateTracker {
  constructor() {
    __publicField$f(this, "subject", new BehaviorSubject(
      SessionState.SignedOut
    ));
    __publicField$f(this, "signedIn", false);
  }
  setIsSignedIn(isSignedIn) {
    if (this.signedIn !== isSignedIn) {
      this.signedIn = isSignedIn;
      this.subject.next(
        this.signedIn ? SessionState.SignedIn : SessionState.SignedOut
      );
    }
  }
  sessionState$() {
    return this.subject;
  }
}

var __defProp$e = Object.defineProperty;
var __defNormalProp$e = (obj, key, value) => key in obj ? __defProp$e(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$e = (obj, key, value) => {
  __defNormalProp$e(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class RefreshingAuthSessionManager {
  constructor(options) {
    __publicField$e(this, "connector");
    __publicField$e(this, "helper");
    __publicField$e(this, "sessionScopesFunc");
    __publicField$e(this, "sessionShouldRefreshFunc");
    __publicField$e(this, "stateTracker", new SessionStateTracker());
    __publicField$e(this, "refreshPromise");
    __publicField$e(this, "currentSession");
    const {
      connector,
      defaultScopes = /* @__PURE__ */ new Set(),
      sessionScopes,
      sessionShouldRefresh
    } = options;
    this.connector = connector;
    this.sessionScopesFunc = sessionScopes;
    this.sessionShouldRefreshFunc = sessionShouldRefresh;
    this.helper = new SessionScopeHelper({ sessionScopes, defaultScopes });
  }
  async getSession(options) {
    if (this.helper.sessionExistsAndHasScope(this.currentSession, options.scopes)) {
      const shouldRefresh = this.sessionShouldRefreshFunc(this.currentSession);
      if (!shouldRefresh) {
        return this.currentSession;
      }
      try {
        const refreshedSession = await this.collapsedSessionRefresh(
          options.scopes
        );
        const currentScopes = this.sessionScopesFunc(this.currentSession);
        const refreshedScopes = this.sessionScopesFunc(refreshedSession);
        if (hasScopes$1(refreshedScopes, currentScopes)) {
          this.currentSession = refreshedSession;
        }
        return refreshedSession;
      } catch (error) {
        if (options.optional) {
          return void 0;
        }
        throw error;
      }
    }
    if (!this.currentSession && !options.instantPopup) {
      try {
        const newSession = await this.collapsedSessionRefresh(options.scopes);
        this.currentSession = newSession;
        return this.getSession(options);
      } catch {
      }
    }
    if (options.optional) {
      return void 0;
    }
    this.currentSession = await this.connector.createSession({
      ...options,
      scopes: this.helper.getExtendedScope(this.currentSession, options.scopes)
    });
    this.stateTracker.setIsSignedIn(true);
    return this.currentSession;
  }
  async removeSession() {
    this.currentSession = void 0;
    await this.connector.removeSession();
    this.stateTracker.setIsSignedIn(false);
  }
  sessionState$() {
    return this.stateTracker.sessionState$();
  }
  async collapsedSessionRefresh(scopes) {
    if (this.refreshPromise) {
      return this.refreshPromise;
    }
    this.refreshPromise = this.connector.refreshSession(
      this.helper.getExtendedScope(this.currentSession, scopes)
    );
    try {
      const session = await this.refreshPromise;
      this.stateTracker.setIsSignedIn(true);
      return session;
    } finally {
      delete this.refreshPromise;
    }
  }
}

var __defProp$d = Object.defineProperty;
var __defNormalProp$d = (obj, key, value) => key in obj ? __defProp$d(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$d = (obj, key, value) => {
  __defNormalProp$d(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class StaticAuthSessionManager {
  constructor(options) {
    __publicField$d(this, "connector");
    __publicField$d(this, "helper");
    __publicField$d(this, "stateTracker", new SessionStateTracker());
    __publicField$d(this, "currentSession");
    const { connector, defaultScopes = /* @__PURE__ */ new Set(), sessionScopes } = options;
    this.connector = connector;
    this.helper = new SessionScopeHelper({ sessionScopes, defaultScopes });
  }
  setSession(session) {
    this.currentSession = session;
    this.stateTracker.setIsSignedIn(Boolean(session));
  }
  async getSession(options) {
    if (this.helper.sessionExistsAndHasScope(this.currentSession, options.scopes)) {
      return this.currentSession;
    }
    if (options.optional) {
      return void 0;
    }
    this.currentSession = await this.connector.createSession({
      ...options,
      scopes: this.helper.getExtendedScope(this.currentSession, options.scopes)
    });
    this.stateTracker.setIsSignedIn(true);
    return this.currentSession;
  }
  /**
   * We don't call this.connector.removeSession here, since this session manager
   * is intended to be static. As such there's no need to hit the remote logout
   * endpoint - simply discarding the local session state when signing out is
   * enough.
   */
  async removeSession() {
    this.currentSession = void 0;
    this.stateTracker.setIsSignedIn(false);
  }
  sessionState$() {
    return this.stateTracker.sessionState$();
  }
}

var __defProp$c = Object.defineProperty;
var __defNormalProp$c = (obj, key, value) => key in obj ? __defProp$c(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$c = (obj, key, value) => {
  __defNormalProp$c(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class AuthSessionStore {
  constructor(options) {
    __publicField$c(this, "manager");
    __publicField$c(this, "storageKey");
    __publicField$c(this, "schema");
    __publicField$c(this, "sessionShouldRefreshFunc");
    __publicField$c(this, "helper");
    const {
      manager,
      storageKey,
      schema,
      sessionScopes,
      sessionShouldRefresh = () => false
    } = options;
    this.manager = manager;
    this.storageKey = storageKey;
    this.schema = schema;
    this.sessionShouldRefreshFunc = sessionShouldRefresh;
    this.helper = new SessionScopeHelper({
      sessionScopes,
      defaultScopes: /* @__PURE__ */ new Set()
    });
  }
  setSession(session) {
    this.manager.setSession(session);
    this.saveSession(session);
  }
  async getSession(options) {
    const { scopes } = options;
    const session = this.loadSession();
    if (this.helper.sessionExistsAndHasScope(session, scopes)) {
      const shouldRefresh = this.sessionShouldRefreshFunc(session);
      if (!shouldRefresh) {
        this.manager.setSession(session);
        return session;
      }
    }
    const newSession = await this.manager.getSession(options);
    this.saveSession(newSession);
    return newSession;
  }
  async removeSession() {
    localStorage.removeItem(this.storageKey);
    await this.manager.removeSession();
  }
  sessionState$() {
    return this.manager.sessionState$();
  }
  loadSession() {
    try {
      const sessionJson = localStorage.getItem(this.storageKey);
      if (sessionJson) {
        const session = JSON.parse(sessionJson, (_key, value) => {
          if ((value == null ? void 0 : value.__type) === "Set") {
            return new Set(value.__value);
          }
          return value;
        });
        try {
          return this.schema.parse(session);
        } catch (e) {
          console.log(
            `Failed to load session from local storage because it did not conform to the expected schema, ${e}`
          );
          throw e;
        }
      }
      return void 0;
    } catch (error) {
      localStorage.removeItem(this.storageKey);
      return void 0;
    }
  }
  saveSession(session) {
    if (session === void 0) {
      localStorage.removeItem(this.storageKey);
      return;
    }
    try {
      this.schema.parse(session);
    } catch (e) {
      console.warn(
        `Failed to save session to local storage because it did not conform to the expected schema, ${e}`
      );
      return;
    }
    localStorage.setItem(
      this.storageKey,
      JSON.stringify(session, (_key, value) => {
        if (value instanceof Set) {
          return {
            __type: "Set",
            __value: Array.from(value)
          };
        }
        return value;
      })
    );
  }
}

var __defProp$b = Object.defineProperty;
var __defNormalProp$b = (obj, key, value) => key in obj ? __defProp$b(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$b = (obj, key, value) => {
  __defNormalProp$b(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
const DEFAULT_PROVIDER$a = {
  id: "oauth2",
  title: "Your Identity Provider",
  icon: () => null
};
class OAuth2 {
  constructor(options) {
    __publicField$b(this, "sessionManager");
    __publicField$b(this, "scopeTransform");
    this.sessionManager = options.sessionManager;
    this.scopeTransform = options.scopeTransform;
  }
  static create(options) {
    const {
      configApi,
      discoveryApi,
      environment = "development",
      provider = DEFAULT_PROVIDER$a,
      oauthRequestApi,
      defaultScopes = [],
      scopeTransform = (x) => x,
      popupOptions
    } = options;
    const connector = new DefaultAuthConnector({
      configApi,
      discoveryApi,
      environment,
      provider,
      oauthRequestApi,
      sessionTransform({
        backstageIdentity,
        ...res
      }) {
        var _a;
        const session = {
          ...res,
          providerInfo: {
            idToken: res.providerInfo.idToken,
            accessToken: res.providerInfo.accessToken,
            scopes: OAuth2.normalizeScopes(
              scopeTransform,
              res.providerInfo.scope
            ),
            expiresAt: res.providerInfo.expiresInSeconds ? new Date(Date.now() + res.providerInfo.expiresInSeconds * 1e3) : void 0
          }
        };
        if (backstageIdentity) {
          const expInSec = (_a = backstageIdentity.expiresInSeconds) != null ? _a : res.providerInfo.expiresInSeconds;
          session.backstageIdentity = {
            token: backstageIdentity.token,
            identity: backstageIdentity.identity,
            expiresAt: expInSec ? new Date(Date.now() + expInSec * 1e3) : void 0
          };
        }
        return session;
      },
      popupOptions
    });
    const sessionManager = new RefreshingAuthSessionManager({
      connector,
      defaultScopes: new Set(defaultScopes),
      sessionScopes: (session) => session.providerInfo.scopes,
      sessionShouldRefresh: (session) => {
        var _a, _b;
        let min = Infinity;
        if ((_a = session.providerInfo) == null ? void 0 : _a.expiresAt) {
          min = Math.min(
            min,
            (session.providerInfo.expiresAt.getTime() - Date.now()) / 1e3
          );
        }
        if ((_b = session.backstageIdentity) == null ? void 0 : _b.expiresAt) {
          min = Math.min(
            min,
            (session.backstageIdentity.expiresAt.getTime() - Date.now()) / 1e3
          );
        }
        return min < 60 * 5;
      }
    });
    return new OAuth2({ sessionManager, scopeTransform });
  }
  async signIn() {
    await this.getAccessToken();
  }
  async signOut() {
    await this.sessionManager.removeSession();
  }
  sessionState$() {
    return this.sessionManager.sessionState$();
  }
  async getAccessToken(scope, options) {
    var _a;
    const normalizedScopes = OAuth2.normalizeScopes(this.scopeTransform, scope);
    const session = await this.sessionManager.getSession({
      ...options,
      scopes: normalizedScopes
    });
    return (_a = session == null ? void 0 : session.providerInfo.accessToken) != null ? _a : "";
  }
  async getIdToken(options = {}) {
    var _a;
    const session = await this.sessionManager.getSession({
      ...options,
      scopes: /* @__PURE__ */ new Set(["openid"])
    });
    return (_a = session == null ? void 0 : session.providerInfo.idToken) != null ? _a : "";
  }
  async getBackstageIdentity(options = {}) {
    const session = await this.sessionManager.getSession(options);
    return session == null ? void 0 : session.backstageIdentity;
  }
  async getProfile(options = {}) {
    const session = await this.sessionManager.getSession(options);
    return session == null ? void 0 : session.profile;
  }
  static normalizeScopes(scopeTransform, scopes) {
    if (!scopes) {
      return /* @__PURE__ */ new Set();
    }
    const scopeList = Array.isArray(scopes) ? scopes : scopes.split(/[\s|,]/).filter(Boolean);
    return new Set(scopeTransform(scopeList));
  }
}

const DEFAULT_PROVIDER$9 = {
  id: "github",
  title: "GitHub",
  icon: () => null
};
class GithubAuth {
  static create(options) {
    const {
      configApi,
      discoveryApi,
      environment = "development",
      provider = DEFAULT_PROVIDER$9,
      oauthRequestApi,
      defaultScopes = ["read:user"]
    } = options;
    return OAuth2.create({
      configApi,
      discoveryApi,
      oauthRequestApi,
      provider,
      environment,
      defaultScopes
    });
  }
}

const DEFAULT_PROVIDER$8 = {
  id: "gitlab",
  title: "GitLab",
  icon: () => null
};
class GitlabAuth {
  static create(options) {
    const {
      configApi,
      discoveryApi,
      environment = "development",
      provider = DEFAULT_PROVIDER$8,
      oauthRequestApi,
      defaultScopes = ["read_user"]
    } = options;
    return OAuth2.create({
      configApi,
      discoveryApi,
      oauthRequestApi,
      provider,
      environment,
      defaultScopes
    });
  }
}

const DEFAULT_PROVIDER$7 = {
  id: "google",
  title: "Google",
  icon: () => null
};
const SCOPE_PREFIX$1 = "https://www.googleapis.com/auth/";
class GoogleAuth {
  static create(options) {
    const {
      configApi,
      discoveryApi,
      oauthRequestApi,
      environment = "development",
      provider = DEFAULT_PROVIDER$7,
      defaultScopes = [
        "openid",
        `${SCOPE_PREFIX$1}userinfo.email`,
        `${SCOPE_PREFIX$1}userinfo.profile`
      ]
    } = options;
    return OAuth2.create({
      configApi,
      discoveryApi,
      oauthRequestApi,
      provider,
      environment,
      defaultScopes,
      scopeTransform(scopes) {
        return scopes.map((scope) => {
          if (scope === "openid") {
            return scope;
          }
          if (scope === "profile" || scope === "email") {
            return `${SCOPE_PREFIX$1}userinfo.${scope}`;
          }
          if (scope.startsWith(SCOPE_PREFIX$1)) {
            return scope;
          }
          return `${SCOPE_PREFIX$1}${scope}`;
        });
      }
    });
  }
}

const DEFAULT_PROVIDER$6 = {
  id: "okta",
  title: "Okta",
  icon: () => null
};
const OKTA_OIDC_SCOPES = /* @__PURE__ */ new Set([
  "openid",
  "profile",
  "email",
  "phone",
  "address",
  "groups",
  "offline_access"
]);
const OKTA_SCOPE_PREFIX = "okta.";
class OktaAuth {
  static create(options) {
    const {
      configApi,
      discoveryApi,
      environment = "development",
      provider = DEFAULT_PROVIDER$6,
      oauthRequestApi,
      defaultScopes = ["openid", "email", "profile", "offline_access"]
    } = options;
    return OAuth2.create({
      configApi,
      discoveryApi,
      oauthRequestApi,
      provider,
      environment,
      defaultScopes,
      scopeTransform(scopes) {
        return scopes.map((scope) => {
          if (OKTA_OIDC_SCOPES.has(scope)) {
            return scope;
          }
          if (scope.startsWith(OKTA_SCOPE_PREFIX)) {
            return scope;
          }
          return `${OKTA_SCOPE_PREFIX}${scope}`;
        });
      }
    });
  }
}

const samlSessionSchema = z.object({
  profile: z.object({
    email: z.string().optional(),
    displayName: z.string().optional(),
    picture: z.string().optional()
  }),
  backstageIdentity: z.object({
    token: z.string(),
    identity: z.object({
      type: z.literal("user"),
      userEntityRef: z.string(),
      ownershipEntityRefs: z.array(z.string())
    })
  })
});

const DEFAULT_PROVIDER$5 = {
  id: "saml",
  title: "SAML",
  icon: () => null
};
class SamlAuth {
  constructor(sessionManager) {
    this.sessionManager = sessionManager;
  }
  static create(options) {
    const {
      discoveryApi,
      environment = "development",
      provider = DEFAULT_PROVIDER$5
    } = options;
    const connector = new DirectAuthConnector({
      discoveryApi,
      environment,
      provider
    });
    const sessionManager = new StaticAuthSessionManager({
      connector
    });
    const authSessionStore = new AuthSessionStore({
      manager: sessionManager,
      storageKey: `${provider.id}Session`,
      schema: samlSessionSchema
    });
    return new SamlAuth(authSessionStore);
  }
  sessionState$() {
    return this.sessionManager.sessionState$();
  }
  async signIn() {
    await this.getBackstageIdentity({});
  }
  async signOut() {
    await this.sessionManager.removeSession();
  }
  async getBackstageIdentity(options = {}) {
    const session = await this.sessionManager.getSession(options);
    return session == null ? void 0 : session.backstageIdentity;
  }
  async getProfile(options = {}) {
    const session = await this.sessionManager.getSession(options);
    return session == null ? void 0 : session.profile;
  }
}

var __defProp$a = Object.defineProperty;
var __defNormalProp$a = (obj, key, value) => key in obj ? __defProp$a(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$a = (obj, key, value) => {
  __defNormalProp$a(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
const DEFAULT_PROVIDER$4 = {
  id: "microsoft",
  title: "Microsoft",
  icon: () => null
};
const _MicrosoftAuth = class _MicrosoftAuth {
  constructor(options) {
    __publicField$a(this, "oauth2");
    __publicField$a(this, "configApi");
    __publicField$a(this, "environment");
    __publicField$a(this, "provider");
    __publicField$a(this, "oauthRequestApi");
    __publicField$a(this, "discoveryApi");
    __publicField$a(this, "scopeTransform");
    const {
      configApi,
      environment = "development",
      provider = DEFAULT_PROVIDER$4,
      oauthRequestApi,
      discoveryApi,
      defaultScopes = [
        "openid",
        "offline_access",
        "profile",
        "email",
        "User.Read"
      ],
      scopeTransform = (scopes) => scopes.concat("offline_access")
    } = options;
    this.configApi = configApi;
    this.environment = environment;
    this.provider = provider;
    this.oauthRequestApi = oauthRequestApi;
    this.discoveryApi = discoveryApi;
    this.scopeTransform = scopeTransform;
    this.oauth2 = {
      [_MicrosoftAuth.MicrosoftGraphID]: OAuth2.create({
        configApi: this.configApi,
        discoveryApi: this.discoveryApi,
        oauthRequestApi: this.oauthRequestApi,
        provider: this.provider,
        environment: this.environment,
        scopeTransform: this.scopeTransform,
        defaultScopes
      })
    };
  }
  static create(options) {
    return new _MicrosoftAuth(options);
  }
  microsoftGraph() {
    return this.oauth2[_MicrosoftAuth.MicrosoftGraphID];
  }
  static resourceForScopes(scope) {
    var _a;
    const audiences = [
      ...new Set(
        scope.split(" ").map(_MicrosoftAuth.resourceForScope).filter((aud) => aud !== "openid")
      )
    ];
    if (audiences.length > 1) {
      return Promise.reject(
        new Error(
          `Requested access token with scopes from multiple Azure resources: ${audiences.join(
            ", "
          )}. Access tokens can only have a single audience.`
        )
      );
    }
    const audience = (_a = audiences[0]) != null ? _a : _MicrosoftAuth.MicrosoftGraphID;
    return Promise.resolve(audience);
  }
  static resourceForScope(scope) {
    var _a;
    const groups = (_a = scope.match(/^(?<resourceURI>.*)\/(?<scp>[^\/]*)$/)) == null ? void 0 : _a.groups;
    if (groups) {
      const { resourceURI } = groups;
      const aud = resourceURI.replace(/^api:\/\//, "");
      return aud;
    }
    switch (scope) {
      case "email":
      case "openid":
      case "offline_access":
      case "profile": {
        return "openid";
      }
      default:
        return _MicrosoftAuth.MicrosoftGraphID;
    }
  }
  async getAccessToken(scope, options) {
    const aud = scope === void 0 ? _MicrosoftAuth.MicrosoftGraphID : await _MicrosoftAuth.resourceForScopes(
      Array.isArray(scope) ? scope.join(" ") : scope
    );
    if (!(aud in this.oauth2)) {
      this.oauth2[aud] = OAuth2.create({
        configApi: this.configApi,
        discoveryApi: this.discoveryApi,
        oauthRequestApi: this.oauthRequestApi,
        provider: this.provider,
        environment: this.environment,
        scopeTransform: this.scopeTransform
      });
    }
    return this.oauth2[aud].getAccessToken(scope, options);
  }
  getIdToken(options) {
    return this.microsoftGraph().getIdToken(options);
  }
  getProfile(options) {
    return this.microsoftGraph().getProfile(options);
  }
  getBackstageIdentity(options) {
    return this.microsoftGraph().getBackstageIdentity(options);
  }
  signIn() {
    return this.microsoftGraph().signIn();
  }
  signOut() {
    return this.microsoftGraph().signOut();
  }
  sessionState$() {
    return this.microsoftGraph().sessionState$();
  }
};
__publicField$a(_MicrosoftAuth, "MicrosoftGraphID", "00000003-0000-0000-c000-000000000000");
let MicrosoftAuth = _MicrosoftAuth;

const DEFAULT_PROVIDER$3 = {
  id: "onelogin",
  title: "onelogin",
  icon: () => null
};
const OIDC_SCOPES = /* @__PURE__ */ new Set([
  "openid",
  "profile",
  "email",
  "phone",
  "address",
  "groups",
  "offline_access"
]);
const SCOPE_PREFIX = "onelogin.";
class OneLoginAuth {
  static create(options) {
    const {
      configApi,
      discoveryApi,
      environment = "development",
      provider = DEFAULT_PROVIDER$3,
      oauthRequestApi
    } = options;
    return OAuth2.create({
      configApi,
      discoveryApi,
      oauthRequestApi,
      provider,
      environment,
      defaultScopes: ["openid", "email", "profile", "offline_access"],
      scopeTransform(scopes) {
        return scopes.map((scope) => {
          if (OIDC_SCOPES.has(scope)) {
            return scope;
          }
          if (scope.startsWith(SCOPE_PREFIX)) {
            return scope;
          }
          return `${SCOPE_PREFIX}${scope}`;
        });
      }
    });
  }
}

const DEFAULT_PROVIDER$2 = {
  id: "bitbucket",
  title: "Bitbucket",
  icon: () => null
};
class BitbucketAuth {
  static create(options) {
    const {
      configApi,
      discoveryApi,
      environment = "development",
      provider = DEFAULT_PROVIDER$2,
      oauthRequestApi,
      defaultScopes = ["team"]
    } = options;
    return OAuth2.create({
      configApi,
      discoveryApi,
      oauthRequestApi,
      provider,
      environment,
      defaultScopes
    });
  }
}

const DEFAULT_PROVIDER$1 = {
  id: "bitbucketServer",
  title: "Bitbucket Server",
  icon: () => null
};
class BitbucketServerAuth {
  static create(options) {
    const {
      configApi,
      discoveryApi,
      environment = "development",
      provider = DEFAULT_PROVIDER$1,
      oauthRequestApi,
      defaultScopes = ["PROJECT_ADMIN"]
    } = options;
    return OAuth2.create({
      configApi,
      discoveryApi,
      oauthRequestApi,
      provider,
      environment,
      defaultScopes
    });
  }
}

const DEFAULT_PROVIDER = {
  id: "atlassian",
  title: "Atlassian",
  icon: () => null
};
class AtlassianAuth {
  static create(options) {
    const {
      configApi,
      discoveryApi,
      environment = "development",
      provider = DEFAULT_PROVIDER,
      oauthRequestApi
    } = options;
    return OAuth2.create({
      configApi,
      discoveryApi,
      oauthRequestApi,
      provider,
      environment
    });
  }
}

var __defProp$9 = Object.defineProperty;
var __defNormalProp$9 = (obj, key, value) => key in obj ? __defProp$9(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$9 = (obj, key, value) => {
  __defNormalProp$9(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class AlertApiForwarder {
  constructor() {
    __publicField$9(this, "subject", new PublishSubject());
  }
  post(alert) {
    this.subject.next(alert);
  }
  alert$() {
    return this.subject;
  }
}

class MultipleAnalyticsApi {
  constructor(actualApis) {
    this.actualApis = actualApis;
  }
  /**
   * Create an AnalyticsApi implementation from an array of concrete
   * implementations.
   *
   * @example
   *
   * ```jsx
   * MultipleAnalyticsApi.fromApis([
   *   SomeAnalyticsApi.fromConfig(configApi),
   *   new CustomAnalyticsApi(),
   * ]);
   * ```
   */
  static fromApis(actualApis) {
    return new MultipleAnalyticsApi(actualApis);
  }
  /**
   * Forward the event to all configured analytics API implementations.
   */
  captureEvent(event) {
    this.actualApis.forEach((analyticsApi) => {
      try {
        analyticsApi.captureEvent(event);
      } catch {
      }
    });
  }
}

class NoOpAnalyticsApi {
  captureEvent(_event) {
  }
}

var __defProp$8 = Object.defineProperty;
var __defNormalProp$8 = (obj, key, value) => key in obj ? __defProp$8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$8 = (obj, key, value) => {
  __defNormalProp$8(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
const STORAGE_KEY$1 = "theme";
class AppThemeSelector {
  constructor(themes) {
    this.themes = themes;
    __publicField$8(this, "activeThemeId");
    __publicField$8(this, "subject", new BehaviorSubject(void 0));
  }
  static createWithStorage(themes) {
    var _a;
    const selector = new AppThemeSelector(themes);
    if (!window.localStorage) {
      return selector;
    }
    const initialThemeId = (_a = window.localStorage.getItem(STORAGE_KEY$1)) != null ? _a : void 0;
    selector.setActiveThemeId(initialThemeId);
    selector.activeThemeId$().subscribe((themeId) => {
      if (themeId) {
        window.localStorage.setItem(STORAGE_KEY$1, themeId);
      } else {
        window.localStorage.removeItem(STORAGE_KEY$1);
      }
    });
    window.addEventListener("storage", (event) => {
      var _a2;
      if (event.key === STORAGE_KEY$1) {
        const themeId = (_a2 = localStorage.getItem(STORAGE_KEY$1)) != null ? _a2 : void 0;
        selector.setActiveThemeId(themeId);
      }
    });
    return selector;
  }
  getInstalledThemes() {
    return this.themes.slice();
  }
  activeThemeId$() {
    return this.subject;
  }
  getActiveThemeId() {
    return this.activeThemeId;
  }
  setActiveThemeId(themeId) {
    this.activeThemeId = themeId;
    this.subject.next(themeId);
  }
}

const ERROR_PREFIX = "Invalid discovery URL pattern,";
class UrlPatternDiscovery {
  constructor(parts) {
    this.parts = parts;
  }
  /**
   * Creates a new UrlPatternDiscovery given a template. The the only
   * interpolation done for the template is to replace instances of `{{pluginId}}`
   * with the ID of the plugin being requested.
   *
   * Example pattern: `http://localhost:7007/api/{{ pluginId }}`
   */
  static compile(pattern) {
    const parts = pattern.split(/\{\{\s*pluginId\s*\}\}/);
    const urlStr = parts.join("pluginId");
    let url;
    try {
      url = new URL(urlStr);
    } catch {
      throw new Error(`${ERROR_PREFIX} URL '${urlStr}' is invalid`);
    }
    if (url.hash) {
      throw new Error(`${ERROR_PREFIX} URL must not have a hash`);
    }
    if (url.search) {
      throw new Error(`${ERROR_PREFIX} URL must not have a query`);
    }
    if (urlStr.endsWith("/")) {
      throw new Error(`${ERROR_PREFIX} URL must not end with a slash`);
    }
    return new UrlPatternDiscovery(parts);
  }
  async getBaseUrl(pluginId) {
    return this.parts.join(encodeURIComponent(pluginId));
  }
}

class FrontendHostDiscovery {
  constructor(endpoints, defaultEndpoint) {
    this.endpoints = endpoints;
    this.defaultEndpoint = defaultEndpoint;
  }
  /**
   * Creates a new FrontendHostDiscovery discovery instance by reading
   * the external target URL from the `discovery.endpoints` config section.
   *
   * eg.
   * ```yaml
   * discovery:
   *  endpoints:
   *    - target: https://internal.example.com/internal-catalog
   *      plugins: [catalog]
   *    - target: https://internal.example.com/secure/api/{{pluginId}}
   *      plugins: [auth, permissions]
   *    - target:
   *        internal: https://internal.example.com/search
   *        external: https://example.com/search
   *      plugins: [search]
   * ```
   *
   * If a plugin is not declared in the config, the discovery will fall back to using the baseUrl with
   * the provided `pathPattern` appended. The default path pattern is `"/api/{{ pluginId }}"`.
   */
  static fromConfig(config, options) {
    var _a, _b;
    const path = (_a = options == null ? void 0 : options.pathPattern) != null ? _a : "/api/{{ pluginId }}";
    const baseUrl = config.getString("backend.baseUrl");
    const endpoints = (_b = config.getOptionalConfigArray("discovery.endpoints")) == null ? void 0 : _b.flatMap((e) => {
      const target = typeof e.get("target") === "object" ? e.getString("target.external") : e.getString("target");
      const discovery = UrlPatternDiscovery.compile(target);
      return e.getStringArray("plugins").map((pluginId) => [pluginId, discovery]);
    });
    return new FrontendHostDiscovery(
      new Map(endpoints),
      UrlPatternDiscovery.compile(`${baseUrl}${path}`)
    );
  }
  async getBaseUrl(pluginId) {
    const endpoint = this.endpoints.get(pluginId);
    if (endpoint) {
      return endpoint.getBaseUrl(pluginId);
    }
    return this.defaultEndpoint.getBaseUrl(pluginId);
  }
}

class ErrorAlerter {
  constructor(alertApi, errorApi) {
    this.alertApi = alertApi;
    this.errorApi = errorApi;
  }
  post(error, context) {
    if (!(context == null ? void 0 : context.hidden)) {
      this.alertApi.post({ message: error.message, severity: "error" });
    }
    return this.errorApi.post(error, context);
  }
  error$() {
    return this.errorApi.error$();
  }
}

var __defProp$7 = Object.defineProperty;
var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$7 = (obj, key, value) => {
  __defNormalProp$7(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class ErrorApiForwarder {
  constructor() {
    __publicField$7(this, "subject", new PublishSubject());
  }
  post(error, context) {
    this.subject.next({ error, context });
  }
  error$() {
    return this.subject;
  }
}

class UnhandledErrorForwarder {
  /**
   * Add event listener, such that unhandled errors can be forwarded using an given `ErrorApi` instance
   */
  static forward(errorApi, errorContext) {
    window.addEventListener(
      "unhandledrejection",
      (e) => {
        errorApi.post(e.reason, errorContext);
      }
    );
  }
}

var __defProp$6 = Object.defineProperty;
var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$6 = (obj, key, value) => {
  __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
function validateFlagName(name) {
  if (name.length < 3) {
    throw new Error(
      `The '${name}' feature flag must have a minimum length of three characters.`
    );
  }
  if (name.length > 150) {
    throw new Error(
      `The '${name}' feature flag must not exceed 150 characters.`
    );
  }
  if (!name.match(/^[a-z]+[a-z0-9-]+$/)) {
    throw new Error(
      `The '${name}' feature flag must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. Examples: feature-flag-one, alpha, release-2020`
    );
  }
}
class LocalStorageFeatureFlags {
  constructor() {
    __publicField$6(this, "registeredFeatureFlags", []);
    __publicField$6(this, "flags");
  }
  registerFlag(flag) {
    validateFlagName(flag.name);
    this.registeredFeatureFlags.push(flag);
  }
  getRegisteredFlags() {
    return this.registeredFeatureFlags.slice();
  }
  isActive(name) {
    if (!this.flags) {
      this.flags = this.load();
    }
    return this.flags.get(name) === FeatureFlagState.Active;
  }
  save(options) {
    if (!this.flags) {
      this.flags = this.load();
    }
    if (!options.merge) {
      this.flags.clear();
    }
    for (const [name, state] of Object.entries(options.states)) {
      this.flags.set(name, state);
    }
    const enabled = Array.from(this.flags.entries()).filter(
      ([, state]) => state === FeatureFlagState.Active
    );
    window.localStorage.setItem(
      "featureFlags",
      JSON.stringify(Object.fromEntries(enabled))
    );
  }
  load() {
    try {
      const jsonStr = window.localStorage.getItem("featureFlags");
      if (!jsonStr) {
        return /* @__PURE__ */ new Map();
      }
      const json = JSON.parse(jsonStr);
      if (typeof json !== "object" || json === null || Array.isArray(json)) {
        return /* @__PURE__ */ new Map();
      }
      const entries = Object.entries(json).filter(([name, value]) => {
        validateFlagName(name);
        return value === FeatureFlagState.Active;
      });
      return new Map(entries);
    } catch {
      return /* @__PURE__ */ new Map();
    }
  }
}

function createFetchApi(options) {
  var _a;
  let result = options.baseImplementation || global.fetch;
  const middleware = [(_a = options.middleware) != null ? _a : []].flat().reverse();
  for (const m of middleware) {
    result = m.apply(result);
  }
  return {
    fetch: result
  };
}

class IdentityAuthInjectorFetchMiddleware {
  constructor(identityApi, allowUrl, headerName, headerValue) {
    this.identityApi = identityApi;
    this.allowUrl = allowUrl;
    this.headerName = headerName;
    this.headerValue = headerValue;
  }
  static create(options) {
    var _a, _b;
    const matcher = buildMatcher(options);
    const headerName = ((_a = options.header) == null ? void 0 : _a.name) || "authorization";
    const headerValue = ((_b = options.header) == null ? void 0 : _b.value) || ((token) => `Bearer ${token}`);
    return new IdentityAuthInjectorFetchMiddleware(
      options.identityApi,
      matcher,
      headerName,
      headerValue
    );
  }
  apply(next) {
    return async (input, init) => {
      const request = new Request(input, init);
      const { token } = await this.identityApi.getCredentials();
      if (request.headers.get(this.headerName) || typeof token !== "string" || !token || !this.allowUrl(request.url)) {
        return next(input, init);
      }
      request.headers.set(this.headerName, this.headerValue(token));
      return next(request);
    };
  }
}
function buildMatcher(options) {
  if (options.allowUrl) {
    return options.allowUrl;
  } else if (options.urlPrefixAllowlist) {
    return buildPrefixMatcher(options.urlPrefixAllowlist);
  } else if (options.config) {
    return buildPrefixMatcher([options.config.getString("backend.baseUrl")]);
  }
  return () => false;
}
function buildPrefixMatcher(prefixes) {
  const trimmedPrefixes = prefixes.map((prefix) => prefix.replace(/\/$/, ""));
  return (url) => trimmedPrefixes.some(
    (prefix) => url === prefix || url.startsWith(`${prefix}/`)
  );
}

function join(left, right) {
  if (!right || right === "/") {
    return left;
  }
  return `${left.replace(/\/$/, "")}/${right.replace(/^\//, "")}`;
}
class PluginProtocolResolverFetchMiddleware {
  constructor(discoveryApi) {
    this.discoveryApi = discoveryApi;
  }
  apply(next) {
    return async (input, init) => {
      const request = new Request(input, init);
      const prefix = "plugin://";
      if (!request.url.startsWith(prefix)) {
        return next(input, init);
      }
      const { hostname, pathname, search, hash, username, password } = new URL(
        `http://${request.url.substring(prefix.length)}`
      );
      let base = await this.discoveryApi.getBaseUrl(hostname);
      if (username || password) {
        const baseUrl = new URL(base);
        const authority = `${username}${password ? `:${password}` : ""}@`;
        base = `${baseUrl.protocol}//${authority}${baseUrl.host}${baseUrl.pathname}`;
      }
      const target = `${join(base, pathname)}${search}${hash}`;
      return next(
        target,
        typeof input === "string" || isUrl(input) ? init : input
      );
    };
  }
}
function isUrl(a) {
  return typeof a === "object" && (a == null ? void 0 : a.constructor) === URL;
}

class FetchMiddlewares {
  /**
   * Handles translation from `plugin://` URLs to concrete http(s) URLs based on
   * the discovery API.
   *
   * @remarks
   *
   * If the request is for `plugin://catalog/entities?filter=x=y`, the discovery
   * API will be queried for `'catalog'`. If it returned
   * `https://backstage.example.net/api/catalog`, the resulting query would be
   * `https://backstage.example.net/api/catalog/entities?filter=x=y`.
   *
   * If the incoming URL protocol was not `plugin`, the request is just passed
   * through verbatim to the underlying implementation.
   */
  static resolvePluginProtocol(options) {
    return new PluginProtocolResolverFetchMiddleware(options.discoveryApi);
  }
  /**
   * Injects a Backstage token header when the user is signed in.
   *
   * @remarks
   *
   * Per default, an `Authorization: Bearer <token>` is generated. This can be
   * customized using the `header` option.
   *
   * The header injection only happens on allowlisted URLs. Per default, if the
   * `config` option is passed in, the `backend.baseUrl` is allowlisted, unless
   * the `urlPrefixAllowlist` or `allowUrl` options are passed in, in which case
   * they take precedence. If you pass in neither config nor an
   * allowlist/callback, the middleware will have no effect since effectively no
   * request will match the (nonexistent) rules.
   */
  static injectIdentityAuth(options) {
    return IdentityAuthInjectorFetchMiddleware.create(options);
  }
  constructor() {
  }
}

var __defProp$5 = Object.defineProperty;
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$5 = (obj, key, value) => {
  __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
function hasScopes(searched, searchFor) {
  for (const scope of searchFor) {
    if (!searched.has(scope)) {
      return false;
    }
  }
  return true;
}
function joinScopes(scopes, ...moreScopess) {
  const result = new Set(scopes);
  for (const moreScopes of moreScopess) {
    for (const scope of moreScopes) {
      result.add(scope);
    }
  }
  return result;
}
class OAuthPendingRequests {
  constructor() {
    __publicField$5(this, "requests", []);
    __publicField$5(this, "subject", new BehaviorSubject(
      this.getCurrentPending()
    ));
  }
  request(scopes) {
    return new Promise((resolve, reject) => {
      this.requests.push({ scopes, resolve, reject });
      this.subject.next(this.getCurrentPending());
    });
  }
  resolve(scopes, result) {
    this.requests = this.requests.filter((request) => {
      if (hasScopes(scopes, request.scopes)) {
        request.resolve(result);
        return false;
      }
      return true;
    });
    this.subject.next(this.getCurrentPending());
  }
  reject(error) {
    this.requests.forEach((request) => request.reject(error));
    this.requests = [];
    this.subject.next(this.getCurrentPending());
  }
  pending() {
    return this.subject;
  }
  getCurrentPending() {
    const currentScopes = this.requests.length === 0 ? void 0 : this.requests.slice(1).reduce(
      (acc, current) => joinScopes(acc, current.scopes),
      this.requests[0].scopes
    );
    return {
      scopes: currentScopes,
      resolve: (value) => {
        if (currentScopes) {
          this.resolve(currentScopes, value);
        }
      },
      reject: (reason) => {
        if (currentScopes) {
          this.reject(reason);
        }
      }
    };
  }
}

var __defProp$4 = Object.defineProperty;
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$4 = (obj, key, value) => {
  __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class OAuthRequestManager {
  constructor() {
    __publicField$4(this, "subject", new BehaviorSubject([]));
    __publicField$4(this, "currentRequests", []);
    __publicField$4(this, "handlerCount", 0);
  }
  createAuthRequester(options) {
    const handler = new OAuthPendingRequests();
    const index = this.handlerCount;
    this.handlerCount++;
    handler.pending().subscribe({
      next: (scopeRequest) => {
        const newRequests = this.currentRequests.slice();
        const request = this.makeAuthRequest(scopeRequest, options);
        if (!request) {
          delete newRequests[index];
        } else {
          newRequests[index] = request;
        }
        this.currentRequests = newRequests;
        this.subject.next(newRequests.filter(Boolean));
      }
    });
    return (scopes) => {
      return handler.request(scopes);
    };
  }
  // Converts the pending request and popup options into a popup request that we can forward to subscribers.
  makeAuthRequest(request, options) {
    const { scopes } = request;
    if (!scopes) {
      return void 0;
    }
    return {
      provider: options.provider,
      trigger: async () => {
        const result = await options.onAuthRequest(scopes);
        request.resolve(result);
      },
      reject: () => {
        const error = new Error("Login failed, rejected by user");
        error.name = "RejectedError";
        request.reject(error);
      }
    };
  }
  authRequest$() {
    return this.subject;
  }
}

var __defProp$3 = Object.defineProperty;
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$3 = (obj, key, value) => {
  __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
const buckets = /* @__PURE__ */ new Map();
const _WebStorage = class _WebStorage {
  constructor(namespace, errorApi) {
    this.namespace = namespace;
    this.errorApi = errorApi;
    __publicField$3(this, "subscribers", /* @__PURE__ */ new Set());
    __publicField$3(this, "observable", new ObservableImpl((subscriber) => {
      this.subscribers.add(subscriber);
      return () => {
        this.subscribers.delete(subscriber);
      };
    }));
  }
  static create(options) {
    var _a;
    return new _WebStorage((_a = options.namespace) != null ? _a : "", options.errorApi);
  }
  static addStorageEventListener() {
    window.addEventListener("storage", (event) => {
      var _a;
      for (const [bucketPath, webStorage] of buckets.entries()) {
        if ((_a = event.key) == null ? void 0 : _a.startsWith(bucketPath)) {
          webStorage.handleStorageChange(event.key);
        }
      }
    });
  }
  get(key) {
    return this.snapshot(key).value;
  }
  snapshot(key) {
    let value = void 0;
    let presence = "absent";
    try {
      const item = localStorage.getItem(this.getKeyName(key));
      if (item) {
        value = JSON.parse(item, (_key, val) => {
          if (typeof val === "object" && val !== null) {
            Object.freeze(val);
          }
          return val;
        });
        presence = "present";
      }
    } catch (e) {
      this.errorApi.post(
        new Error(`Error when parsing JSON config from storage for: ${key}`)
      );
    }
    return { key, value, presence };
  }
  forBucket(name) {
    const bucketPath = `${this.namespace}/${name}`;
    if (!buckets.has(bucketPath)) {
      buckets.set(bucketPath, new _WebStorage(bucketPath, this.errorApi));
    }
    return buckets.get(bucketPath);
  }
  async set(key, data) {
    localStorage.setItem(this.getKeyName(key), JSON.stringify(data));
    this.notifyChanges(key);
  }
  async remove(key) {
    localStorage.removeItem(this.getKeyName(key));
    this.notifyChanges(key);
  }
  observe$(key) {
    if (!_WebStorage.hasSubscribed) {
      _WebStorage.addStorageEventListener();
      _WebStorage.hasSubscribed = true;
    }
    return this.observable.filter(({ key: messageKey }) => messageKey === key);
  }
  handleStorageChange(eventKey) {
    if (!(eventKey == null ? void 0 : eventKey.startsWith(this.namespace))) {
      return;
    }
    const trimmedKey = eventKey == null ? void 0 : eventKey.slice(`${this.namespace}/`.length);
    if (!trimmedKey.includes("/")) {
      this.notifyChanges(decodeURIComponent(trimmedKey));
    }
  }
  getKeyName(key) {
    return `${this.namespace}/${encodeURIComponent(key)}`;
  }
  notifyChanges(key) {
    const snapshot = this.snapshot(key);
    for (const subscription of this.subscribers) {
      subscription.next(snapshot);
    }
  }
};
__publicField$3(_WebStorage, "hasSubscribed", false);
let WebStorage = _WebStorage;

const InternalAppContext = createContext(void 0);

function isReactRouterBeta() {
  const [obj] = createRoutesFromChildren(/* @__PURE__ */ React.createElement(Route, { index: true, element: /* @__PURE__ */ React.createElement("div", null) }));
  return !obj.index;
}

const getExtensionContext = (pathname, routes) => {
  try {
    const matches = matchRoutes(routes, { pathname });
    const routeMatch = matches == null ? void 0 : matches.filter((match) => {
      var _a;
      return ((_a = match == null ? void 0 : match.route.routeRefs) == null ? void 0 : _a.size) > 0;
    }).pop();
    const routeObject = routeMatch == null ? void 0 : routeMatch.route;
    if (!routeObject) {
      return void 0;
    }
    if (routeObject.path === "" && pathname !== "/") {
      return void 0;
    }
    let routeRef;
    if (routeObject.routeRefs.size === 1) {
      routeRef = routeObject.routeRefs.values().next().value;
    }
    let plugin;
    if (routeObject.plugins.size === 1) {
      plugin = routeObject.plugins.values().next().value;
    }
    const params = Object.entries(
      (routeMatch == null ? void 0 : routeMatch.params) || {}
    ).reduce((acc, [key, value]) => {
      if (value !== void 0 && key !== "*") {
        acc[key] = value;
      }
      return acc;
    }, {});
    return {
      extension: "App",
      pluginId: (plugin == null ? void 0 : plugin.getId()) || "root",
      ...routeRef ? { routeRef: routeRef.id } : {},
      _routeNodeType: routeObject.element,
      params
    };
  } catch {
    return void 0;
  }
};
const TrackNavigation = ({
  pathname,
  search,
  hash,
  attributes
}) => {
  const analytics = useAnalytics();
  useEffect(() => {
    analytics.captureEvent("navigate", `${pathname}${search}${hash}`, {
      attributes
    });
  }, [analytics, pathname, search, hash, attributes]);
  return null;
};
const RouteTracker = ({
  routeObjects
}) => {
  const { pathname, search, hash } = useLocation();
  const { params, ...attributes } = getExtensionContext(
    pathname,
    routeObjects
  ) || { params: {} };
  return /* @__PURE__ */ React.createElement(AnalyticsContext, { attributes }, /* @__PURE__ */ React.createElement(
    TrackNavigation,
    {
      pathname,
      search,
      hash,
      attributes: params
    }
  ));
};

function getBasePath(configApi) {
  if (!isReactRouterBeta()) {
    return "";
  }
  return readBasePath(configApi);
}
function readBasePath(configApi) {
  var _a;
  let { pathname } = new URL(
    (_a = configApi.getOptionalString("app.baseUrl")) != null ? _a : "/",
    "http://sample.dev"
    // baseUrl can be specified as just a path
  );
  pathname = pathname.replace(/\/*$/, "");
  return pathname;
}
function SignInPageWrapper({
  component: Component,
  appIdentityProxy,
  children
}) {
  const [identityApi, setIdentityApi] = useState();
  const configApi = useApi(configApiRef);
  const basePath = readBasePath(configApi);
  if (!identityApi) {
    return /* @__PURE__ */ React.createElement(Component, { onSignInSuccess: setIdentityApi });
  }
  appIdentityProxy.setTarget(identityApi, {
    signOutTargetUrl: basePath || "/"
  });
  return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
}
function AppRouter(props) {
  const { Router: RouterComponent, SignInPage: SignInPageComponent } = useApp().getComponents();
  const configApi = useApi(configApiRef);
  const basePath = readBasePath(configApi);
  const mountPath = `${basePath}/*`;
  const internalAppContext = useContext(InternalAppContext);
  if (!internalAppContext) {
    throw new Error("AppRouter must be rendered within the AppProvider");
  }
  const { routeObjects, appIdentityProxy } = internalAppContext;
  if (!SignInPageComponent) {
    appIdentityProxy.setTarget(
      {
        getUserId: () => "guest",
        getIdToken: async () => void 0,
        getProfile: () => ({
          email: "guest@example.com",
          displayName: "Guest"
        }),
        getProfileInfo: async () => ({
          email: "guest@example.com",
          displayName: "Guest"
        }),
        getBackstageIdentity: async () => ({
          type: "user",
          userEntityRef: "user:default/guest",
          ownershipEntityRefs: ["user:default/guest"]
        }),
        getCredentials: async () => ({}),
        signOut: async () => {
        }
      },
      { signOutTargetUrl: basePath || "/" }
    );
    if (isReactRouterBeta()) {
      return /* @__PURE__ */ React.createElement(RouterComponent, null, /* @__PURE__ */ React.createElement(RouteTracker, { routeObjects }), /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, { path: mountPath, element: /* @__PURE__ */ React.createElement(React.Fragment, null, props.children) })));
    }
    return /* @__PURE__ */ React.createElement(RouterComponent, { basename: basePath }, /* @__PURE__ */ React.createElement(RouteTracker, { routeObjects }), props.children);
  }
  if (isReactRouterBeta()) {
    return /* @__PURE__ */ React.createElement(RouterComponent, null, /* @__PURE__ */ React.createElement(RouteTracker, { routeObjects }), /* @__PURE__ */ React.createElement(
      SignInPageWrapper,
      {
        component: SignInPageComponent,
        appIdentityProxy
      },
      /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, { path: mountPath, element: /* @__PURE__ */ React.createElement(React.Fragment, null, props.children) }))
    ));
  }
  return /* @__PURE__ */ React.createElement(RouterComponent, { basename: basePath }, /* @__PURE__ */ React.createElement(RouteTracker, { routeObjects }), /* @__PURE__ */ React.createElement(
    SignInPageWrapper,
    {
      component: SignInPageComponent,
      appIdentityProxy
    },
    props.children
  ));
}
attachComponentData(AppRouter, "core.type", "AppRouter");

function traverseElementTree(options) {
  const collectors = {};
  for (const name in options.collectors) {
    if (options.collectors.hasOwnProperty(name)) {
      collectors[name] = options.collectors[name]();
    }
  }
  const queue = [
    {
      node: Children.toArray(options.root),
      parent: void 0,
      contexts: {}
    }
  ];
  while (queue.length !== 0) {
    const { node, parent, contexts } = queue.shift();
    Children.forEach(node, (element) => {
      if (!isValidElement(element)) {
        return;
      }
      const nextContexts = {};
      for (const name in collectors) {
        if (collectors.hasOwnProperty(name)) {
          const collector = collectors[name];
          nextContexts[name] = collector.visit(
            collector.accumulator,
            element,
            parent,
            contexts[name]
          );
        }
      }
      for (const discoverer of options.discoverers) {
        const children = discoverer(element);
        if (children) {
          queue.push({
            node: children,
            parent: element,
            contexts: nextContexts
          });
        }
      }
    });
  }
  return Object.fromEntries(
    Object.entries(collectors).map(([name, c]) => [name, c.accumulator])
  );
}
function createCollector(accumulatorFactory, visit) {
  return () => ({ accumulator: accumulatorFactory(), visit });
}
function childDiscoverer(element) {
  var _a;
  return (_a = element.props) == null ? void 0 : _a.children;
}
function routeElementDiscoverer(element) {
  var _a, _b, _c;
  if (((_a = element.props) == null ? void 0 : _a.path) && ((_b = element.props) == null ? void 0 : _b.element)) {
    return (_c = element.props) == null ? void 0 : _c.element;
  }
  return void 0;
}

const pluginCollector = createCollector(
  () => /* @__PURE__ */ new Set(),
  (acc, node) => {
    const plugin = getComponentData(node, "core.plugin");
    if (plugin) {
      acc.add(plugin);
    }
  }
);

const FeatureFlagged = (props) => {
  const { children } = props;
  const featureFlagApi = useApi(featureFlagsApiRef);
  const isEnabled = "with" in props ? featureFlagApi.isActive(props.with) : !featureFlagApi.isActive(props.without);
  return /* @__PURE__ */ React.createElement(React.Fragment, null, isEnabled ? children : null);
};
attachComponentData(FeatureFlagged, "core.featureFlagged", true);

const MATCH_ALL_ROUTE = {
  caseSensitive: false,
  path: "*",
  element: "match-all",
  // These elements aren't used, so we add in a bit of debug information
  routeRefs: /* @__PURE__ */ new Set(),
  plugins: /* @__PURE__ */ new Set()
};
function stringifyNode(node) {
  var _a, _b;
  const anyNode = node;
  if (anyNode == null ? void 0 : anyNode.type) {
    return (_b = (_a = anyNode.type.displayName) != null ? _a : anyNode.type.name) != null ? _b : String(anyNode.type);
  }
  return String(anyNode);
}
const pluginSet = (plugin) => {
  const set = /* @__PURE__ */ new Set();
  if (plugin) {
    set.add(plugin);
  }
  return set;
};
function collectSubTree(node, entries = new Array()) {
  Children.forEach(node, (element) => {
    if (!isValidElement(element)) {
      return;
    }
    const routeRef = getComponentData(element, "core.mountPoint");
    if (routeRef) {
      const plugin = getComponentData(element, "core.plugin");
      entries.push({ routeRef, plugin });
    }
    collectSubTree(element.props.children, entries);
  });
  return entries;
}
const routingV2Collector = createCollector(
  () => ({
    paths: /* @__PURE__ */ new Map(),
    parents: /* @__PURE__ */ new Map(),
    objects: new Array()
  }),
  (acc, node, parent, ctx) => {
    var _a, _b, _c, _d, _e, _f, _g;
    if (ctx == null ? void 0 : ctx.isElementAncestor) {
      return ctx;
    }
    if ((parent == null ? void 0 : parent.props.element) === node) {
      return { ...ctx, isElementAncestor: true };
    }
    const pathProp = (_a = node.props) == null ? void 0 : _a.path;
    const mountPoint = getComponentData(node, "core.mountPoint");
    if (mountPoint && pathProp) {
      throw new Error(
        `Path property may not be set directly on a routable extension "${stringifyNode(
          node
        )}"`
      );
    }
    const parentChildren = (_c = (_b = ctx == null ? void 0 : ctx.obj) == null ? void 0 : _b.children) != null ? _c : acc.objects;
    if (pathProp !== void 0) {
      if (typeof pathProp !== "string") {
        throw new Error(
          `Element path must be a string at "${stringifyNode(node)}"`
        );
      }
      const path = pathProp.startsWith("/") ? pathProp.slice(1) : pathProp;
      const elementProp = node.props.element;
      if (getComponentData(node, "core.gatherMountPoints")) {
        if (elementProp) {
          throw new Error(
            `Mount point gatherers may not have an element prop "${stringifyNode(
              node
            )}"`
          );
        }
        const newObj = {
          path,
          element: "gathered",
          routeRefs: /* @__PURE__ */ new Set(),
          caseSensitive: Boolean((_d = node.props) == null ? void 0 : _d.caseSensitive),
          children: [MATCH_ALL_ROUTE],
          plugins: /* @__PURE__ */ new Set()
        };
        parentChildren.push(newObj);
        return {
          obj: newObj,
          gatherPath: path,
          routeRef: ctx == null ? void 0 : ctx.routeRef,
          gatherRouteRef: ctx == null ? void 0 : ctx.routeRef
        };
      }
      if (elementProp) {
        const [extension, ...others] = collectSubTree(elementProp);
        if (others.length > 0) {
          throw new Error(
            `Route element with path "${pathProp}" may not contain multiple routable extensions`
          );
        }
        if (!extension) {
          return ctx;
        }
        const { routeRef, plugin } = extension;
        const newObj = {
          path,
          element: "mounted",
          routeRefs: /* @__PURE__ */ new Set([routeRef]),
          caseSensitive: Boolean((_e = node.props) == null ? void 0 : _e.caseSensitive),
          children: [MATCH_ALL_ROUTE],
          plugins: pluginSet(plugin)
        };
        parentChildren.push(newObj);
        acc.paths.set(routeRef, path);
        acc.parents.set(routeRef, ctx == null ? void 0 : ctx.routeRef);
        return {
          obj: newObj,
          routeRef: routeRef != null ? routeRef : ctx == null ? void 0 : ctx.routeRef,
          gatherPath: path,
          gatherRouteRef: ctx == null ? void 0 : ctx.gatherRouteRef
        };
      }
    }
    if (mountPoint) {
      if ((ctx == null ? void 0 : ctx.gatherPath) === void 0) {
        throw new Error(
          `Routable extension "${stringifyNode(
            node
          )}" with mount point "${mountPoint}" must be assigned a path`
        );
      }
      (_f = ctx == null ? void 0 : ctx.obj) == null ? void 0 : _f.routeRefs.add(mountPoint);
      const mountPointPlugin = getComponentData(
        node,
        "core.plugin"
      );
      if (mountPointPlugin) {
        (_g = ctx == null ? void 0 : ctx.obj) == null ? void 0 : _g.plugins.add(mountPointPlugin);
      }
      acc.paths.set(mountPoint, ctx.gatherPath);
      acc.parents.set(mountPoint, ctx == null ? void 0 : ctx.gatherRouteRef);
      return {
        ...ctx,
        routeRef: mountPoint
      };
    }
    return ctx;
  }
);
const routingV1Collector = createCollector(
  () => ({
    paths: /* @__PURE__ */ new Map(),
    parents: /* @__PURE__ */ new Map(),
    objects: new Array()
  }),
  (acc, node, parent, ctx) => {
    var _a, _b, _c, _d, _e;
    if ((parent == null ? void 0 : parent.props.element) === node) {
      return ctx;
    }
    let currentObj = ctx == null ? void 0 : ctx.obj;
    let currentParentRouteRef = ctx == null ? void 0 : ctx.routeRef;
    let sticky = ctx == null ? void 0 : ctx.sticky;
    const path = (_a = node.props) == null ? void 0 : _a.path;
    const parentChildren = (_b = currentObj == null ? void 0 : currentObj.children) != null ? _b : acc.objects;
    const caseSensitive = Boolean((_c = node.props) == null ? void 0 : _c.caseSensitive);
    let currentCtxPath = ctx == null ? void 0 : ctx.path;
    if (getComponentData(node, "core.gatherMountPoints")) {
      if (!path) {
        throw new Error("Mount point gatherer must have a path");
      }
      currentCtxPath = path;
    }
    const element = (_d = node.props) == null ? void 0 : _d.element;
    let routeRef = getComponentData(node, "core.mountPoint");
    if (!routeRef && isValidElement(element)) {
      routeRef = getComponentData(element, "core.mountPoint");
    }
    if (routeRef) {
      let routePath = path;
      if (currentCtxPath) {
        if (routePath) {
          currentCtxPath = void 0;
        } else {
          routePath = currentCtxPath;
        }
      }
      if (!routePath) {
        throw new Error("Mounted routable extension must have a path");
      }
      acc.paths.set(routeRef, routePath);
      if (currentParentRouteRef && sticky) {
        acc.parents.set(routeRef, currentParentRouteRef);
        if (path) {
          currentParentRouteRef = routeRef;
          sticky = false;
        }
      } else {
        acc.parents.set(routeRef, currentParentRouteRef);
        currentParentRouteRef = routeRef;
      }
      if (path) {
        currentObj = {
          caseSensitive,
          path,
          element: "mounted",
          routeRefs: /* @__PURE__ */ new Set([routeRef]),
          children: [MATCH_ALL_ROUTE],
          plugins: pluginSet(
            getComponentData(
              node.props.element,
              "core.plugin"
            )
          )
        };
        parentChildren.push(currentObj);
      } else {
        currentObj == null ? void 0 : currentObj.routeRefs.add(routeRef);
      }
    }
    if (getComponentData(node, "core.gatherMountPoints")) {
      sticky = true;
    }
    const isGatherer = getComponentData(
      node,
      "core.gatherMountPoints"
    );
    if (isGatherer) {
      if (!path) {
        throw new Error("Mount point gatherer must have a path");
      }
      if (!routeRef) {
        currentObj = {
          caseSensitive,
          path,
          element: "gathered",
          routeRefs: /* @__PURE__ */ new Set(),
          children: [MATCH_ALL_ROUTE],
          plugins: ((_e = ctx == null ? void 0 : ctx.obj) == null ? void 0 : _e.plugins) || /* @__PURE__ */ new Set()
        };
        parentChildren.push(currentObj);
      }
    }
    return {
      obj: currentObj,
      path: currentCtxPath,
      routeRef: currentParentRouteRef,
      sticky
    };
  }
);
const featureFlagCollector = createCollector(
  () => /* @__PURE__ */ new Set(),
  (acc, node) => {
    if (node.type === FeatureFlagged) {
      const props = node.props;
      acc.add("with" in props ? props.with : props.without);
    }
  }
);

const routeRefType = getOrCreateGlobalSingleton(
  "route-ref-type",
  () => Symbol("route-ref-type")
);
function isRouteRef(routeRef) {
  return routeRef[routeRefType] === "absolute";
}
function isSubRouteRef(routeRef) {
  return routeRef[routeRefType] === "sub";
}
function isExternalRouteRef(routeRef) {
  return routeRef[routeRefType] === "external";
}

function joinPaths(...paths) {
  const normalized = paths.join("/").replace(/\/\/+/g, "/");
  if (normalized !== "/" && normalized.endsWith("/")) {
    return normalized.slice(0, -1);
  }
  return normalized;
}

function resolveTargetRef(anyRouteRef, routePaths, routeBindings) {
  let targetRef;
  let subRoutePath = "";
  if (isRouteRef(anyRouteRef)) {
    targetRef = anyRouteRef;
  } else if (isSubRouteRef(anyRouteRef)) {
    targetRef = anyRouteRef.parent;
    subRoutePath = anyRouteRef.path;
  } else if (isExternalRouteRef(anyRouteRef)) {
    const resolvedRoute = routeBindings.get(anyRouteRef);
    if (!resolvedRoute) {
      return [void 0, ""];
    }
    if (isRouteRef(resolvedRoute)) {
      targetRef = resolvedRoute;
    } else if (isSubRouteRef(resolvedRoute)) {
      targetRef = resolvedRoute.parent;
      subRoutePath = resolvedRoute.path;
    } else {
      throw new Error(
        `ExternalRouteRef was bound to invalid target, ${resolvedRoute}`
      );
    }
  } else if (anyRouteRef[routeRefType]) {
    throw new Error(
      `Unknown or invalid route ref type, ${anyRouteRef[routeRefType]}`
    );
  } else {
    throw new Error(`Unknown object passed to useRouteRef, got ${anyRouteRef}`);
  }
  if (!targetRef) {
    return [void 0, ""];
  }
  const resolvedPath = routePaths.get(targetRef);
  if (resolvedPath === void 0) {
    return [void 0, ""];
  }
  const targetPath = joinPaths(resolvedPath, subRoutePath);
  return [targetRef, targetPath];
}
function resolveBasePath(targetRef, sourceLocation, routePaths, routeParents, routeObjects) {
  var _a;
  const match = (_a = matchRoutes(routeObjects, sourceLocation)) != null ? _a : [];
  const refDiffList = Array();
  let matchIndex = -1;
  for (let targetSearchRef = targetRef; targetSearchRef; targetSearchRef = routeParents.get(targetSearchRef)) {
    matchIndex = match.findIndex(
      (m) => m.route.routeRefs.has(targetSearchRef)
    );
    if (matchIndex !== -1) {
      break;
    }
    refDiffList.unshift(targetSearchRef);
  }
  if (refDiffList.length === 0) {
    matchIndex -= 1;
  }
  const parentPath = matchIndex === -1 ? "" : match[matchIndex].pathname;
  const diffPaths = refDiffList.slice(0, -1).map((ref) => {
    const path = routePaths.get(ref);
    if (path === void 0) {
      throw new Error(`No path for ${ref}`);
    }
    if (path.includes(":")) {
      throw new Error(
        `Cannot route to ${targetRef} with parent ${ref} as it has parameters`
      );
    }
    return path;
  });
  return `${joinPaths(parentPath, ...diffPaths)}/`;
}
class RouteResolver {
  constructor(routePaths, routeParents, routeObjects, routeBindings, appBasePath) {
    this.routePaths = routePaths;
    this.routeParents = routeParents;
    this.routeObjects = routeObjects;
    this.routeBindings = routeBindings;
    this.appBasePath = appBasePath;
  }
  resolve(anyRouteRef, sourceLocation) {
    const [targetRef, targetPath] = resolveTargetRef(
      anyRouteRef,
      this.routePaths,
      this.routeBindings
    );
    if (!targetRef) {
      return void 0;
    }
    let relativeSourceLocation;
    if (typeof sourceLocation === "string") {
      relativeSourceLocation = this.trimPath(sourceLocation);
    } else if (sourceLocation.pathname) {
      relativeSourceLocation = {
        ...sourceLocation,
        pathname: this.trimPath(sourceLocation.pathname)
      };
    } else {
      relativeSourceLocation = sourceLocation;
    }
    const basePath = this.appBasePath + resolveBasePath(
      targetRef,
      relativeSourceLocation,
      this.routePaths,
      this.routeParents,
      this.routeObjects
    );
    const routeFunc = (...[params]) => {
      const encodedParams = params && mapValues(params, (value) => {
        if (typeof value === "string") {
          return value.replaceAll(/[&?#;\/]/g, (c) => encodeURIComponent(c));
        }
        return value;
      });
      return joinPaths(basePath, generatePath(targetPath, encodedParams));
    };
    return routeFunc;
  }
  trimPath(targetPath) {
    if (!targetPath) {
      return targetPath;
    }
    if (targetPath.startsWith(this.appBasePath)) {
      return targetPath.slice(this.appBasePath.length);
    }
    return targetPath;
  }
}

const RoutingContext = createVersionedContext(
  "routing-context"
);
const RoutingProvider = ({
  routePaths,
  routeParents,
  routeObjects,
  routeBindings,
  basePath = "",
  children
}) => {
  const resolver = new RouteResolver(
    routePaths,
    routeParents,
    routeObjects,
    routeBindings,
    basePath
  );
  const versionedValue = createVersionedValueMap({ 1: resolver });
  return /* @__PURE__ */ React.createElement(RoutingContext.Provider, { value: versionedValue }, children);
};

function validateRouteParameters(routePaths, routeParents) {
  const notLeafRoutes = new Set(routeParents.values());
  notLeafRoutes.delete(void 0);
  for (const route of routeParents.keys()) {
    if (notLeafRoutes.has(route)) {
      continue;
    }
    let currentRouteRef = route;
    let fullPath = "";
    while (currentRouteRef) {
      const path = routePaths.get(currentRouteRef);
      if (path === void 0) {
        throw new Error(`No path for ${currentRouteRef}`);
      }
      fullPath = joinPaths(path, fullPath);
      currentRouteRef = routeParents.get(currentRouteRef);
    }
    const params = fullPath.match(/:(\w+)/g);
    if (params) {
      for (let j = 0; j < params.length; j++) {
        for (let i = j + 1; i < params.length; i++) {
          if (params[i] === params[j]) {
            throw new Error(
              `Parameter ${params[i]} is duplicated in path ${fullPath}`
            );
          }
        }
      }
    }
  }
}
function validateRouteBindings(routeBindings, plugins) {
  for (const plugin of plugins) {
    if (!plugin.externalRoutes) {
      continue;
    }
    for (const [name, externalRouteRef] of Object.entries(
      plugin.externalRoutes
    )) {
      if (externalRouteRef.optional) {
        continue;
      }
      if (!routeBindings.has(externalRouteRef)) {
        throw new Error(
          `External route '${name}' of the '${plugin.getId()}' plugin must be bound to a target route. See https://backstage.io/link?bind-routes for details.`
        );
      }
    }
  }
}

const AppContext = createVersionedContext("app-context");
const AppContextProvider = ({
  appContext,
  children
}) => {
  const versionedValue = createVersionedValueMap({ 1: appContext });
  return /* @__PURE__ */ React.createElement(AppContext.Provider, { value: versionedValue, children });
};

var __defProp$2 = Object.defineProperty;
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$2 = (obj, key, value) => {
  __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
function mkError(thing) {
  return new Error(
    `Tried to access IdentityApi ${thing} before app was loaded`
  );
}
function logDeprecation(thing) {
  console.warn(
    `WARNING: Call to ${thing} is deprecated and will break in the future`
  );
}
class AppIdentityProxy {
  constructor() {
    __publicField$2(this, "target");
    __publicField$2(this, "waitForTarget");
    __publicField$2(this, "resolveTarget", () => {
    });
    __publicField$2(this, "signOutTargetUrl", "/");
    this.waitForTarget = new Promise((resolve) => {
      this.resolveTarget = resolve;
    });
  }
  // This is called by the app manager once the sign-in page provides us with an implementation
  setTarget(identityApi, targetOptions) {
    this.target = identityApi;
    this.signOutTargetUrl = targetOptions.signOutTargetUrl;
    this.resolveTarget(identityApi);
  }
  getUserId() {
    if (!this.target) {
      throw mkError("getUserId");
    }
    if (!this.target.getUserId) {
      throw new Error("IdentityApi does not implement getUserId");
    }
    logDeprecation("getUserId");
    return this.target.getUserId();
  }
  getProfile() {
    if (!this.target) {
      throw mkError("getProfile");
    }
    if (!this.target.getProfile) {
      throw new Error("IdentityApi does not implement getProfile");
    }
    logDeprecation("getProfile");
    return this.target.getProfile();
  }
  async getProfileInfo() {
    return this.waitForTarget.then((target) => target.getProfileInfo());
  }
  async getBackstageIdentity() {
    const identity = await this.waitForTarget.then(
      (target) => target.getBackstageIdentity()
    );
    if (!identity.userEntityRef.match(/^.*:.*\/.*$/)) {
      console.warn(
        `WARNING: The App IdentityApi provided an invalid userEntityRef, '${identity.userEntityRef}'. It must be a full Entity Reference of the form '<kind>:<namespace>/<name>'.`
      );
    }
    return identity;
  }
  async getCredentials() {
    return this.waitForTarget.then((target) => target.getCredentials());
  }
  async getIdToken() {
    return this.waitForTarget.then((target) => {
      if (!target.getIdToken) {
        throw new Error("IdentityApi does not implement getIdToken");
      }
      logDeprecation("getIdToken");
      return target.getIdToken();
    });
  }
  async signOut() {
    await this.waitForTarget.then((target) => target.signOut());
    window.location.href = this.signOutTargetUrl;
  }
}

function resolveTheme(themeId, shouldPreferDark, themes) {
  if (themeId !== void 0) {
    const selectedTheme = themes.find((theme) => theme.id === themeId);
    if (selectedTheme) {
      return selectedTheme;
    }
  }
  if (shouldPreferDark) {
    const darkTheme = themes.find((theme) => theme.variant === "dark");
    if (darkTheme) {
      return darkTheme;
    }
  }
  const lightTheme = themes.find((theme) => theme.variant === "light");
  if (lightTheme) {
    return lightTheme;
  }
  return themes[0];
}
const useShouldPreferDarkTheme = () => {
  const mediaQuery = useMemo(
    () => window.matchMedia("(prefers-color-scheme: dark)"),
    []
  );
  const [shouldPreferDark, setPrefersDark] = useState(mediaQuery.matches);
  useEffect(() => {
    const listener = (event) => {
      setPrefersDark(event.matches);
    };
    mediaQuery.addListener(listener);
    return () => {
      mediaQuery.removeListener(listener);
    };
  }, [mediaQuery]);
  return shouldPreferDark;
};
function AppThemeProvider({ children }) {
  const appThemeApi = useApi(appThemeApiRef);
  const themeId = useObservable(
    appThemeApi.activeThemeId$(),
    appThemeApi.getActiveThemeId()
  );
  const shouldPreferDark = Boolean(window.matchMedia) ? useShouldPreferDarkTheme() : false;
  const appTheme = resolveTheme(
    themeId,
    shouldPreferDark,
    appThemeApi.getInstalledThemes()
  );
  if (!appTheme) {
    throw new Error("App has no themes");
  }
  return /* @__PURE__ */ React.createElement(appTheme.Provider, { children });
}

const defaultConfigLoader = async () => defaultConfigLoaderSync();
function defaultConfigLoaderSync(runtimeConfigJson = "__APP_INJECTED_RUNTIME_CONFIG__") {
  const appConfig = process.env.APP_CONFIG;
  if (!appConfig) {
    throw new Error("No static configuration provided");
  }
  if (!Array.isArray(appConfig)) {
    throw new Error("Static configuration has invalid format");
  }
  const configs = appConfig.slice();
  if (runtimeConfigJson !== "__app_injected_runtime_config__".toLocaleUpperCase("en-US")) {
    try {
      const data = JSON.parse(runtimeConfigJson);
      if (Array.isArray(data)) {
        configs.push(...data);
      } else {
        configs.push({ data, context: "env" });
      }
    } catch (error) {
      throw new Error(`Failed to load runtime configuration, ${error}`);
    }
  }
  const windowAppConfig = window.__APP_CONFIG__;
  if (windowAppConfig) {
    configs.push({
      context: "window",
      data: windowAppConfig
    });
  }
  return configs;
}

var __defProp$1 = Object.defineProperty;
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$1 = (obj, key, value) => {
  __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
class ApiRegistryBuilder {
  constructor() {
    __publicField$1(this, "apis", []);
  }
  add(api, impl) {
    this.apis.push([api.id, impl]);
    return impl;
  }
  build() {
    return new ApiRegistry(new Map(this.apis));
  }
}
class ApiRegistry {
  constructor(apis) {
    this.apis = apis;
  }
  static builder() {
    return new ApiRegistryBuilder();
  }
  /**
   * Creates a new ApiRegistry with a list of API implementations.
   *
   * @param apis - A list of pairs mapping an ApiRef to its respective implementation
   */
  static from(apis) {
    return new ApiRegistry(new Map(apis.map(([api, impl]) => [api.id, impl])));
  }
  /**
   * Creates a new ApiRegistry with a single API implementation.
   *
   * @param api - ApiRef for the API to add
   * @param impl - Implementation of the API to add
   */
  static with(api, impl) {
    return new ApiRegistry(/* @__PURE__ */ new Map([[api.id, impl]]));
  }
  /**
   * Returns a new ApiRegistry with the provided API added to the existing ones.
   *
   * @param api - ApiRef for the API to add
   * @param impl - Implementation of the API to add
   */
  with(api, impl) {
    return new ApiRegistry(new Map([...this.apis, [api.id, impl]]));
  }
  get(api) {
    return this.apis.get(api.id);
  }
}

function resolveRouteBindings(bindRoutes) {
  const result = /* @__PURE__ */ new Map();
  if (bindRoutes) {
    const bind = (externalRoutes, targetRoutes) => {
      for (const [key, value] of Object.entries(targetRoutes)) {
        const externalRoute = externalRoutes[key];
        if (!externalRoute) {
          throw new Error(`Key ${key} is not an existing external route`);
        }
        if (!value && !externalRoute.optional) {
          throw new Error(
            `External route ${key} is required but was undefined`
          );
        }
        if (value) {
          result.set(externalRoute, value);
        }
      }
    };
    bindRoutes({ bind });
  }
  return result;
}

var __accessCheck$2 = (obj, member, msg) => {
  if (!member.has(obj))
    throw TypeError("Cannot " + msg);
};
var __privateGet$2 = (obj, member, getter) => {
  __accessCheck$2(obj, member, "read from private field");
  return getter ? getter.call(obj) : member.get(obj);
};
var __privateAdd$2 = (obj, member, value) => {
  if (member.has(obj))
    throw TypeError("Cannot add the same private member more than once");
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
};
var __privateSet$2 = (obj, member, value, setter) => {
  __accessCheck$2(obj, member, "write to private field");
  setter ? setter.call(obj, value) : member.set(obj, value);
  return value;
};
var _languages, _language$1, _subject;
const STORAGE_KEY = "language";
const DEFAULT_LANGUAGE = "en";
const _AppLanguageSelector = class _AppLanguageSelector {
  constructor(languages, initialLanguage) {
    __privateAdd$2(this, _languages, void 0);
    __privateAdd$2(this, _language$1, void 0);
    __privateAdd$2(this, _subject, void 0);
    __privateSet$2(this, _languages, languages);
    __privateSet$2(this, _language$1, initialLanguage);
    __privateSet$2(this, _subject, new BehaviorSubject({
      language: __privateGet$2(this, _language$1)
    }));
  }
  static create(options) {
    var _a, _b;
    const languages = (_a = options == null ? void 0 : options.availableLanguages) != null ? _a : [DEFAULT_LANGUAGE];
    if (languages.length !== new Set(languages).size) {
      throw new Error(
        `Supported languages may not contain duplicates, got '${languages.join(
          "', '"
        )}'`
      );
    }
    if (!languages.includes(DEFAULT_LANGUAGE)) {
      throw new Error(`Supported languages must include '${DEFAULT_LANGUAGE}'`);
    }
    const initialLanguage = (_b = options == null ? void 0 : options.defaultLanguage) != null ? _b : DEFAULT_LANGUAGE;
    if (!languages.includes(initialLanguage)) {
      throw new Error(
        `Initial language must be one of the supported languages, got '${initialLanguage}'`
      );
    }
    return new _AppLanguageSelector(languages, initialLanguage);
  }
  static createWithStorage(options) {
    const selector = _AppLanguageSelector.create(options);
    if (!window.localStorage) {
      return selector;
    }
    const storedLanguage = window.localStorage.getItem(STORAGE_KEY);
    const { languages } = selector.getAvailableLanguages();
    if (storedLanguage && languages.includes(storedLanguage)) {
      selector.setLanguage(storedLanguage);
    }
    selector.language$().subscribe(({ language }) => {
      if (language !== window.localStorage.getItem(STORAGE_KEY)) {
        window.localStorage.setItem(STORAGE_KEY, language);
      }
    });
    window.addEventListener("storage", (event) => {
      var _a;
      if (event.key === STORAGE_KEY) {
        const language = (_a = localStorage.getItem(STORAGE_KEY)) != null ? _a : void 0;
        if (language) {
          selector.setLanguage(language);
        }
      }
    });
    return selector;
  }
  getAvailableLanguages() {
    return { languages: __privateGet$2(this, _languages).slice() };
  }
  setLanguage(language) {
    const lng = language != null ? language : DEFAULT_LANGUAGE;
    if (lng === __privateGet$2(this, _language$1)) {
      return;
    }
    if (lng && !__privateGet$2(this, _languages).includes(lng)) {
      throw new Error(
        `Failed to change language to '${lng}', available languages are '${__privateGet$2(this, _languages).join(
          "', '"
        )}'`
      );
    }
    __privateSet$2(this, _language$1, lng);
    __privateGet$2(this, _subject).next({ language: lng });
  }
  getLanguage() {
    return { language: __privateGet$2(this, _language$1) };
  }
  language$() {
    return __privateGet$2(this, _subject);
  }
};
_languages = new WeakMap();
_language$1 = new WeakMap();
_subject = new WeakMap();
let AppLanguageSelector = _AppLanguageSelector;

function toInternalTranslationResource(resource) {
  const r = resource;
  if (r.$$type !== "@backstage/TranslationResource") {
    throw new Error(`Invalid translation resource, bad type '${r.$$type}'`);
  }
  if (r.version !== "v1") {
    throw new Error(`Invalid translation resource, bad version '${r.version}'`);
  }
  return r;
}

function toInternalTranslationRef(ref) {
  const r = ref;
  if (r.$$type !== "@backstage/TranslationRef") {
    throw new Error(`Invalid translation ref, bad type '${r.$$type}'`);
  }
  if (r.version !== "v1") {
    throw new Error(`Invalid translation ref, bad version '${r.version}'`);
  }
  return r;
}

var __accessCheck$1 = (obj, member, msg) => {
  if (!member.has(obj))
    throw TypeError("Cannot " + msg);
};
var __privateGet$1 = (obj, member, getter) => {
  __accessCheck$1(obj, member, "read from private field");
  return getter ? getter.call(obj) : member.get(obj);
};
var __privateAdd$1 = (obj, member, value) => {
  if (member.has(obj))
    throw TypeError("Cannot add the same private member more than once");
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
};
var __privateSet$1 = (obj, member, value, setter) => {
  __accessCheck$1(obj, member, "write to private field");
  setter ? setter.call(obj, value) : member.set(obj, value);
  return value;
};
var __privateMethod = (obj, member, method) => {
  __accessCheck$1(obj, member, "access private method");
  return method;
};
var _loaded, _loading, _loaders, _getLoaderKey, getLoaderKey_fn, _i18n, _loader, _language, _registeredRefs, _languageChangeListeners, _changeLanguage, changeLanguage_fn, _createSnapshot, createSnapshot_fn, _registerDefaults, registerDefaults_fn;
function removeNulls(messages) {
  return Object.fromEntries(
    Object.entries(messages).filter(
      (e) => e[1] !== null
    )
  );
}
class ResourceLoader {
  constructor(onLoad) {
    this.onLoad = onLoad;
    __privateAdd$1(this, _getLoaderKey);
    /** Loaded resources by loader key */
    __privateAdd$1(this, _loaded, /* @__PURE__ */ new Set());
    /** Resource loading promises by loader key */
    __privateAdd$1(this, _loading, /* @__PURE__ */ new Map());
    /** Loaders for each resource language */
    __privateAdd$1(this, _loaders, /* @__PURE__ */ new Map());
  }
  addTranslationResource(resource) {
    const internalResource = toInternalTranslationResource(resource);
    for (const entry of internalResource.resources) {
      const key = __privateMethod(this, _getLoaderKey, getLoaderKey_fn).call(this, entry.language, internalResource.id);
      if (!__privateGet$1(this, _loaders).has(key)) {
        __privateGet$1(this, _loaders).set(key, entry.loader);
      }
    }
  }
  needsLoading(language, namespace) {
    const key = __privateMethod(this, _getLoaderKey, getLoaderKey_fn).call(this, language, namespace);
    const loader = __privateGet$1(this, _loaders).get(key);
    if (!loader) {
      return false;
    }
    return !__privateGet$1(this, _loaded).has(key);
  }
  async load(language, namespace) {
    const key = __privateMethod(this, _getLoaderKey, getLoaderKey_fn).call(this, language, namespace);
    const loader = __privateGet$1(this, _loaders).get(key);
    if (!loader) {
      return;
    }
    if (__privateGet$1(this, _loaded).has(key)) {
      return;
    }
    const loading = __privateGet$1(this, _loading).get(key);
    if (loading) {
      await loading;
      return;
    }
    const load = loader().then(
      (result) => {
        this.onLoad({ language, namespace, messages: result.messages });
        __privateGet$1(this, _loaded).add(key);
      },
      (error) => {
        __privateGet$1(this, _loaded).add(key);
        throw error;
      }
    );
    __privateGet$1(this, _loading).set(key, load);
    await load;
  }
}
_loaded = new WeakMap();
_loading = new WeakMap();
_loaders = new WeakMap();
_getLoaderKey = new WeakSet();
getLoaderKey_fn = function(language, namespace) {
  return `${language}/${namespace}`;
};
const _I18nextTranslationApi = class _I18nextTranslationApi {
  constructor(i18n, loader, language) {
    __privateAdd$1(this, _changeLanguage);
    __privateAdd$1(this, _createSnapshot);
    __privateAdd$1(this, _registerDefaults);
    __privateAdd$1(this, _i18n, void 0);
    __privateAdd$1(this, _loader, void 0);
    __privateAdd$1(this, _language, void 0);
    /** Keep track of which refs we have registered default resources for */
    __privateAdd$1(this, _registeredRefs, /* @__PURE__ */ new Set());
    /** Notify observers when language changes */
    __privateAdd$1(this, _languageChangeListeners, /* @__PURE__ */ new Set());
    __privateSet$1(this, _i18n, i18n);
    __privateSet$1(this, _loader, loader);
    __privateSet$1(this, _language, language);
  }
  static create(options) {
    const { languages } = options.languageApi.getAvailableLanguages();
    const i18n = createInstance({
      fallbackLng: DEFAULT_LANGUAGE,
      supportedLngs: languages,
      interpolation: {
        escapeValue: false
      },
      ns: [],
      defaultNS: false,
      fallbackNS: false,
      // Disable resource loading on init, meaning i18n will be ready to use immediately
      initImmediate: false
    });
    i18n.init();
    if (!i18n.isInitialized) {
      throw new Error("i18next was unexpectedly not initialized");
    }
    const { language: initialLanguage } = options.languageApi.getLanguage();
    if (initialLanguage !== DEFAULT_LANGUAGE) {
      i18n.changeLanguage(initialLanguage);
    }
    const loader = new ResourceLoader((loaded) => {
      i18n.addResourceBundle(
        loaded.language,
        loaded.namespace,
        removeNulls(loaded.messages),
        false,
        // do not merge with existing translations
        true
        // overwrite translations
      );
    });
    const resources = (options == null ? void 0 : options.resources) || [];
    for (let i = resources.length - 1; i >= 0; i--) {
      const resource = resources[i];
      if (resource.$$type === "@backstage/TranslationResource") {
        loader.addTranslationResource(resource);
      } else if (resource.$$type === "@backstage/TranslationMessages") {
        i18n.addResourceBundle(
          DEFAULT_LANGUAGE,
          resource.id,
          removeNulls(resource.messages),
          true,
          // merge with existing translations
          false
          // do not overwrite translations
        );
      }
    }
    const instance = new _I18nextTranslationApi(
      i18n,
      loader,
      options.languageApi.getLanguage().language
    );
    options.languageApi.language$().subscribe(({ language }) => {
      var _a;
      __privateMethod(_a = instance, _changeLanguage, changeLanguage_fn).call(_a, language);
    });
    return instance;
  }
  getTranslation(translationRef) {
    const internalRef = toInternalTranslationRef(translationRef);
    __privateMethod(this, _registerDefaults, registerDefaults_fn).call(this, internalRef);
    return __privateMethod(this, _createSnapshot, createSnapshot_fn).call(this, internalRef);
  }
  translation$(translationRef) {
    const internalRef = toInternalTranslationRef(translationRef);
    __privateMethod(this, _registerDefaults, registerDefaults_fn).call(this, internalRef);
    return new ObservableImpl((subscriber) => {
      let loadTicket = {};
      const loadResource = () => {
        loadTicket = {};
        const ticket = loadTicket;
        __privateGet$1(this, _loader).load(__privateGet$1(this, _language), internalRef.id).then(
          () => {
            if (ticket === loadTicket) {
              const snapshot = __privateMethod(this, _createSnapshot, createSnapshot_fn).call(this, internalRef);
              if (snapshot.ready) {
                subscriber.next(snapshot);
              }
            }
          },
          (error) => {
            if (ticket === loadTicket) {
              subscriber.error(Array.isArray(error) ? error[0] : error);
            }
          }
        );
      };
      const onChange = () => {
        const snapshot = __privateMethod(this, _createSnapshot, createSnapshot_fn).call(this, internalRef);
        if (snapshot.ready) {
          subscriber.next(snapshot);
        } else {
          loadResource();
        }
      };
      if (__privateGet$1(this, _loader).needsLoading(__privateGet$1(this, _language), internalRef.id)) {
        loadResource();
      }
      __privateGet$1(this, _languageChangeListeners).add(onChange);
      return () => {
        __privateGet$1(this, _languageChangeListeners).delete(onChange);
      };
    });
  }
};
_i18n = new WeakMap();
_loader = new WeakMap();
_language = new WeakMap();
_registeredRefs = new WeakMap();
_languageChangeListeners = new WeakMap();
_changeLanguage = new WeakSet();
changeLanguage_fn = function(language) {
  if (__privateGet$1(this, _language) !== language) {
    __privateSet$1(this, _language, language);
    __privateGet$1(this, _i18n).changeLanguage(language);
    __privateGet$1(this, _languageChangeListeners).forEach((listener) => listener());
  }
};
_createSnapshot = new WeakSet();
createSnapshot_fn = function(internalRef) {
  if (__privateGet$1(this, _loader).needsLoading(__privateGet$1(this, _language), internalRef.id)) {
    return { ready: false };
  }
  const t = __privateGet$1(this, _i18n).getFixedT(
    null,
    internalRef.id
  );
  return {
    ready: true,
    t
  };
};
_registerDefaults = new WeakSet();
registerDefaults_fn = function(internalRef) {
  if (__privateGet$1(this, _registeredRefs).has(internalRef.id)) {
    return;
  }
  __privateGet$1(this, _registeredRefs).add(internalRef.id);
  const defaultMessages = internalRef.getDefaultMessages();
  __privateGet$1(this, _i18n).addResourceBundle(
    DEFAULT_LANGUAGE,
    internalRef.id,
    defaultMessages,
    true,
    // merge with existing translations
    false
    // do not overwrite translations
  );
  const defaultResource = internalRef.getDefaultResource();
  if (defaultResource) {
    __privateGet$1(this, _loader).addTranslationResource(defaultResource);
  }
};
let I18nextTranslationApi = _I18nextTranslationApi;

function createLocalBaseUrl(fullUrl) {
  const url = new URL(fullUrl);
  url.protocol = document.location.protocol;
  url.hostname = document.location.hostname;
  url.port = document.location.port;
  return url.toString().replace(/\/$/, "");
}
function overrideBaseUrlConfigs(inputConfigs) {
  const urlConfigReader = ConfigReader.fromConfigs(inputConfigs);
  const appBaseUrl = urlConfigReader.getOptionalString("app.baseUrl");
  const backendBaseUrl = urlConfigReader.getOptionalString("backend.baseUrl");
  let configs = inputConfigs;
  let newBackendBaseUrl = void 0;
  let newAppBaseUrl = void 0;
  if (appBaseUrl && backendBaseUrl) {
    const appOrigin = new URL(appBaseUrl).origin;
    const backendOrigin = new URL(backendBaseUrl).origin;
    if (appOrigin === backendOrigin) {
      const maybeNewBackendBaseUrl = createLocalBaseUrl(backendBaseUrl);
      if (backendBaseUrl !== maybeNewBackendBaseUrl) {
        newBackendBaseUrl = maybeNewBackendBaseUrl;
      }
    }
  }
  if (appBaseUrl) {
    const maybeNewAppBaseUrl = createLocalBaseUrl(appBaseUrl);
    if (appBaseUrl !== maybeNewAppBaseUrl) {
      newAppBaseUrl = maybeNewAppBaseUrl;
    }
  }
  if (newAppBaseUrl || newBackendBaseUrl) {
    configs = configs.concat({
      data: {
        app: newAppBaseUrl && {
          baseUrl: newAppBaseUrl
        },
        backend: newBackendBaseUrl && {
          baseUrl: newBackendBaseUrl
        }
      },
      context: "relative-resolver"
    });
  }
  return configs;
}

var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
var __accessCheck = (obj, member, msg) => {
  if (!member.has(obj))
    throw TypeError("Cannot " + msg);
};
var __privateGet = (obj, member, getter) => {
  __accessCheck(obj, member, "read from private field");
  return getter ? getter.call(obj) : member.get(obj);
};
var __privateAdd = (obj, member, value) => {
  if (member.has(obj))
    throw TypeError("Cannot add the same private member more than once");
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
};
var __privateSet = (obj, member, value, setter) => {
  __accessCheck(obj, member, "write to private field");
  setter ? setter.call(obj, value) : member.set(obj, value);
  return value;
};
var _getProviderCalled;
function useConfigLoader(configLoader, components, appThemeApi) {
  var _a;
  const hasConfig = Boolean(configLoader);
  const config = useAsync(configLoader || (() => Promise.resolve([])));
  let noConfigNode = void 0;
  if (hasConfig && config.loading) {
    const { Progress } = components;
    noConfigNode = /* @__PURE__ */ React.createElement(Progress, null);
  } else if (config.error) {
    const { BootErrorPage } = components;
    noConfigNode = /* @__PURE__ */ React.createElement(BootErrorPage, { step: "load-config", error: config.error });
  }
  const { ThemeProvider = AppThemeProvider } = components;
  if (noConfigNode) {
    return {
      node: /* @__PURE__ */ React.createElement(ApiProvider, { apis: ApiRegistry.with(appThemeApiRef, appThemeApi) }, /* @__PURE__ */ React.createElement(ThemeProvider, null, noConfigNode))
    };
  }
  const configReader = ConfigReader.fromConfigs(
    ((_a = config.value) == null ? void 0 : _a.length) ? overrideBaseUrlConfigs(config.value) : []
  );
  return { api: configReader };
}
class AppContextImpl {
  constructor(app) {
    this.app = app;
  }
  getPlugins() {
    return this.app.getPlugins();
  }
  getSystemIcon(key) {
    return this.app.getSystemIcon(key);
  }
  getSystemIcons() {
    return this.app.getSystemIcons();
  }
  getComponents() {
    return this.app.getComponents();
  }
}
class AppManager {
  constructor(options) {
    __publicField(this, "apiHolder");
    __publicField(this, "configApi");
    __publicField(this, "apis");
    __publicField(this, "icons");
    __publicField(this, "plugins");
    __publicField(this, "featureFlags");
    __publicField(this, "components");
    __publicField(this, "themes");
    __publicField(this, "configLoader");
    __publicField(this, "defaultApis");
    __publicField(this, "bindRoutes");
    __publicField(this, "appLanguageApi");
    __publicField(this, "translationResources");
    __publicField(this, "appIdentityProxy", new AppIdentityProxy());
    __publicField(this, "apiFactoryRegistry");
    __privateAdd(this, _getProviderCalled, false);
    var _a, _b, _c, _d, _e, _f, _g, _h, _i;
    this.apis = (_a = options.apis) != null ? _a : [];
    this.icons = options.icons;
    this.plugins = new Set((_b = options.plugins) != null ? _b : []);
    this.featureFlags = (_c = options.featureFlags) != null ? _c : [];
    this.components = options.components;
    this.themes = options.themes;
    this.configLoader = (_d = options.configLoader) != null ? _d : defaultConfigLoader;
    this.defaultApis = (_e = options.defaultApis) != null ? _e : [];
    this.bindRoutes = options.bindRoutes;
    this.apiFactoryRegistry = new ApiFactoryRegistry();
    this.appLanguageApi = AppLanguageSelector.createWithStorage({
      defaultLanguage: (_f = options.__experimentalTranslations) == null ? void 0 : _f.defaultLanguage,
      availableLanguages: (_g = options.__experimentalTranslations) == null ? void 0 : _g.availableLanguages
    });
    this.translationResources = (_i = (_h = options.__experimentalTranslations) == null ? void 0 : _h.resources) != null ? _i : [];
  }
  getPlugins() {
    return Array.from(this.plugins);
  }
  getSystemIcon(key) {
    return this.icons[key];
  }
  getSystemIcons() {
    return this.icons;
  }
  getComponents() {
    return this.components;
  }
  createRoot(element) {
    const AppProvider = this.getProvider();
    const AppRoot = () => {
      return /* @__PURE__ */ React.createElement(AppProvider, null, element);
    };
    return AppRoot;
  }
  getProvider() {
    if (__privateGet(this, _getProviderCalled)) {
      throw new Error(
        "app.getProvider() or app.createRoot() has already been called, and can only be called once"
      );
    }
    __privateSet(this, _getProviderCalled, true);
    const appContext = new AppContextImpl(this);
    let routesHaveBeenValidated = false;
    const Provider = ({ children }) => {
      const needsFeatureFlagRegistrationRef = useRef(true);
      const appThemeApi = useMemo(
        () => AppThemeSelector.createWithStorage(this.themes),
        []
      );
      const { routing, featureFlags, routeBindings } = useMemo(() => {
        const usesReactRouterBeta = isReactRouterBeta();
        if (usesReactRouterBeta) {
          console.warn(`
DEPRECATION WARNING: React Router Beta is deprecated and support for it will be removed in a future release.
                     Please migrate to use React Router v6 stable.
                     See https://backstage.io/docs/tutorials/react-router-stable-migration
`);
        }
        const result = traverseElementTree({
          root: children,
          discoverers: [childDiscoverer, routeElementDiscoverer],
          collectors: {
            routing: usesReactRouterBeta ? routingV1Collector : routingV2Collector,
            collectedPlugins: pluginCollector,
            featureFlags: featureFlagCollector
          }
        });
        result.collectedPlugins.forEach((plugin) => this.plugins.add(plugin));
        this.verifyPlugins(this.plugins);
        this.getApiHolder();
        return {
          ...result,
          routeBindings: resolveRouteBindings(this.bindRoutes)
        };
      }, [children]);
      if (!routesHaveBeenValidated) {
        routesHaveBeenValidated = true;
        validateRouteParameters(routing.paths, routing.parents);
        validateRouteBindings(
          routeBindings,
          this.plugins
        );
      }
      const loadedConfig = useConfigLoader(
        this.configLoader,
        this.components,
        appThemeApi
      );
      const hasConfigApi = "api" in loadedConfig;
      if (hasConfigApi) {
        const { api } = loadedConfig;
        this.configApi = api;
      }
      if ("node" in loadedConfig) {
        return loadedConfig.node;
      }
      if (hasConfigApi && needsFeatureFlagRegistrationRef.current) {
        needsFeatureFlagRegistrationRef.current = false;
        const featureFlagsApi = this.getApiHolder().get(featureFlagsApiRef);
        if (featureFlagsApi) {
          for (const flag of this.featureFlags) {
            featureFlagsApi.registerFlag({
              ...flag,
              pluginId: ""
            });
          }
          for (const plugin of this.plugins.values()) {
            if ("getFeatureFlags" in plugin) {
              for (const flag of plugin.getFeatureFlags()) {
                featureFlagsApi.registerFlag({
                  name: flag.name,
                  pluginId: plugin.getId()
                });
              }
            } else {
              for (const output of plugin.output()) {
                if (output.type === "feature-flag") {
                  featureFlagsApi.registerFlag({
                    name: output.name,
                    pluginId: plugin.getId()
                  });
                }
              }
            }
          }
          const registeredFlags = featureFlagsApi.getRegisteredFlags();
          const flagNames = new Set(registeredFlags.map((f) => f.name));
          for (const name of featureFlags) {
            if (!flagNames.has(name)) {
              featureFlagsApi.registerFlag({ name, pluginId: "" });
            }
          }
        }
      }
      const { ThemeProvider = AppThemeProvider, Progress } = this.components;
      return /* @__PURE__ */ React.createElement(ApiProvider, { apis: this.getApiHolder() }, /* @__PURE__ */ React.createElement(AppContextProvider, { appContext }, /* @__PURE__ */ React.createElement(ThemeProvider, null, /* @__PURE__ */ React.createElement(
        RoutingProvider,
        {
          routePaths: routing.paths,
          routeParents: routing.parents,
          routeObjects: routing.objects,
          routeBindings,
          basePath: getBasePath(loadedConfig.api)
        },
        /* @__PURE__ */ React.createElement(
          InternalAppContext.Provider,
          {
            value: {
              routeObjects: routing.objects,
              appIdentityProxy: this.appIdentityProxy
            }
          },
          /* @__PURE__ */ React.createElement(Suspense, { fallback: /* @__PURE__ */ React.createElement(Progress, null) }, children)
        )
      ))));
    };
    return Provider;
  }
  getRouter() {
    return AppRouter;
  }
  getApiHolder() {
    if (this.apiHolder) {
      for (const plugin of this.plugins) {
        for (const factory of plugin.getApis()) {
          if (!this.apiFactoryRegistry.get(factory.api)) {
            this.apiFactoryRegistry.register("default", factory);
          }
        }
      }
      ApiResolver.validateFactories(
        this.apiFactoryRegistry,
        this.apiFactoryRegistry.getAllApis()
      );
      return this.apiHolder;
    }
    this.apiFactoryRegistry.register("static", {
      api: appThemeApiRef,
      deps: {},
      factory: () => AppThemeSelector.createWithStorage(this.themes)
    });
    this.apiFactoryRegistry.register("static", {
      api: configApiRef,
      deps: {},
      factory: () => {
        if (!this.configApi) {
          throw new Error(
            "Tried to access config API before config was loaded"
          );
        }
        return this.configApi;
      }
    });
    this.apiFactoryRegistry.register("static", {
      api: identityApiRef,
      deps: {},
      factory: () => this.appIdentityProxy
    });
    this.apiFactoryRegistry.register("static", {
      api: appLanguageApiRef,
      deps: {},
      factory: () => this.appLanguageApi
    });
    this.apiFactoryRegistry.register("default", {
      api: translationApiRef,
      deps: { languageApi: appLanguageApiRef },
      factory: ({ languageApi }) => I18nextTranslationApi.create({
        languageApi,
        resources: this.translationResources
      })
    });
    this.apiFactoryRegistry.register("default", {
      api: featureFlagsApiRef,
      deps: {},
      factory: () => new LocalStorageFeatureFlags()
    });
    for (const factory of this.defaultApis) {
      this.apiFactoryRegistry.register("default", factory);
    }
    for (const plugin of this.plugins) {
      for (const factory of plugin.getApis()) {
        if (!this.apiFactoryRegistry.register("default", factory)) {
          throw new Error(
            `Plugin ${plugin.getId()} tried to register duplicate or forbidden API factory for ${factory.api}`
          );
        }
      }
    }
    for (const factory of this.apis) {
      if (!this.apiFactoryRegistry.register("app", factory)) {
        throw new Error(
          `Duplicate or forbidden API factory for ${factory.api} in app`
        );
      }
    }
    ApiResolver.validateFactories(
      this.apiFactoryRegistry,
      this.apiFactoryRegistry.getAllApis()
    );
    this.apiHolder = new ApiResolver(this.apiFactoryRegistry);
    return this.apiHolder;
  }
  verifyPlugins(plugins) {
    const pluginIds = /* @__PURE__ */ new Set();
    for (const plugin of plugins) {
      const id = plugin.getId();
      if (pluginIds.has(id)) {
        throw new Error(`Duplicate plugin found '${id}'`);
      }
      pluginIds.add(id);
    }
  }
}
_getProviderCalled = new WeakMap();

function createSpecializedApp(options) {
  return new AppManager(options);
}

let warned = false;
const FlatRoutes = (props) => {
  const app = useApp();
  const { NotFoundErrorPage } = app.getComponents();
  const isBeta = useMemo(() => isReactRouterBeta(), []);
  const routes = useElementFilter(
    props.children,
    (elements) => elements.getElements().flatMap((child) => {
      var _a;
      let path = child.props.path;
      if (path === "") {
        return [];
      }
      path = (_a = path == null ? void 0 : path.replace(/\/\*$/, "")) != null ? _a : "/";
      let element = isBeta ? child : child.props.element;
      if (!isBeta && !element) {
        element = child;
        if (!warned && process.env.NODE_ENV !== "test") {
          console.warn(
            "DEPRECATION WARNING: All elements within <FlatRoutes> must be of type <Route> with an element prop. Existing usages of <Navigate key=[path] to=[to] /> should be replaced with <Route path=[path] element={<Navigate to=[to] />} />."
          );
          warned = true;
        }
      }
      return [
        {
          // Each route matches any sub route, except for the explicit root path
          path,
          element,
          children: child.props.children ? [
            // These are the children of each route, which we all add in under a catch-all
            // subroute in order to make them available to `useOutlet`
            {
              path: path === "/" ? "/" : "*",
              // The root path must require an exact match
              element: child.props.children
            }
          ] : void 0
        }
      ];
    }).sort((a, b) => b.path.localeCompare(a.path)).map((obj) => ({ ...obj, path: obj.path === "/" ? "/" : `${obj.path}/*` }))
  );
  const withNotFound = [
    ...routes,
    {
      path: "*",
      element: /* @__PURE__ */ React.createElement(NotFoundErrorPage, null)
    }
  ];
  return useRoutes(withNotFound);
};
attachComponentData(FlatRoutes, "core.type", "FlatRoutes");

export { AlertApiForwarder, ApiFactoryRegistry, ApiProvider, ApiResolver, AppRouter, AppThemeSelector, AtlassianAuth, BitbucketAuth, BitbucketServerAuth, ErrorAlerter, ErrorApiForwarder, FeatureFlagged, FetchMiddlewares, FlatRoutes, FrontendHostDiscovery, GithubAuth, GitlabAuth, GoogleAuth, LocalStorageFeatureFlags, MicrosoftAuth, MultipleAnalyticsApi, NoOpAnalyticsApi, OAuth2, OAuthRequestManager, OktaAuth, OneLoginAuth, SamlAuth, UnhandledErrorForwarder, UrlPatternDiscovery, WebStorage, createFetchApi, createSpecializedApp, defaultConfigLoader };
//# sourceMappingURL=index.esm.js.map
