@backstage/core-app-api 1.2.1-next.1 → 1.2.1-next.3
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 +26 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.esm.js +84 -14
- package/dist/index.esm.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @backstage/core-app-api
|
|
2
2
|
|
|
3
|
+
## 1.2.1-next.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6870b43dd1: Fix for the automatic rewriting of base URLs.
|
|
8
|
+
- 653d7912ac: Made `WebStorage` notify its subscribers when `localStorage` values change in other tabs/windows
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
- @backstage/config@1.0.5-next.1
|
|
11
|
+
- @backstage/core-plugin-api@1.2.0-next.2
|
|
12
|
+
- @backstage/types@1.0.2-next.1
|
|
13
|
+
- @backstage/version-bridge@1.0.3-next.0
|
|
14
|
+
|
|
15
|
+
## 1.2.1-next.2
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- b4b5b02315: Tweak feature flag registration so that it happens immediately before the first rendering of the app, rather than just after.
|
|
20
|
+
- 203271b746: Prevent duplicate feature flag components from rendering in the settings when using <FeatureFlagged /> components
|
|
21
|
+
- 8015ff1258: Tweaked wording to use inclusive terminology
|
|
22
|
+
- 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.
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
- @backstage/core-plugin-api@1.2.0-next.2
|
|
25
|
+
- @backstage/config@1.0.5-next.1
|
|
26
|
+
- @backstage/types@1.0.2-next.1
|
|
27
|
+
- @backstage/version-bridge@1.0.3-next.0
|
|
28
|
+
|
|
3
29
|
## 1.2.1-next.1
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -535,16 +535,19 @@ declare class WebStorage implements StorageApi {
|
|
|
535
535
|
private readonly namespace;
|
|
536
536
|
private readonly errorApi;
|
|
537
537
|
constructor(namespace: string, errorApi: ErrorApi);
|
|
538
|
+
private static hasSubscribed;
|
|
538
539
|
static create(options: {
|
|
539
540
|
errorApi: ErrorApi;
|
|
540
541
|
namespace?: string;
|
|
541
542
|
}): WebStorage;
|
|
543
|
+
private static addStorageEventListener;
|
|
542
544
|
get<T>(key: string): T | undefined;
|
|
543
545
|
snapshot<T extends JsonValue>(key: string): StorageValueSnapshot<T>;
|
|
544
546
|
forBucket(name: string): WebStorage;
|
|
545
547
|
set<T>(key: string, data: T): Promise<void>;
|
|
546
548
|
remove(key: string): Promise<void>;
|
|
547
549
|
observe$<T extends JsonValue>(key: string): Observable<StorageValueSnapshot<T>>;
|
|
550
|
+
private handleStorageChange;
|
|
548
551
|
private getKeyName;
|
|
549
552
|
private notifyChanges;
|
|
550
553
|
private subscribers;
|
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';
|
|
@@ -1613,7 +1613,7 @@ class OAuthRequestManager {
|
|
|
1613
1613
|
}
|
|
1614
1614
|
|
|
1615
1615
|
const buckets = /* @__PURE__ */ new Map();
|
|
1616
|
-
class
|
|
1616
|
+
const _WebStorage = class {
|
|
1617
1617
|
constructor(namespace, errorApi) {
|
|
1618
1618
|
this.namespace = namespace;
|
|
1619
1619
|
this.errorApi = errorApi;
|
|
@@ -1627,7 +1627,17 @@ class WebStorage {
|
|
|
1627
1627
|
}
|
|
1628
1628
|
static create(options) {
|
|
1629
1629
|
var _a;
|
|
1630
|
-
return new
|
|
1630
|
+
return new _WebStorage((_a = options.namespace) != null ? _a : "", options.errorApi);
|
|
1631
|
+
}
|
|
1632
|
+
static addStorageEventListener() {
|
|
1633
|
+
window.addEventListener("storage", (event) => {
|
|
1634
|
+
var _a;
|
|
1635
|
+
for (const [bucketPath, webStorage] of buckets.entries()) {
|
|
1636
|
+
if ((_a = event.key) == null ? void 0 : _a.startsWith(bucketPath)) {
|
|
1637
|
+
webStorage.handleStorageChange(event.key);
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1631
1641
|
}
|
|
1632
1642
|
get(key) {
|
|
1633
1643
|
return this.snapshot(key).value;
|
|
@@ -1656,7 +1666,7 @@ class WebStorage {
|
|
|
1656
1666
|
forBucket(name) {
|
|
1657
1667
|
const bucketPath = `${this.namespace}/${name}`;
|
|
1658
1668
|
if (!buckets.has(bucketPath)) {
|
|
1659
|
-
buckets.set(bucketPath, new
|
|
1669
|
+
buckets.set(bucketPath, new _WebStorage(bucketPath, this.errorApi));
|
|
1660
1670
|
}
|
|
1661
1671
|
return buckets.get(bucketPath);
|
|
1662
1672
|
}
|
|
@@ -1669,8 +1679,21 @@ class WebStorage {
|
|
|
1669
1679
|
this.notifyChanges(key);
|
|
1670
1680
|
}
|
|
1671
1681
|
observe$(key) {
|
|
1682
|
+
if (!_WebStorage.hasSubscribed) {
|
|
1683
|
+
_WebStorage.addStorageEventListener();
|
|
1684
|
+
_WebStorage.hasSubscribed = true;
|
|
1685
|
+
}
|
|
1672
1686
|
return this.observable.filter(({ key: messageKey }) => messageKey === key);
|
|
1673
1687
|
}
|
|
1688
|
+
handleStorageChange(eventKey) {
|
|
1689
|
+
if (!(eventKey == null ? void 0 : eventKey.startsWith(this.namespace))) {
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1692
|
+
const trimmedKey = eventKey == null ? void 0 : eventKey.slice(`${this.namespace}/`.length);
|
|
1693
|
+
if (!trimmedKey.includes("/")) {
|
|
1694
|
+
this.notifyChanges(decodeURIComponent(trimmedKey));
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1674
1697
|
getKeyName(key) {
|
|
1675
1698
|
return `${this.namespace}/${encodeURIComponent(key)}`;
|
|
1676
1699
|
}
|
|
@@ -1680,7 +1703,9 @@ class WebStorage {
|
|
|
1680
1703
|
subscription.next(snapshot);
|
|
1681
1704
|
}
|
|
1682
1705
|
}
|
|
1683
|
-
}
|
|
1706
|
+
};
|
|
1707
|
+
let WebStorage = _WebStorage;
|
|
1708
|
+
WebStorage.hasSubscribed = false;
|
|
1684
1709
|
|
|
1685
1710
|
function traverseElementTree(options) {
|
|
1686
1711
|
const collectors = {};
|
|
@@ -2509,7 +2534,7 @@ function readBasePath(configApi) {
|
|
|
2509
2534
|
var _a;
|
|
2510
2535
|
let { pathname } = new URL(
|
|
2511
2536
|
(_a = configApi.getOptionalString("app.baseUrl")) != null ? _a : "/",
|
|
2512
|
-
"http://
|
|
2537
|
+
"http://sample.dev"
|
|
2513
2538
|
);
|
|
2514
2539
|
pathname = pathname.replace(/\/*$/, "");
|
|
2515
2540
|
return pathname;
|
|
@@ -2532,7 +2557,46 @@ function useConfigLoader(configLoader, components, appThemeApi) {
|
|
|
2532
2557
|
node: /* @__PURE__ */ React.createElement(ApiProvider, { apis: ApiRegistry.with(appThemeApiRef, appThemeApi) }, /* @__PURE__ */ React.createElement(ThemeProvider, null, noConfigNode))
|
|
2533
2558
|
};
|
|
2534
2559
|
}
|
|
2535
|
-
|
|
2560
|
+
let configReader;
|
|
2561
|
+
if ((_a = config.value) == null ? void 0 : _a.length) {
|
|
2562
|
+
const urlConfigReader = ConfigReader.fromConfigs(config.value);
|
|
2563
|
+
const getOrigin = (url) => new URL(url).origin;
|
|
2564
|
+
const overrideOrigin = (fullUrl) => {
|
|
2565
|
+
return new URL(
|
|
2566
|
+
fullUrl.replace(getOrigin(fullUrl), ""),
|
|
2567
|
+
document.location.origin
|
|
2568
|
+
).href.replace(/\/$/, "");
|
|
2569
|
+
};
|
|
2570
|
+
const appBaseUrl = urlConfigReader.getOptionalString("app.baseUrl");
|
|
2571
|
+
const backendBaseUrl = urlConfigReader.getOptionalString("backend.baseUrl");
|
|
2572
|
+
let configs = config.value;
|
|
2573
|
+
const relativeResolverConfig = {
|
|
2574
|
+
data: {},
|
|
2575
|
+
context: "relative-resolver"
|
|
2576
|
+
};
|
|
2577
|
+
if (appBaseUrl && backendBaseUrl) {
|
|
2578
|
+
const appOrigin = getOrigin(appBaseUrl);
|
|
2579
|
+
const backendOrigin = getOrigin(backendBaseUrl);
|
|
2580
|
+
if (appOrigin === backendOrigin) {
|
|
2581
|
+
const newBackendBaseUrl = overrideOrigin(backendBaseUrl);
|
|
2582
|
+
if (backendBaseUrl !== newBackendBaseUrl) {
|
|
2583
|
+
relativeResolverConfig.data.backend = { baseUrl: newBackendBaseUrl };
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
if (appBaseUrl) {
|
|
2588
|
+
const newAppBaseUrl = overrideOrigin(appBaseUrl);
|
|
2589
|
+
if (appBaseUrl !== newAppBaseUrl) {
|
|
2590
|
+
relativeResolverConfig.data.app = { baseUrl: newAppBaseUrl };
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
if (Object.keys(relativeResolverConfig.data).length) {
|
|
2594
|
+
configs = configs.concat([relativeResolverConfig]);
|
|
2595
|
+
}
|
|
2596
|
+
configReader = ConfigReader.fromConfigs(configs);
|
|
2597
|
+
} else {
|
|
2598
|
+
configReader = ConfigReader.fromConfigs([]);
|
|
2599
|
+
}
|
|
2536
2600
|
return { api: configReader };
|
|
2537
2601
|
}
|
|
2538
2602
|
class AppContextImpl {
|
|
@@ -2582,6 +2646,7 @@ class AppManager {
|
|
|
2582
2646
|
const appContext = new AppContextImpl(this);
|
|
2583
2647
|
let routesHaveBeenValidated = false;
|
|
2584
2648
|
const Provider = ({ children }) => {
|
|
2649
|
+
const needsFeatureFlagRegistrationRef = useRef(true);
|
|
2585
2650
|
const appThemeApi = useMemo(
|
|
2586
2651
|
() => AppThemeSelector.createWithStorage(this.themes),
|
|
2587
2652
|
[]
|
|
@@ -2622,9 +2687,13 @@ class AppManager {
|
|
|
2622
2687
|
const { api } = loadedConfig;
|
|
2623
2688
|
this.configApi = api;
|
|
2624
2689
|
}
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2690
|
+
if ("node" in loadedConfig) {
|
|
2691
|
+
return loadedConfig.node;
|
|
2692
|
+
}
|
|
2693
|
+
if (hasConfigApi && needsFeatureFlagRegistrationRef.current) {
|
|
2694
|
+
needsFeatureFlagRegistrationRef.current = false;
|
|
2695
|
+
const featureFlagsApi = this.getApiHolder().get(featureFlagsApiRef);
|
|
2696
|
+
if (featureFlagsApi) {
|
|
2628
2697
|
for (const plugin of this.plugins.values()) {
|
|
2629
2698
|
if ("getFeatureFlags" in plugin) {
|
|
2630
2699
|
for (const flag of plugin.getFeatureFlags()) {
|
|
@@ -2644,13 +2713,14 @@ class AppManager {
|
|
|
2644
2713
|
}
|
|
2645
2714
|
}
|
|
2646
2715
|
}
|
|
2716
|
+
const registeredFlags = featureFlagsApi.getRegisteredFlags();
|
|
2717
|
+
const flagNames = new Set(registeredFlags.map((f) => f.name));
|
|
2647
2718
|
for (const name of featureFlags) {
|
|
2648
|
-
|
|
2719
|
+
if (!flagNames.has(name)) {
|
|
2720
|
+
featureFlagsApi.registerFlag({ name, pluginId: "" });
|
|
2721
|
+
}
|
|
2649
2722
|
}
|
|
2650
2723
|
}
|
|
2651
|
-
}, [hasConfigApi, loadedConfig, featureFlags]);
|
|
2652
|
-
if ("node" in loadedConfig) {
|
|
2653
|
-
return loadedConfig.node;
|
|
2654
2724
|
}
|
|
2655
2725
|
const { ThemeProvider = AppThemeProvider } = this.components;
|
|
2656
2726
|
return /* @__PURE__ */ React.createElement(ApiProvider, { apis: this.getApiHolder() }, /* @__PURE__ */ React.createElement(AppContextProvider, { appContext }, /* @__PURE__ */ React.createElement(ThemeProvider, null, /* @__PURE__ */ React.createElement(
|