@backstage/backend-dynamic-feature-service 0.8.2 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @backstage/backend-dynamic-feature-service
2
2
 
3
+ ## 0.8.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 7005478: Fixed dynamic backend plugin loading to fall back to the main package export when an alpha `package.json` is present but does not provide a plugin entrypoint. Previously, plugins whose alpha export only exposed supplementary APIs (such as permissions) could fail to load even though the main export was valid. The loader now tries the alpha entry first and uses the main export when the alpha module does not export a `BackendFeature`, `BackendFeatureFactory`, or `dynamicPluginInstaller`.
8
+ - Updated dependencies
9
+ - @backstage/plugin-scaffolder-node@0.13.4
10
+ - @backstage/plugin-catalog-backend@3.8.0
11
+ - @backstage/cli-node@0.3.3
12
+ - @backstage/backend-defaults@0.17.3
13
+ - @backstage/plugin-events-backend@0.6.3
14
+ - @backstage/backend-plugin-api@1.9.2
15
+ - @backstage/config-loader@1.10.12
16
+ - @backstage/backend-openapi-utils@0.6.10
17
+ - @backstage/plugin-app-node@0.1.46
18
+ - @backstage/plugin-auth-node@0.7.2
19
+ - @backstage/plugin-events-node@0.4.23
20
+ - @backstage/plugin-permission-node@0.11.1
21
+ - @backstage/plugin-search-backend-node@1.4.5
22
+
23
+ ## 0.8.3-next.0
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies
28
+ - @backstage/plugin-catalog-backend@3.8.0-next.0
29
+ - @backstage/backend-defaults@0.17.2-next.0
30
+ - @backstage/plugin-auth-node@0.7.2-next.0
31
+ - @backstage/plugin-scaffolder-node@0.13.4-next.0
32
+ - @backstage/plugin-events-backend@0.6.3-next.0
33
+ - @backstage/plugin-permission-node@0.11.1-next.0
34
+ - @backstage/plugin-search-backend-node@1.4.5-next.0
35
+ - @backstage/backend-plugin-api@1.9.2-next.0
36
+ - @backstage/plugin-events-node@0.4.23-next.0
37
+ - @backstage/backend-openapi-utils@0.6.10-next.0
38
+ - @backstage/plugin-app-node@0.1.46-next.0
39
+
3
40
  ## 0.8.2
4
41
 
5
42
  ### Patch Changes
@@ -1,14 +1,18 @@
1
1
  'use strict';
2
2
 
3
+ var errors = require('@backstage/errors');
3
4
  var types = require('./types.cjs.js');
4
5
  var pluginScanner = require('../scanner/plugin-scanner.cjs.js');
5
6
  var CommonJSModuleLoader = require('../loader/CommonJSModuleLoader.cjs.js');
7
+ var path = require('node:path');
6
8
  var url = require('node:url');
7
9
  var backendPluginApi = require('@backstage/backend-plugin-api');
8
10
  var cliNode = require('@backstage/cli-node');
9
11
  var cliCommon = require('@backstage/cli-common');
10
12
  var fs = require('node:fs');
11
13
 
14
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
15
+
12
16
  function _interopNamespaceCompat(e) {
13
17
  if (e && typeof e === 'object' && 'default' in e) return e;
14
18
  var n = Object.create(null);
@@ -27,6 +31,7 @@ function _interopNamespaceCompat(e) {
27
31
  return Object.freeze(n);
28
32
  }
29
33
 
34
+ var path__default = /*#__PURE__*/_interopDefaultCompat(path);
30
35
  var url__namespace = /*#__PURE__*/_interopNamespaceCompat(url);
31
36
  var fs__namespace = /*#__PURE__*/_interopNamespaceCompat(fs);
32
37
 
@@ -116,52 +121,60 @@ class DynamicPluginManager {
116
121
  return loadedPlugins;
117
122
  }
118
123
  async loadBackendPlugin(plugin) {
119
- const usedPluginManifest = plugin.alphaManifest?.main ?? plugin.manifest.main;
120
- const usedPluginLocation = plugin.alphaManifest?.main ? `${plugin.location}/alpha` : plugin.location;
121
- const packagePath = url__namespace.fileURLToPath(
122
- `${usedPluginLocation}/${usedPluginManifest}`
123
- );
124
124
  const dynamicPlugin = {
125
125
  name: plugin.manifest.name,
126
126
  version: plugin.manifest.version,
127
127
  platform: "node",
128
128
  role: plugin.manifest.backstage.role
129
129
  };
130
- try {
131
- const pluginModule = await this.moduleLoader.load(packagePath);
132
- if (isBackendFeature(pluginModule.default)) {
133
- dynamicPlugin.installer = {
134
- kind: "new",
135
- install: () => pluginModule.default
136
- };
137
- } else if (isBackendFeatureFactory(pluginModule.default)) {
138
- dynamicPlugin.installer = {
139
- kind: "new",
140
- install: pluginModule.default
141
- };
142
- } else if (types.isBackendDynamicPluginInstaller(pluginModule.dynamicPluginInstaller)) {
143
- dynamicPlugin.installer = pluginModule.dynamicPluginInstaller;
130
+ const entryPoints = [
131
+ ...plugin.alphaManifest?.main ? [
132
+ {
133
+ location: new URL("alpha", `${plugin.location}/`),
134
+ manifest: plugin.alphaManifest.main
135
+ }
136
+ ] : [],
137
+ {
138
+ location: plugin.location,
139
+ manifest: plugin.manifest.main
144
140
  }
145
- if (dynamicPlugin.installer) {
141
+ ];
142
+ if (entryPoints.length === 0) {
143
+ throw new Error(
144
+ `Failed to load dynamic backend plugin '${plugin.manifest.name}': no entry points to load`
145
+ );
146
+ }
147
+ for (const [index, { location, manifest }] of entryPoints.entries()) {
148
+ const loadResult = await tryLoadBackendPluginEntry(
149
+ this.moduleLoader,
150
+ location,
151
+ manifest
152
+ );
153
+ if (loadResult.installer) {
154
+ dynamicPlugin.installer = loadResult.installer;
146
155
  this.logger.info(
147
- `loaded dynamic backend plugin '${plugin.manifest.name}' from '${usedPluginLocation}'`
156
+ `loaded dynamic backend plugin '${plugin.manifest.name}' from '${location}'`
148
157
  );
149
- } else {
150
- dynamicPlugin.failure = `the module should either export a 'BackendFeature' or 'BackendFeatureFactory' as default export, or export a 'const dynamicPluginInstaller: BackendDynamicPluginInstaller' field as dynamic loading entrypoint.`;
158
+ return dynamicPlugin;
159
+ }
160
+ if (!loadResult.error && index < entryPoints.length - 1) {
161
+ continue;
162
+ }
163
+ if (loadResult.error) {
164
+ dynamicPlugin.failure = `${loadResult.error.name}: ${loadResult.error.message}`;
151
165
  this.logger.error(
152
- `dynamic backend plugin '${plugin.manifest.name}' could not be loaded from '${usedPluginLocation}': ${dynamicPlugin.failure}`
166
+ `an error occurred while loading dynamic backend plugin '${plugin.manifest.name}' from '${location}'`,
167
+ loadResult.error
153
168
  );
169
+ return dynamicPlugin;
154
170
  }
155
- return dynamicPlugin;
156
- } catch (error) {
157
- const typedError = typeof error === "object" && "message" in error && "name" in error ? error : new Error(error);
158
- dynamicPlugin.failure = `${typedError.name}: ${typedError.message}`;
171
+ dynamicPlugin.failure = `the module should either export a 'BackendFeature' or 'BackendFeatureFactory' as default export, or export a 'const dynamicPluginInstaller: BackendDynamicPluginInstaller' field as dynamic loading entrypoint.`;
159
172
  this.logger.error(
160
- `an error occurred while loading dynamic backend plugin '${plugin.manifest.name}' from '${usedPluginLocation}'`,
161
- typedError
173
+ `dynamic backend plugin '${plugin.manifest.name}' could not be loaded from '${location}': ${dynamicPlugin.failure}`
162
174
  );
163
175
  return dynamicPlugin;
164
176
  }
177
+ return dynamicPlugin;
165
178
  }
166
179
  backendPlugins(options) {
167
180
  return this.plugins(options).filter(
@@ -245,6 +258,33 @@ const dynamicPluginsFeatureDiscoveryLoader = backendPluginApi.createBackendFeatu
245
258
  return features;
246
259
  }
247
260
  });
261
+ async function tryLoadBackendPluginEntry(moduleLoader, location, manifest) {
262
+ try {
263
+ const packagePath = path__default.default.resolve(url__namespace.fileURLToPath(location), manifest);
264
+ const pluginModule = await moduleLoader.load(packagePath);
265
+ return { installer: resolveInstallerFromModule(pluginModule) };
266
+ } catch (error) {
267
+ return { error: errors.toError(error) };
268
+ }
269
+ }
270
+ function resolveInstallerFromModule(pluginModule) {
271
+ if (isBackendFeature(pluginModule.default)) {
272
+ return {
273
+ kind: "new",
274
+ install: () => pluginModule.default
275
+ };
276
+ }
277
+ if (isBackendFeatureFactory(pluginModule.default)) {
278
+ return {
279
+ kind: "new",
280
+ install: pluginModule.default
281
+ };
282
+ }
283
+ if (types.isBackendDynamicPluginInstaller(pluginModule.dynamicPluginInstaller)) {
284
+ return pluginModule.dynamicPluginInstaller;
285
+ }
286
+ return void 0;
287
+ }
248
288
  function isBackendFeature(value) {
249
289
  return !!value && (typeof value === "object" || typeof value === "function") && value.$$type === "@backstage/BackendFeature";
250
290
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-manager.cjs.js","sources":["../../src/manager/plugin-manager.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Config } from '@backstage/config';\nimport {\n DynamicPluginProvider,\n BackendDynamicPlugin,\n isBackendDynamicPluginInstaller,\n DynamicPlugin,\n FrontendDynamicPlugin,\n} from './types';\nimport { ScannedPluginPackage } from '../scanner';\nimport { PluginScanner } from '../scanner/plugin-scanner';\nimport { ModuleLoader } from '../loader';\nimport { CommonJSModuleLoader } from '../loader/CommonJSModuleLoader';\nimport * as url from 'node:url';\nimport {\n BackendFeature,\n LoggerService,\n coreServices,\n createBackendFeatureLoader,\n createServiceFactory,\n createServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { PackageRole, PackageRoles } from '@backstage/cli-node';\nimport { targetPaths } from '@backstage/cli-common';\nimport * as fs from 'node:fs';\n\n/**\n * @public\n */\nexport interface DynamicPluginManagerOptions {\n config: Config;\n logger: LoggerService;\n preferAlpha?: boolean;\n moduleLoader?: ModuleLoader;\n}\n\n/**\n * @public\n */\nexport class DynamicPluginManager implements DynamicPluginProvider {\n static async create(\n options: DynamicPluginManagerOptions,\n ): Promise<DynamicPluginManager> {\n /* eslint-disable-next-line no-restricted-syntax */\n const backstageRoot = targetPaths.rootDir;\n const scanner = PluginScanner.create({\n config: options.config,\n logger: options.logger,\n backstageRoot,\n preferAlpha: options.preferAlpha,\n });\n const scannedPlugins = (await scanner.scanRoot()).packages;\n await scanner.trackChanges();\n const moduleLoader =\n options.moduleLoader ||\n new CommonJSModuleLoader({ logger: options.logger });\n const manager = new DynamicPluginManager(\n options.logger,\n scannedPlugins,\n moduleLoader,\n );\n\n const scannedPluginManifestsPerRealPath = new Map(\n scannedPlugins.map(p => [\n fs.realpathSync(url.fileURLToPath(p.location)),\n p.manifest,\n ]),\n );\n\n await moduleLoader.bootstrap(\n backstageRoot,\n [...scannedPluginManifestsPerRealPath.keys()],\n scannedPluginManifestsPerRealPath,\n );\n\n scanner.subscribeToRootDirectoryChange(async () => {\n manager._availablePackages = (await scanner.scanRoot()).packages;\n // TODO: do not store _scannedPlugins again, but instead store a diff of the changes\n });\n manager._plugins.push(...(await manager.loadPlugins()));\n\n return manager;\n }\n\n private readonly _plugins: DynamicPlugin[];\n private _availablePackages: ScannedPluginPackage[];\n\n private readonly logger: LoggerService;\n private readonly packages: ScannedPluginPackage[];\n private readonly moduleLoader: ModuleLoader;\n\n private constructor(\n logger: LoggerService,\n packages: ScannedPluginPackage[],\n moduleLoader: ModuleLoader,\n ) {\n this.logger = logger;\n this.packages = packages;\n this.moduleLoader = moduleLoader;\n this._plugins = [];\n this._availablePackages = packages;\n }\n\n get availablePackages(): ScannedPluginPackage[] {\n return this._availablePackages;\n }\n\n addBackendPlugin(plugin: BackendDynamicPlugin): void {\n this._plugins.push(plugin);\n }\n\n private async loadPlugins(): Promise<DynamicPlugin[]> {\n const loadedPlugins: DynamicPlugin[] = [];\n\n for (const scannedPlugin of this.packages) {\n const role = scannedPlugin.manifest.backstage.role;\n const platform = PackageRoles.getRoleInfo(role).platform;\n const isPlugin =\n role.endsWith('-plugin') ||\n role.endsWith('-plugin-module') ||\n role === ('frontend-dynamic-container' as PackageRole);\n\n if (!isPlugin) {\n this.logger.info(\n `skipping dynamic plugin package '${scannedPlugin.manifest.name}' from '${scannedPlugin.location}': incompatible role '${role}'`,\n );\n continue;\n }\n\n switch (platform) {\n case 'node':\n loadedPlugins.push(await this.loadBackendPlugin(scannedPlugin));\n break;\n\n case 'web':\n loadedPlugins.push({\n name: scannedPlugin.manifest.name,\n version: scannedPlugin.manifest.version,\n role: scannedPlugin.manifest.backstage.role,\n platform: 'web',\n // TODO(davidfestal): add required front-end plugin information here.\n });\n break;\n\n default:\n this.logger.info(\n `skipping dynamic plugin package '${scannedPlugin.manifest.name}' from '${scannedPlugin.location}': unrelated platform '${platform}'`,\n );\n }\n }\n return loadedPlugins;\n }\n\n private async loadBackendPlugin(\n plugin: ScannedPluginPackage,\n ): Promise<BackendDynamicPlugin> {\n const usedPluginManifest =\n plugin.alphaManifest?.main ?? plugin.manifest.main;\n const usedPluginLocation = plugin.alphaManifest?.main\n ? `${plugin.location}/alpha`\n : plugin.location;\n const packagePath = url.fileURLToPath(\n `${usedPluginLocation}/${usedPluginManifest}`,\n );\n const dynamicPlugin: BackendDynamicPlugin = {\n name: plugin.manifest.name,\n version: plugin.manifest.version,\n platform: 'node',\n role: plugin.manifest.backstage.role,\n };\n\n try {\n const pluginModule = await this.moduleLoader.load(packagePath);\n\n if (isBackendFeature(pluginModule.default)) {\n dynamicPlugin.installer = {\n kind: 'new',\n install: () => pluginModule.default,\n };\n } else if (isBackendFeatureFactory(pluginModule.default)) {\n dynamicPlugin.installer = {\n kind: 'new',\n install: pluginModule.default,\n };\n } else if (\n isBackendDynamicPluginInstaller(pluginModule.dynamicPluginInstaller)\n ) {\n dynamicPlugin.installer = pluginModule.dynamicPluginInstaller;\n }\n if (dynamicPlugin.installer) {\n this.logger.info(\n `loaded dynamic backend plugin '${plugin.manifest.name}' from '${usedPluginLocation}'`,\n );\n } else {\n dynamicPlugin.failure = `the module should either export a 'BackendFeature' or 'BackendFeatureFactory' as default export, or export a 'const dynamicPluginInstaller: BackendDynamicPluginInstaller' field as dynamic loading entrypoint.`;\n this.logger.error(\n `dynamic backend plugin '${plugin.manifest.name}' could not be loaded from '${usedPluginLocation}': ${dynamicPlugin.failure}`,\n );\n }\n return dynamicPlugin;\n } catch (error) {\n const typedError =\n typeof error === 'object' && 'message' in error && 'name' in error\n ? error\n : new Error(error);\n dynamicPlugin.failure = `${typedError.name}: ${typedError.message}`;\n this.logger.error(\n `an error occurred while loading dynamic backend plugin '${plugin.manifest.name}' from '${usedPluginLocation}'`,\n typedError,\n );\n return dynamicPlugin;\n }\n }\n\n backendPlugins(options?: {\n includeFailed?: boolean;\n }): BackendDynamicPlugin[] {\n return this.plugins(options).filter(\n (p): p is BackendDynamicPlugin => p.platform === 'node',\n );\n }\n\n frontendPlugins(options?: {\n includeFailed?: boolean;\n }): FrontendDynamicPlugin[] {\n return this.plugins(options).filter(\n (p): p is FrontendDynamicPlugin => p.platform === 'web',\n );\n }\n\n plugins(options?: { includeFailed?: boolean }): DynamicPlugin[] {\n return this._plugins.filter(p => options?.includeFailed || !p.failure);\n }\n\n getScannedPackage(plugin: DynamicPlugin): ScannedPluginPackage {\n const pkg = this.packages.find(\n p =>\n p.manifest.name === plugin.name &&\n p.manifest.version === plugin.version,\n );\n if (pkg === undefined) {\n throw new Error(\n `The scanned package of a dynamic plugin should always be available: ${plugin.name}/${plugin.version}`,\n );\n }\n return pkg;\n }\n}\n\n/**\n * @public\n */\nexport const dynamicPluginsServiceRef = createServiceRef<DynamicPluginProvider>(\n {\n id: 'core.dynamicplugins',\n scope: 'root',\n },\n);\n\n/**\n * @public\n */\nexport interface DynamicPluginsFactoryOptions {\n moduleLoader?(logger: LoggerService): ModuleLoader | Promise<ModuleLoader>;\n}\n\n/**\n * @public\n * @deprecated Use {@link dynamicPluginsFeatureLoader} instead, which gathers all services and features required for dynamic plugins.\n */\nexport const dynamicPluginsServiceFactoryWithOptions = (\n options?: DynamicPluginsFactoryOptions,\n) =>\n createServiceFactory({\n service: dynamicPluginsServiceRef,\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.rootLogger,\n },\n async factory({ config, logger }) {\n return await DynamicPluginManager.create({\n config,\n logger,\n preferAlpha: true,\n moduleLoader: await options?.moduleLoader?.(logger),\n });\n },\n });\n\n/**\n * @public\n * @deprecated Use {@link dynamicPluginsFeatureLoader} instead, which gathers all services and features required for dynamic plugins.\n */\nexport const dynamicPluginsServiceFactory = Object.assign(\n dynamicPluginsServiceFactoryWithOptions,\n dynamicPluginsServiceFactoryWithOptions(),\n);\n\nclass DynamicPluginsEnabledFeatureDiscoveryService {\n private readonly dynamicPlugins: DynamicPluginProvider;\n\n constructor(dynamicPlugins: DynamicPluginProvider) {\n this.dynamicPlugins = dynamicPlugins;\n }\n\n async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {\n return {\n features: this.dynamicPlugins\n .backendPlugins()\n .flatMap((plugin): BackendFeature[] => {\n if (plugin.installer?.kind === 'new') {\n const installed = plugin.installer.install();\n if (Array.isArray(installed)) {\n return installed;\n }\n return [installed];\n }\n return [];\n }),\n };\n }\n}\n\n/**\n * @public\n * @deprecated Use {@link dynamicPluginsFeatureLoader} instead, which gathers all services and features required for dynamic plugins.\n */\nexport const dynamicPluginsFeatureDiscoveryLoader = createBackendFeatureLoader({\n deps: {\n dynamicPlugins: dynamicPluginsServiceRef,\n },\n async loader({ dynamicPlugins }) {\n const service = new DynamicPluginsEnabledFeatureDiscoveryService(\n dynamicPlugins,\n );\n const { features } = await service.getBackendFeatures();\n return features;\n },\n});\n\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n (typeof value === 'object' || typeof value === 'function') &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\nfunction isBackendFeatureFactory(\n value: unknown,\n): value is () => BackendFeature {\n return (\n !!value &&\n typeof value === 'function' &&\n (value as any).$$type === '@backstage/BackendFeatureFactory'\n );\n}\n"],"names":["targetPaths","PluginScanner","CommonJSModuleLoader","fs","url","PackageRoles","isBackendDynamicPluginInstaller","createServiceRef","createServiceFactory","coreServices","createBackendFeatureLoader"],"mappings":"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"}
1
+ {"version":3,"file":"plugin-manager.cjs.js","sources":["../../src/manager/plugin-manager.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Config } from '@backstage/config';\nimport { toError } from '@backstage/errors';\nimport {\n DynamicPluginProvider,\n BackendDynamicPlugin,\n isBackendDynamicPluginInstaller,\n DynamicPlugin,\n FrontendDynamicPlugin,\n} from './types';\nimport { ScannedPluginPackage } from '../scanner';\nimport { PluginScanner } from '../scanner/plugin-scanner';\nimport { ModuleLoader } from '../loader';\nimport { CommonJSModuleLoader } from '../loader/CommonJSModuleLoader';\nimport path from 'node:path';\nimport * as url from 'node:url';\nimport {\n BackendFeature,\n LoggerService,\n coreServices,\n createBackendFeatureLoader,\n createServiceFactory,\n createServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { PackageRole, PackageRoles } from '@backstage/cli-node';\nimport { targetPaths } from '@backstage/cli-common';\nimport * as fs from 'node:fs';\n\n/**\n * @public\n */\nexport interface DynamicPluginManagerOptions {\n config: Config;\n logger: LoggerService;\n preferAlpha?: boolean;\n moduleLoader?: ModuleLoader;\n}\n\n/**\n * @public\n */\nexport class DynamicPluginManager implements DynamicPluginProvider {\n static async create(\n options: DynamicPluginManagerOptions,\n ): Promise<DynamicPluginManager> {\n /* eslint-disable-next-line no-restricted-syntax */\n const backstageRoot = targetPaths.rootDir;\n const scanner = PluginScanner.create({\n config: options.config,\n logger: options.logger,\n backstageRoot,\n preferAlpha: options.preferAlpha,\n });\n const scannedPlugins = (await scanner.scanRoot()).packages;\n await scanner.trackChanges();\n const moduleLoader =\n options.moduleLoader ||\n new CommonJSModuleLoader({ logger: options.logger });\n const manager = new DynamicPluginManager(\n options.logger,\n scannedPlugins,\n moduleLoader,\n );\n\n const scannedPluginManifestsPerRealPath = new Map(\n scannedPlugins.map(p => [\n fs.realpathSync(url.fileURLToPath(p.location)),\n p.manifest,\n ]),\n );\n\n await moduleLoader.bootstrap(\n backstageRoot,\n [...scannedPluginManifestsPerRealPath.keys()],\n scannedPluginManifestsPerRealPath,\n );\n\n scanner.subscribeToRootDirectoryChange(async () => {\n manager._availablePackages = (await scanner.scanRoot()).packages;\n // TODO: do not store _scannedPlugins again, but instead store a diff of the changes\n });\n manager._plugins.push(...(await manager.loadPlugins()));\n\n return manager;\n }\n\n private readonly _plugins: DynamicPlugin[];\n private _availablePackages: ScannedPluginPackage[];\n\n private readonly logger: LoggerService;\n private readonly packages: ScannedPluginPackage[];\n private readonly moduleLoader: ModuleLoader;\n\n private constructor(\n logger: LoggerService,\n packages: ScannedPluginPackage[],\n moduleLoader: ModuleLoader,\n ) {\n this.logger = logger;\n this.packages = packages;\n this.moduleLoader = moduleLoader;\n this._plugins = [];\n this._availablePackages = packages;\n }\n\n get availablePackages(): ScannedPluginPackage[] {\n return this._availablePackages;\n }\n\n addBackendPlugin(plugin: BackendDynamicPlugin): void {\n this._plugins.push(plugin);\n }\n\n private async loadPlugins(): Promise<DynamicPlugin[]> {\n const loadedPlugins: DynamicPlugin[] = [];\n\n for (const scannedPlugin of this.packages) {\n const role = scannedPlugin.manifest.backstage.role;\n const platform = PackageRoles.getRoleInfo(role).platform;\n const isPlugin =\n role.endsWith('-plugin') ||\n role.endsWith('-plugin-module') ||\n role === ('frontend-dynamic-container' as PackageRole);\n\n if (!isPlugin) {\n this.logger.info(\n `skipping dynamic plugin package '${scannedPlugin.manifest.name}' from '${scannedPlugin.location}': incompatible role '${role}'`,\n );\n continue;\n }\n\n switch (platform) {\n case 'node':\n loadedPlugins.push(await this.loadBackendPlugin(scannedPlugin));\n break;\n\n case 'web':\n loadedPlugins.push({\n name: scannedPlugin.manifest.name,\n version: scannedPlugin.manifest.version,\n role: scannedPlugin.manifest.backstage.role,\n platform: 'web',\n // TODO(davidfestal): add required front-end plugin information here.\n });\n break;\n\n default:\n this.logger.info(\n `skipping dynamic plugin package '${scannedPlugin.manifest.name}' from '${scannedPlugin.location}': unrelated platform '${platform}'`,\n );\n }\n }\n return loadedPlugins;\n }\n\n private async loadBackendPlugin(\n plugin: ScannedPluginPackage,\n ): Promise<BackendDynamicPlugin> {\n const dynamicPlugin: BackendDynamicPlugin = {\n name: plugin.manifest.name,\n version: plugin.manifest.version,\n platform: 'node',\n role: plugin.manifest.backstage.role,\n };\n\n const entryPoints: Array<{ location: URL; manifest: string }> = [\n ...(plugin.alphaManifest?.main\n ? [\n {\n location: new URL('alpha', `${plugin.location}/`),\n manifest: plugin.alphaManifest.main,\n },\n ]\n : []),\n {\n location: plugin.location,\n manifest: plugin.manifest.main,\n },\n ];\n\n if (entryPoints.length === 0) {\n throw new Error(\n `Failed to load dynamic backend plugin '${plugin.manifest.name}': no entry points to load`,\n );\n }\n\n for (const [index, { location, manifest }] of entryPoints.entries()) {\n const loadResult = await tryLoadBackendPluginEntry(\n this.moduleLoader,\n location,\n manifest,\n );\n\n if (loadResult.installer) {\n dynamicPlugin.installer = loadResult.installer;\n this.logger.info(\n `loaded dynamic backend plugin '${plugin.manifest.name}' from '${location}'`,\n );\n return dynamicPlugin;\n }\n\n if (!loadResult.error && index < entryPoints.length - 1) {\n continue;\n }\n\n if (loadResult.error) {\n dynamicPlugin.failure = `${loadResult.error.name}: ${loadResult.error.message}`;\n this.logger.error(\n `an error occurred while loading dynamic backend plugin '${plugin.manifest.name}' from '${location}'`,\n loadResult.error,\n );\n return dynamicPlugin;\n }\n\n dynamicPlugin.failure = `the module should either export a 'BackendFeature' or 'BackendFeatureFactory' as default export, or export a 'const dynamicPluginInstaller: BackendDynamicPluginInstaller' field as dynamic loading entrypoint.`;\n this.logger.error(\n `dynamic backend plugin '${plugin.manifest.name}' could not be loaded from '${location}': ${dynamicPlugin.failure}`,\n );\n return dynamicPlugin;\n }\n\n return dynamicPlugin;\n }\n\n backendPlugins(options?: {\n includeFailed?: boolean;\n }): BackendDynamicPlugin[] {\n return this.plugins(options).filter(\n (p): p is BackendDynamicPlugin => p.platform === 'node',\n );\n }\n\n frontendPlugins(options?: {\n includeFailed?: boolean;\n }): FrontendDynamicPlugin[] {\n return this.plugins(options).filter(\n (p): p is FrontendDynamicPlugin => p.platform === 'web',\n );\n }\n\n plugins(options?: { includeFailed?: boolean }): DynamicPlugin[] {\n return this._plugins.filter(p => options?.includeFailed || !p.failure);\n }\n\n getScannedPackage(plugin: DynamicPlugin): ScannedPluginPackage {\n const pkg = this.packages.find(\n p =>\n p.manifest.name === plugin.name &&\n p.manifest.version === plugin.version,\n );\n if (pkg === undefined) {\n throw new Error(\n `The scanned package of a dynamic plugin should always be available: ${plugin.name}/${plugin.version}`,\n );\n }\n return pkg;\n }\n}\n\n/**\n * @public\n */\nexport const dynamicPluginsServiceRef = createServiceRef<DynamicPluginProvider>(\n {\n id: 'core.dynamicplugins',\n scope: 'root',\n },\n);\n\n/**\n * @public\n */\nexport interface DynamicPluginsFactoryOptions {\n moduleLoader?(logger: LoggerService): ModuleLoader | Promise<ModuleLoader>;\n}\n\n/**\n * @public\n * @deprecated Use {@link dynamicPluginsFeatureLoader} instead, which gathers all services and features required for dynamic plugins.\n */\nexport const dynamicPluginsServiceFactoryWithOptions = (\n options?: DynamicPluginsFactoryOptions,\n) =>\n createServiceFactory({\n service: dynamicPluginsServiceRef,\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.rootLogger,\n },\n async factory({ config, logger }) {\n return await DynamicPluginManager.create({\n config,\n logger,\n preferAlpha: true,\n moduleLoader: await options?.moduleLoader?.(logger),\n });\n },\n });\n\n/**\n * @public\n * @deprecated Use {@link dynamicPluginsFeatureLoader} instead, which gathers all services and features required for dynamic plugins.\n */\nexport const dynamicPluginsServiceFactory = Object.assign(\n dynamicPluginsServiceFactoryWithOptions,\n dynamicPluginsServiceFactoryWithOptions(),\n);\n\nclass DynamicPluginsEnabledFeatureDiscoveryService {\n private readonly dynamicPlugins: DynamicPluginProvider;\n\n constructor(dynamicPlugins: DynamicPluginProvider) {\n this.dynamicPlugins = dynamicPlugins;\n }\n\n async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {\n return {\n features: this.dynamicPlugins\n .backendPlugins()\n .flatMap((plugin): BackendFeature[] => {\n if (plugin.installer?.kind === 'new') {\n const installed = plugin.installer.install();\n if (Array.isArray(installed)) {\n return installed;\n }\n return [installed];\n }\n return [];\n }),\n };\n }\n}\n\n/**\n * @public\n * @deprecated Use {@link dynamicPluginsFeatureLoader} instead, which gathers all services and features required for dynamic plugins.\n */\nexport const dynamicPluginsFeatureDiscoveryLoader = createBackendFeatureLoader({\n deps: {\n dynamicPlugins: dynamicPluginsServiceRef,\n },\n async loader({ dynamicPlugins }) {\n const service = new DynamicPluginsEnabledFeatureDiscoveryService(\n dynamicPlugins,\n );\n const { features } = await service.getBackendFeatures();\n return features;\n },\n});\n\nasync function tryLoadBackendPluginEntry(\n moduleLoader: ModuleLoader,\n location: URL,\n manifest: string,\n): Promise<{\n installer?: BackendDynamicPlugin['installer'];\n error?: Error;\n}> {\n try {\n const packagePath = path.resolve(url.fileURLToPath(location), manifest);\n const pluginModule = await moduleLoader.load(packagePath);\n return { installer: resolveInstallerFromModule(pluginModule) };\n } catch (error) {\n return { error: toError(error) };\n }\n}\n\nfunction resolveInstallerFromModule(\n pluginModule: Record<string, unknown>,\n): BackendDynamicPlugin['installer'] | undefined {\n if (isBackendFeature(pluginModule.default)) {\n return {\n kind: 'new',\n install: () => pluginModule.default as BackendFeature,\n };\n }\n if (isBackendFeatureFactory(pluginModule.default)) {\n return {\n kind: 'new',\n install: pluginModule.default as () => BackendFeature,\n };\n }\n if (isBackendDynamicPluginInstaller(pluginModule.dynamicPluginInstaller)) {\n return pluginModule.dynamicPluginInstaller;\n }\n return undefined;\n}\n\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n (typeof value === 'object' || typeof value === 'function') &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\nfunction isBackendFeatureFactory(\n value: unknown,\n): value is () => BackendFeature {\n return (\n !!value &&\n typeof value === 'function' &&\n (value as any).$$type === '@backstage/BackendFeatureFactory'\n );\n}\n"],"names":["targetPaths","PluginScanner","CommonJSModuleLoader","fs","url","PackageRoles","createServiceRef","createServiceFactory","coreServices","createBackendFeatureLoader","path","toError","isBackendDynamicPluginInstaller"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/backend-dynamic-feature-service",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Backstage dynamic feature service",
5
5
  "backstage": {
6
6
  "role": "node-library"
@@ -52,23 +52,23 @@
52
52
  "test": "backstage-cli package test"
53
53
  },
54
54
  "dependencies": {
55
- "@backstage/backend-defaults": "^0.17.1",
56
- "@backstage/backend-openapi-utils": "^0.6.9",
57
- "@backstage/backend-plugin-api": "^1.9.1",
55
+ "@backstage/backend-defaults": "^0.17.3",
56
+ "@backstage/backend-openapi-utils": "^0.6.10",
57
+ "@backstage/backend-plugin-api": "^1.9.2",
58
58
  "@backstage/cli-common": "^0.2.2",
59
- "@backstage/cli-node": "^0.3.2",
59
+ "@backstage/cli-node": "^0.3.3",
60
60
  "@backstage/config": "^1.3.8",
61
- "@backstage/config-loader": "^1.10.11",
61
+ "@backstage/config-loader": "^1.10.12",
62
62
  "@backstage/errors": "^1.3.1",
63
- "@backstage/plugin-app-node": "^0.1.45",
64
- "@backstage/plugin-auth-node": "^0.7.1",
65
- "@backstage/plugin-catalog-backend": "^3.7.0",
66
- "@backstage/plugin-events-backend": "^0.6.2",
67
- "@backstage/plugin-events-node": "^0.4.22",
63
+ "@backstage/plugin-app-node": "^0.1.46",
64
+ "@backstage/plugin-auth-node": "^0.7.2",
65
+ "@backstage/plugin-catalog-backend": "^3.8.0",
66
+ "@backstage/plugin-events-backend": "^0.6.3",
67
+ "@backstage/plugin-events-node": "^0.4.23",
68
68
  "@backstage/plugin-permission-common": "^0.9.9",
69
- "@backstage/plugin-permission-node": "^0.11.0",
70
- "@backstage/plugin-scaffolder-node": "^0.13.3",
71
- "@backstage/plugin-search-backend-node": "^1.4.4",
69
+ "@backstage/plugin-permission-node": "^0.11.1",
70
+ "@backstage/plugin-scaffolder-node": "^0.13.4",
71
+ "@backstage/plugin-search-backend-node": "^1.4.5",
72
72
  "@backstage/plugin-search-common": "^1.2.24",
73
73
  "@backstage/types": "^1.2.2",
74
74
  "@manypkg/get-packages": "^1.1.3",
@@ -81,11 +81,11 @@
81
81
  "winston": "^3.2.1"
82
82
  },
83
83
  "devDependencies": {
84
- "@backstage/backend-app-api": "^1.7.0",
85
- "@backstage/backend-test-utils": "^1.11.3",
86
- "@backstage/cli": "^0.36.2",
87
- "@backstage/plugin-app-backend": "^0.5.14",
88
- "@backstage/repo-tools": "^0.17.2",
84
+ "@backstage/backend-app-api": "^1.7.1",
85
+ "@backstage/backend-test-utils": "^1.11.4",
86
+ "@backstage/cli": "^0.36.3",
87
+ "@backstage/plugin-app-backend": "^0.5.15",
88
+ "@backstage/repo-tools": "^0.17.3",
89
89
  "@types/express": "^4.17.6",
90
90
  "triple-beam": "^1.4.1",
91
91
  "wait-for-expect": "^4.0.0"