@backstage/backend-dynamic-feature-service 0.8.8-next.0 → 0.8.8-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
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @backstage/backend-dynamic-feature-service
|
|
2
2
|
|
|
3
|
+
## 0.8.8-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 786a8ea: Corrected the `schemaLocator` documentation to identify `dist/.config-schema.json` as the default dynamic-plugin config-schema path.
|
|
8
|
+
- b9c9dd2: Internal refactor of dynamic backend plugin alpha-to-main entrypoint loading. No intended behavior change.
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
- @backstage/backend-plugin-api@1.11.0-next.1
|
|
11
|
+
- @backstage/backend-defaults@0.18.1-next.1
|
|
12
|
+
- @backstage/backend-openapi-utils@0.7.3-next.1
|
|
13
|
+
- @backstage/plugin-app-node@0.1.50-next.1
|
|
14
|
+
- @backstage/plugin-auth-node@0.7.7-next.1
|
|
15
|
+
- @backstage/plugin-catalog-backend@4.0.1-next.1
|
|
16
|
+
- @backstage/plugin-events-backend@0.6.7-next.1
|
|
17
|
+
- @backstage/plugin-events-node@0.4.27-next.1
|
|
18
|
+
- @backstage/plugin-permission-node@0.11.5-next.1
|
|
19
|
+
- @backstage/plugin-scaffolder-node@0.13.8-next.1
|
|
20
|
+
- @backstage/plugin-search-backend-node@1.4.9-next.1
|
|
21
|
+
|
|
3
22
|
## 0.8.8-next.0
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -196,7 +196,7 @@ interface DynamicPluginsSchemasOptions {
|
|
|
196
196
|
* Function that returns the path to the Json schema file for a given scanned plugin package.
|
|
197
197
|
* The path is either absolute, or relative to the plugin package root directory.
|
|
198
198
|
*
|
|
199
|
-
* Default behavior is to look for the `dist
|
|
199
|
+
* Default behavior is to look for the `dist/.config-schema.json` relative path.
|
|
200
200
|
*
|
|
201
201
|
* @param pluginPackage - The scanned plugin package.
|
|
202
202
|
* @returns the absolute or plugin-relative path to the Json schema file.
|
|
@@ -127,53 +127,46 @@ class DynamicPluginManager {
|
|
|
127
127
|
platform: "node",
|
|
128
128
|
role: plugin.manifest.backstage.role
|
|
129
129
|
};
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
140
|
-
}
|
|
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(
|
|
130
|
+
let loadResult;
|
|
131
|
+
if (plugin.alphaManifest?.main) {
|
|
132
|
+
loadResult = await tryLoadBackendPluginEntry(
|
|
149
133
|
this.moduleLoader,
|
|
150
|
-
location,
|
|
151
|
-
|
|
134
|
+
new URL("alpha", `${plugin.location}/`),
|
|
135
|
+
plugin.alphaManifest.main
|
|
152
136
|
);
|
|
153
|
-
if (loadResult.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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}`;
|
|
165
|
-
this.logger.error(
|
|
166
|
-
`an error occurred while loading dynamic backend plugin '${plugin.manifest.name}' from '${location}'`,
|
|
167
|
-
loadResult.error
|
|
137
|
+
if (loadResult.kind === "no-entrypoint") {
|
|
138
|
+
loadResult = await tryLoadBackendPluginEntry(
|
|
139
|
+
this.moduleLoader,
|
|
140
|
+
plugin.location,
|
|
141
|
+
plugin.manifest.main
|
|
168
142
|
);
|
|
169
|
-
return dynamicPlugin;
|
|
170
143
|
}
|
|
171
|
-
|
|
144
|
+
} else {
|
|
145
|
+
loadResult = await tryLoadBackendPluginEntry(
|
|
146
|
+
this.moduleLoader,
|
|
147
|
+
plugin.location,
|
|
148
|
+
plugin.manifest.main
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
if (loadResult.kind === "success") {
|
|
152
|
+
dynamicPlugin.installer = loadResult.installer;
|
|
153
|
+
this.logger.info(
|
|
154
|
+
`loaded dynamic backend plugin '${plugin.manifest.name}' from '${loadResult.location}'`
|
|
155
|
+
);
|
|
156
|
+
return dynamicPlugin;
|
|
157
|
+
}
|
|
158
|
+
if (loadResult.kind === "error") {
|
|
159
|
+
dynamicPlugin.failure = `${loadResult.error.name}: ${loadResult.error.message}`;
|
|
172
160
|
this.logger.error(
|
|
173
|
-
`dynamic backend plugin '${plugin.manifest.name}'
|
|
161
|
+
`an error occurred while loading dynamic backend plugin '${plugin.manifest.name}' from '${loadResult.location}'`,
|
|
162
|
+
loadResult.error
|
|
174
163
|
);
|
|
175
164
|
return dynamicPlugin;
|
|
176
165
|
}
|
|
166
|
+
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.`;
|
|
167
|
+
this.logger.error(
|
|
168
|
+
`dynamic backend plugin '${plugin.manifest.name}' could not be loaded from '${loadResult.location}': ${dynamicPlugin.failure}`
|
|
169
|
+
);
|
|
177
170
|
return dynamicPlugin;
|
|
178
171
|
}
|
|
179
172
|
backendPlugins(options) {
|
|
@@ -262,9 +255,13 @@ async function tryLoadBackendPluginEntry(moduleLoader, location, manifest) {
|
|
|
262
255
|
try {
|
|
263
256
|
const packagePath = path__default.default.resolve(url__namespace.fileURLToPath(location), manifest);
|
|
264
257
|
const pluginModule = await moduleLoader.load(packagePath);
|
|
265
|
-
|
|
258
|
+
const installer = resolveInstallerFromModule(pluginModule);
|
|
259
|
+
if (installer) {
|
|
260
|
+
return { kind: "success", location, installer };
|
|
261
|
+
}
|
|
262
|
+
return { kind: "no-entrypoint", location };
|
|
266
263
|
} catch (error) {
|
|
267
|
-
return { error: errors.toError(error) };
|
|
264
|
+
return { kind: "error", location, error: errors.toError(error) };
|
|
268
265
|
}
|
|
269
266
|
}
|
|
270
267
|
function resolveInstallerFromModule(pluginModule) {
|
|
@@ -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 { 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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"}
|
|
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 let loadResult: BackendPluginEntryLoadResult;\n\n if (plugin.alphaManifest?.main) {\n loadResult = await tryLoadBackendPluginEntry(\n this.moduleLoader,\n new URL('alpha', `${plugin.location}/`),\n plugin.alphaManifest.main,\n );\n\n if (loadResult.kind === 'no-entrypoint') {\n loadResult = await tryLoadBackendPluginEntry(\n this.moduleLoader,\n plugin.location,\n plugin.manifest.main,\n );\n }\n } else {\n loadResult = await tryLoadBackendPluginEntry(\n this.moduleLoader,\n plugin.location,\n plugin.manifest.main,\n );\n }\n\n if (loadResult.kind === 'success') {\n dynamicPlugin.installer = loadResult.installer;\n this.logger.info(\n `loaded dynamic backend plugin '${plugin.manifest.name}' from '${loadResult.location}'`,\n );\n return dynamicPlugin;\n }\n\n if (loadResult.kind === '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 '${loadResult.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 '${loadResult.location}': ${dynamicPlugin.failure}`,\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\ntype BackendPluginEntryLoadResult =\n | {\n kind: 'success';\n location: URL;\n installer: NonNullable<BackendDynamicPlugin['installer']>;\n }\n | { kind: 'error'; location: URL; error: Error }\n | { kind: 'no-entrypoint'; location: URL };\n\nasync function tryLoadBackendPluginEntry(\n moduleLoader: ModuleLoader,\n location: URL,\n manifest: string,\n): Promise<BackendPluginEntryLoadResult> {\n try {\n const packagePath = path.resolve(url.fileURLToPath(location), manifest);\n const pluginModule = await moduleLoader.load(packagePath);\n const installer = resolveInstallerFromModule(pluginModule);\n if (installer) {\n return { kind: 'success', location, installer };\n }\n return { kind: 'no-entrypoint', location };\n } catch (error) {\n return { kind: 'error', location, 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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"}
|
|
@@ -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 { 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
|
|
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/.config-schema.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.8.8-next.
|
|
3
|
+
"version": "0.8.8-next.1",
|
|
4
4
|
"description": "Backstage dynamic feature service",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "node-library"
|
|
@@ -51,23 +51,23 @@
|
|
|
51
51
|
"test": "backstage-cli package test"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@backstage/backend-defaults": "0.18.1-next.
|
|
55
|
-
"@backstage/backend-openapi-utils": "0.7.3-next.
|
|
56
|
-
"@backstage/backend-plugin-api": "1.
|
|
54
|
+
"@backstage/backend-defaults": "0.18.1-next.1",
|
|
55
|
+
"@backstage/backend-openapi-utils": "0.7.3-next.1",
|
|
56
|
+
"@backstage/backend-plugin-api": "1.11.0-next.1",
|
|
57
57
|
"@backstage/cli-common": "0.3.2-next.0",
|
|
58
58
|
"@backstage/cli-node": "0.3.5-next.0",
|
|
59
59
|
"@backstage/config": "1.3.10-next.0",
|
|
60
60
|
"@backstage/config-loader": "1.11.4-next.0",
|
|
61
61
|
"@backstage/errors": "1.3.2-next.0",
|
|
62
|
-
"@backstage/plugin-app-node": "0.1.50-next.
|
|
63
|
-
"@backstage/plugin-auth-node": "0.7.7-next.
|
|
64
|
-
"@backstage/plugin-catalog-backend": "4.0.1-next.
|
|
65
|
-
"@backstage/plugin-events-backend": "0.6.7-next.
|
|
66
|
-
"@backstage/plugin-events-node": "0.4.27-next.
|
|
62
|
+
"@backstage/plugin-app-node": "0.1.50-next.1",
|
|
63
|
+
"@backstage/plugin-auth-node": "0.7.7-next.1",
|
|
64
|
+
"@backstage/plugin-catalog-backend": "4.0.1-next.1",
|
|
65
|
+
"@backstage/plugin-events-backend": "0.6.7-next.1",
|
|
66
|
+
"@backstage/plugin-events-node": "0.4.27-next.1",
|
|
67
67
|
"@backstage/plugin-permission-common": "0.9.12-next.0",
|
|
68
|
-
"@backstage/plugin-permission-node": "0.11.5-next.
|
|
69
|
-
"@backstage/plugin-scaffolder-node": "0.13.8-next.
|
|
70
|
-
"@backstage/plugin-search-backend-node": "1.4.9-next.
|
|
68
|
+
"@backstage/plugin-permission-node": "0.11.5-next.1",
|
|
69
|
+
"@backstage/plugin-scaffolder-node": "0.13.8-next.1",
|
|
70
|
+
"@backstage/plugin-search-backend-node": "1.4.9-next.1",
|
|
71
71
|
"@backstage/plugin-search-common": "1.2.26-next.0",
|
|
72
72
|
"@backstage/types": "1.2.2",
|
|
73
73
|
"@manypkg/get-packages": "^1.1.3",
|
|
@@ -80,11 +80,11 @@
|
|
|
80
80
|
"winston": "^3.2.1"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
|
-
"@backstage/backend-app-api": "1.
|
|
84
|
-
"@backstage/backend-test-utils": "1.11.8-next.
|
|
83
|
+
"@backstage/backend-app-api": "1.8.0-next.1",
|
|
84
|
+
"@backstage/backend-test-utils": "1.11.8-next.1",
|
|
85
85
|
"@backstage/cli": "0.36.7-next.0",
|
|
86
|
-
"@backstage/plugin-app-backend": "0.5.19-next.
|
|
87
|
-
"@backstage/repo-tools": "0.19.2-next.
|
|
86
|
+
"@backstage/plugin-app-backend": "0.5.19-next.1",
|
|
87
|
+
"@backstage/repo-tools": "0.19.2-next.1",
|
|
88
88
|
"@types/express": "^4.17.6",
|
|
89
89
|
"triple-beam": "^1.4.1",
|
|
90
90
|
"wait-for-expect": "^4.0.0"
|