@backstage/backend-app-api 1.2.3-next.0 → 1.2.3-next.1
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 +42 -0
- package/config.d.ts +21 -0
- package/dist/wiring/BackendInitializer.cjs.js +36 -8
- package/dist/wiring/BackendInitializer.cjs.js.map +1 -1
- package/dist/wiring/createInitializationLogger.cjs.js +13 -0
- package/dist/wiring/createInitializationLogger.cjs.js.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @backstage/backend-app-api
|
|
2
2
|
|
|
3
|
+
## 1.2.3-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 729a7d6: Added a configuration to permit backend plugin module failures on startup:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
backend:
|
|
11
|
+
...
|
|
12
|
+
startup:
|
|
13
|
+
plugins:
|
|
14
|
+
plugin-x:
|
|
15
|
+
modules:
|
|
16
|
+
module-y:
|
|
17
|
+
onPluginModuleBootFailure: continue
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This configuration permits `plugin-x` with `module-y` to fail on startup. Omitting the
|
|
21
|
+
`onPluginModuleBootFailure` configuration matches the previous behavior, wherein any
|
|
22
|
+
individual plugin module failure is forwarded to the plugin and aborts backend startup.
|
|
23
|
+
|
|
24
|
+
The default can also be changed, so that continuing on failure is the default
|
|
25
|
+
unless otherwise specified:
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
backend:
|
|
29
|
+
startup:
|
|
30
|
+
default:
|
|
31
|
+
onPluginModuleBootFailure: continue
|
|
32
|
+
plugins:
|
|
33
|
+
catalog:
|
|
34
|
+
modules:
|
|
35
|
+
github:
|
|
36
|
+
onPluginModuleBootFailure: abort
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- 72d019d: Removed various typos
|
|
40
|
+
- Updated dependencies
|
|
41
|
+
- @backstage/backend-plugin-api@1.3.1-next.1
|
|
42
|
+
- @backstage/config@1.3.2
|
|
43
|
+
- @backstage/errors@1.2.7
|
|
44
|
+
|
|
3
45
|
## 1.2.3-next.0
|
|
4
46
|
|
|
5
47
|
### Patch Changes
|
package/config.d.ts
CHANGED
|
@@ -34,6 +34,14 @@ export interface Config {
|
|
|
34
34
|
* `onPluginBootFailure: abort` to be required.
|
|
35
35
|
*/
|
|
36
36
|
onPluginBootFailure?: 'continue' | 'abort';
|
|
37
|
+
/**
|
|
38
|
+
* The default value for `onPluginModuleBootFailure` if not specified for a particular plugin module.
|
|
39
|
+
* This defaults to 'abort', which means `onPluginModuleBootFailure: continue` must be specified
|
|
40
|
+
* for backend startup to continue on plugin module boot failure. This can also be set to
|
|
41
|
+
* 'continue', which flips the logic for individual plugin modules so that they must be set to
|
|
42
|
+
* `onPluginModuleBootFailure: abort` to be required.
|
|
43
|
+
*/
|
|
44
|
+
onPluginModuleBootFailure?: 'continue' | 'abort';
|
|
37
45
|
};
|
|
38
46
|
plugins?: {
|
|
39
47
|
[pluginId: string]: {
|
|
@@ -46,6 +54,19 @@ export interface Config {
|
|
|
46
54
|
* setting).
|
|
47
55
|
*/
|
|
48
56
|
onPluginBootFailure?: 'continue' | 'abort';
|
|
57
|
+
modules?: {
|
|
58
|
+
[moduleId: string]: {
|
|
59
|
+
/**
|
|
60
|
+
* Used to control backend startup behavior when this plugin module fails to boot up. Setting
|
|
61
|
+
* this to `continue` allows the backend to continue starting up, even if this plugin
|
|
62
|
+
* module fails. This can enable leaving a crashing plugin installed, but still permit backend
|
|
63
|
+
* startup, which may help troubleshoot data-dependent issues. Plugin module failures for plugin modules
|
|
64
|
+
* set to `abort` are fatal (this is the default unless overridden by the `default`
|
|
65
|
+
* setting).
|
|
66
|
+
*/
|
|
67
|
+
onPluginModuleBootFailure?: 'continue' | 'abort';
|
|
68
|
+
};
|
|
69
|
+
};
|
|
49
70
|
};
|
|
50
71
|
};
|
|
51
72
|
};
|
|
@@ -261,17 +261,36 @@ class BackendInitializer {
|
|
|
261
261
|
}
|
|
262
262
|
await tree.parallelTopologicalTraversal(
|
|
263
263
|
async ({ moduleId, moduleInit }) => {
|
|
264
|
-
const
|
|
265
|
-
moduleInit.init.deps,
|
|
264
|
+
const isModuleBootFailurePermitted = this.#getPluginModuleBootFailurePredicate(
|
|
266
265
|
pluginId,
|
|
267
|
-
moduleId
|
|
266
|
+
moduleId,
|
|
267
|
+
rootConfig
|
|
268
268
|
);
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
269
|
+
try {
|
|
270
|
+
const moduleDeps = await this.#getInitDeps(
|
|
271
|
+
moduleInit.init.deps,
|
|
272
|
+
pluginId,
|
|
273
|
+
moduleId
|
|
273
274
|
);
|
|
274
|
-
|
|
275
|
+
await moduleInit.init.func(moduleDeps).catch((error) => {
|
|
276
|
+
throw new errors.ForwardedError(
|
|
277
|
+
`Module '${moduleId}' for plugin '${pluginId}' startup failed`,
|
|
278
|
+
error
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
} catch (error) {
|
|
282
|
+
errors.assertError(error);
|
|
283
|
+
if (isModuleBootFailurePermitted) {
|
|
284
|
+
initLogger.onPermittedPluginModuleFailure(
|
|
285
|
+
pluginId,
|
|
286
|
+
moduleId,
|
|
287
|
+
error
|
|
288
|
+
);
|
|
289
|
+
} else {
|
|
290
|
+
initLogger.onPluginModuleFailed(pluginId, moduleId, error);
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
275
294
|
}
|
|
276
295
|
);
|
|
277
296
|
}
|
|
@@ -458,6 +477,15 @@ class BackendInitializer {
|
|
|
458
477
|
) ?? defaultStartupBootFailureValue;
|
|
459
478
|
return pluginStartupBootFailureValue === "continue";
|
|
460
479
|
}
|
|
480
|
+
#getPluginModuleBootFailurePredicate(pluginId, moduleId, config) {
|
|
481
|
+
const defaultStartupBootFailureValue = config?.getOptionalString(
|
|
482
|
+
"backend.startup.default.onPluginModuleBootFailure"
|
|
483
|
+
) ?? "abort";
|
|
484
|
+
const pluginModuleStartupBootFailureValue = config?.getOptionalString(
|
|
485
|
+
`backend.startup.plugins.${pluginId}.modules.${moduleId}.onPluginModuleBootFailure`
|
|
486
|
+
) ?? defaultStartupBootFailureValue;
|
|
487
|
+
return pluginModuleStartupBootFailureValue === "continue";
|
|
488
|
+
}
|
|
461
489
|
}
|
|
462
490
|
function toInternalBackendFeature(feature) {
|
|
463
491
|
if (feature.$$type !== "@backstage/BackendFeature") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BackendInitializer.cjs.js","sources":["../../src/wiring/BackendInitializer.ts"],"sourcesContent":["/*\n * Copyright 2022 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 ExtensionPoint,\n coreServices,\n ServiceRef,\n ServiceFactory,\n LifecycleService,\n RootLifecycleService,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { ServiceOrExtensionPoint } from './types';\n// Direct internal import to avoid duplication\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport type {\n InternalBackendFeature,\n InternalBackendFeatureLoader,\n InternalBackendRegistrations,\n} from '../../../backend-plugin-api/src/wiring/types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport type { InternalServiceFactory } from '../../../backend-plugin-api/src/services/system/types';\nimport { ForwardedError, ConflictError, assertError } from '@backstage/errors';\nimport {\n instanceMetadataServiceRef,\n BackendFeatureMeta,\n} from '@backstage/backend-plugin-api/alpha';\nimport { DependencyGraph } from '../lib/DependencyGraph';\nimport { ServiceRegistry } from './ServiceRegistry';\nimport { createInitializationLogger } from './createInitializationLogger';\nimport { unwrapFeature } from './helpers';\n\nexport interface BackendRegisterInit {\n consumes: Set<ServiceOrExtensionPoint>;\n provides: Set<ServiceOrExtensionPoint>;\n init: {\n deps: { [name: string]: ServiceOrExtensionPoint };\n func: (deps: { [name: string]: unknown }) => Promise<void>;\n };\n}\n\n/**\n * A registry of backend instances, used to manage process shutdown hooks across all instances.\n */\nconst instanceRegistry = new (class InstanceRegistry {\n #registered = false;\n #instances = new Set<BackendInitializer>();\n\n register(instance: BackendInitializer) {\n if (!this.#registered) {\n this.#registered = true;\n\n process.addListener('SIGTERM', this.#exitHandler);\n process.addListener('SIGINT', this.#exitHandler);\n process.addListener('beforeExit', this.#exitHandler);\n }\n\n this.#instances.add(instance);\n }\n\n unregister(instance: BackendInitializer) {\n this.#instances.delete(instance);\n }\n\n #exitHandler = async () => {\n try {\n const results = await Promise.allSettled(\n Array.from(this.#instances).map(b => b.stop()),\n );\n const errors = results.flatMap(r =>\n r.status === 'rejected' ? [r.reason] : [],\n );\n\n if (errors.length > 0) {\n for (const error of errors) {\n console.error(error);\n }\n process.exit(1);\n } else {\n process.exit(0);\n }\n } catch (error) {\n console.error(error);\n process.exit(1);\n }\n };\n})();\n\nfunction createInstanceMetadataServiceFactory(\n registrations: InternalBackendRegistrations[],\n) {\n const installedFeatures = registrations\n .map(registration => {\n if (registration.featureType === 'registrations') {\n return registration\n .getRegistrations()\n .map(feature => {\n if (feature.type === 'plugin') {\n return Object.defineProperty(\n {\n type: 'plugin',\n pluginId: feature.pluginId,\n },\n 'toString',\n {\n enumerable: false,\n configurable: true,\n value: () => `plugin{pluginId=${feature.pluginId}}`,\n },\n );\n } else if (feature.type === 'module') {\n return Object.defineProperty(\n {\n type: 'module',\n pluginId: feature.pluginId,\n moduleId: feature.moduleId,\n },\n 'toString',\n {\n enumerable: false,\n configurable: true,\n value: () =>\n `module{moduleId=${feature.moduleId},pluginId=${feature.pluginId}}`,\n },\n );\n }\n // Ignore unknown feature types.\n return undefined;\n })\n .filter(Boolean) as BackendFeatureMeta[];\n }\n return [];\n })\n .flat();\n return createServiceFactory({\n service: instanceMetadataServiceRef,\n deps: {},\n factory: async () => ({ getInstalledFeatures: () => installedFeatures }),\n });\n}\n\nexport class BackendInitializer {\n #startPromise?: Promise<void>;\n #stopPromise?: Promise<void>;\n #registrations = new Array<InternalBackendRegistrations>();\n #extensionPoints = new Map<string, { impl: unknown; pluginId: string }>();\n #serviceRegistry: ServiceRegistry;\n #registeredFeatures = new Array<Promise<BackendFeature>>();\n #registeredFeatureLoaders = new Array<InternalBackendFeatureLoader>();\n\n constructor(defaultApiFactories: ServiceFactory[]) {\n this.#serviceRegistry = ServiceRegistry.create([...defaultApiFactories]);\n }\n\n async #getInitDeps(\n deps: { [name: string]: ServiceOrExtensionPoint },\n pluginId: string,\n moduleId?: string,\n ) {\n const result = new Map<string, unknown>();\n const missingRefs = new Set<ServiceOrExtensionPoint>();\n\n for (const [name, ref] of Object.entries(deps)) {\n const ep = this.#extensionPoints.get(ref.id);\n if (ep) {\n if (ep.pluginId !== pluginId) {\n throw new Error(\n `Illegal dependency: Module '${moduleId}' for plugin '${pluginId}' attempted to depend on extension point '${ref.id}' for plugin '${ep.pluginId}'. Extension points can only be used within their plugin's scope.`,\n );\n }\n result.set(name, ep.impl);\n } else {\n const impl = await this.#serviceRegistry.get(\n ref as ServiceRef<unknown>,\n pluginId,\n );\n if (impl) {\n result.set(name, impl);\n } else {\n missingRefs.add(ref);\n }\n }\n }\n\n if (missingRefs.size > 0) {\n const missing = Array.from(missingRefs).join(', ');\n const target = moduleId\n ? `module '${moduleId}' for plugin '${pluginId}'`\n : `plugin '${pluginId}'`;\n throw new Error(\n `Service or extension point dependencies of ${target} are missing for the following ref(s): ${missing}`,\n );\n }\n\n return Object.fromEntries(result);\n }\n\n add(feature: BackendFeature | Promise<BackendFeature>) {\n if (this.#startPromise) {\n throw new Error('feature can not be added after the backend has started');\n }\n this.#registeredFeatures.push(Promise.resolve(feature));\n }\n\n #addFeature(feature: BackendFeature) {\n if (isServiceFactory(feature)) {\n this.#serviceRegistry.add(feature);\n } else if (isBackendFeatureLoader(feature)) {\n this.#registeredFeatureLoaders.push(feature);\n } else if (isBackendRegistrations(feature)) {\n this.#registrations.push(feature);\n } else {\n throw new Error(\n `Failed to add feature, invalid feature ${JSON.stringify(feature)}`,\n );\n }\n }\n\n async start(): Promise<void> {\n if (this.#startPromise) {\n throw new Error('Backend has already started');\n }\n if (this.#stopPromise) {\n throw new Error('Backend has already stopped');\n }\n\n instanceRegistry.register(this);\n\n this.#startPromise = this.#doStart();\n await this.#startPromise;\n }\n\n async #doStart(): Promise<void> {\n this.#serviceRegistry.checkForCircularDeps();\n\n for (const feature of this.#registeredFeatures) {\n this.#addFeature(await feature);\n }\n\n await this.#applyBackendFeatureLoaders(this.#registeredFeatureLoaders);\n\n this.#serviceRegistry.add(\n createInstanceMetadataServiceFactory(this.#registrations),\n );\n\n // Initialize all root scoped services\n await this.#serviceRegistry.initializeEagerServicesWithScope('root');\n\n const pluginInits = new Map<string, BackendRegisterInit>();\n const moduleInits = new Map<string, Map<string, BackendRegisterInit>>();\n\n // Enumerate all registrations\n for (const feature of this.#registrations) {\n for (const r of feature.getRegistrations()) {\n const provides = new Set<ExtensionPoint<unknown>>();\n\n if (r.type === 'plugin' || r.type === 'module') {\n for (const [extRef, extImpl] of r.extensionPoints) {\n if (this.#extensionPoints.has(extRef.id)) {\n throw new Error(\n `ExtensionPoint with ID '${extRef.id}' is already registered`,\n );\n }\n this.#extensionPoints.set(extRef.id, {\n impl: extImpl,\n pluginId: r.pluginId,\n });\n provides.add(extRef);\n }\n }\n\n if (r.type === 'plugin') {\n if (pluginInits.has(r.pluginId)) {\n throw new Error(`Plugin '${r.pluginId}' is already registered`);\n }\n pluginInits.set(r.pluginId, {\n provides,\n consumes: new Set(Object.values(r.init.deps)),\n init: r.init,\n });\n } else if (r.type === 'module') {\n let modules = moduleInits.get(r.pluginId);\n if (!modules) {\n modules = new Map();\n moduleInits.set(r.pluginId, modules);\n }\n if (modules.has(r.moduleId)) {\n throw new Error(\n `Module '${r.moduleId}' for plugin '${r.pluginId}' is already registered`,\n );\n }\n modules.set(r.moduleId, {\n provides,\n consumes: new Set(Object.values(r.init.deps)),\n init: r.init,\n });\n } else {\n throw new Error(`Invalid registration type '${(r as any).type}'`);\n }\n }\n }\n\n const allPluginIds = [...pluginInits.keys()];\n\n const initLogger = createInitializationLogger(\n allPluginIds,\n await this.#serviceRegistry.get(coreServices.rootLogger, 'root'),\n );\n\n const rootConfig = await this.#serviceRegistry.get(\n coreServices.rootConfig,\n 'root',\n );\n\n // All plugins are initialized in parallel\n const results = await Promise.allSettled(\n allPluginIds.map(async pluginId => {\n const isBootFailurePermitted = this.#getPluginBootFailurePredicate(\n pluginId,\n rootConfig,\n );\n\n try {\n // Initialize all eager services\n await this.#serviceRegistry.initializeEagerServicesWithScope(\n 'plugin',\n pluginId,\n );\n\n // Modules are initialized before plugins, so that they can provide extension to the plugin\n const modules = moduleInits.get(pluginId);\n if (modules) {\n const tree = DependencyGraph.fromIterable(\n Array.from(modules).map(([moduleId, moduleInit]) => ({\n value: { moduleId, moduleInit },\n // Relationships are reversed at this point since we're only interested in the extension points.\n // If a modules provides extension point A we want it to be initialized AFTER all modules\n // that depend on extension point A, so that they can provide their extensions.\n consumes: Array.from(moduleInit.provides).map(p => p.id),\n provides: Array.from(moduleInit.consumes).map(c => c.id),\n })),\n );\n const circular = tree.detectCircularDependency();\n if (circular) {\n throw new ConflictError(\n `Circular dependency detected for modules of plugin '${pluginId}', ${circular\n .map(({ moduleId }) => `'${moduleId}'`)\n .join(' -> ')}`,\n );\n }\n await tree.parallelTopologicalTraversal(\n async ({ moduleId, moduleInit }) => {\n const moduleDeps = await this.#getInitDeps(\n moduleInit.init.deps,\n pluginId,\n moduleId,\n );\n await moduleInit.init.func(moduleDeps).catch(error => {\n throw new ForwardedError(\n `Module '${moduleId}' for plugin '${pluginId}' startup failed`,\n error,\n );\n });\n },\n );\n }\n\n // Once all modules have been initialized, we can initialize the plugin itself\n const pluginInit = pluginInits.get(pluginId);\n // We allow modules to be installed without the accompanying plugin, so the plugin may not exist\n if (pluginInit) {\n const pluginDeps = await this.#getInitDeps(\n pluginInit.init.deps,\n pluginId,\n );\n await pluginInit.init.func(pluginDeps).catch(error => {\n throw new ForwardedError(\n `Plugin '${pluginId}' startup failed`,\n error,\n );\n });\n }\n\n initLogger.onPluginStarted(pluginId);\n\n // Once the plugin and all modules have been initialized, we can signal that the plugin has stared up successfully\n const lifecycleService = await this.#getPluginLifecycleImpl(pluginId);\n await lifecycleService.startup();\n } catch (error: unknown) {\n assertError(error);\n if (isBootFailurePermitted) {\n initLogger.onPermittedPluginFailure(pluginId, error);\n } else {\n initLogger.onPluginFailed(pluginId, error);\n throw error;\n }\n }\n }),\n );\n\n const initErrors = results.flatMap(r =>\n r.status === 'rejected' ? [r.reason] : [],\n );\n if (initErrors.length === 1) {\n throw initErrors[0];\n } else if (initErrors.length > 1) {\n // TODO(Rugvip): Seems like there aren't proper types for AggregateError yet\n throw new (AggregateError as any)(initErrors, 'Backend startup failed');\n }\n\n // Once all plugins and modules have been initialized, we can signal that the backend has started up successfully\n const lifecycleService = await this.#getRootLifecycleImpl();\n await lifecycleService.startup();\n\n initLogger.onAllStarted();\n\n // Once the backend is started, any uncaught errors or unhandled rejections are caught\n // and logged, in order to avoid crashing the entire backend on local failures.\n if (process.env.NODE_ENV !== 'test') {\n const rootLogger = await this.#serviceRegistry.get(\n coreServices.rootLogger,\n 'root',\n );\n process.on('unhandledRejection', (reason: Error) => {\n rootLogger\n ?.child({ type: 'unhandledRejection' })\n ?.error('Unhandled rejection', reason);\n });\n process.on('uncaughtException', error => {\n rootLogger\n ?.child({ type: 'uncaughtException' })\n ?.error('Uncaught exception', error);\n });\n }\n }\n\n // It's fine to call .stop() multiple times, which for example can happen with manual stop + process exit\n async stop(): Promise<void> {\n instanceRegistry.unregister(this);\n\n if (!this.#stopPromise) {\n this.#stopPromise = this.#doStop();\n }\n await this.#stopPromise;\n }\n\n async #doStop(): Promise<void> {\n if (!this.#startPromise) {\n return;\n }\n\n try {\n await this.#startPromise;\n } catch (error) {\n // The startup failed, but we may still want to do cleanup so we continue silently\n }\n\n const rootLifecycleService = await this.#getRootLifecycleImpl();\n\n // Root services like the health one need to immediatelly be notified of the shutdown\n await rootLifecycleService.beforeShutdown();\n\n // Get all plugins.\n const allPlugins = new Set<string>();\n for (const feature of this.#registrations) {\n for (const r of feature.getRegistrations()) {\n if (r.type === 'plugin') {\n allPlugins.add(r.pluginId);\n }\n }\n }\n\n // Iterate through all plugins and run their shutdown hooks.\n await Promise.allSettled(\n [...allPlugins].map(async pluginId => {\n const lifecycleService = await this.#getPluginLifecycleImpl(pluginId);\n await lifecycleService.shutdown();\n }),\n );\n\n // Once all plugin shutdown hooks are done, run root shutdown hooks.\n await rootLifecycleService.shutdown();\n }\n\n // Bit of a hacky way to grab the lifecycle services, potentially find a nicer way to do this\n async #getRootLifecycleImpl(): Promise<\n RootLifecycleService & {\n startup(): Promise<void>;\n beforeShutdown(): Promise<void>;\n shutdown(): Promise<void>;\n }\n > {\n const lifecycleService = await this.#serviceRegistry.get(\n coreServices.rootLifecycle,\n 'root',\n );\n\n const service = lifecycleService as any;\n if (\n service &&\n typeof service.startup === 'function' &&\n typeof service.shutdown === 'function'\n ) {\n return service;\n }\n\n throw new Error('Unexpected root lifecycle service implementation');\n }\n\n async #getPluginLifecycleImpl(\n pluginId: string,\n ): Promise<\n LifecycleService & { startup(): Promise<void>; shutdown(): Promise<void> }\n > {\n const lifecycleService = await this.#serviceRegistry.get(\n coreServices.lifecycle,\n pluginId,\n );\n\n const service = lifecycleService as any;\n if (\n service &&\n typeof service.startup === 'function' &&\n typeof service.shutdown === 'function'\n ) {\n return service;\n }\n\n throw new Error('Unexpected plugin lifecycle service implementation');\n }\n\n async #applyBackendFeatureLoaders(loaders: InternalBackendFeatureLoader[]) {\n const servicesAddedByLoaders = new Map<\n string,\n InternalBackendFeatureLoader\n >();\n\n for (const loader of loaders) {\n const deps = new Map<string, unknown>();\n const missingRefs = new Set<ServiceOrExtensionPoint>();\n\n for (const [name, ref] of Object.entries(loader.deps ?? {})) {\n if (ref.scope !== 'root') {\n throw new Error(\n `Feature loaders can only depend on root scoped services, but '${name}' is scoped to '${ref.scope}'. Offending loader is ${loader.description}`,\n );\n }\n const impl = await this.#serviceRegistry.get(\n ref as ServiceRef<unknown>,\n 'root',\n );\n if (impl) {\n deps.set(name, impl);\n } else {\n missingRefs.add(ref);\n }\n }\n\n if (missingRefs.size > 0) {\n const missing = Array.from(missingRefs).join(', ');\n throw new Error(\n `No service available for the following ref(s): ${missing}, depended on by feature loader ${loader.description}`,\n );\n }\n\n const result = await loader\n .loader(Object.fromEntries(deps))\n .then(features => features.map(unwrapFeature))\n .catch(error => {\n throw new ForwardedError(\n `Feature loader ${loader.description} failed`,\n error,\n );\n });\n\n let didAddServiceFactory = false;\n const newLoaders = new Array<InternalBackendFeatureLoader>();\n\n for await (const feature of result) {\n if (isBackendFeatureLoader(feature)) {\n newLoaders.push(feature);\n } else {\n // This block makes sure that feature loaders do not provide duplicate\n // implementations for the same service, but at the same time allows\n // service factories provided by feature loaders to be overridden by\n // ones that are explicitly installed with backend.add(serviceFactory).\n //\n // If a factory has already been explicitly installed, the service\n // factory provided by the loader will simply be ignored.\n if (isServiceFactory(feature) && !feature.service.multiton) {\n const conflictingLoader = servicesAddedByLoaders.get(\n feature.service.id,\n );\n if (conflictingLoader) {\n throw new Error(\n `Duplicate service implementations provided for ${feature.service.id} by both feature loader ${loader.description} and feature loader ${conflictingLoader.description}`,\n );\n }\n\n // Check that this service wasn't already explicitly added by backend.add(serviceFactory)\n if (!this.#serviceRegistry.hasBeenAdded(feature.service)) {\n didAddServiceFactory = true;\n servicesAddedByLoaders.set(feature.service.id, loader);\n this.#addFeature(feature);\n }\n } else {\n this.#addFeature(feature);\n }\n }\n }\n\n // Every time we add a new service factory we need to make sure that we don't have circular dependencies\n if (didAddServiceFactory) {\n this.#serviceRegistry.checkForCircularDeps();\n }\n\n // Apply loaders recursively, depth-first\n if (newLoaders.length > 0) {\n await this.#applyBackendFeatureLoaders(newLoaders);\n }\n }\n }\n\n #getPluginBootFailurePredicate(pluginId: string, config?: Config): boolean {\n const defaultStartupBootFailureValue =\n config?.getOptionalString(\n 'backend.startup.default.onPluginBootFailure',\n ) ?? 'abort';\n\n const pluginStartupBootFailureValue =\n config?.getOptionalString(\n `backend.startup.plugins.${pluginId}.onPluginBootFailure`,\n ) ?? defaultStartupBootFailureValue;\n\n return pluginStartupBootFailureValue === 'continue';\n }\n}\n\nfunction toInternalBackendFeature(\n feature: BackendFeature,\n): InternalBackendFeature {\n if (feature.$$type !== '@backstage/BackendFeature') {\n throw new Error(`Invalid BackendFeature, bad type '${feature.$$type}'`);\n }\n const internal = feature as InternalBackendFeature;\n if (internal.version !== 'v1') {\n throw new Error(\n `Invalid BackendFeature, bad version '${internal.version}'`,\n );\n }\n return internal;\n}\n\nfunction isServiceFactory(\n feature: BackendFeature,\n): feature is InternalServiceFactory {\n const internal = toInternalBackendFeature(feature);\n if (internal.featureType === 'service') {\n return true;\n }\n // Backwards compatibility for v1 registrations that use duck typing\n return 'service' in internal;\n}\n\nfunction isBackendRegistrations(\n feature: BackendFeature,\n): feature is InternalBackendRegistrations {\n const internal = toInternalBackendFeature(feature);\n if (internal.featureType === 'registrations') {\n return true;\n }\n // Backwards compatibility for v1 registrations that use duck typing\n return 'getRegistrations' in internal;\n}\n\nfunction isBackendFeatureLoader(\n feature: BackendFeature,\n): feature is InternalBackendFeatureLoader {\n return toInternalBackendFeature(feature).featureType === 'loader';\n}\n"],"names":["createServiceFactory","instanceMetadataServiceRef","ServiceRegistry","createInitializationLogger","coreServices","DependencyGraph","ConflictError","ForwardedError","lifecycleService","assertError","unwrapFeature"],"mappings":";;;;;;;;;;AA2DA,MAAM,gBAAA,GAAmB,IAAK,MAAM,gBAAiB,CAAA;AAAA,EACnD,WAAc,GAAA,KAAA;AAAA,EACd,UAAA,uBAAiB,GAAwB,EAAA;AAAA,EAEzC,SAAS,QAA8B,EAAA;AACrC,IAAI,IAAA,CAAC,KAAK,WAAa,EAAA;AACrB,MAAA,IAAA,CAAK,WAAc,GAAA,IAAA;AAEnB,MAAQ,OAAA,CAAA,WAAA,CAAY,SAAW,EAAA,IAAA,CAAK,YAAY,CAAA;AAChD,MAAQ,OAAA,CAAA,WAAA,CAAY,QAAU,EAAA,IAAA,CAAK,YAAY,CAAA;AAC/C,MAAQ,OAAA,CAAA,WAAA,CAAY,YAAc,EAAA,IAAA,CAAK,YAAY,CAAA;AAAA;AAGrD,IAAK,IAAA,CAAA,UAAA,CAAW,IAAI,QAAQ,CAAA;AAAA;AAC9B,EAEA,WAAW,QAA8B,EAAA;AACvC,IAAK,IAAA,CAAA,UAAA,CAAW,OAAO,QAAQ,CAAA;AAAA;AACjC,EAEA,eAAe,YAAY;AACzB,IAAI,IAAA;AACF,MAAM,MAAA,OAAA,GAAU,MAAM,OAAQ,CAAA,UAAA;AAAA,QAC5B,KAAA,CAAM,KAAK,IAAK,CAAA,UAAU,EAAE,GAAI,CAAA,CAAA,CAAA,KAAK,CAAE,CAAA,IAAA,EAAM;AAAA,OAC/C;AACA,MAAA,MAAM,SAAS,OAAQ,CAAA,OAAA;AAAA,QAAQ,CAAA,CAAA,KAC7B,EAAE,MAAW,KAAA,UAAA,GAAa,CAAC,CAAE,CAAA,MAAM,IAAI;AAAC,OAC1C;AAEA,MAAI,IAAA,MAAA,CAAO,SAAS,CAAG,EAAA;AACrB,QAAA,KAAA,MAAW,SAAS,MAAQ,EAAA;AAC1B,UAAA,OAAA,CAAQ,MAAM,KAAK,CAAA;AAAA;AAErB,QAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,OACT,MAAA;AACL,QAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA;AAChB,aACO,KAAO,EAAA;AACd,MAAA,OAAA,CAAQ,MAAM,KAAK,CAAA;AACnB,MAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA;AAChB,GACF;AACF,CAAG,EAAA;AAEH,SAAS,qCACP,aACA,EAAA;AACA,EAAM,MAAA,iBAAA,GAAoB,aACvB,CAAA,GAAA,CAAI,CAAgB,YAAA,KAAA;AACnB,IAAI,IAAA,YAAA,CAAa,gBAAgB,eAAiB,EAAA;AAChD,MAAA,OAAO,YACJ,CAAA,gBAAA,EACA,CAAA,GAAA,CAAI,CAAW,OAAA,KAAA;AACd,QAAI,IAAA,OAAA,CAAQ,SAAS,QAAU,EAAA;AAC7B,UAAA,OAAO,MAAO,CAAA,cAAA;AAAA,YACZ;AAAA,cACE,IAAM,EAAA,QAAA;AAAA,cACN,UAAU,OAAQ,CAAA;AAAA,aACpB;AAAA,YACA,UAAA;AAAA,YACA;AAAA,cACE,UAAY,EAAA,KAAA;AAAA,cACZ,YAAc,EAAA,IAAA;AAAA,cACd,KAAO,EAAA,MAAM,CAAmB,gBAAA,EAAA,OAAA,CAAQ,QAAQ,CAAA,CAAA;AAAA;AAClD,WACF;AAAA,SACF,MAAA,IAAW,OAAQ,CAAA,IAAA,KAAS,QAAU,EAAA;AACpC,UAAA,OAAO,MAAO,CAAA,cAAA;AAAA,YACZ;AAAA,cACE,IAAM,EAAA,QAAA;AAAA,cACN,UAAU,OAAQ,CAAA,QAAA;AAAA,cAClB,UAAU,OAAQ,CAAA;AAAA,aACpB;AAAA,YACA,UAAA;AAAA,YACA;AAAA,cACE,UAAY,EAAA,KAAA;AAAA,cACZ,YAAc,EAAA,IAAA;AAAA,cACd,OAAO,MACL,CAAA,gBAAA,EAAmB,QAAQ,QAAQ,CAAA,UAAA,EAAa,QAAQ,QAAQ,CAAA,CAAA;AAAA;AACpE,WACF;AAAA;AAGF,QAAO,OAAA,KAAA,CAAA;AAAA,OACR,CACA,CAAA,MAAA,CAAO,OAAO,CAAA;AAAA;AAEnB,IAAA,OAAO,EAAC;AAAA,GACT,EACA,IAAK,EAAA;AACR,EAAA,OAAOA,qCAAqB,CAAA;AAAA,IAC1B,OAAS,EAAAC,gCAAA;AAAA,IACT,MAAM,EAAC;AAAA,IACP,OAAS,EAAA,aAAa,EAAE,oBAAA,EAAsB,MAAM,iBAAkB,EAAA;AAAA,GACvE,CAAA;AACH;AAEO,MAAM,kBAAmB,CAAA;AAAA,EAC9B,aAAA;AAAA,EACA,YAAA;AAAA,EACA,cAAA,GAAiB,IAAI,KAAoC,EAAA;AAAA,EACzD,gBAAA,uBAAuB,GAAiD,EAAA;AAAA,EACxE,gBAAA;AAAA,EACA,mBAAA,GAAsB,IAAI,KAA+B,EAAA;AAAA,EACzD,yBAAA,GAA4B,IAAI,KAAoC,EAAA;AAAA,EAEpE,YAAY,mBAAuC,EAAA;AACjD,IAAA,IAAA,CAAK,mBAAmBC,+BAAgB,CAAA,MAAA,CAAO,CAAC,GAAG,mBAAmB,CAAC,CAAA;AAAA;AACzE,EAEA,MAAM,YAAA,CACJ,IACA,EAAA,QAAA,EACA,QACA,EAAA;AACA,IAAM,MAAA,MAAA,uBAAa,GAAqB,EAAA;AACxC,IAAM,MAAA,WAAA,uBAAkB,GAA6B,EAAA;AAErD,IAAA,KAAA,MAAW,CAAC,IAAM,EAAA,GAAG,KAAK,MAAO,CAAA,OAAA,CAAQ,IAAI,CAAG,EAAA;AAC9C,MAAA,MAAM,EAAK,GAAA,IAAA,CAAK,gBAAiB,CAAA,GAAA,CAAI,IAAI,EAAE,CAAA;AAC3C,MAAA,IAAI,EAAI,EAAA;AACN,QAAI,IAAA,EAAA,CAAG,aAAa,QAAU,EAAA;AAC5B,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,4BAAA,EAA+B,QAAQ,CAAiB,cAAA,EAAA,QAAQ,6CAA6C,GAAI,CAAA,EAAE,CAAiB,cAAA,EAAA,EAAA,CAAG,QAAQ,CAAA,iEAAA;AAAA,WACjJ;AAAA;AAEF,QAAO,MAAA,CAAA,GAAA,CAAI,IAAM,EAAA,EAAA,CAAG,IAAI,CAAA;AAAA,OACnB,MAAA;AACL,QAAM,MAAA,IAAA,GAAO,MAAM,IAAA,CAAK,gBAAiB,CAAA,GAAA;AAAA,UACvC,GAAA;AAAA,UACA;AAAA,SACF;AACA,QAAA,IAAI,IAAM,EAAA;AACR,UAAO,MAAA,CAAA,GAAA,CAAI,MAAM,IAAI,CAAA;AAAA,SAChB,MAAA;AACL,UAAA,WAAA,CAAY,IAAI,GAAG,CAAA;AAAA;AACrB;AACF;AAGF,IAAI,IAAA,WAAA,CAAY,OAAO,CAAG,EAAA;AACxB,MAAA,MAAM,UAAU,KAAM,CAAA,IAAA,CAAK,WAAW,CAAA,CAAE,KAAK,IAAI,CAAA;AACjD,MAAM,MAAA,MAAA,GAAS,WACX,CAAW,QAAA,EAAA,QAAQ,iBAAiB,QAAQ,CAAA,CAAA,CAAA,GAC5C,WAAW,QAAQ,CAAA,CAAA,CAAA;AACvB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,2CAAA,EAA8C,MAAM,CAAA,uCAAA,EAA0C,OAAO,CAAA;AAAA,OACvG;AAAA;AAGF,IAAO,OAAA,MAAA,CAAO,YAAY,MAAM,CAAA;AAAA;AAClC,EAEA,IAAI,OAAmD,EAAA;AACrD,IAAA,IAAI,KAAK,aAAe,EAAA;AACtB,MAAM,MAAA,IAAI,MAAM,wDAAwD,CAAA;AAAA;AAE1E,IAAA,IAAA,CAAK,mBAAoB,CAAA,IAAA,CAAK,OAAQ,CAAA,OAAA,CAAQ,OAAO,CAAC,CAAA;AAAA;AACxD,EAEA,YAAY,OAAyB,EAAA;AACnC,IAAI,IAAA,gBAAA,CAAiB,OAAO,CAAG,EAAA;AAC7B,MAAK,IAAA,CAAA,gBAAA,CAAiB,IAAI,OAAO,CAAA;AAAA,KACnC,MAAA,IAAW,sBAAuB,CAAA,OAAO,CAAG,EAAA;AAC1C,MAAK,IAAA,CAAA,yBAAA,CAA0B,KAAK,OAAO,CAAA;AAAA,KAC7C,MAAA,IAAW,sBAAuB,CAAA,OAAO,CAAG,EAAA;AAC1C,MAAK,IAAA,CAAA,cAAA,CAAe,KAAK,OAAO,CAAA;AAAA,KAC3B,MAAA;AACL,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAA0C,uCAAA,EAAA,IAAA,CAAK,SAAU,CAAA,OAAO,CAAC,CAAA;AAAA,OACnE;AAAA;AACF;AACF,EAEA,MAAM,KAAuB,GAAA;AAC3B,IAAA,IAAI,KAAK,aAAe,EAAA;AACtB,MAAM,MAAA,IAAI,MAAM,6BAA6B,CAAA;AAAA;AAE/C,IAAA,IAAI,KAAK,YAAc,EAAA;AACrB,MAAM,MAAA,IAAI,MAAM,6BAA6B,CAAA;AAAA;AAG/C,IAAA,gBAAA,CAAiB,SAAS,IAAI,CAAA;AAE9B,IAAK,IAAA,CAAA,aAAA,GAAgB,KAAK,QAAS,EAAA;AACnC,IAAA,MAAM,IAAK,CAAA,aAAA;AAAA;AACb,EAEA,MAAM,QAA0B,GAAA;AAC9B,IAAA,IAAA,CAAK,iBAAiB,oBAAqB,EAAA;AAE3C,IAAW,KAAA,MAAA,OAAA,IAAW,KAAK,mBAAqB,EAAA;AAC9C,MAAK,IAAA,CAAA,WAAA,CAAY,MAAM,OAAO,CAAA;AAAA;AAGhC,IAAM,MAAA,IAAA,CAAK,2BAA4B,CAAA,IAAA,CAAK,yBAAyB,CAAA;AAErE,IAAA,IAAA,CAAK,gBAAiB,CAAA,GAAA;AAAA,MACpB,oCAAA,CAAqC,KAAK,cAAc;AAAA,KAC1D;AAGA,IAAM,MAAA,IAAA,CAAK,gBAAiB,CAAA,gCAAA,CAAiC,MAAM,CAAA;AAEnE,IAAM,MAAA,WAAA,uBAAkB,GAAiC,EAAA;AACzD,IAAM,MAAA,WAAA,uBAAkB,GAA8C,EAAA;AAGtE,IAAW,KAAA,MAAA,OAAA,IAAW,KAAK,cAAgB,EAAA;AACzC,MAAW,KAAA,MAAA,CAAA,IAAK,OAAQ,CAAA,gBAAA,EAAoB,EAAA;AAC1C,QAAM,MAAA,QAAA,uBAAe,GAA6B,EAAA;AAElD,QAAA,IAAI,CAAE,CAAA,IAAA,KAAS,QAAY,IAAA,CAAA,CAAE,SAAS,QAAU,EAAA;AAC9C,UAAA,KAAA,MAAW,CAAC,MAAA,EAAQ,OAAO,CAAA,IAAK,EAAE,eAAiB,EAAA;AACjD,YAAA,IAAI,IAAK,CAAA,gBAAA,CAAiB,GAAI,CAAA,MAAA,CAAO,EAAE,CAAG,EAAA;AACxC,cAAA,MAAM,IAAI,KAAA;AAAA,gBACR,CAAA,wBAAA,EAA2B,OAAO,EAAE,CAAA,uBAAA;AAAA,eACtC;AAAA;AAEF,YAAK,IAAA,CAAA,gBAAA,CAAiB,GAAI,CAAA,MAAA,CAAO,EAAI,EAAA;AAAA,cACnC,IAAM,EAAA,OAAA;AAAA,cACN,UAAU,CAAE,CAAA;AAAA,aACb,CAAA;AACD,YAAA,QAAA,CAAS,IAAI,MAAM,CAAA;AAAA;AACrB;AAGF,QAAI,IAAA,CAAA,CAAE,SAAS,QAAU,EAAA;AACvB,UAAA,IAAI,WAAY,CAAA,GAAA,CAAI,CAAE,CAAA,QAAQ,CAAG,EAAA;AAC/B,YAAA,MAAM,IAAI,KAAA,CAAM,CAAW,QAAA,EAAA,CAAA,CAAE,QAAQ,CAAyB,uBAAA,CAAA,CAAA;AAAA;AAEhE,UAAY,WAAA,CAAA,GAAA,CAAI,EAAE,QAAU,EAAA;AAAA,YAC1B,QAAA;AAAA,YACA,QAAA,EAAU,IAAI,GAAI,CAAA,MAAA,CAAO,OAAO,CAAE,CAAA,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,YAC5C,MAAM,CAAE,CAAA;AAAA,WACT,CAAA;AAAA,SACH,MAAA,IAAW,CAAE,CAAA,IAAA,KAAS,QAAU,EAAA;AAC9B,UAAA,IAAI,OAAU,GAAA,WAAA,CAAY,GAAI,CAAA,CAAA,CAAE,QAAQ,CAAA;AACxC,UAAA,IAAI,CAAC,OAAS,EAAA;AACZ,YAAA,OAAA,uBAAc,GAAI,EAAA;AAClB,YAAY,WAAA,CAAA,GAAA,CAAI,CAAE,CAAA,QAAA,EAAU,OAAO,CAAA;AAAA;AAErC,UAAA,IAAI,OAAQ,CAAA,GAAA,CAAI,CAAE,CAAA,QAAQ,CAAG,EAAA;AAC3B,YAAA,MAAM,IAAI,KAAA;AAAA,cACR,CAAW,QAAA,EAAA,CAAA,CAAE,QAAQ,CAAA,cAAA,EAAiB,EAAE,QAAQ,CAAA,uBAAA;AAAA,aAClD;AAAA;AAEF,UAAQ,OAAA,CAAA,GAAA,CAAI,EAAE,QAAU,EAAA;AAAA,YACtB,QAAA;AAAA,YACA,QAAA,EAAU,IAAI,GAAI,CAAA,MAAA,CAAO,OAAO,CAAE,CAAA,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,YAC5C,MAAM,CAAE,CAAA;AAAA,WACT,CAAA;AAAA,SACI,MAAA;AACL,UAAA,MAAM,IAAI,KAAA,CAAM,CAA+B,2BAAA,EAAA,CAAA,CAAU,IAAI,CAAG,CAAA,CAAA,CAAA;AAAA;AAClE;AACF;AAGF,IAAA,MAAM,YAAe,GAAA,CAAC,GAAG,WAAA,CAAY,MAAM,CAAA;AAE3C,IAAA,MAAM,UAAa,GAAAC,qDAAA;AAAA,MACjB,YAAA;AAAA,MACA,MAAM,IAAK,CAAA,gBAAA,CAAiB,GAAI,CAAAC,6BAAA,CAAa,YAAY,MAAM;AAAA,KACjE;AAEA,IAAM,MAAA,UAAA,GAAa,MAAM,IAAA,CAAK,gBAAiB,CAAA,GAAA;AAAA,MAC7CA,6BAAa,CAAA,UAAA;AAAA,MACb;AAAA,KACF;AAGA,IAAM,MAAA,OAAA,GAAU,MAAM,OAAQ,CAAA,UAAA;AAAA,MAC5B,YAAA,CAAa,GAAI,CAAA,OAAM,QAAY,KAAA;AACjC,QAAA,MAAM,yBAAyB,IAAK,CAAA,8BAAA;AAAA,UAClC,QAAA;AAAA,UACA;AAAA,SACF;AAEA,QAAI,IAAA;AAEF,UAAA,MAAM,KAAK,gBAAiB,CAAA,gCAAA;AAAA,YAC1B,QAAA;AAAA,YACA;AAAA,WACF;AAGA,UAAM,MAAA,OAAA,GAAU,WAAY,CAAA,GAAA,CAAI,QAAQ,CAAA;AACxC,UAAA,IAAI,OAAS,EAAA;AACX,YAAA,MAAM,OAAOC,+BAAgB,CAAA,YAAA;AAAA,cAC3B,KAAA,CAAM,KAAK,OAAO,CAAA,CAAE,IAAI,CAAC,CAAC,QAAU,EAAA,UAAU,CAAO,MAAA;AAAA,gBACnD,KAAA,EAAO,EAAE,QAAA,EAAU,UAAW,EAAA;AAAA;AAAA;AAAA;AAAA,gBAI9B,QAAA,EAAU,MAAM,IAAK,CAAA,UAAA,CAAW,QAAQ,CAAE,CAAA,GAAA,CAAI,CAAK,CAAA,KAAA,CAAA,CAAE,EAAE,CAAA;AAAA,gBACvD,QAAA,EAAU,MAAM,IAAK,CAAA,UAAA,CAAW,QAAQ,CAAE,CAAA,GAAA,CAAI,CAAK,CAAA,KAAA,CAAA,CAAE,EAAE;AAAA,eACvD,CAAA;AAAA,aACJ;AACA,YAAM,MAAA,QAAA,GAAW,KAAK,wBAAyB,EAAA;AAC/C,YAAA,IAAI,QAAU,EAAA;AACZ,cAAA,MAAM,IAAIC,oBAAA;AAAA,gBACR,CAAuD,oDAAA,EAAA,QAAQ,CAAM,GAAA,EAAA,QAAA,CAClE,IAAI,CAAC,EAAE,QAAS,EAAA,KAAM,IAAI,QAAQ,CAAA,CAAA,CAAG,CACrC,CAAA,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,eACjB;AAAA;AAEF,YAAA,MAAM,IAAK,CAAA,4BAAA;AAAA,cACT,OAAO,EAAE,QAAU,EAAA,UAAA,EAAiB,KAAA;AAClC,gBAAM,MAAA,UAAA,GAAa,MAAM,IAAK,CAAA,YAAA;AAAA,kBAC5B,WAAW,IAAK,CAAA,IAAA;AAAA,kBAChB,QAAA;AAAA,kBACA;AAAA,iBACF;AACA,gBAAA,MAAM,WAAW,IAAK,CAAA,IAAA,CAAK,UAAU,CAAA,CAAE,MAAM,CAAS,KAAA,KAAA;AACpD,kBAAA,MAAM,IAAIC,qBAAA;AAAA,oBACR,CAAA,QAAA,EAAW,QAAQ,CAAA,cAAA,EAAiB,QAAQ,CAAA,gBAAA,CAAA;AAAA,oBAC5C;AAAA,mBACF;AAAA,iBACD,CAAA;AAAA;AACH,aACF;AAAA;AAIF,UAAM,MAAA,UAAA,GAAa,WAAY,CAAA,GAAA,CAAI,QAAQ,CAAA;AAE3C,UAAA,IAAI,UAAY,EAAA;AACd,YAAM,MAAA,UAAA,GAAa,MAAM,IAAK,CAAA,YAAA;AAAA,cAC5B,WAAW,IAAK,CAAA,IAAA;AAAA,cAChB;AAAA,aACF;AACA,YAAA,MAAM,WAAW,IAAK,CAAA,IAAA,CAAK,UAAU,CAAA,CAAE,MAAM,CAAS,KAAA,KAAA;AACpD,cAAA,MAAM,IAAIA,qBAAA;AAAA,gBACR,WAAW,QAAQ,CAAA,gBAAA,CAAA;AAAA,gBACnB;AAAA,eACF;AAAA,aACD,CAAA;AAAA;AAGH,UAAA,UAAA,CAAW,gBAAgB,QAAQ,CAAA;AAGnC,UAAA,MAAMC,iBAAmB,GAAA,MAAM,IAAK,CAAA,uBAAA,CAAwB,QAAQ,CAAA;AACpE,UAAA,MAAMA,kBAAiB,OAAQ,EAAA;AAAA,iBACxB,KAAgB,EAAA;AACvB,UAAAC,kBAAA,CAAY,KAAK,CAAA;AACjB,UAAA,IAAI,sBAAwB,EAAA;AAC1B,YAAW,UAAA,CAAA,wBAAA,CAAyB,UAAU,KAAK,CAAA;AAAA,WAC9C,MAAA;AACL,YAAW,UAAA,CAAA,cAAA,CAAe,UAAU,KAAK,CAAA;AACzC,YAAM,MAAA,KAAA;AAAA;AACR;AACF,OACD;AAAA,KACH;AAEA,IAAA,MAAM,aAAa,OAAQ,CAAA,OAAA;AAAA,MAAQ,CAAA,CAAA,KACjC,EAAE,MAAW,KAAA,UAAA,GAAa,CAAC,CAAE,CAAA,MAAM,IAAI;AAAC,KAC1C;AACA,IAAI,IAAA,UAAA,CAAW,WAAW,CAAG,EAAA;AAC3B,MAAA,MAAM,WAAW,CAAC,CAAA;AAAA,KACpB,MAAA,IAAW,UAAW,CAAA,MAAA,GAAS,CAAG,EAAA;AAEhC,MAAM,MAAA,IAAK,cAAuB,CAAA,UAAA,EAAY,wBAAwB,CAAA;AAAA;AAIxE,IAAM,MAAA,gBAAA,GAAmB,MAAM,IAAA,CAAK,qBAAsB,EAAA;AAC1D,IAAA,MAAM,iBAAiB,OAAQ,EAAA;AAE/B,IAAA,UAAA,CAAW,YAAa,EAAA;AAIxB,IAAI,IAAA,OAAA,CAAQ,GAAI,CAAA,QAAA,KAAa,MAAQ,EAAA;AACnC,MAAM,MAAA,UAAA,GAAa,MAAM,IAAA,CAAK,gBAAiB,CAAA,GAAA;AAAA,QAC7CL,6BAAa,CAAA,UAAA;AAAA,QACb;AAAA,OACF;AACA,MAAQ,OAAA,CAAA,EAAA,CAAG,oBAAsB,EAAA,CAAC,MAAkB,KAAA;AAClD,QACI,UAAA,EAAA,KAAA,CAAM,EAAE,IAAM,EAAA,oBAAA,EAAsB,CACpC,EAAA,KAAA,CAAM,uBAAuB,MAAM,CAAA;AAAA,OACxC,CAAA;AACD,MAAQ,OAAA,CAAA,EAAA,CAAG,qBAAqB,CAAS,KAAA,KAAA;AACvC,QACI,UAAA,EAAA,KAAA,CAAM,EAAE,IAAM,EAAA,mBAAA,EAAqB,CACnC,EAAA,KAAA,CAAM,sBAAsB,KAAK,CAAA;AAAA,OACtC,CAAA;AAAA;AACH;AACF;AAAA,EAGA,MAAM,IAAsB,GAAA;AAC1B,IAAA,gBAAA,CAAiB,WAAW,IAAI,CAAA;AAEhC,IAAI,IAAA,CAAC,KAAK,YAAc,EAAA;AACtB,MAAK,IAAA,CAAA,YAAA,GAAe,KAAK,OAAQ,EAAA;AAAA;AAEnC,IAAA,MAAM,IAAK,CAAA,YAAA;AAAA;AACb,EAEA,MAAM,OAAyB,GAAA;AAC7B,IAAI,IAAA,CAAC,KAAK,aAAe,EAAA;AACvB,MAAA;AAAA;AAGF,IAAI,IAAA;AACF,MAAA,MAAM,IAAK,CAAA,aAAA;AAAA,aACJ,KAAO,EAAA;AAAA;AAIhB,IAAM,MAAA,oBAAA,GAAuB,MAAM,IAAA,CAAK,qBAAsB,EAAA;AAG9D,IAAA,MAAM,qBAAqB,cAAe,EAAA;AAG1C,IAAM,MAAA,UAAA,uBAAiB,GAAY,EAAA;AACnC,IAAW,KAAA,MAAA,OAAA,IAAW,KAAK,cAAgB,EAAA;AACzC,MAAW,KAAA,MAAA,CAAA,IAAK,OAAQ,CAAA,gBAAA,EAAoB,EAAA;AAC1C,QAAI,IAAA,CAAA,CAAE,SAAS,QAAU,EAAA;AACvB,UAAW,UAAA,CAAA,GAAA,CAAI,EAAE,QAAQ,CAAA;AAAA;AAC3B;AACF;AAIF,IAAA,MAAM,OAAQ,CAAA,UAAA;AAAA,MACZ,CAAC,GAAG,UAAU,CAAE,CAAA,GAAA,CAAI,OAAM,QAAY,KAAA;AACpC,QAAA,MAAM,gBAAmB,GAAA,MAAM,IAAK,CAAA,uBAAA,CAAwB,QAAQ,CAAA;AACpE,QAAA,MAAM,iBAAiB,QAAS,EAAA;AAAA,OACjC;AAAA,KACH;AAGA,IAAA,MAAM,qBAAqB,QAAS,EAAA;AAAA;AACtC;AAAA,EAGA,MAAM,qBAMJ,GAAA;AACA,IAAM,MAAA,gBAAA,GAAmB,MAAM,IAAA,CAAK,gBAAiB,CAAA,GAAA;AAAA,MACnDA,6BAAa,CAAA,aAAA;AAAA,MACb;AAAA,KACF;AAEA,IAAA,MAAM,OAAU,GAAA,gBAAA;AAChB,IACE,IAAA,OAAA,IACA,OAAO,OAAQ,CAAA,OAAA,KAAY,cAC3B,OAAO,OAAA,CAAQ,aAAa,UAC5B,EAAA;AACA,MAAO,OAAA,OAAA;AAAA;AAGT,IAAM,MAAA,IAAI,MAAM,kDAAkD,CAAA;AAAA;AACpE,EAEA,MAAM,wBACJ,QAGA,EAAA;AACA,IAAM,MAAA,gBAAA,GAAmB,MAAM,IAAA,CAAK,gBAAiB,CAAA,GAAA;AAAA,MACnDA,6BAAa,CAAA,SAAA;AAAA,MACb;AAAA,KACF;AAEA,IAAA,MAAM,OAAU,GAAA,gBAAA;AAChB,IACE,IAAA,OAAA,IACA,OAAO,OAAQ,CAAA,OAAA,KAAY,cAC3B,OAAO,OAAA,CAAQ,aAAa,UAC5B,EAAA;AACA,MAAO,OAAA,OAAA;AAAA;AAGT,IAAM,MAAA,IAAI,MAAM,oDAAoD,CAAA;AAAA;AACtE,EAEA,MAAM,4BAA4B,OAAyC,EAAA;AACzE,IAAM,MAAA,sBAAA,uBAA6B,GAGjC,EAAA;AAEF,IAAA,KAAA,MAAW,UAAU,OAAS,EAAA;AAC5B,MAAM,MAAA,IAAA,uBAAW,GAAqB,EAAA;AACtC,MAAM,MAAA,WAAA,uBAAkB,GAA6B,EAAA;AAErD,MAAW,KAAA,MAAA,CAAC,IAAM,EAAA,GAAG,CAAK,IAAA,MAAA,CAAO,QAAQ,MAAO,CAAA,IAAA,IAAQ,EAAE,CAAG,EAAA;AAC3D,QAAI,IAAA,GAAA,CAAI,UAAU,MAAQ,EAAA;AACxB,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,iEAAiE,IAAI,CAAA,gBAAA,EAAmB,IAAI,KAAK,CAAA,uBAAA,EAA0B,OAAO,WAAW,CAAA;AAAA,WAC/I;AAAA;AAEF,QAAM,MAAA,IAAA,GAAO,MAAM,IAAA,CAAK,gBAAiB,CAAA,GAAA;AAAA,UACvC,GAAA;AAAA,UACA;AAAA,SACF;AACA,QAAA,IAAI,IAAM,EAAA;AACR,UAAK,IAAA,CAAA,GAAA,CAAI,MAAM,IAAI,CAAA;AAAA,SACd,MAAA;AACL,UAAA,WAAA,CAAY,IAAI,GAAG,CAAA;AAAA;AACrB;AAGF,MAAI,IAAA,WAAA,CAAY,OAAO,CAAG,EAAA;AACxB,QAAA,MAAM,UAAU,KAAM,CAAA,IAAA,CAAK,WAAW,CAAA,CAAE,KAAK,IAAI,CAAA;AACjD,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAkD,+CAAA,EAAA,OAAO,CAAmC,gCAAA,EAAA,MAAA,CAAO,WAAW,CAAA;AAAA,SAChH;AAAA;AAGF,MAAA,MAAM,SAAS,MAAM,MAAA,CAClB,MAAO,CAAA,MAAA,CAAO,YAAY,IAAI,CAAC,CAC/B,CAAA,IAAA,CAAK,cAAY,QAAS,CAAA,GAAA,CAAIM,qBAAa,CAAC,CAAA,CAC5C,MAAM,CAAS,KAAA,KAAA;AACd,QAAA,MAAM,IAAIH,qBAAA;AAAA,UACR,CAAA,eAAA,EAAkB,OAAO,WAAW,CAAA,OAAA,CAAA;AAAA,UACpC;AAAA,SACF;AAAA,OACD,CAAA;AAEH,MAAA,IAAI,oBAAuB,GAAA,KAAA;AAC3B,MAAM,MAAA,UAAA,GAAa,IAAI,KAAoC,EAAA;AAE3D,MAAA,WAAA,MAAiB,WAAW,MAAQ,EAAA;AAClC,QAAI,IAAA,sBAAA,CAAuB,OAAO,CAAG,EAAA;AACnC,UAAA,UAAA,CAAW,KAAK,OAAO,CAAA;AAAA,SAClB,MAAA;AAQL,UAAA,IAAI,iBAAiB,OAAO,CAAA,IAAK,CAAC,OAAA,CAAQ,QAAQ,QAAU,EAAA;AAC1D,YAAA,MAAM,oBAAoB,sBAAuB,CAAA,GAAA;AAAA,cAC/C,QAAQ,OAAQ,CAAA;AAAA,aAClB;AACA,YAAA,IAAI,iBAAmB,EAAA;AACrB,cAAA,MAAM,IAAI,KAAA;AAAA,gBACR,CAAA,+CAAA,EAAkD,QAAQ,OAAQ,CAAA,EAAE,2BAA2B,MAAO,CAAA,WAAW,CAAuB,oBAAA,EAAA,iBAAA,CAAkB,WAAW,CAAA;AAAA,eACvK;AAAA;AAIF,YAAA,IAAI,CAAC,IAAK,CAAA,gBAAA,CAAiB,YAAa,CAAA,OAAA,CAAQ,OAAO,CAAG,EAAA;AACxD,cAAuB,oBAAA,GAAA,IAAA;AACvB,cAAA,sBAAA,CAAuB,GAAI,CAAA,OAAA,CAAQ,OAAQ,CAAA,EAAA,EAAI,MAAM,CAAA;AACrD,cAAA,IAAA,CAAK,YAAY,OAAO,CAAA;AAAA;AAC1B,WACK,MAAA;AACL,YAAA,IAAA,CAAK,YAAY,OAAO,CAAA;AAAA;AAC1B;AACF;AAIF,MAAA,IAAI,oBAAsB,EAAA;AACxB,QAAA,IAAA,CAAK,iBAAiB,oBAAqB,EAAA;AAAA;AAI7C,MAAI,IAAA,UAAA,CAAW,SAAS,CAAG,EAAA;AACzB,QAAM,MAAA,IAAA,CAAK,4BAA4B,UAAU,CAAA;AAAA;AACnD;AACF;AACF,EAEA,8BAAA,CAA+B,UAAkB,MAA0B,EAAA;AACzE,IAAA,MAAM,iCACJ,MAAQ,EAAA,iBAAA;AAAA,MACN;AAAA,KACG,IAAA,OAAA;AAEP,IAAA,MAAM,gCACJ,MAAQ,EAAA,iBAAA;AAAA,MACN,2BAA2B,QAAQ,CAAA,oBAAA;AAAA,KAChC,IAAA,8BAAA;AAEP,IAAA,OAAO,6BAAkC,KAAA,UAAA;AAAA;AAE7C;AAEA,SAAS,yBACP,OACwB,EAAA;AACxB,EAAI,IAAA,OAAA,CAAQ,WAAW,2BAA6B,EAAA;AAClD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAqC,kCAAA,EAAA,OAAA,CAAQ,MAAM,CAAG,CAAA,CAAA,CAAA;AAAA;AAExE,EAAA,MAAM,QAAW,GAAA,OAAA;AACjB,EAAI,IAAA,QAAA,CAAS,YAAY,IAAM,EAAA;AAC7B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,SAAS,OAAO,CAAA,CAAA;AAAA,KAC1D;AAAA;AAEF,EAAO,OAAA,QAAA;AACT;AAEA,SAAS,iBACP,OACmC,EAAA;AACnC,EAAM,MAAA,QAAA,GAAW,yBAAyB,OAAO,CAAA;AACjD,EAAI,IAAA,QAAA,CAAS,gBAAgB,SAAW,EAAA;AACtC,IAAO,OAAA,IAAA;AAAA;AAGT,EAAA,OAAO,SAAa,IAAA,QAAA;AACtB;AAEA,SAAS,uBACP,OACyC,EAAA;AACzC,EAAM,MAAA,QAAA,GAAW,yBAAyB,OAAO,CAAA;AACjD,EAAI,IAAA,QAAA,CAAS,gBAAgB,eAAiB,EAAA;AAC5C,IAAO,OAAA,IAAA;AAAA;AAGT,EAAA,OAAO,kBAAsB,IAAA,QAAA;AAC/B;AAEA,SAAS,uBACP,OACyC,EAAA;AACzC,EAAO,OAAA,wBAAA,CAAyB,OAAO,CAAA,CAAE,WAAgB,KAAA,QAAA;AAC3D;;;;"}
|
|
1
|
+
{"version":3,"file":"BackendInitializer.cjs.js","sources":["../../src/wiring/BackendInitializer.ts"],"sourcesContent":["/*\n * Copyright 2022 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 ExtensionPoint,\n coreServices,\n ServiceRef,\n ServiceFactory,\n LifecycleService,\n RootLifecycleService,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { ServiceOrExtensionPoint } from './types';\n// Direct internal import to avoid duplication\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport type {\n InternalBackendFeature,\n InternalBackendFeatureLoader,\n InternalBackendRegistrations,\n} from '../../../backend-plugin-api/src/wiring/types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport type { InternalServiceFactory } from '../../../backend-plugin-api/src/services/system/types';\nimport { ForwardedError, ConflictError, assertError } from '@backstage/errors';\nimport {\n instanceMetadataServiceRef,\n BackendFeatureMeta,\n} from '@backstage/backend-plugin-api/alpha';\nimport { DependencyGraph } from '../lib/DependencyGraph';\nimport { ServiceRegistry } from './ServiceRegistry';\nimport { createInitializationLogger } from './createInitializationLogger';\nimport { unwrapFeature } from './helpers';\n\nexport interface BackendRegisterInit {\n consumes: Set<ServiceOrExtensionPoint>;\n provides: Set<ServiceOrExtensionPoint>;\n init: {\n deps: { [name: string]: ServiceOrExtensionPoint };\n func: (deps: { [name: string]: unknown }) => Promise<void>;\n };\n}\n\n/**\n * A registry of backend instances, used to manage process shutdown hooks across all instances.\n */\nconst instanceRegistry = new (class InstanceRegistry {\n #registered = false;\n #instances = new Set<BackendInitializer>();\n\n register(instance: BackendInitializer) {\n if (!this.#registered) {\n this.#registered = true;\n\n process.addListener('SIGTERM', this.#exitHandler);\n process.addListener('SIGINT', this.#exitHandler);\n process.addListener('beforeExit', this.#exitHandler);\n }\n\n this.#instances.add(instance);\n }\n\n unregister(instance: BackendInitializer) {\n this.#instances.delete(instance);\n }\n\n #exitHandler = async () => {\n try {\n const results = await Promise.allSettled(\n Array.from(this.#instances).map(b => b.stop()),\n );\n const errors = results.flatMap(r =>\n r.status === 'rejected' ? [r.reason] : [],\n );\n\n if (errors.length > 0) {\n for (const error of errors) {\n console.error(error);\n }\n process.exit(1);\n } else {\n process.exit(0);\n }\n } catch (error) {\n console.error(error);\n process.exit(1);\n }\n };\n})();\n\nfunction createInstanceMetadataServiceFactory(\n registrations: InternalBackendRegistrations[],\n) {\n const installedFeatures = registrations\n .map(registration => {\n if (registration.featureType === 'registrations') {\n return registration\n .getRegistrations()\n .map(feature => {\n if (feature.type === 'plugin') {\n return Object.defineProperty(\n {\n type: 'plugin',\n pluginId: feature.pluginId,\n },\n 'toString',\n {\n enumerable: false,\n configurable: true,\n value: () => `plugin{pluginId=${feature.pluginId}}`,\n },\n );\n } else if (feature.type === 'module') {\n return Object.defineProperty(\n {\n type: 'module',\n pluginId: feature.pluginId,\n moduleId: feature.moduleId,\n },\n 'toString',\n {\n enumerable: false,\n configurable: true,\n value: () =>\n `module{moduleId=${feature.moduleId},pluginId=${feature.pluginId}}`,\n },\n );\n }\n // Ignore unknown feature types.\n return undefined;\n })\n .filter(Boolean) as BackendFeatureMeta[];\n }\n return [];\n })\n .flat();\n return createServiceFactory({\n service: instanceMetadataServiceRef,\n deps: {},\n factory: async () => ({ getInstalledFeatures: () => installedFeatures }),\n });\n}\n\nexport class BackendInitializer {\n #startPromise?: Promise<void>;\n #stopPromise?: Promise<void>;\n #registrations = new Array<InternalBackendRegistrations>();\n #extensionPoints = new Map<string, { impl: unknown; pluginId: string }>();\n #serviceRegistry: ServiceRegistry;\n #registeredFeatures = new Array<Promise<BackendFeature>>();\n #registeredFeatureLoaders = new Array<InternalBackendFeatureLoader>();\n\n constructor(defaultApiFactories: ServiceFactory[]) {\n this.#serviceRegistry = ServiceRegistry.create([...defaultApiFactories]);\n }\n\n async #getInitDeps(\n deps: { [name: string]: ServiceOrExtensionPoint },\n pluginId: string,\n moduleId?: string,\n ) {\n const result = new Map<string, unknown>();\n const missingRefs = new Set<ServiceOrExtensionPoint>();\n\n for (const [name, ref] of Object.entries(deps)) {\n const ep = this.#extensionPoints.get(ref.id);\n if (ep) {\n if (ep.pluginId !== pluginId) {\n throw new Error(\n `Illegal dependency: Module '${moduleId}' for plugin '${pluginId}' attempted to depend on extension point '${ref.id}' for plugin '${ep.pluginId}'. Extension points can only be used within their plugin's scope.`,\n );\n }\n result.set(name, ep.impl);\n } else {\n const impl = await this.#serviceRegistry.get(\n ref as ServiceRef<unknown>,\n pluginId,\n );\n if (impl) {\n result.set(name, impl);\n } else {\n missingRefs.add(ref);\n }\n }\n }\n\n if (missingRefs.size > 0) {\n const missing = Array.from(missingRefs).join(', ');\n const target = moduleId\n ? `module '${moduleId}' for plugin '${pluginId}'`\n : `plugin '${pluginId}'`;\n throw new Error(\n `Service or extension point dependencies of ${target} are missing for the following ref(s): ${missing}`,\n );\n }\n\n return Object.fromEntries(result);\n }\n\n add(feature: BackendFeature | Promise<BackendFeature>) {\n if (this.#startPromise) {\n throw new Error('feature can not be added after the backend has started');\n }\n this.#registeredFeatures.push(Promise.resolve(feature));\n }\n\n #addFeature(feature: BackendFeature) {\n if (isServiceFactory(feature)) {\n this.#serviceRegistry.add(feature);\n } else if (isBackendFeatureLoader(feature)) {\n this.#registeredFeatureLoaders.push(feature);\n } else if (isBackendRegistrations(feature)) {\n this.#registrations.push(feature);\n } else {\n throw new Error(\n `Failed to add feature, invalid feature ${JSON.stringify(feature)}`,\n );\n }\n }\n\n async start(): Promise<void> {\n if (this.#startPromise) {\n throw new Error('Backend has already started');\n }\n if (this.#stopPromise) {\n throw new Error('Backend has already stopped');\n }\n\n instanceRegistry.register(this);\n\n this.#startPromise = this.#doStart();\n await this.#startPromise;\n }\n\n async #doStart(): Promise<void> {\n this.#serviceRegistry.checkForCircularDeps();\n\n for (const feature of this.#registeredFeatures) {\n this.#addFeature(await feature);\n }\n\n await this.#applyBackendFeatureLoaders(this.#registeredFeatureLoaders);\n\n this.#serviceRegistry.add(\n createInstanceMetadataServiceFactory(this.#registrations),\n );\n\n // Initialize all root scoped services\n await this.#serviceRegistry.initializeEagerServicesWithScope('root');\n\n const pluginInits = new Map<string, BackendRegisterInit>();\n const moduleInits = new Map<string, Map<string, BackendRegisterInit>>();\n\n // Enumerate all registrations\n for (const feature of this.#registrations) {\n for (const r of feature.getRegistrations()) {\n const provides = new Set<ExtensionPoint<unknown>>();\n\n if (r.type === 'plugin' || r.type === 'module') {\n for (const [extRef, extImpl] of r.extensionPoints) {\n if (this.#extensionPoints.has(extRef.id)) {\n throw new Error(\n `ExtensionPoint with ID '${extRef.id}' is already registered`,\n );\n }\n this.#extensionPoints.set(extRef.id, {\n impl: extImpl,\n pluginId: r.pluginId,\n });\n provides.add(extRef);\n }\n }\n\n if (r.type === 'plugin') {\n if (pluginInits.has(r.pluginId)) {\n throw new Error(`Plugin '${r.pluginId}' is already registered`);\n }\n pluginInits.set(r.pluginId, {\n provides,\n consumes: new Set(Object.values(r.init.deps)),\n init: r.init,\n });\n } else if (r.type === 'module') {\n let modules = moduleInits.get(r.pluginId);\n if (!modules) {\n modules = new Map();\n moduleInits.set(r.pluginId, modules);\n }\n if (modules.has(r.moduleId)) {\n throw new Error(\n `Module '${r.moduleId}' for plugin '${r.pluginId}' is already registered`,\n );\n }\n modules.set(r.moduleId, {\n provides,\n consumes: new Set(Object.values(r.init.deps)),\n init: r.init,\n });\n } else {\n throw new Error(`Invalid registration type '${(r as any).type}'`);\n }\n }\n }\n\n const allPluginIds = [...pluginInits.keys()];\n\n const initLogger = createInitializationLogger(\n allPluginIds,\n await this.#serviceRegistry.get(coreServices.rootLogger, 'root'),\n );\n\n const rootConfig = await this.#serviceRegistry.get(\n coreServices.rootConfig,\n 'root',\n );\n\n // All plugins are initialized in parallel\n const results = await Promise.allSettled(\n allPluginIds.map(async pluginId => {\n const isBootFailurePermitted = this.#getPluginBootFailurePredicate(\n pluginId,\n rootConfig,\n );\n\n try {\n // Initialize all eager services\n await this.#serviceRegistry.initializeEagerServicesWithScope(\n 'plugin',\n pluginId,\n );\n\n // Modules are initialized before plugins, so that they can provide extension to the plugin\n const modules = moduleInits.get(pluginId);\n if (modules) {\n const tree = DependencyGraph.fromIterable(\n Array.from(modules).map(([moduleId, moduleInit]) => ({\n value: { moduleId, moduleInit },\n // Relationships are reversed at this point since we're only interested in the extension points.\n // If a modules provides extension point A we want it to be initialized AFTER all modules\n // that depend on extension point A, so that they can provide their extensions.\n consumes: Array.from(moduleInit.provides).map(p => p.id),\n provides: Array.from(moduleInit.consumes).map(c => c.id),\n })),\n );\n const circular = tree.detectCircularDependency();\n if (circular) {\n throw new ConflictError(\n `Circular dependency detected for modules of plugin '${pluginId}', ${circular\n .map(({ moduleId }) => `'${moduleId}'`)\n .join(' -> ')}`,\n );\n }\n await tree.parallelTopologicalTraversal(\n async ({ moduleId, moduleInit }) => {\n const isModuleBootFailurePermitted =\n this.#getPluginModuleBootFailurePredicate(\n pluginId,\n moduleId,\n rootConfig,\n );\n\n try {\n const moduleDeps = await this.#getInitDeps(\n moduleInit.init.deps,\n pluginId,\n moduleId,\n );\n await moduleInit.init.func(moduleDeps).catch(error => {\n throw new ForwardedError(\n `Module '${moduleId}' for plugin '${pluginId}' startup failed`,\n error,\n );\n });\n } catch (error: unknown) {\n assertError(error);\n if (isModuleBootFailurePermitted) {\n initLogger.onPermittedPluginModuleFailure(\n pluginId,\n moduleId,\n error,\n );\n } else {\n initLogger.onPluginModuleFailed(pluginId, moduleId, error);\n throw error;\n }\n }\n },\n );\n }\n\n // Once all modules have been initialized, we can initialize the plugin itself\n const pluginInit = pluginInits.get(pluginId);\n // We allow modules to be installed without the accompanying plugin, so the plugin may not exist\n if (pluginInit) {\n const pluginDeps = await this.#getInitDeps(\n pluginInit.init.deps,\n pluginId,\n );\n await pluginInit.init.func(pluginDeps).catch(error => {\n throw new ForwardedError(\n `Plugin '${pluginId}' startup failed`,\n error,\n );\n });\n }\n\n initLogger.onPluginStarted(pluginId);\n\n // Once the plugin and all modules have been initialized, we can signal that the plugin has stared up successfully\n const lifecycleService = await this.#getPluginLifecycleImpl(pluginId);\n await lifecycleService.startup();\n } catch (error: unknown) {\n assertError(error);\n if (isBootFailurePermitted) {\n initLogger.onPermittedPluginFailure(pluginId, error);\n } else {\n initLogger.onPluginFailed(pluginId, error);\n throw error;\n }\n }\n }),\n );\n\n const initErrors = results.flatMap(r =>\n r.status === 'rejected' ? [r.reason] : [],\n );\n if (initErrors.length === 1) {\n throw initErrors[0];\n } else if (initErrors.length > 1) {\n // TODO(Rugvip): Seems like there aren't proper types for AggregateError yet\n throw new (AggregateError as any)(initErrors, 'Backend startup failed');\n }\n\n // Once all plugins and modules have been initialized, we can signal that the backend has started up successfully\n const lifecycleService = await this.#getRootLifecycleImpl();\n await lifecycleService.startup();\n\n initLogger.onAllStarted();\n\n // Once the backend is started, any uncaught errors or unhandled rejections are caught\n // and logged, in order to avoid crashing the entire backend on local failures.\n if (process.env.NODE_ENV !== 'test') {\n const rootLogger = await this.#serviceRegistry.get(\n coreServices.rootLogger,\n 'root',\n );\n process.on('unhandledRejection', (reason: Error) => {\n rootLogger\n ?.child({ type: 'unhandledRejection' })\n ?.error('Unhandled rejection', reason);\n });\n process.on('uncaughtException', error => {\n rootLogger\n ?.child({ type: 'uncaughtException' })\n ?.error('Uncaught exception', error);\n });\n }\n }\n\n // It's fine to call .stop() multiple times, which for example can happen with manual stop + process exit\n async stop(): Promise<void> {\n instanceRegistry.unregister(this);\n\n if (!this.#stopPromise) {\n this.#stopPromise = this.#doStop();\n }\n await this.#stopPromise;\n }\n\n async #doStop(): Promise<void> {\n if (!this.#startPromise) {\n return;\n }\n\n try {\n await this.#startPromise;\n } catch (error) {\n // The startup failed, but we may still want to do cleanup so we continue silently\n }\n\n const rootLifecycleService = await this.#getRootLifecycleImpl();\n\n // Root services like the health one need to immediately be notified of the shutdown\n await rootLifecycleService.beforeShutdown();\n\n // Get all plugins.\n const allPlugins = new Set<string>();\n for (const feature of this.#registrations) {\n for (const r of feature.getRegistrations()) {\n if (r.type === 'plugin') {\n allPlugins.add(r.pluginId);\n }\n }\n }\n\n // Iterate through all plugins and run their shutdown hooks.\n await Promise.allSettled(\n [...allPlugins].map(async pluginId => {\n const lifecycleService = await this.#getPluginLifecycleImpl(pluginId);\n await lifecycleService.shutdown();\n }),\n );\n\n // Once all plugin shutdown hooks are done, run root shutdown hooks.\n await rootLifecycleService.shutdown();\n }\n\n // Bit of a hacky way to grab the lifecycle services, potentially find a nicer way to do this\n async #getRootLifecycleImpl(): Promise<\n RootLifecycleService & {\n startup(): Promise<void>;\n beforeShutdown(): Promise<void>;\n shutdown(): Promise<void>;\n }\n > {\n const lifecycleService = await this.#serviceRegistry.get(\n coreServices.rootLifecycle,\n 'root',\n );\n\n const service = lifecycleService as any;\n if (\n service &&\n typeof service.startup === 'function' &&\n typeof service.shutdown === 'function'\n ) {\n return service;\n }\n\n throw new Error('Unexpected root lifecycle service implementation');\n }\n\n async #getPluginLifecycleImpl(\n pluginId: string,\n ): Promise<\n LifecycleService & { startup(): Promise<void>; shutdown(): Promise<void> }\n > {\n const lifecycleService = await this.#serviceRegistry.get(\n coreServices.lifecycle,\n pluginId,\n );\n\n const service = lifecycleService as any;\n if (\n service &&\n typeof service.startup === 'function' &&\n typeof service.shutdown === 'function'\n ) {\n return service;\n }\n\n throw new Error('Unexpected plugin lifecycle service implementation');\n }\n\n async #applyBackendFeatureLoaders(loaders: InternalBackendFeatureLoader[]) {\n const servicesAddedByLoaders = new Map<\n string,\n InternalBackendFeatureLoader\n >();\n\n for (const loader of loaders) {\n const deps = new Map<string, unknown>();\n const missingRefs = new Set<ServiceOrExtensionPoint>();\n\n for (const [name, ref] of Object.entries(loader.deps ?? {})) {\n if (ref.scope !== 'root') {\n throw new Error(\n `Feature loaders can only depend on root scoped services, but '${name}' is scoped to '${ref.scope}'. Offending loader is ${loader.description}`,\n );\n }\n const impl = await this.#serviceRegistry.get(\n ref as ServiceRef<unknown>,\n 'root',\n );\n if (impl) {\n deps.set(name, impl);\n } else {\n missingRefs.add(ref);\n }\n }\n\n if (missingRefs.size > 0) {\n const missing = Array.from(missingRefs).join(', ');\n throw new Error(\n `No service available for the following ref(s): ${missing}, depended on by feature loader ${loader.description}`,\n );\n }\n\n const result = await loader\n .loader(Object.fromEntries(deps))\n .then(features => features.map(unwrapFeature))\n .catch(error => {\n throw new ForwardedError(\n `Feature loader ${loader.description} failed`,\n error,\n );\n });\n\n let didAddServiceFactory = false;\n const newLoaders = new Array<InternalBackendFeatureLoader>();\n\n for await (const feature of result) {\n if (isBackendFeatureLoader(feature)) {\n newLoaders.push(feature);\n } else {\n // This block makes sure that feature loaders do not provide duplicate\n // implementations for the same service, but at the same time allows\n // service factories provided by feature loaders to be overridden by\n // ones that are explicitly installed with backend.add(serviceFactory).\n //\n // If a factory has already been explicitly installed, the service\n // factory provided by the loader will simply be ignored.\n if (isServiceFactory(feature) && !feature.service.multiton) {\n const conflictingLoader = servicesAddedByLoaders.get(\n feature.service.id,\n );\n if (conflictingLoader) {\n throw new Error(\n `Duplicate service implementations provided for ${feature.service.id} by both feature loader ${loader.description} and feature loader ${conflictingLoader.description}`,\n );\n }\n\n // Check that this service wasn't already explicitly added by backend.add(serviceFactory)\n if (!this.#serviceRegistry.hasBeenAdded(feature.service)) {\n didAddServiceFactory = true;\n servicesAddedByLoaders.set(feature.service.id, loader);\n this.#addFeature(feature);\n }\n } else {\n this.#addFeature(feature);\n }\n }\n }\n\n // Every time we add a new service factory we need to make sure that we don't have circular dependencies\n if (didAddServiceFactory) {\n this.#serviceRegistry.checkForCircularDeps();\n }\n\n // Apply loaders recursively, depth-first\n if (newLoaders.length > 0) {\n await this.#applyBackendFeatureLoaders(newLoaders);\n }\n }\n }\n\n #getPluginBootFailurePredicate(pluginId: string, config?: Config): boolean {\n const defaultStartupBootFailureValue =\n config?.getOptionalString(\n 'backend.startup.default.onPluginBootFailure',\n ) ?? 'abort';\n\n const pluginStartupBootFailureValue =\n config?.getOptionalString(\n `backend.startup.plugins.${pluginId}.onPluginBootFailure`,\n ) ?? defaultStartupBootFailureValue;\n\n return pluginStartupBootFailureValue === 'continue';\n }\n\n #getPluginModuleBootFailurePredicate(\n pluginId: string,\n moduleId: string,\n config?: Config,\n ): boolean {\n const defaultStartupBootFailureValue =\n config?.getOptionalString(\n 'backend.startup.default.onPluginModuleBootFailure',\n ) ?? 'abort';\n\n const pluginModuleStartupBootFailureValue =\n config?.getOptionalString(\n `backend.startup.plugins.${pluginId}.modules.${moduleId}.onPluginModuleBootFailure`,\n ) ?? defaultStartupBootFailureValue;\n\n return pluginModuleStartupBootFailureValue === 'continue';\n }\n}\n\nfunction toInternalBackendFeature(\n feature: BackendFeature,\n): InternalBackendFeature {\n if (feature.$$type !== '@backstage/BackendFeature') {\n throw new Error(`Invalid BackendFeature, bad type '${feature.$$type}'`);\n }\n const internal = feature as InternalBackendFeature;\n if (internal.version !== 'v1') {\n throw new Error(\n `Invalid BackendFeature, bad version '${internal.version}'`,\n );\n }\n return internal;\n}\n\nfunction isServiceFactory(\n feature: BackendFeature,\n): feature is InternalServiceFactory {\n const internal = toInternalBackendFeature(feature);\n if (internal.featureType === 'service') {\n return true;\n }\n // Backwards compatibility for v1 registrations that use duck typing\n return 'service' in internal;\n}\n\nfunction isBackendRegistrations(\n feature: BackendFeature,\n): feature is InternalBackendRegistrations {\n const internal = toInternalBackendFeature(feature);\n if (internal.featureType === 'registrations') {\n return true;\n }\n // Backwards compatibility for v1 registrations that use duck typing\n return 'getRegistrations' in internal;\n}\n\nfunction isBackendFeatureLoader(\n feature: BackendFeature,\n): feature is InternalBackendFeatureLoader {\n return toInternalBackendFeature(feature).featureType === 'loader';\n}\n"],"names":["createServiceFactory","instanceMetadataServiceRef","ServiceRegistry","createInitializationLogger","coreServices","DependencyGraph","ConflictError","ForwardedError","assertError","lifecycleService","unwrapFeature"],"mappings":"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"}
|
|
@@ -51,6 +51,19 @@ function createInitializationLogger(pluginIds, rootLogger) {
|
|
|
51
51
|
error
|
|
52
52
|
);
|
|
53
53
|
},
|
|
54
|
+
onPluginModuleFailed(pluginId, moduleId, error) {
|
|
55
|
+
const status = starting.size > 0 ? `, waiting for ${starting.size} other plugins to finish before shutting down the process` : "";
|
|
56
|
+
logger?.error(
|
|
57
|
+
`Module ${moduleId} in Plugin '${pluginId}' threw an error during startup${status}.`,
|
|
58
|
+
error
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
onPermittedPluginModuleFailure(pluginId, moduleId, error) {
|
|
62
|
+
logger?.error(
|
|
63
|
+
`Module ${moduleId} in Plugin '${pluginId}' threw an error during startup, but boot failure is permitted for this plugin module so startup will continue.`,
|
|
64
|
+
error
|
|
65
|
+
);
|
|
66
|
+
},
|
|
54
67
|
onAllStarted() {
|
|
55
68
|
logger?.info(`Plugin initialization complete${getInitStatus()}`);
|
|
56
69
|
if (timeout) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createInitializationLogger.cjs.js","sources":["../../src/wiring/createInitializationLogger.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { RootLoggerService } from '@backstage/backend-plugin-api';\n\nconst LOGGER_INTERVAL_MAX = 60_000;\n\nfunction joinIds(ids: Iterable<string>): string {\n return [...ids].map(id => `'${id}'`).join(', ');\n}\n\nexport function createInitializationLogger(\n pluginIds: string[],\n rootLogger?: RootLoggerService,\n): {\n onPluginStarted(pluginId: string): void;\n onPluginFailed(pluginId: string, error: Error): void;\n onPermittedPluginFailure(pluginId: string, error: Error): void;\n onAllStarted(): void;\n} {\n const logger = rootLogger?.child({ type: 'initialization' });\n const starting = new Set(pluginIds);\n const started = new Set<string>();\n\n logger?.info(`Plugin initialization started: ${joinIds(pluginIds)}`);\n\n const getInitStatus = () => {\n let status = '';\n if (started.size > 0) {\n status = `, newly initialized: ${joinIds(started)}`;\n started.clear();\n }\n if (starting.size > 0) {\n status += `, still initializing: ${joinIds(starting)}`;\n }\n return status;\n };\n\n // Periodically log the initialization status with a fibonacci backoff\n let interval = 1000;\n let prevInterval = 0;\n let timeout: NodeJS.Timeout | undefined;\n const onTimeout = () => {\n logger?.info(`Plugin initialization in progress${getInitStatus()}`);\n\n const nextInterval = Math.min(interval + prevInterval, LOGGER_INTERVAL_MAX);\n prevInterval = interval;\n interval = nextInterval;\n\n timeout = setTimeout(onTimeout, nextInterval);\n };\n timeout = setTimeout(onTimeout, interval);\n\n return {\n onPluginStarted(pluginId: string) {\n starting.delete(pluginId);\n started.add(pluginId);\n },\n onPluginFailed(pluginId: string, error: Error) {\n starting.delete(pluginId);\n const status =\n starting.size > 0\n ? `, waiting for ${starting.size} other plugins to finish before shutting down the process`\n : '';\n logger?.error(\n `Plugin '${pluginId}' threw an error during startup${status}.`,\n error,\n );\n },\n onPermittedPluginFailure(pluginId: string, error: Error) {\n starting.delete(pluginId);\n logger?.error(\n `Plugin '${pluginId}' threw an error during startup, but boot failure is permitted for this plugin so startup will continue.`,\n error,\n );\n },\n onAllStarted() {\n logger?.info(`Plugin initialization complete${getInitStatus()}`);\n\n if (timeout) {\n clearTimeout(timeout);\n timeout = undefined;\n }\n },\n };\n}\n"],"names":[],"mappings":";;AAkBA,MAAM,mBAAsB,GAAA,GAAA;AAE5B,SAAS,QAAQ,GAA+B,EAAA;AAC9C,EAAO,OAAA,CAAC,GAAG,GAAG,CAAE,CAAA,GAAA,CAAI,CAAM,EAAA,KAAA,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,CAAG,CAAE,CAAA,IAAA,CAAK,IAAI,CAAA;AAChD;AAEgB,SAAA,0BAAA,CACd,WACA,
|
|
1
|
+
{"version":3,"file":"createInitializationLogger.cjs.js","sources":["../../src/wiring/createInitializationLogger.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { RootLoggerService } from '@backstage/backend-plugin-api';\n\nconst LOGGER_INTERVAL_MAX = 60_000;\n\nfunction joinIds(ids: Iterable<string>): string {\n return [...ids].map(id => `'${id}'`).join(', ');\n}\n\nexport function createInitializationLogger(\n pluginIds: string[],\n rootLogger?: RootLoggerService,\n): {\n onPluginStarted(pluginId: string): void;\n onPluginFailed(pluginId: string, error: Error): void;\n onPermittedPluginFailure(pluginId: string, error: Error): void;\n onPluginModuleFailed(pluginId: string, moduleId: string, error: Error): void;\n onPermittedPluginModuleFailure(\n pluginId: string,\n moduleId: string,\n error: Error,\n ): void;\n onAllStarted(): void;\n} {\n const logger = rootLogger?.child({ type: 'initialization' });\n const starting = new Set(pluginIds);\n const started = new Set<string>();\n\n logger?.info(`Plugin initialization started: ${joinIds(pluginIds)}`);\n\n const getInitStatus = () => {\n let status = '';\n if (started.size > 0) {\n status = `, newly initialized: ${joinIds(started)}`;\n started.clear();\n }\n if (starting.size > 0) {\n status += `, still initializing: ${joinIds(starting)}`;\n }\n return status;\n };\n\n // Periodically log the initialization status with a fibonacci backoff\n let interval = 1000;\n let prevInterval = 0;\n let timeout: NodeJS.Timeout | undefined;\n const onTimeout = () => {\n logger?.info(`Plugin initialization in progress${getInitStatus()}`);\n\n const nextInterval = Math.min(interval + prevInterval, LOGGER_INTERVAL_MAX);\n prevInterval = interval;\n interval = nextInterval;\n\n timeout = setTimeout(onTimeout, nextInterval);\n };\n timeout = setTimeout(onTimeout, interval);\n\n return {\n onPluginStarted(pluginId: string) {\n starting.delete(pluginId);\n started.add(pluginId);\n },\n onPluginFailed(pluginId: string, error: Error) {\n starting.delete(pluginId);\n const status =\n starting.size > 0\n ? `, waiting for ${starting.size} other plugins to finish before shutting down the process`\n : '';\n logger?.error(\n `Plugin '${pluginId}' threw an error during startup${status}.`,\n error,\n );\n },\n onPermittedPluginFailure(pluginId: string, error: Error) {\n starting.delete(pluginId);\n logger?.error(\n `Plugin '${pluginId}' threw an error during startup, but boot failure is permitted for this plugin so startup will continue.`,\n error,\n );\n },\n onPluginModuleFailed(pluginId: string, moduleId: string, error: Error) {\n const status =\n starting.size > 0\n ? `, waiting for ${starting.size} other plugins to finish before shutting down the process`\n : '';\n logger?.error(\n `Module ${moduleId} in Plugin '${pluginId}' threw an error during startup${status}.`,\n error,\n );\n },\n onPermittedPluginModuleFailure(\n pluginId: string,\n moduleId: string,\n error: Error,\n ) {\n logger?.error(\n `Module ${moduleId} in Plugin '${pluginId}' threw an error during startup, but boot failure is permitted for this plugin module so startup will continue.`,\n error,\n );\n },\n onAllStarted() {\n logger?.info(`Plugin initialization complete${getInitStatus()}`);\n\n if (timeout) {\n clearTimeout(timeout);\n timeout = undefined;\n }\n },\n };\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/backend-app-api",
|
|
3
|
-
"version": "1.2.3-next.
|
|
3
|
+
"version": "1.2.3-next.1",
|
|
4
4
|
"description": "Core API used by Backstage backend apps",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "node-library"
|
|
@@ -50,14 +50,14 @@
|
|
|
50
50
|
"test": "backstage-cli package test"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@backstage/backend-plugin-api": "1.3.1-next.
|
|
53
|
+
"@backstage/backend-plugin-api": "1.3.1-next.1",
|
|
54
54
|
"@backstage/config": "1.3.2",
|
|
55
55
|
"@backstage/errors": "1.2.7"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@backstage/backend-defaults": "0.
|
|
59
|
-
"@backstage/backend-test-utils": "1.5.0-next.
|
|
60
|
-
"@backstage/cli": "0.32.1-next.
|
|
58
|
+
"@backstage/backend-defaults": "0.10.0-next.1",
|
|
59
|
+
"@backstage/backend-test-utils": "1.5.0-next.1",
|
|
60
|
+
"@backstage/cli": "0.32.1-next.1"
|
|
61
61
|
},
|
|
62
62
|
"configSchema": "config.d.ts"
|
|
63
63
|
}
|