@backstage/backend-app-api 0.8.1-next.0 → 0.8.1-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/alpha/package.json +1 -1
- package/config.d.ts +1 -0
- package/dist/alpha.d.ts +1 -1
- package/dist/index.cjs.js +208 -108
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +18 -18
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @backstage/backend-app-api
|
|
2
2
|
|
|
3
|
+
## 0.8.1-next.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8b13183: Added support for the latest version of `BackendFeature`s from `@backstage/backend-plugin-api`, including feature loaders.
|
|
8
|
+
- 93095ee: Make sure node-fetch is version 2.7.0 or greater
|
|
9
|
+
- 7c5f3b0: Update the `ServiceRegister` implementation to enable registering multiple service implementations for a given service ref.
|
|
10
|
+
- 80a0737: Added configuration for the `packages` options to config schema
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @backstage/backend-plugin-api@0.8.0-next.2
|
|
13
|
+
- @backstage/backend-common@0.23.4-next.2
|
|
14
|
+
- @backstage/config-loader@1.9.0-next.2
|
|
15
|
+
- @backstage/plugin-auth-node@0.5.0-next.2
|
|
16
|
+
- @backstage/plugin-permission-node@0.8.1-next.2
|
|
17
|
+
- @backstage/backend-tasks@0.5.28-next.2
|
|
18
|
+
- @backstage/cli-node@0.2.7
|
|
19
|
+
- @backstage/cli-common@0.1.14
|
|
20
|
+
- @backstage/config@1.2.0
|
|
21
|
+
- @backstage/errors@1.2.4
|
|
22
|
+
- @backstage/types@1.1.1
|
|
23
|
+
|
|
24
|
+
## 0.8.1-next.1
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies
|
|
29
|
+
- @backstage/config-loader@1.9.0-next.1
|
|
30
|
+
- @backstage/plugin-permission-node@0.8.1-next.1
|
|
31
|
+
- @backstage/backend-plugin-api@0.7.1-next.1
|
|
32
|
+
- @backstage/backend-common@0.23.4-next.1
|
|
33
|
+
- @backstage/backend-tasks@0.5.28-next.1
|
|
34
|
+
- @backstage/cli-common@0.1.14
|
|
35
|
+
- @backstage/cli-node@0.2.7
|
|
36
|
+
- @backstage/config@1.2.0
|
|
37
|
+
- @backstage/errors@1.2.4
|
|
38
|
+
- @backstage/types@1.1.1
|
|
39
|
+
- @backstage/plugin-auth-node@0.4.18-next.1
|
|
40
|
+
|
|
3
41
|
## 0.8.1-next.0
|
|
4
42
|
|
|
5
43
|
### Patch Changes
|
package/alpha/package.json
CHANGED
package/config.d.ts
CHANGED
package/dist/alpha.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
|
2
2
|
import { FeatureDiscoveryService } from '@backstage/backend-plugin-api/alpha';
|
|
3
3
|
|
|
4
4
|
/** @alpha */
|
|
5
|
-
declare const featureDiscoveryServiceFactory: _backstage_backend_plugin_api.ServiceFactoryCompat<FeatureDiscoveryService, "root", undefined>;
|
|
5
|
+
declare const featureDiscoveryServiceFactory: _backstage_backend_plugin_api.ServiceFactoryCompat<FeatureDiscoveryService, "root", "singleton", undefined>;
|
|
6
6
|
|
|
7
7
|
export { featureDiscoveryServiceFactory };
|
package/dist/index.cjs.js
CHANGED
|
@@ -1243,7 +1243,19 @@ function createPluginMetadataServiceFactory(pluginId) {
|
|
|
1243
1243
|
}
|
|
1244
1244
|
class ServiceRegistry {
|
|
1245
1245
|
static create(factories) {
|
|
1246
|
-
const
|
|
1246
|
+
const factoryMap = /* @__PURE__ */ new Map();
|
|
1247
|
+
for (const factory of factories) {
|
|
1248
|
+
if (factory.service.multiton) {
|
|
1249
|
+
const existing = factoryMap.get(factory.service.id) ?? [];
|
|
1250
|
+
factoryMap.set(
|
|
1251
|
+
factory.service.id,
|
|
1252
|
+
existing.concat(toInternalServiceFactory(factory))
|
|
1253
|
+
);
|
|
1254
|
+
} else {
|
|
1255
|
+
factoryMap.set(factory.service.id, [toInternalServiceFactory(factory)]);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
const registry = new ServiceRegistry(factoryMap);
|
|
1247
1259
|
registry.checkForCircularDeps();
|
|
1248
1260
|
return registry;
|
|
1249
1261
|
}
|
|
@@ -1254,17 +1266,15 @@ class ServiceRegistry {
|
|
|
1254
1266
|
#addedFactoryIds = /* @__PURE__ */ new Set();
|
|
1255
1267
|
#instantiatedFactories = /* @__PURE__ */ new Set();
|
|
1256
1268
|
constructor(factories) {
|
|
1257
|
-
this.#providedFactories =
|
|
1258
|
-
factories.map((sf) => [sf.service.id, toInternalServiceFactory(sf)])
|
|
1259
|
-
);
|
|
1269
|
+
this.#providedFactories = factories;
|
|
1260
1270
|
this.#loadedDefaultFactories = /* @__PURE__ */ new Map();
|
|
1261
1271
|
this.#implementations = /* @__PURE__ */ new Map();
|
|
1262
1272
|
}
|
|
1263
1273
|
#resolveFactory(ref, pluginId) {
|
|
1264
1274
|
if (ref.id === backendPluginApi.coreServices.pluginMetadata.id) {
|
|
1265
|
-
return Promise.resolve(
|
|
1275
|
+
return Promise.resolve([
|
|
1266
1276
|
toInternalServiceFactory(createPluginMetadataServiceFactory(pluginId))
|
|
1267
|
-
);
|
|
1277
|
+
]);
|
|
1268
1278
|
}
|
|
1269
1279
|
let resolvedFactory = this.#providedFactories.get(ref.id);
|
|
1270
1280
|
const { __defaultFactory: defaultFactory } = ref;
|
|
@@ -1279,13 +1289,16 @@ class ServiceRegistry {
|
|
|
1279
1289
|
);
|
|
1280
1290
|
this.#loadedDefaultFactories.set(defaultFactory, loadedFactory);
|
|
1281
1291
|
}
|
|
1282
|
-
resolvedFactory = loadedFactory.
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1292
|
+
resolvedFactory = loadedFactory.then(
|
|
1293
|
+
(factory) => [factory],
|
|
1294
|
+
(error) => {
|
|
1295
|
+
throw new Error(
|
|
1296
|
+
`Failed to instantiate service '${ref.id}' because the default factory loader threw an error, ${errors.stringifyError(
|
|
1297
|
+
error
|
|
1298
|
+
)}`
|
|
1299
|
+
);
|
|
1300
|
+
}
|
|
1301
|
+
);
|
|
1289
1302
|
}
|
|
1290
1303
|
return Promise.resolve(resolvedFactory);
|
|
1291
1304
|
}
|
|
@@ -1297,6 +1310,9 @@ class ServiceRegistry {
|
|
|
1297
1310
|
if (this.#providedFactories.get(ref.id)) {
|
|
1298
1311
|
return false;
|
|
1299
1312
|
}
|
|
1313
|
+
if (ref.multiton) {
|
|
1314
|
+
return false;
|
|
1315
|
+
}
|
|
1300
1316
|
return !ref.__defaultFactory;
|
|
1301
1317
|
});
|
|
1302
1318
|
if (missingDeps.length) {
|
|
@@ -1308,13 +1324,13 @@ class ServiceRegistry {
|
|
|
1308
1324
|
}
|
|
1309
1325
|
checkForCircularDeps() {
|
|
1310
1326
|
const graph = DependencyGraph.fromIterable(
|
|
1311
|
-
Array.from(this.#providedFactories).map(
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
)
|
|
1327
|
+
Array.from(this.#providedFactories).map(([serviceId, factories]) => ({
|
|
1328
|
+
value: serviceId,
|
|
1329
|
+
provides: [serviceId],
|
|
1330
|
+
consumes: factories.flatMap(
|
|
1331
|
+
(factory) => Object.values(factory.deps).map((d) => d.id)
|
|
1332
|
+
)
|
|
1333
|
+
}))
|
|
1318
1334
|
);
|
|
1319
1335
|
const circularDependencies = Array.from(graph.detectCircularDependencies());
|
|
1320
1336
|
if (circularDependencies.length) {
|
|
@@ -1330,21 +1346,28 @@ class ServiceRegistry {
|
|
|
1330
1346
|
`The ${backendPluginApi.coreServices.pluginMetadata.id} service cannot be overridden`
|
|
1331
1347
|
);
|
|
1332
1348
|
}
|
|
1333
|
-
if (this.#addedFactoryIds.has(factoryId)) {
|
|
1334
|
-
throw new Error(
|
|
1335
|
-
`Duplicate service implementations provided for ${factoryId}`
|
|
1336
|
-
);
|
|
1337
|
-
}
|
|
1338
1349
|
if (this.#instantiatedFactories.has(factoryId)) {
|
|
1339
1350
|
throw new Error(
|
|
1340
1351
|
`Unable to set service factory with id ${factoryId}, service has already been instantiated`
|
|
1341
1352
|
);
|
|
1342
1353
|
}
|
|
1343
|
-
|
|
1344
|
-
|
|
1354
|
+
if (factory.service.multiton) {
|
|
1355
|
+
const newFactories = (this.#providedFactories.get(factoryId) ?? []).concat(toInternalServiceFactory(factory));
|
|
1356
|
+
this.#providedFactories.set(factoryId, newFactories);
|
|
1357
|
+
} else {
|
|
1358
|
+
if (this.#addedFactoryIds.has(factoryId)) {
|
|
1359
|
+
throw new Error(
|
|
1360
|
+
`Duplicate service implementations provided for ${factoryId}`
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1363
|
+
this.#addedFactoryIds.add(factoryId);
|
|
1364
|
+
this.#providedFactories.set(factoryId, [
|
|
1365
|
+
toInternalServiceFactory(factory)
|
|
1366
|
+
]);
|
|
1367
|
+
}
|
|
1345
1368
|
}
|
|
1346
1369
|
async initializeEagerServicesWithScope(scope, pluginId = "root") {
|
|
1347
|
-
for (const factory of this.#providedFactories.values()) {
|
|
1370
|
+
for (const [factory] of this.#providedFactories.values()) {
|
|
1348
1371
|
if (factory.service.scope === scope) {
|
|
1349
1372
|
if (scope === "root" && factory.initialization !== "lazy") {
|
|
1350
1373
|
await this.get(factory.service, pluginId);
|
|
@@ -1356,72 +1379,80 @@ class ServiceRegistry {
|
|
|
1356
1379
|
}
|
|
1357
1380
|
get(ref, pluginId) {
|
|
1358
1381
|
this.#instantiatedFactories.add(ref.id);
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1382
|
+
const resolvedFactory = this.#resolveFactory(ref, pluginId);
|
|
1383
|
+
if (!resolvedFactory) {
|
|
1384
|
+
return ref.multiton ? Promise.resolve([]) : void 0;
|
|
1385
|
+
}
|
|
1386
|
+
return resolvedFactory.then((factories) => {
|
|
1387
|
+
return Promise.all(
|
|
1388
|
+
factories.map((factory) => {
|
|
1389
|
+
if (factory.service.scope === "root") {
|
|
1390
|
+
let existing = this.#rootServiceImplementations.get(factory);
|
|
1391
|
+
if (!existing) {
|
|
1392
|
+
this.#checkForMissingDeps(factory, pluginId);
|
|
1393
|
+
const rootDeps = new Array();
|
|
1394
|
+
for (const [name, serviceRef] of Object.entries(factory.deps)) {
|
|
1395
|
+
if (serviceRef.scope !== "root") {
|
|
1396
|
+
throw new Error(
|
|
1397
|
+
`Failed to instantiate 'root' scoped service '${ref.id}' because it depends on '${serviceRef.scope}' scoped service '${serviceRef.id}'.`
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
const target = this.get(serviceRef, pluginId);
|
|
1401
|
+
rootDeps.push(target.then((impl) => [name, impl]));
|
|
1402
|
+
}
|
|
1403
|
+
existing = Promise.all(rootDeps).then(
|
|
1404
|
+
(entries) => factory.factory(Object.fromEntries(entries), void 0)
|
|
1369
1405
|
);
|
|
1406
|
+
this.#rootServiceImplementations.set(factory, existing);
|
|
1370
1407
|
}
|
|
1371
|
-
|
|
1372
|
-
rootDeps.push(target.then((impl) => [name, impl]));
|
|
1408
|
+
return existing;
|
|
1373
1409
|
}
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1410
|
+
let implementation = this.#implementations.get(factory);
|
|
1411
|
+
if (!implementation) {
|
|
1412
|
+
this.#checkForMissingDeps(factory, pluginId);
|
|
1413
|
+
const rootDeps = new Array();
|
|
1414
|
+
for (const [name, serviceRef] of Object.entries(factory.deps)) {
|
|
1415
|
+
if (serviceRef.scope === "root") {
|
|
1416
|
+
const target = this.get(serviceRef, pluginId);
|
|
1417
|
+
rootDeps.push(target.then((impl) => [name, impl]));
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
implementation = {
|
|
1421
|
+
context: Promise.all(rootDeps).then(
|
|
1422
|
+
(entries) => factory.createRootContext?.(Object.fromEntries(entries))
|
|
1423
|
+
).catch((error) => {
|
|
1424
|
+
const cause = errors.stringifyError(error);
|
|
1425
|
+
throw new Error(
|
|
1426
|
+
`Failed to instantiate service '${ref.id}' because createRootContext threw an error, ${cause}`
|
|
1427
|
+
);
|
|
1428
|
+
}),
|
|
1429
|
+
byPlugin: /* @__PURE__ */ new Map()
|
|
1430
|
+
};
|
|
1431
|
+
this.#implementations.set(factory, implementation);
|
|
1389
1432
|
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
(
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
(entries) => factory.factory(Object.fromEntries(entries), context)
|
|
1414
|
-
)
|
|
1415
|
-
).catch((error) => {
|
|
1416
|
-
const cause = errors.stringifyError(error);
|
|
1417
|
-
throw new Error(
|
|
1418
|
-
`Failed to instantiate service '${ref.id}' for '${pluginId}' because the factory function threw an error, ${cause}`
|
|
1419
|
-
);
|
|
1420
|
-
});
|
|
1421
|
-
implementation.byPlugin.set(pluginId, result);
|
|
1422
|
-
}
|
|
1423
|
-
return result;
|
|
1424
|
-
});
|
|
1433
|
+
let result = implementation.byPlugin.get(pluginId);
|
|
1434
|
+
if (!result) {
|
|
1435
|
+
const allDeps = new Array();
|
|
1436
|
+
for (const [name, serviceRef] of Object.entries(factory.deps)) {
|
|
1437
|
+
const target = this.get(serviceRef, pluginId);
|
|
1438
|
+
allDeps.push(target.then((impl) => [name, impl]));
|
|
1439
|
+
}
|
|
1440
|
+
result = implementation.context.then(
|
|
1441
|
+
(context) => Promise.all(allDeps).then(
|
|
1442
|
+
(entries) => factory.factory(Object.fromEntries(entries), context)
|
|
1443
|
+
)
|
|
1444
|
+
).catch((error) => {
|
|
1445
|
+
const cause = errors.stringifyError(error);
|
|
1446
|
+
throw new Error(
|
|
1447
|
+
`Failed to instantiate service '${ref.id}' for '${pluginId}' because the factory function threw an error, ${cause}`
|
|
1448
|
+
);
|
|
1449
|
+
});
|
|
1450
|
+
implementation.byPlugin.set(pluginId, result);
|
|
1451
|
+
}
|
|
1452
|
+
return result;
|
|
1453
|
+
})
|
|
1454
|
+
);
|
|
1455
|
+
}).then((results) => ref.multiton ? results : results[0]);
|
|
1425
1456
|
}
|
|
1426
1457
|
}
|
|
1427
1458
|
|
|
@@ -1473,10 +1504,11 @@ function createInitializationLogger(pluginIds, rootLogger) {
|
|
|
1473
1504
|
|
|
1474
1505
|
class BackendInitializer {
|
|
1475
1506
|
#startPromise;
|
|
1476
|
-
#
|
|
1507
|
+
#registrations = new Array();
|
|
1477
1508
|
#extensionPoints = /* @__PURE__ */ new Map();
|
|
1478
1509
|
#serviceRegistry;
|
|
1479
1510
|
#registeredFeatures = new Array();
|
|
1511
|
+
#registeredFeatureLoaders = new Array();
|
|
1480
1512
|
constructor(defaultApiFactories) {
|
|
1481
1513
|
this.#serviceRegistry = ServiceRegistry.create([...defaultApiFactories]);
|
|
1482
1514
|
}
|
|
@@ -1519,20 +1551,12 @@ class BackendInitializer {
|
|
|
1519
1551
|
this.#registeredFeatures.push(Promise.resolve(feature));
|
|
1520
1552
|
}
|
|
1521
1553
|
#addFeature(feature) {
|
|
1522
|
-
if (feature.$$type !== "@backstage/BackendFeature") {
|
|
1523
|
-
throw new Error(
|
|
1524
|
-
`Failed to add feature, invalid type '${feature.$$type}'`
|
|
1525
|
-
);
|
|
1526
|
-
}
|
|
1527
1554
|
if (isServiceFactory(feature)) {
|
|
1528
1555
|
this.#serviceRegistry.add(feature);
|
|
1529
|
-
} else if (
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
);
|
|
1534
|
-
}
|
|
1535
|
-
this.#features.push(feature);
|
|
1556
|
+
} else if (isBackendFeatureLoader(feature)) {
|
|
1557
|
+
this.#registeredFeatureLoaders.push(feature);
|
|
1558
|
+
} else if (isBackendRegistrations(feature)) {
|
|
1559
|
+
this.#registrations.push(feature);
|
|
1536
1560
|
} else {
|
|
1537
1561
|
throw new Error(
|
|
1538
1562
|
`Failed to add feature, invalid feature ${JSON.stringify(feature)}`
|
|
@@ -1577,10 +1601,11 @@ class BackendInitializer {
|
|
|
1577
1601
|
}
|
|
1578
1602
|
this.#serviceRegistry.checkForCircularDeps();
|
|
1579
1603
|
}
|
|
1604
|
+
await this.#applyBackendFeatureLoaders(this.#registeredFeatureLoaders);
|
|
1580
1605
|
await this.#serviceRegistry.initializeEagerServicesWithScope("root");
|
|
1581
1606
|
const pluginInits = /* @__PURE__ */ new Map();
|
|
1582
1607
|
const moduleInits = /* @__PURE__ */ new Map();
|
|
1583
|
-
for (const feature of this.#
|
|
1608
|
+
for (const feature of this.#registrations) {
|
|
1584
1609
|
for (const r of feature.getRegistrations()) {
|
|
1585
1610
|
const provides = /* @__PURE__ */ new Set();
|
|
1586
1611
|
if (r.type === "plugin" || r.type === "module") {
|
|
@@ -1606,7 +1631,7 @@ class BackendInitializer {
|
|
|
1606
1631
|
consumes: new Set(Object.values(r.init.deps)),
|
|
1607
1632
|
init: r.init
|
|
1608
1633
|
});
|
|
1609
|
-
} else {
|
|
1634
|
+
} else if (r.type === "module") {
|
|
1610
1635
|
let modules = moduleInits.get(r.pluginId);
|
|
1611
1636
|
if (!modules) {
|
|
1612
1637
|
modules = /* @__PURE__ */ new Map();
|
|
@@ -1622,6 +1647,8 @@ class BackendInitializer {
|
|
|
1622
1647
|
consumes: new Set(Object.values(r.init.deps)),
|
|
1623
1648
|
init: r.init
|
|
1624
1649
|
});
|
|
1650
|
+
} else {
|
|
1651
|
+
throw new Error(`Invalid registration type '${r.type}'`);
|
|
1625
1652
|
}
|
|
1626
1653
|
}
|
|
1627
1654
|
}
|
|
@@ -1738,12 +1765,85 @@ class BackendInitializer {
|
|
|
1738
1765
|
}
|
|
1739
1766
|
throw new Error("Unexpected plugin lifecycle service implementation");
|
|
1740
1767
|
}
|
|
1768
|
+
async #applyBackendFeatureLoaders(loaders) {
|
|
1769
|
+
for (const loader of loaders) {
|
|
1770
|
+
const deps = /* @__PURE__ */ new Map();
|
|
1771
|
+
const missingRefs = /* @__PURE__ */ new Set();
|
|
1772
|
+
for (const [name, ref] of Object.entries(loader.deps ?? {})) {
|
|
1773
|
+
if (ref.scope !== "root") {
|
|
1774
|
+
throw new Error(
|
|
1775
|
+
`Feature loaders can only depend on root scoped services, but '${name}' is scoped to '${ref.scope}'. Offending loader is ${loader.description}`
|
|
1776
|
+
);
|
|
1777
|
+
}
|
|
1778
|
+
const impl = await this.#serviceRegistry.get(
|
|
1779
|
+
ref,
|
|
1780
|
+
"root"
|
|
1781
|
+
);
|
|
1782
|
+
if (impl) {
|
|
1783
|
+
deps.set(name, impl);
|
|
1784
|
+
} else {
|
|
1785
|
+
missingRefs.add(ref);
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
if (missingRefs.size > 0) {
|
|
1789
|
+
const missing = Array.from(missingRefs).join(", ");
|
|
1790
|
+
throw new Error(
|
|
1791
|
+
`No service available for the following ref(s): ${missing}, depended on by feature loader ${loader.description}`
|
|
1792
|
+
);
|
|
1793
|
+
}
|
|
1794
|
+
const result = await loader.loader(Object.fromEntries(deps)).catch((error) => {
|
|
1795
|
+
throw new errors.ForwardedError(
|
|
1796
|
+
`Feature loader ${loader.description} failed`,
|
|
1797
|
+
error
|
|
1798
|
+
);
|
|
1799
|
+
});
|
|
1800
|
+
let didAddServiceFactory = false;
|
|
1801
|
+
const newLoaders = new Array();
|
|
1802
|
+
for await (const feature of result) {
|
|
1803
|
+
if (isBackendFeatureLoader(feature)) {
|
|
1804
|
+
newLoaders.push(feature);
|
|
1805
|
+
} else {
|
|
1806
|
+
didAddServiceFactory ||= isServiceFactory(feature);
|
|
1807
|
+
this.#addFeature(feature);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
if (didAddServiceFactory) {
|
|
1811
|
+
this.#serviceRegistry.checkForCircularDeps();
|
|
1812
|
+
}
|
|
1813
|
+
if (newLoaders.length > 0) {
|
|
1814
|
+
await this.#applyBackendFeatureLoaders(newLoaders);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
function toInternalBackendFeature(feature) {
|
|
1820
|
+
if (feature.$$type !== "@backstage/BackendFeature") {
|
|
1821
|
+
throw new Error(`Invalid BackendFeature, bad type '${feature.$$type}'`);
|
|
1822
|
+
}
|
|
1823
|
+
const internal = feature;
|
|
1824
|
+
if (internal.version !== "v1") {
|
|
1825
|
+
throw new Error(
|
|
1826
|
+
`Invalid BackendFeature, bad version '${internal.version}'`
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
return internal;
|
|
1741
1830
|
}
|
|
1742
1831
|
function isServiceFactory(feature) {
|
|
1743
|
-
|
|
1832
|
+
const internal = toInternalBackendFeature(feature);
|
|
1833
|
+
if (internal.featureType === "service") {
|
|
1834
|
+
return true;
|
|
1835
|
+
}
|
|
1836
|
+
return "service" in internal;
|
|
1837
|
+
}
|
|
1838
|
+
function isBackendRegistrations(feature) {
|
|
1839
|
+
const internal = toInternalBackendFeature(feature);
|
|
1840
|
+
if (internal.featureType === "registrations") {
|
|
1841
|
+
return true;
|
|
1842
|
+
}
|
|
1843
|
+
return "getRegistrations" in internal;
|
|
1744
1844
|
}
|
|
1745
|
-
function
|
|
1746
|
-
return
|
|
1845
|
+
function isBackendFeatureLoader(feature) {
|
|
1846
|
+
return toInternalBackendFeature(feature).featureType === "loader";
|
|
1747
1847
|
}
|
|
1748
1848
|
|
|
1749
1849
|
class BackstageBackend {
|