@backstage/backend-dynamic-feature-service 0.6.2-next.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,58 @@
1
1
  # @backstage/backend-dynamic-feature-service
2
2
 
3
+ ## 0.6.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 3bee3c3: The new package `frontend-dynamic-features-loader` provides a frontend feature loader that dynamically
8
+ loads frontend features based on the new frontend system and exposed as module federation remotes.
9
+ This new frontend feature loader works hand-in-hand with a new server of frontend plugin module federation
10
+ remotes, which is added as part of backend dynamic feature service in package `@backstage/backend-dynamic-feature-service`.
11
+ - Updated dependencies
12
+ - @backstage/backend-defaults@0.9.0
13
+ - @backstage/plugin-catalog-backend@1.32.1
14
+ - @backstage/plugin-scaffolder-node@0.8.1
15
+ - @backstage/backend-plugin-api@1.3.0
16
+ - @backstage/plugin-auth-node@0.6.2
17
+ - @backstage/plugin-events-backend@0.5.1
18
+ - @backstage/plugin-permission-node@0.9.1
19
+ - @backstage/plugin-search-backend-node@1.3.10
20
+ - @backstage/backend-openapi-utils@0.5.2
21
+ - @backstage/cli-common@0.1.15
22
+ - @backstage/cli-node@0.2.13
23
+ - @backstage/config@1.3.2
24
+ - @backstage/config-loader@1.10.0
25
+ - @backstage/errors@1.2.7
26
+ - @backstage/types@1.2.1
27
+ - @backstage/plugin-app-node@0.1.32
28
+ - @backstage/plugin-events-node@0.4.10
29
+ - @backstage/plugin-permission-common@0.8.4
30
+ - @backstage/plugin-search-common@1.2.17
31
+
32
+ ## 0.6.2-next.2
33
+
34
+ ### Patch Changes
35
+
36
+ - Updated dependencies
37
+ - @backstage/plugin-catalog-backend@1.32.1-next.1
38
+ - @backstage/backend-defaults@0.9.0-next.2
39
+ - @backstage/backend-plugin-api@1.2.1
40
+ - @backstage/cli-common@0.1.15
41
+ - @backstage/cli-node@0.2.13
42
+ - @backstage/config@1.3.2
43
+ - @backstage/config-loader@1.10.0
44
+ - @backstage/errors@1.2.7
45
+ - @backstage/types@1.2.1
46
+ - @backstage/plugin-app-node@0.1.31
47
+ - @backstage/plugin-auth-node@0.6.1
48
+ - @backstage/plugin-events-backend@0.5.0
49
+ - @backstage/plugin-events-node@0.4.9
50
+ - @backstage/plugin-permission-common@0.8.4
51
+ - @backstage/plugin-permission-node@0.9.0
52
+ - @backstage/plugin-scaffolder-node@0.8.1-next.1
53
+ - @backstage/plugin-search-backend-node@1.3.9
54
+ - @backstage/plugin-search-common@1.2.17
55
+
3
56
  ## 0.6.2-next.1
4
57
 
5
58
  ### Patch Changes
@@ -6,6 +6,7 @@ var pluginScanner = require('../scanner/plugin-scanner.cjs.js');
6
6
  var schemas = require('../schemas/schemas.cjs.js');
7
7
  var frontend = require('../schemas/frontend.cjs.js');
8
8
  var rootLogger = require('../schemas/rootLogger.cjs.js');
9
+ var frontendRemotesServer = require('../server/frontendRemotesServer.cjs.js');
9
10
 
10
11
  const dynamicPluginsFeatureLoaderWithOptions = (options) => backendPluginApi.createBackendFeatureLoader({
11
12
  deps: {
@@ -25,6 +26,7 @@ const dynamicPluginsFeatureLoaderWithOptions = (options) => backendPluginApi.cre
25
26
  yield* [
26
27
  rootLogger.dynamicPluginsRootLoggerServiceFactory(rootLoggerOptions),
27
28
  frontend.dynamicPluginsFrontendSchemas,
29
+ frontendRemotesServer.frontendRemotesServerService,
28
30
  pluginManager.dynamicPluginsFeatureDiscoveryLoader
29
31
  ];
30
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"features.cjs.js","sources":["../../src/features/features.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 {\n coreServices,\n createBackendFeatureLoader,\n} from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport {\n DynamicPluginsFactoryOptions,\n dynamicPluginsFeatureDiscoveryLoader,\n dynamicPluginsServiceFactory,\n} from '../manager';\nimport { configKey } from '../scanner/plugin-scanner';\nimport {\n DynamicPluginsRootLoggerFactoryOptions,\n DynamicPluginsSchemasOptions,\n dynamicPluginsFrontendSchemas,\n dynamicPluginsRootLoggerServiceFactory,\n dynamicPluginsSchemasServiceFactory,\n} from '../schemas';\n\n/**\n * @public\n */\nexport type DynamicPluginsFeatureLoaderOptions = DynamicPluginsFactoryOptions &\n DynamicPluginsSchemasOptions & {\n logger?: (config?: Config) => DynamicPluginsRootLoggerFactoryOptions;\n };\n\nconst dynamicPluginsFeatureLoaderWithOptions = (\n options?: DynamicPluginsFeatureLoaderOptions,\n) =>\n createBackendFeatureLoader({\n deps: {\n config: coreServices.rootConfig,\n },\n *loader({ config }) {\n const dynamicPluginsEnabled = config.has(configKey);\n\n let rootLoggerOptions: DynamicPluginsRootLoggerFactoryOptions = {};\n if (options?.logger) {\n rootLoggerOptions = options.logger(config);\n }\n\n yield* [\n dynamicPluginsSchemasServiceFactory(options),\n dynamicPluginsServiceFactory(options),\n ];\n if (dynamicPluginsEnabled) {\n yield* [\n dynamicPluginsRootLoggerServiceFactory(rootLoggerOptions),\n dynamicPluginsFrontendSchemas,\n dynamicPluginsFeatureDiscoveryLoader,\n ];\n }\n },\n });\n\n/**\n * A backend feature loader that fully enable backend dynamic plugins.\n * More precisely it:\n * - adds the dynamic plugins root service (typically depended upon by plugins),\n * - adds additional required features to allow supporting dynamic plugins config schemas\n * in the frontend application and the backend root logger,\n * - uses the dynamic plugins service to discover and expose dynamic plugins as features.\n *\n * @public\n *\n * @example\n * Using the `dynamicPluginsFeatureLoader` loader in a backend instance:\n * ```ts\n * //...\n * import { createBackend } from '@backstage/backend-defaults';\n * import { dynamicPluginsFeatureLoader } from '@backstage/backend-dynamic-feature-service';\n *\n * const backend = createBackend();\n * backend.add(dynamicPluginsFeatureLoader);\n * //...\n * backend.start();\n * ```\n *\n * @example\n * Passing options to the `dynamicPluginsFeatureLoader` loader in a backend instance:\n * ```ts\n * //...\n * import { createBackend } from '@backstage/backend-defaults';\n * import { dynamicPluginsFeatureLoader } from '@backstage/backend-dynamic-feature-service';\n * import { myCustomModuleLoader } from './myCustomModuleLoader';\n * import { myCustomSchemaLocator } from './myCustomSchemaLocator';\n * import { myConfiguredLoggerOptions } from './myConfiguredLoggerOptions';\n *\n * const backend = createBackend();\n * backend.add(dynamicPluginsFeatureLoader({\n * moduleLoader: myCustomModuleLoader,\n * schemaLocator: myCustomSchemaLocator,\n * logger: (config) => myConfiguredLoggerOptions(config),\n * }));\n * //...\n * backend.start();\n * ```\n */\nexport const dynamicPluginsFeatureLoader = Object.assign(\n dynamicPluginsFeatureLoaderWithOptions,\n dynamicPluginsFeatureLoaderWithOptions(),\n);\n"],"names":["createBackendFeatureLoader","coreServices","configKey","dynamicPluginsSchemasServiceFactory","dynamicPluginsServiceFactory","dynamicPluginsRootLoggerServiceFactory","dynamicPluginsFrontendSchemas","dynamicPluginsFeatureDiscoveryLoader"],"mappings":";;;;;;;;;AA2CA,MAAM,sCAAA,GAAyC,CAC7C,OAAA,KAEAA,2CAA2B,CAAA;AAAA,EACzB,IAAM,EAAA;AAAA,IACJ,QAAQC,6BAAa,CAAA;AAAA,GACvB;AAAA,EACA,CAAC,MAAA,CAAO,EAAE,MAAA,EAAU,EAAA;AAClB,IAAM,MAAA,qBAAA,GAAwB,MAAO,CAAA,GAAA,CAAIC,uBAAS,CAAA;AAElD,IAAA,IAAI,oBAA4D,EAAC;AACjE,IAAA,IAAI,SAAS,MAAQ,EAAA;AACnB,MAAoB,iBAAA,GAAA,OAAA,CAAQ,OAAO,MAAM,CAAA;AAAA;AAG3C,IAAO,OAAA;AAAA,MACLC,4CAAoC,OAAO,CAAA;AAAA,MAC3CC,2CAA6B,OAAO;AAAA,KACtC;AACA,IAAA,IAAI,qBAAuB,EAAA;AACzB,MAAO,OAAA;AAAA,QACLC,kDAAuC,iBAAiB,CAAA;AAAA,QACxDC,sCAAA;AAAA,QACAC;AAAA,OACF;AAAA;AACF;AAEJ,CAAC,CAAA;AA6CI,MAAM,8BAA8B,MAAO,CAAA,MAAA;AAAA,EAChD,sCAAA;AAAA,EACA,sCAAuC;AACzC;;;;"}
1
+ {"version":3,"file":"features.cjs.js","sources":["../../src/features/features.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 {\n coreServices,\n createBackendFeatureLoader,\n} from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport {\n DynamicPluginsFactoryOptions,\n dynamicPluginsFeatureDiscoveryLoader,\n dynamicPluginsServiceFactory,\n} from '../manager';\nimport { configKey } from '../scanner/plugin-scanner';\nimport {\n DynamicPluginsRootLoggerFactoryOptions,\n DynamicPluginsSchemasOptions,\n dynamicPluginsFrontendSchemas,\n dynamicPluginsRootLoggerServiceFactory,\n dynamicPluginsSchemasServiceFactory,\n} from '../schemas';\nimport { frontendRemotesServerService } from '../server/frontendRemotesServer';\n\n/**\n * @public\n */\nexport type DynamicPluginsFeatureLoaderOptions = DynamicPluginsFactoryOptions &\n DynamicPluginsSchemasOptions & {\n logger?: (config?: Config) => DynamicPluginsRootLoggerFactoryOptions;\n };\n\nconst dynamicPluginsFeatureLoaderWithOptions = (\n options?: DynamicPluginsFeatureLoaderOptions,\n) =>\n createBackendFeatureLoader({\n deps: {\n config: coreServices.rootConfig,\n },\n *loader({ config }) {\n const dynamicPluginsEnabled = config.has(configKey);\n\n let rootLoggerOptions: DynamicPluginsRootLoggerFactoryOptions = {};\n if (options?.logger) {\n rootLoggerOptions = options.logger(config);\n }\n\n yield* [\n dynamicPluginsSchemasServiceFactory(options),\n dynamicPluginsServiceFactory(options),\n ];\n if (dynamicPluginsEnabled) {\n yield* [\n dynamicPluginsRootLoggerServiceFactory(rootLoggerOptions),\n dynamicPluginsFrontendSchemas,\n frontendRemotesServerService,\n dynamicPluginsFeatureDiscoveryLoader,\n ];\n }\n },\n });\n\n/**\n * A backend feature loader that fully enable backend dynamic plugins.\n * More precisely it:\n * - adds the dynamic plugins root service (typically depended upon by plugins),\n * - adds additional required features to allow supporting dynamic plugins config schemas\n * in the frontend application and the backend root logger,\n * - uses the dynamic plugins service to discover and expose dynamic plugins as features.\n *\n * @public\n *\n * @example\n * Using the `dynamicPluginsFeatureLoader` loader in a backend instance:\n * ```ts\n * //...\n * import { createBackend } from '@backstage/backend-defaults';\n * import { dynamicPluginsFeatureLoader } from '@backstage/backend-dynamic-feature-service';\n *\n * const backend = createBackend();\n * backend.add(dynamicPluginsFeatureLoader);\n * //...\n * backend.start();\n * ```\n *\n * @example\n * Passing options to the `dynamicPluginsFeatureLoader` loader in a backend instance:\n * ```ts\n * //...\n * import { createBackend } from '@backstage/backend-defaults';\n * import { dynamicPluginsFeatureLoader } from '@backstage/backend-dynamic-feature-service';\n * import { myCustomModuleLoader } from './myCustomModuleLoader';\n * import { myCustomSchemaLocator } from './myCustomSchemaLocator';\n * import { myConfiguredLoggerOptions } from './myConfiguredLoggerOptions';\n *\n * const backend = createBackend();\n * backend.add(dynamicPluginsFeatureLoader({\n * moduleLoader: myCustomModuleLoader,\n * schemaLocator: myCustomSchemaLocator,\n * logger: (config) => myConfiguredLoggerOptions(config),\n * }));\n * //...\n * backend.start();\n * ```\n */\nexport const dynamicPluginsFeatureLoader = Object.assign(\n dynamicPluginsFeatureLoaderWithOptions,\n dynamicPluginsFeatureLoaderWithOptions(),\n);\n"],"names":["createBackendFeatureLoader","coreServices","configKey","dynamicPluginsSchemasServiceFactory","dynamicPluginsServiceFactory","dynamicPluginsRootLoggerServiceFactory","dynamicPluginsFrontendSchemas","frontendRemotesServerService","dynamicPluginsFeatureDiscoveryLoader"],"mappings":";;;;;;;;;;AA4CA,MAAM,sCAAA,GAAyC,CAC7C,OAAA,KAEAA,2CAA2B,CAAA;AAAA,EACzB,IAAM,EAAA;AAAA,IACJ,QAAQC,6BAAa,CAAA;AAAA,GACvB;AAAA,EACA,CAAC,MAAA,CAAO,EAAE,MAAA,EAAU,EAAA;AAClB,IAAM,MAAA,qBAAA,GAAwB,MAAO,CAAA,GAAA,CAAIC,uBAAS,CAAA;AAElD,IAAA,IAAI,oBAA4D,EAAC;AACjE,IAAA,IAAI,SAAS,MAAQ,EAAA;AACnB,MAAoB,iBAAA,GAAA,OAAA,CAAQ,OAAO,MAAM,CAAA;AAAA;AAG3C,IAAO,OAAA;AAAA,MACLC,4CAAoC,OAAO,CAAA;AAAA,MAC3CC,2CAA6B,OAAO;AAAA,KACtC;AACA,IAAA,IAAI,qBAAuB,EAAA;AACzB,MAAO,OAAA;AAAA,QACLC,kDAAuC,iBAAiB,CAAA;AAAA,QACxDC,sCAAA;AAAA,QACAC,kDAAA;AAAA,QACAC;AAAA,OACF;AAAA;AACF;AAEJ,CAAC,CAAA;AA6CI,MAAM,8BAA8B,MAAO,CAAA,MAAA;AAAA,EAChD,sCAAA;AAAA,EACA,sCAAuC;AACzC;;;;"}
package/dist/index.cjs.js CHANGED
@@ -7,6 +7,7 @@ var schemas = require('./schemas/schemas.cjs.js');
7
7
  var frontend = require('./schemas/frontend.cjs.js');
8
8
  var rootLogger = require('./schemas/rootLogger.cjs.js');
9
9
  var features = require('./features/features.cjs.js');
10
+ var frontendRemotesServer = require('./server/frontendRemotesServer.cjs.js');
10
11
 
11
12
 
12
13
 
@@ -21,4 +22,5 @@ exports.dynamicPluginsSchemasServiceFactory = schemas.dynamicPluginsSchemasServi
21
22
  exports.dynamicPluginsFrontendSchemas = frontend.dynamicPluginsFrontendSchemas;
22
23
  exports.dynamicPluginsRootLoggerServiceFactory = rootLogger.dynamicPluginsRootLoggerServiceFactory;
23
24
  exports.dynamicPluginsFeatureLoader = features.dynamicPluginsFeatureLoader;
25
+ exports.dynamicPluginsFrontendServiceRef = frontendRemotesServer.dynamicPluginsFrontendServiceRef;
24
26
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ import { IndexBuilder } from '@backstage/plugin-search-backend-node';
13
13
  import { PermissionPolicy } from '@backstage/plugin-permission-node';
14
14
  import { ConfigSchema } from '@backstage/config-loader';
15
15
  import { WinstonLoggerOptions } from '@backstage/backend-defaults/rootLogger';
16
+ import { JsonObject } from '@backstage/types';
16
17
 
17
18
  /**
18
19
  * @public
@@ -334,4 +335,87 @@ type DynamicPluginsFeatureLoaderOptions = DynamicPluginsFactoryOptions & Dynamic
334
335
  */
335
336
  declare const dynamicPluginsFeatureLoader: ((options?: DynamicPluginsFeatureLoaderOptions) => _backstage_backend_plugin_api.BackendFeature) & _backstage_backend_plugin_api.BackendFeature;
336
337
 
337
- export { type BackendDynamicPlugin, type BackendDynamicPluginInstaller, type BackendPluginProvider, type BaseDynamicPlugin, CommonJSModuleLoader, type CommonJSModuleLoaderOptions, type DynamicPlugin, DynamicPluginManager, type DynamicPluginManagerOptions, type DynamicPluginProvider, type DynamicPluginsFactoryOptions, type DynamicPluginsFeatureLoaderOptions, type DynamicPluginsRootLoggerFactoryOptions, type DynamicPluginsSchemasOptions, type DynamicPluginsSchemasService, type FrontendDynamicPlugin, type FrontendPluginProvider, type LegacyBackendPluginInstaller, type LegacyPluginEnvironment, type ModuleLoader, type NewBackendPluginInstaller, type ScannedPluginManifest, type ScannedPluginPackage, dynamicPluginsFeatureDiscoveryLoader, dynamicPluginsFeatureLoader, dynamicPluginsFrontendSchemas, dynamicPluginsRootLoggerServiceFactory, dynamicPluginsSchemasServiceFactory, dynamicPluginsServiceFactory, dynamicPluginsServiceFactoryWithOptions, dynamicPluginsServiceRef, isBackendDynamicPluginInstaller };
338
+ /**
339
+ * Definition of a frontend plugin Module Federation remote served by the backend
340
+ * @public
341
+ */
342
+ interface RemoteInfo {
343
+ /**
344
+ * Name of the module federation remote
345
+ */
346
+ name: string;
347
+ /**
348
+ * Remote entry, either the remote manifest file, or the remote entry Javascript file.
349
+ */
350
+ entry: string;
351
+ entryGlobalName?: string;
352
+ shareScope?: string;
353
+ type?: RemoteInfoTypeEnum;
354
+ }
355
+ /**
356
+ * @public
357
+ */
358
+ type RemoteInfoTypeEnum = 'var' | 'module' | 'assign' | 'assign-properties' | 'this' | 'window' | 'self' | 'global' | 'commonjs' | 'commonjs2' | 'commonjs-module' | 'commonjs-static' | 'amd' | 'amd-require' | 'umd' | 'umd2' | 'jsonp' | 'system';
359
+
360
+ /**
361
+ *
362
+ * @public
363
+ * */
364
+ type AdditionalRemoteInfo = Omit<RemoteInfo, 'name' | 'entry'>;
365
+ /**
366
+ *
367
+ * @public
368
+ * */
369
+ type FrontendRemoteResolver = {
370
+ /**
371
+ * Relative path to the module federation assets folder from thr root folder of the plugin package.
372
+ * Default value is `dist`.
373
+ */
374
+ assetsPathFromPackage?: string;
375
+ /**
376
+ * File name of the module federation manifest inside the module federation assets folder.
377
+ * Default value is `mf-manifest.json`.
378
+ */
379
+ manifestFileName?: string;
380
+ /**
381
+ * Type of the remote entry returned in the RemoteInfo for this remote.
382
+ * Default value is `manifest`.
383
+ */
384
+ getRemoteEntryType?: (manifestContent: JsonObject) => 'manifest' | 'javascript';
385
+ /**
386
+ * Additional module federation fields, which might be required if the remote entry type is 'javascript'.
387
+ */
388
+ getAdditionaRemoteInfo?: (manifestContent: JsonObject) => AdditionalRemoteInfo;
389
+ /**
390
+ * Overrides the list of exposed modules. By default the exposed modules are read from the manifest file.
391
+ */
392
+ overrideExposedModules?: (exposedModules: string[], manifestContent: JsonObject) => string[];
393
+ /**
394
+ * Customizes the manifest before returning it as the remote entry.
395
+ */
396
+ customizeManifest?: (content: JsonObject) => JsonObject;
397
+ };
398
+ /**
399
+ *
400
+ * @public
401
+ * */
402
+ type FrontendRemoteResolverProvider = {
403
+ for(pluginName: string, pluginPackagePath: string): Partial<FrontendRemoteResolver> | undefined;
404
+ };
405
+ /**
406
+ *
407
+ * @public
408
+ * */
409
+ interface DynamicPluginsFrontendRemotesService {
410
+ setResolverProvider(provider: FrontendRemoteResolverProvider): void;
411
+ }
412
+ /**
413
+ * A service that serves the frontend module federation remotes,
414
+ * and allows a plugin to customize the way remotes are served,
415
+ * by setting a ResolverProvider.
416
+ *
417
+ * @public
418
+ */
419
+ declare const dynamicPluginsFrontendServiceRef: _backstage_backend_plugin_api.ServiceRef<DynamicPluginsFrontendRemotesService, "root", "singleton">;
420
+
421
+ export { type AdditionalRemoteInfo, type BackendDynamicPlugin, type BackendDynamicPluginInstaller, type BackendPluginProvider, type BaseDynamicPlugin, CommonJSModuleLoader, type CommonJSModuleLoaderOptions, type DynamicPlugin, DynamicPluginManager, type DynamicPluginManagerOptions, type DynamicPluginProvider, type DynamicPluginsFactoryOptions, type DynamicPluginsFeatureLoaderOptions, type DynamicPluginsFrontendRemotesService, type DynamicPluginsRootLoggerFactoryOptions, type DynamicPluginsSchemasOptions, type DynamicPluginsSchemasService, type FrontendDynamicPlugin, type FrontendPluginProvider, type FrontendRemoteResolver, type FrontendRemoteResolverProvider, type LegacyBackendPluginInstaller, type LegacyPluginEnvironment, type ModuleLoader, type NewBackendPluginInstaller, type RemoteInfo, type RemoteInfoTypeEnum, type ScannedPluginManifest, type ScannedPluginPackage, dynamicPluginsFeatureDiscoveryLoader, dynamicPluginsFeatureLoader, dynamicPluginsFrontendSchemas, dynamicPluginsFrontendServiceRef, dynamicPluginsRootLoggerServiceFactory, dynamicPluginsSchemasServiceFactory, dynamicPluginsServiceFactory, dynamicPluginsServiceFactoryWithOptions, dynamicPluginsServiceRef, isBackendDynamicPluginInstaller };
@@ -0,0 +1,213 @@
1
+ 'use strict';
2
+
3
+ var backendOpenapiUtils = require('@backstage/backend-openapi-utils');
4
+
5
+ const spec = {
6
+ openapi: "3.0.3",
7
+ info: {
8
+ title: ".backstage/dynamic-features",
9
+ version: "1",
10
+ description: "The Backstage backend plugin that serves the frontend plugins module federation manifests and assets",
11
+ license: {
12
+ name: "Apache-2.0",
13
+ url: "http://www.apache.org/licenses/LICENSE-2.0.html"
14
+ },
15
+ contact: {}
16
+ },
17
+ servers: [
18
+ {
19
+ url: "/"
20
+ }
21
+ ],
22
+ components: {
23
+ examples: {},
24
+ headers: {},
25
+ parameters: {},
26
+ requestBodies: {},
27
+ responses: {
28
+ RemotesResponse: {
29
+ description: "List of Module Federation Remotes exposed by the backend",
30
+ content: {
31
+ "application/json": {
32
+ schema: {
33
+ $ref: "#/components/schemas/Remotes"
34
+ }
35
+ }
36
+ }
37
+ },
38
+ ErrorResponse: {
39
+ description: "An error response from the backend.",
40
+ content: {
41
+ "application/json": {
42
+ schema: {
43
+ $ref: "#/components/schemas/Error"
44
+ }
45
+ }
46
+ }
47
+ }
48
+ },
49
+ schemas: {
50
+ Remotes: {
51
+ type: "array",
52
+ items: {
53
+ $ref: "#/components/schemas/Remote"
54
+ }
55
+ },
56
+ Remote: {
57
+ description: "Definition of a frontend plugin Module Federation remote served by the backend",
58
+ type: "object",
59
+ properties: {
60
+ packageName: {
61
+ description: "Name of the package exposed through this Module Federation remote",
62
+ type: "string"
63
+ },
64
+ remoteInfo: {
65
+ $ref: "#/components/schemas/RemoteInfo"
66
+ },
67
+ exposedModules: {
68
+ description: "Names of modules exposed by this module federation remote",
69
+ type: "array",
70
+ items: {
71
+ type: "string"
72
+ }
73
+ }
74
+ },
75
+ required: ["packageName", "remoteInfo", "exposedModules"]
76
+ },
77
+ RemoteInfo: {
78
+ description: "Definition of a frontend plugin Module Federation remote served by the backend",
79
+ externalDocs: {
80
+ url: "https://module-federation.io/guide/basic/runtime.html#init"
81
+ },
82
+ type: "object",
83
+ properties: {
84
+ name: {
85
+ description: "Name of the module federation remote",
86
+ type: "string"
87
+ },
88
+ entry: {
89
+ description: "Remote entry, either the remote manifest file, or the remote entry Javascript file.",
90
+ type: "string"
91
+ },
92
+ entryGlobalName: {
93
+ type: "string"
94
+ },
95
+ shareScope: {
96
+ type: "string"
97
+ },
98
+ type: {
99
+ type: "string",
100
+ enum: [
101
+ "var",
102
+ "module",
103
+ "assign",
104
+ "assign-properties",
105
+ "this",
106
+ "window",
107
+ "self",
108
+ "global",
109
+ "commonjs",
110
+ "commonjs2",
111
+ "commonjs-module",
112
+ "commonjs-static",
113
+ "amd",
114
+ "amd-require",
115
+ "umd",
116
+ "umd2",
117
+ "jsonp",
118
+ "system"
119
+ ]
120
+ }
121
+ },
122
+ required: ["name", "entry"]
123
+ },
124
+ Error: {
125
+ type: "object",
126
+ properties: {
127
+ error: {
128
+ type: "object",
129
+ properties: {
130
+ name: {
131
+ type: "string"
132
+ },
133
+ message: {
134
+ type: "string"
135
+ },
136
+ stack: {
137
+ type: "string"
138
+ },
139
+ code: {
140
+ type: "string"
141
+ }
142
+ },
143
+ required: ["name", "message"]
144
+ },
145
+ request: {
146
+ type: "object",
147
+ properties: {
148
+ method: {
149
+ type: "string"
150
+ },
151
+ url: {
152
+ type: "string"
153
+ }
154
+ },
155
+ required: ["method", "url"]
156
+ },
157
+ response: {
158
+ type: "object",
159
+ properties: {
160
+ statusCode: {
161
+ type: "number"
162
+ }
163
+ },
164
+ required: ["statusCode"]
165
+ }
166
+ },
167
+ required: ["error", "response"],
168
+ additionalProperties: {}
169
+ }
170
+ },
171
+ securitySchemes: {
172
+ JWT: {
173
+ type: "http",
174
+ scheme: "bearer",
175
+ bearerFormat: "JWT"
176
+ }
177
+ }
178
+ },
179
+ paths: {
180
+ "/remotes": {
181
+ get: {
182
+ operationId: "GetRemotes",
183
+ description: "Get the Module Federation remote definitions.",
184
+ responses: {
185
+ "200": {
186
+ $ref: "#/components/responses/RemotesResponse"
187
+ },
188
+ "400": {
189
+ $ref: "#/components/responses/ErrorResponse"
190
+ },
191
+ default: {
192
+ $ref: "#/components/responses/ErrorResponse"
193
+ }
194
+ },
195
+ security: [
196
+ {},
197
+ {
198
+ JWT: []
199
+ }
200
+ ],
201
+ parameters: []
202
+ }
203
+ }
204
+ }
205
+ };
206
+ const createOpenApiRouter = async (options) => backendOpenapiUtils.createValidatedOpenApiRouterFromGeneratedEndpointMap(
207
+ spec,
208
+ options
209
+ );
210
+
211
+ exports.createOpenApiRouter = createOpenApiRouter;
212
+ exports.spec = spec;
213
+ //# sourceMappingURL=router.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.cjs.js","sources":["../../../../src/schema/openapi/generated/router.ts"],"sourcesContent":["/*\n * Copyright 2025 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\n// ******************************************************************\n// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *\n// ******************************************************************\nimport { createValidatedOpenApiRouterFromGeneratedEndpointMap } from '@backstage/backend-openapi-utils';\nimport { EndpointMap } from './';\n\nexport const spec = {\n openapi: '3.0.3',\n info: {\n title: '.backstage/dynamic-features',\n version: '1',\n description:\n 'The Backstage backend plugin that serves the frontend plugins module federation manifests and assets',\n license: {\n name: 'Apache-2.0',\n url: 'http://www.apache.org/licenses/LICENSE-2.0.html',\n },\n contact: {},\n },\n servers: [\n {\n url: '/',\n },\n ],\n components: {\n examples: {},\n headers: {},\n parameters: {},\n requestBodies: {},\n responses: {\n RemotesResponse: {\n description: 'List of Module Federation Remotes exposed by the backend',\n content: {\n 'application/json': {\n schema: {\n $ref: '#/components/schemas/Remotes',\n },\n },\n },\n },\n ErrorResponse: {\n description: 'An error response from the backend.',\n content: {\n 'application/json': {\n schema: {\n $ref: '#/components/schemas/Error',\n },\n },\n },\n },\n },\n schemas: {\n Remotes: {\n type: 'array',\n items: {\n $ref: '#/components/schemas/Remote',\n },\n },\n Remote: {\n description:\n 'Definition of a frontend plugin Module Federation remote served by the backend',\n type: 'object',\n properties: {\n packageName: {\n description:\n 'Name of the package exposed through this Module Federation remote',\n type: 'string',\n },\n remoteInfo: {\n $ref: '#/components/schemas/RemoteInfo',\n },\n exposedModules: {\n description:\n 'Names of modules exposed by this module federation remote',\n type: 'array',\n items: {\n type: 'string',\n },\n },\n },\n required: ['packageName', 'remoteInfo', 'exposedModules'],\n },\n RemoteInfo: {\n description:\n 'Definition of a frontend plugin Module Federation remote served by the backend',\n externalDocs: {\n url: 'https://module-federation.io/guide/basic/runtime.html#init',\n },\n type: 'object',\n properties: {\n name: {\n description: 'Name of the module federation remote',\n type: 'string',\n },\n entry: {\n description:\n 'Remote entry, either the remote manifest file, or the remote entry Javascript file.',\n type: 'string',\n },\n entryGlobalName: {\n type: 'string',\n },\n shareScope: {\n type: 'string',\n },\n type: {\n type: 'string',\n enum: [\n 'var',\n 'module',\n 'assign',\n 'assign-properties',\n 'this',\n 'window',\n 'self',\n 'global',\n 'commonjs',\n 'commonjs2',\n 'commonjs-module',\n 'commonjs-static',\n 'amd',\n 'amd-require',\n 'umd',\n 'umd2',\n 'jsonp',\n 'system',\n ],\n },\n },\n required: ['name', 'entry'],\n },\n Error: {\n type: 'object',\n properties: {\n error: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n },\n message: {\n type: 'string',\n },\n stack: {\n type: 'string',\n },\n code: {\n type: 'string',\n },\n },\n required: ['name', 'message'],\n },\n request: {\n type: 'object',\n properties: {\n method: {\n type: 'string',\n },\n url: {\n type: 'string',\n },\n },\n required: ['method', 'url'],\n },\n response: {\n type: 'object',\n properties: {\n statusCode: {\n type: 'number',\n },\n },\n required: ['statusCode'],\n },\n },\n required: ['error', 'response'],\n additionalProperties: {},\n },\n },\n securitySchemes: {\n JWT: {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n },\n },\n },\n paths: {\n '/remotes': {\n get: {\n operationId: 'GetRemotes',\n description: 'Get the Module Federation remote definitions.',\n responses: {\n '200': {\n $ref: '#/components/responses/RemotesResponse',\n },\n '400': {\n $ref: '#/components/responses/ErrorResponse',\n },\n default: {\n $ref: '#/components/responses/ErrorResponse',\n },\n },\n security: [\n {},\n {\n JWT: [],\n },\n ],\n parameters: [],\n },\n },\n },\n} as const;\nexport const createOpenApiRouter = async (\n options?: Parameters<\n typeof createValidatedOpenApiRouterFromGeneratedEndpointMap\n >['1'],\n) =>\n createValidatedOpenApiRouterFromGeneratedEndpointMap<EndpointMap>(\n spec,\n options,\n );\n"],"names":["createValidatedOpenApiRouterFromGeneratedEndpointMap"],"mappings":"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"}
@@ -96,7 +96,7 @@ const dynamicPluginsSchemasServiceFactory = Object.assign(
96
96
  dynamicPluginsSchemasServiceFactoryWithOptions,
97
97
  dynamicPluginsSchemasServiceFactoryWithOptions()
98
98
  );
99
- async function gatherDynamicPluginsSchemas(packages, logger, schemaLocator = () => path__namespace.join("dist", "configSchema.json")) {
99
+ async function gatherDynamicPluginsSchemas(packages, logger, schemaLocator = () => path__namespace.join("dist", ".config-schema.json")) {
100
100
  const allSchemas = {};
101
101
  for (const pluginPackage of packages) {
102
102
  let schemaLocation = schemaLocator(pluginPackage);
@@ -107,13 +107,18 @@ async function gatherDynamicPluginsSchemas(packages, logger, schemaLocator = ()
107
107
  if (!await fs__default.default.pathExists(schemaLocation)) {
108
108
  continue;
109
109
  }
110
- const serialized = await fs__default.default.readJson(schemaLocation);
110
+ let serialized = await fs__default.default.readJson(schemaLocation);
111
111
  if (!serialized) {
112
112
  continue;
113
113
  }
114
114
  if (lodash.isEmpty(serialized)) {
115
115
  continue;
116
116
  }
117
+ if (serialized?.backstageConfigSchemaVersion === 1) {
118
+ serialized = configLoader.mergeConfigSchemas(
119
+ (serialized?.schemas).map((_) => _.value)
120
+ );
121
+ }
117
122
  if (!serialized?.$schema || serialized?.type !== "object") {
118
123
  logger.error(
119
124
  `Serialized configuration schema is invalid for plugin ${pluginPackage.manifest.name}`
@@ -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 'path';\nimport * as url from 'url';\nimport { isEmpty } from 'lodash';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport { PluginScanner } from '../scanner/plugin-scanner';\nimport { ConfigSchema, loadConfigSchema } 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', 'configSchema.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 const serialized = await fs.readJson(schemaLocation);\n if (!serialized) {\n continue;\n }\n\n if (isEmpty(serialized)) {\n continue;\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"],"mappings":"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"}
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 'path';\nimport * as url from '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":"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"}
@@ -0,0 +1,58 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+ var router$1 = require('./router.cjs.js');
5
+ var backendDynamicFeatureService = require('@backstage/backend-dynamic-feature-service');
6
+ var router = require('../schema/openapi/generated/router.cjs.js');
7
+ var sdk = require('@module-federation/sdk');
8
+
9
+ const dynamicPluginsFrontendServiceRef = backendPluginApi.createServiceRef({
10
+ id: "core.dynamicplugins.frontendRemotes",
11
+ scope: "root"
12
+ });
13
+ const frontendRemotesServerService = backendPluginApi.createServiceFactory({
14
+ service: dynamicPluginsFrontendServiceRef,
15
+ deps: {
16
+ logger: backendPluginApi.coreServices.rootLogger,
17
+ rootHttpRouter: backendPluginApi.coreServices.rootHttpRouter,
18
+ config: backendPluginApi.coreServices.rootConfig,
19
+ dynamicPlugins: backendDynamicFeatureService.dynamicPluginsServiceRef,
20
+ lifecycle: backendPluginApi.coreServices.rootLifecycle
21
+ },
22
+ async factory({ logger, rootHttpRouter, config, dynamicPlugins, lifecycle }) {
23
+ const resolvers = {
24
+ default: {
25
+ assetsPathFromPackage: "dist",
26
+ manifestFileName: sdk.ManifestFileName,
27
+ getRemoteEntryType: () => "manifest"
28
+ },
29
+ provider: void 0
30
+ };
31
+ lifecycle.addStartupHook(async () => {
32
+ rootHttpRouter.use(
33
+ `/${router.spec.info.title}`,
34
+ await router$1.createRouter({
35
+ logger,
36
+ config,
37
+ dynamicPlugins,
38
+ resolvers
39
+ })
40
+ );
41
+ });
42
+ return {
43
+ setResolverProvider(resolver) {
44
+ logger.info("Setting resolver provider");
45
+ if (resolvers.provider) {
46
+ throw new Error(
47
+ "Attempted to install a frontend remote resolver provider twice"
48
+ );
49
+ }
50
+ resolvers.provider = resolver;
51
+ }
52
+ };
53
+ }
54
+ });
55
+
56
+ exports.dynamicPluginsFrontendServiceRef = dynamicPluginsFrontendServiceRef;
57
+ exports.frontendRemotesServerService = frontendRemotesServerService;
58
+ //# sourceMappingURL=frontendRemotesServer.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontendRemotesServer.cjs.js","sources":["../../src/server/frontendRemotesServer.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 */\nimport {\n coreServices,\n createServiceFactory,\n createServiceRef,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './router';\nimport { dynamicPluginsServiceRef } from '@backstage/backend-dynamic-feature-service';\nimport { spec } from '../schema/openapi';\nimport { ManifestFileName } from '@module-federation/sdk';\nimport { RemoteInfo } from '../schema/openapi/generated/models';\nimport { JsonObject } from '@backstage/types';\n\n/**\n *\n * @public\n * */\nexport type AdditionalRemoteInfo = Omit<RemoteInfo, 'name' | 'entry'>;\n\n/**\n *\n * @public\n * */\nexport type FrontendRemoteResolver = {\n /**\n * Relative path to the module federation assets folder from thr root folder of the plugin package.\n * Default value is `dist`.\n */\n assetsPathFromPackage?: string;\n\n /**\n * File name of the module federation manifest inside the module federation assets folder.\n * Default value is `mf-manifest.json`.\n */\n manifestFileName?: string;\n\n /**\n * Type of the remote entry returned in the RemoteInfo for this remote.\n * Default value is `manifest`.\n */\n getRemoteEntryType?: (\n manifestContent: JsonObject,\n ) => 'manifest' | 'javascript';\n\n /**\n * Additional module federation fields, which might be required if the remote entry type is 'javascript'.\n */\n getAdditionaRemoteInfo?: (\n manifestContent: JsonObject,\n ) => AdditionalRemoteInfo;\n\n /**\n * Overrides the list of exposed modules. By default the exposed modules are read from the manifest file.\n */\n overrideExposedModules?: (\n exposedModules: string[],\n manifestContent: JsonObject,\n ) => string[];\n\n /**\n * Customizes the manifest before returning it as the remote entry.\n */\n customizeManifest?: (content: JsonObject) => JsonObject;\n};\n\n/**\n *\n * @public\n * */\nexport type FrontendRemoteResolverProvider = {\n for(\n pluginName: string,\n pluginPackagePath: string,\n ): Partial<FrontendRemoteResolver> | undefined;\n};\n\n/**\n *\n * @public\n * */\nexport interface DynamicPluginsFrontendRemotesService {\n setResolverProvider(provider: FrontendRemoteResolverProvider): void;\n}\n\n/**\n * A service that serves the frontend module federation remotes,\n * and allows a plugin to customize the way remotes are served,\n * by setting a ResolverProvider.\n *\n * @public\n */\nexport const dynamicPluginsFrontendServiceRef =\n createServiceRef<DynamicPluginsFrontendRemotesService>({\n id: 'core.dynamicplugins.frontendRemotes',\n scope: 'root',\n });\n\nexport type FrontendRemoteResolvers = {\n default: FrontendRemoteResolver &\n Required<\n Pick<\n FrontendRemoteResolver,\n 'assetsPathFromPackage' | 'manifestFileName' | 'getRemoteEntryType'\n >\n >;\n provider?: FrontendRemoteResolverProvider;\n};\n\nexport const frontendRemotesServerService = createServiceFactory({\n service: dynamicPluginsFrontendServiceRef,\n deps: {\n logger: coreServices.rootLogger,\n rootHttpRouter: coreServices.rootHttpRouter,\n config: coreServices.rootConfig,\n dynamicPlugins: dynamicPluginsServiceRef,\n lifecycle: coreServices.rootLifecycle,\n },\n async factory({ logger, rootHttpRouter, config, dynamicPlugins, lifecycle }) {\n const resolvers: FrontendRemoteResolvers = {\n default: {\n assetsPathFromPackage: 'dist',\n manifestFileName: ManifestFileName,\n getRemoteEntryType: () => 'manifest',\n },\n provider: undefined,\n };\n\n lifecycle.addStartupHook(async () => {\n rootHttpRouter.use(\n `/${spec.info.title}`,\n await createRouter({\n logger,\n config,\n dynamicPlugins,\n resolvers,\n }),\n );\n });\n\n return {\n setResolverProvider(resolver) {\n logger.info('Setting resolver provider');\n if (resolvers.provider) {\n throw new Error(\n 'Attempted to install a frontend remote resolver provider twice',\n );\n }\n resolvers.provider = resolver;\n },\n };\n },\n});\n"],"names":["createServiceRef","createServiceFactory","coreServices","dynamicPluginsServiceRef","ManifestFileName","spec","createRouter"],"mappings":";;;;;;;;AAyGO,MAAM,mCACXA,iCAAuD,CAAA;AAAA,EACrD,EAAI,EAAA,qCAAA;AAAA,EACJ,KAAO,EAAA;AACT,CAAC;AAaI,MAAM,+BAA+BC,qCAAqB,CAAA;AAAA,EAC/D,OAAS,EAAA,gCAAA;AAAA,EACT,IAAM,EAAA;AAAA,IACJ,QAAQC,6BAAa,CAAA,UAAA;AAAA,IACrB,gBAAgBA,6BAAa,CAAA,cAAA;AAAA,IAC7B,QAAQA,6BAAa,CAAA,UAAA;AAAA,IACrB,cAAgB,EAAAC,qDAAA;AAAA,IAChB,WAAWD,6BAAa,CAAA;AAAA,GAC1B;AAAA,EACA,MAAM,QAAQ,EAAE,MAAA,EAAQ,gBAAgB,MAAQ,EAAA,cAAA,EAAgB,WAAa,EAAA;AAC3E,IAAA,MAAM,SAAqC,GAAA;AAAA,MACzC,OAAS,EAAA;AAAA,QACP,qBAAuB,EAAA,MAAA;AAAA,QACvB,gBAAkB,EAAAE,oBAAA;AAAA,QAClB,oBAAoB,MAAM;AAAA,OAC5B;AAAA,MACA,QAAU,EAAA,KAAA;AAAA,KACZ;AAEA,IAAA,SAAA,CAAU,eAAe,YAAY;AACnC,MAAe,cAAA,CAAA,GAAA;AAAA,QACb,CAAA,CAAA,EAAIC,WAAK,CAAA,IAAA,CAAK,KAAK,CAAA,CAAA;AAAA,QACnB,MAAMC,qBAAa,CAAA;AAAA,UACjB,MAAA;AAAA,UACA,MAAA;AAAA,UACA,cAAA;AAAA,UACA;AAAA,SACD;AAAA,OACH;AAAA,KACD,CAAA;AAED,IAAO,OAAA;AAAA,MACL,oBAAoB,QAAU,EAAA;AAC5B,QAAA,MAAA,CAAO,KAAK,2BAA2B,CAAA;AACvC,QAAA,IAAI,UAAU,QAAU,EAAA;AACtB,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA;AAEF,QAAA,SAAA,CAAU,QAAW,GAAA,QAAA;AAAA;AACvB,KACF;AAAA;AAEJ,CAAC;;;;;"}
@@ -0,0 +1,152 @@
1
+ 'use strict';
2
+
3
+ var express = require('express');
4
+ var router = require('../schema/openapi/generated/router.cjs.js');
5
+ var fs = require('fs');
6
+ var path = require('path');
7
+ var url = require('url');
8
+
9
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
10
+
11
+ function _interopNamespaceCompat(e) {
12
+ if (e && typeof e === 'object' && 'default' in e) return e;
13
+ var n = Object.create(null);
14
+ if (e) {
15
+ Object.keys(e).forEach(function (k) {
16
+ if (k !== 'default') {
17
+ var d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: function () { return e[k]; }
21
+ });
22
+ }
23
+ });
24
+ }
25
+ n.default = e;
26
+ return Object.freeze(n);
27
+ }
28
+
29
+ var express__default = /*#__PURE__*/_interopDefaultCompat(express);
30
+ var fs__namespace = /*#__PURE__*/_interopNamespaceCompat(fs);
31
+ var path__namespace = /*#__PURE__*/_interopNamespaceCompat(path);
32
+ var url__namespace = /*#__PURE__*/_interopNamespaceCompat(url);
33
+
34
+ async function createRouter({
35
+ logger,
36
+ config,
37
+ dynamicPlugins,
38
+ resolvers
39
+ }) {
40
+ const externalBaseUrl = `${config.getString("backend.baseUrl")}/${router.spec.info.title}`;
41
+ const typedRouter = await router.createOpenApiRouter();
42
+ const frontendPluginRemotes = [];
43
+ const { default: defaultResolver, provider: resolverProvider } = resolvers;
44
+ for (const plugin of dynamicPlugins.frontendPlugins()) {
45
+ try {
46
+ const pluginScannedPackage = dynamicPlugins.getScannedPackage(plugin);
47
+ const pluginScannedPackagePath = path__namespace.resolve(
48
+ url__namespace.fileURLToPath(pluginScannedPackage.location)
49
+ );
50
+ const providedResolver = resolverProvider?.for(
51
+ plugin.name,
52
+ pluginScannedPackagePath
53
+ );
54
+ const assetsPath = path__namespace.resolve(
55
+ pluginScannedPackagePath,
56
+ providedResolver?.assetsPathFromPackage ?? defaultResolver.assetsPathFromPackage
57
+ );
58
+ const manifestFileName = providedResolver?.manifestFileName ?? defaultResolver.manifestFileName;
59
+ const manifestLocation = path__namespace.resolve(assetsPath, manifestFileName);
60
+ if (!fs__namespace.existsSync(manifestLocation)) {
61
+ logger.error(
62
+ `Could not find manifest '${manifestLocation}' for frontend plugin ${plugin.name}@${plugin.version}`
63
+ );
64
+ continue;
65
+ }
66
+ let manifest;
67
+ try {
68
+ manifest = JSON.parse(fs__namespace.readFileSync(manifestLocation).toString());
69
+ } catch (error) {
70
+ logger.error(
71
+ `Dynamic frontend plugin manifest '${manifestLocation}' could not be parsed for plugin ${plugin.name}@${plugin.version}`
72
+ );
73
+ continue;
74
+ }
75
+ if (!manifest.name || typeof manifest.name !== "string") {
76
+ logger.error(
77
+ `Error in manifest '${manifestLocation}' for plugin ${plugin.name}@${plugin.version}: module name not found`
78
+ );
79
+ continue;
80
+ }
81
+ if (!manifest.metaData || typeof manifest.metaData !== "object" || !("remoteEntry" in manifest.metaData) || !manifest.metaData.remoteEntry || typeof manifest.metaData.remoteEntry !== "object" || !("name" in manifest.metaData.remoteEntry) || typeof manifest.metaData.remoteEntry.name !== "string") {
82
+ logger.error(
83
+ `Could not find remote entry asset in the manifest '${manifestLocation}' for plugin ${plugin.name}@${plugin.version}`
84
+ );
85
+ continue;
86
+ }
87
+ if (!manifest.exposes || !Array.isArray(manifest.exposes) || !manifest.exposes.every(
88
+ (i) => i !== null && typeof i === "object" && "name" in i
89
+ )) {
90
+ logger.error(
91
+ `Could not find the exposes field in the manifest '${manifestLocation}' for plugin ${plugin.name}@${plugin.version}`
92
+ );
93
+ continue;
94
+ }
95
+ const getAdditionalRemoteInfo = providedResolver?.getAdditionaRemoteInfo ?? defaultResolver.getAdditionaRemoteInfo;
96
+ const getRemoteEntryType = providedResolver?.getRemoteEntryType ?? defaultResolver.getRemoteEntryType;
97
+ const remoteEntryType = getRemoteEntryType(manifest);
98
+ let remoteEntryAsset = manifestFileName;
99
+ if (remoteEntryType === "javascript") {
100
+ remoteEntryAsset = manifest.metaData.remoteEntry.name;
101
+ }
102
+ const remoteEntryAssetLocation = path__namespace.resolve(
103
+ assetsPath,
104
+ remoteEntryAsset
105
+ );
106
+ if (!fs__namespace.existsSync(remoteEntryAssetLocation)) {
107
+ logger.error(
108
+ `Could not find remote entry asset '${remoteEntryAssetLocation}' for frontend plugin ${plugin.name}@${plugin.version}`
109
+ );
110
+ continue;
111
+ }
112
+ const remoteAssetsPrefix = `/remotes/${plugin.name}`;
113
+ const remoteEntryPath = `${remoteAssetsPrefix}/${remoteEntryAsset}`;
114
+ const overrideExposedModules = providedResolver?.overrideExposedModules ?? defaultResolver.overrideExposedModules;
115
+ const exposedModules = manifest.exposes.map((e) => e.name);
116
+ frontendPluginRemotes.push({
117
+ packageName: plugin.name,
118
+ remoteInfo: {
119
+ name: manifest.name,
120
+ entry: `${externalBaseUrl}${remoteEntryPath}`,
121
+ ...getAdditionalRemoteInfo?.(manifest)
122
+ },
123
+ exposedModules: overrideExposedModules?.(exposedModules, manifest) ?? exposedModules
124
+ });
125
+ const customizeManifest = providedResolver?.customizeManifest ?? defaultResolver.customizeManifest;
126
+ if (remoteEntryType === "manifest" && customizeManifest) {
127
+ const customizedContent = customizeManifest(manifest);
128
+ typedRouter.use(`${remoteEntryPath}`, (_, res) => {
129
+ res.json(customizedContent);
130
+ });
131
+ }
132
+ typedRouter.use(remoteAssetsPrefix, express__default.default.static(assetsPath));
133
+ logger.info(
134
+ `Exposed dynamic frontend plugin '${plugin.name}' from '${assetsPath}' `
135
+ );
136
+ } catch (error) {
137
+ logger.error(
138
+ `Unexpected error when exposing dynamic frontend plugin '${plugin.name}@${plugin.version}'`,
139
+ error
140
+ );
141
+ continue;
142
+ }
143
+ }
144
+ logger.info(`/remotes => ${JSON.stringify(frontendPluginRemotes)}`);
145
+ typedRouter.get("/remotes", (_, res) => {
146
+ res.status(200).json(frontendPluginRemotes);
147
+ });
148
+ return typedRouter;
149
+ }
150
+
151
+ exports.createRouter = createRouter;
152
+ //# sourceMappingURL=router.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.cjs.js","sources":["../../src/server/router.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 {\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport express from 'express';\nimport { createOpenApiRouter, spec } from '../schema/openapi';\nimport { DynamicPluginProvider } from '@backstage/backend-dynamic-feature-service';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as url from 'url';\nimport { FrontendRemoteResolvers } from './frontendRemotesServer';\nimport { Remote } from '../schema/openapi/generated/models';\nimport { JsonObject } from '@backstage/types';\n\nexport async function createRouter({\n logger,\n config,\n dynamicPlugins,\n resolvers,\n}: {\n logger: LoggerService;\n config: RootConfigService;\n dynamicPlugins: DynamicPluginProvider;\n resolvers: FrontendRemoteResolvers;\n}): Promise<express.Router> {\n const externalBaseUrl = `${config.getString('backend.baseUrl')}/${\n spec.info.title\n }`;\n\n const typedRouter = await createOpenApiRouter();\n\n const frontendPluginRemotes: Remote[] = [];\n\n const { default: defaultResolver, provider: resolverProvider } = resolvers;\n for (const plugin of dynamicPlugins.frontendPlugins()) {\n try {\n const pluginScannedPackage = dynamicPlugins.getScannedPackage(plugin);\n const pluginScannedPackagePath = path.resolve(\n url.fileURLToPath(pluginScannedPackage.location),\n );\n const providedResolver = resolverProvider?.for(\n plugin.name,\n pluginScannedPackagePath,\n );\n\n const assetsPath = path.resolve(\n pluginScannedPackagePath,\n providedResolver?.assetsPathFromPackage ??\n defaultResolver.assetsPathFromPackage,\n );\n\n const manifestFileName =\n providedResolver?.manifestFileName ?? defaultResolver.manifestFileName;\n const manifestLocation = path.resolve(assetsPath, manifestFileName);\n if (!fs.existsSync(manifestLocation)) {\n logger.error(\n `Could not find manifest '${manifestLocation}' for frontend plugin ${plugin.name}@${plugin.version}`,\n );\n continue;\n }\n\n let manifest: JsonObject;\n try {\n manifest = JSON.parse(fs.readFileSync(manifestLocation).toString());\n } catch (error) {\n logger.error(\n `Dynamic frontend plugin manifest '${manifestLocation}' could not be parsed for plugin ${plugin.name}@${plugin.version}`,\n );\n continue;\n }\n\n if (!manifest.name || typeof manifest.name !== 'string') {\n logger.error(\n `Error in manifest '${manifestLocation}' for plugin ${plugin.name}@${plugin.version}: module name not found`,\n );\n continue;\n }\n if (\n !manifest.metaData ||\n typeof manifest.metaData !== 'object' ||\n !('remoteEntry' in manifest.metaData) ||\n !manifest.metaData.remoteEntry ||\n typeof manifest.metaData.remoteEntry !== 'object' ||\n !('name' in manifest.metaData.remoteEntry) ||\n typeof manifest.metaData.remoteEntry.name !== 'string'\n ) {\n logger.error(\n `Could not find remote entry asset in the manifest '${manifestLocation}' for plugin ${plugin.name}@${plugin.version}`,\n );\n continue;\n }\n\n if (\n !manifest.exposes ||\n !Array.isArray(manifest.exposes) ||\n !manifest.exposes.every<{ name: string }>(\n (i): i is { name: string } =>\n i !== null && typeof i === 'object' && 'name' in i,\n )\n ) {\n logger.error(\n `Could not find the exposes field in the manifest '${manifestLocation}' for plugin ${plugin.name}@${plugin.version}`,\n );\n continue;\n }\n\n const getAdditionalRemoteInfo =\n providedResolver?.getAdditionaRemoteInfo ??\n defaultResolver.getAdditionaRemoteInfo;\n const getRemoteEntryType =\n providedResolver?.getRemoteEntryType ??\n defaultResolver.getRemoteEntryType;\n const remoteEntryType = getRemoteEntryType(manifest);\n\n let remoteEntryAsset = manifestFileName;\n if (remoteEntryType === 'javascript') {\n remoteEntryAsset = manifest.metaData.remoteEntry.name;\n }\n\n const remoteEntryAssetLocation = path.resolve(\n assetsPath,\n remoteEntryAsset,\n );\n if (!fs.existsSync(remoteEntryAssetLocation)) {\n logger.error(\n `Could not find remote entry asset '${remoteEntryAssetLocation}' for frontend plugin ${plugin.name}@${plugin.version}`,\n );\n continue;\n }\n\n const remoteAssetsPrefix = `/remotes/${plugin.name}`;\n const remoteEntryPath = `${remoteAssetsPrefix}/${remoteEntryAsset}`;\n\n const overrideExposedModules =\n providedResolver?.overrideExposedModules ??\n defaultResolver.overrideExposedModules;\n\n const exposedModules = manifest.exposes.map(e => e.name);\n\n frontendPluginRemotes.push({\n packageName: plugin.name,\n remoteInfo: {\n name: manifest.name,\n entry: `${externalBaseUrl}${remoteEntryPath}`,\n ...getAdditionalRemoteInfo?.(manifest),\n },\n exposedModules:\n overrideExposedModules?.(exposedModules, manifest) ?? exposedModules,\n });\n\n const customizeManifest =\n providedResolver?.customizeManifest ??\n defaultResolver.customizeManifest;\n if (remoteEntryType === 'manifest' && customizeManifest) {\n const customizedContent = customizeManifest(manifest);\n typedRouter.use(`${remoteEntryPath}`, (_, res) => {\n res.json(customizedContent);\n });\n }\n typedRouter.use(remoteAssetsPrefix, express.static(assetsPath));\n logger.info(\n `Exposed dynamic frontend plugin '${plugin.name}' from '${assetsPath}' `,\n );\n } catch (error) {\n logger.error(\n `Unexpected error when exposing dynamic frontend plugin '${plugin.name}@${plugin.version}'`,\n error,\n );\n continue;\n }\n }\n\n logger.info(`/remotes => ${JSON.stringify(frontendPluginRemotes)}`);\n typedRouter.get('/remotes', (_, res) => {\n res.status(200).json(frontendPluginRemotes);\n });\n\n return typedRouter;\n}\n"],"names":["spec","createOpenApiRouter","path","url","fs","express"],"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.6.2-next.1",
3
+ "version": "0.6.2",
4
4
  "description": "Backstage dynamic feature service",
5
5
  "backstage": {
6
6
  "role": "node-library"
@@ -28,6 +28,13 @@
28
28
  },
29
29
  "main": "./dist/index.cjs.js",
30
30
  "types": "./dist/index.d.ts",
31
+ "typesVersions": {
32
+ "*": {
33
+ "package.json": [
34
+ "package.json"
35
+ ]
36
+ }
37
+ },
31
38
  "files": [
32
39
  "dist",
33
40
  "config.d.ts"
@@ -35,6 +42,9 @@
35
42
  "scripts": {
36
43
  "build": "backstage-cli package build",
37
44
  "clean": "backstage-cli package clean",
45
+ "diff": "backstage-repo-tools package schema openapi diff",
46
+ "fuzz": "backstage-repo-tools package schema openapi fuzz --exclude-checks response_schema_conformance",
47
+ "generate": "backstage-repo-tools package schema openapi generate --server --client-package packages/frontend-dynamic-feature-loader",
38
48
  "lint": "backstage-cli package lint",
39
49
  "prepack": "backstage-cli package prepack",
40
50
  "postpack": "backstage-cli package postpack",
@@ -42,38 +52,43 @@
42
52
  "test": "backstage-cli package test"
43
53
  },
44
54
  "dependencies": {
45
- "@backstage/backend-defaults": "0.9.0-next.1",
46
- "@backstage/backend-plugin-api": "1.2.1",
47
- "@backstage/cli-common": "0.1.15",
48
- "@backstage/cli-node": "0.2.13",
49
- "@backstage/config": "1.3.2",
50
- "@backstage/config-loader": "1.10.0",
51
- "@backstage/errors": "1.2.7",
52
- "@backstage/plugin-app-node": "0.1.31",
53
- "@backstage/plugin-auth-node": "0.6.1",
54
- "@backstage/plugin-catalog-backend": "1.32.1-next.0",
55
- "@backstage/plugin-events-backend": "0.5.0",
56
- "@backstage/plugin-events-node": "0.4.9",
57
- "@backstage/plugin-permission-common": "0.8.4",
58
- "@backstage/plugin-permission-node": "0.9.0",
59
- "@backstage/plugin-scaffolder-node": "0.8.1-next.1",
60
- "@backstage/plugin-search-backend-node": "1.3.9",
61
- "@backstage/plugin-search-common": "1.2.17",
62
- "@backstage/types": "1.2.1",
55
+ "@backstage/backend-defaults": "^0.9.0",
56
+ "@backstage/backend-openapi-utils": "^0.5.2",
57
+ "@backstage/backend-plugin-api": "^1.3.0",
58
+ "@backstage/cli-common": "^0.1.15",
59
+ "@backstage/cli-node": "^0.2.13",
60
+ "@backstage/config": "^1.3.2",
61
+ "@backstage/config-loader": "^1.10.0",
62
+ "@backstage/errors": "^1.2.7",
63
+ "@backstage/plugin-app-node": "^0.1.32",
64
+ "@backstage/plugin-auth-node": "^0.6.2",
65
+ "@backstage/plugin-catalog-backend": "^1.32.1",
66
+ "@backstage/plugin-events-backend": "^0.5.1",
67
+ "@backstage/plugin-events-node": "^0.4.10",
68
+ "@backstage/plugin-permission-common": "^0.8.4",
69
+ "@backstage/plugin-permission-node": "^0.9.1",
70
+ "@backstage/plugin-scaffolder-node": "^0.8.1",
71
+ "@backstage/plugin-search-backend-node": "^1.3.10",
72
+ "@backstage/plugin-search-common": "^1.2.17",
73
+ "@backstage/types": "^1.2.1",
63
74
  "@manypkg/get-packages": "^1.1.3",
75
+ "@module-federation/sdk": "^0.9.0",
64
76
  "@types/express": "^4.17.6",
65
77
  "chokidar": "^3.5.3",
66
78
  "express": "^4.17.1",
79
+ "express-promise-router": "^4.1.0",
67
80
  "fs-extra": "^11.2.0",
68
81
  "lodash": "^4.17.21",
69
82
  "winston": "^3.2.1"
70
83
  },
71
84
  "devDependencies": {
72
- "@backstage/backend-app-api": "1.2.1",
73
- "@backstage/backend-test-utils": "1.3.2-next.1",
74
- "@backstage/cli": "0.32.0-next.1",
75
- "@backstage/plugin-app-backend": "0.5.0",
85
+ "@backstage/backend-app-api": "^1.2.2",
86
+ "@backstage/backend-test-utils": "^1.4.0",
87
+ "@backstage/cli": "^0.32.0",
88
+ "@backstage/plugin-app-backend": "^0.5.1",
89
+ "@backstage/repo-tools": "^0.13.2",
76
90
  "triple-beam": "^1.4.1",
77
91
  "wait-for-expect": "^3.0.2"
78
- }
92
+ },
93
+ "configSchema": "config.d.ts"
79
94
  }