Merge branch 'master' of github.com:backstage/backstage into entity-labels-card
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
Added `legacyPlugin` and the lower level `makeLegacyPlugin` wrappers that convert legacy plugins to the new backend system. This will be used to ease the future migration to the new backend system, but we discourage use of it for now.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-tasks': patch
|
||||
---
|
||||
|
||||
Added a new static `TaskScheduler.forPlugin` method.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
'@backstage/plugin-bazaar': patch
|
||||
'@backstage/plugin-fossa': patch
|
||||
'@backstage/plugin-github-actions': patch
|
||||
'@backstage/plugin-home': patch
|
||||
'@backstage/plugin-jenkins': patch
|
||||
'@backstage/plugin-kubernetes': patch
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
'@backstage/plugin-stack-overflow': patch
|
||||
'@backstage/plugin-tech-radar': patch
|
||||
'@backstage/plugin-xcmetrics': patch
|
||||
---
|
||||
|
||||
Small updates to some paragraph components to ensure theme typography properties are inherited correctly.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-sentry': minor
|
||||
---
|
||||
|
||||
Add Sentry "Create Project" Scaffolder as new package
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-test-utils': patch
|
||||
---
|
||||
|
||||
The backend started by `startTestBackend` now has default implementations of all core services. It now also returns a `TestBackend` instance, which provides access to the underlying `server` that can be used with testing libraries such as `supertest`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-tech-insights-backend': patch
|
||||
---
|
||||
|
||||
Expose optional `persistenceContext` on `TechInsights` construction to enable integrators to provide their own database implementations for fact handling.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-app-api': patch
|
||||
---
|
||||
|
||||
Exported the default root HTTP router implementation as `DefaultRootHttpRouter`. It only implements the routing layer and needs to be exposed via an HTTP server similar to the built-in setup in the `rootHttpRouterFactory`.
|
||||
@@ -247,6 +247,7 @@ module.exports = {
|
||||
{
|
||||
forbid: [
|
||||
{ element: 'button', message: 'use MUI <Button> instead' },
|
||||
{ element: 'p', message: 'use MUI <Typography> instead' },
|
||||
{
|
||||
element: 'span',
|
||||
message: 'use a MUI <Typography> variant instead',
|
||||
|
||||
@@ -226,3 +226,4 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
|
||||
| [Tractable AI](https://tractable.ai/) | [Stephan Schielke](https://github.com/stephanschielke) | We are hitting a critical point in our scale (100+ engineers) and need to get a handle on discoverability and ownership. The Service Catalog, TechDocs and Search are essential to us to achieve that. |
|
||||
| [Garanti BBVA Teknoloji](https://www.linkedin.com/company/garanti-teknoloji/) | [Caglar Cataloglu](https://github.com/crozwise) | We are using Backstage focusing on improving experience of developers, minimizing friction from idea to production. We call our portal as "Hyperspace" and very excited for our community (2000+ engineers) that finally we have a platform to boost our productivity!
|
||||
| [Booking.com](https://www.linkedin.com/company/booking.com/) | [Mesut Yilmazyildirim](https://www.linkedin.com/in/myilmazyildirim) | We are adopting Backstage as the new reliability platform inside the company. We are migrating UIs of our internal developer tools to Backstage for a better user experience.
|
||||
| [Swissquote Bank](https://swissquote.com/company/jobs/open-positions) | [Bruno Rocha](https://www.linkedin.com/in/bruno-rocha1/) | Integrating Backstage as the visualization layer & tactical overview of our services and teams.
|
||||
|
||||
@@ -12,6 +12,7 @@ import { CorsOptions } from 'cors';
|
||||
import { ErrorRequestHandler } from 'express';
|
||||
import { Express as Express_2 } from 'express';
|
||||
import { ExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { Handler } from 'express';
|
||||
import { HelmetOptions } from 'helmet';
|
||||
import * as http from 'http';
|
||||
import { HttpRouterService } from '@backstage/backend-plugin-api';
|
||||
@@ -79,6 +80,21 @@ export const databaseFactory: (
|
||||
options?: undefined,
|
||||
) => ServiceFactory<PluginDatabaseManager>;
|
||||
|
||||
// @public
|
||||
export class DefaultRootHttpRouter implements RootHttpRouterService {
|
||||
// (undocumented)
|
||||
static create(options?: DefaultRootHttpRouterOptions): DefaultRootHttpRouter;
|
||||
// (undocumented)
|
||||
handler(): Handler;
|
||||
// (undocumented)
|
||||
use(path: string, handler: Handler): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface DefaultRootHttpRouterOptions {
|
||||
indexPath?: string | false;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const discoveryFactory: (
|
||||
options?: undefined,
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"express-promise-router": "^4.1.0",
|
||||
"fs-extra": "10.1.0",
|
||||
"helmet": "^6.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"minimatch": "^5.0.0",
|
||||
"morgan": "^1.10.0",
|
||||
"node-forge": "^1.3.1",
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
coreServices,
|
||||
createServiceFactory,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
|
||||
/** @public */
|
||||
export const databaseFactory = createServiceFactory({
|
||||
@@ -28,7 +29,16 @@ export const databaseFactory = createServiceFactory({
|
||||
plugin: coreServices.pluginMetadata,
|
||||
},
|
||||
async factory({ config }) {
|
||||
const databaseManager = DatabaseManager.fromConfig(config);
|
||||
const databaseManager = config.getOptional('backend.database')
|
||||
? DatabaseManager.fromConfig(config)
|
||||
: DatabaseManager.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: {
|
||||
database: { client: 'better-sqlite3', connection: ':memory:' },
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return async ({ plugin }) => {
|
||||
return databaseManager.forPlugin(plugin.getId());
|
||||
};
|
||||
|
||||
+10
-4
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { RestrictedIndexedRouter } from './RestrictedIndexedRouter';
|
||||
import { DefaultRootHttpRouter } from './DefaultRootHttpRouter';
|
||||
|
||||
describe('RestrictedIndexedRouter', () => {
|
||||
describe('DefaultRootHttpRouter', () => {
|
||||
it.each([
|
||||
[['/b'], '/a'],
|
||||
[['/a'], '/aa/b'],
|
||||
@@ -25,7 +25,7 @@ describe('RestrictedIndexedRouter', () => {
|
||||
[['/b/a'], '/a'],
|
||||
[['/a'], '/aa'],
|
||||
])(`with existing paths %s, adds %s without conflict`, (existing, added) => {
|
||||
const router = new RestrictedIndexedRouter(false);
|
||||
const router = DefaultRootHttpRouter.create();
|
||||
for (const path of existing) {
|
||||
router.use(path, () => {});
|
||||
}
|
||||
@@ -39,7 +39,7 @@ describe('RestrictedIndexedRouter', () => {
|
||||
])(
|
||||
`find conflict when existing paths %s, adds %s`,
|
||||
(existing, added, conflict) => {
|
||||
const router = new RestrictedIndexedRouter(false);
|
||||
const router = DefaultRootHttpRouter.create();
|
||||
for (const path of existing) {
|
||||
router.use(path, () => {});
|
||||
}
|
||||
@@ -48,4 +48,10 @@ describe('RestrictedIndexedRouter', () => {
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it('should not be possible to supply an empty indexPath', () => {
|
||||
expect(() => DefaultRootHttpRouter.create({ indexPath: '' })).toThrow(
|
||||
'indexPath option may not be an empty string',
|
||||
);
|
||||
});
|
||||
});
|
||||
+41
-5
@@ -16,23 +16,59 @@
|
||||
|
||||
import { RootHttpRouterService } from '@backstage/backend-plugin-api';
|
||||
import { Handler, Router } from 'express';
|
||||
import trimEnd from 'lodash/trimEnd';
|
||||
|
||||
function normalizePath(path: string): string {
|
||||
return path.replace(/\/*$/, '/');
|
||||
return `${trimEnd(path, '/')}/`;
|
||||
}
|
||||
|
||||
export class RestrictedIndexedRouter implements RootHttpRouterService {
|
||||
#indexPath?: false | string;
|
||||
/**
|
||||
* Options for the {@link DefaultRootHttpRouter} class.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DefaultRootHttpRouterOptions {
|
||||
/**
|
||||
* The path to forward all unmatched requests to. Defaults to '/api/app' if
|
||||
* not given. Disables index path behavior if false is given.
|
||||
*/
|
||||
indexPath?: string | false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation of the {@link @backstage/backend-plugin-api#RootHttpRouterService} interface for
|
||||
* {@link @backstage/backend-plugin-api#coreServices.rootHttpRouter}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class DefaultRootHttpRouter implements RootHttpRouterService {
|
||||
#indexPath?: string;
|
||||
|
||||
#router = Router();
|
||||
#namedRoutes = Router();
|
||||
#indexRouter = Router();
|
||||
#existingPaths = new Array<string>();
|
||||
|
||||
constructor(indexPath?: false | string) {
|
||||
static create(options?: DefaultRootHttpRouterOptions) {
|
||||
let indexPath;
|
||||
if (options?.indexPath === false) {
|
||||
indexPath = undefined;
|
||||
} else if (options?.indexPath === undefined) {
|
||||
indexPath = '/api/app';
|
||||
} else if (options?.indexPath === '') {
|
||||
throw new Error('indexPath option may not be an empty string');
|
||||
} else {
|
||||
indexPath = options.indexPath;
|
||||
}
|
||||
return new DefaultRootHttpRouter(indexPath);
|
||||
}
|
||||
|
||||
private constructor(indexPath?: string) {
|
||||
this.#indexPath = indexPath;
|
||||
this.#router.use(this.#namedRoutes);
|
||||
this.#router.use(this.#indexRouter);
|
||||
if (this.#indexPath) {
|
||||
this.#router.use(this.#indexRouter);
|
||||
}
|
||||
}
|
||||
|
||||
use(path: string, handler: Handler) {
|
||||
@@ -14,8 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { rootHttpRouterFactory } from './rootHttpRouterFactory';
|
||||
export type {
|
||||
RootHttpRouterFactoryOptions,
|
||||
RootHttpRouterConfigureOptions,
|
||||
export {
|
||||
rootHttpRouterFactory,
|
||||
type RootHttpRouterFactoryOptions,
|
||||
type RootHttpRouterConfigureOptions,
|
||||
} from './rootHttpRouterFactory';
|
||||
export {
|
||||
DefaultRootHttpRouter,
|
||||
type DefaultRootHttpRouterOptions,
|
||||
} from './DefaultRootHttpRouter';
|
||||
|
||||
+4
-4
@@ -27,7 +27,7 @@ import {
|
||||
MiddlewareFactory,
|
||||
readHttpServerOptions,
|
||||
} from '../../../http';
|
||||
import { RestrictedIndexedRouter } from './RestrictedIndexedRouter';
|
||||
import { DefaultRootHttpRouter } from './DefaultRootHttpRouter';
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -46,7 +46,8 @@ export interface RootHttpRouterConfigureOptions {
|
||||
*/
|
||||
export type RootHttpRouterFactoryOptions = {
|
||||
/**
|
||||
* The path to forward all unmatched requests to. Defaults to '/api/app'
|
||||
* The path to forward all unmatched requests to. Defaults to '/api/app' if
|
||||
* not given. Disables index path behavior if false is given.
|
||||
*/
|
||||
indexPath?: string | false;
|
||||
|
||||
@@ -82,11 +83,10 @@ export const rootHttpRouterFactory = createServiceFactory({
|
||||
configure = defaultConfigure,
|
||||
}: RootHttpRouterFactoryOptions = {},
|
||||
) {
|
||||
const router = new RestrictedIndexedRouter(indexPath ?? '/api/app');
|
||||
const logger = rootLogger.child({ service: 'rootHttpRouter' });
|
||||
|
||||
const app = express();
|
||||
|
||||
const router = DefaultRootHttpRouter.create({ indexPath });
|
||||
const middleware = MiddlewareFactory.create({ config, logger });
|
||||
|
||||
configure({
|
||||
|
||||
+10
-5
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { loggerToWinstonLogger } from '@backstage/backend-common';
|
||||
import {
|
||||
coreServices,
|
||||
createServiceFactory,
|
||||
@@ -24,13 +25,17 @@ import { TaskScheduler } from '@backstage/backend-tasks';
|
||||
export const schedulerFactory = createServiceFactory({
|
||||
service: coreServices.scheduler,
|
||||
deps: {
|
||||
config: coreServices.config,
|
||||
plugin: coreServices.pluginMetadata,
|
||||
databaseManager: coreServices.database,
|
||||
logger: coreServices.logger,
|
||||
},
|
||||
async factory({ config }) {
|
||||
const taskScheduler = TaskScheduler.fromConfig(config);
|
||||
return async ({ plugin }) => {
|
||||
return taskScheduler.forPlugin(plugin.getId());
|
||||
async factory() {
|
||||
return async ({ plugin, databaseManager, logger }) => {
|
||||
return TaskScheduler.forPlugin({
|
||||
pluginId: plugin.getId(),
|
||||
databaseManager,
|
||||
logger: loggerToWinstonLogger(logger),
|
||||
});
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import aws from 'aws-sdk';
|
||||
import { AwsS3Integration } from '@backstage/integration';
|
||||
import { AzureIntegration } from '@backstage/integration';
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { BitbucketCloudIntegration } from '@backstage/integration';
|
||||
import { BitbucketIntegration } from '@backstage/integration';
|
||||
import { BitbucketServerIntegration } from '@backstage/integration';
|
||||
@@ -16,6 +17,7 @@ import { CacheClient } from '@backstage/backend-plugin-api';
|
||||
import { CacheClientOptions } from '@backstage/backend-plugin-api';
|
||||
import { CacheClientSetOptions } from '@backstage/backend-plugin-api';
|
||||
import { Config } from '@backstage/config';
|
||||
import { ConfigService } from '@backstage/backend-plugin-api';
|
||||
import cors from 'cors';
|
||||
import Docker from 'dockerode';
|
||||
import { Duration } from 'luxon';
|
||||
@@ -26,6 +28,7 @@ import { GiteaIntegration } from '@backstage/integration';
|
||||
import { GithubCredentialsProvider } from '@backstage/integration';
|
||||
import { GithubIntegration } from '@backstage/integration';
|
||||
import { GitLabIntegration } from '@backstage/integration';
|
||||
import { IdentityService } from '@backstage/backend-plugin-api';
|
||||
import { isChildPath } from '@backstage/cli-common';
|
||||
import { Knex } from 'knex';
|
||||
import { KubeConfig } from '@kubernetes/client-node';
|
||||
@@ -33,6 +36,7 @@ import { LoadConfigOptionsRemote } from '@backstage/config-loader';
|
||||
import { Logger } from 'winston';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { MergeResult } from 'isomorphic-git';
|
||||
import { PermissionsService } from '@backstage/backend-plugin-api';
|
||||
import { CacheService as PluginCacheManager } from '@backstage/backend-plugin-api';
|
||||
import { DatabaseService as PluginDatabaseManager } from '@backstage/backend-plugin-api';
|
||||
import { DiscoveryService as PluginEndpointDiscovery } from '@backstage/backend-plugin-api';
|
||||
@@ -47,10 +51,12 @@ import { ReadUrlOptions } from '@backstage/backend-plugin-api';
|
||||
import { ReadUrlResponse } from '@backstage/backend-plugin-api';
|
||||
import { RequestHandler } from 'express';
|
||||
import { Router } from 'express';
|
||||
import { SchedulerService } from '@backstage/backend-plugin-api';
|
||||
import { SearchOptions } from '@backstage/backend-plugin-api';
|
||||
import { SearchResponse } from '@backstage/backend-plugin-api';
|
||||
import { SearchResponseFile } from '@backstage/backend-plugin-api';
|
||||
import { Server } from 'http';
|
||||
import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
import { TokenManagerService as TokenManager } from '@backstage/backend-plugin-api';
|
||||
import { TransportStreamOptions } from 'winston-transport';
|
||||
import { UrlReaderService as UrlReader } from '@backstage/backend-plugin-api';
|
||||
@@ -500,6 +506,35 @@ export type KubernetesContainerRunnerOptions = {
|
||||
timeoutMs?: number;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type LegacyCreateRouter<TEnv> = (deps: TEnv) => Promise<RequestHandler>;
|
||||
|
||||
// @public
|
||||
export const legacyPlugin: (
|
||||
name: string,
|
||||
createRouterImport: Promise<{
|
||||
default: LegacyCreateRouter<
|
||||
TransformedEnv<
|
||||
{
|
||||
cache: PluginCacheManager;
|
||||
config: ConfigService;
|
||||
database: PluginDatabaseManager;
|
||||
discovery: PluginEndpointDiscovery;
|
||||
logger: LoggerService;
|
||||
permissions: PermissionsService;
|
||||
scheduler: SchedulerService;
|
||||
tokenManager: TokenManager;
|
||||
reader: UrlReader;
|
||||
identity: IdentityService;
|
||||
},
|
||||
{
|
||||
logger: (log: LoggerService) => Logger;
|
||||
}
|
||||
>
|
||||
>;
|
||||
}>,
|
||||
) => BackendFeature;
|
||||
|
||||
// @public
|
||||
export function loadBackendConfig(options: {
|
||||
logger: LoggerService;
|
||||
@@ -513,6 +548,24 @@ export function loggerToWinstonLogger(
|
||||
opts?: TransportStreamOptions,
|
||||
): Logger;
|
||||
|
||||
// @public
|
||||
export function makeLegacyPlugin<
|
||||
TEnv extends Record<string, unknown>,
|
||||
TEnvTransforms extends {
|
||||
[key in keyof TEnv]?: (dep: TEnv[key]) => unknown;
|
||||
},
|
||||
>(
|
||||
envMapping: {
|
||||
[key in keyof TEnv]: ServiceRef<TEnv[key]>;
|
||||
},
|
||||
envTransforms: TEnvTransforms,
|
||||
): (
|
||||
name: string,
|
||||
createRouterImport: Promise<{
|
||||
default: LegacyCreateRouter<TransformedEnv<TEnv, TEnvTransforms>>;
|
||||
}>,
|
||||
) => BackendFeature;
|
||||
|
||||
// @public
|
||||
export function notFoundHandler(): RequestHandler;
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export { legacyPlugin, makeLegacyPlugin } from './legacy';
|
||||
export type { LegacyCreateRouter } from './legacy';
|
||||
export * from './cache';
|
||||
export { loadBackendConfig } from './config';
|
||||
export * from './context';
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2023 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,
|
||||
ServiceRef,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { RequestHandler } from 'express';
|
||||
import { loggerToWinstonLogger } from './logging';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type LegacyCreateRouter<TEnv> = (deps: TEnv) => Promise<RequestHandler>;
|
||||
|
||||
/** @ignore */
|
||||
type TransformedEnv<
|
||||
TEnv extends Record<string, unknown>,
|
||||
TEnvTransforms extends { [key in keyof TEnv]?: (dep: TEnv[key]) => unknown },
|
||||
> = {
|
||||
[key in keyof TEnv]: TEnvTransforms[key] extends (dep: TEnv[key]) => infer R
|
||||
? R
|
||||
: TEnv[key];
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new custom plugin compatibility wrapper.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* Usually you can use {@link legacyPlugin} directly instead, but you might
|
||||
* need to use this if you have customized the plugin environment in your backend.
|
||||
*/
|
||||
export function makeLegacyPlugin<
|
||||
TEnv extends Record<string, unknown>,
|
||||
TEnvTransforms extends { [key in keyof TEnv]?: (dep: TEnv[key]) => unknown },
|
||||
>(
|
||||
envMapping: { [key in keyof TEnv]: ServiceRef<TEnv[key]> },
|
||||
envTransforms: TEnvTransforms,
|
||||
) {
|
||||
return (
|
||||
name: string,
|
||||
createRouterImport: Promise<{
|
||||
default: LegacyCreateRouter<TransformedEnv<TEnv, TEnvTransforms>>;
|
||||
}>,
|
||||
) => {
|
||||
const compatPlugin = createBackendPlugin({
|
||||
id: name,
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { ...envMapping, _router: coreServices.httpRouter },
|
||||
async init({ _router, ...envDeps }) {
|
||||
const { default: createRouter } = await createRouterImport;
|
||||
const pluginEnv = Object.fromEntries(
|
||||
Object.entries(envDeps).map(([key, dep]) => {
|
||||
const transform = envTransforms[key];
|
||||
if (transform) {
|
||||
return [key, transform(dep)];
|
||||
}
|
||||
return [key, dep];
|
||||
}),
|
||||
);
|
||||
const router = await createRouter(
|
||||
pluginEnv as TransformedEnv<TEnv, TEnvTransforms>,
|
||||
);
|
||||
_router.use(router);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return compatPlugin();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a plugin from a legacy createRouter function and
|
||||
* register it with the http router based on the plugin id.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* This is intended to be used by plugin authors to ease the transition to the
|
||||
* new backend system.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
*```ts
|
||||
*backend.add(legacyPlugin('kafka', import('./plugins/kafka')));
|
||||
*```
|
||||
*/
|
||||
export const legacyPlugin = makeLegacyPlugin(
|
||||
{
|
||||
cache: coreServices.cache,
|
||||
config: coreServices.config,
|
||||
database: coreServices.database,
|
||||
discovery: coreServices.discovery,
|
||||
logger: coreServices.logger,
|
||||
permissions: coreServices.permissions,
|
||||
scheduler: coreServices.scheduler,
|
||||
tokenManager: coreServices.tokenManager,
|
||||
reader: coreServices.urlReader,
|
||||
identity: coreServices.identity,
|
||||
},
|
||||
{
|
||||
logger: log => loggerToWinstonLogger(log),
|
||||
},
|
||||
);
|
||||
@@ -8,6 +8,7 @@ import { DatabaseManager } from '@backstage/backend-common';
|
||||
import { Duration } from 'luxon';
|
||||
import { HumanDuration as HumanDuration_2 } from '@backstage/types';
|
||||
import { Logger } from 'winston';
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
|
||||
// @public @deprecated
|
||||
export type HumanDuration = HumanDuration_2;
|
||||
@@ -74,6 +75,12 @@ export class TaskScheduler {
|
||||
constructor(databaseManager: DatabaseManager, logger: Logger);
|
||||
forPlugin(pluginId: string): PluginTaskScheduler;
|
||||
// (undocumented)
|
||||
static forPlugin(opts: {
|
||||
pluginId: string;
|
||||
databaseManager: PluginDatabaseManager;
|
||||
logger: Logger;
|
||||
}): PluginTaskScheduler;
|
||||
// (undocumented)
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options?: {
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DatabaseManager, getRootLogger } from '@backstage/backend-common';
|
||||
import {
|
||||
DatabaseManager,
|
||||
getRootLogger,
|
||||
PluginDatabaseManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import { once } from 'lodash';
|
||||
import { Duration } from 'luxon';
|
||||
@@ -57,27 +61,35 @@ export class TaskScheduler {
|
||||
* @returns A {@link PluginTaskScheduler} instance
|
||||
*/
|
||||
forPlugin(pluginId: string): PluginTaskScheduler {
|
||||
const databaseFactory = once(async () => {
|
||||
const databaseManager = this.databaseManager.forPlugin(pluginId);
|
||||
const knex = await databaseManager.getClient();
|
||||
return TaskScheduler.forPlugin({
|
||||
pluginId,
|
||||
databaseManager: this.databaseManager.forPlugin(pluginId),
|
||||
logger: this.logger,
|
||||
});
|
||||
}
|
||||
|
||||
if (!databaseManager.migrations?.skip) {
|
||||
static forPlugin(opts: {
|
||||
pluginId: string;
|
||||
databaseManager: PluginDatabaseManager;
|
||||
logger: Logger;
|
||||
}): PluginTaskScheduler {
|
||||
const databaseFactory = once(async () => {
|
||||
const knex = await opts.databaseManager.getClient();
|
||||
|
||||
if (!opts.databaseManager.migrations?.skip) {
|
||||
await migrateBackendTasks(knex);
|
||||
}
|
||||
|
||||
const janitor = new PluginTaskSchedulerJanitor({
|
||||
knex,
|
||||
waitBetweenRuns: Duration.fromObject({ minutes: 1 }),
|
||||
logger: this.logger,
|
||||
logger: opts.logger,
|
||||
});
|
||||
janitor.start();
|
||||
|
||||
return knex;
|
||||
});
|
||||
|
||||
return new PluginTaskSchedulerImpl(
|
||||
databaseFactory,
|
||||
this.logger.child({ plugin: pluginId }),
|
||||
);
|
||||
return new PluginTaskSchedulerImpl(databaseFactory, opts.logger);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
```ts
|
||||
import { Backend } from '@backstage/backend-app-api';
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { ConfigService } from '@backstage/backend-plugin-api';
|
||||
import { ExtendedHttpServer } from '@backstage/backend-app-api';
|
||||
import { ExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { Knex } from 'knex';
|
||||
import { ServiceFactory } from '@backstage/backend-plugin-api';
|
||||
import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
@@ -13,6 +16,15 @@ import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
// @public (undocumented)
|
||||
export function isDockerDisabledForTests(): boolean;
|
||||
|
||||
// @public (undocumented)
|
||||
export const mockConfigFactory: (
|
||||
options?:
|
||||
| {
|
||||
data?: JsonObject | undefined;
|
||||
}
|
||||
| undefined,
|
||||
) => ServiceFactory<ConfigService>;
|
||||
|
||||
// @public
|
||||
export function setupRequestMockHandlers(worker: {
|
||||
listen: (t: any) => void;
|
||||
@@ -24,7 +36,14 @@ export function setupRequestMockHandlers(worker: {
|
||||
export function startTestBackend<
|
||||
TServices extends any[],
|
||||
TExtensionPoints extends any[],
|
||||
>(options: TestBackendOptions<TServices, TExtensionPoints>): Promise<Backend>;
|
||||
>(
|
||||
options: TestBackendOptions<TServices, TExtensionPoints>,
|
||||
): Promise<TestBackend>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface TestBackend extends Backend {
|
||||
readonly server: ExtendedHttpServer;
|
||||
}
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface TestBackendOptions<
|
||||
|
||||
@@ -39,7 +39,10 @@
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
"better-sqlite3": "^8.0.0",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"knex": "^2.0.0",
|
||||
"msw": "^0.49.0",
|
||||
"mysql2": "^2.2.5",
|
||||
@@ -48,7 +51,9 @@
|
||||
"uuid": "^8.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"supertest": "^6.1.3"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2023 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 { mockConfigFactory } from './mockConfigService';
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2022 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,
|
||||
createServiceFactory,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
|
||||
/** @public */
|
||||
export const mockConfigFactory = createServiceFactory({
|
||||
service: coreServices.config,
|
||||
deps: {},
|
||||
async factory(_, options?: { data?: JsonObject }) {
|
||||
return new ConfigReader(options?.data, 'mock-config');
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2023 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 { TokenManager } from '@backstage/backend-common';
|
||||
import {
|
||||
coreServices,
|
||||
createServiceFactory,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
class TokenManagerMock implements TokenManager {
|
||||
async getToken(): Promise<{ token: string }> {
|
||||
return { token: 'mock-token' };
|
||||
}
|
||||
async authenticate(token: string): Promise<void> {
|
||||
if (token !== 'mock-token') {
|
||||
throw new Error('Invalid token');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const mockTokenManagerFactory = createServiceFactory({
|
||||
service: coreServices.tokenManager,
|
||||
deps: {},
|
||||
async factory() {
|
||||
return async () => {
|
||||
return new TokenManagerMock();
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -15,3 +15,4 @@
|
||||
*/
|
||||
|
||||
export * from './wiring';
|
||||
export * from './implementations';
|
||||
|
||||
@@ -20,7 +20,11 @@ import {
|
||||
createServiceFactory,
|
||||
createServiceRef,
|
||||
coreServices,
|
||||
createBackendPlugin,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { Router } from 'express';
|
||||
import request from 'supertest';
|
||||
|
||||
import { startTestBackend } from './TestBackend';
|
||||
|
||||
// This bit makes sure that test backends are cleaned up properly
|
||||
@@ -156,4 +160,66 @@ describe('TestBackend', () => {
|
||||
await backend.stop();
|
||||
expect(shutdownSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should provide a set of default services', async () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const testPlugin = createBackendPlugin({
|
||||
id: 'test',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: {
|
||||
cache: coreServices.cache,
|
||||
config: coreServices.config,
|
||||
database: coreServices.database,
|
||||
discovery: coreServices.discovery,
|
||||
httpRouter: coreServices.httpRouter,
|
||||
lifecycle: coreServices.lifecycle,
|
||||
logger: coreServices.logger,
|
||||
permissions: coreServices.permissions,
|
||||
pluginMetadata: coreServices.pluginMetadata,
|
||||
rootHttpRouter: coreServices.rootHttpRouter,
|
||||
rootLifecycle: coreServices.rootLifecycle,
|
||||
rootLogger: coreServices.rootLogger,
|
||||
scheduler: coreServices.scheduler,
|
||||
tokenManager: coreServices.tokenManager,
|
||||
urlReader: coreServices.urlReader,
|
||||
},
|
||||
async init(deps) {
|
||||
expect(Object.keys(deps)).toHaveLength(15);
|
||||
expect(Object.values(deps)).not.toContain(undefined);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
await startTestBackend({
|
||||
services: [],
|
||||
features: [testPlugin()],
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow making requests via supertest', async () => {
|
||||
const testPlugin = createBackendPlugin({
|
||||
id: 'test',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: {
|
||||
httpRouter: coreServices.httpRouter,
|
||||
},
|
||||
async init({ httpRouter }) {
|
||||
const router = Router();
|
||||
router.use('/ping-me', (_, res) => res.json({ message: 'pong' }));
|
||||
httpRouter.use(router);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const { server } = await startTestBackend({ features: [testPlugin()] });
|
||||
|
||||
const res = await request(server).get('/api/test/ping-me');
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body).toEqual({ message: 'pong' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,15 +21,32 @@ import {
|
||||
rootLifecycleFactory,
|
||||
loggerFactory,
|
||||
rootLoggerFactory,
|
||||
cacheFactory,
|
||||
permissionsFactory,
|
||||
schedulerFactory,
|
||||
urlReaderFactory,
|
||||
databaseFactory,
|
||||
httpRouterFactory,
|
||||
MiddlewareFactory,
|
||||
createHttpServer,
|
||||
ExtendedHttpServer,
|
||||
DefaultRootHttpRouter,
|
||||
} from '@backstage/backend-app-api';
|
||||
import { SingleHostDiscovery } from '@backstage/backend-common';
|
||||
import {
|
||||
ServiceFactory,
|
||||
ServiceRef,
|
||||
createServiceFactory,
|
||||
BackendFeature,
|
||||
ExtensionPoint,
|
||||
coreServices,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
import { mockConfigFactory } from '../implementations/mockConfigService';
|
||||
import { mockTokenManagerFactory } from '../implementations/mockTokenManagerService';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import express from 'express';
|
||||
|
||||
/** @alpha */
|
||||
export interface TestBackendOptions<
|
||||
TServices extends any[],
|
||||
@@ -54,11 +71,30 @@ export interface TestBackendOptions<
|
||||
features?: BackendFeature[];
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
export interface TestBackend extends Backend {
|
||||
/**
|
||||
* Provides access to the underling HTTP server for use with utilities
|
||||
* such as `supertest`.
|
||||
*
|
||||
* If the root http router service has been replaced, this will throw an error.
|
||||
*/
|
||||
readonly server: ExtendedHttpServer;
|
||||
}
|
||||
|
||||
const defaultServiceFactories = [
|
||||
rootLoggerFactory(),
|
||||
loggerFactory(),
|
||||
cacheFactory(),
|
||||
databaseFactory(),
|
||||
httpRouterFactory(),
|
||||
lifecycleFactory(),
|
||||
loggerFactory(),
|
||||
mockConfigFactory(),
|
||||
mockTokenManagerFactory(),
|
||||
permissionsFactory(),
|
||||
rootLifecycleFactory(),
|
||||
rootLoggerFactory(),
|
||||
schedulerFactory(),
|
||||
urlReaderFactory(),
|
||||
];
|
||||
|
||||
const backendInstancesToCleanUp = new Array<Backend>();
|
||||
@@ -67,7 +103,9 @@ const backendInstancesToCleanUp = new Array<Backend>();
|
||||
export async function startTestBackend<
|
||||
TServices extends any[],
|
||||
TExtensionPoints extends any[],
|
||||
>(options: TestBackendOptions<TServices, TExtensionPoints>): Promise<Backend> {
|
||||
>(
|
||||
options: TestBackendOptions<TServices, TExtensionPoints>,
|
||||
): Promise<TestBackend> {
|
||||
const {
|
||||
services = [],
|
||||
extensionPoints = [],
|
||||
@@ -75,6 +113,65 @@ export async function startTestBackend<
|
||||
...otherOptions
|
||||
} = options;
|
||||
|
||||
let server: ExtendedHttpServer;
|
||||
|
||||
const rootHttpRouterFactory = createServiceFactory({
|
||||
service: coreServices.rootHttpRouter,
|
||||
deps: {
|
||||
config: coreServices.config,
|
||||
lifecycle: coreServices.rootLifecycle,
|
||||
rootLogger: coreServices.rootLogger,
|
||||
},
|
||||
async factory({ config, lifecycle, rootLogger }) {
|
||||
const router = DefaultRootHttpRouter.create();
|
||||
const logger = rootLogger.child({ service: 'rootHttpRouter' });
|
||||
|
||||
const app = express();
|
||||
|
||||
const middleware = MiddlewareFactory.create({ config, logger });
|
||||
|
||||
app.use(router.handler());
|
||||
app.use(middleware.notFound());
|
||||
app.use(middleware.error());
|
||||
|
||||
server = await createHttpServer(
|
||||
app,
|
||||
{ listen: { host: '', port: 0 } },
|
||||
{ logger },
|
||||
);
|
||||
|
||||
lifecycle.addShutdownHook({
|
||||
async fn() {
|
||||
await server.stop();
|
||||
},
|
||||
logger,
|
||||
});
|
||||
|
||||
await server.start();
|
||||
|
||||
return router;
|
||||
},
|
||||
});
|
||||
|
||||
const discoveryFactory = createServiceFactory({
|
||||
service: coreServices.discovery,
|
||||
deps: {
|
||||
rootHttpRouter: coreServices.rootHttpRouter,
|
||||
},
|
||||
async factory() {
|
||||
if (!server) {
|
||||
throw new Error('Test server not started yet');
|
||||
}
|
||||
const port = server.port();
|
||||
const discovery = SingleHostDiscovery.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: { baseUrl: `http://localhost:${port}`, listen: { port } },
|
||||
}),
|
||||
);
|
||||
return async () => discovery;
|
||||
},
|
||||
});
|
||||
|
||||
const factories = services.map(serviceDef => {
|
||||
if (Array.isArray(serviceDef)) {
|
||||
// if type is ExtensionPoint?
|
||||
@@ -107,7 +204,7 @@ export async function startTestBackend<
|
||||
|
||||
const backend = createSpecializedBackend({
|
||||
...otherOptions,
|
||||
services: factories,
|
||||
services: [...factories, rootHttpRouterFactory, discoveryFactory],
|
||||
});
|
||||
|
||||
backendInstancesToCleanUp.push(backend);
|
||||
@@ -129,7 +226,14 @@ export async function startTestBackend<
|
||||
|
||||
await backend.start();
|
||||
|
||||
return backend;
|
||||
return Object.assign(backend, {
|
||||
get server() {
|
||||
if (!server) {
|
||||
throw new Error('TestBackend server is not available');
|
||||
}
|
||||
return server;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let registered = false;
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
*/
|
||||
|
||||
export { startTestBackend } from './TestBackend';
|
||||
export type { TestBackendOptions } from './TestBackend';
|
||||
export type { TestBackend, TestBackendOptions } from './TestBackend';
|
||||
|
||||
@@ -36,9 +36,9 @@ export default {
|
||||
export const InHeader = () => (
|
||||
<MemoryRouter>
|
||||
<h2>Standard breadcrumbs</h2>
|
||||
<p>
|
||||
<Typography paragraph>
|
||||
Underlined pages are links. This should show a hierarchical relationship.
|
||||
</p>
|
||||
</Typography>
|
||||
|
||||
<Page themeId="other">
|
||||
<Header title="Current Page" type="General Page" typeLink="/" />
|
||||
@@ -61,17 +61,17 @@ export const OutsideOfHeader = () => {
|
||||
const open = Boolean(anchorEl);
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<p>
|
||||
<Typography paragraph>
|
||||
It might be the case that you want to keep your breadcrumbs outside of
|
||||
the header. In that case, they should be positioned above the title of
|
||||
the page.
|
||||
</p>
|
||||
</Typography>
|
||||
|
||||
<h2>Standard breadcrumbs</h2>
|
||||
<p>
|
||||
<Typography paragraph>
|
||||
Underlined pages are links. This should show a hierarchical
|
||||
relationship.
|
||||
</p>
|
||||
</Typography>
|
||||
|
||||
<Breadcrumbs color="primaryText" />
|
||||
|
||||
@@ -82,10 +82,10 @@ export const OutsideOfHeader = () => {
|
||||
</Breadcrumbs>
|
||||
|
||||
<h2>Hidden breadcrumbs</h2>
|
||||
<p>
|
||||
<Typography paragraph>
|
||||
Use this when you have more than three breadcrumbs. When user clicks on
|
||||
ellipses, expand the breadcrumbs out.
|
||||
</p>
|
||||
</Typography>
|
||||
|
||||
<Breadcrumbs color="primaryText">
|
||||
<Link to="/">General Page</Link>
|
||||
@@ -96,10 +96,10 @@ export const OutsideOfHeader = () => {
|
||||
</Breadcrumbs>
|
||||
|
||||
<h2>Layered breadcrumbs</h2>
|
||||
<p>
|
||||
<Typography paragraph>
|
||||
Use this when you want to show alternative breadcrumbs on the same
|
||||
hierarchical level.
|
||||
</p>
|
||||
</Typography>
|
||||
|
||||
<Fragment>
|
||||
<Breadcrumbs color="primaryText">
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"get-port": "^6.1.2",
|
||||
"mock-fs": "^5.1.0",
|
||||
"msw": "^0.49.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
|
||||
@@ -17,18 +17,14 @@
|
||||
import mockFs from 'mock-fs';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import fetch from 'node-fetch';
|
||||
import { coreServices } from '@backstage/backend-plugin-api';
|
||||
import { startTestBackend } from '@backstage/backend-test-utils';
|
||||
import { appPlugin } from './appPlugin';
|
||||
import {
|
||||
databaseFactory,
|
||||
httpRouterFactory,
|
||||
rootHttpRouterFactory,
|
||||
loggerFactory,
|
||||
rootLoggerFactory,
|
||||
} from '@backstage/backend-app-api';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import getPort from 'get-port';
|
||||
|
||||
describe('appPlugin', () => {
|
||||
beforeEach(() => {
|
||||
@@ -48,23 +44,12 @@ describe('appPlugin', () => {
|
||||
});
|
||||
|
||||
it('boots', async () => {
|
||||
const port = await getPort();
|
||||
await startTestBackend({
|
||||
const { server } = await startTestBackend({
|
||||
services: [
|
||||
[
|
||||
coreServices.config,
|
||||
new ConfigReader({
|
||||
backend: {
|
||||
listen: { port },
|
||||
database: { client: 'better-sqlite3', connection: ':memory:' },
|
||||
},
|
||||
}),
|
||||
],
|
||||
loggerFactory(),
|
||||
rootLoggerFactory(),
|
||||
databaseFactory(),
|
||||
httpRouterFactory(),
|
||||
rootHttpRouterFactory(),
|
||||
],
|
||||
features: [
|
||||
appPlugin({
|
||||
@@ -75,12 +60,12 @@ describe('appPlugin', () => {
|
||||
});
|
||||
|
||||
await expect(
|
||||
fetch(`http://localhost:${port}/api/app/derp.html`).then(res =>
|
||||
fetch(`http://localhost:${server.port()}/api/app/derp.html`).then(res =>
|
||||
res.text(),
|
||||
),
|
||||
).resolves.toBe('winning');
|
||||
await expect(
|
||||
fetch(`http://localhost:${port}`).then(res => res.text()),
|
||||
fetch(`http://localhost:${server.port()}`).then(res => res.text()),
|
||||
).resolves.toBe('winning');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { CardHeader, Divider, IconButton, makeStyles } from '@material-ui/core';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import {
|
||||
HeaderIconLinkRow,
|
||||
IconLinkVerticalProps,
|
||||
@@ -182,7 +183,11 @@ export const EntityBazaarInfoContent = ({
|
||||
)}
|
||||
|
||||
<CardHeader
|
||||
title={<p className={classes.wordBreak}>{bazaarProject?.title!}</p>}
|
||||
title={
|
||||
<Typography paragraph className={classes.wordBreak}>
|
||||
{bazaarProject?.title!}
|
||||
</Typography>
|
||||
}
|
||||
action={
|
||||
<div>
|
||||
<IconButton
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
IconButton,
|
||||
makeStyles,
|
||||
} from '@material-ui/core';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import {
|
||||
HeaderIconLinkRow,
|
||||
IconLinkVerticalProps,
|
||||
@@ -256,9 +257,9 @@ export const HomePageBazaarInfoCard = ({
|
||||
|
||||
<CardHeader
|
||||
title={
|
||||
<p className={classes.wordBreak}>
|
||||
<Typography paragraph className={classes.wordBreak}>
|
||||
{bazaarProject.value?.title || initProject.title}
|
||||
</p>
|
||||
</Typography>
|
||||
}
|
||||
action={
|
||||
<div>
|
||||
|
||||
+19
-27
@@ -14,18 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
getVoidLogger,
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { coreServices } from '@backstage/backend-plugin-api';
|
||||
import {
|
||||
PluginTaskScheduler,
|
||||
TaskScheduleDefinition,
|
||||
} from '@backstage/backend-tasks';
|
||||
import { startTestBackend } from '@backstage/backend-test-utils';
|
||||
import {
|
||||
startTestBackend,
|
||||
mockConfigFactory,
|
||||
} from '@backstage/backend-test-utils';
|
||||
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node';
|
||||
import { eventsExtensionPoint } from '@backstage/plugin-events-node';
|
||||
import { Duration } from 'luxon';
|
||||
@@ -55,22 +52,6 @@ describe('bitbucketCloudEntityProviderCatalogModule', () => {
|
||||
return runner;
|
||||
},
|
||||
} as unknown as PluginTaskScheduler;
|
||||
const discovery = jest.fn() as any as PluginEndpointDiscovery;
|
||||
const tokenManager = jest.fn() as any as TokenManager;
|
||||
|
||||
const config = new ConfigReader({
|
||||
catalog: {
|
||||
providers: {
|
||||
bitbucketCloud: {
|
||||
schedule: {
|
||||
frequency: 'P1M',
|
||||
timeout: 'PT3M',
|
||||
},
|
||||
workspace: 'test-ws',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await startTestBackend({
|
||||
extensionPoints: [
|
||||
@@ -78,11 +59,22 @@ describe('bitbucketCloudEntityProviderCatalogModule', () => {
|
||||
[eventsExtensionPoint, eventsExtensionPointImpl],
|
||||
],
|
||||
services: [
|
||||
[coreServices.config, config],
|
||||
[coreServices.discovery, discovery],
|
||||
[coreServices.logger, getVoidLogger()],
|
||||
mockConfigFactory({
|
||||
data: {
|
||||
catalog: {
|
||||
providers: {
|
||||
bitbucketCloud: {
|
||||
schedule: {
|
||||
frequency: 'P1M',
|
||||
timeout: 'PT3M',
|
||||
},
|
||||
workspace: 'test-ws',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
[coreServices.scheduler, scheduler],
|
||||
[coreServices.tokenManager, tokenManager],
|
||||
],
|
||||
features: [bitbucketCloudEntityProviderCatalogModule()],
|
||||
});
|
||||
|
||||
@@ -56,8 +56,7 @@
|
||||
"devDependencies": {
|
||||
"@backstage/backend-app-api": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/plugin-catalog-backend": "workspace:^",
|
||||
"get-port": "^6.1.2"
|
||||
"@backstage/plugin-catalog-backend": "workspace:^"
|
||||
},
|
||||
"files": [
|
||||
"alpha",
|
||||
|
||||
@@ -14,11 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import {
|
||||
createBackendModule,
|
||||
coreServices,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { createBackendModule } from '@backstage/backend-plugin-api';
|
||||
import { startTestBackend } from '@backstage/backend-test-utils';
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
import { catalogServiceRef } from './catalogService';
|
||||
@@ -42,9 +38,6 @@ describe('catalogServiceRef', () => {
|
||||
});
|
||||
|
||||
await startTestBackend({
|
||||
services: [
|
||||
[coreServices.discovery, {} as unknown as PluginEndpointDiscovery],
|
||||
],
|
||||
features: [testModule()],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -166,7 +166,8 @@ export const FossaCard = (props: { variant?: InfoCardVariants }) => {
|
||||
spacing={0}
|
||||
>
|
||||
<Grid item>
|
||||
<p
|
||||
<Typography
|
||||
paragraph
|
||||
className={
|
||||
value.issueCount > 0 || value.dependencyCount === 0
|
||||
? classes.numberError
|
||||
@@ -174,16 +175,18 @@ export const FossaCard = (props: { variant?: InfoCardVariants }) => {
|
||||
}
|
||||
>
|
||||
{value.issueCount}
|
||||
</p>
|
||||
</Typography>
|
||||
{value.dependencyCount > 0 && (
|
||||
<p className={classes.description}>Number of issues</p>
|
||||
<Typography paragraph className={classes.description}>
|
||||
Number of issues
|
||||
</Typography>
|
||||
)}
|
||||
{value.dependencyCount === 0 && (
|
||||
<p className={classes.description}>
|
||||
<Typography paragraph className={classes.description}>
|
||||
No Dependencies.
|
||||
<br />
|
||||
Please check your FOSSA project settings.
|
||||
</p>
|
||||
</Typography>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -68,8 +68,12 @@ const generatedColumns: TableColumn[] = [
|
||||
title: 'Source',
|
||||
render: (row: Partial<WorkflowRun>) => (
|
||||
<Typography variant="body2" noWrap>
|
||||
<p>{row.source?.branchName}</p>
|
||||
<p>{row.source?.commit.hash}</p>
|
||||
<Typography paragraph variant="body2">
|
||||
{row.source?.branchName}
|
||||
</Typography>
|
||||
<Typography paragraph variant="body2">
|
||||
{row.source?.commit.hash}
|
||||
</Typography>
|
||||
</Typography>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -14,18 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import React from 'react';
|
||||
import { useRandomJoke } from './Context';
|
||||
|
||||
export const Content = () => {
|
||||
const { joke, loading } = useRandomJoke();
|
||||
|
||||
if (loading) return <p>Loading...</p>;
|
||||
if (loading) return <Typography paragraph>Loading...</Typography>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>{joke.setup}</p>
|
||||
<p>{joke.punchline}</p>
|
||||
<Typography paragraph>{joke.setup}</Typography>
|
||||
<Typography paragraph>{joke.punchline}</Typography>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -126,12 +126,12 @@ const generatedColumns: TableColumn[] = [
|
||||
field: 'lastBuild.source.branchName',
|
||||
render: (row: Partial<Project>) => (
|
||||
<>
|
||||
<p>
|
||||
<Typography paragraph>
|
||||
<Link to={row.lastBuild?.source?.url ?? ''}>
|
||||
{row.lastBuild?.source?.branchName}
|
||||
</Link>
|
||||
</p>
|
||||
<p>{row.lastBuild?.source?.commit?.hash}</p>
|
||||
</Typography>
|
||||
<Typography paragraph>{row.lastBuild?.source?.commit?.hash}</Typography>
|
||||
</>
|
||||
),
|
||||
},
|
||||
@@ -152,7 +152,7 @@ const generatedColumns: TableColumn[] = [
|
||||
render: (row: Partial<Project>) => {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<Typography paragraph>
|
||||
{row.lastBuild?.tests && (
|
||||
<Link to={row.lastBuild?.tests.testUrl ?? ''}>
|
||||
{row.lastBuild?.tests.passed} / {row.lastBuild?.tests.total}{' '}
|
||||
@@ -165,7 +165,7 @@ const generatedColumns: TableColumn[] = [
|
||||
)}
|
||||
|
||||
{!row.lastBuild?.tests && 'n/a'}
|
||||
</p>
|
||||
</Typography>
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Step, StepLabel, Stepper } from '@material-ui/core';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import {
|
||||
ArgoRolloutCanaryStep,
|
||||
SetWeightStep,
|
||||
@@ -49,11 +50,11 @@ const createLabelForStep = (step: ArgoRolloutCanaryStep): React.ReactNode => {
|
||||
} else if (isAnalysisStep(step)) {
|
||||
return (
|
||||
<div>
|
||||
<p>analysis templates:</p>
|
||||
<Typography paragraph>analysis templates:</Typography>
|
||||
{step.analysis.templates.map((t, i) => (
|
||||
<p key={i}>{`${t.templateName}${
|
||||
<Typography paragraph key={i}>{`${t.templateName}${
|
||||
t.clusterScope ? ' (cluster scoped)' : ''
|
||||
}`}</p>
|
||||
}`}</Typography>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,160 @@
|
||||
# scaffolder-backend-module-sentry
|
||||
|
||||
Welcome to the Sentry Module for Scaffolder.
|
||||
|
||||
Here you can find all Sentry related features to improve your scaffolder:
|
||||
|
||||
## Getting started
|
||||
|
||||
You need to configure the action in your backend:
|
||||
|
||||
## From your Backstage root directory
|
||||
|
||||
```bash
|
||||
# From your Backstage root directory
|
||||
yarn add --cwd packages/backend @backstage/plugin-scaffolder-backend-module-sentry
|
||||
```
|
||||
|
||||
Configure the action (you can check
|
||||
the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to
|
||||
see all options):
|
||||
|
||||
```typescript
|
||||
const actions = [
|
||||
createSentryCreateProjectAction({
|
||||
integrations,
|
||||
reader: env.reader,
|
||||
containerRunner,
|
||||
}),
|
||||
];
|
||||
|
||||
return await createRouter({
|
||||
containerRunner,
|
||||
catalogClient,
|
||||
actions,
|
||||
logger: env.logger,
|
||||
config: env.config,
|
||||
database: env.database,
|
||||
reader: env.reader,
|
||||
});
|
||||
```
|
||||
|
||||
You need to define your Sentry API Token in your `app-config.yaml`:
|
||||
|
||||
```yaml
|
||||
scaffolder:
|
||||
sentry:
|
||||
token: ${SENTRY_TOKEN}
|
||||
```
|
||||
|
||||
After that you can use the action in your template:
|
||||
|
||||
```yaml
|
||||
apiVersion: scaffolder.backstage.io/v1beta3
|
||||
kind: Template
|
||||
metadata:
|
||||
name: sentry-demo
|
||||
title: Sentry template
|
||||
description: scaffolder sentry app
|
||||
spec:
|
||||
owner: backstage/techdocs-core
|
||||
type: service
|
||||
|
||||
parameters:
|
||||
- title: Fill in some steps
|
||||
required:
|
||||
- name
|
||||
- owner
|
||||
properties:
|
||||
name:
|
||||
title: Name
|
||||
type: string
|
||||
description: Unique name of the component
|
||||
ui:autofocus: true
|
||||
ui:options:
|
||||
rows: 5
|
||||
owner:
|
||||
title: Owner
|
||||
type: string
|
||||
description: Owner of the component
|
||||
ui:field: OwnerPicker
|
||||
ui:options:
|
||||
catalogFilter:
|
||||
kind: Group
|
||||
system:
|
||||
title: System
|
||||
type: string
|
||||
description: System of the component
|
||||
ui:field: EntityPicker
|
||||
ui:options:
|
||||
catalogFilter:
|
||||
kind: System
|
||||
defaultKind: System
|
||||
|
||||
- title: Choose a location
|
||||
required:
|
||||
- repoUrl
|
||||
- dryRun
|
||||
properties:
|
||||
repoUrl:
|
||||
title: Repository Location
|
||||
type: string
|
||||
ui:field: RepoUrlPicker
|
||||
ui:options:
|
||||
allowedHosts:
|
||||
- github.com
|
||||
dryRun:
|
||||
title: Only perform a dry run, don't publish anything
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
steps:
|
||||
- id: fetch
|
||||
name: Fetch
|
||||
action: fetch:template
|
||||
input:
|
||||
url: https://github.com/TEMPLATE
|
||||
values:
|
||||
name: ${{ parameters.name }}
|
||||
|
||||
- id: create-sentry-project
|
||||
if: ${{ parameters.dryRun !== true }}
|
||||
name: Create Sentry Project
|
||||
action: sentry:create-project
|
||||
input:
|
||||
organizationSlug: ORG-SLUG
|
||||
teamSlug: TEAM-SLUG
|
||||
name: ${{ parameters.name }}
|
||||
|
||||
- id: publish
|
||||
if: ${{ parameters.dryRun !== true }}
|
||||
name: Publish
|
||||
action: publish:github
|
||||
input:
|
||||
allowedHosts:
|
||||
- github.com
|
||||
description: This is ${{ parameters.name }}
|
||||
repoUrl: ${{ parameters.repoUrl }}
|
||||
|
||||
- id: register
|
||||
if: ${{ parameters.dryRun !== true }}
|
||||
name: Register
|
||||
action: catalog:register
|
||||
input:
|
||||
repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
|
||||
catalogInfoPath: '/catalog-info.yaml'
|
||||
|
||||
- name: Results
|
||||
if: ${{ parameters.dryRun }}
|
||||
action: debug:log
|
||||
input:
|
||||
listWorkspace: true
|
||||
|
||||
output:
|
||||
links:
|
||||
- title: Repository
|
||||
url: ${{ steps['publish'].output.remoteUrl }}
|
||||
- title: Open in catalog
|
||||
icon: catalog
|
||||
entityRef: ${{ steps['register'].output.entityRef }}
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
## API Report File for "@backstage/plugin-scaffolder-backend-module-sentry"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { Config } from '@backstage/config';
|
||||
import { TemplateAction } from '@backstage/plugin-scaffolder-backend';
|
||||
|
||||
// @public
|
||||
export function createSentryCreateProjectAction(options: {
|
||||
config: Config;
|
||||
}): TemplateAction<{
|
||||
organizationSlug: string;
|
||||
teamSlug: string;
|
||||
name: string;
|
||||
slug?: string | undefined;
|
||||
authToken?: string | undefined;
|
||||
}>;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "@backstage/plugin-scaffolder-backend-module-sentry",
|
||||
"version": "0.0.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "frontend-plugin"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
"build": "backstage-cli package build",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"clean": "backstage-cli package clean",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/integration": "workspace:^",
|
||||
"@backstage/plugin-scaffolder-backend": "workspace:^"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/core-app-api": "workspace:^",
|
||||
"@backstage/dev-utils": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^12.1.3",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"@types/node": "*",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"msw": "^0.49.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2021 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 { createTemplateAction } from '@backstage/plugin-scaffolder-backend';
|
||||
import { InputError } from '@backstage/errors';
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
/**
|
||||
* Creates the `sentry:craete-project` Scaffolder action.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://backstage.io/docs/features/software-templates/writing-custom-actions}.
|
||||
*
|
||||
* @param options - Configuration of the Sentry API.
|
||||
* @public
|
||||
*/
|
||||
export function createSentryCreateProjectAction(options: { config: Config }) {
|
||||
const { config } = options;
|
||||
|
||||
return createTemplateAction<{
|
||||
organizationSlug: string;
|
||||
teamSlug: string;
|
||||
name: string;
|
||||
slug?: string;
|
||||
authToken?: string;
|
||||
}>({
|
||||
id: 'sentry:project:create',
|
||||
schema: {
|
||||
input: {
|
||||
required: ['organizationSlug', 'teamSlug', 'name'],
|
||||
type: 'object',
|
||||
properties: {
|
||||
organizationSlug: {
|
||||
title: 'The slug of the organization the team belongs to',
|
||||
type: 'string',
|
||||
},
|
||||
teamSlug: {
|
||||
title: 'The slug of the team to create a new project for',
|
||||
type: 'string',
|
||||
},
|
||||
name: {
|
||||
title: 'The name for the new project',
|
||||
type: 'string',
|
||||
},
|
||||
slug: {
|
||||
title:
|
||||
'Optional slug for the new project. If not provided a slug is generated from the name',
|
||||
type: 'string',
|
||||
},
|
||||
authToken: {
|
||||
title:
|
||||
'authenticate via bearer auth token. Requires scope: project:write',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async handler(ctx) {
|
||||
const { organizationSlug, teamSlug, name, slug, authToken } = ctx.input;
|
||||
|
||||
const body: any = {
|
||||
name: name,
|
||||
};
|
||||
|
||||
if (slug) {
|
||||
body.slug = slug;
|
||||
}
|
||||
|
||||
const token = authToken
|
||||
? authToken
|
||||
: config.getOptionalString('scaffolder.sentry.token');
|
||||
|
||||
if (!token) {
|
||||
throw new InputError(`No valid sentry token given`);
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`https://sentry.io/api/0/teams/${organizationSlug}/${teamSlug}/projects/`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
);
|
||||
|
||||
const contentType = response.headers.get('content-type');
|
||||
|
||||
if (contentType !== 'application/json') {
|
||||
throw new InputError(
|
||||
`Unexpected Sentry Response Type: ${await response.text()}`,
|
||||
);
|
||||
}
|
||||
|
||||
const code = response.status;
|
||||
const result = await response.json();
|
||||
|
||||
if (code !== 201) {
|
||||
throw new InputError(`Sentry Response was: ${await result.detail}`);
|
||||
}
|
||||
|
||||
ctx.output('id', result.id);
|
||||
ctx.output('result', result);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2021 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 { createSentryCreateProjectAction } from './actions/createProject';
|
||||
@@ -16,6 +16,7 @@
|
||||
import { DateTime, Interval } from 'luxon';
|
||||
import humanizeDuration from 'humanize-duration';
|
||||
import React from 'react';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
export const CreatedAtColumn = ({ createdAt }: { createdAt: string }) => {
|
||||
const createdAtTime = DateTime.fromISO(createdAt);
|
||||
@@ -23,5 +24,9 @@ export const CreatedAtColumn = ({ createdAt }: { createdAt: string }) => {
|
||||
.toDuration()
|
||||
.valueOf();
|
||||
|
||||
return <p>{humanizeDuration(formatted, { round: true })} ago</p>;
|
||||
return (
|
||||
<Typography paragraph>
|
||||
{humanizeDuration(formatted, { round: true })} ago
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ import useAsync from 'react-use/lib/useAsync';
|
||||
|
||||
import { catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react';
|
||||
import { parseEntityRef, UserEntity } from '@backstage/catalog-model';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
export const OwnerEntityColumn = ({ entityRef }: { entityRef?: string }) => {
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
@@ -30,7 +31,7 @@ export const OwnerEntityColumn = ({ entityRef }: { entityRef?: string }) => {
|
||||
);
|
||||
|
||||
if (!entityRef) {
|
||||
return <p>Unknown</p>;
|
||||
return <Typography paragraph>Unknown</Typography>;
|
||||
}
|
||||
|
||||
if (loading || error) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
ListItemSecondaryAction,
|
||||
ListItemIcon,
|
||||
} from '@material-ui/core';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import _unescape from 'lodash/unescape';
|
||||
@@ -51,11 +52,11 @@ export const Content = (props: StackOverflowQuestionsContentProps) => {
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return <p>loading...</p>;
|
||||
return <Typography paragraph>loading...</Typography>;
|
||||
}
|
||||
|
||||
if (error || !value || !value.length) {
|
||||
return <p>could not load questions</p>;
|
||||
return <Typography paragraph>could not load questions</Typography>;
|
||||
}
|
||||
|
||||
const getSecondaryText = (answer_count: Number) =>
|
||||
|
||||
@@ -77,11 +77,22 @@ export interface FactRetrieverRegistry {
|
||||
register(registration: FactRetrieverRegistration): Promise<void>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const initializePersistenceContext: (
|
||||
database: PluginDatabaseManager,
|
||||
options?: PersistenceContextOptions,
|
||||
) => Promise<PersistenceContext>;
|
||||
|
||||
// @public
|
||||
export type PersistenceContext = {
|
||||
techInsightsStore: TechInsightsStore;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type PersistenceContextOptions = {
|
||||
logger: Logger;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface RouterOptions<
|
||||
CheckType extends TechInsightCheck,
|
||||
@@ -122,6 +133,7 @@ export interface TechInsightsOptions<
|
||||
factRetrievers?: FactRetrieverRegistration[];
|
||||
// (undocumented)
|
||||
logger: Logger;
|
||||
persistenceContext?: PersistenceContext;
|
||||
// (undocumented)
|
||||
scheduler: PluginTaskScheduler;
|
||||
// (undocumented)
|
||||
|
||||
@@ -18,12 +18,16 @@ export * from './service/router';
|
||||
export type { RouterOptions } from './service/router';
|
||||
|
||||
export { buildTechInsightsContext } from './service/techInsightsContextBuilder';
|
||||
export { initializePersistenceContext } from './service/persistence/persistenceContext';
|
||||
export type {
|
||||
TechInsightsOptions,
|
||||
TechInsightsContext,
|
||||
} from './service/techInsightsContextBuilder';
|
||||
export type { FactRetrieverEngine } from './service/fact/FactRetrieverEngine';
|
||||
export type { PersistenceContext } from './service/persistence/persistenceContext';
|
||||
export type {
|
||||
PersistenceContext,
|
||||
PersistenceContextOptions,
|
||||
} from './service/persistence/persistenceContext';
|
||||
export { createFactRetrieverRegistration } from './service/fact/createFactRetriever';
|
||||
export type { FactRetrieverRegistry } from './service/fact/FactRetrieverRegistry';
|
||||
export type { FactRetrieverRegistrationOptions } from './service/fact/createFactRetriever';
|
||||
|
||||
@@ -46,6 +46,11 @@ type RawDbFactSchemaRow = {
|
||||
entityFilter?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default TechInsightsDatabase implementation.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class TechInsightsDatabase implements TechInsightsStore {
|
||||
private readonly CHUNK_SIZE = 50;
|
||||
|
||||
|
||||
@@ -36,22 +36,27 @@ export type PersistenceContext = {
|
||||
techInsightsStore: TechInsightsStore;
|
||||
};
|
||||
|
||||
export type CreateDatabaseOptions = {
|
||||
/**
|
||||
* A Container for persistence context initialization options
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type PersistenceContextOptions = {
|
||||
logger: Logger;
|
||||
};
|
||||
|
||||
const defaultOptions: CreateDatabaseOptions = {
|
||||
const defaultOptions: PersistenceContextOptions = {
|
||||
logger: getVoidLogger(),
|
||||
};
|
||||
|
||||
/**
|
||||
* A factory method to construct persistence context for running implementation.
|
||||
* A factory function to construct persistence context for running implementation.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const initializePersistenceContext = async (
|
||||
database: PluginDatabaseManager,
|
||||
options: CreateDatabaseOptions = defaultOptions,
|
||||
options: PersistenceContextOptions = defaultOptions,
|
||||
): Promise<PersistenceContext> => {
|
||||
const client = await database.getClient();
|
||||
|
||||
|
||||
@@ -74,6 +74,12 @@ export interface TechInsightsOptions<
|
||||
*/
|
||||
factRetrieverRegistry?: FactRetrieverRegistry;
|
||||
|
||||
/**
|
||||
* Optional persistenceContext implementation that replaces the default one.
|
||||
* This can be used to replace underlying database with a more suitable implementation if needed
|
||||
*/
|
||||
persistenceContext?: PersistenceContext;
|
||||
|
||||
logger: Logger;
|
||||
config: Config;
|
||||
discovery: PluginEndpointDiscovery;
|
||||
@@ -139,9 +145,11 @@ export const buildTechInsightsContext = async <
|
||||
|
||||
const factRetrieverRegistry = buildFactRetrieverRegistry();
|
||||
|
||||
const persistenceContext = await initializePersistenceContext(database, {
|
||||
logger,
|
||||
});
|
||||
const persistenceContext =
|
||||
options.persistenceContext ??
|
||||
(await initializePersistenceContext(database, {
|
||||
logger,
|
||||
}));
|
||||
|
||||
const factRetrieverEngine = await DefaultFactRetrieverEngine.create({
|
||||
scheduler,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ClassNameMap } from '@material-ui/core/styles/withStyles';
|
||||
import Typography from '@material-ui/core/Typography/Typography';
|
||||
import React from 'react';
|
||||
import { Entry, Ring } from '../../utils/types';
|
||||
import { RadarLegendLink } from './RadarLegendLink';
|
||||
@@ -38,7 +39,9 @@ export const RadarLegendRing = ({
|
||||
<div data-testid="radar-ring" key={ring.id} className={classes.ring}>
|
||||
<h3 className={classes.ringHeading}>{ring.name}</h3>
|
||||
{entries.length === 0 ? (
|
||||
<p className={classes.ringEmpty}>(empty)</p>
|
||||
<Typography paragraph className={classes.ringEmpty}>
|
||||
(empty)
|
||||
</Typography>
|
||||
) : (
|
||||
<ol className={classes.ringList}>
|
||||
{entries.map(entry => (
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
Link,
|
||||
} from '@backstage/core-components';
|
||||
import { Grid, Input, makeStyles } from '@material-ui/core';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import React from 'react';
|
||||
import { RadarComponent, TechRadarComponentProps } from './RadarComponent';
|
||||
|
||||
@@ -79,7 +80,7 @@ export function RadarPage(props: TechRadarPageProps) {
|
||||
onChange={e => setSearchText(e.target.value)}
|
||||
/>
|
||||
<SupportButton>
|
||||
<p>
|
||||
<Typography paragraph>
|
||||
This is used for visualizing the official guidelines of different
|
||||
areas of software development such as languages, frameworks,
|
||||
infrastructure and processes. You can find an explanation for the
|
||||
@@ -88,7 +89,7 @@ export function RadarPage(props: TechRadarPageProps) {
|
||||
Zalando Tech Radar
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</Typography>
|
||||
</SupportButton>
|
||||
</ContentHeader>
|
||||
<Grid container spacing={3} direction="row">
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { createStyles, makeStyles, useTheme } from '@material-ui/core';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import React from 'react';
|
||||
import {
|
||||
Bar,
|
||||
@@ -91,7 +92,7 @@ export const BuildTimeline = ({
|
||||
width,
|
||||
}: BuildTimelineProps) => {
|
||||
const theme = useTheme();
|
||||
if (!targets.length) return <p>No Targets</p>;
|
||||
if (!targets.length) return <Typography paragraph>No Targets</Typography>;
|
||||
|
||||
const data = getTimelineData(targets);
|
||||
|
||||
|
||||
@@ -3290,7 +3290,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime-corejs3@npm:^7.10.2, @babel/runtime-corejs3@npm:^7.11.2, @babel/runtime-corejs3@npm:^7.18.9":
|
||||
"@babel/runtime-corejs3@npm:^7.11.2, @babel/runtime-corejs3@npm:^7.18.9":
|
||||
version: 7.18.9
|
||||
resolution: "@babel/runtime-corejs3@npm:7.18.9"
|
||||
dependencies:
|
||||
@@ -3300,12 +3300,12 @@ __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.10.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.18.9, @babel/runtime@npm:^7.20.1, @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.2, @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.20.6
|
||||
resolution: "@babel/runtime@npm:7.20.6"
|
||||
"@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.14.6, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.7, @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.2, @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.20.7
|
||||
resolution: "@babel/runtime@npm:7.20.7"
|
||||
dependencies:
|
||||
regenerator-runtime: ^0.13.11
|
||||
checksum: 42a8504db21031b1859fbc0f52d698a3d2f5ada9519eb76c6f96a7e657d8d555732a18fe71ef428a67cc9fc81ca0d3562fb7afdc70549c5fec343190cbaa9b03
|
||||
checksum: 4629ce5c46f06cca9cfb9b7fc00d48003335a809888e2b91ec2069a2dcfbfef738480cff32ba81e0b7c290f8918e5c22ddcf2b710001464ee84ba62c7e32a3a3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -3400,6 +3400,7 @@ __metadata:
|
||||
fs-extra: 10.1.0
|
||||
helmet: ^6.0.0
|
||||
http-errors: ^2.0.0
|
||||
lodash: ^4.17.21
|
||||
minimatch: ^5.0.0
|
||||
morgan: ^1.10.0
|
||||
node-forge: ^1.3.1
|
||||
@@ -3558,11 +3559,16 @@ __metadata:
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/config": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
"@types/supertest": ^2.0.8
|
||||
better-sqlite3: ^8.0.0
|
||||
express: ^4.17.1
|
||||
express-promise-router: ^4.1.0
|
||||
knex: ^2.0.0
|
||||
msw: ^0.49.0
|
||||
mysql2: ^2.2.5
|
||||
pg: ^8.3.0
|
||||
supertest: ^6.1.3
|
||||
testcontainers: ^8.1.2
|
||||
uuid: ^8.0.0
|
||||
languageName: unknown
|
||||
@@ -4513,7 +4519,6 @@ __metadata:
|
||||
express: ^4.17.1
|
||||
express-promise-router: ^4.1.0
|
||||
fs-extra: 10.1.0
|
||||
get-port: ^6.1.2
|
||||
globby: ^11.0.0
|
||||
helmet: ^6.0.0
|
||||
knex: ^2.0.0
|
||||
@@ -5112,7 +5117,6 @@ __metadata:
|
||||
"@types/luxon": ^3.0.0
|
||||
express: ^4.17.1
|
||||
express-promise-router: ^4.1.0
|
||||
get-port: ^6.1.2
|
||||
knex: ^2.0.0
|
||||
lodash: ^4.17.21
|
||||
luxon: ^3.0.0
|
||||
@@ -7330,6 +7334,29 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-scaffolder-backend-module-sentry@workspace:plugins/scaffolder-backend-module-sentry":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-scaffolder-backend-module-sentry@workspace:plugins/scaffolder-backend-module-sentry"
|
||||
dependencies:
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/config": "workspace:^"
|
||||
"@backstage/core-app-api": "workspace:^"
|
||||
"@backstage/dev-utils": "workspace:^"
|
||||
"@backstage/errors": "workspace:^"
|
||||
"@backstage/integration": "workspace:^"
|
||||
"@backstage/plugin-scaffolder-backend": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@testing-library/jest-dom": ^5.10.1
|
||||
"@testing-library/react": ^12.1.3
|
||||
"@testing-library/user-event": ^14.0.0
|
||||
"@types/node": "*"
|
||||
cross-fetch: ^3.1.5
|
||||
msw: ^0.49.0
|
||||
peerDependencies:
|
||||
react: ^16.13.1 || ^17.0.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-scaffolder-backend-module-yeoman@workspace:plugins/scaffolder-backend-module-yeoman":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-scaffolder-backend-module-yeoman@workspace:plugins/scaffolder-backend-module-yeoman"
|
||||
@@ -16828,20 +16855,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"aria-query@npm:^4.2.2":
|
||||
version: 4.2.2
|
||||
resolution: "aria-query@npm:4.2.2"
|
||||
"aria-query@npm:^5.0.0, aria-query@npm:^5.1.3":
|
||||
version: 5.1.3
|
||||
resolution: "aria-query@npm:5.1.3"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.10.2
|
||||
"@babel/runtime-corejs3": ^7.10.2
|
||||
checksum: 38401a9a400f26f3dcc24b84997461a16b32869a9893d323602bed8da40a8bcc0243b8d2880e942249a1496cea7a7de769e93d21c0baa439f01e1ee936fed665
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"aria-query@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "aria-query@npm:5.0.0"
|
||||
checksum: c41f98866c5a304561ee8cae55856711cddad6f3f85d8cb43cc5f79667078d9b8979ce32d244c1ff364e6463a4d0b6865804a33ccc717fed701b281cf7dc6296
|
||||
deep-equal: ^2.0.5
|
||||
checksum: 929ff95f02857b650fb4cbcd2f41072eee2f46159a6605ea03bf63aa572e35ffdff43d69e815ddc462e16e07de8faba3978afc2813650b4448ee18c9895d982b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -16852,13 +16871,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"array-filter@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "array-filter@npm:1.0.0"
|
||||
checksum: 467054291f522d7f633b1f5e79aac9008ade50a7354e0178d9ec8f0091ec03bc19a41d4eb22985daf2279a5c27be6d7cf410733539e7fccb0742145b89aca438
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"array-flatten@npm:1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "array-flatten@npm:1.1.1"
|
||||
@@ -17114,12 +17126,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"available-typed-arrays@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "available-typed-arrays@npm:1.0.2"
|
||||
dependencies:
|
||||
array-filter: ^1.0.0
|
||||
checksum: 915a89f31bb9ba51f7396d5ae7d8eff99bc6d6ba9f337068a6916e9ba56fa47bfea7ea69f6f6ad131eac57f76582c721e5f0594e8fea7156894313fc41203fbd
|
||||
"available-typed-arrays@npm:^1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "available-typed-arrays@npm:1.0.5"
|
||||
checksum: 20eb47b3cefd7db027b9bbb993c658abd36d4edd3fe1060e83699a03ee275b0c9b216cc076ff3f2db29073225fb70e7613987af14269ac1fe2a19803ccc97f1a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -17215,10 +17225,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"axe-core@npm:^4.4.3":
|
||||
version: 4.4.3
|
||||
resolution: "axe-core@npm:4.4.3"
|
||||
checksum: c3ea000d9ace3ba0bc747c8feafc24b0de62a0f7d93021d0f77b19c73fca15341843510f6170da563d51535d6cfb7a46c5fc0ea36170549dbb44b170208450a2
|
||||
"axe-core@npm:^4.6.2":
|
||||
version: 4.6.2
|
||||
resolution: "axe-core@npm:4.6.2"
|
||||
checksum: 81523eeaf101a3a129545a936d448d235ecf1f8c0daccdee224d29f63bec716fa38cf1a65c8462548b1f995624277eed790d9d9977ae40ba692c4cadf1196403
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -17273,10 +17283,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"axobject-query@npm:^2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "axobject-query@npm:2.2.0"
|
||||
checksum: 96b8c7d807ca525f41ad9b286186e2089b561ba63a6d36c3e7d73dc08150714660995c7ad19cda05784458446a0793b45246db45894631e13853f48c1aa3117f
|
||||
"axobject-query@npm:^3.1.1":
|
||||
version: 3.1.1
|
||||
resolution: "axobject-query@npm:3.1.1"
|
||||
dependencies:
|
||||
deep-equal: ^2.0.5
|
||||
checksum: c12a5da10dc7bab75e1cda9b6a3b5fcf10eba426ddf1a17b71ef65a434ed707ede7d1c4f013ba1609e970bc8c0cddac01365080d376204314e9b294719acd8a5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -20492,6 +20504,31 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"deep-equal@npm:^2.0.5":
|
||||
version: 2.2.0
|
||||
resolution: "deep-equal@npm:2.2.0"
|
||||
dependencies:
|
||||
call-bind: ^1.0.2
|
||||
es-get-iterator: ^1.1.2
|
||||
get-intrinsic: ^1.1.3
|
||||
is-arguments: ^1.1.1
|
||||
is-array-buffer: ^3.0.1
|
||||
is-date-object: ^1.0.5
|
||||
is-regex: ^1.1.4
|
||||
is-shared-array-buffer: ^1.0.2
|
||||
isarray: ^2.0.5
|
||||
object-is: ^1.1.5
|
||||
object-keys: ^1.1.1
|
||||
object.assign: ^4.1.4
|
||||
regexp.prototype.flags: ^1.4.3
|
||||
side-channel: ^1.0.4
|
||||
which-boxed-primitive: ^1.0.2
|
||||
which-collection: ^1.0.1
|
||||
which-typed-array: ^1.1.9
|
||||
checksum: 46a34509d2766d6c6dc5aec4756089cf0cc137e46787e91f08f1ee0bb570d874f19f0493146907df0cf18aed4a7b4b50f6f62c899240a76c323f057528b122e3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"deep-extend@npm:0.6.0, deep-extend@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "deep-extend@npm:0.6.0"
|
||||
@@ -21394,7 +21431,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"es-abstract@npm:^1.18.0-next.1, es-abstract@npm:^1.18.0-next.2, es-abstract@npm:^1.19.0, es-abstract@npm:^1.19.2, es-abstract@npm:^1.19.5, es-abstract@npm:^1.20.4":
|
||||
"es-abstract@npm:^1.19.0, es-abstract@npm:^1.19.2, es-abstract@npm:^1.19.5, es-abstract@npm:^1.20.4":
|
||||
version: 1.20.4
|
||||
resolution: "es-abstract@npm:1.20.4"
|
||||
dependencies:
|
||||
@@ -21426,6 +21463,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"es-get-iterator@npm:^1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "es-get-iterator@npm:1.1.2"
|
||||
dependencies:
|
||||
call-bind: ^1.0.2
|
||||
get-intrinsic: ^1.1.0
|
||||
has-symbols: ^1.0.1
|
||||
is-arguments: ^1.1.0
|
||||
is-map: ^2.0.2
|
||||
is-set: ^2.0.2
|
||||
is-string: ^1.0.5
|
||||
isarray: ^2.0.5
|
||||
checksum: f75e66acb6a45686fa08b3ade9c9421a70d36a0c43ed4363e67f4d7aab2226cb73dd977cb48abbaf75721b946d3cd810682fcf310c7ad0867802fbf929b17dcf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"es-module-lexer@npm:^0.9.0, es-module-lexer@npm:^0.9.3":
|
||||
version: 0.9.3
|
||||
resolution: "es-module-lexer@npm:0.9.3"
|
||||
@@ -21960,25 +22013,28 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"eslint-plugin-jsx-a11y@npm:^6.5.1":
|
||||
version: 6.6.1
|
||||
resolution: "eslint-plugin-jsx-a11y@npm:6.6.1"
|
||||
version: 6.7.0
|
||||
resolution: "eslint-plugin-jsx-a11y@npm:6.7.0"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.18.9
|
||||
aria-query: ^4.2.2
|
||||
array-includes: ^3.1.5
|
||||
"@babel/runtime": ^7.20.7
|
||||
aria-query: ^5.1.3
|
||||
array-includes: ^3.1.6
|
||||
array.prototype.flatmap: ^1.3.1
|
||||
ast-types-flow: ^0.0.7
|
||||
axe-core: ^4.4.3
|
||||
axobject-query: ^2.2.0
|
||||
axe-core: ^4.6.2
|
||||
axobject-query: ^3.1.1
|
||||
damerau-levenshtein: ^1.0.8
|
||||
emoji-regex: ^9.2.2
|
||||
has: ^1.0.3
|
||||
jsx-ast-utils: ^3.3.2
|
||||
language-tags: ^1.0.5
|
||||
jsx-ast-utils: ^3.3.3
|
||||
language-tags: =1.0.5
|
||||
minimatch: ^3.1.2
|
||||
object.entries: ^1.1.6
|
||||
object.fromentries: ^2.0.6
|
||||
semver: ^6.3.0
|
||||
peerDependencies:
|
||||
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
|
||||
checksum: baae7377f0e25a0cc9b34dc333a3dc6ead9ee8365e445451eff554c3ca267a0a6cb88127fe90395c578ab1b92cfed246aef7dc8d2b48b603389e10181799e144
|
||||
checksum: b7ea212bcf84912d264229e5e3cf255bc95a1193de1c7453d275a7afc959ce679c1bffb77cfd3d17f9b7105f41e0f62c8edb7f6d76985c79647edaa9f08aa814
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -22020,8 +22076,8 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"eslint-plugin-react@npm:^7.28.0":
|
||||
version: 7.31.11
|
||||
resolution: "eslint-plugin-react@npm:7.31.11"
|
||||
version: 7.32.0
|
||||
resolution: "eslint-plugin-react@npm:7.32.0"
|
||||
dependencies:
|
||||
array-includes: ^3.1.6
|
||||
array.prototype.flatmap: ^1.3.1
|
||||
@@ -22035,12 +22091,12 @@ __metadata:
|
||||
object.hasown: ^1.1.2
|
||||
object.values: ^1.1.6
|
||||
prop-types: ^15.8.1
|
||||
resolve: ^2.0.0-next.3
|
||||
resolve: ^2.0.0-next.4
|
||||
semver: ^6.3.0
|
||||
string.prototype.matchall: ^4.0.8
|
||||
peerDependencies:
|
||||
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
|
||||
checksum: a3d612f6647bef33cf2a67c81a6b37b42c075300ed079cffecf5fb475c0d6ab855c1de340d1cbf361a0126429fb906dda597527235d2d12c4404453dbc712fc6
|
||||
checksum: b81ce2623b50a936287d8e21997bd855094e643856c99b42a9f0c10e1c7b123e469c3d75f77df9eefb719fee2b47a763862f1cdca1e7cc26edc7cde2fb8cba87
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -23264,7 +23320,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"foreach@npm:^2.0.4, foreach@npm:^2.0.5":
|
||||
"for-each@npm:^0.3.3":
|
||||
version: 0.3.3
|
||||
resolution: "for-each@npm:0.3.3"
|
||||
dependencies:
|
||||
is-callable: ^1.1.3
|
||||
checksum: 6c48ff2bc63362319c65e2edca4a8e1e3483a2fabc72fbe7feaf8c73db94fc7861bd53bc02c8a66a0c1dd709da6b04eec42e0abdd6b40ce47305ae92a25e5d28
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"foreach@npm:^2.0.4":
|
||||
version: 2.0.5
|
||||
resolution: "foreach@npm:2.0.5"
|
||||
checksum: dab4fbfef0b40b69ee5eab81bcb9626b8fa8b3469c8cfa26480f3e5e1ee08c40eae07048c9a967c65aeda26e774511ccc70b3f10a604c01753c6ef24361f0fc8
|
||||
@@ -23310,8 +23375,8 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"fork-ts-checker-webpack-plugin@npm:^7.0.0-alpha.8":
|
||||
version: 7.2.14
|
||||
resolution: "fork-ts-checker-webpack-plugin@npm:7.2.14"
|
||||
version: 7.3.0
|
||||
resolution: "fork-ts-checker-webpack-plugin@npm:7.3.0"
|
||||
dependencies:
|
||||
"@babel/code-frame": ^7.16.7
|
||||
chalk: ^4.1.2
|
||||
@@ -23332,7 +23397,7 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
vue-template-compiler:
|
||||
optional: true
|
||||
checksum: bf4b44e606677da14c5b8127693897003607e7f13ed93a92991665aea5ad5aa3f5629c022d95dbf9380b40bfc02d34fa42857f298525c7688194d1e94f2a2850
|
||||
checksum: 49c2af801e264349a3fdf0afe4ad33065960c43bd7e56c8351a5e0d32c8c54146cc89d6a0b70b1e0f810de96787bd0c7fd275cc8727a9aea1a077c53de99659a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -23748,13 +23813,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-port@npm:^6.1.2":
|
||||
version: 6.1.2
|
||||
resolution: "get-port@npm:6.1.2"
|
||||
checksum: e3c3d591492a11393455ef220f24c812a28f7da56ec3e4a2512d931a1f196d42850b50ac6138349a44622eda6dc3c0ccd8495cd91376d968e2d9e6f6f849e0a9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-stdin@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "get-stdin@npm:8.0.0"
|
||||
@@ -24071,6 +24129,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gopd@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "gopd@npm:1.0.1"
|
||||
dependencies:
|
||||
get-intrinsic: ^1.1.3
|
||||
checksum: a5ccfb8806e0917a94e0b3de2af2ea4979c1da920bc381667c260e00e7cafdbe844e2cb9c5bcfef4e5412e8bf73bab837285bc35c7ba73aaaf0134d4583393a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"got@npm:^11.8.3":
|
||||
version: 11.8.5
|
||||
resolution: "got@npm:11.8.5"
|
||||
@@ -25377,10 +25444,24 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-arguments@npm:^1.0.4":
|
||||
version: 1.0.4
|
||||
resolution: "is-arguments@npm:1.0.4"
|
||||
checksum: a40ce1580cbb28b67790afe91d9c39a9016f165e724021f2c61da016d7382a1b04a202d9d4ea1c8b5d7fda7c15144aa5c4e92ea4ed0896e2b95f4f665a966cd5
|
||||
"is-arguments@npm:^1.0.4, is-arguments@npm:^1.1.0, is-arguments@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "is-arguments@npm:1.1.1"
|
||||
dependencies:
|
||||
call-bind: ^1.0.2
|
||||
has-tostringtag: ^1.0.0
|
||||
checksum: 7f02700ec2171b691ef3e4d0e3e6c0ba408e8434368504bb593d0d7c891c0dbfda6d19d30808b904a6cb1929bca648c061ba438c39f296c2a8ca083229c49f27
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-array-buffer@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "is-array-buffer@npm:3.0.1"
|
||||
dependencies:
|
||||
call-bind: ^1.0.2
|
||||
get-intrinsic: ^1.1.3
|
||||
is-typed-array: ^1.1.10
|
||||
checksum: f26ab87448e698285daf707e52a533920449f7abf63714140ffab9d5571aa5a71ac2fa2677e8b793ad0d5d3e40078d4d2c8a0ab39c957e3cfc6513bb6c9dfdc9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -25442,7 +25523,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-callable@npm:^1.1.4, is-callable@npm:^1.2.7":
|
||||
"is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.7":
|
||||
version: 1.2.7
|
||||
resolution: "is-callable@npm:1.2.7"
|
||||
checksum: 61fd57d03b0d984e2ed3720fb1c7a897827ea174bd44402878e059542ea8c4aeedee0ea0985998aa5cc2736b2fa6e271c08587addb5b3959ac52cf665173d1ac
|
||||
@@ -25471,7 +25552,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-core-module@npm:^2.1.0, is-core-module@npm:^2.2.0, is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0":
|
||||
"is-core-module@npm:^2.1.0, is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0":
|
||||
version: 2.10.0
|
||||
resolution: "is-core-module@npm:2.10.0"
|
||||
dependencies:
|
||||
@@ -25480,7 +25561,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-date-object@npm:^1.0.1":
|
||||
"is-date-object@npm:^1.0.1, is-date-object@npm:^1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "is-date-object@npm:1.0.5"
|
||||
dependencies:
|
||||
@@ -25626,6 +25707,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-map@npm:^2.0.1, is-map@npm:^2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "is-map@npm:2.0.2"
|
||||
checksum: ace3d0ecd667bbdefdb1852de601268f67f2db725624b1958f279316e13fecb8fa7df91fd60f690d7417b4ec180712f5a7ee967008e27c65cfd475cc84337728
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-module@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "is-module@npm:1.0.0"
|
||||
@@ -25800,6 +25888,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-set@npm:^2.0.1, is-set@npm:^2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "is-set@npm:2.0.2"
|
||||
checksum: b64343faf45e9387b97a6fd32be632ee7b269bd8183701f3b3f5b71a7cf00d04450ed8669d0bd08753e08b968beda96fca73a10fd0ff56a32603f64deba55a57
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-shared-array-buffer@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "is-shared-array-buffer@npm:1.0.2"
|
||||
@@ -25866,16 +25961,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-typed-array@npm:^1.1.3":
|
||||
version: 1.1.5
|
||||
resolution: "is-typed-array@npm:1.1.5"
|
||||
"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.3":
|
||||
version: 1.1.10
|
||||
resolution: "is-typed-array@npm:1.1.10"
|
||||
dependencies:
|
||||
available-typed-arrays: ^1.0.2
|
||||
available-typed-arrays: ^1.0.5
|
||||
call-bind: ^1.0.2
|
||||
es-abstract: ^1.18.0-next.2
|
||||
foreach: ^2.0.5
|
||||
has-symbols: ^1.0.1
|
||||
checksum: ba435c83dc1dc0f205c0169f7e93a082816c6b261631a55e473f6f4e18fdf76c1997b326e2e63ae6139e0f75fb47d76252fc76ce75e6b2a74aa41c39743774cb
|
||||
for-each: ^0.3.3
|
||||
gopd: ^1.0.1
|
||||
has-tostringtag: ^1.0.0
|
||||
checksum: aac6ecb59d4c56a1cdeb69b1f129154ef462bbffe434cb8a8235ca89b42f258b7ae94073c41b3cb7bce37f6a1733ad4499f07882d5d5093a7ba84dfc4ebb8017
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -25918,6 +26013,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-weakmap@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "is-weakmap@npm:2.0.1"
|
||||
checksum: 1222bb7e90c32bdb949226e66d26cb7bce12e1e28e3e1b40bfa6b390ba3e08192a8664a703dff2a00a84825f4e022f9cd58c4599ff9981ab72b1d69479f4f7f6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-weakref@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "is-weakref@npm:1.0.2"
|
||||
@@ -25927,6 +26029,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-weakset@npm:^2.0.1":
|
||||
version: 2.0.2
|
||||
resolution: "is-weakset@npm:2.0.2"
|
||||
dependencies:
|
||||
call-bind: ^1.0.2
|
||||
get-intrinsic: ^1.1.1
|
||||
checksum: 5d8698d1fa599a0635d7ca85be9c26d547b317ed8fd83fc75f03efbe75d50001b5eececb1e9971de85fcde84f69ae6f8346bc92d20d55d46201d328e4c74a367
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-windows@npm:^1.0.0, is-windows@npm:^1.0.1":
|
||||
version: 1.0.2
|
||||
resolution: "is-windows@npm:1.0.2"
|
||||
@@ -25964,6 +26076,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"isarray@npm:^2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "isarray@npm:2.0.5"
|
||||
checksum: bd5bbe4104438c4196ba58a54650116007fa0262eccef13a4c55b2e09a5b36b59f1e75b9fcc49883dd9d4953892e6fc007eef9e9155648ceea036e184b0f930a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"isbinaryfile@npm:^4.0.10, isbinaryfile@npm:^4.0.8":
|
||||
version: 4.0.10
|
||||
resolution: "isbinaryfile@npm:4.0.10"
|
||||
@@ -27487,13 +27606,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.2":
|
||||
version: 3.3.2
|
||||
resolution: "jsx-ast-utils@npm:3.3.2"
|
||||
"jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.3":
|
||||
version: 3.3.3
|
||||
resolution: "jsx-ast-utils@npm:3.3.3"
|
||||
dependencies:
|
||||
array-includes: ^3.1.5
|
||||
object.assign: ^4.1.2
|
||||
checksum: 61d4596d44480afc03ae0a7ebb272aa6603dc4c3645805dea0fc8d9f0693542cd0959f3ba7c0c9b16c13dd5a900c7c4310108bada273132a8355efe3fed22064
|
||||
object.assign: ^4.1.3
|
||||
checksum: a2ed78cac49a0f0c4be8b1eafe3c5257a1411341d8e7f1ac740debae003de04e5f6372bfcfbd9d082e954ffd99aac85bcda85b7c6bc11609992483f4cdc0f745
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -27682,7 +27801,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"language-tags@npm:^1.0.5":
|
||||
"language-tags@npm:=1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "language-tags@npm:1.0.5"
|
||||
dependencies:
|
||||
@@ -30483,6 +30602,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object-is@npm:^1.1.5":
|
||||
version: 1.1.5
|
||||
resolution: "object-is@npm:1.1.5"
|
||||
dependencies:
|
||||
call-bind: ^1.0.2
|
||||
define-properties: ^1.1.3
|
||||
checksum: 989b18c4cba258a6b74dc1d74a41805c1a1425bce29f6cabb50dcb1a6a651ea9104a1b07046739a49a5bb1bc49727bcb00efd5c55f932f6ea04ec8927a7901fe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object-keys@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "object-keys@npm:1.1.1"
|
||||
@@ -30490,7 +30619,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object.assign@npm:^4.1.0, object.assign@npm:^4.1.2, object.assign@npm:^4.1.4":
|
||||
"object.assign@npm:^4.1.0, object.assign@npm:^4.1.3, object.assign@npm:^4.1.4":
|
||||
version: 4.1.4
|
||||
resolution: "object.assign@npm:4.1.4"
|
||||
dependencies:
|
||||
@@ -34229,13 +34358,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"resolve@npm:^2.0.0-next.3":
|
||||
version: 2.0.0-next.3
|
||||
resolution: "resolve@npm:2.0.0-next.3"
|
||||
"resolve@npm:^2.0.0-next.4":
|
||||
version: 2.0.0-next.4
|
||||
resolution: "resolve@npm:2.0.0-next.4"
|
||||
dependencies:
|
||||
is-core-module: ^2.2.0
|
||||
path-parse: ^1.0.6
|
||||
checksum: f34b3b93ada77d64a6d590c06a83e198f3a827624c4ec972260905fa6c4d612164fbf0200d16d2beefea4ad1755b001f4a9a1293d8fc2322a8f7d6bf692c4ff5
|
||||
is-core-module: ^2.9.0
|
||||
path-parse: ^1.0.7
|
||||
supports-preserve-symlinks-flag: ^1.0.0
|
||||
bin:
|
||||
resolve: bin/resolve
|
||||
checksum: c438ac9a650f2030fd074219d7f12ceb983b475da2d89ad3d6dd05fbf6b7a0a8cd37d4d10b43cb1f632bc19f22246ab7f36ebda54d84a29bfb2910a0680906d3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -34271,13 +34403,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"resolve@patch:resolve@^2.0.0-next.3#~builtin<compat/resolve>":
|
||||
version: 2.0.0-next.3
|
||||
resolution: "resolve@patch:resolve@npm%3A2.0.0-next.3#~builtin<compat/resolve>::version=2.0.0-next.3&hash=07638b"
|
||||
"resolve@patch:resolve@^2.0.0-next.4#~builtin<compat/resolve>":
|
||||
version: 2.0.0-next.4
|
||||
resolution: "resolve@patch:resolve@npm%3A2.0.0-next.4#~builtin<compat/resolve>::version=2.0.0-next.4&hash=07638b"
|
||||
dependencies:
|
||||
is-core-module: ^2.2.0
|
||||
path-parse: ^1.0.6
|
||||
checksum: 21684b4d99a4877337cdbd5484311c811b3e8910edb5d868eec85c6e6550b0f570d911f9a384f9e176172d6713f2715bd0b0887fa512cb8c6aeece018de6a9f8
|
||||
is-core-module: ^2.9.0
|
||||
path-parse: ^1.0.7
|
||||
supports-preserve-symlinks-flag: ^1.0.0
|
||||
bin:
|
||||
resolve: bin/resolve
|
||||
checksum: 4bf9f4f8a458607af90518ff73c67a4bc1a38b5a23fef2bb0ccbd45e8be89820a1639b637b0ba377eb2be9eedfb1739a84cde24fe4cd670c8207d8fea922b011
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -38606,6 +38741,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which-collection@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "which-collection@npm:1.0.1"
|
||||
dependencies:
|
||||
is-map: ^2.0.1
|
||||
is-set: ^2.0.1
|
||||
is-weakmap: ^2.0.1
|
||||
is-weakset: ^2.0.1
|
||||
checksum: c815bbd163107ef9cb84f135e6f34453eaf4cca994e7ba85ddb0d27cea724c623fae2a473ceccfd5549c53cc65a5d82692de418166df3f858e1e5dc60818581c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which-module@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "which-module@npm:1.0.0"
|
||||
@@ -38630,18 +38777,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which-typed-array@npm:^1.1.2":
|
||||
version: 1.1.4
|
||||
resolution: "which-typed-array@npm:1.1.4"
|
||||
"which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9":
|
||||
version: 1.1.9
|
||||
resolution: "which-typed-array@npm:1.1.9"
|
||||
dependencies:
|
||||
available-typed-arrays: ^1.0.2
|
||||
call-bind: ^1.0.0
|
||||
es-abstract: ^1.18.0-next.1
|
||||
foreach: ^2.0.5
|
||||
function-bind: ^1.1.1
|
||||
has-symbols: ^1.0.1
|
||||
is-typed-array: ^1.1.3
|
||||
checksum: 369597a623b0e446eb7b6ce9e2f515c2f6a0b3f5040b9c592d9ed07fb3357a90ab45311230f7e687cf0f0d410b47e98fba620dbb7eece9f556309a3448b4fa3e
|
||||
available-typed-arrays: ^1.0.5
|
||||
call-bind: ^1.0.2
|
||||
for-each: ^0.3.3
|
||||
gopd: ^1.0.1
|
||||
has-tostringtag: ^1.0.0
|
||||
is-typed-array: ^1.1.10
|
||||
checksum: fe0178ca44c57699ca2c0e657b64eaa8d2db2372a4e2851184f568f98c478ae3dc3fdb5f7e46c384487046b0cf9e23241423242b277e03e8ba3dabc7c84c98ef
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user