@backstage/frontend-app-api 0.7.0 → 0.7.1-next.0

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/apis/implementations/ComponentsApi/DefaultComponentsApi.esm.js +10 -33
  3. package/dist/apis/implementations/ComponentsApi/DefaultComponentsApi.esm.js.map +1 -1
  4. package/dist/apis/implementations/IconsApi/DefaultIconsApi.esm.js +4 -24
  5. package/dist/apis/implementations/IconsApi/DefaultIconsApi.esm.js.map +1 -1
  6. package/dist/app-defaults/src/defaults/components.esm.js +1 -1
  7. package/dist/app-defaults/src/defaults/components.esm.js.map +1 -1
  8. package/dist/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.esm.js +26 -51
  9. package/dist/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.esm.js.map +1 -1
  10. package/dist/core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.esm.js +2 -10
  11. package/dist/core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.esm.js.map +1 -1
  12. package/dist/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.esm.js +10 -37
  13. package/dist/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.esm.js.map +1 -1
  14. package/dist/core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.esm.js +4 -4
  15. package/dist/core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.esm.js.map +1 -1
  16. package/dist/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js +85 -126
  17. package/dist/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js.map +1 -1
  18. package/dist/core-app-api/src/app/isProtectedApp.esm.js +1 -2
  19. package/dist/core-app-api/src/app/isProtectedApp.esm.js.map +1 -1
  20. package/dist/core-app-api/src/lib/subjects.esm.js +5 -11
  21. package/dist/core-app-api/src/lib/subjects.esm.js.map +1 -1
  22. package/dist/extensions/AppNav.esm.js +2 -4
  23. package/dist/extensions/AppNav.esm.js.map +1 -1
  24. package/dist/extensions/AppRoot.esm.js +2 -3
  25. package/dist/extensions/AppRoot.esm.js.map +1 -1
  26. package/dist/extensions/components.esm.js +1 -1
  27. package/dist/extensions/components.esm.js.map +1 -1
  28. package/dist/frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js +1 -2
  29. package/dist/frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js.map +1 -1
  30. package/dist/routing/RouteResolver.esm.js +2 -4
  31. package/dist/routing/RouteResolver.esm.js.map +1 -1
  32. package/dist/routing/RouteTracker.esm.js +7 -11
  33. package/dist/routing/RouteTracker.esm.js.map +1 -1
  34. package/dist/routing/extractRouteInfoFromAppNode.esm.js +5 -6
  35. package/dist/routing/extractRouteInfoFromAppNode.esm.js.map +1 -1
  36. package/dist/routing/getBasePath.esm.js +1 -2
  37. package/dist/routing/getBasePath.esm.js.map +1 -1
  38. package/dist/routing/resolveRouteBindings.esm.js +1 -2
  39. package/dist/routing/resolveRouteBindings.esm.js.map +1 -1
  40. package/dist/tree/instantiateAppNodeTree.esm.js +4 -7
  41. package/dist/tree/instantiateAppNodeTree.esm.js.map +1 -1
  42. package/dist/tree/resolveAppNodeSpecs.esm.js +4 -6
  43. package/dist/tree/resolveAppNodeSpecs.esm.js.map +1 -1
  44. package/dist/tree/resolveAppTree.esm.js +6 -12
  45. package/dist/tree/resolveAppTree.esm.js.map +1 -1
  46. package/dist/wiring/createApp.esm.js +13 -24
  47. package/dist/wiring/createApp.esm.js.map +1 -1
  48. package/dist/wiring/discovery.esm.js +3 -5
  49. package/dist/wiring/discovery.esm.js.map +1 -1
  50. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @backstage/frontend-app-api
2
2
 
3
+ ## 0.7.1-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/theme@0.5.6-next.0
9
+ - @backstage/core-components@0.14.8-next.0
10
+ - @backstage/config@1.2.0
11
+ - @backstage/core-app-api@1.12.5
12
+ - @backstage/core-plugin-api@1.9.2
13
+ - @backstage/errors@1.2.4
14
+ - @backstage/frontend-plugin-api@0.6.6-next.0
15
+ - @backstage/types@1.1.1
16
+ - @backstage/version-bridge@1.0.8
17
+
3
18
  ## 0.7.0
4
19
 
5
20
  ### Minor Changes
@@ -1,34 +1,10 @@
1
1
  import { createComponentExtension } from '@backstage/frontend-plugin-api';
2
2
 
3
- var __accessCheck = (obj, member, msg) => {
4
- if (!member.has(obj))
5
- throw TypeError("Cannot " + msg);
6
- };
7
- var __privateGet = (obj, member, getter) => {
8
- __accessCheck(obj, member, "read from private field");
9
- return member.get(obj);
10
- };
11
- var __privateAdd = (obj, member, value) => {
12
- if (member.has(obj))
13
- throw TypeError("Cannot add the same private member more than once");
14
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
15
- };
16
- var __privateSet = (obj, member, value, setter) => {
17
- __accessCheck(obj, member, "write to private field");
18
- member.set(obj, value);
19
- return value;
20
- };
21
- var _components;
22
- const _DefaultComponentsApi = class _DefaultComponentsApi {
23
- constructor(components) {
24
- __privateAdd(this, _components, void 0);
25
- __privateSet(this, _components, components);
26
- }
3
+ class DefaultComponentsApi {
4
+ #components;
27
5
  static fromTree(tree) {
28
- var _a;
29
- const componentEntries = (_a = tree.root.edges.attachments.get("components")) == null ? void 0 : _a.reduce((map, e) => {
30
- var _a2;
31
- const data = (_a2 = e.instance) == null ? void 0 : _a2.getData(
6
+ const componentEntries = tree.root.edges.attachments.get("components")?.reduce((map, e) => {
7
+ const data = e.instance?.getData(
32
8
  createComponentExtension.componentDataRef
33
9
  );
34
10
  if (data) {
@@ -36,18 +12,19 @@ const _DefaultComponentsApi = class _DefaultComponentsApi {
36
12
  }
37
13
  return map;
38
14
  }, /* @__PURE__ */ new Map());
39
- return new _DefaultComponentsApi(componentEntries != null ? componentEntries : /* @__PURE__ */ new Map());
15
+ return new DefaultComponentsApi(componentEntries ?? /* @__PURE__ */ new Map());
16
+ }
17
+ constructor(components) {
18
+ this.#components = components;
40
19
  }
41
20
  getComponent(ref) {
42
- const impl = __privateGet(this, _components).get(ref.id);
21
+ const impl = this.#components.get(ref.id);
43
22
  if (!impl) {
44
23
  throw new Error(`No implementation found for component ref ${ref}`);
45
24
  }
46
25
  return impl;
47
26
  }
48
- };
49
- _components = new WeakMap();
50
- let DefaultComponentsApi = _DefaultComponentsApi;
27
+ }
51
28
 
52
29
  export { DefaultComponentsApi };
53
30
  //# sourceMappingURL=DefaultComponentsApi.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultComponentsApi.esm.js","sources":["../../../../src/apis/implementations/ComponentsApi/DefaultComponentsApi.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { ComponentType } from 'react';\nimport {\n AppTree,\n ComponentRef,\n ComponentsApi,\n createComponentExtension,\n} from '@backstage/frontend-plugin-api';\n\n/**\n * Implementation for the {@linkComponentApi}\n *\n * @internal\n */\nexport class DefaultComponentsApi implements ComponentsApi {\n #components: Map<string, ComponentType<any>>;\n\n static fromTree(tree: AppTree) {\n const componentEntries = tree.root.edges.attachments\n .get('components')\n ?.reduce((map, e) => {\n const data = e.instance?.getData(\n createComponentExtension.componentDataRef,\n );\n if (data) {\n map.set(data.ref.id, data.impl);\n }\n return map;\n }, new Map<string, ComponentType>());\n return new DefaultComponentsApi(componentEntries ?? new Map());\n }\n\n constructor(components: Map<string, any>) {\n this.#components = components;\n }\n\n getComponent<T extends {}>(ref: ComponentRef<T>): ComponentType<T> {\n const impl = this.#components.get(ref.id);\n if (!impl) {\n throw new Error(`No implementation found for component ref ${ref}`);\n }\n return impl;\n }\n}\n"],"names":["_a"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,IAAA,WAAA,CAAA;AA6BO,MAAM,qBAAA,GAAN,MAAM,qBAA8C,CAAA;AAAA,EAkBzD,YAAY,UAA8B,EAAA;AAjB1C,IAAA,YAAA,CAAA,IAAA,EAAA,WAAA,EAAA,KAAA,CAAA,CAAA,CAAA;AAkBE,IAAA,YAAA,CAAA,IAAA,EAAK,WAAc,EAAA,UAAA,CAAA,CAAA;AAAA,GACrB;AAAA,EAjBA,OAAO,SAAS,IAAe,EAAA;AAhCjC,IAAA,IAAA,EAAA,CAAA;AAiCI,IAAA,MAAM,gBAAmB,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,IAAK,CAAA,KAAA,CAAM,WACtC,CAAA,GAAA,CAAI,YAAY,CAAA,KADM,IAErB,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,MAAA,CAAO,CAAC,GAAA,EAAK,CAAM,KAAA;AAnC3B,MAAAA,IAAAA,GAAAA,CAAAA;AAoCQ,MAAA,MAAM,IAAOA,GAAAA,CAAAA,GAAAA,GAAA,CAAE,CAAA,QAAA,KAAF,gBAAAA,GAAY,CAAA,OAAA;AAAA,QACvB,wBAAyB,CAAA,gBAAA;AAAA,OAAA,CAAA;AAE3B,MAAA,IAAI,IAAM,EAAA;AACR,QAAA,GAAA,CAAI,GAAI,CAAA,IAAA,CAAK,GAAI,CAAA,EAAA,EAAI,KAAK,IAAI,CAAA,CAAA;AAAA,OAChC;AACA,MAAO,OAAA,GAAA,CAAA;AAAA,KACT,sBAAO,GAA2B,EAAA,CAAA,CAAA;AACpC,IAAA,OAAO,IAAI,qBAAA,CAAqB,gBAAoB,IAAA,IAAA,GAAA,gBAAA,mBAAA,IAAI,KAAK,CAAA,CAAA;AAAA,GAC/D;AAAA,EAMA,aAA2B,GAAwC,EAAA;AACjE,IAAA,MAAM,IAAO,GAAA,YAAA,CAAA,IAAA,EAAK,WAAY,CAAA,CAAA,GAAA,CAAI,IAAI,EAAE,CAAA,CAAA;AACxC,IAAA,IAAI,CAAC,IAAM,EAAA;AACT,MAAA,MAAM,IAAI,KAAA,CAAM,CAA6C,0CAAA,EAAA,GAAG,CAAE,CAAA,CAAA,CAAA;AAAA,KACpE;AACA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AACF,CAAA,CAAA;AA5BE,WAAA,GAAA,IAAA,OAAA,EAAA,CAAA;AADK,IAAM,oBAAN,GAAA;;;;"}
1
+ {"version":3,"file":"DefaultComponentsApi.esm.js","sources":["../../../../src/apis/implementations/ComponentsApi/DefaultComponentsApi.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { ComponentType } from 'react';\nimport {\n AppTree,\n ComponentRef,\n ComponentsApi,\n createComponentExtension,\n} from '@backstage/frontend-plugin-api';\n\n/**\n * Implementation for the {@linkComponentApi}\n *\n * @internal\n */\nexport class DefaultComponentsApi implements ComponentsApi {\n #components: Map<string, ComponentType<any>>;\n\n static fromTree(tree: AppTree) {\n const componentEntries = tree.root.edges.attachments\n .get('components')\n ?.reduce((map, e) => {\n const data = e.instance?.getData(\n createComponentExtension.componentDataRef,\n );\n if (data) {\n map.set(data.ref.id, data.impl);\n }\n return map;\n }, new Map<string, ComponentType>());\n return new DefaultComponentsApi(componentEntries ?? new Map());\n }\n\n constructor(components: Map<string, any>) {\n this.#components = components;\n }\n\n getComponent<T extends {}>(ref: ComponentRef<T>): ComponentType<T> {\n const impl = this.#components.get(ref.id);\n if (!impl) {\n throw new Error(`No implementation found for component ref ${ref}`);\n }\n return impl;\n }\n}\n"],"names":[],"mappings":";;AA6BO,MAAM,oBAA8C,CAAA;AAAA,EACzD,WAAA,CAAA;AAAA,EAEA,OAAO,SAAS,IAAe,EAAA;AAC7B,IAAM,MAAA,gBAAA,GAAmB,IAAK,CAAA,IAAA,CAAK,KAAM,CAAA,WAAA,CACtC,GAAI,CAAA,YAAY,CACf,EAAA,MAAA,CAAO,CAAC,GAAA,EAAK,CAAM,KAAA;AACnB,MAAM,MAAA,IAAA,GAAO,EAAE,QAAU,EAAA,OAAA;AAAA,QACvB,wBAAyB,CAAA,gBAAA;AAAA,OAC3B,CAAA;AACA,MAAA,IAAI,IAAM,EAAA;AACR,QAAA,GAAA,CAAI,GAAI,CAAA,IAAA,CAAK,GAAI,CAAA,EAAA,EAAI,KAAK,IAAI,CAAA,CAAA;AAAA,OAChC;AACA,MAAO,OAAA,GAAA,CAAA;AAAA,KACT,kBAAO,IAAA,GAAA,EAA4B,CAAA,CAAA;AACrC,IAAA,OAAO,IAAI,oBAAA,CAAqB,gBAAoB,oBAAA,IAAI,KAAK,CAAA,CAAA;AAAA,GAC/D;AAAA,EAEA,YAAY,UAA8B,EAAA;AACxC,IAAA,IAAA,CAAK,WAAc,GAAA,UAAA,CAAA;AAAA,GACrB;AAAA,EAEA,aAA2B,GAAwC,EAAA;AACjE,IAAA,MAAM,IAAO,GAAA,IAAA,CAAK,WAAY,CAAA,GAAA,CAAI,IAAI,EAAE,CAAA,CAAA;AACxC,IAAA,IAAI,CAAC,IAAM,EAAA;AACT,MAAA,MAAM,IAAI,KAAA,CAAM,CAA6C,0CAAA,EAAA,GAAG,CAAE,CAAA,CAAA,CAAA;AAAA,KACpE;AACA,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AACF;;;;"}
@@ -1,35 +1,15 @@
1
- var __accessCheck = (obj, member, msg) => {
2
- if (!member.has(obj))
3
- throw TypeError("Cannot " + msg);
4
- };
5
- var __privateGet = (obj, member, getter) => {
6
- __accessCheck(obj, member, "read from private field");
7
- return member.get(obj);
8
- };
9
- var __privateAdd = (obj, member, value) => {
10
- if (member.has(obj))
11
- throw TypeError("Cannot add the same private member more than once");
12
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
13
- };
14
- var __privateSet = (obj, member, value, setter) => {
15
- __accessCheck(obj, member, "write to private field");
16
- member.set(obj, value);
17
- return value;
18
- };
19
- var _icons;
20
1
  class DefaultIconsApi {
2
+ #icons;
21
3
  constructor(icons) {
22
- __privateAdd(this, _icons, void 0);
23
- __privateSet(this, _icons, new Map(Object.entries(icons)));
4
+ this.#icons = new Map(Object.entries(icons));
24
5
  }
25
6
  getIcon(key) {
26
- return __privateGet(this, _icons).get(key);
7
+ return this.#icons.get(key);
27
8
  }
28
9
  listIconKeys() {
29
- return Array.from(__privateGet(this, _icons).keys());
10
+ return Array.from(this.#icons.keys());
30
11
  }
31
12
  }
32
- _icons = new WeakMap();
33
13
 
34
14
  export { DefaultIconsApi };
35
15
  //# sourceMappingURL=DefaultIconsApi.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultIconsApi.esm.js","sources":["../../../../src/apis/implementations/IconsApi/DefaultIconsApi.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { IconComponent, IconsApi } from '@backstage/frontend-plugin-api';\n\n/**\n * Implementation for the {@link IconsApi}\n *\n * @internal\n */\nexport class DefaultIconsApi implements IconsApi {\n #icons: Map<string, IconComponent>;\n\n constructor(icons: { [key in string]: IconComponent }) {\n this.#icons = new Map(Object.entries(icons));\n }\n\n getIcon(key: string): IconComponent | undefined {\n return this.#icons.get(key);\n }\n\n listIconKeys(): string[] {\n return Array.from(this.#icons.keys());\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,IAAA,MAAA,CAAA;AAuBO,MAAM,eAAoC,CAAA;AAAA,EAG/C,YAAY,KAA2C,EAAA;AAFvD,IAAA,YAAA,CAAA,IAAA,EAAA,MAAA,EAAA,KAAA,CAAA,CAAA,CAAA;AAGE,IAAA,YAAA,CAAA,IAAA,EAAK,QAAS,IAAI,GAAA,CAAI,MAAO,CAAA,OAAA,CAAQ,KAAK,CAAC,CAAA,CAAA,CAAA;AAAA,GAC7C;AAAA,EAEA,QAAQ,GAAwC,EAAA;AAC9C,IAAO,OAAA,YAAA,CAAA,IAAA,EAAK,MAAO,CAAA,CAAA,GAAA,CAAI,GAAG,CAAA,CAAA;AAAA,GAC5B;AAAA,EAEA,YAAyB,GAAA;AACvB,IAAA,OAAO,KAAM,CAAA,IAAA,CAAK,YAAK,CAAA,IAAA,EAAA,MAAA,CAAA,CAAO,MAAM,CAAA,CAAA;AAAA,GACtC;AACF,CAAA;AAbE,MAAA,GAAA,IAAA,OAAA,EAAA;;;;"}
1
+ {"version":3,"file":"DefaultIconsApi.esm.js","sources":["../../../../src/apis/implementations/IconsApi/DefaultIconsApi.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { IconComponent, IconsApi } from '@backstage/frontend-plugin-api';\n\n/**\n * Implementation for the {@link IconsApi}\n *\n * @internal\n */\nexport class DefaultIconsApi implements IconsApi {\n #icons: Map<string, IconComponent>;\n\n constructor(icons: { [key in string]: IconComponent }) {\n this.#icons = new Map(Object.entries(icons));\n }\n\n getIcon(key: string): IconComponent | undefined {\n return this.#icons.get(key);\n }\n\n listIconKeys(): string[] {\n return Array.from(this.#icons.keys());\n }\n}\n"],"names":[],"mappings":"AAuBO,MAAM,eAAoC,CAAA;AAAA,EAC/C,MAAA,CAAA;AAAA,EAEA,YAAY,KAA2C,EAAA;AACrD,IAAA,IAAA,CAAK,SAAS,IAAI,GAAA,CAAI,MAAO,CAAA,OAAA,CAAQ,KAAK,CAAC,CAAA,CAAA;AAAA,GAC7C;AAAA,EAEA,QAAQ,GAAwC,EAAA;AAC9C,IAAO,OAAA,IAAA,CAAK,MAAO,CAAA,GAAA,CAAI,GAAG,CAAA,CAAA;AAAA,GAC5B;AAAA,EAEA,YAAyB,GAAA;AACvB,IAAA,OAAO,KAAM,CAAA,IAAA,CAAK,IAAK,CAAA,MAAA,CAAO,MAAM,CAAA,CAAA;AAAA,GACtC;AACF;;;;"}
@@ -27,7 +27,7 @@ const DefaultErrorBoundaryFallback = ({
27
27
  return /* @__PURE__ */ React.createElement(
28
28
  ErrorPanel,
29
29
  {
30
- title: `Error in ${plugin == null ? void 0 : plugin.getId()}`,
30
+ title: `Error in ${plugin?.getId()}`,
31
31
  defaultExpanded: true,
32
32
  error
33
33
  },
@@ -1 +1 @@
1
- {"version":3,"file":"components.esm.js","sources":["../../../../../app-defaults/src/defaults/components.tsx"],"sourcesContent":["/*\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, { ReactNode } from 'react';\nimport Button from '@material-ui/core/Button';\nimport { ErrorPanel, Progress, ErrorPage } from '@backstage/core-components';\nimport {\n MemoryRouter,\n useInRouterContext,\n BrowserRouter,\n} from 'react-router-dom';\nimport {\n AppComponents,\n BootErrorPageProps,\n ErrorBoundaryFallbackProps,\n} from '@backstage/core-plugin-api';\n\nexport function OptionallyWrapInRouter({ children }: { children: ReactNode }) {\n if (useInRouterContext()) {\n return <>{children}</>;\n }\n return <MemoryRouter>{children}</MemoryRouter>;\n}\n\nconst DefaultNotFoundPage = () => (\n <ErrorPage status=\"404\" statusMessage=\"PAGE NOT FOUND\" />\n);\n\nconst DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => {\n let message = '';\n if (step === 'load-config') {\n message = `The configuration failed to load, someone should have a look at this error: ${error.message}`;\n } else if (step === 'load-chunk') {\n message = `Lazy loaded chunk failed to load, try to reload the page: ${error.message}`;\n }\n // TODO: figure out a nicer way to handle routing on the error page, when it can be done.\n return (\n <OptionallyWrapInRouter>\n <ErrorPage statusMessage={message} stack={error.stack} />\n </OptionallyWrapInRouter>\n );\n};\n\nconst DefaultErrorBoundaryFallback = ({\n error,\n resetError,\n plugin,\n}: ErrorBoundaryFallbackProps) => {\n return (\n <ErrorPanel\n title={`Error in ${plugin?.getId()}`}\n defaultExpanded\n error={error}\n >\n <Button variant=\"outlined\" onClick={resetError}>\n Retry\n </Button>\n </ErrorPanel>\n );\n};\n\n/**\n * Creates a set of default components to pass along to {@link @backstage/core-app-api#createSpecializedApp}.\n *\n * @public\n */\nexport const components: AppComponents = {\n Progress,\n Router: BrowserRouter,\n NotFoundErrorPage: DefaultNotFoundPage,\n BootErrorPage: DefaultBootErrorPage,\n ErrorBoundaryFallback: DefaultErrorBoundaryFallback,\n};\n"],"names":[],"mappings":";;;;;AA8BgB,SAAA,sBAAA,CAAuB,EAAE,QAAA,EAAqC,EAAA;AAC5E,EAAA,IAAI,oBAAsB,EAAA;AACxB,IAAA,iEAAU,QAAS,CAAA,CAAA;AAAA,GACrB;AACA,EAAO,uBAAA,KAAA,CAAA,aAAA,CAAC,oBAAc,QAAS,CAAA,CAAA;AACjC,CAAA;AAEA,MAAM,sBAAsB,sBAC1B,KAAA,CAAA,aAAA,CAAC,aAAU,MAAO,EAAA,KAAA,EAAM,eAAc,gBAAiB,EAAA,CAAA,CAAA;AAGzD,MAAM,oBAAuB,GAAA,CAAC,EAAE,IAAA,EAAM,OAAgC,KAAA;AACpE,EAAA,IAAI,OAAU,GAAA,EAAA,CAAA;AACd,EAAA,IAAI,SAAS,aAAe,EAAA;AAC1B,IAAU,OAAA,GAAA,CAAA,4EAAA,EAA+E,MAAM,OAAO,CAAA,CAAA,CAAA;AAAA,GACxG,MAAA,IAAW,SAAS,YAAc,EAAA;AAChC,IAAU,OAAA,GAAA,CAAA,0DAAA,EAA6D,MAAM,OAAO,CAAA,CAAA,CAAA;AAAA,GACtF;AAEA,EACE,uBAAA,KAAA,CAAA,aAAA,CAAC,8CACE,KAAA,CAAA,aAAA,CAAA,SAAA,EAAA,EAAU,eAAe,OAAS,EAAA,KAAA,EAAO,KAAM,CAAA,KAAA,EAAO,CACzD,CAAA,CAAA;AAEJ,CAAA,CAAA;AAEA,MAAM,+BAA+B,CAAC;AAAA,EACpC,KAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AACF,CAAkC,KAAA;AAChC,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAY,SAAA,EAAA,MAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,MAAA,CAAQ,KAAO,EAAA,CAAA,CAAA;AAAA,MAClC,eAAe,EAAA,IAAA;AAAA,MACf,KAAA;AAAA,KAAA;AAAA,wCAEC,MAAO,EAAA,EAAA,OAAA,EAAQ,UAAW,EAAA,OAAA,EAAS,cAAY,OAEhD,CAAA;AAAA,GACF,CAAA;AAEJ,CAAA,CAAA;AAOO,MAAM,UAA4B,GAAA;AAAA,EACvC,QAAA;AAAA,EACA,MAAQ,EAAA,aAAA;AAAA,EACR,iBAAmB,EAAA,mBAAA;AAAA,EACnB,aAAe,EAAA,oBAAA;AAAA,EACf,qBAAuB,EAAA,4BAAA;AACzB;;;;"}
1
+ {"version":3,"file":"components.esm.js","sources":["../../../../../app-defaults/src/defaults/components.tsx"],"sourcesContent":["/*\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, { ReactNode } from 'react';\nimport Button from '@material-ui/core/Button';\nimport { ErrorPanel, Progress, ErrorPage } from '@backstage/core-components';\nimport {\n MemoryRouter,\n useInRouterContext,\n BrowserRouter,\n} from 'react-router-dom';\nimport {\n AppComponents,\n BootErrorPageProps,\n ErrorBoundaryFallbackProps,\n} from '@backstage/core-plugin-api';\n\nexport function OptionallyWrapInRouter({ children }: { children: ReactNode }) {\n if (useInRouterContext()) {\n return <>{children}</>;\n }\n return <MemoryRouter>{children}</MemoryRouter>;\n}\n\nconst DefaultNotFoundPage = () => (\n <ErrorPage status=\"404\" statusMessage=\"PAGE NOT FOUND\" />\n);\n\nconst DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => {\n let message = '';\n if (step === 'load-config') {\n message = `The configuration failed to load, someone should have a look at this error: ${error.message}`;\n } else if (step === 'load-chunk') {\n message = `Lazy loaded chunk failed to load, try to reload the page: ${error.message}`;\n }\n // TODO: figure out a nicer way to handle routing on the error page, when it can be done.\n return (\n <OptionallyWrapInRouter>\n <ErrorPage statusMessage={message} stack={error.stack} />\n </OptionallyWrapInRouter>\n );\n};\n\nconst DefaultErrorBoundaryFallback = ({\n error,\n resetError,\n plugin,\n}: ErrorBoundaryFallbackProps) => {\n return (\n <ErrorPanel\n title={`Error in ${plugin?.getId()}`}\n defaultExpanded\n error={error}\n >\n <Button variant=\"outlined\" onClick={resetError}>\n Retry\n </Button>\n </ErrorPanel>\n );\n};\n\n/**\n * Creates a set of default components to pass along to {@link @backstage/core-app-api#createSpecializedApp}.\n *\n * @public\n */\nexport const components: AppComponents = {\n Progress,\n Router: BrowserRouter,\n NotFoundErrorPage: DefaultNotFoundPage,\n BootErrorPage: DefaultBootErrorPage,\n ErrorBoundaryFallback: DefaultErrorBoundaryFallback,\n};\n"],"names":[],"mappings":";;;;;AA8BgB,SAAA,sBAAA,CAAuB,EAAE,QAAA,EAAqC,EAAA;AAC5E,EAAA,IAAI,oBAAsB,EAAA;AACxB,IAAA,iEAAU,QAAS,CAAA,CAAA;AAAA,GACrB;AACA,EAAO,uBAAA,KAAA,CAAA,aAAA,CAAC,oBAAc,QAAS,CAAA,CAAA;AACjC,CAAA;AAEA,MAAM,sBAAsB,sBAC1B,KAAA,CAAA,aAAA,CAAC,aAAU,MAAO,EAAA,KAAA,EAAM,eAAc,gBAAiB,EAAA,CAAA,CAAA;AAGzD,MAAM,oBAAuB,GAAA,CAAC,EAAE,IAAA,EAAM,OAAgC,KAAA;AACpE,EAAA,IAAI,OAAU,GAAA,EAAA,CAAA;AACd,EAAA,IAAI,SAAS,aAAe,EAAA;AAC1B,IAAU,OAAA,GAAA,CAAA,4EAAA,EAA+E,MAAM,OAAO,CAAA,CAAA,CAAA;AAAA,GACxG,MAAA,IAAW,SAAS,YAAc,EAAA;AAChC,IAAU,OAAA,GAAA,CAAA,0DAAA,EAA6D,MAAM,OAAO,CAAA,CAAA,CAAA;AAAA,GACtF;AAEA,EACE,uBAAA,KAAA,CAAA,aAAA,CAAC,8CACE,KAAA,CAAA,aAAA,CAAA,SAAA,EAAA,EAAU,eAAe,OAAS,EAAA,KAAA,EAAO,KAAM,CAAA,KAAA,EAAO,CACzD,CAAA,CAAA;AAEJ,CAAA,CAAA;AAEA,MAAM,+BAA+B,CAAC;AAAA,EACpC,KAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AACF,CAAkC,KAAA;AAChC,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,KAAO,EAAA,CAAA,SAAA,EAAY,MAAQ,EAAA,KAAA,EAAO,CAAA,CAAA;AAAA,MAClC,eAAe,EAAA,IAAA;AAAA,MACf,KAAA;AAAA,KAAA;AAAA,wCAEC,MAAO,EAAA,EAAA,OAAA,EAAQ,UAAW,EAAA,OAAA,EAAS,cAAY,OAEhD,CAAA;AAAA,GACF,CAAA;AAEJ,CAAA,CAAA;AAOO,MAAM,UAA4B,GAAA;AAAA,EACvC,QAAA;AAAA,EACA,MAAQ,EAAA,aAAA;AAAA,EACR,iBAAmB,EAAA,mBAAA;AAAA,EACnB,aAAe,EAAA,oBAAA;AAAA,EACf,qBAAuB,EAAA,4BAAA;AACzB;;;;"}
@@ -1,41 +1,11 @@
1
1
  import { BehaviorSubject } from '../../../lib/subjects.esm.js';
2
2
  import '@backstage/core-plugin-api';
3
3
 
4
- var __accessCheck = (obj, member, msg) => {
5
- if (!member.has(obj))
6
- throw TypeError("Cannot " + msg);
7
- };
8
- var __privateGet = (obj, member, getter) => {
9
- __accessCheck(obj, member, "read from private field");
10
- return member.get(obj);
11
- };
12
- var __privateAdd = (obj, member, value) => {
13
- if (member.has(obj))
14
- throw TypeError("Cannot add the same private member more than once");
15
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
16
- };
17
- var __privateSet = (obj, member, value, setter) => {
18
- __accessCheck(obj, member, "write to private field");
19
- member.set(obj, value);
20
- return value;
21
- };
22
- var _languages, _language, _subject;
23
4
  const STORAGE_KEY = "language";
24
5
  const DEFAULT_LANGUAGE = "en";
25
- const _AppLanguageSelector = class _AppLanguageSelector {
26
- constructor(languages, initialLanguage) {
27
- __privateAdd(this, _languages, void 0);
28
- __privateAdd(this, _language, void 0);
29
- __privateAdd(this, _subject, void 0);
30
- __privateSet(this, _languages, languages);
31
- __privateSet(this, _language, initialLanguage);
32
- __privateSet(this, _subject, new BehaviorSubject({
33
- language: __privateGet(this, _language)
34
- }));
35
- }
6
+ class AppLanguageSelector {
36
7
  static create(options) {
37
- var _a, _b;
38
- const languages = (_a = options == null ? void 0 : options.availableLanguages) != null ? _a : [DEFAULT_LANGUAGE];
8
+ const languages = options?.availableLanguages ?? [DEFAULT_LANGUAGE];
39
9
  if (languages.length !== new Set(languages).size) {
40
10
  throw new Error(
41
11
  `Supported languages may not contain duplicates, got '${languages.join(
@@ -46,16 +16,16 @@ const _AppLanguageSelector = class _AppLanguageSelector {
46
16
  if (!languages.includes(DEFAULT_LANGUAGE)) {
47
17
  throw new Error(`Supported languages must include '${DEFAULT_LANGUAGE}'`);
48
18
  }
49
- const initialLanguage = (_b = options == null ? void 0 : options.defaultLanguage) != null ? _b : DEFAULT_LANGUAGE;
19
+ const initialLanguage = options?.defaultLanguage ?? DEFAULT_LANGUAGE;
50
20
  if (!languages.includes(initialLanguage)) {
51
21
  throw new Error(
52
22
  `Initial language must be one of the supported languages, got '${initialLanguage}'`
53
23
  );
54
24
  }
55
- return new _AppLanguageSelector(languages, initialLanguage);
25
+ return new AppLanguageSelector(languages, initialLanguage);
56
26
  }
57
27
  static createWithStorage(options) {
58
- const selector = _AppLanguageSelector.create(options);
28
+ const selector = AppLanguageSelector.create(options);
59
29
  if (!window.localStorage) {
60
30
  return selector;
61
31
  }
@@ -70,9 +40,8 @@ const _AppLanguageSelector = class _AppLanguageSelector {
70
40
  }
71
41
  });
72
42
  window.addEventListener("storage", (event) => {
73
- var _a;
74
43
  if (event.key === STORAGE_KEY) {
75
- const language = (_a = localStorage.getItem(STORAGE_KEY)) != null ? _a : void 0;
44
+ const language = localStorage.getItem(STORAGE_KEY) ?? void 0;
76
45
  if (language) {
77
46
  selector.setLanguage(language);
78
47
  }
@@ -80,35 +49,41 @@ const _AppLanguageSelector = class _AppLanguageSelector {
80
49
  });
81
50
  return selector;
82
51
  }
52
+ #languages;
53
+ #language;
54
+ #subject;
55
+ constructor(languages, initialLanguage) {
56
+ this.#languages = languages;
57
+ this.#language = initialLanguage;
58
+ this.#subject = new BehaviorSubject({
59
+ language: this.#language
60
+ });
61
+ }
83
62
  getAvailableLanguages() {
84
- return { languages: __privateGet(this, _languages).slice() };
63
+ return { languages: this.#languages.slice() };
85
64
  }
86
65
  setLanguage(language) {
87
- const lng = language != null ? language : DEFAULT_LANGUAGE;
88
- if (lng === __privateGet(this, _language)) {
66
+ const lng = language ?? DEFAULT_LANGUAGE;
67
+ if (lng === this.#language) {
89
68
  return;
90
69
  }
91
- if (lng && !__privateGet(this, _languages).includes(lng)) {
70
+ if (lng && !this.#languages.includes(lng)) {
92
71
  throw new Error(
93
- `Failed to change language to '${lng}', available languages are '${__privateGet(this, _languages).join(
72
+ `Failed to change language to '${lng}', available languages are '${this.#languages.join(
94
73
  "', '"
95
74
  )}'`
96
75
  );
97
76
  }
98
- __privateSet(this, _language, lng);
99
- __privateGet(this, _subject).next({ language: lng });
77
+ this.#language = lng;
78
+ this.#subject.next({ language: lng });
100
79
  }
101
80
  getLanguage() {
102
- return { language: __privateGet(this, _language) };
81
+ return { language: this.#language };
103
82
  }
104
83
  language$() {
105
- return __privateGet(this, _subject);
84
+ return this.#subject;
106
85
  }
107
- };
108
- _languages = new WeakMap();
109
- _language = new WeakMap();
110
- _subject = new WeakMap();
111
- let AppLanguageSelector = _AppLanguageSelector;
86
+ }
112
87
 
113
88
  export { AppLanguageSelector, DEFAULT_LANGUAGE };
114
89
  //# sourceMappingURL=AppLanguageSelector.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AppLanguageSelector.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.ts"],"sourcesContent":["/*\n * Copyright 2023 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// Internal import to avoid code duplication, this will lead to duplication in build output\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppLanguageApi } from '@backstage/core-plugin-api/alpha';\nimport { Observable } from '@backstage/types';\nimport { BehaviorSubject } from '../../../lib';\n\nconst STORAGE_KEY = 'language';\nexport const DEFAULT_LANGUAGE = 'en';\n\n/** @alpha */\nexport interface AppLanguageSelectorOptions {\n defaultLanguage?: string;\n availableLanguages?: string[];\n}\n\n/**\n * Exposes the available languages in the app and allows for switching of the active language.\n *\n * @alpha\n */\nexport class AppLanguageSelector implements AppLanguageApi {\n static create(options?: AppLanguageSelectorOptions) {\n const languages = options?.availableLanguages ?? [DEFAULT_LANGUAGE];\n if (languages.length !== new Set(languages).size) {\n throw new Error(\n `Supported languages may not contain duplicates, got '${languages.join(\n \"', '\",\n )}'`,\n );\n }\n if (!languages.includes(DEFAULT_LANGUAGE)) {\n throw new Error(`Supported languages must include '${DEFAULT_LANGUAGE}'`);\n }\n\n const initialLanguage = options?.defaultLanguage ?? DEFAULT_LANGUAGE;\n if (!languages.includes(initialLanguage)) {\n throw new Error(\n `Initial language must be one of the supported languages, got '${initialLanguage}'`,\n );\n }\n\n return new AppLanguageSelector(languages, initialLanguage);\n }\n\n static createWithStorage(options?: AppLanguageSelectorOptions) {\n const selector = AppLanguageSelector.create(options);\n\n if (!window.localStorage) {\n return selector;\n }\n\n const storedLanguage = window.localStorage.getItem(STORAGE_KEY);\n const { languages } = selector.getAvailableLanguages();\n if (storedLanguage && languages.includes(storedLanguage)) {\n selector.setLanguage(storedLanguage);\n }\n\n selector.language$().subscribe(({ language }) => {\n if (language !== window.localStorage.getItem(STORAGE_KEY)) {\n window.localStorage.setItem(STORAGE_KEY, language);\n }\n });\n\n window.addEventListener('storage', event => {\n if (event.key === STORAGE_KEY) {\n const language = localStorage.getItem(STORAGE_KEY) ?? undefined;\n if (language) {\n selector.setLanguage(language);\n }\n }\n });\n\n return selector;\n }\n\n #languages: string[];\n #language: string;\n #subject: BehaviorSubject<{ language: string }>;\n\n private constructor(languages: string[], initialLanguage: string) {\n this.#languages = languages;\n this.#language = initialLanguage;\n this.#subject = new BehaviorSubject<{ language: string }>({\n language: this.#language,\n });\n }\n\n getAvailableLanguages(): { languages: string[] } {\n return { languages: this.#languages.slice() };\n }\n\n setLanguage(language?: string | undefined): void {\n const lng = language ?? DEFAULT_LANGUAGE;\n if (lng === this.#language) {\n return;\n }\n if (lng && !this.#languages.includes(lng)) {\n throw new Error(\n `Failed to change language to '${lng}', available languages are '${this.#languages.join(\n \"', '\",\n )}'`,\n );\n }\n this.#language = lng;\n this.#subject.next({ language: lng });\n }\n\n getLanguage(): { language: string } {\n return { language: this.#language };\n }\n\n language$(): Observable<{ language: string }> {\n return this.#subject;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AppLanguageSelector.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.ts"],"sourcesContent":["/*\n * Copyright 2023 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// Internal import to avoid code duplication, this will lead to duplication in build output\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppLanguageApi } from '@backstage/core-plugin-api/alpha';\nimport { Observable } from '@backstage/types';\nimport { BehaviorSubject } from '../../../lib';\n\nconst STORAGE_KEY = 'language';\nexport const DEFAULT_LANGUAGE = 'en';\n\n/** @alpha */\nexport interface AppLanguageSelectorOptions {\n defaultLanguage?: string;\n availableLanguages?: string[];\n}\n\n/**\n * Exposes the available languages in the app and allows for switching of the active language.\n *\n * @alpha\n */\nexport class AppLanguageSelector implements AppLanguageApi {\n static create(options?: AppLanguageSelectorOptions) {\n const languages = options?.availableLanguages ?? [DEFAULT_LANGUAGE];\n if (languages.length !== new Set(languages).size) {\n throw new Error(\n `Supported languages may not contain duplicates, got '${languages.join(\n \"', '\",\n )}'`,\n );\n }\n if (!languages.includes(DEFAULT_LANGUAGE)) {\n throw new Error(`Supported languages must include '${DEFAULT_LANGUAGE}'`);\n }\n\n const initialLanguage = options?.defaultLanguage ?? DEFAULT_LANGUAGE;\n if (!languages.includes(initialLanguage)) {\n throw new Error(\n `Initial language must be one of the supported languages, got '${initialLanguage}'`,\n );\n }\n\n return new AppLanguageSelector(languages, initialLanguage);\n }\n\n static createWithStorage(options?: AppLanguageSelectorOptions) {\n const selector = AppLanguageSelector.create(options);\n\n if (!window.localStorage) {\n return selector;\n }\n\n const storedLanguage = window.localStorage.getItem(STORAGE_KEY);\n const { languages } = selector.getAvailableLanguages();\n if (storedLanguage && languages.includes(storedLanguage)) {\n selector.setLanguage(storedLanguage);\n }\n\n selector.language$().subscribe(({ language }) => {\n if (language !== window.localStorage.getItem(STORAGE_KEY)) {\n window.localStorage.setItem(STORAGE_KEY, language);\n }\n });\n\n window.addEventListener('storage', event => {\n if (event.key === STORAGE_KEY) {\n const language = localStorage.getItem(STORAGE_KEY) ?? undefined;\n if (language) {\n selector.setLanguage(language);\n }\n }\n });\n\n return selector;\n }\n\n #languages: string[];\n #language: string;\n #subject: BehaviorSubject<{ language: string }>;\n\n private constructor(languages: string[], initialLanguage: string) {\n this.#languages = languages;\n this.#language = initialLanguage;\n this.#subject = new BehaviorSubject<{ language: string }>({\n language: this.#language,\n });\n }\n\n getAvailableLanguages(): { languages: string[] } {\n return { languages: this.#languages.slice() };\n }\n\n setLanguage(language?: string | undefined): void {\n const lng = language ?? DEFAULT_LANGUAGE;\n if (lng === this.#language) {\n return;\n }\n if (lng && !this.#languages.includes(lng)) {\n throw new Error(\n `Failed to change language to '${lng}', available languages are '${this.#languages.join(\n \"', '\",\n )}'`,\n );\n }\n this.#language = lng;\n this.#subject.next({ language: lng });\n }\n\n getLanguage(): { language: string } {\n return { language: this.#language };\n }\n\n language$(): Observable<{ language: string }> {\n return this.#subject;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,11 +1,5 @@
1
1
  import { FeatureFlagState } from '@backstage/core-plugin-api';
2
2
 
3
- var __defProp = Object.defineProperty;
4
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
5
- var __publicField = (obj, key, value) => {
6
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
7
- return value;
8
- };
9
3
  function validateFlagName(name) {
10
4
  if (name.length < 3) {
11
5
  throw new Error(
@@ -24,10 +18,8 @@ function validateFlagName(name) {
24
18
  }
25
19
  }
26
20
  class LocalStorageFeatureFlags {
27
- constructor() {
28
- __publicField(this, "registeredFeatureFlags", []);
29
- __publicField(this, "flags");
30
- }
21
+ registeredFeatureFlags = [];
22
+ flags;
31
23
  registerFlag(flag) {
32
24
  validateFlagName(flag.name);
33
25
  this.registeredFeatureFlags.push(flag);
@@ -1 +1 @@
1
- {"version":3,"file":"LocalStorageFeatureFlags.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.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 {\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"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LocalStorageFeatureFlags.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.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 {\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"],"names":[],"mappings":"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"}
@@ -1,30 +1,5 @@
1
1
  import { startCookieAuthRefresh } from './startCookieAuthRefresh.esm.js';
2
2
 
3
- var __defProp = Object.defineProperty;
4
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
5
- var __publicField = (obj, key, value) => {
6
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
7
- return value;
8
- };
9
- var __accessCheck = (obj, member, msg) => {
10
- if (!member.has(obj))
11
- throw TypeError("Cannot " + msg);
12
- };
13
- var __privateGet = (obj, member, getter) => {
14
- __accessCheck(obj, member, "read from private field");
15
- return member.get(obj);
16
- };
17
- var __privateAdd = (obj, member, value) => {
18
- if (member.has(obj))
19
- throw TypeError("Cannot add the same private member more than once");
20
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
21
- };
22
- var __privateSet = (obj, member, value, setter) => {
23
- __accessCheck(obj, member, "write to private field");
24
- member.set(obj, value);
25
- return value;
26
- };
27
- var _cookieAuthSignOut;
28
3
  function mkError(thing) {
29
4
  return new Error(
30
5
  `Tried to access IdentityApi ${thing} before app was loaded`
@@ -36,13 +11,13 @@ function logDeprecation(thing) {
36
11
  );
37
12
  }
38
13
  class AppIdentityProxy {
14
+ target;
15
+ waitForTarget;
16
+ resolveTarget = () => {
17
+ };
18
+ signOutTargetUrl = "/";
19
+ #cookieAuthSignOut;
39
20
  constructor() {
40
- __publicField(this, "target");
41
- __publicField(this, "waitForTarget");
42
- __publicField(this, "resolveTarget", () => {
43
- });
44
- __publicField(this, "signOutTargetUrl", "/");
45
- __privateAdd(this, _cookieAuthSignOut, void 0);
46
21
  this.waitForTarget = new Promise((resolve) => {
47
22
  this.resolveTarget = resolve;
48
23
  });
@@ -100,17 +75,16 @@ class AppIdentityProxy {
100
75
  });
101
76
  }
102
77
  async signOut() {
103
- var _a;
104
78
  await this.waitForTarget.then((target) => target.signOut());
105
- await ((_a = __privateGet(this, _cookieAuthSignOut)) == null ? void 0 : _a.call(this));
79
+ await this.#cookieAuthSignOut?.();
106
80
  window.location.href = this.signOutTargetUrl;
107
81
  }
108
82
  enableCookieAuth(ctx) {
109
- if (__privateGet(this, _cookieAuthSignOut)) {
83
+ if (this.#cookieAuthSignOut) {
110
84
  return;
111
85
  }
112
86
  const stopRefresh = startCookieAuthRefresh(ctx);
113
- __privateSet(this, _cookieAuthSignOut, async () => {
87
+ this.#cookieAuthSignOut = async () => {
114
88
  stopRefresh();
115
89
  const appBaseUrl = await ctx.discoveryApi.getBaseUrl("app");
116
90
  try {
@@ -119,10 +93,9 @@ class AppIdentityProxy {
119
93
  });
120
94
  } catch {
121
95
  }
122
- });
96
+ };
123
97
  }
124
98
  }
125
- _cookieAuthSignOut = new WeakMap();
126
99
 
127
100
  export { AppIdentityProxy };
128
101
  //# sourceMappingURL=AppIdentityProxy.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AppIdentityProxy.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.ts"],"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 {\n IdentityApi,\n ProfileInfo,\n BackstageUserIdentity,\n ErrorApi,\n DiscoveryApi,\n FetchApi,\n} from '@backstage/core-plugin-api';\nimport { startCookieAuthRefresh } from './startCookieAuthRefresh';\n\nfunction mkError(thing: string) {\n return new Error(\n `Tried to access IdentityApi ${thing} before app was loaded`,\n );\n}\n\nfunction logDeprecation(thing: string) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: Call to ${thing} is deprecated and will break in the future`,\n );\n}\n\n// We use this for a period of backwards compatibility. It is a hidden\n// compatibility that will allow old plugins to continue working for a limited time.\ntype CompatibilityIdentityApi = IdentityApi & {\n getUserId?(): string;\n getIdToken?(): Promise<string | undefined>;\n getProfile?(): ProfileInfo;\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?: CompatibilityIdentityApi;\n private waitForTarget: Promise<CompatibilityIdentityApi>;\n private resolveTarget: (api: CompatibilityIdentityApi) => void = () => {};\n private signOutTargetUrl = '/';\n\n #cookieAuthSignOut?: () => Promise<void>;\n\n constructor() {\n this.waitForTarget = new Promise<CompatibilityIdentityApi>(resolve => {\n this.resolveTarget = resolve;\n });\n }\n\n // This is called by the app manager once the sign-in page provides us with an implementation\n setTarget(\n identityApi: CompatibilityIdentityApi,\n targetOptions: { signOutTargetUrl: string },\n ) {\n this.target = identityApi;\n this.signOutTargetUrl = targetOptions.signOutTargetUrl;\n this.resolveTarget(identityApi);\n }\n\n getUserId(): string {\n if (!this.target) {\n throw mkError('getUserId');\n }\n if (!this.target.getUserId) {\n throw new Error('IdentityApi does not implement getUserId');\n }\n logDeprecation('getUserId');\n return this.target.getUserId();\n }\n\n getProfile(): ProfileInfo {\n if (!this.target) {\n throw mkError('getProfile');\n }\n if (!this.target.getProfile) {\n throw new Error('IdentityApi does not implement getProfile');\n }\n logDeprecation('getProfile');\n return this.target.getProfile();\n }\n\n async getProfileInfo(): Promise<ProfileInfo> {\n return this.waitForTarget.then(target => target.getProfileInfo());\n }\n\n async getBackstageIdentity(): Promise<BackstageUserIdentity> {\n const identity = await this.waitForTarget.then(target =>\n target.getBackstageIdentity(),\n );\n if (!identity.userEntityRef.match(/^.*:.*\\/.*$/)) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: The App IdentityApi provided an invalid userEntityRef, '${identity.userEntityRef}'. ` +\n `It must be a full Entity Reference of the form '<kind>:<namespace>/<name>'.`,\n );\n }\n\n return identity;\n }\n\n async getCredentials(): Promise<{ token?: string | undefined }> {\n return this.waitForTarget.then(target => target.getCredentials());\n }\n\n async getIdToken(): Promise<string | undefined> {\n return this.waitForTarget.then(target => {\n if (!target.getIdToken) {\n throw new Error('IdentityApi does not implement getIdToken');\n }\n logDeprecation('getIdToken');\n return target.getIdToken();\n });\n }\n\n async signOut(): Promise<void> {\n await this.waitForTarget.then(target => target.signOut());\n\n await this.#cookieAuthSignOut?.();\n\n window.location.href = this.signOutTargetUrl;\n }\n\n enableCookieAuth(ctx: {\n errorApi: ErrorApi;\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n }) {\n if (this.#cookieAuthSignOut) {\n return;\n }\n\n const stopRefresh = startCookieAuthRefresh(ctx);\n\n this.#cookieAuthSignOut = async () => {\n stopRefresh();\n\n // It is fine if we do NOT worry yet about deleting cookies for OTHER backends like techdocs\n const appBaseUrl = await ctx.discoveryApi.getBaseUrl('app');\n try {\n await ctx.fetchApi.fetch(`${appBaseUrl}/.backstage/auth/v1/cookie`, {\n method: 'DELETE',\n });\n } catch {\n // Ignore the error for those who use static serving of the frontend\n }\n };\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AppIdentityProxy.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.ts"],"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 {\n IdentityApi,\n ProfileInfo,\n BackstageUserIdentity,\n ErrorApi,\n DiscoveryApi,\n FetchApi,\n} from '@backstage/core-plugin-api';\nimport { startCookieAuthRefresh } from './startCookieAuthRefresh';\n\nfunction mkError(thing: string) {\n return new Error(\n `Tried to access IdentityApi ${thing} before app was loaded`,\n );\n}\n\nfunction logDeprecation(thing: string) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: Call to ${thing} is deprecated and will break in the future`,\n );\n}\n\n// We use this for a period of backwards compatibility. It is a hidden\n// compatibility that will allow old plugins to continue working for a limited time.\ntype CompatibilityIdentityApi = IdentityApi & {\n getUserId?(): string;\n getIdToken?(): Promise<string | undefined>;\n getProfile?(): ProfileInfo;\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?: CompatibilityIdentityApi;\n private waitForTarget: Promise<CompatibilityIdentityApi>;\n private resolveTarget: (api: CompatibilityIdentityApi) => void = () => {};\n private signOutTargetUrl = '/';\n\n #cookieAuthSignOut?: () => Promise<void>;\n\n constructor() {\n this.waitForTarget = new Promise<CompatibilityIdentityApi>(resolve => {\n this.resolveTarget = resolve;\n });\n }\n\n // This is called by the app manager once the sign-in page provides us with an implementation\n setTarget(\n identityApi: CompatibilityIdentityApi,\n targetOptions: { signOutTargetUrl: string },\n ) {\n this.target = identityApi;\n this.signOutTargetUrl = targetOptions.signOutTargetUrl;\n this.resolveTarget(identityApi);\n }\n\n getUserId(): string {\n if (!this.target) {\n throw mkError('getUserId');\n }\n if (!this.target.getUserId) {\n throw new Error('IdentityApi does not implement getUserId');\n }\n logDeprecation('getUserId');\n return this.target.getUserId();\n }\n\n getProfile(): ProfileInfo {\n if (!this.target) {\n throw mkError('getProfile');\n }\n if (!this.target.getProfile) {\n throw new Error('IdentityApi does not implement getProfile');\n }\n logDeprecation('getProfile');\n return this.target.getProfile();\n }\n\n async getProfileInfo(): Promise<ProfileInfo> {\n return this.waitForTarget.then(target => target.getProfileInfo());\n }\n\n async getBackstageIdentity(): Promise<BackstageUserIdentity> {\n const identity = await this.waitForTarget.then(target =>\n target.getBackstageIdentity(),\n );\n if (!identity.userEntityRef.match(/^.*:.*\\/.*$/)) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: The App IdentityApi provided an invalid userEntityRef, '${identity.userEntityRef}'. ` +\n `It must be a full Entity Reference of the form '<kind>:<namespace>/<name>'.`,\n );\n }\n\n return identity;\n }\n\n async getCredentials(): Promise<{ token?: string | undefined }> {\n return this.waitForTarget.then(target => target.getCredentials());\n }\n\n async getIdToken(): Promise<string | undefined> {\n return this.waitForTarget.then(target => {\n if (!target.getIdToken) {\n throw new Error('IdentityApi does not implement getIdToken');\n }\n logDeprecation('getIdToken');\n return target.getIdToken();\n });\n }\n\n async signOut(): Promise<void> {\n await this.waitForTarget.then(target => target.signOut());\n\n await this.#cookieAuthSignOut?.();\n\n window.location.href = this.signOutTargetUrl;\n }\n\n enableCookieAuth(ctx: {\n errorApi: ErrorApi;\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n }) {\n if (this.#cookieAuthSignOut) {\n return;\n }\n\n const stopRefresh = startCookieAuthRefresh(ctx);\n\n this.#cookieAuthSignOut = async () => {\n stopRefresh();\n\n // It is fine if we do NOT worry yet about deleting cookies for OTHER backends like techdocs\n const appBaseUrl = await ctx.discoveryApi.getBaseUrl('app');\n try {\n await ctx.fetchApi.fetch(`${appBaseUrl}/.backstage/auth/v1/cookie`, {\n method: 'DELETE',\n });\n } catch {\n // Ignore the error for those who use static serving of the frontend\n }\n };\n }\n}\n"],"names":[],"mappings":"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"}
@@ -40,7 +40,7 @@ function startCookieAuthRefresh({
40
40
  throw new Error("Invalid expiration date in response");
41
41
  }
42
42
  firstError = true;
43
- channel == null ? void 0 : channel.postMessage({
43
+ channel?.postMessage({
44
44
  action: "COOKIE_REFRESH_SUCCESS",
45
45
  payload: { expiresAt: new Date(expiresAt).toISOString() }
46
46
  });
@@ -89,15 +89,15 @@ function startCookieAuthRefresh({
89
89
  }
90
90
  timeout = setTimeout(refresh, delayMs);
91
91
  }
92
- channel == null ? void 0 : channel.addEventListener("message", onMessage);
92
+ channel?.addEventListener("message", onMessage);
93
93
  refresh();
94
94
  return () => {
95
95
  stopped = true;
96
96
  if (timeout) {
97
97
  clearTimeout(timeout);
98
98
  }
99
- channel == null ? void 0 : channel.removeEventListener("message", onMessage);
100
- channel == null ? void 0 : channel.close();
99
+ channel?.removeEventListener("message", onMessage);
100
+ channel?.close();
101
101
  };
102
102
  }
103
103
 
@@ -1 +1 @@
1
- {"version":3,"file":"startCookieAuthRefresh.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.ts"],"sourcesContent":["/*\n * Copyright 2024 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, ErrorApi, FetchApi } from '@backstage/core-plugin-api';\n\nconst PLUGIN_ID = 'app';\nconst CHANNEL_ID = `${PLUGIN_ID}-auth-cookie-expires-at`;\n\nconst MIN_BASE_DELAY_MS = 5 * 60_000;\n\nconst ERROR_BACKOFF_START = 5_000;\nconst ERROR_BACKOFF_FACTOR = 2;\nconst ERROR_BACKOFF_MAX = 5 * 60_000;\n\n// Messaging implementation and IDs must match\n// plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx\nexport function startCookieAuthRefresh({\n discoveryApi,\n fetchApi,\n errorApi,\n}: {\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n errorApi: ErrorApi;\n}) {\n let stopped = false;\n let timeout: NodeJS.Timeout | undefined;\n let firstError = true;\n let errorBackoff = ERROR_BACKOFF_START;\n\n const channel =\n 'BroadcastChannel' in window ? new BroadcastChannel(CHANNEL_ID) : undefined;\n\n // Randomize the refreshing margin with a margin of 1-4 minutes to avoid all tabs refreshing at the same time\n const getDelay = (expiresAt: number) => {\n const margin = (1 + 3 * Math.random()) * 60000;\n const delay = Math.max(expiresAt - Date.now(), MIN_BASE_DELAY_MS) - margin;\n return delay;\n };\n\n const refresh = async () => {\n try {\n const baseUrl = await discoveryApi.getBaseUrl(PLUGIN_ID);\n const requestUrl = `${baseUrl}/.backstage/auth/v1/cookie`;\n const res = await fetchApi.fetch(requestUrl, {\n credentials: 'include',\n });\n\n if (!res.ok) {\n throw new Error(\n `Request failed with status ${res.status} ${res.statusText}, see request towards ${requestUrl} for more details`,\n );\n }\n\n const data = await res.json();\n if (!data.expiresAt) {\n throw new Error('No expiration date in response');\n }\n\n const expiresAt = Date.parse(data.expiresAt);\n if (Number.isNaN(expiresAt)) {\n throw new Error('Invalid expiration date in response');\n }\n\n firstError = true;\n\n channel?.postMessage({\n action: 'COOKIE_REFRESH_SUCCESS',\n payload: { expiresAt: new Date(expiresAt).toISOString() },\n });\n\n scheduleRefresh(getDelay(expiresAt));\n } catch (error) {\n // Ignore the first error after successful requests\n if (firstError) {\n firstError = false;\n errorBackoff = ERROR_BACKOFF_START;\n } else {\n errorBackoff = Math.min(\n ERROR_BACKOFF_MAX,\n errorBackoff * ERROR_BACKOFF_FACTOR,\n );\n // eslint-disable-next-line no-console\n console.error('Session cookie refresh failed', error);\n errorApi.post(\n new Error(\n `Session refresh failed, see developer console for details`,\n ),\n );\n }\n\n scheduleRefresh(errorBackoff);\n }\n };\n\n const onMessage = (\n event: MessageEvent<\n | {\n action: 'COOKIE_REFRESH_SUCCESS';\n payload: { expiresAt: string };\n }\n | object\n >,\n ) => {\n const { data } = event;\n if (data === null || typeof data !== 'object') {\n return;\n }\n if ('action' in data && data.action === 'COOKIE_REFRESH_SUCCESS') {\n const expiresAt = Date.parse(data.payload.expiresAt);\n if (Number.isNaN(expiresAt)) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Received invalid expiration from session refresh channel',\n );\n return;\n }\n\n scheduleRefresh(getDelay(expiresAt));\n }\n };\n\n function scheduleRefresh(delayMs: number) {\n if (stopped) {\n return;\n }\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(refresh, delayMs);\n }\n\n channel?.addEventListener('message', onMessage);\n refresh();\n\n return () => {\n stopped = true;\n if (timeout) {\n clearTimeout(timeout);\n }\n channel?.removeEventListener('message', onMessage);\n channel?.close();\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"startCookieAuthRefresh.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.ts"],"sourcesContent":["/*\n * Copyright 2024 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, ErrorApi, FetchApi } from '@backstage/core-plugin-api';\n\nconst PLUGIN_ID = 'app';\nconst CHANNEL_ID = `${PLUGIN_ID}-auth-cookie-expires-at`;\n\nconst MIN_BASE_DELAY_MS = 5 * 60_000;\n\nconst ERROR_BACKOFF_START = 5_000;\nconst ERROR_BACKOFF_FACTOR = 2;\nconst ERROR_BACKOFF_MAX = 5 * 60_000;\n\n// Messaging implementation and IDs must match\n// plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx\nexport function startCookieAuthRefresh({\n discoveryApi,\n fetchApi,\n errorApi,\n}: {\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n errorApi: ErrorApi;\n}) {\n let stopped = false;\n let timeout: NodeJS.Timeout | undefined;\n let firstError = true;\n let errorBackoff = ERROR_BACKOFF_START;\n\n const channel =\n 'BroadcastChannel' in window ? new BroadcastChannel(CHANNEL_ID) : undefined;\n\n // Randomize the refreshing margin with a margin of 1-4 minutes to avoid all tabs refreshing at the same time\n const getDelay = (expiresAt: number) => {\n const margin = (1 + 3 * Math.random()) * 60000;\n const delay = Math.max(expiresAt - Date.now(), MIN_BASE_DELAY_MS) - margin;\n return delay;\n };\n\n const refresh = async () => {\n try {\n const baseUrl = await discoveryApi.getBaseUrl(PLUGIN_ID);\n const requestUrl = `${baseUrl}/.backstage/auth/v1/cookie`;\n const res = await fetchApi.fetch(requestUrl, {\n credentials: 'include',\n });\n\n if (!res.ok) {\n throw new Error(\n `Request failed with status ${res.status} ${res.statusText}, see request towards ${requestUrl} for more details`,\n );\n }\n\n const data = await res.json();\n if (!data.expiresAt) {\n throw new Error('No expiration date in response');\n }\n\n const expiresAt = Date.parse(data.expiresAt);\n if (Number.isNaN(expiresAt)) {\n throw new Error('Invalid expiration date in response');\n }\n\n firstError = true;\n\n channel?.postMessage({\n action: 'COOKIE_REFRESH_SUCCESS',\n payload: { expiresAt: new Date(expiresAt).toISOString() },\n });\n\n scheduleRefresh(getDelay(expiresAt));\n } catch (error) {\n // Ignore the first error after successful requests\n if (firstError) {\n firstError = false;\n errorBackoff = ERROR_BACKOFF_START;\n } else {\n errorBackoff = Math.min(\n ERROR_BACKOFF_MAX,\n errorBackoff * ERROR_BACKOFF_FACTOR,\n );\n // eslint-disable-next-line no-console\n console.error('Session cookie refresh failed', error);\n errorApi.post(\n new Error(\n `Session refresh failed, see developer console for details`,\n ),\n );\n }\n\n scheduleRefresh(errorBackoff);\n }\n };\n\n const onMessage = (\n event: MessageEvent<\n | {\n action: 'COOKIE_REFRESH_SUCCESS';\n payload: { expiresAt: string };\n }\n | object\n >,\n ) => {\n const { data } = event;\n if (data === null || typeof data !== 'object') {\n return;\n }\n if ('action' in data && data.action === 'COOKIE_REFRESH_SUCCESS') {\n const expiresAt = Date.parse(data.payload.expiresAt);\n if (Number.isNaN(expiresAt)) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Received invalid expiration from session refresh channel',\n );\n return;\n }\n\n scheduleRefresh(getDelay(expiresAt));\n }\n };\n\n function scheduleRefresh(delayMs: number) {\n if (stopped) {\n return;\n }\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(refresh, delayMs);\n }\n\n channel?.addEventListener('message', onMessage);\n refresh();\n\n return () => {\n stopped = true;\n if (timeout) {\n clearTimeout(timeout);\n }\n channel?.removeEventListener('message', onMessage);\n channel?.close();\n };\n}\n"],"names":[],"mappings":"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"}