@backstage/backend-app-api 0.5.3-next.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @backstage/backend-app-api might be problematic. Click here for more details.

package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # @backstage/backend-app-api
2
2
 
3
+ ## 0.5.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 154632d8753b: Add support for discovering additional service factories during startup.
8
+ - 37a20c7f14aa: Adds include and exclude configuration to feature discovery of backend packages
9
+ Adds alpha modules to feature discovery
10
+ - cb7fc410ed99: The experimental backend feature discovery now only considers default exports from packages. It no longer filters packages to include based on the package role, except that `'cli'` packages are ignored. However, the `"backstage"` field is still required in `package.json`.
11
+ - 3fc64b9e2f8f: Extension points are now tracked via their ID rather than reference, in order to support package duplication.
12
+ - 3b30b179cb38: Add support for installing features as package imports, for example `backend.add(import('my-plugin'))`.
13
+ - b219d097b3f4: Backend startup will now fail if any circular service dependencies are detected.
14
+ - Updated dependencies
15
+ - @backstage/backend-tasks@0.5.8
16
+ - @backstage/backend-common@0.19.5
17
+ - @backstage/plugin-auth-node@0.3.0
18
+ - @backstage/config@1.1.0
19
+ - @backstage/errors@1.2.2
20
+ - @backstage/types@1.1.1
21
+ - @backstage/plugin-permission-node@0.7.14
22
+ - @backstage/backend-plugin-api@0.6.3
23
+ - @backstage/config-loader@1.5.0
24
+ - @backstage/cli-common@0.1.12
25
+ - @backstage/cli-node@0.1.4
26
+
27
+ ## 0.5.3-next.3
28
+
29
+ ### Patch Changes
30
+
31
+ - 154632d8753b: Add support for discovering additional service factories during startup.
32
+ - cb7fc410ed99: The experimental backend feature discovery now only considers default exports from packages. It no longer filters packages to include based on the package role, except that `'cli'` packages are ignored. However, the `"backstage"` field is still required in `package.json`.
33
+ - 3b30b179cb38: Add support for installing features as package imports, for example `backend.add(import('my-plugin'))`.
34
+ - Updated dependencies
35
+ - @backstage/config@1.1.0-next.2
36
+ - @backstage/errors@1.2.2-next.0
37
+ - @backstage/types@1.1.1-next.0
38
+ - @backstage/plugin-permission-node@0.7.14-next.3
39
+ - @backstage/backend-plugin-api@0.6.3-next.3
40
+ - @backstage/backend-common@0.19.5-next.3
41
+ - @backstage/backend-tasks@0.5.8-next.3
42
+ - @backstage/cli-common@0.1.12
43
+ - @backstage/cli-node@0.1.4-next.0
44
+ - @backstage/config-loader@1.5.0-next.3
45
+ - @backstage/plugin-auth-node@0.3.0-next.3
46
+
3
47
  ## 0.5.3-next.2
4
48
 
5
49
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/backend-app-api",
3
- "version": "0.5.3-next.2",
3
+ "version": "0.5.3",
4
4
  "main": "../dist/alpha.cjs.js",
5
5
  "types": "../dist/alpha.d.ts"
6
6
  }
package/dist/alpha.cjs.js CHANGED
@@ -11,7 +11,6 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
11
11
 
12
12
  var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
13
13
 
14
- const LOADED_PACKAGE_ROLES = ["backend-plugin", "backend-plugin-module"];
15
14
  async function findClosestPackageDir(searchDir) {
16
15
  let path$1 = searchDir;
17
16
  for (let i = 0; i < 1e3; i++) {
@@ -52,7 +51,7 @@ class PackageDiscoveryService {
52
51
  return [...includedPackages].filter((name) => !excludedPackagesSet.has(name));
53
52
  }
54
53
  async getBackendFeatures() {
55
- var _a, _b;
54
+ var _a;
56
55
  const packagesConfig = this.config.getOptional("backend.packages");
57
56
  if (!packagesConfig || Object.keys(packagesConfig).length === 0) {
58
57
  return { features: [] };
@@ -69,7 +68,7 @@ class PackageDiscoveryService {
69
68
  const depPkg = require(require.resolve(`${name}/package.json`, {
70
69
  paths: [packageDir]
71
70
  }));
72
- if (!LOADED_PACKAGE_ROLES.includes((_b = (_a = depPkg == null ? void 0 : depPkg.backstage) == null ? void 0 : _a.role) != null ? _b : "")) {
71
+ if (!(depPkg == null ? void 0 : depPkg.backstage) || ((_a = depPkg == null ? void 0 : depPkg.backstage) == null ? void 0 : _a.role) === "cli") {
73
72
  continue;
74
73
  }
75
74
  const exportedModulePaths = [
@@ -84,16 +83,14 @@ class PackageDiscoveryService {
84
83
  } catch {
85
84
  }
86
85
  for (const modulePath of exportedModulePaths) {
87
- const module = require(modulePath);
88
- for (const [exportName, exportValue] of Object.entries(module)) {
89
- if (isBackendFeature(exportValue)) {
90
- this.logger.info(`Detected: ${name}#${exportName}`);
91
- features.push(exportValue);
92
- }
93
- if (isBackendFeatureFactory(exportValue)) {
94
- this.logger.info(`Detected: ${name}#${exportName}`);
95
- features.push(exportValue());
96
- }
86
+ const mod = require(modulePath);
87
+ if (isBackendFeature(mod.default)) {
88
+ this.logger.info(`Detected: ${name}`);
89
+ features.push(mod.default);
90
+ }
91
+ if (isBackendFeatureFactory(mod.default)) {
92
+ this.logger.info(`Detected: ${name}`);
93
+ features.push(mod.default());
97
94
  }
98
95
  }
99
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"alpha.cjs.js","sources":["../src/alpha/featureDiscoveryServiceFactory.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 {\n BackendFeature,\n RootConfigService,\n RootLoggerService,\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport {\n featureDiscoveryServiceRef,\n FeatureDiscoveryService,\n} from '@backstage/backend-plugin-api/alpha';\nimport { resolve as resolvePath, dirname } from 'path';\nimport fs from 'fs-extra';\nimport { BackstagePackageJson } from '@backstage/cli-node';\n\nconst LOADED_PACKAGE_ROLES = ['backend-plugin', 'backend-plugin-module'];\n\n/** @internal */\nasync function findClosestPackageDir(\n searchDir: string,\n): Promise<string | undefined> {\n let path = searchDir;\n\n // Some confidence check to avoid infinite loop\n for (let i = 0; i < 1000; i++) {\n const packagePath = resolvePath(path, 'package.json');\n const exists = await fs.pathExists(packagePath);\n if (exists) {\n return path;\n }\n\n const newPath = dirname(path);\n if (newPath === path) {\n return undefined;\n }\n path = newPath;\n }\n\n throw new Error(\n `Iteration limit reached when searching for root package.json at ${searchDir}`,\n );\n}\n\n/** @internal */\nclass PackageDiscoveryService implements FeatureDiscoveryService {\n constructor(\n private readonly config: RootConfigService,\n private readonly logger: RootLoggerService,\n ) {}\n\n getDependencyNames(path: string) {\n const { dependencies } = require(path) as BackstagePackageJson;\n const packagesConfig = this.config.getOptional('backend.packages');\n\n const dependencyNames = Object.keys(dependencies || {});\n\n if (packagesConfig === 'all') {\n return dependencyNames;\n }\n\n const includedPackagesConfig = this.config.getOptionalStringArray(\n 'backend.packages.include',\n );\n\n const includedPackages = includedPackagesConfig\n ? new Set(includedPackagesConfig)\n : dependencyNames;\n const excludedPackagesSet = new Set(\n this.config.getOptionalStringArray('backend.packages.exclude'),\n );\n\n return [...includedPackages].filter(name => !excludedPackagesSet.has(name));\n }\n\n async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {\n const packagesConfig = this.config.getOptional('backend.packages');\n if (!packagesConfig || Object.keys(packagesConfig).length === 0) {\n return { features: [] };\n }\n\n const packageDir = await findClosestPackageDir(process.argv[1]);\n if (!packageDir) {\n throw new Error('Package discovery failed to find package.json');\n }\n const dependencyNames = this.getDependencyNames(\n resolvePath(packageDir, 'package.json'),\n );\n\n const features: BackendFeature[] = [];\n\n for (const name of dependencyNames) {\n const depPkg = require(require.resolve(`${name}/package.json`, {\n paths: [packageDir],\n })) as BackstagePackageJson;\n if (!LOADED_PACKAGE_ROLES.includes(depPkg?.backstage?.role ?? '')) {\n continue;\n }\n\n const exportedModulePaths = [\n require.resolve(name, {\n paths: [packageDir],\n }),\n ];\n\n // Find modules exported as alpha\n try {\n exportedModulePaths.push(\n require.resolve(`${name}/alpha`, { paths: [packageDir] }),\n );\n } catch {\n /* ignore */\n }\n\n for (const modulePath of exportedModulePaths) {\n const module = require(modulePath);\n for (const [exportName, exportValue] of Object.entries(module)) {\n if (isBackendFeature(exportValue)) {\n this.logger.info(`Detected: ${name}#${exportName}`);\n features.push(exportValue);\n }\n if (isBackendFeatureFactory(exportValue)) {\n this.logger.info(`Detected: ${name}#${exportName}`);\n features.push(exportValue());\n }\n }\n }\n }\n\n return { features };\n }\n}\n\n/** @alpha */\nexport const featureDiscoveryServiceFactory = createServiceFactory({\n service: featureDiscoveryServiceRef,\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.rootLogger,\n },\n factory({ config, logger }) {\n return new PackageDiscoveryService(config, logger);\n },\n});\n\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n typeof value === 'object' &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\nfunction isBackendFeatureFactory(\n value: unknown,\n): value is () => BackendFeature {\n return (\n !!value &&\n typeof value === 'function' &&\n (value as any).$$type === '@backstage/BackendFeatureFactory'\n );\n}\n"],"names":["path","resolvePath","fs","dirname","createServiceFactory","featureDiscoveryServiceRef","coreServices"],"mappings":"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"}
1
+ {"version":3,"file":"alpha.cjs.js","sources":["../src/alpha/featureDiscoveryServiceFactory.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 {\n BackendFeature,\n RootConfigService,\n RootLoggerService,\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport {\n featureDiscoveryServiceRef,\n FeatureDiscoveryService,\n} from '@backstage/backend-plugin-api/alpha';\nimport { resolve as resolvePath, dirname } from 'path';\nimport fs from 'fs-extra';\nimport { BackstagePackageJson } from '@backstage/cli-node';\n\n/** @internal */\nasync function findClosestPackageDir(\n searchDir: string,\n): Promise<string | undefined> {\n let path = searchDir;\n\n // Some confidence check to avoid infinite loop\n for (let i = 0; i < 1000; i++) {\n const packagePath = resolvePath(path, 'package.json');\n const exists = await fs.pathExists(packagePath);\n if (exists) {\n return path;\n }\n\n const newPath = dirname(path);\n if (newPath === path) {\n return undefined;\n }\n path = newPath;\n }\n\n throw new Error(\n `Iteration limit reached when searching for root package.json at ${searchDir}`,\n );\n}\n\n/** @internal */\nclass PackageDiscoveryService implements FeatureDiscoveryService {\n constructor(\n private readonly config: RootConfigService,\n private readonly logger: RootLoggerService,\n ) {}\n\n getDependencyNames(path: string) {\n const { dependencies } = require(path) as BackstagePackageJson;\n const packagesConfig = this.config.getOptional('backend.packages');\n\n const dependencyNames = Object.keys(dependencies || {});\n\n if (packagesConfig === 'all') {\n return dependencyNames;\n }\n\n const includedPackagesConfig = this.config.getOptionalStringArray(\n 'backend.packages.include',\n );\n\n const includedPackages = includedPackagesConfig\n ? new Set(includedPackagesConfig)\n : dependencyNames;\n const excludedPackagesSet = new Set(\n this.config.getOptionalStringArray('backend.packages.exclude'),\n );\n\n return [...includedPackages].filter(name => !excludedPackagesSet.has(name));\n }\n\n async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {\n const packagesConfig = this.config.getOptional('backend.packages');\n if (!packagesConfig || Object.keys(packagesConfig).length === 0) {\n return { features: [] };\n }\n\n const packageDir = await findClosestPackageDir(process.argv[1]);\n if (!packageDir) {\n throw new Error('Package discovery failed to find package.json');\n }\n const dependencyNames = this.getDependencyNames(\n resolvePath(packageDir, 'package.json'),\n );\n\n const features: BackendFeature[] = [];\n\n for (const name of dependencyNames) {\n const depPkg = require(require.resolve(`${name}/package.json`, {\n paths: [packageDir],\n })) as BackstagePackageJson;\n if (!depPkg?.backstage || depPkg?.backstage?.role === 'cli') {\n continue; // Not a backstage package, ignore\n }\n\n const exportedModulePaths = [\n require.resolve(name, {\n paths: [packageDir],\n }),\n ];\n\n // Find modules exported as alpha\n try {\n exportedModulePaths.push(\n require.resolve(`${name}/alpha`, { paths: [packageDir] }),\n );\n } catch {\n /* ignore */\n }\n\n for (const modulePath of exportedModulePaths) {\n const mod = require(modulePath);\n\n if (isBackendFeature(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default);\n }\n if (isBackendFeatureFactory(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default());\n }\n }\n }\n\n return { features };\n }\n}\n\n/** @alpha */\nexport const featureDiscoveryServiceFactory = createServiceFactory({\n service: featureDiscoveryServiceRef,\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.rootLogger,\n },\n factory({ config, logger }) {\n return new PackageDiscoveryService(config, logger);\n },\n});\n\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n typeof value === 'object' &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\nfunction isBackendFeatureFactory(\n value: unknown,\n): value is () => BackendFeature {\n return (\n !!value &&\n typeof value === 'function' &&\n (value as any).$$type === '@backstage/BackendFeatureFactory'\n );\n}\n"],"names":["path","resolvePath","fs","dirname","createServiceFactory","featureDiscoveryServiceRef","coreServices"],"mappings":"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"}
package/dist/index.cjs.js CHANGED
@@ -1306,7 +1306,7 @@ var __privateMethod$2 = (obj, member, method) => {
1306
1306
  __accessCheck$3(obj, member, "access private method");
1307
1307
  return method;
1308
1308
  };
1309
- var _providedFactories, _loadedDefaultFactories, _implementations, _rootServiceImplementations, _resolveFactory, resolveFactory_fn, _checkForMissingDeps, checkForMissingDeps_fn, _checkForCircularDeps, checkForCircularDeps_fn;
1309
+ var _providedFactories, _loadedDefaultFactories, _implementations, _rootServiceImplementations, _addedFactoryIds, _instantiatedFactories, _resolveFactory, resolveFactory_fn, _checkForMissingDeps, checkForMissingDeps_fn;
1310
1310
  function toInternalServiceFactory(factory) {
1311
1311
  const f = factory;
1312
1312
  if (f.$$type !== "@backstage/BackendFeature") {
@@ -1328,11 +1328,12 @@ const _ServiceRegistry = class _ServiceRegistry {
1328
1328
  constructor(factories) {
1329
1329
  __privateAdd$3(this, _resolveFactory);
1330
1330
  __privateAdd$3(this, _checkForMissingDeps);
1331
- __privateAdd$3(this, _checkForCircularDeps);
1332
1331
  __privateAdd$3(this, _providedFactories, void 0);
1333
1332
  __privateAdd$3(this, _loadedDefaultFactories, void 0);
1334
1333
  __privateAdd$3(this, _implementations, void 0);
1335
1334
  __privateAdd$3(this, _rootServiceImplementations, /* @__PURE__ */ new Map());
1335
+ __privateAdd$3(this, _addedFactoryIds, /* @__PURE__ */ new Set());
1336
+ __privateAdd$3(this, _instantiatedFactories, /* @__PURE__ */ new Set());
1336
1337
  __privateSet$3(this, _providedFactories, new Map(
1337
1338
  factories.map((sf) => [sf.service.id, toInternalServiceFactory(sf)])
1338
1339
  ));
@@ -1340,16 +1341,53 @@ const _ServiceRegistry = class _ServiceRegistry {
1340
1341
  __privateSet$3(this, _implementations, /* @__PURE__ */ new Map());
1341
1342
  }
1342
1343
  static create(factories) {
1343
- var _a;
1344
1344
  const registry = new _ServiceRegistry(factories);
1345
- __privateMethod$2(_a = registry, _checkForCircularDeps, checkForCircularDeps_fn).call(_a);
1345
+ registry.checkForCircularDeps();
1346
1346
  return registry;
1347
1347
  }
1348
+ checkForCircularDeps() {
1349
+ const graph = DependencyGraph.fromIterable(
1350
+ Array.from(__privateGet$3(this, _providedFactories)).map(
1351
+ ([serviceId, serviceFactory]) => ({
1352
+ value: serviceId,
1353
+ provides: [serviceId],
1354
+ consumes: Object.values(serviceFactory.deps).map((d) => d.id)
1355
+ })
1356
+ )
1357
+ );
1358
+ const circularDependencies = Array.from(graph.detectCircularDependencies());
1359
+ if (circularDependencies.length) {
1360
+ const cycles = circularDependencies.map((c) => c.map((id) => `'${id}'`).join(" -> ")).join("\n ");
1361
+ throw new errors.ConflictError(`Circular dependencies detected:
1362
+ ${cycles}`);
1363
+ }
1364
+ }
1365
+ add(factory) {
1366
+ const factoryId = factory.service.id;
1367
+ if (factoryId === backendPluginApi.coreServices.pluginMetadata.id) {
1368
+ throw new Error(
1369
+ `The ${backendPluginApi.coreServices.pluginMetadata.id} service cannot be overridden`
1370
+ );
1371
+ }
1372
+ if (__privateGet$3(this, _addedFactoryIds).has(factoryId)) {
1373
+ throw new Error(
1374
+ `Duplicate service implementations provided for ${factoryId}`
1375
+ );
1376
+ }
1377
+ if (__privateGet$3(this, _instantiatedFactories).has(factoryId)) {
1378
+ throw new Error(
1379
+ `Unable to set service factory with id ${factoryId}, service has already been instantiated`
1380
+ );
1381
+ }
1382
+ __privateGet$3(this, _addedFactoryIds).add(factoryId);
1383
+ __privateGet$3(this, _providedFactories).set(factoryId, toInternalServiceFactory(factory));
1384
+ }
1348
1385
  getServiceRefs() {
1349
1386
  return Array.from(__privateGet$3(this, _providedFactories).values()).map((f) => f.service);
1350
1387
  }
1351
1388
  get(ref, pluginId) {
1352
1389
  var _a;
1390
+ __privateGet$3(this, _instantiatedFactories).add(ref.id);
1353
1391
  return (_a = __privateMethod$2(this, _resolveFactory, resolveFactory_fn).call(this, ref, pluginId)) == null ? void 0 : _a.then((factory) => {
1354
1392
  if (factory.service.scope === "root") {
1355
1393
  let existing = __privateGet$3(this, _rootServiceImplementations).get(factory);
@@ -1425,6 +1463,8 @@ _providedFactories = new WeakMap();
1425
1463
  _loadedDefaultFactories = new WeakMap();
1426
1464
  _implementations = new WeakMap();
1427
1465
  _rootServiceImplementations = new WeakMap();
1466
+ _addedFactoryIds = new WeakMap();
1467
+ _instantiatedFactories = new WeakMap();
1428
1468
  _resolveFactory = new WeakSet();
1429
1469
  resolveFactory_fn = function(ref, pluginId) {
1430
1470
  if (ref.id === backendPluginApi.coreServices.pluginMetadata.id) {
@@ -1473,24 +1513,6 @@ checkForMissingDeps_fn = function(factory, pluginId) {
1473
1513
  );
1474
1514
  }
1475
1515
  };
1476
- _checkForCircularDeps = new WeakSet();
1477
- checkForCircularDeps_fn = function() {
1478
- const graph = DependencyGraph.fromIterable(
1479
- Array.from(__privateGet$3(this, _providedFactories)).map(
1480
- ([serviceId, serviceFactory]) => ({
1481
- value: serviceId,
1482
- provides: [serviceId],
1483
- consumes: Object.values(serviceFactory.deps).map((d) => d.id)
1484
- })
1485
- )
1486
- );
1487
- const circularDependencies = Array.from(graph.detectCircularDependencies());
1488
- if (circularDependencies.length) {
1489
- const cycles = circularDependencies.map((c) => c.map((id) => `'${id}'`).join(" -> ")).join("\n ");
1490
- throw new errors.ConflictError(`Circular dependencies detected:
1491
- ${cycles}`);
1492
- }
1493
- };
1494
1516
  let ServiceRegistry = _ServiceRegistry;
1495
1517
 
1496
1518
  var __accessCheck$2 = (obj, member, msg) => {
@@ -1515,7 +1537,7 @@ var __privateMethod$1 = (obj, member, method) => {
1515
1537
  __accessCheck$2(obj, member, "access private method");
1516
1538
  return method;
1517
1539
  };
1518
- var _startPromise, _features, _extensionPoints, _serviceHolder, _providedServiceFactories, _defaultApiFactories, _getInitDeps, getInitDeps_fn, _addFeature, addFeature_fn, _doStart, doStart_fn, _getRootLifecycleImpl, getRootLifecycleImpl_fn, _getPluginLifecycleImpl, getPluginLifecycleImpl_fn;
1540
+ var _startPromise, _features, _extensionPoints, _serviceRegistry, _registeredFeatures, _getInitDeps, getInitDeps_fn, _addFeature, addFeature_fn, _doStart, doStart_fn, _getRootLifecycleImpl, getRootLifecycleImpl_fn, _getPluginLifecycleImpl, getPluginLifecycleImpl_fn;
1519
1541
  class BackendInitializer {
1520
1542
  constructor(defaultApiFactories) {
1521
1543
  __privateAdd$2(this, _getInitDeps);
@@ -1527,16 +1549,15 @@ class BackendInitializer {
1527
1549
  __privateAdd$2(this, _startPromise, void 0);
1528
1550
  __privateAdd$2(this, _features, new Array());
1529
1551
  __privateAdd$2(this, _extensionPoints, /* @__PURE__ */ new Map());
1530
- __privateAdd$2(this, _serviceHolder, void 0);
1531
- __privateAdd$2(this, _providedServiceFactories, new Array());
1532
- __privateAdd$2(this, _defaultApiFactories, void 0);
1533
- __privateSet$2(this, _defaultApiFactories, defaultApiFactories);
1552
+ __privateAdd$2(this, _serviceRegistry, void 0);
1553
+ __privateAdd$2(this, _registeredFeatures, new Array());
1554
+ __privateSet$2(this, _serviceRegistry, ServiceRegistry.create([...defaultApiFactories]));
1534
1555
  }
1535
1556
  add(feature) {
1536
1557
  if (__privateGet$2(this, _startPromise)) {
1537
1558
  throw new Error("feature can not be added after the backend has started");
1538
1559
  }
1539
- __privateMethod$1(this, _addFeature, addFeature_fn).call(this, feature);
1560
+ __privateGet$2(this, _registeredFeatures).push(Promise.resolve(feature));
1540
1561
  }
1541
1562
  async start() {
1542
1563
  if (__privateGet$2(this, _startPromise)) {
@@ -1575,9 +1596,8 @@ class BackendInitializer {
1575
1596
  _startPromise = new WeakMap();
1576
1597
  _features = new WeakMap();
1577
1598
  _extensionPoints = new WeakMap();
1578
- _serviceHolder = new WeakMap();
1579
- _providedServiceFactories = new WeakMap();
1580
- _defaultApiFactories = new WeakMap();
1599
+ _serviceRegistry = new WeakMap();
1600
+ _registeredFeatures = new WeakMap();
1581
1601
  _getInitDeps = new WeakSet();
1582
1602
  getInitDeps_fn = async function(deps, pluginId) {
1583
1603
  const result = /* @__PURE__ */ new Map();
@@ -1592,7 +1612,7 @@ getInitDeps_fn = async function(deps, pluginId) {
1592
1612
  }
1593
1613
  result.set(name, ep.impl);
1594
1614
  } else {
1595
- const impl = await __privateGet$2(this, _serviceHolder).get(
1615
+ const impl = await __privateGet$2(this, _serviceRegistry).get(
1596
1616
  ref,
1597
1617
  pluginId
1598
1618
  );
@@ -1619,19 +1639,7 @@ addFeature_fn = function(feature) {
1619
1639
  );
1620
1640
  }
1621
1641
  if (isServiceFactory(feature)) {
1622
- if (feature.service.id === backendPluginApi.coreServices.pluginMetadata.id) {
1623
- throw new Error(
1624
- `The ${backendPluginApi.coreServices.pluginMetadata.id} service cannot be overridden`
1625
- );
1626
- }
1627
- if (__privateGet$2(this, _providedServiceFactories).find(
1628
- (sf) => sf.service.id === feature.service.id
1629
- )) {
1630
- throw new Error(
1631
- `Duplicate service implementations provided for ${feature.service.id}`
1632
- );
1633
- }
1634
- __privateGet$2(this, _providedServiceFactories).push(feature);
1642
+ __privateGet$2(this, _serviceRegistry).add(feature);
1635
1643
  } else if (isInternalBackendFeature(feature)) {
1636
1644
  if (feature.version !== "v1") {
1637
1645
  throw new Error(
@@ -1647,11 +1655,11 @@ addFeature_fn = function(feature) {
1647
1655
  };
1648
1656
  _doStart = new WeakSet();
1649
1657
  doStart_fn = async function() {
1650
- __privateSet$2(this, _serviceHolder, ServiceRegistry.create([
1651
- ...__privateGet$2(this, _defaultApiFactories),
1652
- ...__privateGet$2(this, _providedServiceFactories)
1653
- ]));
1654
- const featureDiscovery = await __privateGet$2(this, _serviceHolder).get(
1658
+ __privateGet$2(this, _serviceRegistry).checkForCircularDeps();
1659
+ for (const feature of __privateGet$2(this, _registeredFeatures)) {
1660
+ __privateMethod$1(this, _addFeature, addFeature_fn).call(this, await feature);
1661
+ }
1662
+ const featureDiscovery = await __privateGet$2(this, _serviceRegistry).get(
1655
1663
  alpha.featureDiscoveryServiceRef,
1656
1664
  "root"
1657
1665
  );
@@ -1660,10 +1668,11 @@ doStart_fn = async function() {
1660
1668
  for (const feature of features) {
1661
1669
  __privateMethod$1(this, _addFeature, addFeature_fn).call(this, feature);
1662
1670
  }
1671
+ __privateGet$2(this, _serviceRegistry).checkForCircularDeps();
1663
1672
  }
1664
- for (const ref of __privateGet$2(this, _serviceHolder).getServiceRefs()) {
1673
+ for (const ref of __privateGet$2(this, _serviceRegistry).getServiceRefs()) {
1665
1674
  if (ref.scope === "root") {
1666
- await __privateGet$2(this, _serviceHolder).get(ref, "root");
1675
+ await __privateGet$2(this, _serviceRegistry).get(ref, "root");
1667
1676
  }
1668
1677
  }
1669
1678
  const pluginInits = /* @__PURE__ */ new Map();
@@ -1765,7 +1774,7 @@ doStart_fn = async function() {
1765
1774
  const lifecycleService = await __privateMethod$1(this, _getRootLifecycleImpl, getRootLifecycleImpl_fn).call(this);
1766
1775
  await lifecycleService.startup();
1767
1776
  if (process.env.NODE_ENV !== "test") {
1768
- const rootLogger = await __privateGet$2(this, _serviceHolder).get(
1777
+ const rootLogger = await __privateGet$2(this, _serviceRegistry).get(
1769
1778
  backendPluginApi.coreServices.rootLogger,
1770
1779
  "root"
1771
1780
  );
@@ -1781,7 +1790,7 @@ doStart_fn = async function() {
1781
1790
  };
1782
1791
  _getRootLifecycleImpl = new WeakSet();
1783
1792
  getRootLifecycleImpl_fn = async function() {
1784
- const lifecycleService = await __privateGet$2(this, _serviceHolder).get(
1793
+ const lifecycleService = await __privateGet$2(this, _serviceRegistry).get(
1785
1794
  backendPluginApi.coreServices.rootLifecycle,
1786
1795
  "root"
1787
1796
  );
@@ -1792,7 +1801,7 @@ getRootLifecycleImpl_fn = async function() {
1792
1801
  };
1793
1802
  _getPluginLifecycleImpl = new WeakSet();
1794
1803
  getPluginLifecycleImpl_fn = async function(pluginId) {
1795
- const lifecycleService = await __privateGet$2(this, _serviceHolder).get(
1804
+ const lifecycleService = await __privateGet$2(this, _serviceRegistry).get(
1796
1805
  backendPluginApi.coreServices.lifecycle,
1797
1806
  pluginId
1798
1807
  );
@@ -1833,7 +1842,11 @@ class BackstageBackend {
1833
1842
  __privateSet$1(this, _initializer, new BackendInitializer(defaultServiceFactories));
1834
1843
  }
1835
1844
  add(feature) {
1836
- __privateGet$1(this, _initializer).add(typeof feature === "function" ? feature() : feature);
1845
+ if (isPromise(feature)) {
1846
+ __privateGet$1(this, _initializer).add(feature.then((f) => unwrapFeature(f.default)));
1847
+ } else {
1848
+ __privateGet$1(this, _initializer).add(unwrapFeature(feature));
1849
+ }
1837
1850
  }
1838
1851
  async start() {
1839
1852
  await __privateGet$1(this, _initializer).start();
@@ -1843,6 +1856,12 @@ class BackstageBackend {
1843
1856
  }
1844
1857
  }
1845
1858
  _initializer = new WeakMap();
1859
+ function isPromise(value) {
1860
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
1861
+ }
1862
+ function unwrapFeature(feature) {
1863
+ return typeof feature === "function" ? feature() : feature;
1864
+ }
1846
1865
 
1847
1866
  function createSpecializedBackend(options) {
1848
1867
  const services = options.defaultServiceFactories.map(