@backstage/frontend-app-api 0.2.0-next.2 → 0.2.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.
package/dist/index.esm.js CHANGED
@@ -6,9 +6,11 @@ import { SidebarPage, sidebarConfig, Sidebar, SidebarDivider, useSidebarOpenStat
6
6
  import { useRouteRef, useApi, appThemeApiRef, FeatureFlagState, createApiFactory, discoveryApiRef, configApiRef, alertApiRef, analyticsApiRef, errorApiRef, storageApiRef, fetchApiRef, identityApiRef, oauthRequestApiRef, googleAuthApiRef, microsoftAuthApiRef, githubAuthApiRef, oktaAuthApiRef, gitlabAuthApiRef, oneloginAuthApiRef, bitbucketAuthApiRef, bitbucketServerAuthApiRef, atlassianAuthApiRef, attachComponentData, featureFlagsApiRef } from '@backstage/core-plugin-api';
7
7
  import { makeStyles } from '@material-ui/core';
8
8
  import mapValues from 'lodash/mapValues';
9
- import { UrlPatternDiscovery, AlertApiForwarder, NoOpAnalyticsApi, ErrorAlerter, ErrorApiForwarder, UnhandledErrorForwarder, WebStorage, createFetchApi, FetchMiddlewares, OAuthRequestManager, GoogleAuth, MicrosoftAuth, GithubAuth, OktaAuth, GitlabAuth, OneLoginAuth, BitbucketAuth, BitbucketServerAuth, AtlassianAuth, ApiFactoryRegistry, AppThemeSelector, ApiResolver, ApiProvider } from '@backstage/core-app-api';
9
+ import { UrlPatternDiscovery, AlertApiForwarder, NoOpAnalyticsApi, ErrorAlerter, ErrorApiForwarder, UnhandledErrorForwarder, WebStorage, createFetchApi, FetchMiddlewares, OAuthRequestManager, GoogleAuth, MicrosoftAuth, GithubAuth, OktaAuth, GitlabAuth, OneLoginAuth, BitbucketAuth, BitbucketServerAuth, AtlassianAuth, ApiProvider, ApiFactoryRegistry, AppThemeSelector, ApiResolver } from '@backstage/core-app-api';
10
10
  import useObservable from 'react-use/lib/useObservable';
11
11
  import { createVersionedContext, createVersionedValueMap, getOrCreateGlobalSingleton } from '@backstage/version-bridge';
12
+ import ObservableImpl from 'zen-observable';
13
+ import { createInstance } from 'i18next';
12
14
  import { permissionApiRef, IdentityPermissionApi } from '@backstage/plugin-permission-react';
13
15
  import Button from '@material-ui/core/Button';
14
16
  import MuiApartmentIcon from '@material-ui/icons/Apartment';
@@ -35,6 +37,7 @@ import MuiFeaturedPlayListIcon from '@material-ui/icons/FeaturedPlayList';
35
37
  import { UnifiedThemeProvider, themes } from '@backstage/theme';
36
38
  import DarkIcon from '@material-ui/icons/Brightness2';
37
39
  import LightIcon from '@material-ui/icons/WbSunny';
40
+ import { appLanguageApiRef, translationApiRef } from '@backstage/core-plugin-api/alpha';
38
41
 
39
42
  const Core = createExtension({
40
43
  id: "core",
@@ -42,10 +45,24 @@ const Core = createExtension({
42
45
  inputs: {
43
46
  apis: createExtensionInput({
44
47
  api: coreExtensionData.apiFactory
45
- })
48
+ }),
49
+ themes: createExtensionInput({
50
+ theme: coreExtensionData.theme
51
+ }),
52
+ root: createExtensionInput(
53
+ {
54
+ element: coreExtensionData.reactElement
55
+ },
56
+ { singleton: true }
57
+ )
58
+ },
59
+ output: {
60
+ root: coreExtensionData.reactElement
46
61
  },
47
- output: {},
48
- factory() {
62
+ factory({ bind, inputs }) {
63
+ bind({
64
+ root: inputs.root.element
65
+ });
49
66
  }
50
67
  });
51
68
 
@@ -66,7 +83,7 @@ const CoreRoutes = createExtension({
66
83
  const Routes = () => {
67
84
  const element = useRoutes(
68
85
  inputs.routes.map((route) => ({
69
- path: route.path,
86
+ path: `${route.path}/*`,
70
87
  element: route.element
71
88
  }))
72
89
  );
@@ -80,7 +97,7 @@ const CoreRoutes = createExtension({
80
97
 
81
98
  const CoreLayout = createExtension({
82
99
  id: "core.layout",
83
- attachTo: { id: "root", input: "default" },
100
+ attachTo: { id: "core", input: "root" },
84
101
  inputs: {
85
102
  nav: createExtensionInput(
86
103
  {
@@ -203,27 +220,27 @@ const CoreNav = createExtension({
203
220
  }
204
221
  });
205
222
 
206
- var __defProp$2 = Object.defineProperty;
207
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
208
- var __publicField$2 = (obj, key, value) => {
209
- __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
223
+ var __defProp$3 = Object.defineProperty;
224
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
225
+ var __publicField$3 = (obj, key, value) => {
226
+ __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
210
227
  return value;
211
228
  };
212
- var __accessCheck = (obj, member, msg) => {
229
+ var __accessCheck$2 = (obj, member, msg) => {
213
230
  if (!member.has(obj))
214
231
  throw TypeError("Cannot " + msg);
215
232
  };
216
- var __privateGet = (obj, member, getter) => {
217
- __accessCheck(obj, member, "read from private field");
233
+ var __privateGet$2 = (obj, member, getter) => {
234
+ __accessCheck$2(obj, member, "read from private field");
218
235
  return getter ? getter.call(obj) : member.get(obj);
219
236
  };
220
- var __privateAdd = (obj, member, value) => {
237
+ var __privateAdd$2 = (obj, member, value) => {
221
238
  if (member.has(obj))
222
239
  throw TypeError("Cannot add the same private member more than once");
223
240
  member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
224
241
  };
225
- var __privateSet = (obj, member, value, setter) => {
226
- __accessCheck(obj, member, "write to private field");
242
+ var __privateSet$2 = (obj, member, value, setter) => {
243
+ __accessCheck$2(obj, member, "write to private field");
227
244
  setter ? setter.call(obj, value) : member.set(obj, value);
228
245
  return value;
229
246
  };
@@ -240,6 +257,16 @@ function resolveInputData(dataMap, attachment, inputName) {
240
257
  });
241
258
  }
242
259
  function resolveInputs(inputMap, attachments) {
260
+ const undeclaredAttachments = Array.from(attachments.entries()).filter(
261
+ ([inputName]) => inputMap[inputName] === void 0
262
+ );
263
+ if (undeclaredAttachments.length > 0) {
264
+ throw new Error(
265
+ `received undeclared input${undeclaredAttachments.length > 1 ? "s" : ""} ${undeclaredAttachments.map(
266
+ ([k, exts]) => `'${k}' from extension${exts.length > 1 ? "s" : ""} '${exts.map((e) => e.id).join("', '")}'`
267
+ ).join(" and ")}`
268
+ );
269
+ }
243
270
  return mapValues(inputMap, (input, inputName) => {
244
271
  var _a;
245
272
  const attachedInstances = (_a = attachments.get(inputName)) != null ? _a : [];
@@ -270,28 +297,28 @@ function indent(str) {
270
297
  }
271
298
  class ExtensionInstanceImpl {
272
299
  constructor(id, extensionData, attachments, source) {
273
- __publicField$2(this, "$$type", "@backstage/ExtensionInstance");
274
- __publicField$2(this, "id");
275
- __privateAdd(this, _extensionData, void 0);
276
- __publicField$2(this, "attachments");
277
- __publicField$2(this, "source");
300
+ __publicField$3(this, "$$type", "@backstage/ExtensionInstance");
301
+ __publicField$3(this, "id");
302
+ __privateAdd$2(this, _extensionData, void 0);
303
+ __publicField$3(this, "attachments");
304
+ __publicField$3(this, "source");
278
305
  this.id = id;
279
- __privateSet(this, _extensionData, extensionData);
306
+ __privateSet$2(this, _extensionData, extensionData);
280
307
  this.attachments = attachments;
281
308
  this.source = source;
282
309
  }
283
310
  getData(ref) {
284
- return __privateGet(this, _extensionData).get(ref.id);
311
+ return __privateGet$2(this, _extensionData).get(ref.id);
285
312
  }
286
313
  toJSON() {
287
314
  return {
288
315
  id: this.id,
289
- output: __privateGet(this, _extensionData).size > 0 ? [...__privateGet(this, _extensionData).keys()] : void 0,
316
+ output: __privateGet$2(this, _extensionData).size > 0 ? [...__privateGet$2(this, _extensionData).keys()] : void 0,
290
317
  attachments: this.attachments.size > 0 ? Object.fromEntries(this.attachments) : void 0
291
318
  };
292
319
  }
293
320
  toString() {
294
- const out = __privateGet(this, _extensionData).size > 0 ? ` out=[${[...__privateGet(this, _extensionData).keys()].join(", ")}]` : "";
321
+ const out = __privateGet$2(this, _extensionData).size > 0 ? ` out=[${[...__privateGet$2(this, _extensionData).keys()].join(", ")}]` : "";
295
322
  if (this.attachments.size === 0) {
296
323
  return `<${this.id}${out} />`;
297
324
  }
@@ -350,6 +377,21 @@ function createExtensionInstance(options) {
350
377
  );
351
378
  }
352
379
 
380
+ function toInternalExtensionOverrides(overrides) {
381
+ const internal = overrides;
382
+ if (internal.$$type !== "@backstage/ExtensionOverrides") {
383
+ throw new Error(
384
+ `Invalid translation resource, bad type '${internal.$$type}'`
385
+ );
386
+ }
387
+ if (internal.version !== "v1") {
388
+ throw new Error(
389
+ `Invalid translation resource, bad version '${internal.version}'`
390
+ );
391
+ }
392
+ return internal;
393
+ }
394
+
353
395
  const knownExtensionParameters = ["attachTo", "disabled", "config"];
354
396
  function readAppExtensionParameters(rootConfig) {
355
397
  const arr = rootConfig.getOptional("app.extensions");
@@ -462,19 +504,47 @@ function expandShorthandExtensionParameters(arrayEntry, arrayIndex) {
462
504
  };
463
505
  }
464
506
  function mergeExtensionParameters(options) {
465
- const { sources, builtinExtensions, parameters } = options;
466
- const pluginExtensions = sources.flatMap((source) => {
507
+ var _a;
508
+ const { builtinExtensions, parameters } = options;
509
+ const plugins = options.features.filter(
510
+ (f) => f.$$type === "@backstage/BackstagePlugin"
511
+ );
512
+ const overrides = options.features.filter(
513
+ (f) => f.$$type === "@backstage/ExtensionOverrides"
514
+ );
515
+ const pluginExtensions = plugins.flatMap((source) => {
467
516
  return source.extensions.map((extension) => ({ ...extension, source }));
468
517
  });
469
- if (pluginExtensions.some(({ id }) => id === "root")) {
470
- const rootPluginIds = pluginExtensions.filter(({ id }) => id === "root").map(({ source }) => source.id);
518
+ const overrideExtensions = overrides.flatMap(
519
+ (override) => toInternalExtensionOverrides(override).extensions
520
+ );
521
+ if (pluginExtensions.some(({ id }) => id === "core")) {
522
+ const pluginIds = pluginExtensions.filter(({ id }) => id === "core").map(({ source }) => source.id);
471
523
  throw new Error(
472
- `The following plugin(s) are overriding the 'root' extension which is forbidden: ${rootPluginIds.join(
524
+ `The following plugin(s) are overriding the 'core' extension which is forbidden: ${pluginIds.join(
473
525
  ","
474
526
  )}`
475
527
  );
476
528
  }
477
- const overrides = [
529
+ if (overrideExtensions.some(({ id }) => id === "root")) {
530
+ throw new Error(
531
+ `An extension override is overriding the 'root' extension which is forbidden`
532
+ );
533
+ }
534
+ const overrideExtensionIds = overrideExtensions.map(({ id }) => id);
535
+ if (overrideExtensionIds.length !== new Set(overrideExtensionIds).size) {
536
+ const counts = /* @__PURE__ */ new Map();
537
+ for (const id of overrideExtensionIds) {
538
+ counts.set(id, ((_a = counts.get(id)) != null ? _a : 0) + 1);
539
+ }
540
+ const duplicated = Array.from(counts.entries()).filter(([, count]) => count > 1).map(([id]) => id);
541
+ throw new Error(
542
+ `The following extensions had duplicate overrides: ${duplicated.join(
543
+ ", "
544
+ )}`
545
+ );
546
+ }
547
+ const configuredExtensions = [
478
548
  ...pluginExtensions.map(({ source, ...extension }) => ({
479
549
  extension,
480
550
  params: {
@@ -494,14 +564,34 @@ function mergeExtensionParameters(options) {
494
564
  }
495
565
  }))
496
566
  ];
567
+ for (const extension of overrideExtensions) {
568
+ const index = configuredExtensions.findIndex(
569
+ (e) => e.extension.id === extension.id
570
+ );
571
+ if (index !== -1) {
572
+ configuredExtensions[index].extension = extension;
573
+ configuredExtensions[index].params.attachTo = extension.attachTo;
574
+ configuredExtensions[index].params.disabled = extension.disabled;
575
+ } else {
576
+ configuredExtensions.push({
577
+ extension,
578
+ params: {
579
+ source: void 0,
580
+ attachTo: extension.attachTo,
581
+ disabled: extension.disabled,
582
+ config: void 0
583
+ }
584
+ });
585
+ }
586
+ }
497
587
  const duplicatedExtensionIds = /* @__PURE__ */ new Set();
498
- const duplicatedExtensionData = overrides.reduce((data, { extension, params }) => {
499
- var _a, _b, _c;
588
+ const duplicatedExtensionData = configuredExtensions.reduce((data, { extension, params }) => {
589
+ var _a2, _b, _c;
500
590
  const extensionId = extension.id;
501
591
  const extensionData = data == null ? void 0 : data[extensionId];
502
592
  if (extensionData)
503
593
  duplicatedExtensionIds.add(extensionId);
504
- const pluginId = (_b = (_a = params.source) == null ? void 0 : _a.id) != null ? _b : "internal";
594
+ const pluginId = (_b = (_a2 = params.source) == null ? void 0 : _a2.id) != null ? _b : "internal";
505
595
  const pluginCount = (_c = extensionData == null ? void 0 : extensionData[pluginId]) != null ? _c : 0;
506
596
  return {
507
597
  ...data,
@@ -523,16 +613,16 @@ function mergeExtensionParameters(options) {
523
613
  }
524
614
  for (const overrideParam of parameters) {
525
615
  const extensionId = overrideParam.id;
526
- if (extensionId === "root") {
616
+ if (extensionId === "core") {
527
617
  throw new Error(
528
- "A 'root' extension configuration was detected, but the root extension is not configurable"
618
+ "A 'core' extension configuration was detected, but the core extension is not configurable"
529
619
  );
530
620
  }
531
- const existingIndex = overrides.findIndex(
621
+ const existingIndex = configuredExtensions.findIndex(
532
622
  (e) => e.extension.id === extensionId
533
623
  );
534
624
  if (existingIndex !== -1) {
535
- const existing = overrides[existingIndex];
625
+ const existing = configuredExtensions[existingIndex];
536
626
  if (overrideParam.attachTo) {
537
627
  existing.params.attachTo = overrideParam.attachTo;
538
628
  }
@@ -542,15 +632,15 @@ function mergeExtensionParameters(options) {
542
632
  if (Boolean(existing.params.disabled) !== Boolean(overrideParam.disabled)) {
543
633
  existing.params.disabled = Boolean(overrideParam.disabled);
544
634
  if (!existing.params.disabled) {
545
- overrides.splice(existingIndex, 1);
546
- overrides.push(existing);
635
+ configuredExtensions.splice(existingIndex, 1);
636
+ configuredExtensions.push(existing);
547
637
  }
548
638
  }
549
639
  } else {
550
640
  throw new Error(`Extension ${extensionId} does not exist`);
551
641
  }
552
642
  }
553
- return overrides.filter((override) => !override.params.disabled).map((param) => ({
643
+ return configuredExtensions.filter((override) => !override.params.disabled).map((param) => ({
554
644
  extension: param.extension,
555
645
  attachTo: param.params.attachTo,
556
646
  source: param.params.source,
@@ -558,14 +648,54 @@ function mergeExtensionParameters(options) {
558
648
  }));
559
649
  }
560
650
 
561
- function getAvailablePlugins() {
651
+ function readPackageDetectionConfig(config) {
652
+ const packages = config.getOptional("app.experimental.packages");
653
+ if (packages === void 0 || packages === null) {
654
+ return void 0;
655
+ }
656
+ if (typeof packages === "string") {
657
+ if (packages !== "all") {
658
+ throw new Error(
659
+ `Invalid app.experimental.packages mode, got '${packages}', expected 'all'`
660
+ );
661
+ }
662
+ return {};
663
+ }
664
+ if (typeof packages !== "object" || Array.isArray(packages)) {
665
+ throw new Error(
666
+ "Invalid config at 'app.experimental.packages', expected object"
667
+ );
668
+ }
669
+ const packagesConfig = new ConfigReader(
670
+ packages,
671
+ "app.experimental.packages"
672
+ );
673
+ return {
674
+ include: packagesConfig.getOptionalStringArray("include"),
675
+ exclude: packagesConfig.getOptionalStringArray("exclude")
676
+ };
677
+ }
678
+ function getAvailableFeatures(config) {
562
679
  var _a;
563
680
  const discovered = window["__@backstage/discovered__"];
564
- return (_a = discovered == null ? void 0 : discovered.modules.map((m) => m.default).filter(isBackstagePlugin)) != null ? _a : [];
681
+ const detection = readPackageDetectionConfig(config);
682
+ if (!detection) {
683
+ return [];
684
+ }
685
+ return (_a = discovered == null ? void 0 : discovered.modules.filter(({ name }) => {
686
+ var _a2;
687
+ if ((_a2 = detection.exclude) == null ? void 0 : _a2.includes(name)) {
688
+ return false;
689
+ }
690
+ if (detection.include && !detection.include.includes(name)) {
691
+ return false;
692
+ }
693
+ return true;
694
+ }).map((m) => m.default).filter(isBackstageFeature)) != null ? _a : [];
565
695
  }
566
- function isBackstagePlugin(obj) {
696
+ function isBackstageFeature(obj) {
567
697
  if (obj !== null && typeof obj === "object" && "$$type" in obj) {
568
- return obj.$$type === "@backstage/BackstagePlugin";
698
+ return obj.$$type === "@backstage/BackstagePlugin" || obj.$$type === "@backstage/ExtensionOverrides";
569
699
  }
570
700
  return false;
571
701
  }
@@ -624,10 +754,10 @@ function AppThemeProvider({ children }) {
624
754
  return /* @__PURE__ */ React.createElement(appTheme.Provider, { children });
625
755
  }
626
756
 
627
- var __defProp$1 = Object.defineProperty;
628
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
629
- var __publicField$1 = (obj, key, value) => {
630
- __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
757
+ var __defProp$2 = Object.defineProperty;
758
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
759
+ var __publicField$2 = (obj, key, value) => {
760
+ __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
631
761
  return value;
632
762
  };
633
763
  function mkError(thing) {
@@ -642,11 +772,11 @@ function logDeprecation(thing) {
642
772
  }
643
773
  class AppIdentityProxy {
644
774
  constructor() {
645
- __publicField$1(this, "target");
646
- __publicField$1(this, "waitForTarget");
647
- __publicField$1(this, "resolveTarget", () => {
775
+ __publicField$2(this, "target");
776
+ __publicField$2(this, "waitForTarget");
777
+ __publicField$2(this, "resolveTarget", () => {
648
778
  });
649
- __publicField$1(this, "signOutTargetUrl", "/");
779
+ __publicField$2(this, "signOutTargetUrl", "/");
650
780
  this.waitForTarget = new Promise((resolve) => {
651
781
  this.resolveTarget = resolve;
652
782
  });
@@ -718,10 +848,10 @@ const AppContextProvider = ({
718
848
  return /* @__PURE__ */ React.createElement(AppContext.Provider, { value: versionedValue, children });
719
849
  };
720
850
 
721
- var __defProp = Object.defineProperty;
722
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
723
- var __publicField = (obj, key, value) => {
724
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
851
+ var __defProp$1 = Object.defineProperty;
852
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
853
+ var __publicField$1 = (obj, key, value) => {
854
+ __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
725
855
  return value;
726
856
  };
727
857
  function validateFlagName(name) {
@@ -743,8 +873,8 @@ function validateFlagName(name) {
743
873
  }
744
874
  class LocalStorageFeatureFlags {
745
875
  constructor() {
746
- __publicField(this, "registeredFeatureFlags", []);
747
- __publicField(this, "flags");
876
+ __publicField$1(this, "registeredFeatureFlags", []);
877
+ __publicField$1(this, "flags");
748
878
  }
749
879
  registerFlag(flag) {
750
880
  validateFlagName(flag.name);
@@ -1048,6 +1178,499 @@ const RoutingProvider = ({
1048
1178
  return /* @__PURE__ */ React.createElement(RoutingContext.Provider, { value: versionedValue }, children);
1049
1179
  };
1050
1180
 
1181
+ function resolveRouteBindings(bindRoutes) {
1182
+ const result = /* @__PURE__ */ new Map();
1183
+ if (bindRoutes) {
1184
+ const bind = (externalRoutes, targetRoutes) => {
1185
+ for (const [key, value] of Object.entries(targetRoutes)) {
1186
+ const externalRoute = externalRoutes[key];
1187
+ if (!externalRoute) {
1188
+ throw new Error(`Key ${key} is not an existing external route`);
1189
+ }
1190
+ if (!value && !externalRoute.optional) {
1191
+ throw new Error(
1192
+ `External route ${key} is required but was undefined`
1193
+ );
1194
+ }
1195
+ if (value) {
1196
+ result.set(externalRoute, value);
1197
+ }
1198
+ }
1199
+ };
1200
+ bindRoutes({ bind });
1201
+ }
1202
+ return result;
1203
+ }
1204
+
1205
+ var __defProp = Object.defineProperty;
1206
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1207
+ var __publicField = (obj, key, value) => {
1208
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1209
+ return value;
1210
+ };
1211
+ class BehaviorSubject {
1212
+ constructor(value) {
1213
+ __publicField(this, "isClosed");
1214
+ __publicField(this, "currentValue");
1215
+ __publicField(this, "terminatingError");
1216
+ __publicField(this, "observable");
1217
+ __publicField(this, "subscribers", /* @__PURE__ */ new Set());
1218
+ this.isClosed = false;
1219
+ this.currentValue = value;
1220
+ this.terminatingError = void 0;
1221
+ this.observable = new ObservableImpl((subscriber) => {
1222
+ if (this.isClosed) {
1223
+ if (this.terminatingError) {
1224
+ subscriber.error(this.terminatingError);
1225
+ } else {
1226
+ subscriber.complete();
1227
+ }
1228
+ return () => {
1229
+ };
1230
+ }
1231
+ subscriber.next(this.currentValue);
1232
+ this.subscribers.add(subscriber);
1233
+ return () => {
1234
+ this.subscribers.delete(subscriber);
1235
+ };
1236
+ });
1237
+ }
1238
+ [Symbol.observable]() {
1239
+ return this;
1240
+ }
1241
+ get closed() {
1242
+ return this.isClosed;
1243
+ }
1244
+ next(value) {
1245
+ if (this.isClosed) {
1246
+ throw new Error("BehaviorSubject is closed");
1247
+ }
1248
+ this.currentValue = value;
1249
+ this.subscribers.forEach((subscriber) => subscriber.next(value));
1250
+ }
1251
+ error(error) {
1252
+ if (this.isClosed) {
1253
+ throw new Error("BehaviorSubject is closed");
1254
+ }
1255
+ this.isClosed = true;
1256
+ this.terminatingError = error;
1257
+ this.subscribers.forEach((subscriber) => subscriber.error(error));
1258
+ }
1259
+ complete() {
1260
+ if (this.isClosed) {
1261
+ throw new Error("BehaviorSubject is closed");
1262
+ }
1263
+ this.isClosed = true;
1264
+ this.subscribers.forEach((subscriber) => subscriber.complete());
1265
+ }
1266
+ subscribe(onNext, onError, onComplete) {
1267
+ const observer = typeof onNext === "function" ? {
1268
+ next: onNext,
1269
+ error: onError,
1270
+ complete: onComplete
1271
+ } : onNext;
1272
+ return this.observable.subscribe(observer);
1273
+ }
1274
+ }
1275
+
1276
+ var __accessCheck$1 = (obj, member, msg) => {
1277
+ if (!member.has(obj))
1278
+ throw TypeError("Cannot " + msg);
1279
+ };
1280
+ var __privateGet$1 = (obj, member, getter) => {
1281
+ __accessCheck$1(obj, member, "read from private field");
1282
+ return getter ? getter.call(obj) : member.get(obj);
1283
+ };
1284
+ var __privateAdd$1 = (obj, member, value) => {
1285
+ if (member.has(obj))
1286
+ throw TypeError("Cannot add the same private member more than once");
1287
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1288
+ };
1289
+ var __privateSet$1 = (obj, member, value, setter) => {
1290
+ __accessCheck$1(obj, member, "write to private field");
1291
+ setter ? setter.call(obj, value) : member.set(obj, value);
1292
+ return value;
1293
+ };
1294
+ var _languages, _language$1, _subject;
1295
+ const STORAGE_KEY = "language";
1296
+ const DEFAULT_LANGUAGE = "en";
1297
+ const _AppLanguageSelector = class _AppLanguageSelector {
1298
+ constructor(languages, initialLanguage) {
1299
+ __privateAdd$1(this, _languages, void 0);
1300
+ __privateAdd$1(this, _language$1, void 0);
1301
+ __privateAdd$1(this, _subject, void 0);
1302
+ __privateSet$1(this, _languages, languages);
1303
+ __privateSet$1(this, _language$1, initialLanguage);
1304
+ __privateSet$1(this, _subject, new BehaviorSubject({
1305
+ language: __privateGet$1(this, _language$1)
1306
+ }));
1307
+ }
1308
+ static create(options) {
1309
+ var _a, _b;
1310
+ const languages = (_a = options == null ? void 0 : options.availableLanguages) != null ? _a : [DEFAULT_LANGUAGE];
1311
+ if (languages.length !== new Set(languages).size) {
1312
+ throw new Error(
1313
+ `Supported languages may not contain duplicates, got '${languages.join(
1314
+ "', '"
1315
+ )}'`
1316
+ );
1317
+ }
1318
+ if (!languages.includes(DEFAULT_LANGUAGE)) {
1319
+ throw new Error(`Supported languages must include '${DEFAULT_LANGUAGE}'`);
1320
+ }
1321
+ const initialLanguage = (_b = options == null ? void 0 : options.defaultLanguage) != null ? _b : DEFAULT_LANGUAGE;
1322
+ if (!languages.includes(initialLanguage)) {
1323
+ throw new Error(
1324
+ `Initial language must be one of the supported languages, got '${initialLanguage}'`
1325
+ );
1326
+ }
1327
+ return new _AppLanguageSelector(languages, initialLanguage);
1328
+ }
1329
+ static createWithStorage(options) {
1330
+ const selector = _AppLanguageSelector.create(options);
1331
+ if (!window.localStorage) {
1332
+ return selector;
1333
+ }
1334
+ const storedLanguage = window.localStorage.getItem(STORAGE_KEY);
1335
+ const { languages } = selector.getAvailableLanguages();
1336
+ if (storedLanguage && languages.includes(storedLanguage)) {
1337
+ selector.setLanguage(storedLanguage);
1338
+ }
1339
+ selector.language$().subscribe(({ language }) => {
1340
+ if (language !== window.localStorage.getItem(STORAGE_KEY)) {
1341
+ window.localStorage.setItem(STORAGE_KEY, language);
1342
+ }
1343
+ });
1344
+ window.addEventListener("storage", (event) => {
1345
+ var _a;
1346
+ if (event.key === STORAGE_KEY) {
1347
+ const language = (_a = localStorage.getItem(STORAGE_KEY)) != null ? _a : void 0;
1348
+ if (language) {
1349
+ selector.setLanguage(language);
1350
+ }
1351
+ }
1352
+ });
1353
+ return selector;
1354
+ }
1355
+ getAvailableLanguages() {
1356
+ return { languages: __privateGet$1(this, _languages).slice() };
1357
+ }
1358
+ setLanguage(language) {
1359
+ const lng = language != null ? language : DEFAULT_LANGUAGE;
1360
+ if (lng === __privateGet$1(this, _language$1)) {
1361
+ return;
1362
+ }
1363
+ if (lng && !__privateGet$1(this, _languages).includes(lng)) {
1364
+ throw new Error(
1365
+ `Failed to change language to '${lng}', available languages are '${__privateGet$1(this, _languages).join(
1366
+ "', '"
1367
+ )}'`
1368
+ );
1369
+ }
1370
+ __privateSet$1(this, _language$1, lng);
1371
+ __privateGet$1(this, _subject).next({ language: lng });
1372
+ }
1373
+ getLanguage() {
1374
+ return { language: __privateGet$1(this, _language$1) };
1375
+ }
1376
+ language$() {
1377
+ return __privateGet$1(this, _subject);
1378
+ }
1379
+ };
1380
+ _languages = new WeakMap();
1381
+ _language$1 = new WeakMap();
1382
+ _subject = new WeakMap();
1383
+ let AppLanguageSelector = _AppLanguageSelector;
1384
+
1385
+ function toInternalTranslationResource(resource) {
1386
+ const r = resource;
1387
+ if (r.$$type !== "@backstage/TranslationResource") {
1388
+ throw new Error(`Invalid translation resource, bad type '${r.$$type}'`);
1389
+ }
1390
+ if (r.version !== "v1") {
1391
+ throw new Error(`Invalid translation resource, bad version '${r.version}'`);
1392
+ }
1393
+ return r;
1394
+ }
1395
+
1396
+ function toInternalTranslationRef(ref) {
1397
+ const r = ref;
1398
+ if (r.$$type !== "@backstage/TranslationRef") {
1399
+ throw new Error(`Invalid translation ref, bad type '${r.$$type}'`);
1400
+ }
1401
+ if (r.version !== "v1") {
1402
+ throw new Error(`Invalid translation ref, bad version '${r.version}'`);
1403
+ }
1404
+ return r;
1405
+ }
1406
+
1407
+ var __accessCheck = (obj, member, msg) => {
1408
+ if (!member.has(obj))
1409
+ throw TypeError("Cannot " + msg);
1410
+ };
1411
+ var __privateGet = (obj, member, getter) => {
1412
+ __accessCheck(obj, member, "read from private field");
1413
+ return getter ? getter.call(obj) : member.get(obj);
1414
+ };
1415
+ var __privateAdd = (obj, member, value) => {
1416
+ if (member.has(obj))
1417
+ throw TypeError("Cannot add the same private member more than once");
1418
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1419
+ };
1420
+ var __privateSet = (obj, member, value, setter) => {
1421
+ __accessCheck(obj, member, "write to private field");
1422
+ setter ? setter.call(obj, value) : member.set(obj, value);
1423
+ return value;
1424
+ };
1425
+ var __privateMethod = (obj, member, method) => {
1426
+ __accessCheck(obj, member, "access private method");
1427
+ return method;
1428
+ };
1429
+ var _loaded, _loading, _loaders, _getLoaderKey, getLoaderKey_fn, _i18n, _loader, _language, _registeredRefs, _languageChangeListeners, _changeLanguage, changeLanguage_fn, _createSnapshot, createSnapshot_fn, _registerDefaults, registerDefaults_fn;
1430
+ function removeNulls(messages) {
1431
+ return Object.fromEntries(
1432
+ Object.entries(messages).filter(
1433
+ (e) => e[1] !== null
1434
+ )
1435
+ );
1436
+ }
1437
+ class ResourceLoader {
1438
+ constructor(onLoad) {
1439
+ this.onLoad = onLoad;
1440
+ __privateAdd(this, _getLoaderKey);
1441
+ /** Loaded resources by loader key */
1442
+ __privateAdd(this, _loaded, /* @__PURE__ */ new Set());
1443
+ /** Resource loading promises by loader key */
1444
+ __privateAdd(this, _loading, /* @__PURE__ */ new Map());
1445
+ /** Loaders for each resource language */
1446
+ __privateAdd(this, _loaders, /* @__PURE__ */ new Map());
1447
+ }
1448
+ addTranslationResource(resource) {
1449
+ const internalResource = toInternalTranslationResource(resource);
1450
+ for (const entry of internalResource.resources) {
1451
+ const key = __privateMethod(this, _getLoaderKey, getLoaderKey_fn).call(this, entry.language, internalResource.id);
1452
+ if (!__privateGet(this, _loaders).has(key)) {
1453
+ __privateGet(this, _loaders).set(key, entry.loader);
1454
+ }
1455
+ }
1456
+ }
1457
+ needsLoading(language, namespace) {
1458
+ const key = __privateMethod(this, _getLoaderKey, getLoaderKey_fn).call(this, language, namespace);
1459
+ const loader = __privateGet(this, _loaders).get(key);
1460
+ if (!loader) {
1461
+ return false;
1462
+ }
1463
+ return !__privateGet(this, _loaded).has(key);
1464
+ }
1465
+ async load(language, namespace) {
1466
+ const key = __privateMethod(this, _getLoaderKey, getLoaderKey_fn).call(this, language, namespace);
1467
+ const loader = __privateGet(this, _loaders).get(key);
1468
+ if (!loader) {
1469
+ return;
1470
+ }
1471
+ if (__privateGet(this, _loaded).has(key)) {
1472
+ return;
1473
+ }
1474
+ const loading = __privateGet(this, _loading).get(key);
1475
+ if (loading) {
1476
+ await loading;
1477
+ return;
1478
+ }
1479
+ const load = loader().then(
1480
+ (result) => {
1481
+ this.onLoad({ language, namespace, messages: result.messages });
1482
+ __privateGet(this, _loaded).add(key);
1483
+ },
1484
+ (error) => {
1485
+ __privateGet(this, _loaded).add(key);
1486
+ throw error;
1487
+ }
1488
+ );
1489
+ __privateGet(this, _loading).set(key, load);
1490
+ await load;
1491
+ }
1492
+ }
1493
+ _loaded = new WeakMap();
1494
+ _loading = new WeakMap();
1495
+ _loaders = new WeakMap();
1496
+ _getLoaderKey = new WeakSet();
1497
+ getLoaderKey_fn = function(language, namespace) {
1498
+ return `${language}/${namespace}`;
1499
+ };
1500
+ const _I18nextTranslationApi = class _I18nextTranslationApi {
1501
+ constructor(i18n, loader, language) {
1502
+ __privateAdd(this, _changeLanguage);
1503
+ __privateAdd(this, _createSnapshot);
1504
+ __privateAdd(this, _registerDefaults);
1505
+ __privateAdd(this, _i18n, void 0);
1506
+ __privateAdd(this, _loader, void 0);
1507
+ __privateAdd(this, _language, void 0);
1508
+ /** Keep track of which refs we have registered default resources for */
1509
+ __privateAdd(this, _registeredRefs, /* @__PURE__ */ new Set());
1510
+ /** Notify observers when language changes */
1511
+ __privateAdd(this, _languageChangeListeners, /* @__PURE__ */ new Set());
1512
+ __privateSet(this, _i18n, i18n);
1513
+ __privateSet(this, _loader, loader);
1514
+ __privateSet(this, _language, language);
1515
+ }
1516
+ static create(options) {
1517
+ const { languages } = options.languageApi.getAvailableLanguages();
1518
+ const i18n = createInstance({
1519
+ fallbackLng: DEFAULT_LANGUAGE,
1520
+ supportedLngs: languages,
1521
+ interpolation: {
1522
+ escapeValue: false
1523
+ },
1524
+ ns: [],
1525
+ defaultNS: false,
1526
+ fallbackNS: false,
1527
+ // Disable resource loading on init, meaning i18n will be ready to use immediately
1528
+ initImmediate: false
1529
+ });
1530
+ i18n.init();
1531
+ if (!i18n.isInitialized) {
1532
+ throw new Error("i18next was unexpectedly not initialized");
1533
+ }
1534
+ const { language: initialLanguage } = options.languageApi.getLanguage();
1535
+ if (initialLanguage !== DEFAULT_LANGUAGE) {
1536
+ i18n.changeLanguage(initialLanguage);
1537
+ }
1538
+ const loader = new ResourceLoader((loaded) => {
1539
+ i18n.addResourceBundle(
1540
+ loaded.language,
1541
+ loaded.namespace,
1542
+ removeNulls(loaded.messages),
1543
+ false,
1544
+ // do not merge with existing translations
1545
+ true
1546
+ // overwrite translations
1547
+ );
1548
+ });
1549
+ const resources = (options == null ? void 0 : options.resources) || [];
1550
+ for (let i = resources.length - 1; i >= 0; i--) {
1551
+ const resource = resources[i];
1552
+ if (resource.$$type === "@backstage/TranslationResource") {
1553
+ loader.addTranslationResource(resource);
1554
+ } else if (resource.$$type === "@backstage/TranslationMessages") {
1555
+ i18n.addResourceBundle(
1556
+ DEFAULT_LANGUAGE,
1557
+ resource.id,
1558
+ removeNulls(resource.messages),
1559
+ true,
1560
+ // merge with existing translations
1561
+ false
1562
+ // do not overwrite translations
1563
+ );
1564
+ }
1565
+ }
1566
+ const instance = new _I18nextTranslationApi(
1567
+ i18n,
1568
+ loader,
1569
+ options.languageApi.getLanguage().language
1570
+ );
1571
+ options.languageApi.language$().subscribe(({ language }) => {
1572
+ var _a;
1573
+ __privateMethod(_a = instance, _changeLanguage, changeLanguage_fn).call(_a, language);
1574
+ });
1575
+ return instance;
1576
+ }
1577
+ getTranslation(translationRef) {
1578
+ const internalRef = toInternalTranslationRef(translationRef);
1579
+ __privateMethod(this, _registerDefaults, registerDefaults_fn).call(this, internalRef);
1580
+ return __privateMethod(this, _createSnapshot, createSnapshot_fn).call(this, internalRef);
1581
+ }
1582
+ translation$(translationRef) {
1583
+ const internalRef = toInternalTranslationRef(translationRef);
1584
+ __privateMethod(this, _registerDefaults, registerDefaults_fn).call(this, internalRef);
1585
+ return new ObservableImpl((subscriber) => {
1586
+ let loadTicket = {};
1587
+ const loadResource = () => {
1588
+ loadTicket = {};
1589
+ const ticket = loadTicket;
1590
+ __privateGet(this, _loader).load(__privateGet(this, _language), internalRef.id).then(
1591
+ () => {
1592
+ if (ticket === loadTicket) {
1593
+ const snapshot = __privateMethod(this, _createSnapshot, createSnapshot_fn).call(this, internalRef);
1594
+ if (snapshot.ready) {
1595
+ subscriber.next(snapshot);
1596
+ }
1597
+ }
1598
+ },
1599
+ (error) => {
1600
+ if (ticket === loadTicket) {
1601
+ subscriber.error(Array.isArray(error) ? error[0] : error);
1602
+ }
1603
+ }
1604
+ );
1605
+ };
1606
+ const onChange = () => {
1607
+ const snapshot = __privateMethod(this, _createSnapshot, createSnapshot_fn).call(this, internalRef);
1608
+ if (snapshot.ready) {
1609
+ subscriber.next(snapshot);
1610
+ } else {
1611
+ loadResource();
1612
+ }
1613
+ };
1614
+ if (__privateGet(this, _loader).needsLoading(__privateGet(this, _language), internalRef.id)) {
1615
+ loadResource();
1616
+ }
1617
+ __privateGet(this, _languageChangeListeners).add(onChange);
1618
+ return () => {
1619
+ __privateGet(this, _languageChangeListeners).delete(onChange);
1620
+ };
1621
+ });
1622
+ }
1623
+ };
1624
+ _i18n = new WeakMap();
1625
+ _loader = new WeakMap();
1626
+ _language = new WeakMap();
1627
+ _registeredRefs = new WeakMap();
1628
+ _languageChangeListeners = new WeakMap();
1629
+ _changeLanguage = new WeakSet();
1630
+ changeLanguage_fn = function(language) {
1631
+ if (__privateGet(this, _language) !== language) {
1632
+ __privateSet(this, _language, language);
1633
+ __privateGet(this, _i18n).changeLanguage(language);
1634
+ __privateGet(this, _languageChangeListeners).forEach((listener) => listener());
1635
+ }
1636
+ };
1637
+ _createSnapshot = new WeakSet();
1638
+ createSnapshot_fn = function(internalRef) {
1639
+ if (__privateGet(this, _loader).needsLoading(__privateGet(this, _language), internalRef.id)) {
1640
+ return { ready: false };
1641
+ }
1642
+ const t = __privateGet(this, _i18n).getFixedT(
1643
+ null,
1644
+ internalRef.id
1645
+ );
1646
+ return {
1647
+ ready: true,
1648
+ t
1649
+ };
1650
+ };
1651
+ _registerDefaults = new WeakSet();
1652
+ registerDefaults_fn = function(internalRef) {
1653
+ if (__privateGet(this, _registeredRefs).has(internalRef.id)) {
1654
+ return;
1655
+ }
1656
+ __privateGet(this, _registeredRefs).add(internalRef.id);
1657
+ const defaultMessages = internalRef.getDefaultMessages();
1658
+ __privateGet(this, _i18n).addResourceBundle(
1659
+ DEFAULT_LANGUAGE,
1660
+ internalRef.id,
1661
+ defaultMessages,
1662
+ true,
1663
+ // merge with existing translations
1664
+ false
1665
+ // do not overwrite translations
1666
+ );
1667
+ const defaultResource = internalRef.getDefaultResource();
1668
+ if (defaultResource) {
1669
+ __privateGet(this, _loader).addTranslationResource(defaultResource);
1670
+ }
1671
+ };
1672
+ let I18nextTranslationApi = _I18nextTranslationApi;
1673
+
1051
1674
  const apis = [
1052
1675
  createApiFactory({
1053
1676
  api: discoveryApiRef,
@@ -1343,7 +1966,7 @@ function joinPaths(...paths) {
1343
1966
  }
1344
1967
  return normalized;
1345
1968
  }
1346
- function extractRouteInfoFromInstanceTree(roots) {
1969
+ function extractRouteInfoFromInstanceTree(core) {
1347
1970
  const routePaths = /* @__PURE__ */ new Map();
1348
1971
  const routeParents = /* @__PURE__ */ new Map();
1349
1972
  const routeObjects = new Array();
@@ -1409,16 +2032,14 @@ function extractRouteInfoFromInstanceTree(roots) {
1409
2032
  }
1410
2033
  }
1411
2034
  }
1412
- for (const root of roots) {
1413
- visit(root);
1414
- }
2035
+ visit(core);
1415
2036
  return { routePaths, routeParents, routeObjects };
1416
2037
  }
1417
2038
 
1418
2039
  function createExtensionTree(options) {
1419
- const plugins = getAvailablePlugins();
2040
+ const features = getAvailableFeatures(options.config);
1420
2041
  const { instances } = createInstances({
1421
- plugins,
2042
+ features,
1422
2043
  config: options.config
1423
2044
  });
1424
2045
  return {
@@ -1468,7 +2089,6 @@ function createExtensionTree(options) {
1468
2089
  };
1469
2090
  }
1470
2091
  function createInstances(options) {
1471
- var _a, _b;
1472
2092
  const builtinExtensions = [
1473
2093
  Core,
1474
2094
  CoreRoutes,
@@ -1478,7 +2098,7 @@ function createInstances(options) {
1478
2098
  DarkTheme
1479
2099
  ];
1480
2100
  const extensionParams = mergeExtensionParameters({
1481
- sources: options.plugins,
2101
+ features: options.features,
1482
2102
  builtinExtensions,
1483
2103
  parameters: readAppExtensionParameters(options.config)
1484
2104
  });
@@ -1500,14 +2120,14 @@ function createInstances(options) {
1500
2120
  }
1501
2121
  const instances = /* @__PURE__ */ new Map();
1502
2122
  function createInstance(instanceParams) {
1503
- var _a2, _b2;
2123
+ var _a, _b;
1504
2124
  const extensionId = instanceParams.extension.id;
1505
2125
  const existingInstance = instances.get(extensionId);
1506
2126
  if (existingInstance) {
1507
2127
  return existingInstance;
1508
2128
  }
1509
2129
  const attachments = new Map(
1510
- Array.from((_b2 = (_a2 = attachmentMap.get(extensionId)) == null ? void 0 : _a2.entries()) != null ? _b2 : []).map(
2130
+ Array.from((_b = (_a = attachmentMap.get(extensionId)) == null ? void 0 : _a.entries()) != null ? _b : []).map(
1511
2131
  ([inputName, attachmentConfigs]) => {
1512
2132
  return [inputName, attachmentConfigs.map(createInstance)];
1513
2133
  }
@@ -1522,38 +2142,43 @@ function createInstances(options) {
1522
2142
  instances.set(extensionId, newInstance);
1523
2143
  return newInstance;
1524
2144
  }
1525
- const rootConfigs = (_b = (_a = attachmentMap.get("root")) == null ? void 0 : _a.get("default")) != null ? _b : [];
1526
- const rootInstances = rootConfigs.map(
1527
- (instanceParams) => createInstance(instanceParams)
2145
+ const coreInstance = createInstance(
2146
+ extensionParams.find((p) => p.extension.id === "core")
1528
2147
  );
1529
- return { instances, rootInstances };
2148
+ return { coreInstance, instances };
1530
2149
  }
1531
2150
  function createApp(options) {
1532
2151
  async function appLoader() {
1533
- var _a, _b, _c, _d;
2152
+ var _a, _b, _c, _d, _e;
1534
2153
  const config = (_b = await ((_a = options == null ? void 0 : options.configLoader) == null ? void 0 : _a.call(options))) != null ? _b : ConfigReader.fromConfigs(
1535
2154
  overrideBaseUrlConfigs(defaultConfigLoaderSync())
1536
2155
  );
1537
- const discoveredPlugins = getAvailablePlugins();
1538
- const loadedPlugins = (_d = await ((_c = options.pluginLoader) == null ? void 0 : _c.call(options, { config }))) != null ? _d : [];
1539
- const allPlugins = Array.from(
1540
- /* @__PURE__ */ new Set([...discoveredPlugins, ...options.plugins, ...loadedPlugins])
2156
+ const discoveredFeatures = getAvailableFeatures(config);
2157
+ const loadedFeatures = (_d = await ((_c = options.featureLoader) == null ? void 0 : _c.call(options, { config }))) != null ? _d : [];
2158
+ const allFeatures = Array.from(
2159
+ /* @__PURE__ */ new Set([
2160
+ ...discoveredFeatures,
2161
+ ...(_e = options.features) != null ? _e : [],
2162
+ ...loadedFeatures
2163
+ ])
1541
2164
  );
1542
- const { rootInstances } = createInstances({
1543
- plugins: allPlugins,
2165
+ const { coreInstance } = createInstances({
2166
+ features: allFeatures,
1544
2167
  config
1545
2168
  });
1546
- const routeInfo = extractRouteInfoFromInstanceTree(rootInstances);
1547
- const coreInstance = rootInstances.find(({ id }) => id === "core");
1548
- if (!coreInstance) {
1549
- throw Error("Unable to find core extension instance");
1550
- }
1551
- const apiHolder = createApiHolder(coreInstance, config);
1552
- const appContext = createLegacyAppContext(allPlugins);
1553
- const rootElements = rootInstances.map((e) => /* @__PURE__ */ React.createElement(React.Fragment, { key: e.id }, e.getData(coreExtensionData.reactElement))).filter((x) => !!x);
1554
- const App = () => /* @__PURE__ */ React.createElement(ApiProvider, { apis: apiHolder }, /* @__PURE__ */ React.createElement(AppContextProvider, { appContext }, /* @__PURE__ */ React.createElement(AppThemeProvider, null, /* @__PURE__ */ React.createElement(RoutingProvider, { ...routeInfo, routeBindings: /* @__PURE__ */ new Map(
1555
- /* TODO */
1556
- ) }, /* @__PURE__ */ React.createElement(BrowserRouter, null, rootElements)))));
2169
+ const appContext = createLegacyAppContext(
2170
+ allFeatures.filter(
2171
+ (f) => f.$$type === "@backstage/BackstagePlugin"
2172
+ )
2173
+ );
2174
+ const App = () => /* @__PURE__ */ React.createElement(ApiProvider, { apis: createApiHolder(coreInstance, config) }, /* @__PURE__ */ React.createElement(AppContextProvider, { appContext }, /* @__PURE__ */ React.createElement(AppThemeProvider, null, /* @__PURE__ */ React.createElement(
2175
+ RoutingProvider,
2176
+ {
2177
+ ...extractRouteInfoFromInstanceTree(coreInstance),
2178
+ routeBindings: resolveRouteBindings(options.bindRoutes)
2179
+ },
2180
+ /* @__PURE__ */ React.createElement(BrowserRouter, null, coreInstance.getData(coreExtensionData.reactElement))
2181
+ ))));
1557
2182
  return { default: App };
1558
2183
  }
1559
2184
  return {
@@ -1664,6 +2289,18 @@ function createApiHolder(coreExtension, configApi) {
1664
2289
  deps: {},
1665
2290
  factory: () => configApi
1666
2291
  });
2292
+ factoryRegistry.register("static", {
2293
+ api: appLanguageApiRef,
2294
+ deps: {},
2295
+ factory: () => AppLanguageSelector.createWithStorage()
2296
+ });
2297
+ factoryRegistry.register("default", {
2298
+ api: translationApiRef,
2299
+ deps: { languageApi: appLanguageApiRef },
2300
+ factory: ({ languageApi }) => I18nextTranslationApi.create({
2301
+ languageApi
2302
+ })
2303
+ });
1667
2304
  for (const factory of apis) {
1668
2305
  if (!factoryRegistry.register("app", factory)) {
1669
2306
  throw new Error(