Update Backend Dynamic Feature Service to provide the router for frontend plugins

Signed-off-by: David Festal <dfestal@redhat.com>
This commit is contained in:
David Festal
2024-12-05 22:08:21 +01:00
parent 182efca350
commit 9c4f72b2db
20 changed files with 674 additions and 115 deletions
@@ -38,6 +38,9 @@
"scripts": {
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"diff": "backstage-repo-tools package schema openapi diff",
"fuzz": "backstage-repo-tools package schema openapi fuzz --exclude-checks response_schema_conformance",
"generate": "backstage-repo-tools package schema openapi generate --server --client-package packages/frontend-dynamic-feature-loader",
"lint": "backstage-cli package lint",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack",
@@ -46,6 +49,7 @@
},
"dependencies": {
"@backstage/backend-defaults": "workspace:^",
"@backstage/backend-openapi-utils": "workspace:^",
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/cli-common": "workspace:^",
"@backstage/cli-node": "workspace:^",
@@ -64,9 +68,11 @@
"@backstage/plugin-search-common": "workspace:^",
"@backstage/types": "workspace:^",
"@manypkg/get-packages": "^1.1.3",
"@module-federation/sdk": "^0.9.0",
"@types/express": "^4.17.6",
"chokidar": "^3.5.3",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
"fs-extra": "^11.2.0",
"lodash": "^4.17.21",
"winston": "^3.2.1"
@@ -76,7 +82,9 @@
"@backstage/backend-test-utils": "workspace:^",
"@backstage/cli": "workspace:^",
"@backstage/plugin-app-backend": "workspace:^",
"@backstage/repo-tools": "workspace:^",
"triple-beam": "^1.4.1",
"wait-for-expect": "^3.0.2"
}
},
"configSchema": "config.d.ts"
}
@@ -31,27 +31,8 @@ const testPlugin = backendPluginApi.createBackendPlugin({
metadata,
}) {
logger.info("This secret value should be hidden by the dynamic-plugin-aware logger: AVerySecretValue");
const externalBaseUrl = await discovery.getExternalBaseUrl(metadata.getId());
const router = express.Router();
const frontendPluginsIndexPath = privateDep.frontendPluginsIndexPath;
const frontendPluginManifests = Object.fromEntries(dynamicPlugins.frontendPlugins().map(fp => {
const pluginScannedPackage = dynamicPlugins.getScannedPackage(fp);
const pkgDistLocation = path.resolve(
url.fileURLToPath(pluginScannedPackage.location),
'dist',
);
router.use(`/${frontendPluginsIndexPath}/${fp.name}`, express.static(pkgDistLocation))
return [fp.name, `${externalBaseUrl}/${frontendPluginsIndexPath}/${fp.name}/mf-manifest.json`]
}));
router.get(`/${frontendPluginsIndexPath}`, (req, res) => {
res.status(200).json(frontendPluginManifests);
});
http.use(router);
http.addAuthPolicy({
path: `/`,
allow: 'unauthenticated',
});
const messageFromPrivateDep = privateDep.message;
logger.info(messageFromPrivateDep);
}
});
}
@@ -2,6 +2,6 @@
Object.defineProperty(exports, '__esModule', { value: true });
const frontendPluginsIndexPath = 'frontend-plugins';
const message = 'This message comes from a plugin private dependency';
exports.frontendPluginsIndexPath = frontendPluginsIndexPath;
exports.message = message;
@@ -389,15 +389,15 @@ Require stack:
});
const list = await fetch(
`http://localhost:${server.port()}/api/test/frontend-plugins`,
`http://localhost:${server.port()}/api/core.dynamicplugins.frontendRemotes/manifests`,
);
expect(list.ok).toBe(true);
expect(await list.json()).toEqual({
'plugin-test-dynamic': `http://localhost:${server.port()}/api/test/frontend-plugins/plugin-test-dynamic/mf-manifest.json`,
'plugin-test-dynamic': `http://localhost:${server.port()}/api/core.dynamicplugins.frontendRemotes/remotes/plugin-test-dynamic/mf-manifest.json`,
});
const manifest = await fetch(
`http://localhost:${server.port()}/api/test/frontend-plugins/plugin-test-dynamic/mf-manifest.json`,
`http://localhost:${server.port()}/api/core.dynamicplugins.frontendRemotes/remotes/plugin-test-dynamic/mf-manifest.json`,
);
expect(manifest.ok).toBe(true);
expect(await manifest.json()).toMatchObject({
@@ -32,6 +32,7 @@ import {
dynamicPluginsRootLoggerServiceFactory,
dynamicPluginsSchemasServiceFactory,
} from '../schemas';
import frontendRemotesServerPlugin from '../server';
/**
* @public
@@ -64,6 +65,7 @@ const dynamicPluginsFeatureLoaderWithOptions = (
yield* [
dynamicPluginsRootLoggerServiceFactory(rootLoggerOptions),
dynamicPluginsFrontendSchemas,
frontendRemotesServerPlugin,
dynamicPluginsFeatureDiscoveryLoader,
];
}
@@ -0,0 +1,90 @@
openapi: 3.0.3
info:
title: core.dynamicplugins.frontendRemotes
version: '1'
description: The Backstage backend plugin that serves the frontend plugins module federation manifests and assets
license:
name: Apache-2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
contact: {}
servers:
- url: /
components:
examples: {}
headers: {}
parameters: {}
requestBodies: {}
responses:
ErrorResponse:
description: An error response from the backend.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
schemas:
Error:
type: object
properties:
error:
type: object
properties:
name:
type: string
message:
type: string
stack:
type: string
code:
type: string
required:
- name
- message
request:
type: object
properties:
method:
type: string
url:
type: string
required:
- method
- url
response:
type: object
properties:
statusCode:
type: number
required:
- statusCode
required:
- error
- response
additionalProperties: {}
securitySchemes:
JWT:
type: http
scheme: bearer
bearerFormat: JWT
paths:
/manifests:
get:
operationId: GetManifests
description: Get the Module Federation manifest files of dynamic frontend plugins.
responses:
'200':
description: ''
content:
application/json:
schema:
type: object
properties: {}
additionalProperties:
type: string
'400':
$ref: '#/components/responses/ErrorResponse'
default:
$ref: '#/components/responses/ErrorResponse'
security:
- {}
- JWT: []
parameters: []
@@ -0,0 +1,34 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ******************************************************************
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
// ******************************************************************
/**
* @public
*/
export type GetManifests = {
response: { [key: string]: string } | Error | Error;
};
/**
* no description
*/
export type EndpointMap = {
'#get|/manifests': GetManifests;
};
@@ -0,0 +1,17 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './DefaultApi.server';
@@ -0,0 +1,18 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './apis';
export * from './router';
@@ -0,0 +1,29 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ******************************************************************
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
// ******************************************************************
/**
* @public
*/
export interface ErrorError {
name: string;
message: string;
stack?: string;
code?: string;
}
@@ -0,0 +1,27 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ******************************************************************
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
// ******************************************************************
/**
* @public
*/
export interface ErrorRequest {
method: string;
url: string;
}
@@ -0,0 +1,26 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ******************************************************************
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
// ******************************************************************
/**
* @public
*/
export interface ErrorResponse {
statusCode: number;
}
@@ -0,0 +1,33 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ******************************************************************
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
// ******************************************************************
import { ErrorError } from '../models/ErrorError.model';
import { ErrorRequest } from '../models/ErrorRequest.model';
import { ErrorResponse } from '../models/ErrorResponse.model';
/**
* @public
*/
export interface ModelError {
[key: string]: any;
error: ErrorError;
request?: ErrorRequest;
response: ErrorResponse;
}
@@ -0,0 +1,20 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from '../models/ErrorError.model';
export * from '../models/ErrorRequest.model';
export * from '../models/ErrorResponse.model';
export * from '../models/ModelError.model';
@@ -0,0 +1,161 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ******************************************************************
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
// ******************************************************************
import { createValidatedOpenApiRouterFromGeneratedEndpointMap } from '@backstage/backend-openapi-utils';
import { EndpointMap } from './';
export const spec = {
openapi: '3.0.3',
info: {
title: 'core.dynamicplugins.frontendRemotes',
version: '1',
description:
'The Backstage backend plugin that serves the frontend plugins module federation manifests and assets',
license: {
name: 'Apache-2.0',
url: 'http://www.apache.org/licenses/LICENSE-2.0.html',
},
contact: {},
},
servers: [
{
url: '/',
},
],
components: {
examples: {},
headers: {},
parameters: {},
requestBodies: {},
responses: {
ErrorResponse: {
description: 'An error response from the backend.',
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Error',
},
},
},
},
},
schemas: {
Error: {
type: 'object',
properties: {
error: {
type: 'object',
properties: {
name: {
type: 'string',
},
message: {
type: 'string',
},
stack: {
type: 'string',
},
code: {
type: 'string',
},
},
required: ['name', 'message'],
},
request: {
type: 'object',
properties: {
method: {
type: 'string',
},
url: {
type: 'string',
},
},
required: ['method', 'url'],
},
response: {
type: 'object',
properties: {
statusCode: {
type: 'number',
},
},
required: ['statusCode'],
},
},
required: ['error', 'response'],
additionalProperties: {},
},
},
securitySchemes: {
JWT: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
},
},
paths: {
'/manifests': {
get: {
operationId: 'GetManifests',
description:
'Get the Module Federation manifest files of dynamic frontend plugins.',
responses: {
'200': {
description: '',
content: {
'application/json': {
schema: {
type: 'object',
properties: {},
additionalProperties: {
type: 'string',
},
},
},
},
},
'400': {
$ref: '#/components/responses/ErrorResponse',
},
default: {
$ref: '#/components/responses/ErrorResponse',
},
},
security: [
{},
{
JWT: [],
},
],
parameters: [],
},
},
},
} as const;
export const createOpenApiRouter = async (
options?: Parameters<
typeof createValidatedOpenApiRouterFromGeneratedEndpointMap
>['1'],
) =>
createValidatedOpenApiRouterFromGeneratedEndpointMap<EndpointMap>(
spec,
options,
);
@@ -0,0 +1,17 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './generated';
@@ -0,0 +1,16 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { frontendRemotesServerPlugin as default } from './plugin';
@@ -0,0 +1,57 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
coreServices,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
import { createRouter } from './router';
import { dynamicPluginsServiceRef } from '@backstage/backend-dynamic-feature-service';
/**
* frontendRemotesServerPlugin backend plugin
*
* @internal
*/
export const frontendRemotesServerPlugin = createBackendPlugin({
pluginId: 'core.dynamicplugins.frontendRemotes',
register(env) {
env.registerInit({
deps: {
logger: coreServices.logger,
auth: coreServices.auth,
httpAuth: coreServices.httpAuth,
httpRouter: coreServices.httpRouter,
discovery: coreServices.discovery,
metadata: coreServices.pluginMetadata,
dynamicPlugins: dynamicPluginsServiceRef,
},
async init({ logger, httpRouter, discovery, metadata, dynamicPlugins }) {
httpRouter.use(
await createRouter({
logger,
discovery,
metadata,
dynamicPlugins,
}),
);
httpRouter.addAuthPolicy({
path: `/`,
allow: 'unauthenticated',
});
},
});
},
});
@@ -0,0 +1,100 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
DiscoveryService,
LoggerService,
PluginMetadataService,
} from '@backstage/backend-plugin-api';
import express from 'express';
import { createOpenApiRouter } from '../schema/openapi';
import { DynamicPluginProvider } from '@backstage/backend-dynamic-feature-service';
import { ManifestFileName } from '@module-federation/sdk';
import * as fs from 'fs';
import * as path from 'path';
import * as url from 'url';
export async function createRouter({
logger,
discovery,
metadata,
dynamicPlugins,
}: {
logger: LoggerService;
discovery: DiscoveryService;
metadata: PluginMetadataService;
dynamicPlugins: DynamicPluginProvider;
}): Promise<express.Router> {
const typedRouter = await createOpenApiRouter();
const externalBaseUrl = await discovery.getExternalBaseUrl(metadata.getId());
const frontendPluginManifests: {
[key: string]: string;
} = {};
for (const plugin of dynamicPlugins.frontendPlugins()) {
const pluginScannedPackage = dynamicPlugins.getScannedPackage(plugin);
const pkgDistLocation = path.resolve(
url.fileURLToPath(pluginScannedPackage.location),
'dist',
);
const pkgManifestLocation = path.resolve(pkgDistLocation, ManifestFileName);
if (!fs.existsSync(pkgManifestLocation)) {
logger.warn(
`Could not find '${pkgManifestLocation}' for frontend plugin ${plugin.name}@${plugin.version}`,
);
continue;
}
let moduleName: string | undefined;
try {
const pkgManifest = JSON.parse(
fs.readFileSync(pkgManifestLocation).toString(),
);
moduleName = pkgManifest.name;
if (!moduleName) {
logger.error(
`Dynamic frontend plugin module name not found in manifest for plugin ${plugin.name}@${plugin.version}`,
);
continue;
}
} catch (error) {
logger.error(
`Dynamic frontend plugin manifest could not be loaded for plugin ${plugin.name}@${plugin.version}`,
);
continue;
}
const remoteAssetsPrefix = `/remotes/${plugin.name}`;
typedRouter.use(remoteAssetsPrefix, express.static(pkgDistLocation));
logger.info(
`Exposed dynamic frontend plugin '${plugin.name}' from '${pluginScannedPackage.location}' `,
);
frontendPluginManifests[
plugin.name
] = `${externalBaseUrl}/remotes/${plugin.name}/${ManifestFileName}`;
}
typedRouter.get('/manifests', (_, res) => {
res.status(200).json(frontendPluginManifests);
});
return typedRouter;
}
+11 -88
View File
@@ -3335,16 +3335,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
version: 7.26.7
resolution: "@babel/runtime@npm:7.26.7"
dependencies:
regenerator-runtime: "npm:^0.14.0"
checksum: 10/c7a661a6836b332d9d2e047cba77ba1862c1e4f78cec7146db45808182ef7636d8a7170be9797e5d8fd513180bffb9fa16f6ca1c69341891efec56113cf22bfc
languageName: node
linkType: hard
"@babel/runtime@npm:^7.26.10":
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.26.10, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
version: 7.27.0
resolution: "@babel/runtime@npm:7.27.0"
dependencies:
@@ -3665,6 +3656,7 @@ __metadata:
dependencies:
"@backstage/backend-app-api": "workspace:^"
"@backstage/backend-defaults": "workspace:^"
"@backstage/backend-openapi-utils": "workspace:^"
"@backstage/backend-plugin-api": "workspace:^"
"@backstage/backend-test-utils": "workspace:^"
"@backstage/cli": "workspace:^"
@@ -3684,11 +3676,14 @@ __metadata:
"@backstage/plugin-scaffolder-node": "workspace:^"
"@backstage/plugin-search-backend-node": "workspace:^"
"@backstage/plugin-search-common": "workspace:^"
"@backstage/repo-tools": "workspace:^"
"@backstage/types": "workspace:^"
"@manypkg/get-packages": "npm:^1.1.3"
"@module-federation/sdk": "npm:^0.9.0"
"@types/express": "npm:^4.17.6"
chokidar: "npm:^3.5.3"
express: "npm:^4.17.1"
express-promise-router: "npm:^4.1.0"
fs-extra: "npm:^11.2.0"
lodash: "npm:^4.17.21"
triple-beam: "npm:^1.4.1"
@@ -20558,16 +20553,7 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:*, @types/node@npm:>=13.7.0, @types/node@npm:^22.0.0":
version: 22.13.9
resolution: "@types/node@npm:22.13.9"
dependencies:
undici-types: "npm:~6.20.0"
checksum: 10/23560df3ee99c907179c688754486b969a72144f2e2bdefe974d320dddc5ca8f93365842966ecbd5c5bba34e919fc1a5a6627712beb8e7f71d71347dcf414a35
languageName: node
linkType: hard
"@types/node@npm:>=12, @types/node@npm:>=12.0.0, @types/node@npm:>=18.0.0":
"@types/node@npm:*, @types/node@npm:>=12, @types/node@npm:>=12.0.0, @types/node@npm:>=13.7.0, @types/node@npm:>=18.0.0, @types/node@npm:^22.0.0":
version: 22.13.10
resolution: "@types/node@npm:22.13.10"
dependencies:
@@ -30842,25 +30828,7 @@ __metadata:
languageName: node
linkType: hard
"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6":
version: 1.2.6
resolution: "get-intrinsic@npm:1.2.6"
dependencies:
call-bind-apply-helpers: "npm:^1.0.1"
dunder-proto: "npm:^1.0.0"
es-define-property: "npm:^1.0.1"
es-errors: "npm:^1.3.0"
es-object-atoms: "npm:^1.0.0"
function-bind: "npm:^1.1.2"
gopd: "npm:^1.2.0"
has-symbols: "npm:^1.1.0"
hasown: "npm:^2.0.2"
math-intrinsics: "npm:^1.0.0"
checksum: 10/a1ffae6d7893a6fa0f4d1472adbc85095edd6b3b0943ead97c3738539cecb19d422ff4d48009eed8c3c27ad678c2b1e38a83b1a1e96b691d13ed8ecefca1068d
languageName: node
linkType: hard
"get-intrinsic@npm:^1.2.1":
"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6":
version: 1.3.0
resolution: "get-intrinsic@npm:1.3.0"
dependencies:
@@ -31924,7 +31892,7 @@ __metadata:
languageName: node
linkType: hard
"html-entities@npm:^2.1.0, html-entities@npm:^2.4.0, html-entities@npm:^2.5.2":
"html-entities@npm:^2.1.0, html-entities@npm:^2.5.2":
version: 2.5.2
resolution: "html-entities@npm:2.5.2"
checksum: 10/4ec12ebdf2d5ba8192c68e1aef3c1e4a4f36b29246a0a88464fe278a54517d0196d3489af46a3145c7ecacb4fc5fd50497be19eb713b810acab3f0efcf36fdc2
@@ -32125,7 +32093,7 @@ __metadata:
languageName: node
linkType: hard
"http-proxy-middleware@npm:^2.0.0, http-proxy-middleware@npm:^2.0.3, http-proxy-middleware@npm:^2.0.6, http-proxy-middleware@npm:^2.0.7":
"http-proxy-middleware@npm:^2.0.0, http-proxy-middleware@npm:^2.0.6, http-proxy-middleware@npm:^2.0.7":
version: 2.0.7
resolution: "http-proxy-middleware@npm:2.0.7"
dependencies:
@@ -36389,7 +36357,7 @@ __metadata:
languageName: node
linkType: hard
"math-intrinsics@npm:^1.0.0, math-intrinsics@npm:^1.1.0":
"math-intrinsics@npm:^1.1.0":
version: 1.1.0
resolution: "math-intrinsics@npm:1.1.0"
checksum: 10/11df2eda46d092a6035479632e1ec865b8134bdfc4bd9e571a656f4191525404f13a283a515938c3a8de934dbfd9c09674d9da9fa831e6eb7e22b50b197d2edd
@@ -47769,7 +47737,7 @@ __metadata:
languageName: node
linkType: hard
"webpack-dev-server@npm:5.2.0":
"webpack-dev-server@npm:5.2.0, webpack-dev-server@npm:^5.0.0":
version: 5.2.0
resolution: "webpack-dev-server@npm:5.2.0"
dependencies:
@@ -47813,51 +47781,6 @@ __metadata:
languageName: node
linkType: hard
"webpack-dev-server@npm:^5.0.0":
version: 5.1.0
resolution: "webpack-dev-server@npm:5.1.0"
dependencies:
"@types/bonjour": "npm:^3.5.13"
"@types/connect-history-api-fallback": "npm:^1.5.4"
"@types/express": "npm:^4.17.21"
"@types/serve-index": "npm:^1.9.4"
"@types/serve-static": "npm:^1.15.5"
"@types/sockjs": "npm:^0.3.36"
"@types/ws": "npm:^8.5.10"
ansi-html-community: "npm:^0.0.8"
bonjour-service: "npm:^1.2.1"
chokidar: "npm:^3.6.0"
colorette: "npm:^2.0.10"
compression: "npm:^1.7.4"
connect-history-api-fallback: "npm:^2.0.0"
express: "npm:^4.19.2"
graceful-fs: "npm:^4.2.6"
html-entities: "npm:^2.4.0"
http-proxy-middleware: "npm:^2.0.3"
ipaddr.js: "npm:^2.1.0"
launch-editor: "npm:^2.6.1"
open: "npm:^10.0.3"
p-retry: "npm:^6.2.0"
schema-utils: "npm:^4.2.0"
selfsigned: "npm:^2.4.1"
serve-index: "npm:^1.9.1"
sockjs: "npm:^0.3.24"
spdy: "npm:^4.0.2"
webpack-dev-middleware: "npm:^7.4.2"
ws: "npm:^8.18.0"
peerDependencies:
webpack: ^5.0.0
peerDependenciesMeta:
webpack:
optional: true
webpack-cli:
optional: true
bin:
webpack-dev-server: bin/webpack-dev-server.js
checksum: 10/f23255681cc5e2c2709b23ca7b2185aeed83b1c9912657d4512eda8685625a46d7a103a92446494a55fe2afdfab936f9bd4f037d20b52f7fdfff303e7e7199c7
languageName: node
linkType: hard
"webpack-hot-middleware@npm:^2.25.1":
version: 2.26.1
resolution: "webpack-hot-middleware@npm:2.26.1"