@backstage/core-app-api 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @backstage/core-app-api
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - c11ce4f552: Deprecated `Auth0Auth`, pointing to using `OAuth2` directly instead.
8
+ - 9d6503e86c: Switched out usage of deprecated `OAuthRequestApi` types from `@backstage/core-plugin-api`.
9
+ - Updated dependencies
10
+ - @backstage/core-plugin-api@0.3.1
11
+ - @backstage/core-components@0.8.1
12
+
3
13
  ## 0.2.0
4
14
 
5
15
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ReactNode, PropsWithChildren, ComponentType } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { ApiHolder, ApiRef, ApiFactory, AnyApiRef, ProfileInfo, BackstageIdentity, SessionState, OAuthRequestApi, DiscoveryApi, AuthProvider, OAuthApi, SessionApi, AuthRequestOptions, gitlabAuthApiRef, googleAuthApiRef, OpenIdConnectApi, ProfileInfoApi, BackstageIdentityApi, oktaAuthApiRef, auth0AuthApiRef, microsoftAuthApiRef, oneloginAuthApiRef, bitbucketAuthApiRef, atlassianAuthApiRef, AlertApi, AlertMessage, AnalyticsApi, AnalyticsEvent, AppThemeApi, AppTheme, ErrorApi, ErrorApiError, ErrorApiErrorContext, FeatureFlagsApi, FeatureFlag, FeatureFlagsSaveOptions, AuthRequesterOptions, AuthRequester, PendingAuthRequest, StorageApi, StorageValueChange, IdentityApi, BackstagePlugin, IconComponent, ExternalRouteRef, PluginOutput, AnyApiFactory, RouteRef, SubRouteRef } from '@backstage/core-plugin-api';
3
+ import { ApiHolder, ApiRef, ApiFactory, AnyApiRef, ProfileInfo, BackstageIdentity, SessionState, OAuthRequestApi, DiscoveryApi, AuthProviderInfo, OAuthApi, SessionApi, AuthRequestOptions, gitlabAuthApiRef, googleAuthApiRef, OpenIdConnectApi, ProfileInfoApi, BackstageIdentityApi, oktaAuthApiRef, auth0AuthApiRef, microsoftAuthApiRef, oneloginAuthApiRef, bitbucketAuthApiRef, atlassianAuthApiRef, AlertApi, AlertMessage, AnalyticsApi, AnalyticsEvent, AppThemeApi, AppTheme, ErrorApi, ErrorApiError, ErrorApiErrorContext, FeatureFlagsApi, FeatureFlag, FeatureFlagsSaveOptions, OAuthRequesterOptions, OAuthRequester, PendingOAuthRequest, StorageApi, StorageValueChange, IdentityApi, BackstagePlugin, IconComponent, ExternalRouteRef, PluginOutput, AnyApiFactory, RouteRef, SubRouteRef } from '@backstage/core-plugin-api';
4
4
  import * as _backstage_types from '@backstage/types';
5
5
  import { Observable } from '@backstage/types';
6
6
  import { AppConfig } from '@backstage/config';
@@ -177,9 +177,7 @@ declare type OAuthApiCreateOptions = AuthApiCreateOptions & {
177
177
  declare type AuthApiCreateOptions = {
178
178
  discoveryApi: DiscoveryApi;
179
179
  environment?: string;
180
- provider?: AuthProvider & {
181
- id: string;
182
- };
180
+ provider?: AuthProviderInfo;
183
181
  };
184
182
 
185
183
  /**
@@ -313,6 +311,23 @@ declare class SamlAuth implements ProfileInfoApi, BackstageIdentityApi, SessionA
313
311
  * Implements the OAuth flow to Auth0 products.
314
312
  *
315
313
  * @public
314
+ * @deprecated Use {@link OAuth2} instead
315
+ *
316
+ * @example
317
+ *
318
+ * ```ts
319
+ * OAuth2.create({
320
+ * discoveryApi,
321
+ * oauthRequestApi,
322
+ * provider: {
323
+ * id: 'auth0',
324
+ * title: 'Auth0',
325
+ * icon: () => null,
326
+ * },
327
+ * defaultScopes: ['openid', 'email', 'profile'],
328
+ * environment: configApi.getOptionalString('auth.environment'),
329
+ * })
330
+ * ```
316
331
  */
317
332
  declare class Auth0Auth {
318
333
  static create(options: OAuthApiCreateOptions): typeof auth0AuthApiRef.T;
@@ -335,9 +350,7 @@ declare type OneLoginAuthCreateOptions = {
335
350
  discoveryApi: DiscoveryApi;
336
351
  oauthRequestApi: OAuthRequestApi;
337
352
  environment?: string;
338
- provider?: AuthProvider & {
339
- id: string;
340
- };
353
+ provider?: AuthProviderInfo;
341
354
  };
342
355
  /**
343
356
  * Implements a OneLogin OAuth flow.
@@ -511,9 +524,9 @@ declare class OAuthRequestManager implements OAuthRequestApi {
511
524
  private readonly subject;
512
525
  private currentRequests;
513
526
  private handlerCount;
514
- createAuthRequester<T>(options: AuthRequesterOptions<T>): AuthRequester<T>;
527
+ createAuthRequester<T>(options: OAuthRequesterOptions<T>): OAuthRequester<T>;
515
528
  private makeAuthRequest;
516
- authRequest$(): Observable<PendingAuthRequest[]>;
529
+ authRequest$(): Observable<PendingOAuthRequest[]>;
517
530
  }
518
531
 
519
532
  /**
package/dist/index.esm.js CHANGED
@@ -1536,6 +1536,9 @@ class OAuthRequestManager {
1536
1536
  this.handlerCount = 0;
1537
1537
  }
1538
1538
  createAuthRequester(options) {
1539
+ if (!options.provider.id) {
1540
+ console.warn("DEPRECATION WARNING: Not passing a provider id to createAuthRequester is deprecated, it will be required in the future");
1541
+ }
1539
1542
  const handler = new OAuthPendingRequests();
1540
1543
  const index = this.handlerCount;
1541
1544
  this.handlerCount++;
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/apis/system/ApiAggregator.ts","../src/apis/system/ApiProvider.tsx","../src/apis/system/ApiRegistry.ts","../src/apis/system/ApiResolver.ts","../src/apis/system/ApiFactoryRegistry.ts","../src/lib/loginPopup.ts","../src/lib/AuthConnector/DefaultAuthConnector.ts","../src/lib/AuthConnector/DirectAuthConnector.ts","../src/lib/AuthSessionManager/common.ts","../src/lib/subjects.ts","../src/lib/AuthSessionManager/SessionStateTracker.ts","../src/lib/AuthSessionManager/RefreshingAuthSessionManager.ts","../src/lib/AuthSessionManager/StaticAuthSessionManager.ts","../src/lib/AuthSessionManager/AuthSessionStore.ts","../src/lib/AuthSessionManager/OptionalRefreshSessionManagerMux.ts","../src/apis/implementations/auth/github/GithubAuth.ts","../src/apis/implementations/auth/oauth2/OAuth2.ts","../src/apis/implementations/auth/gitlab/GitlabAuth.ts","../src/apis/implementations/auth/google/GoogleAuth.ts","../src/apis/implementations/auth/okta/OktaAuth.ts","../src/apis/implementations/auth/saml/SamlAuth.ts","../src/apis/implementations/auth/auth0/Auth0Auth.ts","../src/apis/implementations/auth/microsoft/MicrosoftAuth.ts","../src/apis/implementations/auth/onelogin/OneLoginAuth.ts","../src/apis/implementations/auth/bitbucket/BitbucketAuth.ts","../src/apis/implementations/auth/atlassian/AtlassianAuth.ts","../src/apis/implementations/AlertApi/AlertApiForwarder.ts","../src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts","../src/apis/implementations/AppThemeApi/AppThemeSelector.ts","../src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.ts","../src/apis/implementations/ErrorApi/ErrorAlerter.ts","../src/apis/implementations/ErrorApi/ErrorApiForwarder.ts","../src/apis/implementations/ErrorApi/UnhandledErrorForwarder.ts","../src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.tsx","../src/apis/implementations/OAuthRequestApi/OAuthPendingRequests.ts","../src/apis/implementations/OAuthRequestApi/OAuthRequestManager.ts","../src/apis/implementations/StorageApi/WebStorage.ts","../src/app/createApp.tsx","../src/extensions/traversal.ts","../src/plugins/collectors.ts","../src/routing/FeatureFlagged.tsx","../src/routing/collectors.tsx","../src/routing/types.ts","../src/routing/RouteResolver.ts","../src/routing/RoutingProvider.tsx","../src/routing/RouteTracker.tsx","../src/routing/validation.ts","../src/app/AppContext.tsx","../src/apis/implementations/IdentityApi/AppIdentityProxy.ts","../src/app/AppThemeProvider.tsx","../src/app/defaultConfigLoader.ts","../src/app/AppManager.tsx","../src/app/createSpecializedApp.tsx","../src/routing/FlatRoutes.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ApiRef, ApiHolder } from '@backstage/core-plugin-api';\n\n/**\n * An ApiHolder that queries multiple other holders from for\n * an Api implementation, returning the first one encountered..\n */\nexport class ApiAggregator implements ApiHolder {\n private readonly holders: ApiHolder[];\n\n constructor(...holders: ApiHolder[]) {\n this.holders = holders;\n }\n\n get<T>(apiRef: ApiRef<T>): T | undefined {\n for (const holder of this.holders) {\n const api = holder.get(apiRef);\n if (api) {\n return api;\n }\n }\n return undefined;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useContext, ReactNode, PropsWithChildren } from 'react';\nimport PropTypes from 'prop-types';\nimport { ApiHolder } from '@backstage/core-plugin-api';\nimport { ApiAggregator } from './ApiAggregator';\nimport {\n createVersionedValueMap,\n createVersionedContext,\n} from '@backstage/version-bridge';\n\n/**\n * Prop types for the ApiProvider component.\n * @public\n */\nexport type ApiProviderProps = {\n apis: ApiHolder;\n children: ReactNode;\n};\n\nconst ApiContext = createVersionedContext<{ 1: ApiHolder }>('api-context');\n\n/**\n * Provides an {@link @backstage/core-plugin-api#ApiHolder} for consumption in\n * the React tree.\n *\n * @public\n */\nexport const ApiProvider = (props: PropsWithChildren<ApiProviderProps>) => {\n const { apis, children } = props;\n const parentHolder = useContext(ApiContext)?.atVersion(1);\n const holder = parentHolder ? new ApiAggregator(apis, parentHolder) : apis;\n\n return (\n <ApiContext.Provider\n value={createVersionedValueMap({ 1: holder })}\n children={children}\n />\n );\n};\n\nApiProvider.propTypes = {\n apis: PropTypes.shape({ get: PropTypes.func.isRequired }).isRequired,\n children: PropTypes.node,\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ApiRef, ApiHolder } from '@backstage/core-plugin-api';\n\ntype ApiImpl<T = unknown> = readonly [ApiRef<T>, T];\n\nclass ApiRegistryBuilder {\n private apis: [string, unknown][] = [];\n\n add<T, I extends T>(api: ApiRef<T>, impl: I): I {\n this.apis.push([api.id, impl]);\n return impl;\n }\n\n build(): ApiRegistry {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n return new ApiRegistry(new Map(this.apis));\n }\n}\n\n/**\n * A registry for utility APIs.\n *\n * @public\n * @deprecated Will be removed, use {@link @backstage/test-utils#TestApiProvider} or {@link @backstage/test-utils#TestApiRegistry} instead.\n */\nexport class ApiRegistry implements ApiHolder {\n static builder() {\n return new ApiRegistryBuilder();\n }\n\n /**\n * Creates a new ApiRegistry with a list of API implementations.\n *\n * @param apis - A list of pairs mapping an ApiRef to its respective implementation\n */\n static from(apis: ApiImpl[]) {\n return new ApiRegistry(new Map(apis.map(([api, impl]) => [api.id, impl])));\n }\n\n /**\n * Creates a new ApiRegistry with a single API implementation.\n *\n * @param api - ApiRef for the API to add\n * @param impl - Implementation of the API to add\n */\n static with<T>(api: ApiRef<T>, impl: T): ApiRegistry {\n return new ApiRegistry(new Map([[api.id, impl]]));\n }\n\n constructor(private readonly apis: Map<string, unknown>) {}\n\n /**\n * Returns a new ApiRegistry with the provided API added to the existing ones.\n *\n * @param api - ApiRef for the API to add\n * @param impl - Implementation of the API to add\n */\n with<T>(api: ApiRef<T>, impl: T): ApiRegistry {\n return new ApiRegistry(new Map([...this.apis, [api.id, impl]]));\n }\n\n get<T>(api: ApiRef<T>): T | undefined {\n return this.apis.get(api.id) as T | undefined;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ApiRef,\n ApiHolder,\n AnyApiRef,\n TypesToApiRefs,\n} from '@backstage/core-plugin-api';\nimport { ApiFactoryHolder } from './types';\n\n/**\n * Handles the actual on-demand instantiation and memoization of APIs out of\n * an {@link ApiFactoryHolder}.\n *\n * @public\n */\nexport class ApiResolver implements ApiHolder {\n /**\n * Validate factories by making sure that each of the apis can be created\n * without hitting any circular dependencies.\n */\n static validateFactories(\n factories: ApiFactoryHolder,\n apis: Iterable<AnyApiRef>,\n ) {\n for (const api of apis) {\n const heap = [api];\n const allDeps = new Set<AnyApiRef>();\n\n while (heap.length) {\n const apiRef = heap.shift()!;\n const factory = factories.get(apiRef);\n if (!factory) {\n continue;\n }\n\n for (const dep of Object.values(factory.deps)) {\n if (dep.id === api.id) {\n throw new Error(`Circular dependency of api factory for ${api}`);\n }\n if (!allDeps.has(dep)) {\n allDeps.add(dep);\n heap.push(dep);\n }\n }\n }\n }\n }\n\n private readonly apis = new Map<string, unknown>();\n\n constructor(private readonly factories: ApiFactoryHolder) {}\n\n get<T>(ref: ApiRef<T>): T | undefined {\n return this.load(ref);\n }\n\n private load<T>(ref: ApiRef<T>, loading: AnyApiRef[] = []): T | undefined {\n const impl = this.apis.get(ref.id);\n if (impl) {\n return impl as T;\n }\n\n const factory = this.factories.get(ref);\n if (!factory) {\n return undefined;\n }\n\n if (loading.includes(factory.api)) {\n throw new Error(`Circular dependency of api factory for ${factory.api}`);\n }\n\n const deps = this.loadDeps(ref, factory.deps, [...loading, factory.api]);\n const api = factory.factory(deps);\n this.apis.set(ref.id, api);\n return api as T;\n }\n\n private loadDeps<T>(\n dependent: ApiRef<unknown>,\n apis: TypesToApiRefs<T>,\n loading: AnyApiRef[],\n ): T {\n const impls = {} as T;\n\n for (const key in apis) {\n if (apis.hasOwnProperty(key)) {\n const ref = apis[key];\n\n const api = this.load(ref, loading);\n if (!api) {\n throw new Error(\n `No API factory available for dependency ${ref} of dependent ${dependent}`,\n );\n }\n impls[key] = api;\n }\n }\n\n return impls;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ApiFactoryHolder } from './types';\nimport {\n ApiRef,\n ApiFactory,\n AnyApiRef,\n AnyApiFactory,\n} from '@backstage/core-plugin-api';\n\n/**\n * Scope type when registering API factories.\n * @public\n */\nexport type ApiFactoryScope =\n | 'default' // Default factories registered by core and plugins\n | 'app' // Factories registered in the app, overriding default ones\n | 'static'; // APIs that can't be overridden, e.g. config\n\nenum ScopePriority {\n default = 10,\n app = 50,\n static = 100,\n}\n\ntype FactoryTuple = {\n priority: number;\n factory: AnyApiFactory;\n};\n\n/**\n * ApiFactoryRegistry is an ApiFactoryHolder implementation that enables\n * registration of API Factories with different scope.\n *\n * Each scope has an assigned priority, where factories registered with\n * higher priority scopes override ones with lower priority.\n *\n * @public\n */\nexport class ApiFactoryRegistry implements ApiFactoryHolder {\n private readonly factories = new Map<string, FactoryTuple>();\n\n /**\n * Register a new API factory. Returns true if the factory was added\n * to the registry.\n *\n * A factory will not be added to the registry if there is already\n * an existing factory with the same or higher priority.\n */\n register<Api, Impl extends Api, Deps extends { [name in string]: unknown }>(\n scope: ApiFactoryScope,\n factory: ApiFactory<Api, Impl, Deps>,\n ) {\n const priority = ScopePriority[scope];\n const existing = this.factories.get(factory.api.id);\n if (existing && existing.priority >= priority) {\n return false;\n }\n\n this.factories.set(factory.api.id, { priority, factory });\n return true;\n }\n\n get<T>(\n api: ApiRef<T>,\n ): ApiFactory<T, T, { [x: string]: unknown }> | undefined {\n const tuple = this.factories.get(api.id);\n if (!tuple) {\n return undefined;\n }\n return tuple.factory as ApiFactory<T, T, { [x: string]: unknown }>;\n }\n\n getAllApis(): Set<AnyApiRef> {\n const refs = new Set<AnyApiRef>();\n for (const { factory } of this.factories.values()) {\n refs.add(factory.api);\n }\n return refs;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Options used to open a login popup.\n */\nexport type LoginPopupOptions = {\n /**\n * The URL that the auth popup should point to\n */\n url: string;\n\n /**\n * The name of the popup, as in second argument to window.open\n */\n name: string;\n\n /**\n * The origin of the final popup page that will post a message to this window.\n */\n origin: string;\n\n /**\n * The width of the popup in pixels, defaults to 500\n */\n width?: number;\n\n /**\n * The height of the popup in pixels, defaults to 700\n */\n height?: number;\n};\n\ntype AuthResult =\n | {\n type: 'authorization_response';\n response: unknown;\n }\n | {\n type: 'authorization_response';\n error: {\n name: string;\n message: string;\n };\n };\n\n/**\n * Show a popup pointing to a URL that starts an auth flow. Implementing the receiving\n * end of the postMessage mechanism outlined in https://tools.ietf.org/html/draft-sakimura-oauth-wmrm-00\n *\n * The redirect handler of the flow should use postMessage to communicate back\n * to the app window. The message posted to the app must match the AuthResult type.\n *\n * The returned promise resolves to the response of the message that was posted from the auth popup.\n */\nexport function showLoginPopup(options: LoginPopupOptions): Promise<any> {\n return new Promise((resolve, reject) => {\n const width = options.width || 500;\n const height = options.height || 700;\n const left = window.screen.width / 2 - width / 2;\n const top = window.screen.height / 2 - height / 2;\n\n const popup = window.open(\n options.url,\n options.name,\n `menubar=no,location=no,resizable=no,scrollbars=no,status=no,width=${width},height=${height},top=${top},left=${left}`,\n );\n\n let targetOrigin = '';\n\n if (!popup || typeof popup.closed === 'undefined' || popup.closed) {\n const error = new Error('Failed to open auth popup.');\n error.name = 'PopupRejectedError';\n reject(error);\n return;\n }\n\n const messageListener = (event: MessageEvent) => {\n if (event.source !== popup) {\n return;\n }\n if (event.origin !== options.origin) {\n return;\n }\n const { data } = event;\n\n if (data.type === 'config_info') {\n targetOrigin = data.targetOrigin;\n return;\n }\n\n if (data.type !== 'authorization_response') {\n return;\n }\n const authResult = data as AuthResult;\n\n if ('error' in authResult) {\n const error = new Error(authResult.error.message);\n error.name = authResult.error.name;\n // TODO: proper error type\n // error.extra = authResult.error.extra;\n reject(error);\n } else {\n resolve(authResult.response);\n }\n done();\n };\n\n const intervalId = setInterval(() => {\n if (popup.closed) {\n const errMessage = `Login failed, ${\n targetOrigin && targetOrigin !== window.location.origin\n ? `Incorrect app origin, expected ${targetOrigin}`\n : 'popup was closed'\n }`;\n const error = new Error(errMessage);\n error.name = 'PopupClosedError';\n reject(error);\n done();\n }\n }, 100);\n\n function done() {\n window.removeEventListener('message', messageListener);\n clearInterval(intervalId);\n }\n\n window.addEventListener('message', messageListener);\n });\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AuthRequester,\n OAuthRequestApi,\n AuthProvider,\n DiscoveryApi,\n} from '@backstage/core-plugin-api';\nimport { showLoginPopup } from '../loginPopup';\nimport { AuthConnector, CreateSessionOptions } from './types';\n\ntype Options<AuthSession> = {\n /**\n * DiscoveryApi instance used to locate the auth backend endpoint.\n */\n discoveryApi: DiscoveryApi;\n /**\n * Environment hint passed on to auth backend, for example 'production' or 'development'\n */\n environment: string;\n /**\n * Information about the auth provider to be shown to the user.\n * The ID Must match the backend auth plugin configuration, for example 'google'.\n */\n provider: AuthProvider & { id: string };\n /**\n * API used to instantiate an auth requester.\n */\n oauthRequestApi: OAuthRequestApi;\n /**\n * Function used to join together a set of scopes, defaults to joining with a space character.\n */\n joinScopes?: (scopes: Set<string>) => string;\n /**\n * Function used to transform an auth response into the session type.\n */\n sessionTransform?(response: any): AuthSession | Promise<AuthSession>;\n};\n\nfunction defaultJoinScopes(scopes: Set<string>) {\n return [...scopes].join(' ');\n}\n\n/**\n * DefaultAuthConnector is the default auth connector in Backstage. It talks to the\n * backend auth plugin through the standardized API, and requests user permission\n * via the OAuthRequestApi.\n */\nexport class DefaultAuthConnector<AuthSession>\n implements AuthConnector<AuthSession>\n{\n private readonly discoveryApi: DiscoveryApi;\n private readonly environment: string;\n private readonly provider: AuthProvider & { id: string };\n private readonly joinScopesFunc: (scopes: Set<string>) => string;\n private readonly authRequester: AuthRequester<AuthSession>;\n private readonly sessionTransform: (response: any) => Promise<AuthSession>;\n\n constructor(options: Options<AuthSession>) {\n const {\n discoveryApi,\n environment,\n provider,\n joinScopes = defaultJoinScopes,\n oauthRequestApi,\n sessionTransform = id => id,\n } = options;\n\n this.authRequester = oauthRequestApi.createAuthRequester({\n provider,\n onAuthRequest: scopes => this.showPopup(scopes),\n });\n\n this.discoveryApi = discoveryApi;\n this.environment = environment;\n this.provider = provider;\n this.joinScopesFunc = joinScopes;\n this.sessionTransform = sessionTransform;\n }\n\n async createSession(options: CreateSessionOptions): Promise<AuthSession> {\n if (options.instantPopup) {\n return this.showPopup(options.scopes);\n }\n return this.authRequester(options.scopes);\n }\n\n async refreshSession(): Promise<any> {\n const res = await fetch(\n await this.buildUrl('/refresh', { optional: true }),\n {\n headers: {\n 'x-requested-with': 'XMLHttpRequest',\n },\n credentials: 'include',\n },\n ).catch(error => {\n throw new Error(`Auth refresh request failed, ${error}`);\n });\n\n if (!res.ok) {\n const error: any = new Error(\n `Auth refresh request failed, ${res.statusText}`,\n );\n error.status = res.status;\n throw error;\n }\n\n const authInfo = await res.json();\n\n if (authInfo.error) {\n const error = new Error(authInfo.error.message);\n if (authInfo.error.name) {\n error.name = authInfo.error.name;\n }\n throw error;\n }\n return await this.sessionTransform(authInfo);\n }\n\n async removeSession(): Promise<void> {\n const res = await fetch(await this.buildUrl('/logout'), {\n method: 'POST',\n headers: {\n 'x-requested-with': 'XMLHttpRequest',\n },\n credentials: 'include',\n }).catch(error => {\n throw new Error(`Logout request failed, ${error}`);\n });\n\n if (!res.ok) {\n const error: any = new Error(`Logout request failed, ${res.statusText}`);\n error.status = res.status;\n throw error;\n }\n }\n\n private async showPopup(scopes: Set<string>): Promise<AuthSession> {\n const scope = this.joinScopesFunc(scopes);\n const popupUrl = await this.buildUrl('/start', {\n scope,\n origin: location.origin,\n });\n\n const payload = await showLoginPopup({\n url: popupUrl,\n name: `${this.provider.title} Login`,\n origin: new URL(popupUrl).origin,\n width: 450,\n height: 730,\n });\n\n return await this.sessionTransform(payload);\n }\n\n private async buildUrl(\n path: string,\n query?: { [key: string]: string | boolean | undefined },\n ): Promise<string> {\n const baseUrl = await this.discoveryApi.getBaseUrl('auth');\n const queryString = this.buildQueryString({\n ...query,\n env: this.environment,\n });\n\n return `${baseUrl}/${this.provider.id}${path}${queryString}`;\n }\n\n private buildQueryString(query?: {\n [key: string]: string | boolean | undefined;\n }): string {\n if (!query) {\n return '';\n }\n\n const queryString = Object.entries<string | boolean | undefined>(query)\n .map(([key, value]) => {\n if (typeof value === 'string') {\n return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;\n } else if (value) {\n return encodeURIComponent(key);\n }\n return undefined;\n })\n .filter(Boolean)\n .join('&');\n\n if (!queryString) {\n return '';\n }\n return `?${queryString}`;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { AuthProvider, DiscoveryApi } from '@backstage/core-plugin-api';\nimport { showLoginPopup } from '../loginPopup';\n\ntype Options = {\n discoveryApi: DiscoveryApi;\n environment?: string;\n provider: AuthProvider & { id: string };\n};\nexport class DirectAuthConnector<DirectAuthResponse> {\n private readonly discoveryApi: DiscoveryApi;\n private readonly environment: string | undefined;\n private readonly provider: AuthProvider & { id: string };\n\n constructor(options: Options) {\n const { discoveryApi, environment, provider } = options;\n\n this.discoveryApi = discoveryApi;\n this.environment = environment;\n this.provider = provider;\n }\n\n async createSession(): Promise<DirectAuthResponse> {\n const popupUrl = await this.buildUrl('/start');\n const payload = await showLoginPopup({\n url: popupUrl,\n name: `${this.provider.title} Login`,\n origin: new URL(popupUrl).origin,\n width: 450,\n height: 730,\n });\n\n return {\n ...payload,\n id: payload.profile.email,\n };\n }\n\n async refreshSession(): Promise<any> {}\n\n async removeSession(): Promise<void> {\n const res = await fetch(await this.buildUrl('/logout'), {\n method: 'POST',\n headers: {\n 'x-requested-with': 'XMLHttpRequest',\n },\n credentials: 'include',\n }).catch(error => {\n throw new Error(`Logout request failed, ${error}`);\n });\n\n if (!res.ok) {\n const error: any = new Error(`Logout request failed, ${res.statusText}`);\n error.status = res.status;\n throw error;\n }\n }\n\n private async buildUrl(path: string): Promise<string> {\n const baseUrl = await this.discoveryApi.getBaseUrl('auth');\n return `${baseUrl}/${this.provider.id}${path}?env=${this.environment}`;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { SessionScopesFunc } from './types';\n\nexport function hasScopes(\n searched: Set<string>,\n searchFor: Set<string>,\n): boolean {\n for (const scope of searchFor) {\n if (!searched.has(scope)) {\n return false;\n }\n }\n return true;\n}\n\ntype ScopeHelperOptions<T> = {\n sessionScopes: SessionScopesFunc<T> | undefined;\n defaultScopes?: Set<string>;\n};\n\nexport class SessionScopeHelper<T> {\n constructor(private readonly options: ScopeHelperOptions<T>) {}\n\n sessionExistsAndHasScope(\n session: T | undefined,\n scopes?: Set<string>,\n ): boolean {\n if (!session) {\n return false;\n }\n if (!scopes) {\n return true;\n }\n if (this.options.sessionScopes === undefined) {\n return true;\n }\n const sessionScopes = this.options.sessionScopes(session);\n return hasScopes(sessionScopes, scopes);\n }\n\n getExtendedScope(session: T | undefined, scopes?: Set<string>) {\n const newScope = new Set(this.options.defaultScopes);\n if (session && this.options.sessionScopes !== undefined) {\n const sessionScopes = this.options.sessionScopes(session);\n for (const scope of sessionScopes) {\n newScope.add(scope);\n }\n }\n if (scopes) {\n for (const scope of scopes) {\n newScope.add(scope);\n }\n }\n return newScope;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Observable } from '@backstage/types';\nimport ObservableImpl from 'zen-observable';\n\n// TODO(Rugvip): These are stopgap and probably incomplete implementations of subjects.\n// If we add a more complete Observables library they should be replaced.\n\n/**\n * A basic implementation of ReactiveX publish subjects.\n *\n * A subject is a convenient way to create an observable when you want\n * to fan out a single value to all subscribers.\n *\n * See http://reactivex.io/documentation/subject.html\n */\nexport class PublishSubject<T>\n implements Observable<T>, ZenObservable.SubscriptionObserver<T>\n{\n private isClosed = false;\n private terminatingError?: Error;\n\n private readonly observable = new ObservableImpl<T>(subscriber => {\n if (this.isClosed) {\n if (this.terminatingError) {\n subscriber.error(this.terminatingError);\n } else {\n subscriber.complete();\n }\n return () => {};\n }\n\n this.subscribers.add(subscriber);\n return () => {\n this.subscribers.delete(subscriber);\n };\n });\n\n private readonly subscribers = new Set<\n ZenObservable.SubscriptionObserver<T>\n >();\n\n [Symbol.observable]() {\n return this;\n }\n\n get closed() {\n return this.isClosed;\n }\n\n next(value: T) {\n if (this.isClosed) {\n throw new Error('PublishSubject is closed');\n }\n this.subscribers.forEach(subscriber => subscriber.next(value));\n }\n\n error(error: Error) {\n if (this.isClosed) {\n throw new Error('PublishSubject is closed');\n }\n this.isClosed = true;\n this.terminatingError = error;\n this.subscribers.forEach(subscriber => subscriber.error(error));\n }\n\n complete() {\n if (this.isClosed) {\n throw new Error('PublishSubject is closed');\n }\n this.isClosed = true;\n this.subscribers.forEach(subscriber => subscriber.complete());\n }\n\n subscribe(observer: ZenObservable.Observer<T>): ZenObservable.Subscription;\n subscribe(\n onNext: (value: T) => void,\n onError?: (error: any) => void,\n onComplete?: () => void,\n ): ZenObservable.Subscription;\n subscribe(\n onNext: ZenObservable.Observer<T> | ((value: T) => void),\n onError?: (error: any) => void,\n onComplete?: () => void,\n ): ZenObservable.Subscription {\n const observer =\n typeof onNext === 'function'\n ? {\n next: onNext,\n error: onError,\n complete: onComplete,\n }\n : onNext;\n\n return this.observable.subscribe(observer);\n }\n}\n\n/**\n * A basic implementation of ReactiveX behavior subjects.\n *\n * A subject is a convenient way to create an observable when you want\n * to fan out a single value to all subscribers.\n *\n * The BehaviorSubject will emit the most recently emitted value or error\n * whenever a new observer subscribes to the subject.\n *\n * See http://reactivex.io/documentation/subject.html\n */\n\nexport class BehaviorSubject<T>\n implements Observable<T>, ZenObservable.SubscriptionObserver<T>\n{\n private isClosed: boolean;\n private currentValue: T;\n private terminatingError: Error | undefined;\n private readonly observable: Observable<T>;\n\n constructor(value: T) {\n this.isClosed = false;\n this.currentValue = value;\n this.terminatingError = undefined;\n this.observable = new ObservableImpl<T>(subscriber => {\n if (this.isClosed) {\n if (this.terminatingError) {\n subscriber.error(this.terminatingError);\n } else {\n subscriber.complete();\n }\n return () => {};\n }\n\n subscriber.next(this.currentValue);\n\n this.subscribers.add(subscriber);\n return () => {\n this.subscribers.delete(subscriber);\n };\n });\n }\n\n private readonly subscribers = new Set<\n ZenObservable.SubscriptionObserver<T>\n >();\n\n [Symbol.observable]() {\n return this;\n }\n\n get closed() {\n return this.isClosed;\n }\n\n next(value: T) {\n if (this.isClosed) {\n throw new Error('BehaviorSubject is closed');\n }\n this.currentValue = value;\n this.subscribers.forEach(subscriber => subscriber.next(value));\n }\n\n error(error: Error) {\n if (this.isClosed) {\n throw new Error('BehaviorSubject is closed');\n }\n this.isClosed = true;\n this.terminatingError = error;\n this.subscribers.forEach(subscriber => subscriber.error(error));\n }\n\n complete() {\n if (this.isClosed) {\n throw new Error('BehaviorSubject is closed');\n }\n this.isClosed = true;\n this.subscribers.forEach(subscriber => subscriber.complete());\n }\n\n subscribe(observer: ZenObservable.Observer<T>): ZenObservable.Subscription;\n subscribe(\n onNext: (value: T) => void,\n onError?: (error: any) => void,\n onComplete?: () => void,\n ): ZenObservable.Subscription;\n subscribe(\n onNext: ZenObservable.Observer<T> | ((value: T) => void),\n onError?: (error: any) => void,\n onComplete?: () => void,\n ): ZenObservable.Subscription {\n const observer =\n typeof onNext === 'function'\n ? {\n next: onNext,\n error: onError,\n complete: onComplete,\n }\n : onNext;\n\n return this.observable.subscribe(observer);\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { BehaviorSubject } from '../subjects';\nimport { SessionState } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\n\nexport class SessionStateTracker {\n private readonly subject = new BehaviorSubject<SessionState>(\n SessionState.SignedOut,\n );\n\n private signedIn: boolean = false;\n\n setIsSignedIn(isSignedIn: boolean) {\n if (this.signedIn !== isSignedIn) {\n this.signedIn = isSignedIn;\n this.subject.next(\n this.signedIn ? SessionState.SignedIn : SessionState.SignedOut,\n );\n }\n }\n\n sessionState$(): Observable<SessionState> {\n return this.subject;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n SessionManager,\n SessionScopesFunc,\n SessionShouldRefreshFunc,\n GetSessionOptions,\n} from './types';\nimport { AuthConnector } from '../AuthConnector';\nimport { SessionScopeHelper, hasScopes } from './common';\nimport { SessionStateTracker } from './SessionStateTracker';\n\ntype Options<T> = {\n /** The connector used for acting on the auth session */\n connector: AuthConnector<T>;\n /** Used to get the scope of the session */\n sessionScopes: SessionScopesFunc<T>;\n /** Used to check if the session needs to be refreshed */\n sessionShouldRefresh: SessionShouldRefreshFunc<T>;\n /** The default scopes that should always be present in a session, defaults to none. */\n defaultScopes?: Set<string>;\n};\n\n/**\n * RefreshingAuthSessionManager manages an underlying session that has\n * and expiration time and needs to be refreshed periodically.\n */\nexport class RefreshingAuthSessionManager<T> implements SessionManager<T> {\n private readonly connector: AuthConnector<T>;\n private readonly helper: SessionScopeHelper<T>;\n private readonly sessionScopesFunc: SessionScopesFunc<T>;\n private readonly sessionShouldRefreshFunc: SessionShouldRefreshFunc<T>;\n private readonly stateTracker = new SessionStateTracker();\n\n private refreshPromise?: Promise<T>;\n private currentSession: T | undefined;\n\n constructor(options: Options<T>) {\n const {\n connector,\n defaultScopes = new Set(),\n sessionScopes,\n sessionShouldRefresh,\n } = options;\n\n this.connector = connector;\n this.sessionScopesFunc = sessionScopes;\n this.sessionShouldRefreshFunc = sessionShouldRefresh;\n this.helper = new SessionScopeHelper({ sessionScopes, defaultScopes });\n }\n\n async getSession(options: GetSessionOptions): Promise<T | undefined> {\n if (\n this.helper.sessionExistsAndHasScope(this.currentSession, options.scopes)\n ) {\n const shouldRefresh = this.sessionShouldRefreshFunc(this.currentSession!);\n if (!shouldRefresh) {\n return this.currentSession!;\n }\n\n try {\n const refreshedSession = await this.collapsedSessionRefresh();\n const currentScopes = this.sessionScopesFunc(this.currentSession!);\n const refreshedScopes = this.sessionScopesFunc(refreshedSession);\n if (hasScopes(refreshedScopes, currentScopes)) {\n this.currentSession = refreshedSession;\n }\n return refreshedSession;\n } catch (error) {\n if (options.optional) {\n return undefined;\n }\n throw error;\n }\n }\n\n // The user may still have a valid refresh token in their cookies. Attempt to\n // initiate a fresh session through the backend using that refresh token.\n //\n // We skip this check if an instant login popup is requested, as we need to\n // stay in a synchronous call stack from the user interaction. The downside\n // is that that the user will sometimes be requested to log in even if they\n // already had an existing session.\n if (!this.currentSession && !options.instantPopup) {\n try {\n const newSession = await this.collapsedSessionRefresh();\n this.currentSession = newSession;\n // The session might not have the scopes requested so go back and check again\n return this.getSession(options);\n } catch {\n // If the refresh attempt fails we assume we don't have a session, so continue to create one.\n }\n }\n\n // If we continue here we will show a popup, so exit if this is an optional session request.\n if (options.optional) {\n return undefined;\n }\n\n // We can call authRequester multiple times, the returned session will contain all requested scopes.\n this.currentSession = await this.connector.createSession({\n ...options,\n scopes: this.helper.getExtendedScope(this.currentSession, options.scopes),\n });\n this.stateTracker.setIsSignedIn(true);\n return this.currentSession;\n }\n\n async removeSession() {\n this.currentSession = undefined;\n await this.connector.removeSession();\n this.stateTracker.setIsSignedIn(false);\n }\n\n sessionState$() {\n return this.stateTracker.sessionState$();\n }\n\n private async collapsedSessionRefresh(): Promise<T> {\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.connector.refreshSession();\n\n try {\n const session = await this.refreshPromise;\n this.stateTracker.setIsSignedIn(true);\n return session;\n } finally {\n delete this.refreshPromise;\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { MutableSessionManager, GetSessionOptions } from './types';\nimport { AuthConnector } from '../AuthConnector';\nimport { SessionScopeHelper } from './common';\nimport { SessionStateTracker } from './SessionStateTracker';\n\ntype Options<T> = {\n /** The connector used for acting on the auth session */\n connector: AuthConnector<T>;\n /** Used to get the scope of the session */\n sessionScopes?: (session: T) => Set<string>;\n /** The default scopes that should always be present in a session, defaults to none. */\n defaultScopes?: Set<string>;\n};\n\n/**\n * StaticAuthSessionManager manages an underlying session that does not expire.\n */\nexport class StaticAuthSessionManager<T> implements MutableSessionManager<T> {\n private readonly connector: AuthConnector<T>;\n private readonly helper: SessionScopeHelper<T>;\n private readonly stateTracker = new SessionStateTracker();\n\n private currentSession: T | undefined;\n\n constructor(options: Options<T>) {\n const { connector, defaultScopes = new Set(), sessionScopes } = options;\n\n this.connector = connector;\n this.helper = new SessionScopeHelper({ sessionScopes, defaultScopes });\n }\n\n setSession(session: T | undefined): void {\n this.currentSession = session;\n this.stateTracker.setIsSignedIn(Boolean(session));\n }\n\n async getSession(options: GetSessionOptions): Promise<T | undefined> {\n if (\n this.helper.sessionExistsAndHasScope(this.currentSession, options.scopes)\n ) {\n return this.currentSession;\n }\n\n // If we continue here we will show a popup, so exit if this is an optional session request.\n if (options.optional) {\n return undefined;\n }\n\n // We can call authRequester multiple times, the returned session will contain all requested scopes.\n this.currentSession = await this.connector.createSession({\n ...options,\n scopes: this.helper.getExtendedScope(this.currentSession, options.scopes),\n });\n this.stateTracker.setIsSignedIn(true);\n return this.currentSession;\n }\n\n /**\n * We don't call this.connector.removeSession here, since this session manager\n * is intended to be static. As such there's no need to hit the remote logout\n * endpoint - simply discarding the local session state when signing out is\n * enough.\n */\n async removeSession() {\n this.currentSession = undefined;\n this.stateTracker.setIsSignedIn(false);\n }\n\n sessionState$() {\n return this.stateTracker.sessionState$();\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n MutableSessionManager,\n SessionScopesFunc,\n SessionShouldRefreshFunc,\n GetSessionOptions,\n} from './types';\nimport { SessionScopeHelper } from './common';\n\ntype Options<T> = {\n /** The connector used for acting on the auth session */\n manager: MutableSessionManager<T>;\n /** Storage key to use to store sessions */\n storageKey: string;\n /** Used to get the scope of the session */\n sessionScopes?: SessionScopesFunc<T>;\n /** Used to check if the session needs to be refreshed, defaults to never refresh */\n sessionShouldRefresh?: SessionShouldRefreshFunc<T>;\n};\n\n/**\n * AuthSessionStore decorates another SessionManager with a functionality\n * to store the session in local storage.\n *\n * Session is serialized to JSON with special support for following types: Set.\n */\nexport class AuthSessionStore<T> implements MutableSessionManager<T> {\n private readonly manager: MutableSessionManager<T>;\n private readonly storageKey: string;\n private readonly sessionShouldRefreshFunc: SessionShouldRefreshFunc<T>;\n private readonly helper: SessionScopeHelper<T>;\n\n constructor(options: Options<T>) {\n const {\n manager,\n storageKey,\n sessionScopes,\n sessionShouldRefresh = () => false,\n } = options;\n\n this.manager = manager;\n this.storageKey = storageKey;\n this.sessionShouldRefreshFunc = sessionShouldRefresh;\n this.helper = new SessionScopeHelper({\n sessionScopes,\n defaultScopes: new Set(),\n });\n }\n\n setSession(session: T | undefined): void {\n this.manager.setSession(session);\n this.saveSession(session);\n }\n\n async getSession(options: GetSessionOptions): Promise<T | undefined> {\n const { scopes } = options;\n const session = this.loadSession();\n\n if (this.helper.sessionExistsAndHasScope(session, scopes)) {\n const shouldRefresh = this.sessionShouldRefreshFunc(session!);\n\n if (!shouldRefresh) {\n this.manager.setSession(session!);\n return session!;\n }\n }\n\n const newSession = await this.manager.getSession(options);\n this.saveSession(newSession);\n return newSession;\n }\n\n async removeSession() {\n localStorage.removeItem(this.storageKey);\n await this.manager.removeSession();\n }\n\n sessionState$() {\n return this.manager.sessionState$();\n }\n\n private loadSession(): T | undefined {\n try {\n const sessionJson = localStorage.getItem(this.storageKey);\n if (sessionJson) {\n const session = JSON.parse(sessionJson, (_key, value) => {\n if (value?.__type === 'Set') {\n return new Set(value.__value);\n }\n return value;\n });\n return session;\n }\n\n return undefined;\n } catch (error) {\n localStorage.removeItem(this.storageKey);\n return undefined;\n }\n }\n\n private saveSession(session: T | undefined) {\n if (session === undefined) {\n localStorage.removeItem(this.storageKey);\n } else {\n localStorage.setItem(\n this.storageKey,\n JSON.stringify(session, (_key, value) => {\n if (value instanceof Set) {\n return {\n __type: 'Set',\n __value: Array.from(value),\n };\n }\n return value;\n }),\n );\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { SessionState } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport {\n SessionManager,\n MutableSessionManager,\n GetSessionOptions,\n} from './types';\nimport { SessionStateTracker } from './SessionStateTracker';\n\ntype Options<T> = {\n /**\n * A callback that is called to determine whether a given session supports refresh\n */\n sessionCanRefresh: (session: T) => boolean;\n\n /**\n * The session manager that is used if the a session does not support refresh.\n */\n staticSessionManager: MutableSessionManager<T>;\n\n /**\n * The session manager that is used if the a session supports refresh.\n */\n refreshingSessionManager: SessionManager<T>;\n};\n\n/**\n * OptionalRefreshSessionManagerMux wraps two different session managers, one for\n * static session storage and another one that supports refresh. For each session\n * that is retrieved is checked for whether it supports refresh. If it does, the\n * refreshing session manager is used, otherwise the static session manager is used.\n */\nexport class OptionalRefreshSessionManagerMux<T> implements SessionManager<T> {\n private readonly stateTracker = new SessionStateTracker();\n\n private readonly sessionCanRefresh: (session: T) => boolean;\n private readonly staticSessionManager: MutableSessionManager<T>;\n private readonly refreshingSessionManager: SessionManager<T>;\n\n constructor(options: Options<T>) {\n this.sessionCanRefresh = options.sessionCanRefresh;\n this.staticSessionManager = options.staticSessionManager;\n this.refreshingSessionManager = options.refreshingSessionManager;\n }\n\n async getSession(options: GetSessionOptions): Promise<T | undefined> {\n // First we check if there is an existing static session, using an optional request\n const staticSession = await this.staticSessionManager.getSession({\n ...options,\n optional: true,\n });\n if (staticSession) {\n this.stateTracker.setIsSignedIn(true);\n return staticSession;\n }\n\n // If there is no static session available, we ask the refresh manager to get a session\n const session = await this.refreshingSessionManager.getSession(options);\n\n // Handling the case where the session request is optional\n if (!session) {\n this.stateTracker.setIsSignedIn(false);\n return undefined;\n }\n\n // Next we check if the session we received from the refreshing manager can actually\n // be refreshed. If it can, we use this session without storing it in the static manager.\n if (this.sessionCanRefresh(session)) {\n this.stateTracker.setIsSignedIn(true);\n return session;\n }\n\n // If the session can't be refreshed, we store it in the static manager\n this.staticSessionManager.setSession(session);\n this.stateTracker.setIsSignedIn(true);\n return session;\n }\n\n async removeSession(): Promise<void> {\n await Promise.all([\n this.refreshingSessionManager.removeSession(),\n this.staticSessionManager.removeSession(),\n ]);\n this.stateTracker.setIsSignedIn(false);\n }\n\n sessionState$(): Observable<SessionState> {\n return this.stateTracker.sessionState$();\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DefaultAuthConnector } from '../../../../lib/AuthConnector';\nimport { GithubSession } from './types';\nimport {\n OAuthApi,\n SessionApi,\n SessionState,\n ProfileInfo,\n BackstageIdentity,\n AuthRequestOptions,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { SessionManager } from '../../../../lib/AuthSessionManager/types';\nimport {\n AuthSessionStore,\n RefreshingAuthSessionManager,\n StaticAuthSessionManager,\n} from '../../../../lib/AuthSessionManager';\nimport { OAuthApiCreateOptions } from '../types';\nimport { OptionalRefreshSessionManagerMux } from '../../../../lib/AuthSessionManager/OptionalRefreshSessionManagerMux';\n\nexport type GithubAuthResponse = {\n providerInfo: {\n accessToken: string;\n scope: string;\n expiresInSeconds?: number;\n };\n profile: ProfileInfo;\n backstageIdentity: BackstageIdentity;\n};\n\nconst DEFAULT_PROVIDER = {\n id: 'github',\n title: 'GitHub',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to GitHub products.\n *\n * @public\n */\nexport default class GithubAuth implements OAuthApi, SessionApi {\n static create(options: OAuthApiCreateOptions) {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = ['read:user'],\n } = options;\n\n const connector = new DefaultAuthConnector({\n discoveryApi,\n environment,\n provider,\n oauthRequestApi: oauthRequestApi,\n sessionTransform(res: GithubAuthResponse): GithubSession {\n return {\n ...res,\n providerInfo: {\n accessToken: res.providerInfo.accessToken,\n scopes: GithubAuth.normalizeScope(res.providerInfo.scope),\n expiresAt: res.providerInfo.expiresInSeconds\n ? new Date(Date.now() + res.providerInfo.expiresInSeconds * 1000)\n : undefined,\n },\n };\n },\n });\n\n const refreshingSessionManager = new RefreshingAuthSessionManager({\n connector,\n defaultScopes: new Set(defaultScopes),\n sessionScopes: (session: GithubSession) => session.providerInfo.scopes,\n sessionShouldRefresh: (session: GithubSession) => {\n const { expiresAt } = session.providerInfo;\n if (!expiresAt) {\n return false;\n }\n const expiresInSec = (expiresAt.getTime() - Date.now()) / 1000;\n return expiresInSec < 60 * 5;\n },\n });\n\n const staticSessionManager = new AuthSessionStore<GithubSession>({\n manager: new StaticAuthSessionManager({\n connector,\n defaultScopes: new Set(defaultScopes),\n sessionScopes: (session: GithubSession) => session.providerInfo.scopes,\n }),\n storageKey: `${provider.id}Session`,\n sessionScopes: (session: GithubSession) => session.providerInfo.scopes,\n });\n\n const sessionManagerMux = new OptionalRefreshSessionManagerMux({\n refreshingSessionManager,\n staticSessionManager,\n sessionCanRefresh: session =>\n session.providerInfo.expiresAt !== undefined,\n });\n\n return new GithubAuth(sessionManagerMux);\n }\n\n /**\n * @deprecated will be made private in the future. Use create method instead.\n */\n constructor(private readonly sessionManager: SessionManager<GithubSession>) {}\n\n async signIn() {\n await this.getAccessToken();\n }\n\n async signOut() {\n await this.sessionManager.removeSession();\n }\n\n sessionState$(): Observable<SessionState> {\n return this.sessionManager.sessionState$();\n }\n\n async getAccessToken(scope?: string, options?: AuthRequestOptions) {\n const session = await this.sessionManager.getSession({\n ...options,\n scopes: GithubAuth.normalizeScope(scope),\n });\n return session?.providerInfo.accessToken ?? '';\n }\n\n async getBackstageIdentity(\n options: AuthRequestOptions = {},\n ): Promise<BackstageIdentity | undefined> {\n const session = await this.sessionManager.getSession(options);\n return session?.backstageIdentity;\n }\n\n async getProfile(options: AuthRequestOptions = {}) {\n const session = await this.sessionManager.getSession(options);\n return session?.profile;\n }\n\n static normalizeScope(scope?: string): Set<string> {\n if (!scope) {\n return new Set();\n }\n\n const scopeList = Array.isArray(scope)\n ? scope\n : scope.split(/[\\s|,]/).filter(Boolean);\n\n return new Set(scopeList);\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DefaultAuthConnector } from '../../../../lib/AuthConnector';\nimport { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager';\nimport { SessionManager } from '../../../../lib/AuthSessionManager/types';\nimport {\n AuthRequestOptions,\n BackstageIdentity,\n OAuthApi,\n OpenIdConnectApi,\n ProfileInfo,\n ProfileInfoApi,\n SessionState,\n SessionApi,\n BackstageIdentityApi,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { OAuth2Session } from './types';\nimport { OAuthApiCreateOptions } from '../types';\n\n/**\n * OAuth2 create options.\n * @public\n */\nexport type OAuth2CreateOptions = OAuthApiCreateOptions & {\n scopeTransform?: (scopes: string[]) => string[];\n};\n\nexport type OAuth2Response = {\n providerInfo: {\n accessToken: string;\n idToken: string;\n scope: string;\n expiresInSeconds: number;\n };\n profile: ProfileInfo;\n backstageIdentity: BackstageIdentity;\n};\n\nconst DEFAULT_PROVIDER = {\n id: 'oauth2',\n title: 'Your Identity Provider',\n icon: () => null,\n};\n\n/**\n * Implements a generic OAuth2 flow for auth.\n *\n * @public\n */\nexport default class OAuth2\n implements\n OAuthApi,\n OpenIdConnectApi,\n ProfileInfoApi,\n BackstageIdentityApi,\n SessionApi\n{\n static create(options: OAuth2CreateOptions) {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = [],\n scopeTransform = x => x,\n } = options;\n\n const connector = new DefaultAuthConnector({\n discoveryApi,\n environment,\n provider,\n oauthRequestApi: oauthRequestApi,\n sessionTransform(res: OAuth2Response): OAuth2Session {\n return {\n ...res,\n providerInfo: {\n idToken: res.providerInfo.idToken,\n accessToken: res.providerInfo.accessToken,\n scopes: OAuth2.normalizeScopes(\n scopeTransform,\n res.providerInfo.scope,\n ),\n expiresAt: new Date(\n Date.now() + res.providerInfo.expiresInSeconds * 1000,\n ),\n },\n };\n },\n });\n\n const sessionManager = new RefreshingAuthSessionManager({\n connector,\n defaultScopes: new Set(defaultScopes),\n sessionScopes: (session: OAuth2Session) => session.providerInfo.scopes,\n sessionShouldRefresh: (session: OAuth2Session) => {\n const expiresInSec =\n (session.providerInfo.expiresAt.getTime() - Date.now()) / 1000;\n return expiresInSec < 60 * 5;\n },\n });\n\n return new OAuth2({ sessionManager, scopeTransform });\n }\n\n private readonly sessionManager: SessionManager<OAuth2Session>;\n private readonly scopeTransform: (scopes: string[]) => string[];\n\n /**\n * @deprecated will be made private in the future. Use create method instead.\n */\n constructor(options: {\n sessionManager: SessionManager<OAuth2Session>;\n scopeTransform: (scopes: string[]) => string[];\n }) {\n this.sessionManager = options.sessionManager;\n this.scopeTransform = options.scopeTransform;\n }\n\n async signIn() {\n await this.getAccessToken();\n }\n\n async signOut() {\n await this.sessionManager.removeSession();\n }\n\n sessionState$(): Observable<SessionState> {\n return this.sessionManager.sessionState$();\n }\n\n async getAccessToken(\n scope?: string | string[],\n options?: AuthRequestOptions,\n ) {\n const normalizedScopes = OAuth2.normalizeScopes(this.scopeTransform, scope);\n const session = await this.sessionManager.getSession({\n ...options,\n scopes: normalizedScopes,\n });\n return session?.providerInfo.accessToken ?? '';\n }\n\n async getIdToken(options: AuthRequestOptions = {}) {\n const session = await this.sessionManager.getSession(options);\n return session?.providerInfo.idToken ?? '';\n }\n\n async getBackstageIdentity(\n options: AuthRequestOptions = {},\n ): Promise<BackstageIdentity | undefined> {\n const session = await this.sessionManager.getSession(options);\n return session?.backstageIdentity;\n }\n\n async getProfile(options: AuthRequestOptions = {}) {\n const session = await this.sessionManager.getSession(options);\n return session?.profile;\n }\n\n private static normalizeScopes(\n scopeTransform: (scopes: string[]) => string[],\n scopes?: string | string[],\n ): Set<string> {\n if (!scopes) {\n return new Set();\n }\n\n const scopeList = Array.isArray(scopes)\n ? scopes\n : scopes.split(/[\\s|,]/).filter(Boolean);\n\n return new Set(scopeTransform(scopeList));\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { gitlabAuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'gitlab',\n title: 'GitLab',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to GitLab products.\n *\n * @public\n */\nexport default class GitlabAuth {\n static create(options: OAuthApiCreateOptions): typeof gitlabAuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = ['read_user'],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { googleAuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'google',\n title: 'Google',\n icon: () => null,\n};\n\nconst SCOPE_PREFIX = 'https://www.googleapis.com/auth/';\n\n/**\n * Implements the OAuth flow to Google products.\n *\n * @public\n */\nexport default class GoogleAuth {\n static create(options: OAuthApiCreateOptions): typeof googleAuthApiRef.T {\n const {\n discoveryApi,\n oauthRequestApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n defaultScopes = [\n 'openid',\n `${SCOPE_PREFIX}userinfo.email`,\n `${SCOPE_PREFIX}userinfo.profile`,\n ],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n scopeTransform(scopes: string[]) {\n return scopes.map(scope => {\n if (scope === 'openid') {\n return scope;\n }\n\n if (scope === 'profile' || scope === 'email') {\n return `${SCOPE_PREFIX}userinfo.${scope}`;\n }\n\n if (scope.startsWith(SCOPE_PREFIX)) {\n return scope;\n }\n\n return `${SCOPE_PREFIX}${scope}`;\n });\n },\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { oktaAuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'okta',\n title: 'Okta',\n icon: () => null,\n};\n\nconst OKTA_OIDC_SCOPES: Set<String> = new Set([\n 'openid',\n 'profile',\n 'email',\n 'phone',\n 'address',\n 'groups',\n 'offline_access',\n]);\n\nconst OKTA_SCOPE_PREFIX: string = 'okta.';\n\n/**\n * Implements the OAuth flow to Okta products.\n *\n * @public\n */\nexport default class OktaAuth {\n static create(options: OAuthApiCreateOptions): typeof oktaAuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = ['openid', 'email', 'profile', 'offline_access'],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n scopeTransform(scopes) {\n return scopes.map(scope => {\n if (OKTA_OIDC_SCOPES.has(scope)) {\n return scope;\n }\n\n if (scope.startsWith(OKTA_SCOPE_PREFIX)) {\n return scope;\n }\n\n return `${OKTA_SCOPE_PREFIX}${scope}`;\n });\n },\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DirectAuthConnector } from '../../../../lib/AuthConnector';\nimport { SessionManager } from '../../../../lib/AuthSessionManager/types';\nimport {\n ProfileInfo,\n BackstageIdentity,\n SessionState,\n AuthRequestOptions,\n ProfileInfoApi,\n BackstageIdentityApi,\n SessionApi,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { SamlSession } from './types';\nimport {\n AuthSessionStore,\n StaticAuthSessionManager,\n} from '../../../../lib/AuthSessionManager';\nimport { AuthApiCreateOptions } from '../types';\n\nexport type SamlAuthResponse = {\n profile: ProfileInfo;\n backstageIdentity: BackstageIdentity;\n};\n\nconst DEFAULT_PROVIDER = {\n id: 'saml',\n title: 'SAML',\n icon: () => null,\n};\n\n/**\n * Implements a general SAML based auth flow.\n *\n * @public\n */\nexport default class SamlAuth\n implements ProfileInfoApi, BackstageIdentityApi, SessionApi\n{\n static create(options: AuthApiCreateOptions) {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n } = options;\n\n const connector = new DirectAuthConnector<SamlSession>({\n discoveryApi,\n environment,\n provider,\n });\n\n const sessionManager = new StaticAuthSessionManager<SamlSession>({\n connector,\n });\n\n const authSessionStore = new AuthSessionStore<SamlSession>({\n manager: sessionManager,\n storageKey: `${provider.id}Session`,\n });\n\n return new SamlAuth(authSessionStore);\n }\n\n sessionState$(): Observable<SessionState> {\n return this.sessionManager.sessionState$();\n }\n\n /**\n * @deprecated will be made private in the future. Use create method instead.\n */\n constructor(private readonly sessionManager: SessionManager<SamlSession>) {}\n\n async signIn() {\n await this.getBackstageIdentity({});\n }\n async signOut() {\n await this.sessionManager.removeSession();\n }\n\n async getBackstageIdentity(\n options: AuthRequestOptions = {},\n ): Promise<BackstageIdentity | undefined> {\n const session = await this.sessionManager.getSession(options);\n return session?.backstageIdentity;\n }\n\n async getProfile(options: AuthRequestOptions = {}) {\n const session = await this.sessionManager.getSession(options);\n return session?.profile;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { auth0AuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'auth0',\n title: 'Auth0',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to Auth0 products.\n *\n * @public\n */\nexport default class Auth0Auth {\n static create(options: OAuthApiCreateOptions): typeof auth0AuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = ['openid', `email`, `profile`],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { microsoftAuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'microsoft',\n title: 'Microsoft',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to Microsoft products.\n *\n * @public\n */\nexport default class MicrosoftAuth {\n static create(options: OAuthApiCreateOptions): typeof microsoftAuthApiRef.T {\n const {\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n discoveryApi,\n defaultScopes = [\n 'openid',\n 'offline_access',\n 'profile',\n 'email',\n 'User.Read',\n ],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n oneloginAuthApiRef,\n OAuthRequestApi,\n AuthProvider,\n DiscoveryApi,\n} from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\n\n/**\n * OneLogin auth provider create options.\n * @public\n */\nexport type OneLoginAuthCreateOptions = {\n discoveryApi: DiscoveryApi;\n oauthRequestApi: OAuthRequestApi;\n environment?: string;\n provider?: AuthProvider & { id: string };\n};\n\nconst DEFAULT_PROVIDER = {\n id: 'onelogin',\n title: 'onelogin',\n icon: () => null,\n};\n\nconst OIDC_SCOPES: Set<String> = new Set([\n 'openid',\n 'profile',\n 'email',\n 'phone',\n 'address',\n 'groups',\n 'offline_access',\n]);\n\nconst SCOPE_PREFIX: string = 'onelogin.';\n\n/**\n * Implements a OneLogin OAuth flow.\n *\n * @public\n */\nexport default class OneLoginAuth {\n static create(\n options: OneLoginAuthCreateOptions,\n ): typeof oneloginAuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes: ['openid', 'email', 'profile', 'offline_access'],\n scopeTransform(scopes) {\n return scopes.map(scope => {\n if (OIDC_SCOPES.has(scope)) {\n return scope;\n }\n\n if (scope.startsWith(SCOPE_PREFIX)) {\n return scope;\n }\n\n return `${SCOPE_PREFIX}${scope}`;\n });\n },\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BackstageIdentity,\n bitbucketAuthApiRef,\n ProfileInfo,\n} from '@backstage/core-plugin-api';\n\nimport { OAuthApiCreateOptions } from '../types';\nimport { OAuth2 } from '../oauth2';\n\nexport type BitbucketAuthResponse = {\n providerInfo: {\n accessToken: string;\n scope: string;\n expiresInSeconds: number;\n };\n profile: ProfileInfo;\n backstageIdentity: BackstageIdentity;\n};\n\nconst DEFAULT_PROVIDER = {\n id: 'bitbucket',\n title: 'Bitbucket',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to Bitbucket products.\n *\n * @public\n */\nexport default class BitbucketAuth {\n static create(options: OAuthApiCreateOptions): typeof bitbucketAuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = ['team'],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { atlassianAuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'atlassian',\n title: 'Atlassian',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to Atlassian products.\n *\n * @public\n */\nexport default class AtlassianAuth {\n static create(options: OAuthApiCreateOptions): typeof atlassianAuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AlertApi, AlertMessage } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { PublishSubject } from '../../../lib/subjects';\n\n/**\n * Base implementation for the AlertApi that simply forwards alerts to consumers.\n *\n * @public\n */\nexport class AlertApiForwarder implements AlertApi {\n private readonly subject = new PublishSubject<AlertMessage>();\n\n post(alert: AlertMessage) {\n this.subject.next(alert);\n }\n\n alert$(): Observable<AlertMessage> {\n return this.subject;\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { AnalyticsApi, AnalyticsEvent } from '@backstage/core-plugin-api';\n\n/**\n * Base implementation for the AnalyticsApi that does nothing.\n *\n * @public\n */\nexport class NoOpAnalyticsApi implements AnalyticsApi {\n captureEvent(_event: AnalyticsEvent): void {}\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AppThemeApi, AppTheme } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { BehaviorSubject } from '../../../lib/subjects';\n\nconst STORAGE_KEY = 'theme';\n\n/**\n * Exposes the themes installed in the app, and permits switching the currently\n * active theme.\n *\n * @public\n */\nexport class AppThemeSelector implements AppThemeApi {\n static createWithStorage(themes: AppTheme[]) {\n const selector = new AppThemeSelector(themes);\n\n if (!window.localStorage) {\n return selector;\n }\n\n const initialThemeId =\n window.localStorage.getItem(STORAGE_KEY) ?? undefined;\n\n selector.setActiveThemeId(initialThemeId);\n\n selector.activeThemeId$().subscribe(themeId => {\n if (themeId) {\n window.localStorage.setItem(STORAGE_KEY, themeId);\n } else {\n window.localStorage.removeItem(STORAGE_KEY);\n }\n });\n\n window.addEventListener('storage', event => {\n if (event.key === STORAGE_KEY) {\n const themeId = localStorage.getItem(STORAGE_KEY) ?? undefined;\n selector.setActiveThemeId(themeId);\n }\n });\n\n return selector;\n }\n\n private activeThemeId: string | undefined;\n private readonly subject = new BehaviorSubject<string | undefined>(undefined);\n\n constructor(private readonly themes: AppTheme[]) {}\n\n getInstalledThemes(): AppTheme[] {\n return this.themes.slice();\n }\n\n activeThemeId$(): Observable<string | undefined> {\n return this.subject;\n }\n\n getActiveThemeId(): string | undefined {\n return this.activeThemeId;\n }\n\n setActiveThemeId(themeId?: string): void {\n this.activeThemeId = themeId;\n this.subject.next(themeId);\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DiscoveryApi } from '@backstage/core-plugin-api';\n\nconst ERROR_PREFIX = 'Invalid discovery URL pattern,';\n\n/**\n * UrlPatternDiscovery is a lightweight DiscoveryApi implementation.\n * It uses a single template string to construct URLs for each plugin.\n *\n * @public\n */\nexport class UrlPatternDiscovery implements DiscoveryApi {\n /**\n * Creates a new UrlPatternDiscovery given a template. The the only\n * interpolation done for the template is to replace instances of `{{pluginId}}`\n * with the ID of the plugin being requested.\n *\n * Example pattern: `http://localhost:7007/api/{{ pluginId }}`\n */\n static compile(pattern: string): UrlPatternDiscovery {\n const parts = pattern.split(/\\{\\{\\s*pluginId\\s*\\}\\}/);\n const urlStr = parts.join('pluginId');\n\n let url;\n try {\n url = new URL(urlStr);\n } catch {\n throw new Error(`${ERROR_PREFIX} URL '${urlStr}' is invalid`);\n }\n if (url.hash) {\n throw new Error(`${ERROR_PREFIX} URL must not have a hash`);\n }\n if (url.search) {\n throw new Error(`${ERROR_PREFIX} URL must not have a query`);\n }\n if (urlStr.endsWith('/')) {\n throw new Error(`${ERROR_PREFIX} URL must not end with a slash`);\n }\n\n return new UrlPatternDiscovery(parts);\n }\n\n private constructor(private readonly parts: string[]) {}\n\n async getBaseUrl(pluginId: string): Promise<string> {\n return this.parts.join(pluginId);\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n ErrorApi,\n ErrorApiError,\n ErrorApiErrorContext,\n AlertApi,\n} from '@backstage/core-plugin-api';\n\n/**\n * Decorates an ErrorApi by also forwarding error messages\n * to the alertApi with an 'error' severity.\n *\n * @public\n */\nexport class ErrorAlerter implements ErrorApi {\n constructor(\n private readonly alertApi: AlertApi,\n private readonly errorApi: ErrorApi,\n ) {}\n\n post(error: ErrorApiError, context?: ErrorApiErrorContext) {\n if (!context?.hidden) {\n this.alertApi.post({ message: error.message, severity: 'error' });\n }\n\n return this.errorApi.post(error, context);\n }\n\n error$() {\n return this.errorApi.error$();\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ErrorApi,\n ErrorApiError,\n ErrorApiErrorContext,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { PublishSubject } from '../../../lib/subjects';\n\n/**\n * Base implementation for the ErrorApi that simply forwards errors to consumers.\n *\n * @public\n */\nexport class ErrorApiForwarder implements ErrorApi {\n private readonly subject = new PublishSubject<{\n error: Error;\n context?: ErrorApiErrorContext;\n }>();\n\n post(error: ErrorApiError, context?: ErrorApiErrorContext) {\n this.subject.next({ error, context });\n }\n\n error$(): Observable<{ error: Error; context?: ErrorApiErrorContext }> {\n return this.subject;\n }\n}\n","import {\n ErrorApi,\n ErrorApiError,\n ErrorApiErrorContext,\n} from '@backstage/core-plugin-api';\n\n/*\n * Copyright 2020 Spotify AB\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Utility class that helps with error forwarding.\n *\n * @public\n */\nexport class UnhandledErrorForwarder {\n /**\n * Add event listener, such that unhandled errors can be forwarded using an given `ErrorApi` instance\n */\n static forward(errorApi: ErrorApi, errorContext: ErrorApiErrorContext) {\n window.addEventListener(\n 'unhandledrejection',\n (e: PromiseRejectionEvent) => {\n errorApi.post(e.reason as ErrorApiError, errorContext);\n },\n );\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FeatureFlagState,\n FeatureFlagsApi,\n FeatureFlag,\n FeatureFlagsSaveOptions,\n} from '@backstage/core-plugin-api';\n\nexport function validateFlagName(name: string): void {\n if (name.length < 3) {\n throw new Error(\n `The '${name}' feature flag must have a minimum length of three characters.`,\n );\n }\n\n if (name.length > 150) {\n throw new Error(\n `The '${name}' feature flag must not exceed 150 characters.`,\n );\n }\n\n if (!name.match(/^[a-z]+[a-z0-9-]+$/)) {\n throw new Error(\n `The '${name}' feature flag must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. ` +\n 'Examples: feature-flag-one, alpha, release-2020',\n );\n }\n}\n\n/**\n * A feature flags implementation that stores the flags in the browser's local\n * storage.\n *\n * @public\n */\nexport class LocalStorageFeatureFlags implements FeatureFlagsApi {\n private registeredFeatureFlags: FeatureFlag[] = [];\n private flags?: Map<string, FeatureFlagState>;\n\n registerFlag(flag: FeatureFlag) {\n validateFlagName(flag.name);\n this.registeredFeatureFlags.push(flag);\n }\n\n getRegisteredFlags(): FeatureFlag[] {\n return this.registeredFeatureFlags.slice();\n }\n\n isActive(name: string): boolean {\n if (!this.flags) {\n this.flags = this.load();\n }\n return this.flags.get(name) === FeatureFlagState.Active;\n }\n\n save(options: FeatureFlagsSaveOptions): void {\n if (!this.flags) {\n this.flags = this.load();\n }\n if (!options.merge) {\n this.flags.clear();\n }\n for (const [name, state] of Object.entries(options.states)) {\n this.flags.set(name, state);\n }\n\n const enabled = Array.from(this.flags.entries()).filter(\n ([, state]) => state === FeatureFlagState.Active,\n );\n window.localStorage.setItem(\n 'featureFlags',\n JSON.stringify(Object.fromEntries(enabled)),\n );\n }\n\n private load(): Map<string, FeatureFlagState> {\n try {\n const jsonStr = window.localStorage.getItem('featureFlags');\n if (!jsonStr) {\n return new Map();\n }\n const json = JSON.parse(jsonStr) as unknown;\n if (typeof json !== 'object' || json === null || Array.isArray(json)) {\n return new Map();\n }\n\n const entries = Object.entries(json).filter(([name, value]) => {\n validateFlagName(name);\n return value === FeatureFlagState.Active;\n });\n\n return new Map(entries);\n } catch {\n return new Map();\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Observable } from '@backstage/types';\nimport { BehaviorSubject } from '../../../lib/subjects';\n\ntype RequestQueueEntry<ResultType> = {\n scopes: Set<string>;\n resolve: (value: ResultType | PromiseLike<ResultType>) => void;\n reject: (reason: Error) => void;\n};\n\nexport type PendingRequest<ResultType> = {\n scopes: Set<string> | undefined;\n resolve: (value: ResultType) => void;\n reject: (reason: Error) => void;\n};\n\nexport function hasScopes(\n searched: Set<string>,\n searchFor: Set<string>,\n): boolean {\n for (const scope of searchFor) {\n if (!searched.has(scope)) {\n return false;\n }\n }\n return true;\n}\n\nexport function joinScopes(\n scopes: Set<string>,\n ...moreScopess: Set<string>[]\n): Set<string> {\n const result = new Set(scopes);\n\n for (const moreScopes of moreScopess) {\n for (const scope of moreScopes) {\n result.add(scope);\n }\n }\n\n return result;\n}\n\n/**\n * The OAuthPendingRequests class is a utility for managing and observing\n * a stream of requests for oauth scopes for a single provider, and resolving\n * them correctly once requests are fulfilled.\n */\nexport class OAuthPendingRequests<ResultType> {\n private requests: RequestQueueEntry<ResultType>[] = [];\n private subject = new BehaviorSubject<PendingRequest<ResultType>>(\n this.getCurrentPending(),\n );\n\n request(scopes: Set<string>): Promise<ResultType> {\n return new Promise((resolve, reject) => {\n this.requests.push({ scopes, resolve, reject });\n\n this.subject.next(this.getCurrentPending());\n });\n }\n\n resolve(scopes: Set<string>, result: ResultType): void {\n this.requests = this.requests.filter(request => {\n if (hasScopes(scopes, request.scopes)) {\n request.resolve(result);\n return false;\n }\n return true;\n });\n\n this.subject.next(this.getCurrentPending());\n }\n\n reject(error: Error) {\n this.requests.forEach(request => request.reject(error));\n this.requests = [];\n\n this.subject.next(this.getCurrentPending());\n }\n\n pending(): Observable<PendingRequest<ResultType>> {\n return this.subject;\n }\n\n private getCurrentPending(): PendingRequest<ResultType> {\n const currentScopes =\n this.requests.length === 0\n ? undefined\n : this.requests\n .slice(1)\n .reduce(\n (acc, current) => joinScopes(acc, current.scopes),\n this.requests[0].scopes,\n );\n\n return {\n scopes: currentScopes,\n resolve: (value: ResultType) => {\n if (currentScopes) {\n this.resolve(currentScopes, value);\n }\n },\n reject: (reason: Error) => {\n if (currentScopes) {\n this.reject(reason);\n }\n },\n };\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n OAuthRequestApi,\n PendingAuthRequest,\n AuthRequester,\n AuthRequesterOptions,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { OAuthPendingRequests, PendingRequest } from './OAuthPendingRequests';\nimport { BehaviorSubject } from '../../../lib/subjects';\n\n/**\n * The OAuthRequestManager is an implementation of the OAuthRequestApi.\n *\n * The purpose of this class and the API is to read a stream of incoming requests\n * of OAuth access tokens from different providers with varying scope, and funnel\n * them all together into a single request for each OAuth provider.\n *\n * @public\n */\nexport class OAuthRequestManager implements OAuthRequestApi {\n private readonly subject = new BehaviorSubject<PendingAuthRequest[]>([]);\n private currentRequests: PendingAuthRequest[] = [];\n private handlerCount = 0;\n\n createAuthRequester<T>(options: AuthRequesterOptions<T>): AuthRequester<T> {\n const handler = new OAuthPendingRequests<T>();\n\n const index = this.handlerCount;\n this.handlerCount++;\n\n handler.pending().subscribe({\n next: scopeRequest => {\n const newRequests = this.currentRequests.slice();\n const request = this.makeAuthRequest(scopeRequest, options);\n if (!request) {\n delete newRequests[index];\n } else {\n newRequests[index] = request;\n }\n this.currentRequests = newRequests;\n // Convert from sparse array to array of present items only\n this.subject.next(newRequests.filter(Boolean));\n },\n });\n\n return scopes => {\n return handler.request(scopes);\n };\n }\n\n // Converts the pending request and popup options into a popup request that we can forward to subscribers.\n private makeAuthRequest(\n request: PendingRequest<any>,\n options: AuthRequesterOptions<any>,\n ): PendingAuthRequest | undefined {\n const { scopes } = request;\n if (!scopes) {\n return undefined;\n }\n\n return {\n provider: options.provider,\n trigger: async () => {\n const result = await options.onAuthRequest(scopes);\n request.resolve(result);\n },\n reject: () => {\n const error = new Error('Login failed, rejected by user');\n error.name = 'RejectedError';\n request.reject(error);\n },\n };\n }\n\n authRequest$(): Observable<PendingAuthRequest[]> {\n return this.subject;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n StorageApi,\n StorageValueChange,\n ErrorApi,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport ObservableImpl from 'zen-observable';\n\nconst buckets = new Map<string, WebStorage>();\n\n/**\n * An implementation of the storage API, that uses the browser's local storage.\n *\n * @public\n */\nexport class WebStorage implements StorageApi {\n constructor(\n private readonly namespace: string,\n private readonly errorApi: ErrorApi,\n ) {}\n\n static create(options: {\n errorApi: ErrorApi;\n namespace?: string;\n }): WebStorage {\n return new WebStorage(options.namespace ?? '', options.errorApi);\n }\n\n get<T>(key: string): T | undefined {\n try {\n const storage = JSON.parse(localStorage.getItem(this.getKeyName(key))!);\n return storage ?? undefined;\n } catch (e) {\n this.errorApi.post(\n new Error(`Error when parsing JSON config from storage for: ${key}`),\n );\n }\n\n return undefined;\n }\n\n forBucket(name: string): WebStorage {\n const bucketPath = `${this.namespace}/${name}`;\n if (!buckets.has(bucketPath)) {\n buckets.set(bucketPath, new WebStorage(bucketPath, this.errorApi));\n }\n return buckets.get(bucketPath)!;\n }\n\n async set<T>(key: string, data: T): Promise<void> {\n localStorage.setItem(this.getKeyName(key), JSON.stringify(data, null, 2));\n this.notifyChanges({ key, newValue: data });\n }\n\n async remove(key: string): Promise<void> {\n localStorage.removeItem(this.getKeyName(key));\n this.notifyChanges({ key, newValue: undefined });\n }\n\n observe$<T>(key: string): Observable<StorageValueChange<T>> {\n return this.observable.filter(({ key: messageKey }) => messageKey === key);\n }\n\n private getKeyName(key: string) {\n return `${this.namespace}/${encodeURIComponent(key)}`;\n }\n\n private notifyChanges<T>(message: StorageValueChange<T>) {\n for (const subscription of this.subscribers) {\n subscription.next(message);\n }\n }\n\n private subscribers = new Set<\n ZenObservable.SubscriptionObserver<StorageValueChange>\n >();\n\n private readonly observable = new ObservableImpl<StorageValueChange>(\n subscriber => {\n this.subscribers.add(subscriber);\n return () => {\n this.subscribers.delete(subscriber);\n };\n },\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createApp as createDefaultApp } from '@backstage/app-defaults';\nimport { AppContext, BackstageApp } from './types';\n\n/**\n * Creates a new Backstage App.\n *\n * @deprecated Use {@link @backstage/app-defaults#createApp} from `@backstage/app-defaults` instead\n * @param options - A set of options for creating the app\n * @public\n */\nexport function createApp(\n options?: Parameters<typeof createDefaultApp>[0],\n): BackstageApp & AppContext {\n // eslint-disable-next-line no-console\n console.warn(\n 'DEPRECATION WARNING: The createApp function from @backstage/core-app-api will soon be removed, ' +\n 'migrate to importing createApp from the @backstage/app-defaults package instead. ' +\n 'If you do not wish to use a standard app configuration but instead supply all options yourself ' +\n ' you can use createSpecializedApp from @backstage/core-app-api instead.',\n );\n return createDefaultApp(options) as BackstageApp & AppContext;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isValidElement, ReactNode, ReactElement, Children } from 'react';\n\nexport type Discoverer = (element: ReactElement) => ReactNode;\n\nexport type Collector<Result, Context> = () => {\n accumulator: Result;\n visit(\n accumulator: Result,\n element: ReactElement,\n parent: ReactElement | undefined,\n context: Context,\n ): Context;\n};\n\n/**\n * A function that allows you to traverse a tree of React elements using\n * varying methods to discover child nodes and collect data along the way.\n */\nexport function traverseElementTree<Results>(options: {\n root: ReactNode;\n discoverers: Discoverer[];\n collectors: { [name in keyof Results]: Collector<Results[name], any> };\n}): Results {\n const collectors: {\n [name in string]: ReturnType<Collector<any, any>>;\n } = {};\n\n // Bootstrap all collectors, initializing the accumulators and providing the visitor function\n for (const name in options.collectors) {\n if (options.collectors.hasOwnProperty(name)) {\n collectors[name] = options.collectors[name]();\n }\n }\n\n // Internal representation of an element in the tree that we're iterating over\n type QueueItem = {\n node: ReactNode;\n parent: ReactElement | undefined;\n contexts: { [name in string]: unknown };\n };\n\n const queue = [\n {\n node: Children.toArray(options.root),\n parent: undefined,\n contexts: {},\n } as QueueItem,\n ];\n\n while (queue.length !== 0) {\n const { node, parent, contexts } = queue.shift()!;\n\n // While the parent and the element we pass on to collectors and discoverers\n // have been validated and are known to be React elements, the child nodes\n // emitted by the discoverers are not.\n Children.forEach(node, element => {\n if (!isValidElement(element)) {\n return;\n }\n\n const nextContexts: QueueItem['contexts'] = {};\n\n // Collectors populate their result data using the current node, and compute\n // context for the next iteration\n for (const name in collectors) {\n if (collectors.hasOwnProperty(name)) {\n const collector = collectors[name];\n\n nextContexts[name] = collector.visit(\n collector.accumulator,\n element,\n parent,\n contexts[name],\n );\n }\n }\n\n // Discoverers provide ways to continue the traversal from the current element\n for (const discoverer of options.discoverers) {\n const children = discoverer(element);\n if (children) {\n queue.push({\n node: children,\n parent: element,\n contexts: nextContexts,\n });\n }\n }\n });\n }\n\n return Object.fromEntries(\n Object.entries(collectors).map(([name, c]) => [name, c.accumulator]),\n ) as Results;\n}\n\nexport function createCollector<Result, Context>(\n accumulatorFactory: () => Result,\n visit: ReturnType<Collector<Result, Context>>['visit'],\n): Collector<Result, Context> {\n return () => ({ accumulator: accumulatorFactory(), visit });\n}\n\nexport function childDiscoverer(element: ReactElement): ReactNode {\n return element.props?.children;\n}\n\nexport function routeElementDiscoverer(element: ReactElement): ReactNode {\n if (element.props?.path && element.props?.element) {\n return element.props?.element;\n }\n return undefined;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { BackstagePlugin, getComponentData } from '@backstage/core-plugin-api';\nimport { createCollector } from '../extensions/traversal';\n\nexport const pluginCollector = createCollector(\n () => new Set<BackstagePlugin<any, any>>(),\n (acc, node) => {\n const plugin = getComponentData<BackstagePlugin<any, any>>(\n node,\n 'core.plugin',\n );\n if (plugin) {\n acc.add(plugin);\n }\n },\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n featureFlagsApiRef,\n useApi,\n attachComponentData,\n} from '@backstage/core-plugin-api';\nimport React, { ReactNode } from 'react';\n\n/**\n * Props for the {@link FeatureFlagged} component.\n *\n * @public\n */\nexport type FeatureFlaggedProps = { children: ReactNode } & (\n | { with: string }\n | { without: string }\n);\n\n/**\n * Enables or disables rendering of its children based on the state of a given\n * feature flag.\n *\n * @public\n */\nexport const FeatureFlagged = (props: FeatureFlaggedProps) => {\n const { children } = props;\n const featureFlagApi = useApi(featureFlagsApiRef);\n const isEnabled =\n 'with' in props\n ? featureFlagApi.isActive(props.with)\n : !featureFlagApi.isActive(props.without);\n return <>{isEnabled ? children : null}</>;\n};\n\nattachComponentData(FeatureFlagged, 'core.featureFlagged', true);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isValidElement, ReactElement, ReactNode } from 'react';\nimport {\n RouteRef,\n getComponentData,\n BackstagePlugin,\n} from '@backstage/core-plugin-api';\nimport { BackstageRouteObject } from './types';\nimport { createCollector } from '../extensions/traversal';\nimport { FeatureFlagged, FeatureFlaggedProps } from './FeatureFlagged';\n\nfunction getMountPoint(node: ReactElement): RouteRef | undefined {\n const element: ReactNode = node.props?.element;\n\n let routeRef = getComponentData<RouteRef>(node, 'core.mountPoint');\n if (!routeRef && isValidElement(element)) {\n routeRef = getComponentData<RouteRef>(element, 'core.mountPoint');\n }\n\n return routeRef;\n}\n\nexport const routePathCollector = createCollector(\n () => new Map<RouteRef, string>(),\n (acc, node, parent, ctxPath: string | undefined) => {\n // The context path is used during mount point gathering to assign the same path\n // to all discovered mount points\n let currentCtxPath = ctxPath;\n\n if (parent?.props.element === node) {\n return currentCtxPath;\n }\n\n // Start gathering mount points when we encounter a mount point gathering flag\n if (getComponentData<boolean>(node, 'core.gatherMountPoints')) {\n const path: string | undefined = node.props?.path;\n if (!path) {\n throw new Error('Mount point gatherer must have a path');\n }\n currentCtxPath = path;\n }\n\n const routeRef = getMountPoint(node);\n if (routeRef) {\n let path: string | undefined = node.props?.path;\n // If we're gathering mount points we use the context path as out path, unless\n // the element has its own path, in which case we use that instead and stop gathering\n if (currentCtxPath) {\n if (path) {\n currentCtxPath = undefined;\n } else {\n path = currentCtxPath;\n }\n }\n if (!path) {\n throw new Error('Mounted routable extension must have a path');\n }\n acc.set(routeRef, path);\n }\n return currentCtxPath;\n },\n);\n\nexport const routeParentCollector = createCollector(\n () => new Map<RouteRef, RouteRef | undefined>(),\n (acc, node, parent, parentRouteRef?: RouteRef | { sticky: RouteRef }) => {\n if (parent?.props.element === node) {\n return parentRouteRef;\n }\n\n let nextParent = parentRouteRef;\n\n const routeRef = getMountPoint(node);\n if (routeRef) {\n // \"sticky\" route ref is when we've encountered a mount point gatherer, and we want a\n // mount points beneath it to have the same parent, regardless of internal structure\n if (parentRouteRef && 'sticky' in parentRouteRef) {\n acc.set(routeRef, parentRouteRef.sticky);\n\n // When we encounter a mount point with an explicit path, we stop gathering\n // mount points withing the children and remove the sticky state\n if (node.props?.path) {\n nextParent = routeRef;\n } else {\n nextParent = parentRouteRef;\n }\n } else {\n acc.set(routeRef, parentRouteRef);\n nextParent = routeRef;\n }\n }\n\n // Mount point gatherers are marked as \"sticky\"\n if (getComponentData<boolean>(node, 'core.gatherMountPoints')) {\n return { sticky: nextParent };\n }\n\n return nextParent;\n },\n);\n\n// We always add a child that matches all subroutes but without any route refs. This makes\n// sure that we're always able to match each route no matter how deep the navigation goes.\n// The route resolver then takes care of selecting the most specific match in order to find\n// mount points that are as deep in the routing tree as possible.\nexport const MATCH_ALL_ROUTE: BackstageRouteObject = {\n caseSensitive: false,\n path: '/*',\n element: 'match-all', // These elements aren't used, so we add in a bit of debug information\n routeRefs: new Set(),\n};\n\nexport const routeObjectCollector = createCollector(\n () => Array<BackstageRouteObject>(),\n (acc, node, parent, parentObj: BackstageRouteObject | undefined) => {\n const parentChildren = parentObj?.children ?? acc;\n if (parent?.props.element === node) {\n return parentObj;\n }\n\n const path: string | undefined = node.props?.path;\n const caseSensitive: boolean = Boolean(node.props?.caseSensitive);\n\n const routeRef = getMountPoint(node);\n if (routeRef) {\n if (path) {\n const newObject: BackstageRouteObject = {\n caseSensitive,\n path,\n element: 'mounted',\n routeRefs: new Set([routeRef]),\n children: [MATCH_ALL_ROUTE],\n plugin: getComponentData<BackstagePlugin>(\n node.props.element,\n 'core.plugin',\n ),\n };\n parentChildren.push(newObject);\n return newObject;\n }\n\n parentObj?.routeRefs.add(routeRef);\n }\n\n const isGatherer = getComponentData<boolean>(\n node,\n 'core.gatherMountPoints',\n );\n if (isGatherer) {\n if (!path) {\n throw new Error('Mount point gatherer must have a path');\n }\n const newObject: BackstageRouteObject = {\n caseSensitive,\n path,\n element: 'gathered',\n routeRefs: new Set(),\n children: [MATCH_ALL_ROUTE],\n plugin: parentObj?.plugin,\n };\n parentChildren.push(newObject);\n return newObject;\n }\n\n return parentObj;\n },\n);\n\nexport const featureFlagCollector = createCollector(\n () => new Set<string>(),\n (acc, node) => {\n if (node.type === FeatureFlagged) {\n const props = node.props as FeatureFlaggedProps;\n acc.add('with' in props ? props.with : props.without);\n }\n },\n);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n BackstagePlugin,\n} from '@backstage/core-plugin-api';\nimport { getOrCreateGlobalSingleton } from '@backstage/version-bridge';\n\ntype RouteRefType = Exclude<\n keyof RouteRef,\n 'params' | 'path' | 'title' | 'icon'\n>;\nexport const routeRefType: RouteRefType = getOrCreateGlobalSingleton<any>(\n 'route-ref-type',\n () => Symbol('route-ref-type'),\n);\n\nexport type AnyParams = { [param in string]: string } | undefined;\n\nexport type AnyRouteRef =\n | RouteRef<any>\n | SubRouteRef<any>\n | ExternalRouteRef<any, any>;\n\n// The extra TS magic here is to require a single params argument if the RouteRef\n// had at least one param defined, but require 0 arguments if there are no params defined.\n// Without this we'd have to pass in empty object to all parameter-less RouteRefs\n// just to make TypeScript happy, or we would have to make the argument optional in\n// which case you might forget to pass it in when it is actually required.\nexport type RouteFunc<Params extends AnyParams> = (\n ...[params]: Params extends undefined ? readonly [] : readonly [Params]\n) => string;\n\n// A duplicate of the react-router RouteObject, but with routeRef added\nexport interface BackstageRouteObject {\n caseSensitive: boolean;\n children?: BackstageRouteObject[];\n element: React.ReactNode;\n path: string;\n routeRefs: Set<RouteRef>;\n plugin?: BackstagePlugin;\n}\n\nexport function isRouteRef<Params extends AnyParams>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, any>,\n): routeRef is RouteRef<Params> {\n return routeRef[routeRefType] === 'absolute';\n}\n\nexport function isSubRouteRef<Params extends AnyParams>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, any>,\n): routeRef is SubRouteRef<Params> {\n return routeRef[routeRefType] === 'sub';\n}\n\nexport function isExternalRouteRef<\n Params extends AnyParams,\n Optional extends boolean,\n>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, Optional>,\n): routeRef is ExternalRouteRef<Params, Optional> {\n return routeRef[routeRefType] === 'external';\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { generatePath, matchRoutes } from 'react-router-dom';\nimport {\n AnyRouteRef,\n BackstageRouteObject,\n AnyParams,\n RouteFunc,\n routeRefType,\n isRouteRef,\n isSubRouteRef,\n isExternalRouteRef,\n} from './types';\nimport {\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n} from '@backstage/core-plugin-api';\n\n// Joins a list of paths together, avoiding trailing and duplicate slashes\nfunction joinPaths(...paths: string[]): string {\n const normalized = paths.join('/').replace(/\\/\\/+/g, '/');\n if (normalized !== '/' && normalized.endsWith('/')) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\n/**\n * Resolves the absolute route ref that our target route ref is pointing pointing to, as well\n * as the relative target path.\n *\n * Returns an undefined target ref if one could not be fully resolved.\n */\nfunction resolveTargetRef(\n anyRouteRef: AnyRouteRef,\n routePaths: Map<RouteRef, string>,\n routeBindings: Map<AnyRouteRef, AnyRouteRef | undefined>,\n): readonly [RouteRef | undefined, string] {\n // First we figure out which absolute route ref we're dealing with, an if there was an sub route path to append.\n // For sub routes it will be the parent path, while for external routes it will be the bound route.\n let targetRef: RouteRef;\n let subRoutePath = '';\n if (isRouteRef(anyRouteRef)) {\n targetRef = anyRouteRef;\n } else if (isSubRouteRef(anyRouteRef)) {\n targetRef = anyRouteRef.parent;\n subRoutePath = anyRouteRef.path;\n } else if (isExternalRouteRef(anyRouteRef)) {\n const resolvedRoute = routeBindings.get(anyRouteRef);\n if (!resolvedRoute) {\n return [undefined, ''];\n }\n if (isRouteRef(resolvedRoute)) {\n targetRef = resolvedRoute;\n } else if (isSubRouteRef(resolvedRoute)) {\n targetRef = resolvedRoute.parent;\n subRoutePath = resolvedRoute.path;\n } else {\n throw new Error(\n `ExternalRouteRef was bound to invalid target, ${resolvedRoute}`,\n );\n }\n } else if (anyRouteRef[routeRefType]) {\n throw new Error(\n `Unknown or invalid route ref type, ${anyRouteRef[routeRefType]}`,\n );\n } else {\n throw new Error(`Unknown object passed to useRouteRef, got ${anyRouteRef}`);\n }\n\n // Bail if no absolute path could be resolved\n if (!targetRef) {\n return [undefined, ''];\n }\n\n // Find the path that our target route is bound to\n const resolvedPath = routePaths.get(targetRef);\n if (!resolvedPath) {\n return [undefined, ''];\n }\n\n // SubRouteRefs join the path from the parent route with its own path\n const targetPath = joinPaths(resolvedPath, subRoutePath);\n return [targetRef, targetPath];\n}\n\n/**\n * Resolves the complete base path for navigating to the target RouteRef.\n */\nfunction resolveBasePath(\n targetRef: RouteRef,\n sourceLocation: Parameters<typeof matchRoutes>[1],\n routePaths: Map<RouteRef, string>,\n routeParents: Map<RouteRef, RouteRef | undefined>,\n routeObjects: BackstageRouteObject[],\n) {\n // While traversing the app element tree we build up the routeObjects structure\n // used here. It is the same kind of structure that react-router creates, with the\n // addition that associated route refs are stored throughout the tree. This lets\n // us look up all route refs that can be reached from our source location.\n // Because of the similar route object structure, we can use `matchRoutes` from\n // react-router to do the lookup of our current location.\n const match = matchRoutes(routeObjects, sourceLocation) ?? [];\n\n // While we search for a common routing root between our current location and\n // the target route, we build a list of all route refs we find that we need\n // to traverse to reach the target.\n const refDiffList = Array<RouteRef>();\n\n let matchIndex = -1;\n for (\n let targetSearchRef: RouteRef | undefined = targetRef;\n targetSearchRef;\n targetSearchRef = routeParents.get(targetSearchRef)\n ) {\n // The match contains a list of all ancestral route refs present at our current location\n // Starting at the desired target ref and traversing back through its parents, we search\n // for a target ref that is present in the match for our current location. When a match\n // is found it means we have found a common base to resolve the route from.\n matchIndex = match.findIndex(m =>\n (m.route as BackstageRouteObject).routeRefs.has(targetSearchRef!),\n );\n if (matchIndex !== -1) {\n break;\n }\n\n // Every time we move a step up in the ancestry of the target ref, we add the current ref\n // to the diff list, which ends up being the list of route refs to traverse form the common base\n // in order to reach our target.\n refDiffList.unshift(targetSearchRef);\n }\n\n // If our target route is present in the initial match we need to construct the final path\n // from the parent of the matched route segment. That's to allow the caller of the route\n // function to supply their own params.\n if (refDiffList.length === 0) {\n matchIndex -= 1;\n }\n\n // This is the part of the route tree that the target and source locations have in common.\n // We re-use the existing pathname directly along with all params.\n const parentPath = matchIndex === -1 ? '' : match[matchIndex].pathname;\n\n // This constructs the mid section of the path using paths resolved from all route refs\n // we need to traverse to reach our target except for the very last one. None of these\n // paths are allowed to require any parameters, as the caller would have no way of knowing\n // what parameters those are.\n const diffPath = joinPaths(\n ...refDiffList.slice(0, -1).map(ref => {\n const path = routePaths.get(ref);\n if (!path) {\n throw new Error(`No path for ${ref}`);\n }\n if (path.includes(':')) {\n throw new Error(\n `Cannot route to ${targetRef} with parent ${ref} as it has parameters`,\n );\n }\n return path;\n }),\n );\n\n return parentPath + diffPath;\n}\n\nexport class RouteResolver {\n constructor(\n private readonly routePaths: Map<RouteRef, string>,\n private readonly routeParents: Map<RouteRef, RouteRef | undefined>,\n private readonly routeObjects: BackstageRouteObject[],\n private readonly routeBindings: Map<\n ExternalRouteRef,\n RouteRef | SubRouteRef\n >,\n private readonly appBasePath: string, // base path without a trailing slash\n ) {}\n\n resolve<Params extends AnyParams>(\n anyRouteRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, any>,\n sourceLocation: Parameters<typeof matchRoutes>[1],\n ): RouteFunc<Params> | undefined {\n // First figure out what our target absolute ref is, as well as our target path.\n const [targetRef, targetPath] = resolveTargetRef(\n anyRouteRef,\n this.routePaths,\n this.routeBindings,\n );\n if (!targetRef) {\n return undefined;\n }\n\n // The location that we get passed in uses the full path, so start by trimming off\n // the app base path prefix in case we're running the app on a sub-path.\n let relativeSourceLocation: Parameters<typeof matchRoutes>[1];\n if (typeof sourceLocation === 'string') {\n relativeSourceLocation = this.trimPath(sourceLocation);\n } else if (sourceLocation.pathname) {\n relativeSourceLocation = {\n ...sourceLocation,\n pathname: this.trimPath(sourceLocation.pathname),\n };\n } else {\n relativeSourceLocation = sourceLocation;\n }\n\n // Next we figure out the base path, which is the combination of the common parent path\n // between our current location and our target location, as well as the additional path\n // that is the difference between the parent path and the base of our target location.\n const basePath =\n this.appBasePath +\n resolveBasePath(\n targetRef,\n relativeSourceLocation,\n this.routePaths,\n this.routeParents,\n this.routeObjects,\n );\n\n const routeFunc: RouteFunc<Params> = (...[params]) => {\n return basePath + generatePath(targetPath, params);\n };\n return routeFunc;\n }\n\n private trimPath(targetPath: string) {\n if (!targetPath) {\n return targetPath;\n }\n\n if (targetPath.startsWith(this.appBasePath)) {\n return targetPath.slice(this.appBasePath.length);\n }\n return targetPath;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ReactNode } from 'react';\nimport {\n ExternalRouteRef,\n RouteRef,\n SubRouteRef,\n} from '@backstage/core-plugin-api';\nimport {\n createVersionedValueMap,\n createVersionedContext,\n} from '@backstage/version-bridge';\nimport { RouteResolver } from './RouteResolver';\nimport { BackstageRouteObject } from './types';\n\nconst RoutingContext =\n createVersionedContext<{ 1: RouteResolver }>('routing-context');\n\ntype ProviderProps = {\n routePaths: Map<RouteRef, string>;\n routeParents: Map<RouteRef, RouteRef | undefined>;\n routeObjects: BackstageRouteObject[];\n routeBindings: Map<ExternalRouteRef, RouteRef | SubRouteRef>;\n basePath?: string;\n children: ReactNode;\n};\n\nexport const RoutingProvider = ({\n routePaths,\n routeParents,\n routeObjects,\n routeBindings,\n basePath = '',\n children,\n}: ProviderProps) => {\n const resolver = new RouteResolver(\n routePaths,\n routeParents,\n routeObjects,\n routeBindings,\n basePath,\n );\n\n const versionedValue = createVersionedValueMap({ 1: resolver });\n return (\n <RoutingContext.Provider value={versionedValue}>\n {children}\n </RoutingContext.Provider>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect, useMemo } from 'react';\nimport { matchRoutes, useLocation } from 'react-router-dom';\nimport {\n useAnalytics,\n AnalyticsContext,\n CommonAnalyticsContext,\n RouteRef,\n} from '@backstage/core-plugin-api';\nimport { routeObjectCollector } from './collectors';\nimport {\n childDiscoverer,\n routeElementDiscoverer,\n traverseElementTree,\n} from '../extensions/traversal';\nimport { BackstageRouteObject } from './types';\n\n/**\n * Returns an extension context given the current pathname and a list of\n * Backstage route objects.\n */\nconst getExtensionContext = (\n pathname: string,\n routes: BackstageRouteObject[],\n): CommonAnalyticsContext | {} => {\n try {\n // Find matching routes for the given path name.\n const matches = matchRoutes(routes, { pathname }) as\n | { route: BackstageRouteObject }[]\n | null;\n\n // Of the matching routes, get the last (e.g. most specific) instance of\n // the BackstageRouteObject.\n const routeObject = matches\n ?.filter(match => match?.route.routeRefs?.size > 0)\n .pop()?.route;\n\n // If there is no route object, then allow inheritance of default context.\n if (!routeObject) {\n return {};\n }\n\n // If there is a single route ref, return it.\n // todo: get routeRef of rendered gathered mount point(?)\n let routeRef: RouteRef | undefined;\n if (routeObject.routeRefs.size === 1) {\n routeRef = routeObject.routeRefs.values().next().value;\n }\n\n return {\n extension: 'App',\n pluginId: routeObject.plugin?.getId() || 'root',\n ...(routeRef ? { routeRef: (routeRef as { id?: string }).id } : {}),\n };\n } catch {\n return {};\n }\n};\n\n/**\n * Performs the actual event capture on render.\n */\nconst TrackNavigation = ({\n pathname,\n search,\n hash,\n}: {\n pathname: string;\n search: string;\n hash: string;\n}) => {\n const analytics = useAnalytics();\n\n useEffect(() => {\n analytics.captureEvent('navigate', `${pathname}${search}${hash}`);\n }, [analytics, pathname, search, hash]);\n\n return null;\n};\n\n/**\n * Logs a \"navigate\" event with appropriate plugin-level analytics context\n * attributes each time the user navigates to a page.\n */\nexport const RouteTracker = ({ tree }: { tree: React.ReactNode }) => {\n const { pathname, search, hash } = useLocation();\n // todo(iamEAP): Work this into the existing traversal and make the data\n // available on the provider. Then grab from app instance on the router.\n const { routeObjects } = useMemo(() => {\n return traverseElementTree({\n root: tree,\n discoverers: [childDiscoverer, routeElementDiscoverer],\n collectors: {\n routeObjects: routeObjectCollector,\n },\n });\n }, [tree]);\n\n return (\n <AnalyticsContext attributes={getExtensionContext(pathname, routeObjects)}>\n <TrackNavigation pathname={pathname} search={search} hash={hash} />\n </AnalyticsContext>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AnyRouteRef } from './types';\n\nexport function validateRoutes(\n routePaths: Map<AnyRouteRef, string>,\n routeParents: Map<AnyRouteRef, AnyRouteRef | undefined>,\n) {\n const notLeafRoutes = new Set(routeParents.values());\n notLeafRoutes.delete(undefined);\n\n for (const route of routeParents.keys()) {\n if (notLeafRoutes.has(route)) {\n continue;\n }\n\n let currentRouteRef: AnyRouteRef | undefined = route;\n\n let fullPath = '';\n while (currentRouteRef) {\n const path = routePaths.get(currentRouteRef);\n if (!path) {\n throw new Error(`No path for ${currentRouteRef}`);\n }\n fullPath = `${path}${fullPath}`;\n currentRouteRef = routeParents.get(currentRouteRef);\n }\n\n const params = fullPath.match(/:(\\w+)/g);\n if (params) {\n for (let j = 0; j < params.length; j++) {\n for (let i = j + 1; i < params.length; i++) {\n if (params[i] === params[j]) {\n throw new Error(\n `Parameter ${params[i]} is duplicated in path ${fullPath}`,\n );\n }\n }\n }\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { PropsWithChildren } from 'react';\nimport {\n createVersionedValueMap,\n createVersionedContext,\n} from '@backstage/version-bridge';\nimport { AppContext as AppContextV1 } from './types';\n\nconst AppContext = createVersionedContext<{ 1: AppContextV1 }>('app-context');\n\ntype Props = {\n appContext: AppContextV1;\n};\n\nexport const AppContextProvider = ({\n appContext,\n children,\n}: PropsWithChildren<Props>) => {\n const versionedValue = createVersionedValueMap({ 1: appContext });\n\n return <AppContext.Provider value={versionedValue} children={children} />;\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n IdentityApi,\n ProfileInfo,\n BackstageUserIdentity,\n} from '@backstage/core-plugin-api';\n\nfunction mkError(thing: string) {\n return new Error(\n `Tried to access IdentityApi ${thing} before app was loaded`,\n );\n}\n\n/**\n * Implementation of the connection between the App-wide IdentityApi\n * and sign-in page.\n */\nexport class AppIdentityProxy implements IdentityApi {\n private target?: IdentityApi;\n\n // This is called by the app manager once the sign-in page provides us with an implementation\n setTarget(identityApi: IdentityApi) {\n this.target = identityApi;\n }\n\n getUserId(): string {\n if (!this.target) {\n throw mkError('getUserId');\n }\n return this.target.getUserId();\n }\n\n getProfile(): ProfileInfo {\n if (!this.target) {\n throw mkError('getProfile');\n }\n return this.target.getProfile();\n }\n\n async getProfileInfo(): Promise<ProfileInfo> {\n if (!this.target) {\n throw mkError('getProfileInfo');\n }\n return this.target.getProfileInfo();\n }\n\n async getBackstageIdentity(): Promise<BackstageUserIdentity> {\n if (!this.target) {\n throw mkError('getBackstageIdentity');\n }\n return this.target.getBackstageIdentity();\n }\n\n async getCredentials(): Promise<{ token?: string | undefined }> {\n if (!this.target) {\n throw mkError('getCredentials');\n }\n return this.target.getCredentials();\n }\n\n async getIdToken(): Promise<string | undefined> {\n if (!this.target) {\n throw mkError('getIdToken');\n }\n return this.target.getIdToken();\n }\n\n async signOut(): Promise<void> {\n if (!this.target) {\n throw mkError('signOut');\n }\n await this.target.signOut();\n location.reload();\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useMemo, useEffect, useState, PropsWithChildren } from 'react';\nimport { ThemeProvider } from '@material-ui/core/styles';\nimport CssBaseline from '@material-ui/core/CssBaseline';\nimport { useApi, appThemeApiRef, AppTheme } from '@backstage/core-plugin-api';\nimport { useObservable } from 'react-use';\n\n// This tries to find the most accurate match, but also falls back to less\n// accurate results in order to avoid errors.\nfunction resolveTheme(\n themeId: string | undefined,\n shouldPreferDark: boolean,\n themes: AppTheme[],\n) {\n if (themeId !== undefined) {\n const selectedTheme = themes.find(theme => theme.id === themeId);\n if (selectedTheme) {\n return selectedTheme;\n }\n }\n\n if (shouldPreferDark) {\n const darkTheme = themes.find(theme => theme.variant === 'dark');\n if (darkTheme) {\n return darkTheme;\n }\n }\n\n const lightTheme = themes.find(theme => theme.variant === 'light');\n if (lightTheme) {\n return lightTheme;\n }\n\n return themes[0];\n}\n\nconst useShouldPreferDarkTheme = () => {\n const mediaQuery = useMemo(\n () => window.matchMedia('(prefers-color-scheme: dark)'),\n [],\n );\n const [shouldPreferDark, setPrefersDark] = useState(mediaQuery.matches);\n\n useEffect(() => {\n const listener = (event: MediaQueryListEvent) => {\n setPrefersDark(event.matches);\n };\n mediaQuery.addListener(listener);\n return () => {\n mediaQuery.removeListener(listener);\n };\n }, [mediaQuery]);\n\n return shouldPreferDark;\n};\n\nexport function AppThemeProvider({ children }: PropsWithChildren<{}>) {\n const appThemeApi = useApi(appThemeApiRef);\n const themeId = useObservable(\n appThemeApi.activeThemeId$(),\n appThemeApi.getActiveThemeId(),\n );\n\n // Browser feature detection won't change over time, so ignore lint rule\n const shouldPreferDark = Boolean(window.matchMedia)\n ? useShouldPreferDarkTheme() // eslint-disable-line react-hooks/rules-of-hooks\n : false;\n\n const appTheme = resolveTheme(\n themeId,\n shouldPreferDark,\n appThemeApi.getInstalledThemes(),\n );\n if (!appTheme) {\n throw new Error('App has no themes');\n }\n\n if (appTheme.Provider) {\n return <appTheme.Provider children={children} />;\n }\n\n // eslint-disable-next-line no-console\n console.warn(\n \"DEPRECATION WARNING: A provided app theme is using the deprecated 'theme' property \" +\n 'and should be migrated to use a Provider instead. ' +\n 'See https://backstage.io/docs/api/deprecations#app-theme for more info.',\n );\n\n return (\n <ThemeProvider theme={appTheme.theme}>\n <CssBaseline>{children}</CssBaseline>\n </ThemeProvider>\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AppConfig } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\nimport { AppConfigLoader } from './types';\n\n/**\n * The default config loader, which expects that config is available at compile-time\n * in `process.env.APP_CONFIG`. APP_CONFIG should be an array of config objects as\n * returned by the config loader.\n *\n * It will also load runtime config from the __APP_INJECTED_RUNTIME_CONFIG__ string,\n * which can be rewritten at runtime to contain an additional JSON config object.\n * If runtime config is present, it will be placed first in the config array, overriding\n * other config values.\n *\n * @public\n */\nexport const defaultConfigLoader: AppConfigLoader = async (\n // This string may be replaced at runtime to provide additional config.\n // It should be replaced by a JSON-serialized config object.\n // It's a param so we can test it, but at runtime this will always fall back to default.\n runtimeConfigJson: string = '__APP_INJECTED_RUNTIME_CONFIG__',\n) => {\n const appConfig = process.env.APP_CONFIG;\n if (!appConfig) {\n throw new Error('No static configuration provided');\n }\n if (!Array.isArray(appConfig)) {\n throw new Error('Static configuration has invalid format');\n }\n const configs = appConfig.slice() as unknown as AppConfig[];\n\n // Avoiding this string also being replaced at runtime\n if (\n runtimeConfigJson !==\n '__app_injected_runtime_config__'.toLocaleUpperCase('en-US')\n ) {\n try {\n const data = JSON.parse(runtimeConfigJson) as JsonObject;\n if (Array.isArray(data)) {\n configs.push(...data);\n } else {\n configs.push({ data, context: 'env' });\n }\n } catch (error) {\n throw new Error(`Failed to load runtime configuration, ${error}`);\n }\n }\n\n const windowAppConfig = (window as any).__APP_CONFIG__;\n if (windowAppConfig) {\n configs.push({\n context: 'window',\n data: windowAppConfig,\n });\n }\n return configs;\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport React, {\n ComponentType,\n PropsWithChildren,\n ReactElement,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport { Route, Routes } from 'react-router-dom';\nimport { useAsync } from 'react-use';\nimport {\n ApiProvider,\n ApiRegistry,\n AppThemeSelector,\n ConfigReader,\n LocalStorageFeatureFlags,\n} from '../apis';\nimport {\n useApi,\n AnyApiFactory,\n ApiHolder,\n IconComponent,\n AppTheme,\n appThemeApiRef,\n configApiRef,\n AppThemeApi,\n ConfigApi,\n featureFlagsApiRef,\n IdentityApi,\n identityApiRef,\n BackstagePlugin,\n RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n} from '@backstage/core-plugin-api';\nimport { UserIdentity } from '@backstage/core-components';\nimport { ApiFactoryRegistry, ApiResolver } from '../apis/system';\nimport {\n childDiscoverer,\n routeElementDiscoverer,\n traverseElementTree,\n} from '../extensions/traversal';\nimport { pluginCollector } from '../plugins/collectors';\nimport {\n featureFlagCollector,\n routeObjectCollector,\n routeParentCollector,\n routePathCollector,\n} from '../routing/collectors';\nimport { RoutingProvider } from '../routing/RoutingProvider';\nimport { RouteTracker } from '../routing/RouteTracker';\nimport { validateRoutes } from '../routing/validation';\nimport { AppContextProvider } from './AppContext';\nimport { AppIdentityProxy } from '../apis/implementations/IdentityApi/AppIdentityProxy';\nimport {\n AppComponents,\n AppConfigLoader,\n AppContext,\n AppOptions,\n AppRouteBinder,\n BackstageApp,\n SignInPageProps,\n} from './types';\nimport { AppThemeProvider } from './AppThemeProvider';\nimport { defaultConfigLoader } from './defaultConfigLoader';\n\nexport function generateBoundRoutes(bindRoutes: AppOptions['bindRoutes']) {\n const result = new Map<ExternalRouteRef, RouteRef | SubRouteRef>();\n\n if (bindRoutes) {\n const bind: AppRouteBinder = (\n externalRoutes,\n targetRoutes: { [name: string]: RouteRef | SubRouteRef },\n ) => {\n for (const [key, value] of Object.entries(targetRoutes)) {\n const externalRoute = externalRoutes[key];\n if (!externalRoute) {\n throw new Error(`Key ${key} is not an existing external route`);\n }\n if (!value && !externalRoute.optional) {\n throw new Error(\n `External route ${key} is required but was undefined`,\n );\n }\n if (value) {\n result.set(externalRoute, value);\n }\n }\n };\n bindRoutes({ bind });\n }\n\n return result;\n}\n\n/**\n * Get the app base path from the configured app baseUrl.\n *\n * The returned path does not have a trailing slash.\n */\nfunction getBasePath(configApi: Config) {\n let { pathname } = new URL(\n configApi.getOptionalString('app.baseUrl') ?? '/',\n 'http://dummy.dev', // baseUrl can be specified as just a path\n );\n pathname = pathname.replace(/\\/*$/, '');\n return pathname;\n}\n\nfunction useConfigLoader(\n configLoader: AppConfigLoader | undefined,\n components: AppComponents,\n appThemeApi: AppThemeApi,\n): { api: ConfigApi } | { node: JSX.Element } {\n // Keeping this synchronous when a config loader isn't set simplifies tests a lot\n const hasConfig = Boolean(configLoader);\n const config = useAsync(configLoader || (() => Promise.resolve([])));\n\n let noConfigNode = undefined;\n\n if (hasConfig && config.loading) {\n const { Progress } = components;\n noConfigNode = <Progress />;\n } else if (config.error) {\n const { BootErrorPage } = components;\n noConfigNode = <BootErrorPage step=\"load-config\" error={config.error} />;\n }\n\n const { ThemeProvider = AppThemeProvider } = components;\n\n // Before the config is loaded we can't use a router, so exit early\n if (noConfigNode) {\n return {\n node: (\n <ApiProvider apis={ApiRegistry.from([[appThemeApiRef, appThemeApi]])}>\n <ThemeProvider>{noConfigNode}</ThemeProvider>\n </ApiProvider>\n ),\n };\n }\n\n const configReader = ConfigReader.fromConfigs(config.value ?? []);\n\n return { api: configReader };\n}\n\nclass AppContextImpl implements AppContext {\n constructor(private readonly app: AppManager) {}\n\n getPlugins(): BackstagePlugin<any, any>[] {\n return this.app.getPlugins();\n }\n\n getSystemIcon(key: string): IconComponent | undefined {\n return this.app.getSystemIcon(key);\n }\n\n getComponents(): AppComponents {\n return this.app.getComponents();\n }\n}\n\nexport class AppManager implements BackstageApp {\n private apiHolder?: ApiHolder;\n private configApi?: ConfigApi;\n\n private readonly apis: Iterable<AnyApiFactory>;\n private readonly icons: NonNullable<AppOptions['icons']>;\n private readonly plugins: Set<BackstagePlugin<any, any>>;\n private readonly components: AppComponents;\n private readonly themes: AppTheme[];\n private readonly configLoader?: AppConfigLoader;\n private readonly defaultApis: Iterable<AnyApiFactory>;\n private readonly bindRoutes: AppOptions['bindRoutes'];\n\n private readonly appIdentityProxy = new AppIdentityProxy();\n private readonly apiFactoryRegistry: ApiFactoryRegistry;\n\n constructor(options: AppOptions) {\n this.apis = options.apis ?? [];\n this.icons = options.icons;\n this.plugins = new Set(\n (options.plugins as BackstagePlugin<any, any>[]) ?? [],\n );\n this.components = options.components;\n this.themes = options.themes as AppTheme[];\n this.configLoader = options.configLoader ?? defaultConfigLoader;\n this.defaultApis = options.defaultApis ?? [];\n this.bindRoutes = options.bindRoutes;\n this.apiFactoryRegistry = new ApiFactoryRegistry();\n }\n\n getPlugins(): BackstagePlugin<any, any>[] {\n return Array.from(this.plugins);\n }\n\n getSystemIcon(key: string): IconComponent | undefined {\n return this.icons[key];\n }\n\n getComponents(): AppComponents {\n return this.components;\n }\n\n getProvider(): ComponentType<{}> {\n const appContext = new AppContextImpl(this);\n\n const Provider = ({ children }: PropsWithChildren<{}>) => {\n const appThemeApi = useMemo(\n () => AppThemeSelector.createWithStorage(this.themes),\n [],\n );\n\n const { routePaths, routeParents, routeObjects, featureFlags } =\n useMemo(() => {\n const result = traverseElementTree({\n root: children,\n discoverers: [childDiscoverer, routeElementDiscoverer],\n collectors: {\n routePaths: routePathCollector,\n routeParents: routeParentCollector,\n routeObjects: routeObjectCollector,\n collectedPlugins: pluginCollector,\n featureFlags: featureFlagCollector,\n },\n });\n\n validateRoutes(result.routePaths, result.routeParents);\n\n // TODO(Rugvip): Restructure the public API so that we can get an immediate view of\n // the app, rather than having to wait for the provider to render.\n // For now we need to push the additional plugins we find during\n // collection and then make sure we initialize things afterwards.\n result.collectedPlugins.forEach(plugin => this.plugins.add(plugin));\n this.verifyPlugins(this.plugins);\n\n // Initialize APIs once all plugins are available\n this.getApiHolder();\n return result;\n }, [children]);\n\n const loadedConfig = useConfigLoader(\n this.configLoader,\n this.components,\n appThemeApi,\n );\n\n const hasConfigApi = 'api' in loadedConfig;\n if (hasConfigApi) {\n const { api } = loadedConfig as { api: Config };\n this.configApi = api;\n }\n\n useEffect(() => {\n if (hasConfigApi) {\n const featureFlagsApi = this.getApiHolder().get(featureFlagsApiRef)!;\n\n for (const plugin of this.plugins.values()) {\n if ('getFeatureFlags' in plugin) {\n for (const flag of plugin.getFeatureFlags()) {\n featureFlagsApi.registerFlag({\n name: flag.name,\n pluginId: plugin.getId(),\n });\n }\n } else {\n for (const output of plugin.output()) {\n switch (output.type) {\n case 'feature-flag': {\n featureFlagsApi.registerFlag({\n name: output.name,\n pluginId: plugin.getId(),\n });\n break;\n }\n default:\n break;\n }\n }\n }\n }\n\n // Go through the featureFlags returned from the traversal and\n // register those now the configApi has been loaded\n for (const name of featureFlags) {\n featureFlagsApi.registerFlag({ name, pluginId: '' });\n }\n }\n }, [hasConfigApi, loadedConfig, featureFlags]);\n\n if ('node' in loadedConfig) {\n // Loading or error\n return loadedConfig.node;\n }\n\n const { ThemeProvider = AppThemeProvider } = this.components;\n\n return (\n <ApiProvider apis={this.getApiHolder()}>\n <AppContextProvider appContext={appContext}>\n <ThemeProvider>\n <RoutingProvider\n routePaths={routePaths}\n routeParents={routeParents}\n routeObjects={routeObjects}\n routeBindings={generateBoundRoutes(this.bindRoutes)}\n basePath={getBasePath(loadedConfig.api)}\n >\n {children}\n </RoutingProvider>\n </ThemeProvider>\n </AppContextProvider>\n </ApiProvider>\n );\n };\n return Provider;\n }\n\n getRouter(): ComponentType<{}> {\n const { Router: RouterComponent, SignInPage: SignInPageComponent } =\n this.components;\n\n // This wraps the sign-in page and waits for sign-in to be completed before rendering the app\n const SignInPageWrapper = ({\n component: Component,\n children,\n }: {\n component: ComponentType<SignInPageProps>;\n children: ReactElement;\n }) => {\n const [identityApi, setIdentityApi] = useState<IdentityApi>();\n\n if (!identityApi) {\n return <Component onSignInSuccess={setIdentityApi} />;\n }\n\n this.appIdentityProxy.setTarget(identityApi);\n return children;\n };\n\n const AppRouter = ({ children }: PropsWithChildren<{}>) => {\n const configApi = useApi(configApiRef);\n const mountPath = `${getBasePath(configApi)}/*`;\n\n // If the app hasn't configured a sign-in page, we just continue as guest.\n if (!SignInPageComponent) {\n this.appIdentityProxy.setTarget(UserIdentity.createGuest());\n\n return (\n <RouterComponent>\n <RouteTracker tree={children} />\n <Routes>\n <Route path={mountPath} element={<>{children}</>} />\n </Routes>\n </RouterComponent>\n );\n }\n\n return (\n <RouterComponent>\n <RouteTracker tree={children} />\n <SignInPageWrapper component={SignInPageComponent}>\n <Routes>\n <Route path={mountPath} element={<>{children}</>} />\n </Routes>\n </SignInPageWrapper>\n </RouterComponent>\n );\n };\n\n return AppRouter;\n }\n\n private getApiHolder(): ApiHolder {\n if (this.apiHolder) {\n // Register additional plugins if they have been added.\n // Routes paths, objects and others are already updated in the provider when children of it change\n for (const plugin of this.plugins) {\n for (const factory of plugin.getApis()) {\n if (!this.apiFactoryRegistry.get(factory.api)) {\n this.apiFactoryRegistry.register('default', factory);\n }\n }\n }\n ApiResolver.validateFactories(\n this.apiFactoryRegistry,\n this.apiFactoryRegistry.getAllApis(),\n );\n return this.apiHolder;\n }\n this.apiFactoryRegistry.register('static', {\n api: appThemeApiRef,\n deps: {},\n factory: () => AppThemeSelector.createWithStorage(this.themes),\n });\n this.apiFactoryRegistry.register('static', {\n api: configApiRef,\n deps: {},\n factory: () => {\n if (!this.configApi) {\n throw new Error(\n 'Tried to access config API before config was loaded',\n );\n }\n return this.configApi;\n },\n });\n this.apiFactoryRegistry.register('static', {\n api: identityApiRef,\n deps: {},\n factory: () => this.appIdentityProxy,\n });\n\n // It's possible to replace the feature flag API, but since we must have at least\n // one implementation we add it here directly instead of through the defaultApis.\n this.apiFactoryRegistry.register('default', {\n api: featureFlagsApiRef,\n deps: {},\n factory: () => new LocalStorageFeatureFlags(),\n });\n for (const factory of this.defaultApis) {\n this.apiFactoryRegistry.register('default', factory);\n }\n\n for (const plugin of this.plugins) {\n for (const factory of plugin.getApis()) {\n if (!this.apiFactoryRegistry.register('default', factory)) {\n throw new Error(\n `Plugin ${plugin.getId()} tried to register duplicate or forbidden API factory for ${\n factory.api\n }`,\n );\n }\n }\n }\n\n for (const factory of this.apis) {\n if (!this.apiFactoryRegistry.register('app', factory)) {\n throw new Error(\n `Duplicate or forbidden API factory for ${factory.api} in app`,\n );\n }\n }\n\n ApiResolver.validateFactories(\n this.apiFactoryRegistry,\n this.apiFactoryRegistry.getAllApis(),\n );\n\n this.apiHolder = new ApiResolver(this.apiFactoryRegistry);\n return this.apiHolder;\n }\n\n private verifyPlugins(plugins: Iterable<BackstagePlugin>) {\n const pluginIds = new Set<string>();\n\n for (const plugin of plugins) {\n const id = plugin.getId();\n if (pluginIds.has(id)) {\n throw new Error(`Duplicate plugin found '${id}'`);\n }\n pluginIds.add(id);\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AppManager } from './AppManager';\nimport { AppOptions, BackstageApp } from './types';\n\n/**\n * Creates a new Backstage App where the full set of options are required.\n *\n * @public\n * @param options - A set of options for creating the app\n * @returns\n */\nexport function createSpecializedApp(options: AppOptions): BackstageApp {\n return new AppManager(options);\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ReactNode } from 'react';\nimport { useRoutes } from 'react-router-dom';\nimport { useApp, useElementFilter } from '@backstage/core-plugin-api';\n\ntype RouteObject = {\n path: string;\n element: ReactNode;\n children?: RouteObject[];\n};\n\n/**\n * Props for the {@link FlatRoutes} component.\n *\n * @public\n */\nexport type FlatRoutesProps = {\n children: ReactNode;\n};\n\n/**\n * A wrapper around a set of routes.\n *\n * @remarks\n *\n * The root of the routing hierarchy in your app should use this component,\n * instead of the one from `react-router-dom`. This ensures that all of the\n * plugin route and utility API wiring happens under the hood.\n *\n * @public\n */\nexport const FlatRoutes = (props: FlatRoutesProps): JSX.Element | null => {\n const app = useApp();\n const { NotFoundErrorPage } = app.getComponents();\n const routes = useElementFilter(props.children, elements =>\n elements\n .getElements<{ path?: string; children: ReactNode }>()\n .flatMap<RouteObject>(child => {\n let path = child.props.path;\n\n // TODO(Rugvip): Work around plugins registering empty paths, remove once deprecated routes are gone\n if (path === '') {\n return [];\n }\n path = path?.replace(/\\/\\*$/, '') ?? '/';\n\n return [\n {\n path,\n element: child,\n children: child.props.children\n ? [\n // These are the children of each route, which we all add in under a catch-all\n // subroute in order to make them available to `useOutlet`\n {\n path: path === '/' ? '/' : '/*', // The root path must require an exact match\n element: child.props.children,\n },\n ]\n : undefined,\n },\n ];\n })\n // Routes are sorted to work around a bug where prefixes are unexpectedly matched\n .sort((a, b) => b.path.localeCompare(a.path))\n // We make sure all routes have '/*' appended, except '/'\n .map(obj => {\n obj.path = obj.path === '/' ? '/' : `${obj.path}/*`;\n return obj;\n }),\n );\n\n // TODO(Rugvip): Possibly add a way to skip this, like a noNotFoundPage prop\n routes.push({\n element: <NotFoundErrorPage />,\n path: '/*',\n });\n\n return useRoutes(routes);\n};\n"],"names":["hasScopes","DEFAULT_PROVIDER","SCOPE_PREFIX","createDefaultApp"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/apis/system/ApiAggregator.ts","../src/apis/system/ApiProvider.tsx","../src/apis/system/ApiRegistry.ts","../src/apis/system/ApiResolver.ts","../src/apis/system/ApiFactoryRegistry.ts","../src/lib/loginPopup.ts","../src/lib/AuthConnector/DefaultAuthConnector.ts","../src/lib/AuthConnector/DirectAuthConnector.ts","../src/lib/AuthSessionManager/common.ts","../src/lib/subjects.ts","../src/lib/AuthSessionManager/SessionStateTracker.ts","../src/lib/AuthSessionManager/RefreshingAuthSessionManager.ts","../src/lib/AuthSessionManager/StaticAuthSessionManager.ts","../src/lib/AuthSessionManager/AuthSessionStore.ts","../src/lib/AuthSessionManager/OptionalRefreshSessionManagerMux.ts","../src/apis/implementations/auth/github/GithubAuth.ts","../src/apis/implementations/auth/oauth2/OAuth2.ts","../src/apis/implementations/auth/gitlab/GitlabAuth.ts","../src/apis/implementations/auth/google/GoogleAuth.ts","../src/apis/implementations/auth/okta/OktaAuth.ts","../src/apis/implementations/auth/saml/SamlAuth.ts","../src/apis/implementations/auth/auth0/Auth0Auth.ts","../src/apis/implementations/auth/microsoft/MicrosoftAuth.ts","../src/apis/implementations/auth/onelogin/OneLoginAuth.ts","../src/apis/implementations/auth/bitbucket/BitbucketAuth.ts","../src/apis/implementations/auth/atlassian/AtlassianAuth.ts","../src/apis/implementations/AlertApi/AlertApiForwarder.ts","../src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts","../src/apis/implementations/AppThemeApi/AppThemeSelector.ts","../src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.ts","../src/apis/implementations/ErrorApi/ErrorAlerter.ts","../src/apis/implementations/ErrorApi/ErrorApiForwarder.ts","../src/apis/implementations/ErrorApi/UnhandledErrorForwarder.ts","../src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.tsx","../src/apis/implementations/OAuthRequestApi/OAuthPendingRequests.ts","../src/apis/implementations/OAuthRequestApi/OAuthRequestManager.ts","../src/apis/implementations/StorageApi/WebStorage.ts","../src/app/createApp.tsx","../src/extensions/traversal.ts","../src/plugins/collectors.ts","../src/routing/FeatureFlagged.tsx","../src/routing/collectors.tsx","../src/routing/types.ts","../src/routing/RouteResolver.ts","../src/routing/RoutingProvider.tsx","../src/routing/RouteTracker.tsx","../src/routing/validation.ts","../src/app/AppContext.tsx","../src/apis/implementations/IdentityApi/AppIdentityProxy.ts","../src/app/AppThemeProvider.tsx","../src/app/defaultConfigLoader.ts","../src/app/AppManager.tsx","../src/app/createSpecializedApp.tsx","../src/routing/FlatRoutes.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ApiRef, ApiHolder } from '@backstage/core-plugin-api';\n\n/**\n * An ApiHolder that queries multiple other holders from for\n * an Api implementation, returning the first one encountered..\n */\nexport class ApiAggregator implements ApiHolder {\n private readonly holders: ApiHolder[];\n\n constructor(...holders: ApiHolder[]) {\n this.holders = holders;\n }\n\n get<T>(apiRef: ApiRef<T>): T | undefined {\n for (const holder of this.holders) {\n const api = holder.get(apiRef);\n if (api) {\n return api;\n }\n }\n return undefined;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useContext, ReactNode, PropsWithChildren } from 'react';\nimport PropTypes from 'prop-types';\nimport { ApiHolder } from '@backstage/core-plugin-api';\nimport { ApiAggregator } from './ApiAggregator';\nimport {\n createVersionedValueMap,\n createVersionedContext,\n} from '@backstage/version-bridge';\n\n/**\n * Prop types for the ApiProvider component.\n * @public\n */\nexport type ApiProviderProps = {\n apis: ApiHolder;\n children: ReactNode;\n};\n\nconst ApiContext = createVersionedContext<{ 1: ApiHolder }>('api-context');\n\n/**\n * Provides an {@link @backstage/core-plugin-api#ApiHolder} for consumption in\n * the React tree.\n *\n * @public\n */\nexport const ApiProvider = (props: PropsWithChildren<ApiProviderProps>) => {\n const { apis, children } = props;\n const parentHolder = useContext(ApiContext)?.atVersion(1);\n const holder = parentHolder ? new ApiAggregator(apis, parentHolder) : apis;\n\n return (\n <ApiContext.Provider\n value={createVersionedValueMap({ 1: holder })}\n children={children}\n />\n );\n};\n\nApiProvider.propTypes = {\n apis: PropTypes.shape({ get: PropTypes.func.isRequired }).isRequired,\n children: PropTypes.node,\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ApiRef, ApiHolder } from '@backstage/core-plugin-api';\n\ntype ApiImpl<T = unknown> = readonly [ApiRef<T>, T];\n\nclass ApiRegistryBuilder {\n private apis: [string, unknown][] = [];\n\n add<T, I extends T>(api: ApiRef<T>, impl: I): I {\n this.apis.push([api.id, impl]);\n return impl;\n }\n\n build(): ApiRegistry {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n return new ApiRegistry(new Map(this.apis));\n }\n}\n\n/**\n * A registry for utility APIs.\n *\n * @public\n * @deprecated Will be removed, use {@link @backstage/test-utils#TestApiProvider} or {@link @backstage/test-utils#TestApiRegistry} instead.\n */\nexport class ApiRegistry implements ApiHolder {\n static builder() {\n return new ApiRegistryBuilder();\n }\n\n /**\n * Creates a new ApiRegistry with a list of API implementations.\n *\n * @param apis - A list of pairs mapping an ApiRef to its respective implementation\n */\n static from(apis: ApiImpl[]) {\n return new ApiRegistry(new Map(apis.map(([api, impl]) => [api.id, impl])));\n }\n\n /**\n * Creates a new ApiRegistry with a single API implementation.\n *\n * @param api - ApiRef for the API to add\n * @param impl - Implementation of the API to add\n */\n static with<T>(api: ApiRef<T>, impl: T): ApiRegistry {\n return new ApiRegistry(new Map([[api.id, impl]]));\n }\n\n constructor(private readonly apis: Map<string, unknown>) {}\n\n /**\n * Returns a new ApiRegistry with the provided API added to the existing ones.\n *\n * @param api - ApiRef for the API to add\n * @param impl - Implementation of the API to add\n */\n with<T>(api: ApiRef<T>, impl: T): ApiRegistry {\n return new ApiRegistry(new Map([...this.apis, [api.id, impl]]));\n }\n\n get<T>(api: ApiRef<T>): T | undefined {\n return this.apis.get(api.id) as T | undefined;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ApiRef,\n ApiHolder,\n AnyApiRef,\n TypesToApiRefs,\n} from '@backstage/core-plugin-api';\nimport { ApiFactoryHolder } from './types';\n\n/**\n * Handles the actual on-demand instantiation and memoization of APIs out of\n * an {@link ApiFactoryHolder}.\n *\n * @public\n */\nexport class ApiResolver implements ApiHolder {\n /**\n * Validate factories by making sure that each of the apis can be created\n * without hitting any circular dependencies.\n */\n static validateFactories(\n factories: ApiFactoryHolder,\n apis: Iterable<AnyApiRef>,\n ) {\n for (const api of apis) {\n const heap = [api];\n const allDeps = new Set<AnyApiRef>();\n\n while (heap.length) {\n const apiRef = heap.shift()!;\n const factory = factories.get(apiRef);\n if (!factory) {\n continue;\n }\n\n for (const dep of Object.values(factory.deps)) {\n if (dep.id === api.id) {\n throw new Error(`Circular dependency of api factory for ${api}`);\n }\n if (!allDeps.has(dep)) {\n allDeps.add(dep);\n heap.push(dep);\n }\n }\n }\n }\n }\n\n private readonly apis = new Map<string, unknown>();\n\n constructor(private readonly factories: ApiFactoryHolder) {}\n\n get<T>(ref: ApiRef<T>): T | undefined {\n return this.load(ref);\n }\n\n private load<T>(ref: ApiRef<T>, loading: AnyApiRef[] = []): T | undefined {\n const impl = this.apis.get(ref.id);\n if (impl) {\n return impl as T;\n }\n\n const factory = this.factories.get(ref);\n if (!factory) {\n return undefined;\n }\n\n if (loading.includes(factory.api)) {\n throw new Error(`Circular dependency of api factory for ${factory.api}`);\n }\n\n const deps = this.loadDeps(ref, factory.deps, [...loading, factory.api]);\n const api = factory.factory(deps);\n this.apis.set(ref.id, api);\n return api as T;\n }\n\n private loadDeps<T>(\n dependent: ApiRef<unknown>,\n apis: TypesToApiRefs<T>,\n loading: AnyApiRef[],\n ): T {\n const impls = {} as T;\n\n for (const key in apis) {\n if (apis.hasOwnProperty(key)) {\n const ref = apis[key];\n\n const api = this.load(ref, loading);\n if (!api) {\n throw new Error(\n `No API factory available for dependency ${ref} of dependent ${dependent}`,\n );\n }\n impls[key] = api;\n }\n }\n\n return impls;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ApiFactoryHolder } from './types';\nimport {\n ApiRef,\n ApiFactory,\n AnyApiRef,\n AnyApiFactory,\n} from '@backstage/core-plugin-api';\n\n/**\n * Scope type when registering API factories.\n * @public\n */\nexport type ApiFactoryScope =\n | 'default' // Default factories registered by core and plugins\n | 'app' // Factories registered in the app, overriding default ones\n | 'static'; // APIs that can't be overridden, e.g. config\n\nenum ScopePriority {\n default = 10,\n app = 50,\n static = 100,\n}\n\ntype FactoryTuple = {\n priority: number;\n factory: AnyApiFactory;\n};\n\n/**\n * ApiFactoryRegistry is an ApiFactoryHolder implementation that enables\n * registration of API Factories with different scope.\n *\n * Each scope has an assigned priority, where factories registered with\n * higher priority scopes override ones with lower priority.\n *\n * @public\n */\nexport class ApiFactoryRegistry implements ApiFactoryHolder {\n private readonly factories = new Map<string, FactoryTuple>();\n\n /**\n * Register a new API factory. Returns true if the factory was added\n * to the registry.\n *\n * A factory will not be added to the registry if there is already\n * an existing factory with the same or higher priority.\n */\n register<Api, Impl extends Api, Deps extends { [name in string]: unknown }>(\n scope: ApiFactoryScope,\n factory: ApiFactory<Api, Impl, Deps>,\n ) {\n const priority = ScopePriority[scope];\n const existing = this.factories.get(factory.api.id);\n if (existing && existing.priority >= priority) {\n return false;\n }\n\n this.factories.set(factory.api.id, { priority, factory });\n return true;\n }\n\n get<T>(\n api: ApiRef<T>,\n ): ApiFactory<T, T, { [x: string]: unknown }> | undefined {\n const tuple = this.factories.get(api.id);\n if (!tuple) {\n return undefined;\n }\n return tuple.factory as ApiFactory<T, T, { [x: string]: unknown }>;\n }\n\n getAllApis(): Set<AnyApiRef> {\n const refs = new Set<AnyApiRef>();\n for (const { factory } of this.factories.values()) {\n refs.add(factory.api);\n }\n return refs;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Options used to open a login popup.\n */\nexport type LoginPopupOptions = {\n /**\n * The URL that the auth popup should point to\n */\n url: string;\n\n /**\n * The name of the popup, as in second argument to window.open\n */\n name: string;\n\n /**\n * The origin of the final popup page that will post a message to this window.\n */\n origin: string;\n\n /**\n * The width of the popup in pixels, defaults to 500\n */\n width?: number;\n\n /**\n * The height of the popup in pixels, defaults to 700\n */\n height?: number;\n};\n\ntype AuthResult =\n | {\n type: 'authorization_response';\n response: unknown;\n }\n | {\n type: 'authorization_response';\n error: {\n name: string;\n message: string;\n };\n };\n\n/**\n * Show a popup pointing to a URL that starts an auth flow. Implementing the receiving\n * end of the postMessage mechanism outlined in https://tools.ietf.org/html/draft-sakimura-oauth-wmrm-00\n *\n * The redirect handler of the flow should use postMessage to communicate back\n * to the app window. The message posted to the app must match the AuthResult type.\n *\n * The returned promise resolves to the response of the message that was posted from the auth popup.\n */\nexport function showLoginPopup(options: LoginPopupOptions): Promise<any> {\n return new Promise((resolve, reject) => {\n const width = options.width || 500;\n const height = options.height || 700;\n const left = window.screen.width / 2 - width / 2;\n const top = window.screen.height / 2 - height / 2;\n\n const popup = window.open(\n options.url,\n options.name,\n `menubar=no,location=no,resizable=no,scrollbars=no,status=no,width=${width},height=${height},top=${top},left=${left}`,\n );\n\n let targetOrigin = '';\n\n if (!popup || typeof popup.closed === 'undefined' || popup.closed) {\n const error = new Error('Failed to open auth popup.');\n error.name = 'PopupRejectedError';\n reject(error);\n return;\n }\n\n const messageListener = (event: MessageEvent) => {\n if (event.source !== popup) {\n return;\n }\n if (event.origin !== options.origin) {\n return;\n }\n const { data } = event;\n\n if (data.type === 'config_info') {\n targetOrigin = data.targetOrigin;\n return;\n }\n\n if (data.type !== 'authorization_response') {\n return;\n }\n const authResult = data as AuthResult;\n\n if ('error' in authResult) {\n const error = new Error(authResult.error.message);\n error.name = authResult.error.name;\n // TODO: proper error type\n // error.extra = authResult.error.extra;\n reject(error);\n } else {\n resolve(authResult.response);\n }\n done();\n };\n\n const intervalId = setInterval(() => {\n if (popup.closed) {\n const errMessage = `Login failed, ${\n targetOrigin && targetOrigin !== window.location.origin\n ? `Incorrect app origin, expected ${targetOrigin}`\n : 'popup was closed'\n }`;\n const error = new Error(errMessage);\n error.name = 'PopupClosedError';\n reject(error);\n done();\n }\n }, 100);\n\n function done() {\n window.removeEventListener('message', messageListener);\n clearInterval(intervalId);\n }\n\n window.addEventListener('message', messageListener);\n });\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n OAuthRequester,\n OAuthRequestApi,\n AuthProviderInfo,\n DiscoveryApi,\n} from '@backstage/core-plugin-api';\nimport { showLoginPopup } from '../loginPopup';\nimport { AuthConnector, CreateSessionOptions } from './types';\n\ntype Options<AuthSession> = {\n /**\n * DiscoveryApi instance used to locate the auth backend endpoint.\n */\n discoveryApi: DiscoveryApi;\n /**\n * Environment hint passed on to auth backend, for example 'production' or 'development'\n */\n environment: string;\n /**\n * Information about the auth provider to be shown to the user.\n * The ID Must match the backend auth plugin configuration, for example 'google'.\n */\n provider: AuthProviderInfo;\n /**\n * API used to instantiate an auth requester.\n */\n oauthRequestApi: OAuthRequestApi;\n /**\n * Function used to join together a set of scopes, defaults to joining with a space character.\n */\n joinScopes?: (scopes: Set<string>) => string;\n /**\n * Function used to transform an auth response into the session type.\n */\n sessionTransform?(response: any): AuthSession | Promise<AuthSession>;\n};\n\nfunction defaultJoinScopes(scopes: Set<string>) {\n return [...scopes].join(' ');\n}\n\n/**\n * DefaultAuthConnector is the default auth connector in Backstage. It talks to the\n * backend auth plugin through the standardized API, and requests user permission\n * via the OAuthRequestApi.\n */\nexport class DefaultAuthConnector<AuthSession>\n implements AuthConnector<AuthSession>\n{\n private readonly discoveryApi: DiscoveryApi;\n private readonly environment: string;\n private readonly provider: AuthProviderInfo;\n private readonly joinScopesFunc: (scopes: Set<string>) => string;\n private readonly authRequester: OAuthRequester<AuthSession>;\n private readonly sessionTransform: (response: any) => Promise<AuthSession>;\n\n constructor(options: Options<AuthSession>) {\n const {\n discoveryApi,\n environment,\n provider,\n joinScopes = defaultJoinScopes,\n oauthRequestApi,\n sessionTransform = id => id,\n } = options;\n\n this.authRequester = oauthRequestApi.createAuthRequester({\n provider,\n onAuthRequest: scopes => this.showPopup(scopes),\n });\n\n this.discoveryApi = discoveryApi;\n this.environment = environment;\n this.provider = provider;\n this.joinScopesFunc = joinScopes;\n this.sessionTransform = sessionTransform;\n }\n\n async createSession(options: CreateSessionOptions): Promise<AuthSession> {\n if (options.instantPopup) {\n return this.showPopup(options.scopes);\n }\n return this.authRequester(options.scopes);\n }\n\n async refreshSession(): Promise<any> {\n const res = await fetch(\n await this.buildUrl('/refresh', { optional: true }),\n {\n headers: {\n 'x-requested-with': 'XMLHttpRequest',\n },\n credentials: 'include',\n },\n ).catch(error => {\n throw new Error(`Auth refresh request failed, ${error}`);\n });\n\n if (!res.ok) {\n const error: any = new Error(\n `Auth refresh request failed, ${res.statusText}`,\n );\n error.status = res.status;\n throw error;\n }\n\n const authInfo = await res.json();\n\n if (authInfo.error) {\n const error = new Error(authInfo.error.message);\n if (authInfo.error.name) {\n error.name = authInfo.error.name;\n }\n throw error;\n }\n return await this.sessionTransform(authInfo);\n }\n\n async removeSession(): Promise<void> {\n const res = await fetch(await this.buildUrl('/logout'), {\n method: 'POST',\n headers: {\n 'x-requested-with': 'XMLHttpRequest',\n },\n credentials: 'include',\n }).catch(error => {\n throw new Error(`Logout request failed, ${error}`);\n });\n\n if (!res.ok) {\n const error: any = new Error(`Logout request failed, ${res.statusText}`);\n error.status = res.status;\n throw error;\n }\n }\n\n private async showPopup(scopes: Set<string>): Promise<AuthSession> {\n const scope = this.joinScopesFunc(scopes);\n const popupUrl = await this.buildUrl('/start', {\n scope,\n origin: location.origin,\n });\n\n const payload = await showLoginPopup({\n url: popupUrl,\n name: `${this.provider.title} Login`,\n origin: new URL(popupUrl).origin,\n width: 450,\n height: 730,\n });\n\n return await this.sessionTransform(payload);\n }\n\n private async buildUrl(\n path: string,\n query?: { [key: string]: string | boolean | undefined },\n ): Promise<string> {\n const baseUrl = await this.discoveryApi.getBaseUrl('auth');\n const queryString = this.buildQueryString({\n ...query,\n env: this.environment,\n });\n\n return `${baseUrl}/${this.provider.id}${path}${queryString}`;\n }\n\n private buildQueryString(query?: {\n [key: string]: string | boolean | undefined;\n }): string {\n if (!query) {\n return '';\n }\n\n const queryString = Object.entries<string | boolean | undefined>(query)\n .map(([key, value]) => {\n if (typeof value === 'string') {\n return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;\n } else if (value) {\n return encodeURIComponent(key);\n }\n return undefined;\n })\n .filter(Boolean)\n .join('&');\n\n if (!queryString) {\n return '';\n }\n return `?${queryString}`;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { AuthProviderInfo, DiscoveryApi } from '@backstage/core-plugin-api';\nimport { showLoginPopup } from '../loginPopup';\n\ntype Options = {\n discoveryApi: DiscoveryApi;\n environment?: string;\n provider: AuthProviderInfo;\n};\nexport class DirectAuthConnector<DirectAuthResponse> {\n private readonly discoveryApi: DiscoveryApi;\n private readonly environment: string | undefined;\n private readonly provider: AuthProviderInfo;\n\n constructor(options: Options) {\n const { discoveryApi, environment, provider } = options;\n\n this.discoveryApi = discoveryApi;\n this.environment = environment;\n this.provider = provider;\n }\n\n async createSession(): Promise<DirectAuthResponse> {\n const popupUrl = await this.buildUrl('/start');\n const payload = await showLoginPopup({\n url: popupUrl,\n name: `${this.provider.title} Login`,\n origin: new URL(popupUrl).origin,\n width: 450,\n height: 730,\n });\n\n return {\n ...payload,\n id: payload.profile.email,\n };\n }\n\n async refreshSession(): Promise<any> {}\n\n async removeSession(): Promise<void> {\n const res = await fetch(await this.buildUrl('/logout'), {\n method: 'POST',\n headers: {\n 'x-requested-with': 'XMLHttpRequest',\n },\n credentials: 'include',\n }).catch(error => {\n throw new Error(`Logout request failed, ${error}`);\n });\n\n if (!res.ok) {\n const error: any = new Error(`Logout request failed, ${res.statusText}`);\n error.status = res.status;\n throw error;\n }\n }\n\n private async buildUrl(path: string): Promise<string> {\n const baseUrl = await this.discoveryApi.getBaseUrl('auth');\n return `${baseUrl}/${this.provider.id}${path}?env=${this.environment}`;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { SessionScopesFunc } from './types';\n\nexport function hasScopes(\n searched: Set<string>,\n searchFor: Set<string>,\n): boolean {\n for (const scope of searchFor) {\n if (!searched.has(scope)) {\n return false;\n }\n }\n return true;\n}\n\ntype ScopeHelperOptions<T> = {\n sessionScopes: SessionScopesFunc<T> | undefined;\n defaultScopes?: Set<string>;\n};\n\nexport class SessionScopeHelper<T> {\n constructor(private readonly options: ScopeHelperOptions<T>) {}\n\n sessionExistsAndHasScope(\n session: T | undefined,\n scopes?: Set<string>,\n ): boolean {\n if (!session) {\n return false;\n }\n if (!scopes) {\n return true;\n }\n if (this.options.sessionScopes === undefined) {\n return true;\n }\n const sessionScopes = this.options.sessionScopes(session);\n return hasScopes(sessionScopes, scopes);\n }\n\n getExtendedScope(session: T | undefined, scopes?: Set<string>) {\n const newScope = new Set(this.options.defaultScopes);\n if (session && this.options.sessionScopes !== undefined) {\n const sessionScopes = this.options.sessionScopes(session);\n for (const scope of sessionScopes) {\n newScope.add(scope);\n }\n }\n if (scopes) {\n for (const scope of scopes) {\n newScope.add(scope);\n }\n }\n return newScope;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Observable } from '@backstage/types';\nimport ObservableImpl from 'zen-observable';\n\n// TODO(Rugvip): These are stopgap and probably incomplete implementations of subjects.\n// If we add a more complete Observables library they should be replaced.\n\n/**\n * A basic implementation of ReactiveX publish subjects.\n *\n * A subject is a convenient way to create an observable when you want\n * to fan out a single value to all subscribers.\n *\n * See http://reactivex.io/documentation/subject.html\n */\nexport class PublishSubject<T>\n implements Observable<T>, ZenObservable.SubscriptionObserver<T>\n{\n private isClosed = false;\n private terminatingError?: Error;\n\n private readonly observable = new ObservableImpl<T>(subscriber => {\n if (this.isClosed) {\n if (this.terminatingError) {\n subscriber.error(this.terminatingError);\n } else {\n subscriber.complete();\n }\n return () => {};\n }\n\n this.subscribers.add(subscriber);\n return () => {\n this.subscribers.delete(subscriber);\n };\n });\n\n private readonly subscribers = new Set<\n ZenObservable.SubscriptionObserver<T>\n >();\n\n [Symbol.observable]() {\n return this;\n }\n\n get closed() {\n return this.isClosed;\n }\n\n next(value: T) {\n if (this.isClosed) {\n throw new Error('PublishSubject is closed');\n }\n this.subscribers.forEach(subscriber => subscriber.next(value));\n }\n\n error(error: Error) {\n if (this.isClosed) {\n throw new Error('PublishSubject is closed');\n }\n this.isClosed = true;\n this.terminatingError = error;\n this.subscribers.forEach(subscriber => subscriber.error(error));\n }\n\n complete() {\n if (this.isClosed) {\n throw new Error('PublishSubject is closed');\n }\n this.isClosed = true;\n this.subscribers.forEach(subscriber => subscriber.complete());\n }\n\n subscribe(observer: ZenObservable.Observer<T>): ZenObservable.Subscription;\n subscribe(\n onNext: (value: T) => void,\n onError?: (error: any) => void,\n onComplete?: () => void,\n ): ZenObservable.Subscription;\n subscribe(\n onNext: ZenObservable.Observer<T> | ((value: T) => void),\n onError?: (error: any) => void,\n onComplete?: () => void,\n ): ZenObservable.Subscription {\n const observer =\n typeof onNext === 'function'\n ? {\n next: onNext,\n error: onError,\n complete: onComplete,\n }\n : onNext;\n\n return this.observable.subscribe(observer);\n }\n}\n\n/**\n * A basic implementation of ReactiveX behavior subjects.\n *\n * A subject is a convenient way to create an observable when you want\n * to fan out a single value to all subscribers.\n *\n * The BehaviorSubject will emit the most recently emitted value or error\n * whenever a new observer subscribes to the subject.\n *\n * See http://reactivex.io/documentation/subject.html\n */\n\nexport class BehaviorSubject<T>\n implements Observable<T>, ZenObservable.SubscriptionObserver<T>\n{\n private isClosed: boolean;\n private currentValue: T;\n private terminatingError: Error | undefined;\n private readonly observable: Observable<T>;\n\n constructor(value: T) {\n this.isClosed = false;\n this.currentValue = value;\n this.terminatingError = undefined;\n this.observable = new ObservableImpl<T>(subscriber => {\n if (this.isClosed) {\n if (this.terminatingError) {\n subscriber.error(this.terminatingError);\n } else {\n subscriber.complete();\n }\n return () => {};\n }\n\n subscriber.next(this.currentValue);\n\n this.subscribers.add(subscriber);\n return () => {\n this.subscribers.delete(subscriber);\n };\n });\n }\n\n private readonly subscribers = new Set<\n ZenObservable.SubscriptionObserver<T>\n >();\n\n [Symbol.observable]() {\n return this;\n }\n\n get closed() {\n return this.isClosed;\n }\n\n next(value: T) {\n if (this.isClosed) {\n throw new Error('BehaviorSubject is closed');\n }\n this.currentValue = value;\n this.subscribers.forEach(subscriber => subscriber.next(value));\n }\n\n error(error: Error) {\n if (this.isClosed) {\n throw new Error('BehaviorSubject is closed');\n }\n this.isClosed = true;\n this.terminatingError = error;\n this.subscribers.forEach(subscriber => subscriber.error(error));\n }\n\n complete() {\n if (this.isClosed) {\n throw new Error('BehaviorSubject is closed');\n }\n this.isClosed = true;\n this.subscribers.forEach(subscriber => subscriber.complete());\n }\n\n subscribe(observer: ZenObservable.Observer<T>): ZenObservable.Subscription;\n subscribe(\n onNext: (value: T) => void,\n onError?: (error: any) => void,\n onComplete?: () => void,\n ): ZenObservable.Subscription;\n subscribe(\n onNext: ZenObservable.Observer<T> | ((value: T) => void),\n onError?: (error: any) => void,\n onComplete?: () => void,\n ): ZenObservable.Subscription {\n const observer =\n typeof onNext === 'function'\n ? {\n next: onNext,\n error: onError,\n complete: onComplete,\n }\n : onNext;\n\n return this.observable.subscribe(observer);\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { BehaviorSubject } from '../subjects';\nimport { SessionState } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\n\nexport class SessionStateTracker {\n private readonly subject = new BehaviorSubject<SessionState>(\n SessionState.SignedOut,\n );\n\n private signedIn: boolean = false;\n\n setIsSignedIn(isSignedIn: boolean) {\n if (this.signedIn !== isSignedIn) {\n this.signedIn = isSignedIn;\n this.subject.next(\n this.signedIn ? SessionState.SignedIn : SessionState.SignedOut,\n );\n }\n }\n\n sessionState$(): Observable<SessionState> {\n return this.subject;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n SessionManager,\n SessionScopesFunc,\n SessionShouldRefreshFunc,\n GetSessionOptions,\n} from './types';\nimport { AuthConnector } from '../AuthConnector';\nimport { SessionScopeHelper, hasScopes } from './common';\nimport { SessionStateTracker } from './SessionStateTracker';\n\ntype Options<T> = {\n /** The connector used for acting on the auth session */\n connector: AuthConnector<T>;\n /** Used to get the scope of the session */\n sessionScopes: SessionScopesFunc<T>;\n /** Used to check if the session needs to be refreshed */\n sessionShouldRefresh: SessionShouldRefreshFunc<T>;\n /** The default scopes that should always be present in a session, defaults to none. */\n defaultScopes?: Set<string>;\n};\n\n/**\n * RefreshingAuthSessionManager manages an underlying session that has\n * and expiration time and needs to be refreshed periodically.\n */\nexport class RefreshingAuthSessionManager<T> implements SessionManager<T> {\n private readonly connector: AuthConnector<T>;\n private readonly helper: SessionScopeHelper<T>;\n private readonly sessionScopesFunc: SessionScopesFunc<T>;\n private readonly sessionShouldRefreshFunc: SessionShouldRefreshFunc<T>;\n private readonly stateTracker = new SessionStateTracker();\n\n private refreshPromise?: Promise<T>;\n private currentSession: T | undefined;\n\n constructor(options: Options<T>) {\n const {\n connector,\n defaultScopes = new Set(),\n sessionScopes,\n sessionShouldRefresh,\n } = options;\n\n this.connector = connector;\n this.sessionScopesFunc = sessionScopes;\n this.sessionShouldRefreshFunc = sessionShouldRefresh;\n this.helper = new SessionScopeHelper({ sessionScopes, defaultScopes });\n }\n\n async getSession(options: GetSessionOptions): Promise<T | undefined> {\n if (\n this.helper.sessionExistsAndHasScope(this.currentSession, options.scopes)\n ) {\n const shouldRefresh = this.sessionShouldRefreshFunc(this.currentSession!);\n if (!shouldRefresh) {\n return this.currentSession!;\n }\n\n try {\n const refreshedSession = await this.collapsedSessionRefresh();\n const currentScopes = this.sessionScopesFunc(this.currentSession!);\n const refreshedScopes = this.sessionScopesFunc(refreshedSession);\n if (hasScopes(refreshedScopes, currentScopes)) {\n this.currentSession = refreshedSession;\n }\n return refreshedSession;\n } catch (error) {\n if (options.optional) {\n return undefined;\n }\n throw error;\n }\n }\n\n // The user may still have a valid refresh token in their cookies. Attempt to\n // initiate a fresh session through the backend using that refresh token.\n //\n // We skip this check if an instant login popup is requested, as we need to\n // stay in a synchronous call stack from the user interaction. The downside\n // is that that the user will sometimes be requested to log in even if they\n // already had an existing session.\n if (!this.currentSession && !options.instantPopup) {\n try {\n const newSession = await this.collapsedSessionRefresh();\n this.currentSession = newSession;\n // The session might not have the scopes requested so go back and check again\n return this.getSession(options);\n } catch {\n // If the refresh attempt fails we assume we don't have a session, so continue to create one.\n }\n }\n\n // If we continue here we will show a popup, so exit if this is an optional session request.\n if (options.optional) {\n return undefined;\n }\n\n // We can call authRequester multiple times, the returned session will contain all requested scopes.\n this.currentSession = await this.connector.createSession({\n ...options,\n scopes: this.helper.getExtendedScope(this.currentSession, options.scopes),\n });\n this.stateTracker.setIsSignedIn(true);\n return this.currentSession;\n }\n\n async removeSession() {\n this.currentSession = undefined;\n await this.connector.removeSession();\n this.stateTracker.setIsSignedIn(false);\n }\n\n sessionState$() {\n return this.stateTracker.sessionState$();\n }\n\n private async collapsedSessionRefresh(): Promise<T> {\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.connector.refreshSession();\n\n try {\n const session = await this.refreshPromise;\n this.stateTracker.setIsSignedIn(true);\n return session;\n } finally {\n delete this.refreshPromise;\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { MutableSessionManager, GetSessionOptions } from './types';\nimport { AuthConnector } from '../AuthConnector';\nimport { SessionScopeHelper } from './common';\nimport { SessionStateTracker } from './SessionStateTracker';\n\ntype Options<T> = {\n /** The connector used for acting on the auth session */\n connector: AuthConnector<T>;\n /** Used to get the scope of the session */\n sessionScopes?: (session: T) => Set<string>;\n /** The default scopes that should always be present in a session, defaults to none. */\n defaultScopes?: Set<string>;\n};\n\n/**\n * StaticAuthSessionManager manages an underlying session that does not expire.\n */\nexport class StaticAuthSessionManager<T> implements MutableSessionManager<T> {\n private readonly connector: AuthConnector<T>;\n private readonly helper: SessionScopeHelper<T>;\n private readonly stateTracker = new SessionStateTracker();\n\n private currentSession: T | undefined;\n\n constructor(options: Options<T>) {\n const { connector, defaultScopes = new Set(), sessionScopes } = options;\n\n this.connector = connector;\n this.helper = new SessionScopeHelper({ sessionScopes, defaultScopes });\n }\n\n setSession(session: T | undefined): void {\n this.currentSession = session;\n this.stateTracker.setIsSignedIn(Boolean(session));\n }\n\n async getSession(options: GetSessionOptions): Promise<T | undefined> {\n if (\n this.helper.sessionExistsAndHasScope(this.currentSession, options.scopes)\n ) {\n return this.currentSession;\n }\n\n // If we continue here we will show a popup, so exit if this is an optional session request.\n if (options.optional) {\n return undefined;\n }\n\n // We can call authRequester multiple times, the returned session will contain all requested scopes.\n this.currentSession = await this.connector.createSession({\n ...options,\n scopes: this.helper.getExtendedScope(this.currentSession, options.scopes),\n });\n this.stateTracker.setIsSignedIn(true);\n return this.currentSession;\n }\n\n /**\n * We don't call this.connector.removeSession here, since this session manager\n * is intended to be static. As such there's no need to hit the remote logout\n * endpoint - simply discarding the local session state when signing out is\n * enough.\n */\n async removeSession() {\n this.currentSession = undefined;\n this.stateTracker.setIsSignedIn(false);\n }\n\n sessionState$() {\n return this.stateTracker.sessionState$();\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n MutableSessionManager,\n SessionScopesFunc,\n SessionShouldRefreshFunc,\n GetSessionOptions,\n} from './types';\nimport { SessionScopeHelper } from './common';\n\ntype Options<T> = {\n /** The connector used for acting on the auth session */\n manager: MutableSessionManager<T>;\n /** Storage key to use to store sessions */\n storageKey: string;\n /** Used to get the scope of the session */\n sessionScopes?: SessionScopesFunc<T>;\n /** Used to check if the session needs to be refreshed, defaults to never refresh */\n sessionShouldRefresh?: SessionShouldRefreshFunc<T>;\n};\n\n/**\n * AuthSessionStore decorates another SessionManager with a functionality\n * to store the session in local storage.\n *\n * Session is serialized to JSON with special support for following types: Set.\n */\nexport class AuthSessionStore<T> implements MutableSessionManager<T> {\n private readonly manager: MutableSessionManager<T>;\n private readonly storageKey: string;\n private readonly sessionShouldRefreshFunc: SessionShouldRefreshFunc<T>;\n private readonly helper: SessionScopeHelper<T>;\n\n constructor(options: Options<T>) {\n const {\n manager,\n storageKey,\n sessionScopes,\n sessionShouldRefresh = () => false,\n } = options;\n\n this.manager = manager;\n this.storageKey = storageKey;\n this.sessionShouldRefreshFunc = sessionShouldRefresh;\n this.helper = new SessionScopeHelper({\n sessionScopes,\n defaultScopes: new Set(),\n });\n }\n\n setSession(session: T | undefined): void {\n this.manager.setSession(session);\n this.saveSession(session);\n }\n\n async getSession(options: GetSessionOptions): Promise<T | undefined> {\n const { scopes } = options;\n const session = this.loadSession();\n\n if (this.helper.sessionExistsAndHasScope(session, scopes)) {\n const shouldRefresh = this.sessionShouldRefreshFunc(session!);\n\n if (!shouldRefresh) {\n this.manager.setSession(session!);\n return session!;\n }\n }\n\n const newSession = await this.manager.getSession(options);\n this.saveSession(newSession);\n return newSession;\n }\n\n async removeSession() {\n localStorage.removeItem(this.storageKey);\n await this.manager.removeSession();\n }\n\n sessionState$() {\n return this.manager.sessionState$();\n }\n\n private loadSession(): T | undefined {\n try {\n const sessionJson = localStorage.getItem(this.storageKey);\n if (sessionJson) {\n const session = JSON.parse(sessionJson, (_key, value) => {\n if (value?.__type === 'Set') {\n return new Set(value.__value);\n }\n return value;\n });\n return session;\n }\n\n return undefined;\n } catch (error) {\n localStorage.removeItem(this.storageKey);\n return undefined;\n }\n }\n\n private saveSession(session: T | undefined) {\n if (session === undefined) {\n localStorage.removeItem(this.storageKey);\n } else {\n localStorage.setItem(\n this.storageKey,\n JSON.stringify(session, (_key, value) => {\n if (value instanceof Set) {\n return {\n __type: 'Set',\n __value: Array.from(value),\n };\n }\n return value;\n }),\n );\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { SessionState } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport {\n SessionManager,\n MutableSessionManager,\n GetSessionOptions,\n} from './types';\nimport { SessionStateTracker } from './SessionStateTracker';\n\ntype Options<T> = {\n /**\n * A callback that is called to determine whether a given session supports refresh\n */\n sessionCanRefresh: (session: T) => boolean;\n\n /**\n * The session manager that is used if the a session does not support refresh.\n */\n staticSessionManager: MutableSessionManager<T>;\n\n /**\n * The session manager that is used if the a session supports refresh.\n */\n refreshingSessionManager: SessionManager<T>;\n};\n\n/**\n * OptionalRefreshSessionManagerMux wraps two different session managers, one for\n * static session storage and another one that supports refresh. For each session\n * that is retrieved is checked for whether it supports refresh. If it does, the\n * refreshing session manager is used, otherwise the static session manager is used.\n */\nexport class OptionalRefreshSessionManagerMux<T> implements SessionManager<T> {\n private readonly stateTracker = new SessionStateTracker();\n\n private readonly sessionCanRefresh: (session: T) => boolean;\n private readonly staticSessionManager: MutableSessionManager<T>;\n private readonly refreshingSessionManager: SessionManager<T>;\n\n constructor(options: Options<T>) {\n this.sessionCanRefresh = options.sessionCanRefresh;\n this.staticSessionManager = options.staticSessionManager;\n this.refreshingSessionManager = options.refreshingSessionManager;\n }\n\n async getSession(options: GetSessionOptions): Promise<T | undefined> {\n // First we check if there is an existing static session, using an optional request\n const staticSession = await this.staticSessionManager.getSession({\n ...options,\n optional: true,\n });\n if (staticSession) {\n this.stateTracker.setIsSignedIn(true);\n return staticSession;\n }\n\n // If there is no static session available, we ask the refresh manager to get a session\n const session = await this.refreshingSessionManager.getSession(options);\n\n // Handling the case where the session request is optional\n if (!session) {\n this.stateTracker.setIsSignedIn(false);\n return undefined;\n }\n\n // Next we check if the session we received from the refreshing manager can actually\n // be refreshed. If it can, we use this session without storing it in the static manager.\n if (this.sessionCanRefresh(session)) {\n this.stateTracker.setIsSignedIn(true);\n return session;\n }\n\n // If the session can't be refreshed, we store it in the static manager\n this.staticSessionManager.setSession(session);\n this.stateTracker.setIsSignedIn(true);\n return session;\n }\n\n async removeSession(): Promise<void> {\n await Promise.all([\n this.refreshingSessionManager.removeSession(),\n this.staticSessionManager.removeSession(),\n ]);\n this.stateTracker.setIsSignedIn(false);\n }\n\n sessionState$(): Observable<SessionState> {\n return this.stateTracker.sessionState$();\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DefaultAuthConnector } from '../../../../lib/AuthConnector';\nimport { GithubSession } from './types';\nimport {\n OAuthApi,\n SessionApi,\n SessionState,\n ProfileInfo,\n BackstageIdentity,\n AuthRequestOptions,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { SessionManager } from '../../../../lib/AuthSessionManager/types';\nimport {\n AuthSessionStore,\n RefreshingAuthSessionManager,\n StaticAuthSessionManager,\n} from '../../../../lib/AuthSessionManager';\nimport { OAuthApiCreateOptions } from '../types';\nimport { OptionalRefreshSessionManagerMux } from '../../../../lib/AuthSessionManager/OptionalRefreshSessionManagerMux';\n\nexport type GithubAuthResponse = {\n providerInfo: {\n accessToken: string;\n scope: string;\n expiresInSeconds?: number;\n };\n profile: ProfileInfo;\n backstageIdentity: BackstageIdentity;\n};\n\nconst DEFAULT_PROVIDER = {\n id: 'github',\n title: 'GitHub',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to GitHub products.\n *\n * @public\n */\nexport default class GithubAuth implements OAuthApi, SessionApi {\n static create(options: OAuthApiCreateOptions) {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = ['read:user'],\n } = options;\n\n const connector = new DefaultAuthConnector({\n discoveryApi,\n environment,\n provider,\n oauthRequestApi: oauthRequestApi,\n sessionTransform(res: GithubAuthResponse): GithubSession {\n return {\n ...res,\n providerInfo: {\n accessToken: res.providerInfo.accessToken,\n scopes: GithubAuth.normalizeScope(res.providerInfo.scope),\n expiresAt: res.providerInfo.expiresInSeconds\n ? new Date(Date.now() + res.providerInfo.expiresInSeconds * 1000)\n : undefined,\n },\n };\n },\n });\n\n const refreshingSessionManager = new RefreshingAuthSessionManager({\n connector,\n defaultScopes: new Set(defaultScopes),\n sessionScopes: (session: GithubSession) => session.providerInfo.scopes,\n sessionShouldRefresh: (session: GithubSession) => {\n const { expiresAt } = session.providerInfo;\n if (!expiresAt) {\n return false;\n }\n const expiresInSec = (expiresAt.getTime() - Date.now()) / 1000;\n return expiresInSec < 60 * 5;\n },\n });\n\n const staticSessionManager = new AuthSessionStore<GithubSession>({\n manager: new StaticAuthSessionManager({\n connector,\n defaultScopes: new Set(defaultScopes),\n sessionScopes: (session: GithubSession) => session.providerInfo.scopes,\n }),\n storageKey: `${provider.id}Session`,\n sessionScopes: (session: GithubSession) => session.providerInfo.scopes,\n });\n\n const sessionManagerMux = new OptionalRefreshSessionManagerMux({\n refreshingSessionManager,\n staticSessionManager,\n sessionCanRefresh: session =>\n session.providerInfo.expiresAt !== undefined,\n });\n\n return new GithubAuth(sessionManagerMux);\n }\n\n /**\n * @deprecated will be made private in the future. Use create method instead.\n */\n constructor(private readonly sessionManager: SessionManager<GithubSession>) {}\n\n async signIn() {\n await this.getAccessToken();\n }\n\n async signOut() {\n await this.sessionManager.removeSession();\n }\n\n sessionState$(): Observable<SessionState> {\n return this.sessionManager.sessionState$();\n }\n\n async getAccessToken(scope?: string, options?: AuthRequestOptions) {\n const session = await this.sessionManager.getSession({\n ...options,\n scopes: GithubAuth.normalizeScope(scope),\n });\n return session?.providerInfo.accessToken ?? '';\n }\n\n async getBackstageIdentity(\n options: AuthRequestOptions = {},\n ): Promise<BackstageIdentity | undefined> {\n const session = await this.sessionManager.getSession(options);\n return session?.backstageIdentity;\n }\n\n async getProfile(options: AuthRequestOptions = {}) {\n const session = await this.sessionManager.getSession(options);\n return session?.profile;\n }\n\n static normalizeScope(scope?: string): Set<string> {\n if (!scope) {\n return new Set();\n }\n\n const scopeList = Array.isArray(scope)\n ? scope\n : scope.split(/[\\s|,]/).filter(Boolean);\n\n return new Set(scopeList);\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DefaultAuthConnector } from '../../../../lib/AuthConnector';\nimport { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager';\nimport { SessionManager } from '../../../../lib/AuthSessionManager/types';\nimport {\n AuthRequestOptions,\n BackstageIdentity,\n OAuthApi,\n OpenIdConnectApi,\n ProfileInfo,\n ProfileInfoApi,\n SessionState,\n SessionApi,\n BackstageIdentityApi,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { OAuth2Session } from './types';\nimport { OAuthApiCreateOptions } from '../types';\n\n/**\n * OAuth2 create options.\n * @public\n */\nexport type OAuth2CreateOptions = OAuthApiCreateOptions & {\n scopeTransform?: (scopes: string[]) => string[];\n};\n\nexport type OAuth2Response = {\n providerInfo: {\n accessToken: string;\n idToken: string;\n scope: string;\n expiresInSeconds: number;\n };\n profile: ProfileInfo;\n backstageIdentity: BackstageIdentity;\n};\n\nconst DEFAULT_PROVIDER = {\n id: 'oauth2',\n title: 'Your Identity Provider',\n icon: () => null,\n};\n\n/**\n * Implements a generic OAuth2 flow for auth.\n *\n * @public\n */\nexport default class OAuth2\n implements\n OAuthApi,\n OpenIdConnectApi,\n ProfileInfoApi,\n BackstageIdentityApi,\n SessionApi\n{\n static create(options: OAuth2CreateOptions) {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = [],\n scopeTransform = x => x,\n } = options;\n\n const connector = new DefaultAuthConnector({\n discoveryApi,\n environment,\n provider,\n oauthRequestApi: oauthRequestApi,\n sessionTransform(res: OAuth2Response): OAuth2Session {\n return {\n ...res,\n providerInfo: {\n idToken: res.providerInfo.idToken,\n accessToken: res.providerInfo.accessToken,\n scopes: OAuth2.normalizeScopes(\n scopeTransform,\n res.providerInfo.scope,\n ),\n expiresAt: new Date(\n Date.now() + res.providerInfo.expiresInSeconds * 1000,\n ),\n },\n };\n },\n });\n\n const sessionManager = new RefreshingAuthSessionManager({\n connector,\n defaultScopes: new Set(defaultScopes),\n sessionScopes: (session: OAuth2Session) => session.providerInfo.scopes,\n sessionShouldRefresh: (session: OAuth2Session) => {\n const expiresInSec =\n (session.providerInfo.expiresAt.getTime() - Date.now()) / 1000;\n return expiresInSec < 60 * 5;\n },\n });\n\n return new OAuth2({ sessionManager, scopeTransform });\n }\n\n private readonly sessionManager: SessionManager<OAuth2Session>;\n private readonly scopeTransform: (scopes: string[]) => string[];\n\n /**\n * @deprecated will be made private in the future. Use create method instead.\n */\n constructor(options: {\n sessionManager: SessionManager<OAuth2Session>;\n scopeTransform: (scopes: string[]) => string[];\n }) {\n this.sessionManager = options.sessionManager;\n this.scopeTransform = options.scopeTransform;\n }\n\n async signIn() {\n await this.getAccessToken();\n }\n\n async signOut() {\n await this.sessionManager.removeSession();\n }\n\n sessionState$(): Observable<SessionState> {\n return this.sessionManager.sessionState$();\n }\n\n async getAccessToken(\n scope?: string | string[],\n options?: AuthRequestOptions,\n ) {\n const normalizedScopes = OAuth2.normalizeScopes(this.scopeTransform, scope);\n const session = await this.sessionManager.getSession({\n ...options,\n scopes: normalizedScopes,\n });\n return session?.providerInfo.accessToken ?? '';\n }\n\n async getIdToken(options: AuthRequestOptions = {}) {\n const session = await this.sessionManager.getSession(options);\n return session?.providerInfo.idToken ?? '';\n }\n\n async getBackstageIdentity(\n options: AuthRequestOptions = {},\n ): Promise<BackstageIdentity | undefined> {\n const session = await this.sessionManager.getSession(options);\n return session?.backstageIdentity;\n }\n\n async getProfile(options: AuthRequestOptions = {}) {\n const session = await this.sessionManager.getSession(options);\n return session?.profile;\n }\n\n private static normalizeScopes(\n scopeTransform: (scopes: string[]) => string[],\n scopes?: string | string[],\n ): Set<string> {\n if (!scopes) {\n return new Set();\n }\n\n const scopeList = Array.isArray(scopes)\n ? scopes\n : scopes.split(/[\\s|,]/).filter(Boolean);\n\n return new Set(scopeTransform(scopeList));\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { gitlabAuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'gitlab',\n title: 'GitLab',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to GitLab products.\n *\n * @public\n */\nexport default class GitlabAuth {\n static create(options: OAuthApiCreateOptions): typeof gitlabAuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = ['read_user'],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { googleAuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'google',\n title: 'Google',\n icon: () => null,\n};\n\nconst SCOPE_PREFIX = 'https://www.googleapis.com/auth/';\n\n/**\n * Implements the OAuth flow to Google products.\n *\n * @public\n */\nexport default class GoogleAuth {\n static create(options: OAuthApiCreateOptions): typeof googleAuthApiRef.T {\n const {\n discoveryApi,\n oauthRequestApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n defaultScopes = [\n 'openid',\n `${SCOPE_PREFIX}userinfo.email`,\n `${SCOPE_PREFIX}userinfo.profile`,\n ],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n scopeTransform(scopes: string[]) {\n return scopes.map(scope => {\n if (scope === 'openid') {\n return scope;\n }\n\n if (scope === 'profile' || scope === 'email') {\n return `${SCOPE_PREFIX}userinfo.${scope}`;\n }\n\n if (scope.startsWith(SCOPE_PREFIX)) {\n return scope;\n }\n\n return `${SCOPE_PREFIX}${scope}`;\n });\n },\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { oktaAuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'okta',\n title: 'Okta',\n icon: () => null,\n};\n\nconst OKTA_OIDC_SCOPES: Set<String> = new Set([\n 'openid',\n 'profile',\n 'email',\n 'phone',\n 'address',\n 'groups',\n 'offline_access',\n]);\n\nconst OKTA_SCOPE_PREFIX: string = 'okta.';\n\n/**\n * Implements the OAuth flow to Okta products.\n *\n * @public\n */\nexport default class OktaAuth {\n static create(options: OAuthApiCreateOptions): typeof oktaAuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = ['openid', 'email', 'profile', 'offline_access'],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n scopeTransform(scopes) {\n return scopes.map(scope => {\n if (OKTA_OIDC_SCOPES.has(scope)) {\n return scope;\n }\n\n if (scope.startsWith(OKTA_SCOPE_PREFIX)) {\n return scope;\n }\n\n return `${OKTA_SCOPE_PREFIX}${scope}`;\n });\n },\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DirectAuthConnector } from '../../../../lib/AuthConnector';\nimport { SessionManager } from '../../../../lib/AuthSessionManager/types';\nimport {\n ProfileInfo,\n BackstageIdentity,\n SessionState,\n AuthRequestOptions,\n ProfileInfoApi,\n BackstageIdentityApi,\n SessionApi,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { SamlSession } from './types';\nimport {\n AuthSessionStore,\n StaticAuthSessionManager,\n} from '../../../../lib/AuthSessionManager';\nimport { AuthApiCreateOptions } from '../types';\n\nexport type SamlAuthResponse = {\n profile: ProfileInfo;\n backstageIdentity: BackstageIdentity;\n};\n\nconst DEFAULT_PROVIDER = {\n id: 'saml',\n title: 'SAML',\n icon: () => null,\n};\n\n/**\n * Implements a general SAML based auth flow.\n *\n * @public\n */\nexport default class SamlAuth\n implements ProfileInfoApi, BackstageIdentityApi, SessionApi\n{\n static create(options: AuthApiCreateOptions) {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n } = options;\n\n const connector = new DirectAuthConnector<SamlSession>({\n discoveryApi,\n environment,\n provider,\n });\n\n const sessionManager = new StaticAuthSessionManager<SamlSession>({\n connector,\n });\n\n const authSessionStore = new AuthSessionStore<SamlSession>({\n manager: sessionManager,\n storageKey: `${provider.id}Session`,\n });\n\n return new SamlAuth(authSessionStore);\n }\n\n sessionState$(): Observable<SessionState> {\n return this.sessionManager.sessionState$();\n }\n\n /**\n * @deprecated will be made private in the future. Use create method instead.\n */\n constructor(private readonly sessionManager: SessionManager<SamlSession>) {}\n\n async signIn() {\n await this.getBackstageIdentity({});\n }\n async signOut() {\n await this.sessionManager.removeSession();\n }\n\n async getBackstageIdentity(\n options: AuthRequestOptions = {},\n ): Promise<BackstageIdentity | undefined> {\n const session = await this.sessionManager.getSession(options);\n return session?.backstageIdentity;\n }\n\n async getProfile(options: AuthRequestOptions = {}) {\n const session = await this.sessionManager.getSession(options);\n return session?.profile;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { auth0AuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'auth0',\n title: 'Auth0',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to Auth0 products.\n *\n * @public\n * @deprecated Use {@link OAuth2} instead\n *\n * @example\n *\n * ```ts\n * OAuth2.create({\n * discoveryApi,\n * oauthRequestApi,\n * provider: {\n * id: 'auth0',\n * title: 'Auth0',\n * icon: () => null,\n * },\n * defaultScopes: ['openid', 'email', 'profile'],\n * environment: configApi.getOptionalString('auth.environment'),\n * })\n * ```\n */\nexport default class Auth0Auth {\n static create(options: OAuthApiCreateOptions): typeof auth0AuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = ['openid', `email`, `profile`],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { microsoftAuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'microsoft',\n title: 'Microsoft',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to Microsoft products.\n *\n * @public\n */\nexport default class MicrosoftAuth {\n static create(options: OAuthApiCreateOptions): typeof microsoftAuthApiRef.T {\n const {\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n discoveryApi,\n defaultScopes = [\n 'openid',\n 'offline_access',\n 'profile',\n 'email',\n 'User.Read',\n ],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n oneloginAuthApiRef,\n OAuthRequestApi,\n AuthProviderInfo,\n DiscoveryApi,\n} from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\n\n/**\n * OneLogin auth provider create options.\n * @public\n */\nexport type OneLoginAuthCreateOptions = {\n discoveryApi: DiscoveryApi;\n oauthRequestApi: OAuthRequestApi;\n environment?: string;\n provider?: AuthProviderInfo;\n};\n\nconst DEFAULT_PROVIDER = {\n id: 'onelogin',\n title: 'onelogin',\n icon: () => null,\n};\n\nconst OIDC_SCOPES: Set<String> = new Set([\n 'openid',\n 'profile',\n 'email',\n 'phone',\n 'address',\n 'groups',\n 'offline_access',\n]);\n\nconst SCOPE_PREFIX: string = 'onelogin.';\n\n/**\n * Implements a OneLogin OAuth flow.\n *\n * @public\n */\nexport default class OneLoginAuth {\n static create(\n options: OneLoginAuthCreateOptions,\n ): typeof oneloginAuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes: ['openid', 'email', 'profile', 'offline_access'],\n scopeTransform(scopes) {\n return scopes.map(scope => {\n if (OIDC_SCOPES.has(scope)) {\n return scope;\n }\n\n if (scope.startsWith(SCOPE_PREFIX)) {\n return scope;\n }\n\n return `${SCOPE_PREFIX}${scope}`;\n });\n },\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BackstageIdentity,\n bitbucketAuthApiRef,\n ProfileInfo,\n} from '@backstage/core-plugin-api';\n\nimport { OAuthApiCreateOptions } from '../types';\nimport { OAuth2 } from '../oauth2';\n\nexport type BitbucketAuthResponse = {\n providerInfo: {\n accessToken: string;\n scope: string;\n expiresInSeconds: number;\n };\n profile: ProfileInfo;\n backstageIdentity: BackstageIdentity;\n};\n\nconst DEFAULT_PROVIDER = {\n id: 'bitbucket',\n title: 'Bitbucket',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to Bitbucket products.\n *\n * @public\n */\nexport default class BitbucketAuth {\n static create(options: OAuthApiCreateOptions): typeof bitbucketAuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n defaultScopes = ['team'],\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n defaultScopes,\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { atlassianAuthApiRef } from '@backstage/core-plugin-api';\nimport { OAuth2 } from '../oauth2';\nimport { OAuthApiCreateOptions } from '../types';\n\nconst DEFAULT_PROVIDER = {\n id: 'atlassian',\n title: 'Atlassian',\n icon: () => null,\n};\n\n/**\n * Implements the OAuth flow to Atlassian products.\n *\n * @public\n */\nexport default class AtlassianAuth {\n static create(options: OAuthApiCreateOptions): typeof atlassianAuthApiRef.T {\n const {\n discoveryApi,\n environment = 'development',\n provider = DEFAULT_PROVIDER,\n oauthRequestApi,\n } = options;\n\n return OAuth2.create({\n discoveryApi,\n oauthRequestApi,\n provider,\n environment,\n });\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AlertApi, AlertMessage } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { PublishSubject } from '../../../lib/subjects';\n\n/**\n * Base implementation for the AlertApi that simply forwards alerts to consumers.\n *\n * @public\n */\nexport class AlertApiForwarder implements AlertApi {\n private readonly subject = new PublishSubject<AlertMessage>();\n\n post(alert: AlertMessage) {\n this.subject.next(alert);\n }\n\n alert$(): Observable<AlertMessage> {\n return this.subject;\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { AnalyticsApi, AnalyticsEvent } from '@backstage/core-plugin-api';\n\n/**\n * Base implementation for the AnalyticsApi that does nothing.\n *\n * @public\n */\nexport class NoOpAnalyticsApi implements AnalyticsApi {\n captureEvent(_event: AnalyticsEvent): void {}\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AppThemeApi, AppTheme } from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { BehaviorSubject } from '../../../lib/subjects';\n\nconst STORAGE_KEY = 'theme';\n\n/**\n * Exposes the themes installed in the app, and permits switching the currently\n * active theme.\n *\n * @public\n */\nexport class AppThemeSelector implements AppThemeApi {\n static createWithStorage(themes: AppTheme[]) {\n const selector = new AppThemeSelector(themes);\n\n if (!window.localStorage) {\n return selector;\n }\n\n const initialThemeId =\n window.localStorage.getItem(STORAGE_KEY) ?? undefined;\n\n selector.setActiveThemeId(initialThemeId);\n\n selector.activeThemeId$().subscribe(themeId => {\n if (themeId) {\n window.localStorage.setItem(STORAGE_KEY, themeId);\n } else {\n window.localStorage.removeItem(STORAGE_KEY);\n }\n });\n\n window.addEventListener('storage', event => {\n if (event.key === STORAGE_KEY) {\n const themeId = localStorage.getItem(STORAGE_KEY) ?? undefined;\n selector.setActiveThemeId(themeId);\n }\n });\n\n return selector;\n }\n\n private activeThemeId: string | undefined;\n private readonly subject = new BehaviorSubject<string | undefined>(undefined);\n\n constructor(private readonly themes: AppTheme[]) {}\n\n getInstalledThemes(): AppTheme[] {\n return this.themes.slice();\n }\n\n activeThemeId$(): Observable<string | undefined> {\n return this.subject;\n }\n\n getActiveThemeId(): string | undefined {\n return this.activeThemeId;\n }\n\n setActiveThemeId(themeId?: string): void {\n this.activeThemeId = themeId;\n this.subject.next(themeId);\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DiscoveryApi } from '@backstage/core-plugin-api';\n\nconst ERROR_PREFIX = 'Invalid discovery URL pattern,';\n\n/**\n * UrlPatternDiscovery is a lightweight DiscoveryApi implementation.\n * It uses a single template string to construct URLs for each plugin.\n *\n * @public\n */\nexport class UrlPatternDiscovery implements DiscoveryApi {\n /**\n * Creates a new UrlPatternDiscovery given a template. The the only\n * interpolation done for the template is to replace instances of `{{pluginId}}`\n * with the ID of the plugin being requested.\n *\n * Example pattern: `http://localhost:7007/api/{{ pluginId }}`\n */\n static compile(pattern: string): UrlPatternDiscovery {\n const parts = pattern.split(/\\{\\{\\s*pluginId\\s*\\}\\}/);\n const urlStr = parts.join('pluginId');\n\n let url;\n try {\n url = new URL(urlStr);\n } catch {\n throw new Error(`${ERROR_PREFIX} URL '${urlStr}' is invalid`);\n }\n if (url.hash) {\n throw new Error(`${ERROR_PREFIX} URL must not have a hash`);\n }\n if (url.search) {\n throw new Error(`${ERROR_PREFIX} URL must not have a query`);\n }\n if (urlStr.endsWith('/')) {\n throw new Error(`${ERROR_PREFIX} URL must not end with a slash`);\n }\n\n return new UrlPatternDiscovery(parts);\n }\n\n private constructor(private readonly parts: string[]) {}\n\n async getBaseUrl(pluginId: string): Promise<string> {\n return this.parts.join(pluginId);\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n ErrorApi,\n ErrorApiError,\n ErrorApiErrorContext,\n AlertApi,\n} from '@backstage/core-plugin-api';\n\n/**\n * Decorates an ErrorApi by also forwarding error messages\n * to the alertApi with an 'error' severity.\n *\n * @public\n */\nexport class ErrorAlerter implements ErrorApi {\n constructor(\n private readonly alertApi: AlertApi,\n private readonly errorApi: ErrorApi,\n ) {}\n\n post(error: ErrorApiError, context?: ErrorApiErrorContext) {\n if (!context?.hidden) {\n this.alertApi.post({ message: error.message, severity: 'error' });\n }\n\n return this.errorApi.post(error, context);\n }\n\n error$() {\n return this.errorApi.error$();\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ErrorApi,\n ErrorApiError,\n ErrorApiErrorContext,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { PublishSubject } from '../../../lib/subjects';\n\n/**\n * Base implementation for the ErrorApi that simply forwards errors to consumers.\n *\n * @public\n */\nexport class ErrorApiForwarder implements ErrorApi {\n private readonly subject = new PublishSubject<{\n error: Error;\n context?: ErrorApiErrorContext;\n }>();\n\n post(error: ErrorApiError, context?: ErrorApiErrorContext) {\n this.subject.next({ error, context });\n }\n\n error$(): Observable<{ error: Error; context?: ErrorApiErrorContext }> {\n return this.subject;\n }\n}\n","import {\n ErrorApi,\n ErrorApiError,\n ErrorApiErrorContext,\n} from '@backstage/core-plugin-api';\n\n/*\n * Copyright 2020 Spotify AB\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Utility class that helps with error forwarding.\n *\n * @public\n */\nexport class UnhandledErrorForwarder {\n /**\n * Add event listener, such that unhandled errors can be forwarded using an given `ErrorApi` instance\n */\n static forward(errorApi: ErrorApi, errorContext: ErrorApiErrorContext) {\n window.addEventListener(\n 'unhandledrejection',\n (e: PromiseRejectionEvent) => {\n errorApi.post(e.reason as ErrorApiError, errorContext);\n },\n );\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FeatureFlagState,\n FeatureFlagsApi,\n FeatureFlag,\n FeatureFlagsSaveOptions,\n} from '@backstage/core-plugin-api';\n\nexport function validateFlagName(name: string): void {\n if (name.length < 3) {\n throw new Error(\n `The '${name}' feature flag must have a minimum length of three characters.`,\n );\n }\n\n if (name.length > 150) {\n throw new Error(\n `The '${name}' feature flag must not exceed 150 characters.`,\n );\n }\n\n if (!name.match(/^[a-z]+[a-z0-9-]+$/)) {\n throw new Error(\n `The '${name}' feature flag must start with a lowercase letter and only contain lowercase letters, numbers and hyphens. ` +\n 'Examples: feature-flag-one, alpha, release-2020',\n );\n }\n}\n\n/**\n * A feature flags implementation that stores the flags in the browser's local\n * storage.\n *\n * @public\n */\nexport class LocalStorageFeatureFlags implements FeatureFlagsApi {\n private registeredFeatureFlags: FeatureFlag[] = [];\n private flags?: Map<string, FeatureFlagState>;\n\n registerFlag(flag: FeatureFlag) {\n validateFlagName(flag.name);\n this.registeredFeatureFlags.push(flag);\n }\n\n getRegisteredFlags(): FeatureFlag[] {\n return this.registeredFeatureFlags.slice();\n }\n\n isActive(name: string): boolean {\n if (!this.flags) {\n this.flags = this.load();\n }\n return this.flags.get(name) === FeatureFlagState.Active;\n }\n\n save(options: FeatureFlagsSaveOptions): void {\n if (!this.flags) {\n this.flags = this.load();\n }\n if (!options.merge) {\n this.flags.clear();\n }\n for (const [name, state] of Object.entries(options.states)) {\n this.flags.set(name, state);\n }\n\n const enabled = Array.from(this.flags.entries()).filter(\n ([, state]) => state === FeatureFlagState.Active,\n );\n window.localStorage.setItem(\n 'featureFlags',\n JSON.stringify(Object.fromEntries(enabled)),\n );\n }\n\n private load(): Map<string, FeatureFlagState> {\n try {\n const jsonStr = window.localStorage.getItem('featureFlags');\n if (!jsonStr) {\n return new Map();\n }\n const json = JSON.parse(jsonStr) as unknown;\n if (typeof json !== 'object' || json === null || Array.isArray(json)) {\n return new Map();\n }\n\n const entries = Object.entries(json).filter(([name, value]) => {\n validateFlagName(name);\n return value === FeatureFlagState.Active;\n });\n\n return new Map(entries);\n } catch {\n return new Map();\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Observable } from '@backstage/types';\nimport { BehaviorSubject } from '../../../lib/subjects';\n\ntype RequestQueueEntry<ResultType> = {\n scopes: Set<string>;\n resolve: (value: ResultType | PromiseLike<ResultType>) => void;\n reject: (reason: Error) => void;\n};\n\nexport type PendingRequest<ResultType> = {\n scopes: Set<string> | undefined;\n resolve: (value: ResultType) => void;\n reject: (reason: Error) => void;\n};\n\nexport function hasScopes(\n searched: Set<string>,\n searchFor: Set<string>,\n): boolean {\n for (const scope of searchFor) {\n if (!searched.has(scope)) {\n return false;\n }\n }\n return true;\n}\n\nexport function joinScopes(\n scopes: Set<string>,\n ...moreScopess: Set<string>[]\n): Set<string> {\n const result = new Set(scopes);\n\n for (const moreScopes of moreScopess) {\n for (const scope of moreScopes) {\n result.add(scope);\n }\n }\n\n return result;\n}\n\n/**\n * The OAuthPendingRequests class is a utility for managing and observing\n * a stream of requests for oauth scopes for a single provider, and resolving\n * them correctly once requests are fulfilled.\n */\nexport class OAuthPendingRequests<ResultType> {\n private requests: RequestQueueEntry<ResultType>[] = [];\n private subject = new BehaviorSubject<PendingRequest<ResultType>>(\n this.getCurrentPending(),\n );\n\n request(scopes: Set<string>): Promise<ResultType> {\n return new Promise((resolve, reject) => {\n this.requests.push({ scopes, resolve, reject });\n\n this.subject.next(this.getCurrentPending());\n });\n }\n\n resolve(scopes: Set<string>, result: ResultType): void {\n this.requests = this.requests.filter(request => {\n if (hasScopes(scopes, request.scopes)) {\n request.resolve(result);\n return false;\n }\n return true;\n });\n\n this.subject.next(this.getCurrentPending());\n }\n\n reject(error: Error) {\n this.requests.forEach(request => request.reject(error));\n this.requests = [];\n\n this.subject.next(this.getCurrentPending());\n }\n\n pending(): Observable<PendingRequest<ResultType>> {\n return this.subject;\n }\n\n private getCurrentPending(): PendingRequest<ResultType> {\n const currentScopes =\n this.requests.length === 0\n ? undefined\n : this.requests\n .slice(1)\n .reduce(\n (acc, current) => joinScopes(acc, current.scopes),\n this.requests[0].scopes,\n );\n\n return {\n scopes: currentScopes,\n resolve: (value: ResultType) => {\n if (currentScopes) {\n this.resolve(currentScopes, value);\n }\n },\n reject: (reason: Error) => {\n if (currentScopes) {\n this.reject(reason);\n }\n },\n };\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n OAuthRequestApi,\n PendingOAuthRequest,\n OAuthRequester,\n OAuthRequesterOptions,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport { OAuthPendingRequests, PendingRequest } from './OAuthPendingRequests';\nimport { BehaviorSubject } from '../../../lib/subjects';\n\n/**\n * The OAuthRequestManager is an implementation of the OAuthRequestApi.\n *\n * The purpose of this class and the API is to read a stream of incoming requests\n * of OAuth access tokens from different providers with varying scope, and funnel\n * them all together into a single request for each OAuth provider.\n *\n * @public\n */\nexport class OAuthRequestManager implements OAuthRequestApi {\n private readonly subject = new BehaviorSubject<PendingOAuthRequest[]>([]);\n private currentRequests: PendingOAuthRequest[] = [];\n private handlerCount = 0;\n\n createAuthRequester<T>(options: OAuthRequesterOptions<T>): OAuthRequester<T> {\n if (!options.provider.id) {\n // eslint-disable-next-line no-console\n console.warn(\n 'DEPRECATION WARNING: Not passing a provider id to createAuthRequester is deprecated, it will be required in the future',\n );\n }\n const handler = new OAuthPendingRequests<T>();\n\n const index = this.handlerCount;\n this.handlerCount++;\n\n handler.pending().subscribe({\n next: scopeRequest => {\n const newRequests = this.currentRequests.slice();\n const request = this.makeAuthRequest(scopeRequest, options);\n if (!request) {\n delete newRequests[index];\n } else {\n newRequests[index] = request;\n }\n this.currentRequests = newRequests;\n // Convert from sparse array to array of present items only\n this.subject.next(newRequests.filter(Boolean));\n },\n });\n\n return scopes => {\n return handler.request(scopes);\n };\n }\n\n // Converts the pending request and popup options into a popup request that we can forward to subscribers.\n private makeAuthRequest(\n request: PendingRequest<any>,\n options: OAuthRequesterOptions<any>,\n ): PendingOAuthRequest | undefined {\n const { scopes } = request;\n if (!scopes) {\n return undefined;\n }\n\n return {\n provider: options.provider,\n trigger: async () => {\n const result = await options.onAuthRequest(scopes);\n request.resolve(result);\n },\n reject: () => {\n const error = new Error('Login failed, rejected by user');\n error.name = 'RejectedError';\n request.reject(error);\n },\n };\n }\n\n authRequest$(): Observable<PendingOAuthRequest[]> {\n return this.subject;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n StorageApi,\n StorageValueChange,\n ErrorApi,\n} from '@backstage/core-plugin-api';\nimport { Observable } from '@backstage/types';\nimport ObservableImpl from 'zen-observable';\n\nconst buckets = new Map<string, WebStorage>();\n\n/**\n * An implementation of the storage API, that uses the browser's local storage.\n *\n * @public\n */\nexport class WebStorage implements StorageApi {\n constructor(\n private readonly namespace: string,\n private readonly errorApi: ErrorApi,\n ) {}\n\n static create(options: {\n errorApi: ErrorApi;\n namespace?: string;\n }): WebStorage {\n return new WebStorage(options.namespace ?? '', options.errorApi);\n }\n\n get<T>(key: string): T | undefined {\n try {\n const storage = JSON.parse(localStorage.getItem(this.getKeyName(key))!);\n return storage ?? undefined;\n } catch (e) {\n this.errorApi.post(\n new Error(`Error when parsing JSON config from storage for: ${key}`),\n );\n }\n\n return undefined;\n }\n\n forBucket(name: string): WebStorage {\n const bucketPath = `${this.namespace}/${name}`;\n if (!buckets.has(bucketPath)) {\n buckets.set(bucketPath, new WebStorage(bucketPath, this.errorApi));\n }\n return buckets.get(bucketPath)!;\n }\n\n async set<T>(key: string, data: T): Promise<void> {\n localStorage.setItem(this.getKeyName(key), JSON.stringify(data, null, 2));\n this.notifyChanges({ key, newValue: data });\n }\n\n async remove(key: string): Promise<void> {\n localStorage.removeItem(this.getKeyName(key));\n this.notifyChanges({ key, newValue: undefined });\n }\n\n observe$<T>(key: string): Observable<StorageValueChange<T>> {\n return this.observable.filter(({ key: messageKey }) => messageKey === key);\n }\n\n private getKeyName(key: string) {\n return `${this.namespace}/${encodeURIComponent(key)}`;\n }\n\n private notifyChanges<T>(message: StorageValueChange<T>) {\n for (const subscription of this.subscribers) {\n subscription.next(message);\n }\n }\n\n private subscribers = new Set<\n ZenObservable.SubscriptionObserver<StorageValueChange>\n >();\n\n private readonly observable = new ObservableImpl<StorageValueChange>(\n subscriber => {\n this.subscribers.add(subscriber);\n return () => {\n this.subscribers.delete(subscriber);\n };\n },\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createApp as createDefaultApp } from '@backstage/app-defaults';\nimport { AppContext, BackstageApp } from './types';\n\n/**\n * Creates a new Backstage App.\n *\n * @deprecated Use {@link @backstage/app-defaults#createApp} from `@backstage/app-defaults` instead\n * @param options - A set of options for creating the app\n * @public\n */\nexport function createApp(\n options?: Parameters<typeof createDefaultApp>[0],\n): BackstageApp & AppContext {\n // eslint-disable-next-line no-console\n console.warn(\n 'DEPRECATION WARNING: The createApp function from @backstage/core-app-api will soon be removed, ' +\n 'migrate to importing createApp from the @backstage/app-defaults package instead. ' +\n 'If you do not wish to use a standard app configuration but instead supply all options yourself ' +\n ' you can use createSpecializedApp from @backstage/core-app-api instead.',\n );\n return createDefaultApp(options) as BackstageApp & AppContext;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isValidElement, ReactNode, ReactElement, Children } from 'react';\n\nexport type Discoverer = (element: ReactElement) => ReactNode;\n\nexport type Collector<Result, Context> = () => {\n accumulator: Result;\n visit(\n accumulator: Result,\n element: ReactElement,\n parent: ReactElement | undefined,\n context: Context,\n ): Context;\n};\n\n/**\n * A function that allows you to traverse a tree of React elements using\n * varying methods to discover child nodes and collect data along the way.\n */\nexport function traverseElementTree<Results>(options: {\n root: ReactNode;\n discoverers: Discoverer[];\n collectors: { [name in keyof Results]: Collector<Results[name], any> };\n}): Results {\n const collectors: {\n [name in string]: ReturnType<Collector<any, any>>;\n } = {};\n\n // Bootstrap all collectors, initializing the accumulators and providing the visitor function\n for (const name in options.collectors) {\n if (options.collectors.hasOwnProperty(name)) {\n collectors[name] = options.collectors[name]();\n }\n }\n\n // Internal representation of an element in the tree that we're iterating over\n type QueueItem = {\n node: ReactNode;\n parent: ReactElement | undefined;\n contexts: { [name in string]: unknown };\n };\n\n const queue = [\n {\n node: Children.toArray(options.root),\n parent: undefined,\n contexts: {},\n } as QueueItem,\n ];\n\n while (queue.length !== 0) {\n const { node, parent, contexts } = queue.shift()!;\n\n // While the parent and the element we pass on to collectors and discoverers\n // have been validated and are known to be React elements, the child nodes\n // emitted by the discoverers are not.\n Children.forEach(node, element => {\n if (!isValidElement(element)) {\n return;\n }\n\n const nextContexts: QueueItem['contexts'] = {};\n\n // Collectors populate their result data using the current node, and compute\n // context for the next iteration\n for (const name in collectors) {\n if (collectors.hasOwnProperty(name)) {\n const collector = collectors[name];\n\n nextContexts[name] = collector.visit(\n collector.accumulator,\n element,\n parent,\n contexts[name],\n );\n }\n }\n\n // Discoverers provide ways to continue the traversal from the current element\n for (const discoverer of options.discoverers) {\n const children = discoverer(element);\n if (children) {\n queue.push({\n node: children,\n parent: element,\n contexts: nextContexts,\n });\n }\n }\n });\n }\n\n return Object.fromEntries(\n Object.entries(collectors).map(([name, c]) => [name, c.accumulator]),\n ) as Results;\n}\n\nexport function createCollector<Result, Context>(\n accumulatorFactory: () => Result,\n visit: ReturnType<Collector<Result, Context>>['visit'],\n): Collector<Result, Context> {\n return () => ({ accumulator: accumulatorFactory(), visit });\n}\n\nexport function childDiscoverer(element: ReactElement): ReactNode {\n return element.props?.children;\n}\n\nexport function routeElementDiscoverer(element: ReactElement): ReactNode {\n if (element.props?.path && element.props?.element) {\n return element.props?.element;\n }\n return undefined;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { BackstagePlugin, getComponentData } from '@backstage/core-plugin-api';\nimport { createCollector } from '../extensions/traversal';\n\nexport const pluginCollector = createCollector(\n () => new Set<BackstagePlugin<any, any>>(),\n (acc, node) => {\n const plugin = getComponentData<BackstagePlugin<any, any>>(\n node,\n 'core.plugin',\n );\n if (plugin) {\n acc.add(plugin);\n }\n },\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n featureFlagsApiRef,\n useApi,\n attachComponentData,\n} from '@backstage/core-plugin-api';\nimport React, { ReactNode } from 'react';\n\n/**\n * Props for the {@link FeatureFlagged} component.\n *\n * @public\n */\nexport type FeatureFlaggedProps = { children: ReactNode } & (\n | { with: string }\n | { without: string }\n);\n\n/**\n * Enables or disables rendering of its children based on the state of a given\n * feature flag.\n *\n * @public\n */\nexport const FeatureFlagged = (props: FeatureFlaggedProps) => {\n const { children } = props;\n const featureFlagApi = useApi(featureFlagsApiRef);\n const isEnabled =\n 'with' in props\n ? featureFlagApi.isActive(props.with)\n : !featureFlagApi.isActive(props.without);\n return <>{isEnabled ? children : null}</>;\n};\n\nattachComponentData(FeatureFlagged, 'core.featureFlagged', true);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isValidElement, ReactElement, ReactNode } from 'react';\nimport {\n RouteRef,\n getComponentData,\n BackstagePlugin,\n} from '@backstage/core-plugin-api';\nimport { BackstageRouteObject } from './types';\nimport { createCollector } from '../extensions/traversal';\nimport { FeatureFlagged, FeatureFlaggedProps } from './FeatureFlagged';\n\nfunction getMountPoint(node: ReactElement): RouteRef | undefined {\n const element: ReactNode = node.props?.element;\n\n let routeRef = getComponentData<RouteRef>(node, 'core.mountPoint');\n if (!routeRef && isValidElement(element)) {\n routeRef = getComponentData<RouteRef>(element, 'core.mountPoint');\n }\n\n return routeRef;\n}\n\nexport const routePathCollector = createCollector(\n () => new Map<RouteRef, string>(),\n (acc, node, parent, ctxPath: string | undefined) => {\n // The context path is used during mount point gathering to assign the same path\n // to all discovered mount points\n let currentCtxPath = ctxPath;\n\n if (parent?.props.element === node) {\n return currentCtxPath;\n }\n\n // Start gathering mount points when we encounter a mount point gathering flag\n if (getComponentData<boolean>(node, 'core.gatherMountPoints')) {\n const path: string | undefined = node.props?.path;\n if (!path) {\n throw new Error('Mount point gatherer must have a path');\n }\n currentCtxPath = path;\n }\n\n const routeRef = getMountPoint(node);\n if (routeRef) {\n let path: string | undefined = node.props?.path;\n // If we're gathering mount points we use the context path as out path, unless\n // the element has its own path, in which case we use that instead and stop gathering\n if (currentCtxPath) {\n if (path) {\n currentCtxPath = undefined;\n } else {\n path = currentCtxPath;\n }\n }\n if (!path) {\n throw new Error('Mounted routable extension must have a path');\n }\n acc.set(routeRef, path);\n }\n return currentCtxPath;\n },\n);\n\nexport const routeParentCollector = createCollector(\n () => new Map<RouteRef, RouteRef | undefined>(),\n (acc, node, parent, parentRouteRef?: RouteRef | { sticky: RouteRef }) => {\n if (parent?.props.element === node) {\n return parentRouteRef;\n }\n\n let nextParent = parentRouteRef;\n\n const routeRef = getMountPoint(node);\n if (routeRef) {\n // \"sticky\" route ref is when we've encountered a mount point gatherer, and we want a\n // mount points beneath it to have the same parent, regardless of internal structure\n if (parentRouteRef && 'sticky' in parentRouteRef) {\n acc.set(routeRef, parentRouteRef.sticky);\n\n // When we encounter a mount point with an explicit path, we stop gathering\n // mount points withing the children and remove the sticky state\n if (node.props?.path) {\n nextParent = routeRef;\n } else {\n nextParent = parentRouteRef;\n }\n } else {\n acc.set(routeRef, parentRouteRef);\n nextParent = routeRef;\n }\n }\n\n // Mount point gatherers are marked as \"sticky\"\n if (getComponentData<boolean>(node, 'core.gatherMountPoints')) {\n return { sticky: nextParent };\n }\n\n return nextParent;\n },\n);\n\n// We always add a child that matches all subroutes but without any route refs. This makes\n// sure that we're always able to match each route no matter how deep the navigation goes.\n// The route resolver then takes care of selecting the most specific match in order to find\n// mount points that are as deep in the routing tree as possible.\nexport const MATCH_ALL_ROUTE: BackstageRouteObject = {\n caseSensitive: false,\n path: '/*',\n element: 'match-all', // These elements aren't used, so we add in a bit of debug information\n routeRefs: new Set(),\n};\n\nexport const routeObjectCollector = createCollector(\n () => Array<BackstageRouteObject>(),\n (acc, node, parent, parentObj: BackstageRouteObject | undefined) => {\n const parentChildren = parentObj?.children ?? acc;\n if (parent?.props.element === node) {\n return parentObj;\n }\n\n const path: string | undefined = node.props?.path;\n const caseSensitive: boolean = Boolean(node.props?.caseSensitive);\n\n const routeRef = getMountPoint(node);\n if (routeRef) {\n if (path) {\n const newObject: BackstageRouteObject = {\n caseSensitive,\n path,\n element: 'mounted',\n routeRefs: new Set([routeRef]),\n children: [MATCH_ALL_ROUTE],\n plugin: getComponentData<BackstagePlugin>(\n node.props.element,\n 'core.plugin',\n ),\n };\n parentChildren.push(newObject);\n return newObject;\n }\n\n parentObj?.routeRefs.add(routeRef);\n }\n\n const isGatherer = getComponentData<boolean>(\n node,\n 'core.gatherMountPoints',\n );\n if (isGatherer) {\n if (!path) {\n throw new Error('Mount point gatherer must have a path');\n }\n const newObject: BackstageRouteObject = {\n caseSensitive,\n path,\n element: 'gathered',\n routeRefs: new Set(),\n children: [MATCH_ALL_ROUTE],\n plugin: parentObj?.plugin,\n };\n parentChildren.push(newObject);\n return newObject;\n }\n\n return parentObj;\n },\n);\n\nexport const featureFlagCollector = createCollector(\n () => new Set<string>(),\n (acc, node) => {\n if (node.type === FeatureFlagged) {\n const props = node.props as FeatureFlaggedProps;\n acc.add('with' in props ? props.with : props.without);\n }\n },\n);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n BackstagePlugin,\n} from '@backstage/core-plugin-api';\nimport { getOrCreateGlobalSingleton } from '@backstage/version-bridge';\n\ntype RouteRefType = Exclude<\n keyof RouteRef,\n 'params' | 'path' | 'title' | 'icon'\n>;\nexport const routeRefType: RouteRefType = getOrCreateGlobalSingleton<any>(\n 'route-ref-type',\n () => Symbol('route-ref-type'),\n);\n\nexport type AnyParams = { [param in string]: string } | undefined;\n\nexport type AnyRouteRef =\n | RouteRef<any>\n | SubRouteRef<any>\n | ExternalRouteRef<any, any>;\n\n// The extra TS magic here is to require a single params argument if the RouteRef\n// had at least one param defined, but require 0 arguments if there are no params defined.\n// Without this we'd have to pass in empty object to all parameter-less RouteRefs\n// just to make TypeScript happy, or we would have to make the argument optional in\n// which case you might forget to pass it in when it is actually required.\nexport type RouteFunc<Params extends AnyParams> = (\n ...[params]: Params extends undefined ? readonly [] : readonly [Params]\n) => string;\n\n// A duplicate of the react-router RouteObject, but with routeRef added\nexport interface BackstageRouteObject {\n caseSensitive: boolean;\n children?: BackstageRouteObject[];\n element: React.ReactNode;\n path: string;\n routeRefs: Set<RouteRef>;\n plugin?: BackstagePlugin;\n}\n\nexport function isRouteRef<Params extends AnyParams>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, any>,\n): routeRef is RouteRef<Params> {\n return routeRef[routeRefType] === 'absolute';\n}\n\nexport function isSubRouteRef<Params extends AnyParams>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, any>,\n): routeRef is SubRouteRef<Params> {\n return routeRef[routeRefType] === 'sub';\n}\n\nexport function isExternalRouteRef<\n Params extends AnyParams,\n Optional extends boolean,\n>(\n routeRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, Optional>,\n): routeRef is ExternalRouteRef<Params, Optional> {\n return routeRef[routeRefType] === 'external';\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { generatePath, matchRoutes } from 'react-router-dom';\nimport {\n AnyRouteRef,\n BackstageRouteObject,\n AnyParams,\n RouteFunc,\n routeRefType,\n isRouteRef,\n isSubRouteRef,\n isExternalRouteRef,\n} from './types';\nimport {\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n} from '@backstage/core-plugin-api';\n\n// Joins a list of paths together, avoiding trailing and duplicate slashes\nfunction joinPaths(...paths: string[]): string {\n const normalized = paths.join('/').replace(/\\/\\/+/g, '/');\n if (normalized !== '/' && normalized.endsWith('/')) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\n/**\n * Resolves the absolute route ref that our target route ref is pointing pointing to, as well\n * as the relative target path.\n *\n * Returns an undefined target ref if one could not be fully resolved.\n */\nfunction resolveTargetRef(\n anyRouteRef: AnyRouteRef,\n routePaths: Map<RouteRef, string>,\n routeBindings: Map<AnyRouteRef, AnyRouteRef | undefined>,\n): readonly [RouteRef | undefined, string] {\n // First we figure out which absolute route ref we're dealing with, an if there was an sub route path to append.\n // For sub routes it will be the parent path, while for external routes it will be the bound route.\n let targetRef: RouteRef;\n let subRoutePath = '';\n if (isRouteRef(anyRouteRef)) {\n targetRef = anyRouteRef;\n } else if (isSubRouteRef(anyRouteRef)) {\n targetRef = anyRouteRef.parent;\n subRoutePath = anyRouteRef.path;\n } else if (isExternalRouteRef(anyRouteRef)) {\n const resolvedRoute = routeBindings.get(anyRouteRef);\n if (!resolvedRoute) {\n return [undefined, ''];\n }\n if (isRouteRef(resolvedRoute)) {\n targetRef = resolvedRoute;\n } else if (isSubRouteRef(resolvedRoute)) {\n targetRef = resolvedRoute.parent;\n subRoutePath = resolvedRoute.path;\n } else {\n throw new Error(\n `ExternalRouteRef was bound to invalid target, ${resolvedRoute}`,\n );\n }\n } else if (anyRouteRef[routeRefType]) {\n throw new Error(\n `Unknown or invalid route ref type, ${anyRouteRef[routeRefType]}`,\n );\n } else {\n throw new Error(`Unknown object passed to useRouteRef, got ${anyRouteRef}`);\n }\n\n // Bail if no absolute path could be resolved\n if (!targetRef) {\n return [undefined, ''];\n }\n\n // Find the path that our target route is bound to\n const resolvedPath = routePaths.get(targetRef);\n if (!resolvedPath) {\n return [undefined, ''];\n }\n\n // SubRouteRefs join the path from the parent route with its own path\n const targetPath = joinPaths(resolvedPath, subRoutePath);\n return [targetRef, targetPath];\n}\n\n/**\n * Resolves the complete base path for navigating to the target RouteRef.\n */\nfunction resolveBasePath(\n targetRef: RouteRef,\n sourceLocation: Parameters<typeof matchRoutes>[1],\n routePaths: Map<RouteRef, string>,\n routeParents: Map<RouteRef, RouteRef | undefined>,\n routeObjects: BackstageRouteObject[],\n) {\n // While traversing the app element tree we build up the routeObjects structure\n // used here. It is the same kind of structure that react-router creates, with the\n // addition that associated route refs are stored throughout the tree. This lets\n // us look up all route refs that can be reached from our source location.\n // Because of the similar route object structure, we can use `matchRoutes` from\n // react-router to do the lookup of our current location.\n const match = matchRoutes(routeObjects, sourceLocation) ?? [];\n\n // While we search for a common routing root between our current location and\n // the target route, we build a list of all route refs we find that we need\n // to traverse to reach the target.\n const refDiffList = Array<RouteRef>();\n\n let matchIndex = -1;\n for (\n let targetSearchRef: RouteRef | undefined = targetRef;\n targetSearchRef;\n targetSearchRef = routeParents.get(targetSearchRef)\n ) {\n // The match contains a list of all ancestral route refs present at our current location\n // Starting at the desired target ref and traversing back through its parents, we search\n // for a target ref that is present in the match for our current location. When a match\n // is found it means we have found a common base to resolve the route from.\n matchIndex = match.findIndex(m =>\n (m.route as BackstageRouteObject).routeRefs.has(targetSearchRef!),\n );\n if (matchIndex !== -1) {\n break;\n }\n\n // Every time we move a step up in the ancestry of the target ref, we add the current ref\n // to the diff list, which ends up being the list of route refs to traverse form the common base\n // in order to reach our target.\n refDiffList.unshift(targetSearchRef);\n }\n\n // If our target route is present in the initial match we need to construct the final path\n // from the parent of the matched route segment. That's to allow the caller of the route\n // function to supply their own params.\n if (refDiffList.length === 0) {\n matchIndex -= 1;\n }\n\n // This is the part of the route tree that the target and source locations have in common.\n // We re-use the existing pathname directly along with all params.\n const parentPath = matchIndex === -1 ? '' : match[matchIndex].pathname;\n\n // This constructs the mid section of the path using paths resolved from all route refs\n // we need to traverse to reach our target except for the very last one. None of these\n // paths are allowed to require any parameters, as the caller would have no way of knowing\n // what parameters those are.\n const diffPath = joinPaths(\n ...refDiffList.slice(0, -1).map(ref => {\n const path = routePaths.get(ref);\n if (!path) {\n throw new Error(`No path for ${ref}`);\n }\n if (path.includes(':')) {\n throw new Error(\n `Cannot route to ${targetRef} with parent ${ref} as it has parameters`,\n );\n }\n return path;\n }),\n );\n\n return parentPath + diffPath;\n}\n\nexport class RouteResolver {\n constructor(\n private readonly routePaths: Map<RouteRef, string>,\n private readonly routeParents: Map<RouteRef, RouteRef | undefined>,\n private readonly routeObjects: BackstageRouteObject[],\n private readonly routeBindings: Map<\n ExternalRouteRef,\n RouteRef | SubRouteRef\n >,\n private readonly appBasePath: string, // base path without a trailing slash\n ) {}\n\n resolve<Params extends AnyParams>(\n anyRouteRef:\n | RouteRef<Params>\n | SubRouteRef<Params>\n | ExternalRouteRef<Params, any>,\n sourceLocation: Parameters<typeof matchRoutes>[1],\n ): RouteFunc<Params> | undefined {\n // First figure out what our target absolute ref is, as well as our target path.\n const [targetRef, targetPath] = resolveTargetRef(\n anyRouteRef,\n this.routePaths,\n this.routeBindings,\n );\n if (!targetRef) {\n return undefined;\n }\n\n // The location that we get passed in uses the full path, so start by trimming off\n // the app base path prefix in case we're running the app on a sub-path.\n let relativeSourceLocation: Parameters<typeof matchRoutes>[1];\n if (typeof sourceLocation === 'string') {\n relativeSourceLocation = this.trimPath(sourceLocation);\n } else if (sourceLocation.pathname) {\n relativeSourceLocation = {\n ...sourceLocation,\n pathname: this.trimPath(sourceLocation.pathname),\n };\n } else {\n relativeSourceLocation = sourceLocation;\n }\n\n // Next we figure out the base path, which is the combination of the common parent path\n // between our current location and our target location, as well as the additional path\n // that is the difference between the parent path and the base of our target location.\n const basePath =\n this.appBasePath +\n resolveBasePath(\n targetRef,\n relativeSourceLocation,\n this.routePaths,\n this.routeParents,\n this.routeObjects,\n );\n\n const routeFunc: RouteFunc<Params> = (...[params]) => {\n return basePath + generatePath(targetPath, params);\n };\n return routeFunc;\n }\n\n private trimPath(targetPath: string) {\n if (!targetPath) {\n return targetPath;\n }\n\n if (targetPath.startsWith(this.appBasePath)) {\n return targetPath.slice(this.appBasePath.length);\n }\n return targetPath;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ReactNode } from 'react';\nimport {\n ExternalRouteRef,\n RouteRef,\n SubRouteRef,\n} from '@backstage/core-plugin-api';\nimport {\n createVersionedValueMap,\n createVersionedContext,\n} from '@backstage/version-bridge';\nimport { RouteResolver } from './RouteResolver';\nimport { BackstageRouteObject } from './types';\n\nconst RoutingContext =\n createVersionedContext<{ 1: RouteResolver }>('routing-context');\n\ntype ProviderProps = {\n routePaths: Map<RouteRef, string>;\n routeParents: Map<RouteRef, RouteRef | undefined>;\n routeObjects: BackstageRouteObject[];\n routeBindings: Map<ExternalRouteRef, RouteRef | SubRouteRef>;\n basePath?: string;\n children: ReactNode;\n};\n\nexport const RoutingProvider = ({\n routePaths,\n routeParents,\n routeObjects,\n routeBindings,\n basePath = '',\n children,\n}: ProviderProps) => {\n const resolver = new RouteResolver(\n routePaths,\n routeParents,\n routeObjects,\n routeBindings,\n basePath,\n );\n\n const versionedValue = createVersionedValueMap({ 1: resolver });\n return (\n <RoutingContext.Provider value={versionedValue}>\n {children}\n </RoutingContext.Provider>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect, useMemo } from 'react';\nimport { matchRoutes, useLocation } from 'react-router-dom';\nimport {\n useAnalytics,\n AnalyticsContext,\n CommonAnalyticsContext,\n RouteRef,\n} from '@backstage/core-plugin-api';\nimport { routeObjectCollector } from './collectors';\nimport {\n childDiscoverer,\n routeElementDiscoverer,\n traverseElementTree,\n} from '../extensions/traversal';\nimport { BackstageRouteObject } from './types';\n\n/**\n * Returns an extension context given the current pathname and a list of\n * Backstage route objects.\n */\nconst getExtensionContext = (\n pathname: string,\n routes: BackstageRouteObject[],\n): CommonAnalyticsContext | {} => {\n try {\n // Find matching routes for the given path name.\n const matches = matchRoutes(routes, { pathname }) as\n | { route: BackstageRouteObject }[]\n | null;\n\n // Of the matching routes, get the last (e.g. most specific) instance of\n // the BackstageRouteObject.\n const routeObject = matches\n ?.filter(match => match?.route.routeRefs?.size > 0)\n .pop()?.route;\n\n // If there is no route object, then allow inheritance of default context.\n if (!routeObject) {\n return {};\n }\n\n // If there is a single route ref, return it.\n // todo: get routeRef of rendered gathered mount point(?)\n let routeRef: RouteRef | undefined;\n if (routeObject.routeRefs.size === 1) {\n routeRef = routeObject.routeRefs.values().next().value;\n }\n\n return {\n extension: 'App',\n pluginId: routeObject.plugin?.getId() || 'root',\n ...(routeRef ? { routeRef: (routeRef as { id?: string }).id } : {}),\n };\n } catch {\n return {};\n }\n};\n\n/**\n * Performs the actual event capture on render.\n */\nconst TrackNavigation = ({\n pathname,\n search,\n hash,\n}: {\n pathname: string;\n search: string;\n hash: string;\n}) => {\n const analytics = useAnalytics();\n\n useEffect(() => {\n analytics.captureEvent('navigate', `${pathname}${search}${hash}`);\n }, [analytics, pathname, search, hash]);\n\n return null;\n};\n\n/**\n * Logs a \"navigate\" event with appropriate plugin-level analytics context\n * attributes each time the user navigates to a page.\n */\nexport const RouteTracker = ({ tree }: { tree: React.ReactNode }) => {\n const { pathname, search, hash } = useLocation();\n // todo(iamEAP): Work this into the existing traversal and make the data\n // available on the provider. Then grab from app instance on the router.\n const { routeObjects } = useMemo(() => {\n return traverseElementTree({\n root: tree,\n discoverers: [childDiscoverer, routeElementDiscoverer],\n collectors: {\n routeObjects: routeObjectCollector,\n },\n });\n }, [tree]);\n\n return (\n <AnalyticsContext attributes={getExtensionContext(pathname, routeObjects)}>\n <TrackNavigation pathname={pathname} search={search} hash={hash} />\n </AnalyticsContext>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AnyRouteRef } from './types';\n\nexport function validateRoutes(\n routePaths: Map<AnyRouteRef, string>,\n routeParents: Map<AnyRouteRef, AnyRouteRef | undefined>,\n) {\n const notLeafRoutes = new Set(routeParents.values());\n notLeafRoutes.delete(undefined);\n\n for (const route of routeParents.keys()) {\n if (notLeafRoutes.has(route)) {\n continue;\n }\n\n let currentRouteRef: AnyRouteRef | undefined = route;\n\n let fullPath = '';\n while (currentRouteRef) {\n const path = routePaths.get(currentRouteRef);\n if (!path) {\n throw new Error(`No path for ${currentRouteRef}`);\n }\n fullPath = `${path}${fullPath}`;\n currentRouteRef = routeParents.get(currentRouteRef);\n }\n\n const params = fullPath.match(/:(\\w+)/g);\n if (params) {\n for (let j = 0; j < params.length; j++) {\n for (let i = j + 1; i < params.length; i++) {\n if (params[i] === params[j]) {\n throw new Error(\n `Parameter ${params[i]} is duplicated in path ${fullPath}`,\n );\n }\n }\n }\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { PropsWithChildren } from 'react';\nimport {\n createVersionedValueMap,\n createVersionedContext,\n} from '@backstage/version-bridge';\nimport { AppContext as AppContextV1 } from './types';\n\nconst AppContext = createVersionedContext<{ 1: AppContextV1 }>('app-context');\n\ntype Props = {\n appContext: AppContextV1;\n};\n\nexport const AppContextProvider = ({\n appContext,\n children,\n}: PropsWithChildren<Props>) => {\n const versionedValue = createVersionedValueMap({ 1: appContext });\n\n return <AppContext.Provider value={versionedValue} children={children} />;\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n IdentityApi,\n ProfileInfo,\n BackstageUserIdentity,\n} from '@backstage/core-plugin-api';\n\nfunction mkError(thing: string) {\n return new Error(\n `Tried to access IdentityApi ${thing} before app was loaded`,\n );\n}\n\n/**\n * Implementation of the connection between the App-wide IdentityApi\n * and sign-in page.\n */\nexport class AppIdentityProxy implements IdentityApi {\n private target?: IdentityApi;\n\n // This is called by the app manager once the sign-in page provides us with an implementation\n setTarget(identityApi: IdentityApi) {\n this.target = identityApi;\n }\n\n getUserId(): string {\n if (!this.target) {\n throw mkError('getUserId');\n }\n return this.target.getUserId();\n }\n\n getProfile(): ProfileInfo {\n if (!this.target) {\n throw mkError('getProfile');\n }\n return this.target.getProfile();\n }\n\n async getProfileInfo(): Promise<ProfileInfo> {\n if (!this.target) {\n throw mkError('getProfileInfo');\n }\n return this.target.getProfileInfo();\n }\n\n async getBackstageIdentity(): Promise<BackstageUserIdentity> {\n if (!this.target) {\n throw mkError('getBackstageIdentity');\n }\n return this.target.getBackstageIdentity();\n }\n\n async getCredentials(): Promise<{ token?: string | undefined }> {\n if (!this.target) {\n throw mkError('getCredentials');\n }\n return this.target.getCredentials();\n }\n\n async getIdToken(): Promise<string | undefined> {\n if (!this.target) {\n throw mkError('getIdToken');\n }\n return this.target.getIdToken();\n }\n\n async signOut(): Promise<void> {\n if (!this.target) {\n throw mkError('signOut');\n }\n await this.target.signOut();\n location.reload();\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useMemo, useEffect, useState, PropsWithChildren } from 'react';\nimport { ThemeProvider } from '@material-ui/core/styles';\nimport CssBaseline from '@material-ui/core/CssBaseline';\nimport { useApi, appThemeApiRef, AppTheme } from '@backstage/core-plugin-api';\nimport { useObservable } from 'react-use';\n\n// This tries to find the most accurate match, but also falls back to less\n// accurate results in order to avoid errors.\nfunction resolveTheme(\n themeId: string | undefined,\n shouldPreferDark: boolean,\n themes: AppTheme[],\n) {\n if (themeId !== undefined) {\n const selectedTheme = themes.find(theme => theme.id === themeId);\n if (selectedTheme) {\n return selectedTheme;\n }\n }\n\n if (shouldPreferDark) {\n const darkTheme = themes.find(theme => theme.variant === 'dark');\n if (darkTheme) {\n return darkTheme;\n }\n }\n\n const lightTheme = themes.find(theme => theme.variant === 'light');\n if (lightTheme) {\n return lightTheme;\n }\n\n return themes[0];\n}\n\nconst useShouldPreferDarkTheme = () => {\n const mediaQuery = useMemo(\n () => window.matchMedia('(prefers-color-scheme: dark)'),\n [],\n );\n const [shouldPreferDark, setPrefersDark] = useState(mediaQuery.matches);\n\n useEffect(() => {\n const listener = (event: MediaQueryListEvent) => {\n setPrefersDark(event.matches);\n };\n mediaQuery.addListener(listener);\n return () => {\n mediaQuery.removeListener(listener);\n };\n }, [mediaQuery]);\n\n return shouldPreferDark;\n};\n\nexport function AppThemeProvider({ children }: PropsWithChildren<{}>) {\n const appThemeApi = useApi(appThemeApiRef);\n const themeId = useObservable(\n appThemeApi.activeThemeId$(),\n appThemeApi.getActiveThemeId(),\n );\n\n // Browser feature detection won't change over time, so ignore lint rule\n const shouldPreferDark = Boolean(window.matchMedia)\n ? useShouldPreferDarkTheme() // eslint-disable-line react-hooks/rules-of-hooks\n : false;\n\n const appTheme = resolveTheme(\n themeId,\n shouldPreferDark,\n appThemeApi.getInstalledThemes(),\n );\n if (!appTheme) {\n throw new Error('App has no themes');\n }\n\n if (appTheme.Provider) {\n return <appTheme.Provider children={children} />;\n }\n\n // eslint-disable-next-line no-console\n console.warn(\n \"DEPRECATION WARNING: A provided app theme is using the deprecated 'theme' property \" +\n 'and should be migrated to use a Provider instead. ' +\n 'See https://backstage.io/docs/api/deprecations#app-theme for more info.',\n );\n\n return (\n <ThemeProvider theme={appTheme.theme}>\n <CssBaseline>{children}</CssBaseline>\n </ThemeProvider>\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AppConfig } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\nimport { AppConfigLoader } from './types';\n\n/**\n * The default config loader, which expects that config is available at compile-time\n * in `process.env.APP_CONFIG`. APP_CONFIG should be an array of config objects as\n * returned by the config loader.\n *\n * It will also load runtime config from the __APP_INJECTED_RUNTIME_CONFIG__ string,\n * which can be rewritten at runtime to contain an additional JSON config object.\n * If runtime config is present, it will be placed first in the config array, overriding\n * other config values.\n *\n * @public\n */\nexport const defaultConfigLoader: AppConfigLoader = async (\n // This string may be replaced at runtime to provide additional config.\n // It should be replaced by a JSON-serialized config object.\n // It's a param so we can test it, but at runtime this will always fall back to default.\n runtimeConfigJson: string = '__APP_INJECTED_RUNTIME_CONFIG__',\n) => {\n const appConfig = process.env.APP_CONFIG;\n if (!appConfig) {\n throw new Error('No static configuration provided');\n }\n if (!Array.isArray(appConfig)) {\n throw new Error('Static configuration has invalid format');\n }\n const configs = appConfig.slice() as unknown as AppConfig[];\n\n // Avoiding this string also being replaced at runtime\n if (\n runtimeConfigJson !==\n '__app_injected_runtime_config__'.toLocaleUpperCase('en-US')\n ) {\n try {\n const data = JSON.parse(runtimeConfigJson) as JsonObject;\n if (Array.isArray(data)) {\n configs.push(...data);\n } else {\n configs.push({ data, context: 'env' });\n }\n } catch (error) {\n throw new Error(`Failed to load runtime configuration, ${error}`);\n }\n }\n\n const windowAppConfig = (window as any).__APP_CONFIG__;\n if (windowAppConfig) {\n configs.push({\n context: 'window',\n data: windowAppConfig,\n });\n }\n return configs;\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport React, {\n ComponentType,\n PropsWithChildren,\n ReactElement,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport { Route, Routes } from 'react-router-dom';\nimport { useAsync } from 'react-use';\nimport {\n ApiProvider,\n ApiRegistry,\n AppThemeSelector,\n ConfigReader,\n LocalStorageFeatureFlags,\n} from '../apis';\nimport {\n useApi,\n AnyApiFactory,\n ApiHolder,\n IconComponent,\n AppTheme,\n appThemeApiRef,\n configApiRef,\n AppThemeApi,\n ConfigApi,\n featureFlagsApiRef,\n IdentityApi,\n identityApiRef,\n BackstagePlugin,\n RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n} from '@backstage/core-plugin-api';\nimport { UserIdentity } from '@backstage/core-components';\nimport { ApiFactoryRegistry, ApiResolver } from '../apis/system';\nimport {\n childDiscoverer,\n routeElementDiscoverer,\n traverseElementTree,\n} from '../extensions/traversal';\nimport { pluginCollector } from '../plugins/collectors';\nimport {\n featureFlagCollector,\n routeObjectCollector,\n routeParentCollector,\n routePathCollector,\n} from '../routing/collectors';\nimport { RoutingProvider } from '../routing/RoutingProvider';\nimport { RouteTracker } from '../routing/RouteTracker';\nimport { validateRoutes } from '../routing/validation';\nimport { AppContextProvider } from './AppContext';\nimport { AppIdentityProxy } from '../apis/implementations/IdentityApi/AppIdentityProxy';\nimport {\n AppComponents,\n AppConfigLoader,\n AppContext,\n AppOptions,\n AppRouteBinder,\n BackstageApp,\n SignInPageProps,\n} from './types';\nimport { AppThemeProvider } from './AppThemeProvider';\nimport { defaultConfigLoader } from './defaultConfigLoader';\n\nexport function generateBoundRoutes(bindRoutes: AppOptions['bindRoutes']) {\n const result = new Map<ExternalRouteRef, RouteRef | SubRouteRef>();\n\n if (bindRoutes) {\n const bind: AppRouteBinder = (\n externalRoutes,\n targetRoutes: { [name: string]: RouteRef | SubRouteRef },\n ) => {\n for (const [key, value] of Object.entries(targetRoutes)) {\n const externalRoute = externalRoutes[key];\n if (!externalRoute) {\n throw new Error(`Key ${key} is not an existing external route`);\n }\n if (!value && !externalRoute.optional) {\n throw new Error(\n `External route ${key} is required but was undefined`,\n );\n }\n if (value) {\n result.set(externalRoute, value);\n }\n }\n };\n bindRoutes({ bind });\n }\n\n return result;\n}\n\n/**\n * Get the app base path from the configured app baseUrl.\n *\n * The returned path does not have a trailing slash.\n */\nfunction getBasePath(configApi: Config) {\n let { pathname } = new URL(\n configApi.getOptionalString('app.baseUrl') ?? '/',\n 'http://dummy.dev', // baseUrl can be specified as just a path\n );\n pathname = pathname.replace(/\\/*$/, '');\n return pathname;\n}\n\nfunction useConfigLoader(\n configLoader: AppConfigLoader | undefined,\n components: AppComponents,\n appThemeApi: AppThemeApi,\n): { api: ConfigApi } | { node: JSX.Element } {\n // Keeping this synchronous when a config loader isn't set simplifies tests a lot\n const hasConfig = Boolean(configLoader);\n const config = useAsync(configLoader || (() => Promise.resolve([])));\n\n let noConfigNode = undefined;\n\n if (hasConfig && config.loading) {\n const { Progress } = components;\n noConfigNode = <Progress />;\n } else if (config.error) {\n const { BootErrorPage } = components;\n noConfigNode = <BootErrorPage step=\"load-config\" error={config.error} />;\n }\n\n const { ThemeProvider = AppThemeProvider } = components;\n\n // Before the config is loaded we can't use a router, so exit early\n if (noConfigNode) {\n return {\n node: (\n <ApiProvider apis={ApiRegistry.from([[appThemeApiRef, appThemeApi]])}>\n <ThemeProvider>{noConfigNode}</ThemeProvider>\n </ApiProvider>\n ),\n };\n }\n\n const configReader = ConfigReader.fromConfigs(config.value ?? []);\n\n return { api: configReader };\n}\n\nclass AppContextImpl implements AppContext {\n constructor(private readonly app: AppManager) {}\n\n getPlugins(): BackstagePlugin<any, any>[] {\n return this.app.getPlugins();\n }\n\n getSystemIcon(key: string): IconComponent | undefined {\n return this.app.getSystemIcon(key);\n }\n\n getComponents(): AppComponents {\n return this.app.getComponents();\n }\n}\n\nexport class AppManager implements BackstageApp {\n private apiHolder?: ApiHolder;\n private configApi?: ConfigApi;\n\n private readonly apis: Iterable<AnyApiFactory>;\n private readonly icons: NonNullable<AppOptions['icons']>;\n private readonly plugins: Set<BackstagePlugin<any, any>>;\n private readonly components: AppComponents;\n private readonly themes: AppTheme[];\n private readonly configLoader?: AppConfigLoader;\n private readonly defaultApis: Iterable<AnyApiFactory>;\n private readonly bindRoutes: AppOptions['bindRoutes'];\n\n private readonly appIdentityProxy = new AppIdentityProxy();\n private readonly apiFactoryRegistry: ApiFactoryRegistry;\n\n constructor(options: AppOptions) {\n this.apis = options.apis ?? [];\n this.icons = options.icons;\n this.plugins = new Set(\n (options.plugins as BackstagePlugin<any, any>[]) ?? [],\n );\n this.components = options.components;\n this.themes = options.themes as AppTheme[];\n this.configLoader = options.configLoader ?? defaultConfigLoader;\n this.defaultApis = options.defaultApis ?? [];\n this.bindRoutes = options.bindRoutes;\n this.apiFactoryRegistry = new ApiFactoryRegistry();\n }\n\n getPlugins(): BackstagePlugin<any, any>[] {\n return Array.from(this.plugins);\n }\n\n getSystemIcon(key: string): IconComponent | undefined {\n return this.icons[key];\n }\n\n getComponents(): AppComponents {\n return this.components;\n }\n\n getProvider(): ComponentType<{}> {\n const appContext = new AppContextImpl(this);\n\n const Provider = ({ children }: PropsWithChildren<{}>) => {\n const appThemeApi = useMemo(\n () => AppThemeSelector.createWithStorage(this.themes),\n [],\n );\n\n const { routePaths, routeParents, routeObjects, featureFlags } =\n useMemo(() => {\n const result = traverseElementTree({\n root: children,\n discoverers: [childDiscoverer, routeElementDiscoverer],\n collectors: {\n routePaths: routePathCollector,\n routeParents: routeParentCollector,\n routeObjects: routeObjectCollector,\n collectedPlugins: pluginCollector,\n featureFlags: featureFlagCollector,\n },\n });\n\n validateRoutes(result.routePaths, result.routeParents);\n\n // TODO(Rugvip): Restructure the public API so that we can get an immediate view of\n // the app, rather than having to wait for the provider to render.\n // For now we need to push the additional plugins we find during\n // collection and then make sure we initialize things afterwards.\n result.collectedPlugins.forEach(plugin => this.plugins.add(plugin));\n this.verifyPlugins(this.plugins);\n\n // Initialize APIs once all plugins are available\n this.getApiHolder();\n return result;\n }, [children]);\n\n const loadedConfig = useConfigLoader(\n this.configLoader,\n this.components,\n appThemeApi,\n );\n\n const hasConfigApi = 'api' in loadedConfig;\n if (hasConfigApi) {\n const { api } = loadedConfig as { api: Config };\n this.configApi = api;\n }\n\n useEffect(() => {\n if (hasConfigApi) {\n const featureFlagsApi = this.getApiHolder().get(featureFlagsApiRef)!;\n\n for (const plugin of this.plugins.values()) {\n if ('getFeatureFlags' in plugin) {\n for (const flag of plugin.getFeatureFlags()) {\n featureFlagsApi.registerFlag({\n name: flag.name,\n pluginId: plugin.getId(),\n });\n }\n } else {\n for (const output of plugin.output()) {\n switch (output.type) {\n case 'feature-flag': {\n featureFlagsApi.registerFlag({\n name: output.name,\n pluginId: plugin.getId(),\n });\n break;\n }\n default:\n break;\n }\n }\n }\n }\n\n // Go through the featureFlags returned from the traversal and\n // register those now the configApi has been loaded\n for (const name of featureFlags) {\n featureFlagsApi.registerFlag({ name, pluginId: '' });\n }\n }\n }, [hasConfigApi, loadedConfig, featureFlags]);\n\n if ('node' in loadedConfig) {\n // Loading or error\n return loadedConfig.node;\n }\n\n const { ThemeProvider = AppThemeProvider } = this.components;\n\n return (\n <ApiProvider apis={this.getApiHolder()}>\n <AppContextProvider appContext={appContext}>\n <ThemeProvider>\n <RoutingProvider\n routePaths={routePaths}\n routeParents={routeParents}\n routeObjects={routeObjects}\n routeBindings={generateBoundRoutes(this.bindRoutes)}\n basePath={getBasePath(loadedConfig.api)}\n >\n {children}\n </RoutingProvider>\n </ThemeProvider>\n </AppContextProvider>\n </ApiProvider>\n );\n };\n return Provider;\n }\n\n getRouter(): ComponentType<{}> {\n const { Router: RouterComponent, SignInPage: SignInPageComponent } =\n this.components;\n\n // This wraps the sign-in page and waits for sign-in to be completed before rendering the app\n const SignInPageWrapper = ({\n component: Component,\n children,\n }: {\n component: ComponentType<SignInPageProps>;\n children: ReactElement;\n }) => {\n const [identityApi, setIdentityApi] = useState<IdentityApi>();\n\n if (!identityApi) {\n return <Component onSignInSuccess={setIdentityApi} />;\n }\n\n this.appIdentityProxy.setTarget(identityApi);\n return children;\n };\n\n const AppRouter = ({ children }: PropsWithChildren<{}>) => {\n const configApi = useApi(configApiRef);\n const mountPath = `${getBasePath(configApi)}/*`;\n\n // If the app hasn't configured a sign-in page, we just continue as guest.\n if (!SignInPageComponent) {\n this.appIdentityProxy.setTarget(UserIdentity.createGuest());\n\n return (\n <RouterComponent>\n <RouteTracker tree={children} />\n <Routes>\n <Route path={mountPath} element={<>{children}</>} />\n </Routes>\n </RouterComponent>\n );\n }\n\n return (\n <RouterComponent>\n <RouteTracker tree={children} />\n <SignInPageWrapper component={SignInPageComponent}>\n <Routes>\n <Route path={mountPath} element={<>{children}</>} />\n </Routes>\n </SignInPageWrapper>\n </RouterComponent>\n );\n };\n\n return AppRouter;\n }\n\n private getApiHolder(): ApiHolder {\n if (this.apiHolder) {\n // Register additional plugins if they have been added.\n // Routes paths, objects and others are already updated in the provider when children of it change\n for (const plugin of this.plugins) {\n for (const factory of plugin.getApis()) {\n if (!this.apiFactoryRegistry.get(factory.api)) {\n this.apiFactoryRegistry.register('default', factory);\n }\n }\n }\n ApiResolver.validateFactories(\n this.apiFactoryRegistry,\n this.apiFactoryRegistry.getAllApis(),\n );\n return this.apiHolder;\n }\n this.apiFactoryRegistry.register('static', {\n api: appThemeApiRef,\n deps: {},\n factory: () => AppThemeSelector.createWithStorage(this.themes),\n });\n this.apiFactoryRegistry.register('static', {\n api: configApiRef,\n deps: {},\n factory: () => {\n if (!this.configApi) {\n throw new Error(\n 'Tried to access config API before config was loaded',\n );\n }\n return this.configApi;\n },\n });\n this.apiFactoryRegistry.register('static', {\n api: identityApiRef,\n deps: {},\n factory: () => this.appIdentityProxy,\n });\n\n // It's possible to replace the feature flag API, but since we must have at least\n // one implementation we add it here directly instead of through the defaultApis.\n this.apiFactoryRegistry.register('default', {\n api: featureFlagsApiRef,\n deps: {},\n factory: () => new LocalStorageFeatureFlags(),\n });\n for (const factory of this.defaultApis) {\n this.apiFactoryRegistry.register('default', factory);\n }\n\n for (const plugin of this.plugins) {\n for (const factory of plugin.getApis()) {\n if (!this.apiFactoryRegistry.register('default', factory)) {\n throw new Error(\n `Plugin ${plugin.getId()} tried to register duplicate or forbidden API factory for ${\n factory.api\n }`,\n );\n }\n }\n }\n\n for (const factory of this.apis) {\n if (!this.apiFactoryRegistry.register('app', factory)) {\n throw new Error(\n `Duplicate or forbidden API factory for ${factory.api} in app`,\n );\n }\n }\n\n ApiResolver.validateFactories(\n this.apiFactoryRegistry,\n this.apiFactoryRegistry.getAllApis(),\n );\n\n this.apiHolder = new ApiResolver(this.apiFactoryRegistry);\n return this.apiHolder;\n }\n\n private verifyPlugins(plugins: Iterable<BackstagePlugin>) {\n const pluginIds = new Set<string>();\n\n for (const plugin of plugins) {\n const id = plugin.getId();\n if (pluginIds.has(id)) {\n throw new Error(`Duplicate plugin found '${id}'`);\n }\n pluginIds.add(id);\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AppManager } from './AppManager';\nimport { AppOptions, BackstageApp } from './types';\n\n/**\n * Creates a new Backstage App where the full set of options are required.\n *\n * @public\n * @param options - A set of options for creating the app\n * @returns\n */\nexport function createSpecializedApp(options: AppOptions): BackstageApp {\n return new AppManager(options);\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ReactNode } from 'react';\nimport { useRoutes } from 'react-router-dom';\nimport { useApp, useElementFilter } from '@backstage/core-plugin-api';\n\ntype RouteObject = {\n path: string;\n element: ReactNode;\n children?: RouteObject[];\n};\n\n/**\n * Props for the {@link FlatRoutes} component.\n *\n * @public\n */\nexport type FlatRoutesProps = {\n children: ReactNode;\n};\n\n/**\n * A wrapper around a set of routes.\n *\n * @remarks\n *\n * The root of the routing hierarchy in your app should use this component,\n * instead of the one from `react-router-dom`. This ensures that all of the\n * plugin route and utility API wiring happens under the hood.\n *\n * @public\n */\nexport const FlatRoutes = (props: FlatRoutesProps): JSX.Element | null => {\n const app = useApp();\n const { NotFoundErrorPage } = app.getComponents();\n const routes = useElementFilter(props.children, elements =>\n elements\n .getElements<{ path?: string; children: ReactNode }>()\n .flatMap<RouteObject>(child => {\n let path = child.props.path;\n\n // TODO(Rugvip): Work around plugins registering empty paths, remove once deprecated routes are gone\n if (path === '') {\n return [];\n }\n path = path?.replace(/\\/\\*$/, '') ?? '/';\n\n return [\n {\n path,\n element: child,\n children: child.props.children\n ? [\n // These are the children of each route, which we all add in under a catch-all\n // subroute in order to make them available to `useOutlet`\n {\n path: path === '/' ? '/' : '/*', // The root path must require an exact match\n element: child.props.children,\n },\n ]\n : undefined,\n },\n ];\n })\n // Routes are sorted to work around a bug where prefixes are unexpectedly matched\n .sort((a, b) => b.path.localeCompare(a.path))\n // We make sure all routes have '/*' appended, except '/'\n .map(obj => {\n obj.path = obj.path === '/' ? '/' : `${obj.path}/*`;\n return obj;\n }),\n );\n\n // TODO(Rugvip): Possibly add a way to skip this, like a noNotFoundPage prop\n routes.push({\n element: <NotFoundErrorPage />,\n path: '/*',\n });\n\n return useRoutes(routes);\n};\n"],"names":["hasScopes","DEFAULT_PROVIDER","SCOPE_PREFIX","createDefaultApp"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/core-app-api",
3
3
  "description": "Core app API used by Backstage apps",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "private": false,
6
6
  "publishConfig": {
7
7
  "access": "public",
@@ -31,8 +31,8 @@
31
31
  "dependencies": {
32
32
  "@backstage/app-defaults": "^0.1.2",
33
33
  "@backstage/config": "^0.1.11",
34
- "@backstage/core-components": "^0.8.0",
35
- "@backstage/core-plugin-api": "^0.3.0",
34
+ "@backstage/core-components": "^0.8.1",
35
+ "@backstage/core-plugin-api": "^0.3.1",
36
36
  "@backstage/theme": "^0.2.14",
37
37
  "@backstage/types": "^0.1.1",
38
38
  "@backstage/version-bridge": "^0.1.1",
@@ -49,7 +49,7 @@
49
49
  "react": "^16.13.1 || ^17.0.0"
50
50
  },
51
51
  "devDependencies": {
52
- "@backstage/cli": "^0.10.1",
52
+ "@backstage/cli": "^0.10.2",
53
53
  "@backstage/test-utils": "^0.1.24",
54
54
  "@testing-library/jest-dom": "^5.10.1",
55
55
  "@testing-library/react": "^11.2.5",
@@ -66,5 +66,5 @@
66
66
  "config.d.ts"
67
67
  ],
68
68
  "configSchema": "config.d.ts",
69
- "gitHead": "562be0b43016294e27af3ad024191bb86b13b1c1"
69
+ "gitHead": "9ff0f1e76d4510edda2f1b1b3e58cba168a76190"
70
70
  }