@backstage/backend-dynamic-feature-service 0.7.9 → 0.7.10-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @backstage/backend-dynamic-feature-service
2
2
 
3
+ ## 0.7.10-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 70fc178: Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`.
8
+ - Updated dependencies
9
+ - @backstage/cli-common@0.2.0-next.0
10
+ - @backstage/cli-node@0.2.19-next.0
11
+ - @backstage/backend-defaults@0.15.3-next.0
12
+ - @backstage/plugin-catalog-backend@3.5.0-next.0
13
+ - @backstage/config-loader@1.10.9-next.0
14
+ - @backstage/backend-plugin-api@1.7.1-next.0
15
+ - @backstage/backend-openapi-utils@0.6.7-next.0
16
+ - @backstage/config@1.3.6
17
+ - @backstage/errors@1.2.7
18
+ - @backstage/types@1.2.2
19
+ - @backstage/plugin-app-node@0.1.43-next.0
20
+ - @backstage/plugin-auth-node@0.6.14-next.0
21
+ - @backstage/plugin-events-backend@0.5.12-next.0
22
+ - @backstage/plugin-events-node@0.4.20-next.0
23
+ - @backstage/plugin-permission-common@0.9.6
24
+ - @backstage/plugin-permission-node@0.10.11-next.0
25
+ - @backstage/plugin-scaffolder-node@0.12.6-next.0
26
+ - @backstage/plugin-search-backend-node@1.4.2-next.0
27
+ - @backstage/plugin-search-common@1.2.22
28
+
3
29
  ## 0.7.9
4
30
 
5
31
  ### Patch Changes
@@ -32,7 +32,7 @@ var fs__namespace = /*#__PURE__*/_interopNamespaceCompat(fs);
32
32
 
33
33
  class DynamicPluginManager {
34
34
  static async create(options) {
35
- const backstageRoot = cliCommon.findPaths(__dirname).targetRoot;
35
+ const backstageRoot = cliCommon.targetPaths.rootDir;
36
36
  const scanner = pluginScanner.PluginScanner.create({
37
37
  config: options.config,
38
38
  logger: options.logger,
@@ -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 { findPaths } 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 = findPaths(__dirname).targetRoot;\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":["findPaths","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 {\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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"}
@@ -57,7 +57,7 @@ const dynamicPluginsSchemasServiceFactoryWithOptions = (options) => backendPlugi
57
57
  config,
58
58
  logger,
59
59
  // eslint-disable-next-line no-restricted-syntax
60
- backstageRoot: cliCommon.findPaths(__dirname).targetRoot,
60
+ backstageRoot: cliCommon.targetPaths.rootDir,
61
61
  preferAlpha: true
62
62
  });
63
63
  const { packages } = await scanner.scanRoot();
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.cjs.js","sources":["../../src/schemas/schemas.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 { ScannedPluginPackage } from '../scanner/types';\nimport {\n coreServices,\n createServiceFactory,\n createServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { findPaths } from '@backstage/cli-common';\n\nimport fs from 'fs-extra';\nimport * as path from 'node:path';\nimport * as url from 'node:url';\nimport { isEmpty } from 'lodash';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport { PluginScanner } from '../scanner/plugin-scanner';\nimport {\n ConfigSchema,\n loadConfigSchema,\n mergeConfigSchemas,\n} from '@backstage/config-loader';\nimport { dynamicPluginsFeatureLoader } from '../features';\n\n/**\n *\n * @public\n * */\nexport interface DynamicPluginsSchemasService {\n addDynamicPluginsSchemas(configSchema: ConfigSchema): Promise<{\n schema: ConfigSchema;\n }>;\n}\n\n/**\n * A service that provides the config schemas of scanned dynamic plugins.\n *\n * @public\n */\nexport const dynamicPluginsSchemasServiceRef =\n createServiceRef<DynamicPluginsSchemasService>({\n id: 'core.dynamicplugins.schemas',\n scope: 'root',\n });\n\n/**\n * @public\n */\nexport interface DynamicPluginsSchemasOptions {\n /**\n * Function that returns the path to the Json schema file for a given scanned plugin package.\n * The path is either absolute, or relative to the plugin package root directory.\n *\n * Default behavior is to look for the `dist/configSchema.json` relative path.\n *\n * @param pluginPackage - The scanned plugin package.\n * @returns the absolute or plugin-relative path to the Json schema file.\n */\n schemaLocator?: (pluginPackage: ScannedPluginPackage) => string;\n}\n\nconst dynamicPluginsSchemasServiceFactoryWithOptions = (\n options?: DynamicPluginsSchemasOptions,\n) =>\n createServiceFactory({\n service: dynamicPluginsSchemasServiceRef,\n deps: {\n config: coreServices.rootConfig,\n },\n factory({ config }) {\n let additionalSchemas: { [context: string]: JsonObject } | undefined;\n\n return {\n async addDynamicPluginsSchemas(configSchema: ConfigSchema): Promise<{\n schema: ConfigSchema;\n }> {\n if (!additionalSchemas) {\n const logger = {\n ...console,\n child() {\n return this;\n },\n };\n\n const scanner = PluginScanner.create({\n config,\n logger,\n // eslint-disable-next-line no-restricted-syntax\n backstageRoot: findPaths(__dirname).targetRoot,\n preferAlpha: true,\n });\n\n const { packages } = await scanner.scanRoot();\n\n additionalSchemas = await gatherDynamicPluginsSchemas(\n packages,\n logger,\n options?.schemaLocator,\n );\n }\n\n const serialized = configSchema.serialize();\n if (serialized?.backstageConfigSchemaVersion !== 1) {\n throw new Error(\n 'Serialized configuration schema is invalid or has an invalid version number',\n );\n }\n const schemas = serialized.schemas as {\n value: JsonObject;\n path: string;\n }[];\n\n schemas.push(\n ...Object.keys(additionalSchemas).map(context => {\n return {\n path: context,\n value: additionalSchemas![context],\n };\n }),\n );\n serialized.schemas = schemas;\n return {\n schema: await loadConfigSchema({\n serialized,\n }),\n };\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 dynamicPluginsSchemasServiceFactory = Object.assign(\n dynamicPluginsSchemasServiceFactoryWithOptions,\n dynamicPluginsSchemasServiceFactoryWithOptions(),\n);\n\n/** @internal */\nasync function gatherDynamicPluginsSchemas(\n packages: ScannedPluginPackage[],\n logger: LoggerService,\n schemaLocator: (pluginPackage: ScannedPluginPackage) => string = () =>\n path.join('dist', '.config-schema.json'),\n): Promise<{ [context: string]: JsonObject }> {\n const allSchemas: { [context: string]: JsonObject } = {};\n\n for (const pluginPackage of packages) {\n let schemaLocation = schemaLocator(pluginPackage);\n\n if (!path.isAbsolute(schemaLocation)) {\n const pluginLocation = url.fileURLToPath(pluginPackage.location);\n schemaLocation = path.resolve(pluginLocation, schemaLocation);\n }\n\n if (!(await fs.pathExists(schemaLocation))) {\n continue;\n }\n\n let serialized = await fs.readJson(schemaLocation);\n if (!serialized) {\n continue;\n }\n\n if (isEmpty(serialized)) {\n continue;\n }\n\n if (serialized?.backstageConfigSchemaVersion === 1) {\n serialized = mergeConfigSchemas(\n (serialized?.schemas as JsonObject[]).map(_ => _.value as any),\n );\n }\n\n if (!serialized?.$schema || serialized?.type !== 'object') {\n logger.error(\n `Serialized configuration schema is invalid for plugin ${pluginPackage.manifest.name}`,\n );\n continue;\n }\n\n allSchemas[schemaLocation] = serialized;\n }\n\n return allSchemas;\n}\n"],"names":["createServiceRef","createServiceFactory","coreServices","PluginScanner","findPaths","loadConfigSchema","path","url","fs","isEmpty","mergeConfigSchemas"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDO,MAAM,kCACXA,iCAAA,CAA+C;AAAA,EAC7C,EAAA,EAAI,6BAAA;AAAA,EACJ,KAAA,EAAO;AACT,CAAC;AAkBH,MAAM,8CAAA,GAAiD,CACrD,OAAA,KAEAC,qCAAA,CAAqB;AAAA,EACnB,OAAA,EAAS,+BAAA;AAAA,EACT,IAAA,EAAM;AAAA,IACJ,QAAQC,6BAAA,CAAa;AAAA,GACvB;AAAA,EACA,OAAA,CAAQ,EAAE,MAAA,EAAO,EAAG;AAClB,IAAA,IAAI,iBAAA;AAEJ,IAAA,OAAO;AAAA,MACL,MAAM,yBAAyB,YAAA,EAE5B;AACD,QAAA,IAAI,CAAC,iBAAA,EAAmB;AACtB,UAAA,MAAM,MAAA,GAAS;AAAA,YACb,GAAG,OAAA;AAAA,YACH,KAAA,GAAQ;AACN,cAAA,OAAO,IAAA;AAAA,YACT;AAAA,WACF;AAEA,UAAA,MAAM,OAAA,GAAUC,4BAAc,MAAA,CAAO;AAAA,YACnC,MAAA;AAAA,YACA,MAAA;AAAA;AAAA,YAEA,aAAA,EAAeC,mBAAA,CAAU,SAAS,CAAA,CAAE,UAAA;AAAA,YACpC,WAAA,EAAa;AAAA,WACd,CAAA;AAED,UAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,QAAQ,QAAA,EAAS;AAE5C,UAAA,iBAAA,GAAoB,MAAM,2BAAA;AAAA,YACxB,QAAA;AAAA,YACA,MAAA;AAAA,YACA,OAAA,EAAS;AAAA,WACX;AAAA,QACF;AAEA,QAAA,MAAM,UAAA,GAAa,aAAa,SAAA,EAAU;AAC1C,QAAA,IAAI,UAAA,EAAY,iCAAiC,CAAA,EAAG;AAClD,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA,QACF;AACA,QAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAK3B,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,GAAG,MAAA,CAAO,IAAA,CAAK,iBAAiB,CAAA,CAAE,IAAI,CAAA,OAAA,KAAW;AAC/C,YAAA,OAAO;AAAA,cACL,IAAA,EAAM,OAAA;AAAA,cACN,KAAA,EAAO,kBAAmB,OAAO;AAAA,aACnC;AAAA,UACF,CAAC;AAAA,SACH;AACA,QAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ,MAAMC,6BAAA,CAAiB;AAAA,YAC7B;AAAA,WACD;AAAA,SACH;AAAA,MACF;AAAA,KACF;AAAA,EACF;AACF,CAAC,CAAA;AAMI,MAAM,sCAAsC,MAAA,CAAO,MAAA;AAAA,EACxD,8CAAA;AAAA,EACA,8CAAA;AACF;AAGA,eAAe,2BAAA,CACb,UACA,MAAA,EACA,aAAA,GAAiE,MAC/DC,eAAA,CAAK,IAAA,CAAK,MAAA,EAAQ,qBAAqB,CAAA,EACG;AAC5C,EAAA,MAAM,aAAgD,EAAC;AAEvD,EAAA,KAAA,MAAW,iBAAiB,QAAA,EAAU;AACpC,IAAA,IAAI,cAAA,GAAiB,cAAc,aAAa,CAAA;AAEhD,IAAA,IAAI,CAACA,eAAA,CAAK,UAAA,CAAW,cAAc,CAAA,EAAG;AACpC,MAAA,MAAM,cAAA,GAAiBC,cAAA,CAAI,aAAA,CAAc,aAAA,CAAc,QAAQ,CAAA;AAC/D,MAAA,cAAA,GAAiBD,eAAA,CAAK,OAAA,CAAQ,cAAA,EAAgB,cAAc,CAAA;AAAA,IAC9D;AAEA,IAAA,IAAI,CAAE,MAAME,mBAAA,CAAG,UAAA,CAAW,cAAc,CAAA,EAAI;AAC1C,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,UAAA,GAAa,MAAMA,mBAAA,CAAG,QAAA,CAAS,cAAc,CAAA;AACjD,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA;AAAA,IACF;AAEA,IAAA,IAAIC,cAAA,CAAQ,UAAU,CAAA,EAAG;AACvB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,UAAA,EAAY,iCAAiC,CAAA,EAAG;AAClD,MAAA,UAAA,GAAaC,+BAAA;AAAA,QAAA,CACV,UAAA,EAAY,OAAA,EAAyB,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,KAAY;AAAA,OAC/D;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,UAAA,EAAY,OAAA,IAAW,UAAA,EAAY,SAAS,QAAA,EAAU;AACzD,MAAA,MAAA,CAAO,KAAA;AAAA,QACL,CAAA,sDAAA,EAAyD,aAAA,CAAc,QAAA,CAAS,IAAI,CAAA;AAAA,OACtF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,UAAA,CAAW,cAAc,CAAA,GAAI,UAAA;AAAA,EAC/B;AAEA,EAAA,OAAO,UAAA;AACT;;;;;"}
1
+ {"version":3,"file":"schemas.cjs.js","sources":["../../src/schemas/schemas.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 { ScannedPluginPackage } from '../scanner/types';\nimport {\n coreServices,\n createServiceFactory,\n createServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { targetPaths } from '@backstage/cli-common';\n\nimport fs from 'fs-extra';\nimport * as path from 'node:path';\nimport * as url from 'node:url';\nimport { isEmpty } from 'lodash';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport { PluginScanner } from '../scanner/plugin-scanner';\nimport {\n ConfigSchema,\n loadConfigSchema,\n mergeConfigSchemas,\n} from '@backstage/config-loader';\nimport { dynamicPluginsFeatureLoader } from '../features';\n\n/**\n *\n * @public\n * */\nexport interface DynamicPluginsSchemasService {\n addDynamicPluginsSchemas(configSchema: ConfigSchema): Promise<{\n schema: ConfigSchema;\n }>;\n}\n\n/**\n * A service that provides the config schemas of scanned dynamic plugins.\n *\n * @public\n */\nexport const dynamicPluginsSchemasServiceRef =\n createServiceRef<DynamicPluginsSchemasService>({\n id: 'core.dynamicplugins.schemas',\n scope: 'root',\n });\n\n/**\n * @public\n */\nexport interface DynamicPluginsSchemasOptions {\n /**\n * Function that returns the path to the Json schema file for a given scanned plugin package.\n * The path is either absolute, or relative to the plugin package root directory.\n *\n * Default behavior is to look for the `dist/configSchema.json` relative path.\n *\n * @param pluginPackage - The scanned plugin package.\n * @returns the absolute or plugin-relative path to the Json schema file.\n */\n schemaLocator?: (pluginPackage: ScannedPluginPackage) => string;\n}\n\nconst dynamicPluginsSchemasServiceFactoryWithOptions = (\n options?: DynamicPluginsSchemasOptions,\n) =>\n createServiceFactory({\n service: dynamicPluginsSchemasServiceRef,\n deps: {\n config: coreServices.rootConfig,\n },\n factory({ config }) {\n let additionalSchemas: { [context: string]: JsonObject } | undefined;\n\n return {\n async addDynamicPluginsSchemas(configSchema: ConfigSchema): Promise<{\n schema: ConfigSchema;\n }> {\n if (!additionalSchemas) {\n const logger = {\n ...console,\n child() {\n return this;\n },\n };\n\n const scanner = PluginScanner.create({\n config,\n logger,\n // eslint-disable-next-line no-restricted-syntax\n backstageRoot: targetPaths.rootDir,\n preferAlpha: true,\n });\n\n const { packages } = await scanner.scanRoot();\n\n additionalSchemas = await gatherDynamicPluginsSchemas(\n packages,\n logger,\n options?.schemaLocator,\n );\n }\n\n const serialized = configSchema.serialize();\n if (serialized?.backstageConfigSchemaVersion !== 1) {\n throw new Error(\n 'Serialized configuration schema is invalid or has an invalid version number',\n );\n }\n const schemas = serialized.schemas as {\n value: JsonObject;\n path: string;\n }[];\n\n schemas.push(\n ...Object.keys(additionalSchemas).map(context => {\n return {\n path: context,\n value: additionalSchemas![context],\n };\n }),\n );\n serialized.schemas = schemas;\n return {\n schema: await loadConfigSchema({\n serialized,\n }),\n };\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 dynamicPluginsSchemasServiceFactory = Object.assign(\n dynamicPluginsSchemasServiceFactoryWithOptions,\n dynamicPluginsSchemasServiceFactoryWithOptions(),\n);\n\n/** @internal */\nasync function gatherDynamicPluginsSchemas(\n packages: ScannedPluginPackage[],\n logger: LoggerService,\n schemaLocator: (pluginPackage: ScannedPluginPackage) => string = () =>\n path.join('dist', '.config-schema.json'),\n): Promise<{ [context: string]: JsonObject }> {\n const allSchemas: { [context: string]: JsonObject } = {};\n\n for (const pluginPackage of packages) {\n let schemaLocation = schemaLocator(pluginPackage);\n\n if (!path.isAbsolute(schemaLocation)) {\n const pluginLocation = url.fileURLToPath(pluginPackage.location);\n schemaLocation = path.resolve(pluginLocation, schemaLocation);\n }\n\n if (!(await fs.pathExists(schemaLocation))) {\n continue;\n }\n\n let serialized = await fs.readJson(schemaLocation);\n if (!serialized) {\n continue;\n }\n\n if (isEmpty(serialized)) {\n continue;\n }\n\n if (serialized?.backstageConfigSchemaVersion === 1) {\n serialized = mergeConfigSchemas(\n (serialized?.schemas as JsonObject[]).map(_ => _.value as any),\n );\n }\n\n if (!serialized?.$schema || serialized?.type !== 'object') {\n logger.error(\n `Serialized configuration schema is invalid for plugin ${pluginPackage.manifest.name}`,\n );\n continue;\n }\n\n allSchemas[schemaLocation] = serialized;\n }\n\n return allSchemas;\n}\n"],"names":["createServiceRef","createServiceFactory","coreServices","PluginScanner","targetPaths","loadConfigSchema","path","url","fs","isEmpty","mergeConfigSchemas"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDO,MAAM,kCACXA,iCAAA,CAA+C;AAAA,EAC7C,EAAA,EAAI,6BAAA;AAAA,EACJ,KAAA,EAAO;AACT,CAAC;AAkBH,MAAM,8CAAA,GAAiD,CACrD,OAAA,KAEAC,qCAAA,CAAqB;AAAA,EACnB,OAAA,EAAS,+BAAA;AAAA,EACT,IAAA,EAAM;AAAA,IACJ,QAAQC,6BAAA,CAAa;AAAA,GACvB;AAAA,EACA,OAAA,CAAQ,EAAE,MAAA,EAAO,EAAG;AAClB,IAAA,IAAI,iBAAA;AAEJ,IAAA,OAAO;AAAA,MACL,MAAM,yBAAyB,YAAA,EAE5B;AACD,QAAA,IAAI,CAAC,iBAAA,EAAmB;AACtB,UAAA,MAAM,MAAA,GAAS;AAAA,YACb,GAAG,OAAA;AAAA,YACH,KAAA,GAAQ;AACN,cAAA,OAAO,IAAA;AAAA,YACT;AAAA,WACF;AAEA,UAAA,MAAM,OAAA,GAAUC,4BAAc,MAAA,CAAO;AAAA,YACnC,MAAA;AAAA,YACA,MAAA;AAAA;AAAA,YAEA,eAAeC,qBAAA,CAAY,OAAA;AAAA,YAC3B,WAAA,EAAa;AAAA,WACd,CAAA;AAED,UAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,QAAQ,QAAA,EAAS;AAE5C,UAAA,iBAAA,GAAoB,MAAM,2BAAA;AAAA,YACxB,QAAA;AAAA,YACA,MAAA;AAAA,YACA,OAAA,EAAS;AAAA,WACX;AAAA,QACF;AAEA,QAAA,MAAM,UAAA,GAAa,aAAa,SAAA,EAAU;AAC1C,QAAA,IAAI,UAAA,EAAY,iCAAiC,CAAA,EAAG;AAClD,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA,QACF;AACA,QAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAK3B,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,GAAG,MAAA,CAAO,IAAA,CAAK,iBAAiB,CAAA,CAAE,IAAI,CAAA,OAAA,KAAW;AAC/C,YAAA,OAAO;AAAA,cACL,IAAA,EAAM,OAAA;AAAA,cACN,KAAA,EAAO,kBAAmB,OAAO;AAAA,aACnC;AAAA,UACF,CAAC;AAAA,SACH;AACA,QAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ,MAAMC,6BAAA,CAAiB;AAAA,YAC7B;AAAA,WACD;AAAA,SACH;AAAA,MACF;AAAA,KACF;AAAA,EACF;AACF,CAAC,CAAA;AAMI,MAAM,sCAAsC,MAAA,CAAO,MAAA;AAAA,EACxD,8CAAA;AAAA,EACA,8CAAA;AACF;AAGA,eAAe,2BAAA,CACb,UACA,MAAA,EACA,aAAA,GAAiE,MAC/DC,eAAA,CAAK,IAAA,CAAK,MAAA,EAAQ,qBAAqB,CAAA,EACG;AAC5C,EAAA,MAAM,aAAgD,EAAC;AAEvD,EAAA,KAAA,MAAW,iBAAiB,QAAA,EAAU;AACpC,IAAA,IAAI,cAAA,GAAiB,cAAc,aAAa,CAAA;AAEhD,IAAA,IAAI,CAACA,eAAA,CAAK,UAAA,CAAW,cAAc,CAAA,EAAG;AACpC,MAAA,MAAM,cAAA,GAAiBC,cAAA,CAAI,aAAA,CAAc,aAAA,CAAc,QAAQ,CAAA;AAC/D,MAAA,cAAA,GAAiBD,eAAA,CAAK,OAAA,CAAQ,cAAA,EAAgB,cAAc,CAAA;AAAA,IAC9D;AAEA,IAAA,IAAI,CAAE,MAAME,mBAAA,CAAG,UAAA,CAAW,cAAc,CAAA,EAAI;AAC1C,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,UAAA,GAAa,MAAMA,mBAAA,CAAG,QAAA,CAAS,cAAc,CAAA;AACjD,IAAA,IAAI,CAAC,UAAA,EAAY;AACf,MAAA;AAAA,IACF;AAEA,IAAA,IAAIC,cAAA,CAAQ,UAAU,CAAA,EAAG;AACvB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,UAAA,EAAY,iCAAiC,CAAA,EAAG;AAClD,MAAA,UAAA,GAAaC,+BAAA;AAAA,QAAA,CACV,UAAA,EAAY,OAAA,EAAyB,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,KAAY;AAAA,OAC/D;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,UAAA,EAAY,OAAA,IAAW,UAAA,EAAY,SAAS,QAAA,EAAU;AACzD,MAAA,MAAA,CAAO,KAAA;AAAA,QACL,CAAA,sDAAA,EAAyD,aAAA,CAAc,QAAA,CAAS,IAAI,CAAA;AAAA,OACtF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,UAAA,CAAW,cAAc,CAAA,GAAI,UAAA;AAAA,EAC/B;AAEA,EAAA,OAAO,UAAA;AACT;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/backend-dynamic-feature-service",
3
- "version": "0.7.9",
3
+ "version": "0.7.10-next.0",
4
4
  "description": "Backstage dynamic feature service",
5
5
  "backstage": {
6
6
  "role": "node-library"
@@ -52,25 +52,25 @@
52
52
  "test": "backstage-cli package test"
53
53
  },
54
54
  "dependencies": {
55
- "@backstage/backend-defaults": "^0.15.2",
56
- "@backstage/backend-openapi-utils": "^0.6.6",
57
- "@backstage/backend-plugin-api": "^1.7.0",
58
- "@backstage/cli-common": "^0.1.18",
59
- "@backstage/cli-node": "^0.2.18",
60
- "@backstage/config": "^1.3.6",
61
- "@backstage/config-loader": "^1.10.8",
62
- "@backstage/errors": "^1.2.7",
63
- "@backstage/plugin-app-node": "^0.1.42",
64
- "@backstage/plugin-auth-node": "^0.6.13",
65
- "@backstage/plugin-catalog-backend": "^3.4.0",
66
- "@backstage/plugin-events-backend": "^0.5.11",
67
- "@backstage/plugin-events-node": "^0.4.19",
68
- "@backstage/plugin-permission-common": "^0.9.6",
69
- "@backstage/plugin-permission-node": "^0.10.10",
70
- "@backstage/plugin-scaffolder-node": "^0.12.5",
71
- "@backstage/plugin-search-backend-node": "^1.4.1",
72
- "@backstage/plugin-search-common": "^1.2.22",
73
- "@backstage/types": "^1.2.2",
55
+ "@backstage/backend-defaults": "0.15.3-next.0",
56
+ "@backstage/backend-openapi-utils": "0.6.7-next.0",
57
+ "@backstage/backend-plugin-api": "1.7.1-next.0",
58
+ "@backstage/cli-common": "0.2.0-next.0",
59
+ "@backstage/cli-node": "0.2.19-next.0",
60
+ "@backstage/config": "1.3.6",
61
+ "@backstage/config-loader": "1.10.9-next.0",
62
+ "@backstage/errors": "1.2.7",
63
+ "@backstage/plugin-app-node": "0.1.43-next.0",
64
+ "@backstage/plugin-auth-node": "0.6.14-next.0",
65
+ "@backstage/plugin-catalog-backend": "3.5.0-next.0",
66
+ "@backstage/plugin-events-backend": "0.5.12-next.0",
67
+ "@backstage/plugin-events-node": "0.4.20-next.0",
68
+ "@backstage/plugin-permission-common": "0.9.6",
69
+ "@backstage/plugin-permission-node": "0.10.11-next.0",
70
+ "@backstage/plugin-scaffolder-node": "0.12.6-next.0",
71
+ "@backstage/plugin-search-backend-node": "1.4.2-next.0",
72
+ "@backstage/plugin-search-common": "1.2.22",
73
+ "@backstage/types": "1.2.2",
74
74
  "@manypkg/get-packages": "^1.1.3",
75
75
  "@module-federation/sdk": "^0.21.6",
76
76
  "chokidar": "^3.5.3",
@@ -81,11 +81,11 @@
81
81
  "winston": "^3.2.1"
82
82
  },
83
83
  "devDependencies": {
84
- "@backstage/backend-app-api": "^1.5.0",
85
- "@backstage/backend-test-utils": "^1.11.0",
86
- "@backstage/cli": "^0.35.4",
87
- "@backstage/plugin-app-backend": "^0.5.11",
88
- "@backstage/repo-tools": "^0.16.4",
84
+ "@backstage/backend-app-api": "1.5.1-next.0",
85
+ "@backstage/backend-test-utils": "1.11.1-next.0",
86
+ "@backstage/cli": "0.35.5-next.0",
87
+ "@backstage/plugin-app-backend": "0.5.12-next.0",
88
+ "@backstage/repo-tools": "0.16.6-next.0",
89
89
  "@types/express": "^4.17.6",
90
90
  "triple-beam": "^1.4.1",
91
91
  "wait-for-expect": "^3.0.2"