@backstage/core-app-api 1.2.1-next.0 → 1.2.1-next.2

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,31 @@
1
1
  # @backstage/core-app-api
2
2
 
3
+ ## 1.2.1-next.2
4
+
5
+ ### Patch Changes
6
+
7
+ - b4b5b02315: Tweak feature flag registration so that it happens immediately before the first rendering of the app, rather than just after.
8
+ - 203271b746: Prevent duplicate feature flag components from rendering in the settings when using <FeatureFlagged /> components
9
+ - 8015ff1258: Tweaked wording to use inclusive terminology
10
+ - 63310e3987: Apps will now rewrite the `app.baseUrl` configuration to match the current `location.origin`. The `backend.baseUrl` will also be rewritten in the same way when the `app.baseUrl` and `backend.baseUrl` have matching origins. This will reduce the need for separate frontend builds for different environments.
11
+ - Updated dependencies
12
+ - @backstage/core-plugin-api@1.2.0-next.2
13
+ - @backstage/config@1.0.5-next.1
14
+ - @backstage/types@1.0.2-next.1
15
+ - @backstage/version-bridge@1.0.3-next.0
16
+
17
+ ## 1.2.1-next.1
18
+
19
+ ### Patch Changes
20
+
21
+ - d3fea4ae0a: Internal fixes to avoid implicit usage of globals
22
+ - c3fa90e184: Updated dependency `zen-observable` to `^0.10.0`.
23
+ - Updated dependencies
24
+ - @backstage/version-bridge@1.0.3-next.0
25
+ - @backstage/core-plugin-api@1.1.1-next.1
26
+ - @backstage/types@1.0.2-next.1
27
+ - @backstage/config@1.0.5-next.1
28
+
3
29
  ## 1.2.1-next.0
4
30
 
5
31
  ### Patch Changes
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useContext, Children, isValidElement, useEffect, useMemo, useState, createContext } from 'react';
1
+ import React, { useContext, Children, isValidElement, useEffect, useMemo, useState, createContext, useRef } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { createVersionedContext, createVersionedValueMap, getOrCreateGlobalSingleton } from '@backstage/version-bridge';
4
4
  import ObservableImpl from 'zen-observable';
@@ -31,10 +31,13 @@ const ApiProvider = (props) => {
31
31
  const { apis, children } = props;
32
32
  const parentHolder = (_a = useContext(ApiContext)) == null ? void 0 : _a.atVersion(1);
33
33
  const holder = parentHolder ? new ApiAggregator(apis, parentHolder) : apis;
34
- return /* @__PURE__ */ React.createElement(ApiContext.Provider, {
35
- value: createVersionedValueMap({ 1: holder }),
36
- children
37
- });
34
+ return /* @__PURE__ */ React.createElement(
35
+ ApiContext.Provider,
36
+ {
37
+ value: createVersionedValueMap({ 1: holder }),
38
+ children
39
+ }
40
+ );
38
41
  };
39
42
  ApiProvider.propTypes = {
40
43
  apis: PropTypes.shape({ get: PropTypes.func.isRequired }).isRequired,
@@ -279,7 +282,7 @@ class DefaultAuthConnector {
279
282
  const scope = this.joinScopesFunc(scopes);
280
283
  const popupUrl = await this.buildUrl("/start", {
281
284
  scope,
282
- origin: location.origin
285
+ origin: window.location.origin
283
286
  });
284
287
  const payload = await showLoginPopup({
285
288
  url: popupUrl,
@@ -2167,9 +2170,7 @@ const RoutingProvider = ({
2167
2170
  basePath
2168
2171
  );
2169
2172
  const versionedValue = createVersionedValueMap({ 1: resolver });
2170
- return /* @__PURE__ */ React.createElement(RoutingContext.Provider, {
2171
- value: versionedValue
2172
- }, children);
2173
+ return /* @__PURE__ */ React.createElement(RoutingContext.Provider, { value: versionedValue }, children);
2173
2174
  };
2174
2175
 
2175
2176
  const getExtensionContext = (pathname, routes) => {
@@ -2211,13 +2212,7 @@ const RouteTracker = ({
2211
2212
  routeObjects
2212
2213
  }) => {
2213
2214
  const { pathname, search, hash } = useLocation();
2214
- return /* @__PURE__ */ React.createElement(AnalyticsContext, {
2215
- attributes: getExtensionContext(pathname, routeObjects)
2216
- }, /* @__PURE__ */ React.createElement(TrackNavigation, {
2217
- pathname,
2218
- search,
2219
- hash
2220
- }));
2215
+ return /* @__PURE__ */ React.createElement(AnalyticsContext, { attributes: getExtensionContext(pathname, routeObjects) }, /* @__PURE__ */ React.createElement(TrackNavigation, { pathname, search, hash }));
2221
2216
  };
2222
2217
 
2223
2218
  function validateRouteParameters(routePaths, routeParents) {
@@ -2277,10 +2272,7 @@ const AppContextProvider = ({
2277
2272
  children
2278
2273
  }) => {
2279
2274
  const versionedValue = createVersionedValueMap({ 1: appContext });
2280
- return /* @__PURE__ */ React.createElement(AppContext.Provider, {
2281
- value: versionedValue,
2282
- children
2283
- });
2275
+ return /* @__PURE__ */ React.createElement(AppContext.Provider, { value: versionedValue, children });
2284
2276
  };
2285
2277
 
2286
2278
  function mkError(thing) {
@@ -2355,7 +2347,7 @@ class AppIdentityProxy {
2355
2347
  }
2356
2348
  async signOut() {
2357
2349
  await this.waitForTarget.then((target) => target.signOut());
2358
- location.href = this.signOutTargetUrl;
2350
+ window.location.href = this.signOutTargetUrl;
2359
2351
  }
2360
2352
  }
2361
2353
 
@@ -2410,9 +2402,7 @@ function AppThemeProvider({ children }) {
2410
2402
  if (!appTheme) {
2411
2403
  throw new Error("App has no themes");
2412
2404
  }
2413
- return /* @__PURE__ */ React.createElement(appTheme.Provider, {
2414
- children
2415
- });
2405
+ return /* @__PURE__ */ React.createElement(appTheme.Provider, { children });
2416
2406
  }
2417
2407
 
2418
2408
  const defaultConfigLoader = async (runtimeConfigJson = "__APP_INJECTED_RUNTIME_CONFIG__") => {
@@ -2504,10 +2494,7 @@ function resolveRouteBindings(bindRoutes) {
2504
2494
  }
2505
2495
 
2506
2496
  function isReactRouterBeta() {
2507
- const [obj] = createRoutesFromChildren(/* @__PURE__ */ React.createElement(Route, {
2508
- index: true,
2509
- element: /* @__PURE__ */ React.createElement("div", null)
2510
- }));
2497
+ const [obj] = createRoutesFromChildren(/* @__PURE__ */ React.createElement(Route, { index: true, element: /* @__PURE__ */ React.createElement("div", null) }));
2511
2498
  return !obj.index;
2512
2499
  }
2513
2500
 
@@ -2522,7 +2509,7 @@ function readBasePath(configApi) {
2522
2509
  var _a;
2523
2510
  let { pathname } = new URL(
2524
2511
  (_a = configApi.getOptionalString("app.baseUrl")) != null ? _a : "/",
2525
- "http://dummy.dev"
2512
+ "http://sample.dev"
2526
2513
  );
2527
2514
  pathname = pathname.replace(/\/*$/, "");
2528
2515
  return pathname;
@@ -2537,20 +2524,52 @@ function useConfigLoader(configLoader, components, appThemeApi) {
2537
2524
  noConfigNode = /* @__PURE__ */ React.createElement(Progress, null);
2538
2525
  } else if (config.error) {
2539
2526
  const { BootErrorPage } = components;
2540
- noConfigNode = /* @__PURE__ */ React.createElement(BootErrorPage, {
2541
- step: "load-config",
2542
- error: config.error
2543
- });
2527
+ noConfigNode = /* @__PURE__ */ React.createElement(BootErrorPage, { step: "load-config", error: config.error });
2544
2528
  }
2545
2529
  const { ThemeProvider = AppThemeProvider } = components;
2546
2530
  if (noConfigNode) {
2547
2531
  return {
2548
- node: /* @__PURE__ */ React.createElement(ApiProvider, {
2549
- apis: ApiRegistry.with(appThemeApiRef, appThemeApi)
2550
- }, /* @__PURE__ */ React.createElement(ThemeProvider, null, noConfigNode))
2532
+ node: /* @__PURE__ */ React.createElement(ApiProvider, { apis: ApiRegistry.with(appThemeApiRef, appThemeApi) }, /* @__PURE__ */ React.createElement(ThemeProvider, null, noConfigNode))
2551
2533
  };
2552
2534
  }
2553
- const configReader = ConfigReader.fromConfigs((_a = config.value) != null ? _a : []);
2535
+ let configReader;
2536
+ if ((_a = config.value) == null ? void 0 : _a.length) {
2537
+ const urlConfigReader = ConfigReader.fromConfigs(config.value);
2538
+ const getOrigin = (url) => new URL(url).origin;
2539
+ const overrideOrigin = (fullUrl) => {
2540
+ return new URL(
2541
+ fullUrl.replace(getOrigin(fullUrl), ""),
2542
+ document.location.origin
2543
+ ).href;
2544
+ };
2545
+ const appBaseUrl = urlConfigReader.getOptionalString("app.baseUrl");
2546
+ const backendBaseUrl = urlConfigReader.getOptionalString("backend.baseUrl");
2547
+ let configs = config.value;
2548
+ const relativeResolverConfig = {
2549
+ data: {},
2550
+ context: "relative-resolver"
2551
+ };
2552
+ if (appBaseUrl && backendBaseUrl) {
2553
+ const appOrigin = getOrigin(appBaseUrl);
2554
+ const backendOrigin = getOrigin(backendBaseUrl);
2555
+ if (appOrigin === backendOrigin) {
2556
+ relativeResolverConfig.data.backend = {
2557
+ baseUrl: overrideOrigin(backendBaseUrl)
2558
+ };
2559
+ }
2560
+ }
2561
+ if (appBaseUrl) {
2562
+ relativeResolverConfig.data.app = {
2563
+ baseUrl: overrideOrigin(appBaseUrl)
2564
+ };
2565
+ }
2566
+ if (Object.keys(relativeResolverConfig.data).length) {
2567
+ configs = configs.concat([relativeResolverConfig]);
2568
+ }
2569
+ configReader = ConfigReader.fromConfigs(configs);
2570
+ } else {
2571
+ configReader = ConfigReader.fromConfigs([]);
2572
+ }
2554
2573
  return { api: configReader };
2555
2574
  }
2556
2575
  class AppContextImpl {
@@ -2600,6 +2619,7 @@ class AppManager {
2600
2619
  const appContext = new AppContextImpl(this);
2601
2620
  let routesHaveBeenValidated = false;
2602
2621
  const Provider = ({ children }) => {
2622
+ const needsFeatureFlagRegistrationRef = useRef(true);
2603
2623
  const appThemeApi = useMemo(
2604
2624
  () => AppThemeSelector.createWithStorage(this.themes),
2605
2625
  []
@@ -2640,9 +2660,13 @@ class AppManager {
2640
2660
  const { api } = loadedConfig;
2641
2661
  this.configApi = api;
2642
2662
  }
2643
- useEffect(() => {
2644
- if (hasConfigApi) {
2645
- const featureFlagsApi = this.getApiHolder().get(featureFlagsApiRef);
2663
+ if ("node" in loadedConfig) {
2664
+ return loadedConfig.node;
2665
+ }
2666
+ if (hasConfigApi && needsFeatureFlagRegistrationRef.current) {
2667
+ needsFeatureFlagRegistrationRef.current = false;
2668
+ const featureFlagsApi = this.getApiHolder().get(featureFlagsApiRef);
2669
+ if (featureFlagsApi) {
2646
2670
  for (const plugin of this.plugins.values()) {
2647
2671
  if ("getFeatureFlags" in plugin) {
2648
2672
  for (const flag of plugin.getFeatureFlags()) {
@@ -2662,28 +2686,33 @@ class AppManager {
2662
2686
  }
2663
2687
  }
2664
2688
  }
2689
+ const registeredFlags = featureFlagsApi.getRegisteredFlags();
2690
+ const flagNames = new Set(registeredFlags.map((f) => f.name));
2665
2691
  for (const name of featureFlags) {
2666
- featureFlagsApi.registerFlag({ name, pluginId: "" });
2692
+ if (!flagNames.has(name)) {
2693
+ featureFlagsApi.registerFlag({ name, pluginId: "" });
2694
+ }
2667
2695
  }
2668
2696
  }
2669
- }, [hasConfigApi, loadedConfig, featureFlags]);
2670
- if ("node" in loadedConfig) {
2671
- return loadedConfig.node;
2672
2697
  }
2673
2698
  const { ThemeProvider = AppThemeProvider } = this.components;
2674
- return /* @__PURE__ */ React.createElement(ApiProvider, {
2675
- apis: this.getApiHolder()
2676
- }, /* @__PURE__ */ React.createElement(AppContextProvider, {
2677
- appContext
2678
- }, /* @__PURE__ */ React.createElement(ThemeProvider, null, /* @__PURE__ */ React.createElement(RoutingProvider, {
2679
- routePaths: routing.paths,
2680
- routeParents: routing.parents,
2681
- routeObjects: routing.objects,
2682
- routeBindings,
2683
- basePath: getBasePath(loadedConfig.api)
2684
- }, /* @__PURE__ */ React.createElement(InternalAppContext.Provider, {
2685
- value: { routeObjects: routing.objects }
2686
- }, children)))));
2699
+ return /* @__PURE__ */ React.createElement(ApiProvider, { apis: this.getApiHolder() }, /* @__PURE__ */ React.createElement(AppContextProvider, { appContext }, /* @__PURE__ */ React.createElement(ThemeProvider, null, /* @__PURE__ */ React.createElement(
2700
+ RoutingProvider,
2701
+ {
2702
+ routePaths: routing.paths,
2703
+ routeParents: routing.parents,
2704
+ routeObjects: routing.objects,
2705
+ routeBindings,
2706
+ basePath: getBasePath(loadedConfig.api)
2707
+ },
2708
+ /* @__PURE__ */ React.createElement(
2709
+ InternalAppContext.Provider,
2710
+ {
2711
+ value: { routeObjects: routing.objects }
2712
+ },
2713
+ children
2714
+ )
2715
+ ))));
2687
2716
  };
2688
2717
  return Provider;
2689
2718
  }
@@ -2697,9 +2726,7 @@ class AppManager {
2697
2726
  const configApi = useApi(configApiRef);
2698
2727
  const basePath = getBasePath(configApi);
2699
2728
  if (!identityApi) {
2700
- return /* @__PURE__ */ React.createElement(Component, {
2701
- onSignInSuccess: setIdentityApi
2702
- });
2729
+ return /* @__PURE__ */ React.createElement(Component, { onSignInSuccess: setIdentityApi });
2703
2730
  }
2704
2731
  this.appIdentityProxy.setTarget(identityApi, {
2705
2732
  signOutTargetUrl: basePath || "/"
@@ -2736,36 +2763,14 @@ class AppManager {
2736
2763
  { signOutTargetUrl: basePath || "/" }
2737
2764
  );
2738
2765
  if (isReactRouterBeta()) {
2739
- return /* @__PURE__ */ React.createElement(RouterComponent, null, /* @__PURE__ */ React.createElement(RouteTracker, {
2740
- routeObjects
2741
- }), /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, {
2742
- path: mountPath,
2743
- element: /* @__PURE__ */ React.createElement(React.Fragment, null, children)
2744
- })));
2766
+ return /* @__PURE__ */ React.createElement(RouterComponent, null, /* @__PURE__ */ React.createElement(RouteTracker, { routeObjects }), /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, { path: mountPath, element: /* @__PURE__ */ React.createElement(React.Fragment, null, children) })));
2745
2767
  }
2746
- return /* @__PURE__ */ React.createElement(RouterComponent, {
2747
- basename: basePath
2748
- }, /* @__PURE__ */ React.createElement(RouteTracker, {
2749
- routeObjects
2750
- }), children);
2768
+ return /* @__PURE__ */ React.createElement(RouterComponent, { basename: basePath }, /* @__PURE__ */ React.createElement(RouteTracker, { routeObjects }), children);
2751
2769
  }
2752
2770
  if (isReactRouterBeta()) {
2753
- return /* @__PURE__ */ React.createElement(RouterComponent, null, /* @__PURE__ */ React.createElement(RouteTracker, {
2754
- routeObjects
2755
- }), /* @__PURE__ */ React.createElement(SignInPageWrapper, {
2756
- component: SignInPageComponent
2757
- }, /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, {
2758
- path: mountPath,
2759
- element: /* @__PURE__ */ React.createElement(React.Fragment, null, children)
2760
- }))));
2761
- }
2762
- return /* @__PURE__ */ React.createElement(RouterComponent, {
2763
- basename: basePath
2764
- }, /* @__PURE__ */ React.createElement(RouteTracker, {
2765
- routeObjects
2766
- }), /* @__PURE__ */ React.createElement(SignInPageWrapper, {
2767
- component: SignInPageComponent
2768
- }, /* @__PURE__ */ React.createElement(React.Fragment, null, children)));
2771
+ return /* @__PURE__ */ React.createElement(RouterComponent, null, /* @__PURE__ */ React.createElement(RouteTracker, { routeObjects }), /* @__PURE__ */ React.createElement(SignInPageWrapper, { component: SignInPageComponent }, /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, { path: mountPath, element: /* @__PURE__ */ React.createElement(React.Fragment, null, children) }))));
2772
+ }
2773
+ return /* @__PURE__ */ React.createElement(RouterComponent, { basename: basePath }, /* @__PURE__ */ React.createElement(RouteTracker, { routeObjects }), /* @__PURE__ */ React.createElement(SignInPageWrapper, { component: SignInPageComponent }, /* @__PURE__ */ React.createElement(React.Fragment, null, children)));
2769
2774
  };
2770
2775
  return AppRouter;
2771
2776
  }