Merge branch 'master' into fact-retriever-async

This commit is contained in:
Sam Blausten
2022-07-06 13:55:57 +02:00
committed by GitHub
77 changed files with 1163 additions and 338 deletions
+9
View File
@@ -0,0 +1,9 @@
---
'@backstage/plugin-code-coverage': minor
---
Cleaned up API exports.
The `Router` export has been removed; users are expected to use `EntityCodeCoverageContent` instead.
The `isPluginApplicableToEntity` helper has been deprecated, in favor of the `isCodeCoverageAvailable` helper.
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-code-coverage-backend': minor
---
Cleaned up API exports.
The `CodeCoverageApi` and `makeRouter` exports have been removed from the backend, since they were not meant to be exported in the first place.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-fossa': patch
---
Allow configuration of base URL for Fossa links
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-tech-radar': patch
---
Update tech-radar documentation on how to use an external json data source with dates.
+18
View File
@@ -0,0 +1,18 @@
---
'@backstage/plugin-kubernetes-backend': minor
---
Add new endpoints to Kubernetes backend plugin
BREAKING: Kubernetes backend plugin now depends on CatalogApi
```typescript
// Create new CatalogClient
const catalogApi = new CatalogClient({ discoveryApi: env.discovery });
const { router } = await KubernetesBuilder.createBuilder({
logger: env.logger,
config: env.config,
// Inject it into createBuilder params
catalogApi,
}).build();
```
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-scaffolder-backend': patch
---
new setUserAsOwner flag for publish:gitlab action
The field default is `false`. When true it will use the token configured in the gitlab integration for the matching host, to try and set the user logged in via `repoUrlPicker` `requestUserCredentials` OAuth flow as owner of the repository created in GitLab.
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/core-components': patch
'@backstage/plugin-gcp-projects': patch
'@backstage/plugin-scaffolder': patch
'@backstage/plugin-techdocs-module-addons-contrib': patch
---
Updated dependency `@react-hookz/web` to `^15.0.0`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/config-loader': patch
---
Updated dependency `typescript-json-schema` to `^0.54.0`.
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-kubernetes-backend': patch
'@backstage/plugin-kubernetes-common': patch
'@backstage/plugin-kubernetes': patch
---
Updated dependency `@kubernetes/client-node` to `^0.17.0`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend': minor
---
Allowed post method on /refresh path
+3 -3
View File
@@ -32,7 +32,7 @@ jobs:
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.0
uses: backstage/actions/yarn-install@v0.5.2
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
@@ -61,7 +61,7 @@ jobs:
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.0
uses: backstage/actions/yarn-install@v0.5.2
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
@@ -178,7 +178,7 @@ jobs:
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.0
uses: backstage/actions/yarn-install@v0.5.2
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
cron:
runs-on: ubuntu-latest
steps:
- uses: backstage/actions/cron@v0.5.0
- uses: backstage/actions/cron@v0.5.2
with:
app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }}
private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }}
+3 -3
View File
@@ -69,17 +69,17 @@ jobs:
working-directory: ./example-app
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Build and push
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: './example-app'
file: ./example-app/packages/backend/Dockerfile
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.0
uses: backstage/actions/yarn-install@v0.5.2
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
+2 -2
View File
@@ -67,7 +67,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.0
uses: backstage/actions/yarn-install@v0.5.2
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
@@ -145,7 +145,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.0
uses: backstage/actions/yarn-install@v0.5.2
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
+1 -1
View File
@@ -10,4 +10,4 @@ jobs:
if: github.repository == 'backstage/backstage'
steps:
- name: Issue sync
uses: backstage/actions/issue-sync@v0.5.0
uses: backstage/actions/issue-sync@v0.5.2
@@ -0,0 +1,19 @@
# This workflow is used to trigger the "PR Review Comment" workflow. This chaining is needed
# because workflows triggered by pull_request_review_comment do not have access to secrets.
name: PR Review Comment Trigger
on:
pull_request_review_comment:
types:
- created
jobs:
trigger:
runs-on: ubuntu-latest
# The "PR Review Comment" workflow will check the success status of this workflow and only mark
# the PR for re-review if this workflow was successful, which is when the author leaves a review comment.
if: github.repository == 'backstage/backstage' && github.event.comment.user.id == github.event.pull_request.user.id
steps:
- run: echo "This PR needs another review"
+24
View File
@@ -0,0 +1,24 @@
# This workflow is triggered by "PR Review Comment Trigger"
name: PR Review Comment
on:
workflow_run:
workflows: [PR Review Comment Trigger]
types:
- completed
jobs:
re-review:
runs-on: ubuntu-latest
# The triggering workflow will report success if the PR needs re-review
if: github.repository == 'backstage/backstage' && github.event.workflow_run.conclusion == 'success'
steps:
- uses: backstage/actions/re-review@v0.5.2
with:
app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }}
private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }}
installation-id: ${{ secrets.BACKSTAGE_GOALIE_INSTALLATION_ID }}
project-id: PVT_kwDOBFKqdc02LQ
issue-number: ${{ github.event.workflow_run.pull_requests[0].number }}
+1 -4
View File
@@ -9,9 +9,6 @@ on:
issue_comment:
types:
- created
pull_request_review_comment:
types:
- created
jobs:
sync:
@@ -21,7 +18,7 @@ jobs:
if: github.repository == 'backstage/backstage' && ( github.event.pull_request || github.event.issue.pull_request )
steps:
- name: PR sync
uses: backstage/actions/pr-sync@v0.5.0
uses: backstage/actions/pr-sync@v0.5.2
with:
github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }}
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.0
uses: backstage/actions/yarn-install@v0.5.2
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
@@ -22,7 +22,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.0
uses: backstage/actions/yarn-install@v0.5.2
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.0
uses: backstage/actions/yarn-install@v0.5.2
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
uses: backstage/actions/yarn-install@v0.5.0
uses: backstage/actions/yarn-install@v0.5.2
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
- name: storybook yarn install
+2 -2
View File
@@ -23,7 +23,7 @@ _You can do this by using the [Adopter form](https://form.typeform.com/to/zcOaKi
| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. |
| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. |
| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. |
| [Expedia Group](https://www.expediagroup.com) | [@guillermomanzo](https://github.com/guillermomanzo), [Sheena Sharma](mailto:shesharma@expediagroup.com) | EG Common Developer Toolkit |
| [Expedia Group](https://www.expediagroup.com) | [@guillermomanzo](https://github.com/guillermomanzo), [Sheena Sharma](mailto:shesharma@expediagroup.com), [@ajbw](https://github.com/ajbw) | EG Common Developer Toolkit |
| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go |
| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling |
| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. |
@@ -188,5 +188,5 @@ _You can do this by using the [Adopter form](https://form.typeform.com/to/zcOaKi
| [Cho Tot](https://www.chotot.com) | [Chotot Team](mailto:sre@chotot.vn) | Internal developer portal, service catalog with CI/CD tools. |
| [William Hill](https://www.williamhillgroup.com/) | [Pat Mills](mailto:pat.mills@williamhill.com), [Nathan Flynn](mailto:nflynn@williamhill.co.uk), and [Nishkarsh Raj](mailto:nishkarsh.raj@williamhill.co.uk) | William Hill are leveraging Backstage to build our Engineering Portal. Our mission is to centralize the software catalog inventory to enable service discoverability, reduce the onboarding time for new Engineers, provide a single pane of glass to accelerate Developer Productivity and Save Engineers time. Our aspiration is to create an InnerSource community focussed on organization-wide patterns that are re-usable and can be self-served with the Scaffolder. |
| [Vodafone NewZealand Limited](https://vodafone.co.nz) | [Ankit Gupta](mailto:ankit.gupta@vodafone.nz), [DevOps COE](mailto:devopstooling@vodafone.nz) | Vodafone NZ are leveraging Backstage to build centralised and self service Engineering Portal. Our mission is to standardised Pipeline templates across the Engineering teams, One shop stop to create the pipelines and repository with a template approach which reduces creation part from days to minutes and no wait time for developers. A unified view for Azure DevOps pipeline, Azure Repo pull requests, Deployment status from Azure RedHat Openshift-ArgoCD and SonarQube Security and code quality scans report on a single pan to provide a streamlined view for all microservices across the app stack. |
| [Coamo](http://www.coamo.com.br) | [@holiiveira](https://github.com/holiiveira), [@gpxlnx](https://github.com/gpxlnx) | We're starting to use it as the main tool of a DevOps platform. Our goal is to provide software templates, centralize our software catalog enabling efficient service discovery, and make it easy to manage the entire software ecosystem in one place.
| [Coamo](http://www.coamo.com.br) | [@holiiveira](https://github.com/holiiveira), [@gpxlnx](https://github.com/gpxlnx) | We're starting to use it as the main tool of a DevOps platform. Our goal is to provide software templates, centralize our software catalog enabling efficient service discovery, and make it easy to manage the entire software ecosystem in one place.
|
+1
View File
@@ -350,6 +350,7 @@ router.get('/auth/providerA/handler/frame');
router.post('/auth/providerA/handler/frame');
router.post('/auth/providerA/logout');
router.get('/auth/providerA/refresh'); // if supported
router.post('/auth/providerA/refresh'); // if supported
```
As you can see each endpoint is prefixed with both `/auth` and its provider
+1 -1
View File
@@ -79,7 +79,7 @@ privateKey: |
### Including in Integrations Config
Once the credentials are stored in a YAML file generated by `create-github-app`,
or manually by following the [GitHub Enterprise](#gitHub-enterprise)
or manually by following the [GitHub Enterprise](#github-enterprise)
instructions, they can be included in the `app-config.yaml` under the
`integrations` section.
+2 -1
View File
@@ -341,7 +341,8 @@ separate Docker images.
The backend container can be built by running the following command:
```bash
yarn run docker-build
yarn run build
yarn run build-image
```
This will create a container called `example-backend`.
@@ -1,7 +1,7 @@
---
title: Adopters Community Sessions
date: June 15, 2022
category: Upcoming
category: Meetup
description: Adopters Community Session ✨. It's the monthly meetup where we all come together to listen to the latest maintainer updates, learn from each other about adopting, share exciting new demos or discuss any relevant topic like developer effectiveness, developer experience, developer portals, etc.
youtubeUrl: https://youtu.be/aKZnjnE5Wy8
youtubeImgUrl: https://backstage.io/img/b-sessions.png
+2 -2
View File
@@ -1,9 +1,9 @@
---
title: Contributor Community Sessions
date: June 22, 2022
category: Upcoming
category: Meetup
description: Join the maintainers and contributors for the Contributor Community Sessions
youtubeUrl: https://youtu.be/aKZnjnE5Wy8
youtubeUrl: https://youtu.be/E-jWqWXBxUY
youtubeImgUrl: https://backstage.io/img/b-sessions.png
rsvpUrl: https://calendar.google.com/calendar/embed?src=c_qup9gbhn9sqpuao6trttd8mk5s@group.calendar.google.com
eventUrl: https://github.com/backstage/community/issues/49
+9
View File
@@ -0,0 +1,9 @@
---
title: Adopters Community Sessions
date: July 20, 2022
category: Upcoming
description: Adopters Community Session ✨. It's the monthly meetup where we all come together to listen to the latest maintainer updates, learn from each other about adopting, share exciting new demos or discuss any relevant topic like developer effectiveness, developer experience, developer portals, etc.
youtubeUrl: https://youtu.be/4VFNlPxWcx8
youtubeImgUrl: https://backstage.io/img/b-sessions.png
rsvpUrl: https://calendar.google.com/calendar/embed?src=c_qup9gbhn9sqpuao6trttd8mk5s@group.calendar.google.com
eventUrl: https://github.com/backstage/community/issues/52
+9
View File
@@ -0,0 +1,9 @@
---
title: Contributor Community Sessions
date: July 27, 2022
category: Upcoming
description: Join the maintainers and contributors for the Contributor Community Sessions
youtubeUrl: https://youtu.be/4VFNlPxWcx8
youtubeImgUrl: https://backstage.io/img/b-sessions.png
rsvpUrl: https://calendar.google.com/calendar/embed?src=c_qup9gbhn9sqpuao6trttd8mk5s@group.calendar.google.com
eventUrl: https://github.com/backstage/community/issues/52
+2 -2
View File
@@ -34,7 +34,7 @@ const Background = props => {
<Block.Container style={{ justifyContent: 'flex-start' }}>
<Block.MediaFrame>
<iframe
src="https://www.youtube.com/embed/aKZnjnE5Wy8"
src="https://www.youtube.com/embed/4VFNlPxWcx8"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
@@ -45,7 +45,7 @@ const Background = props => {
<iframe
width="300"
height="500"
src="https://www.youtube.com/live_chat?v=aKZnjnE5Wy8&embed_domain=backstage.io&dark_theme=1"
src="https://www.youtube.com/live_chat?v=4VFNlPxWcx8&embed_domain=backstage.io&dark_theme=1"
></iframe>
</Block.MediaFrame>
</Block.Container>
@@ -17,13 +17,16 @@
import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { CatalogClient } from '@backstage/catalog-client';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const catalogApi = new CatalogClient({ discoveryApi: env.discovery });
const { router } = await KubernetesBuilder.createBuilder({
logger: env.logger,
config: env.config,
catalogApi,
}).build();
return router;
}
+1 -1
View File
@@ -46,7 +46,7 @@
"json-schema-merge-allof": "^0.8.1",
"json-schema-traverse": "^1.0.0",
"node-fetch": "^2.6.7",
"typescript-json-schema": "^0.53.0",
"typescript-json-schema": "^0.54.0",
"yaml": "^1.9.2",
"yup": "^0.32.9"
},
+1 -1
View File
@@ -66,7 +66,7 @@
"react-syntax-highlighter": "^15.4.5",
"react-text-truncate": "^0.19.0",
"react-use": "^17.3.2",
"@react-hookz/web": "^14.0.0",
"@react-hookz/web": "^15.0.0",
"react-virtualized-auto-sizer": "^1.0.6",
"react-window": "^1.8.6",
"remark-gfm": "^3.0.1",
@@ -140,6 +140,7 @@ export async function createRouter(
}
if (provider.refresh) {
r.get('/refresh', provider.refresh.bind(provider));
r.post('/refresh', provider.refresh.bind(provider));
}
router.use(`/${providerId}`, r);
+2 -19
View File
@@ -10,27 +10,10 @@ import { PluginDatabaseManager } from '@backstage/backend-common';
import { PluginEndpointDiscovery } from '@backstage/backend-common';
import { UrlReader } from '@backstage/backend-common';
// Warning: (ae-missing-release-tag) "CodeCoverageApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface CodeCoverageApi {
// (undocumented)
name: string;
}
// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export function createRouter(options: RouterOptions): Promise<express.Router>;
// Warning: (ae-missing-release-tag) "makeRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const makeRouter: (options: RouterOptions) => Promise<express.Router>;
// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export interface RouterOptions {
// (undocumented)
config: Config;
+2 -1
View File
@@ -20,4 +20,5 @@
* @packageDocumentation
*/
export * from './service/router';
export { createRouter } from './service/router';
export type { RouterOptions } from './service/router';
@@ -35,6 +35,11 @@ import { Jacoco } from './converter/jacoco';
import { Converter } from './converter';
import { getEntitySourceLocation } from '@backstage/catalog-model';
/**
* Options for {@link createRouter}.
*
* @public
*/
export interface RouterOptions {
config: Config;
discovery: PluginEndpointDiscovery;
@@ -211,6 +216,11 @@ export const makeRouter = async (
return router;
};
/**
* Creates a code-coverage plugin backend router.
*
* @public
*/
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
+5 -15
View File
@@ -9,8 +9,6 @@ import { BackstagePlugin } from '@backstage/core-plugin-api';
import { Entity } from '@backstage/catalog-model';
import { RouteRef } from '@backstage/core-plugin-api';
// Warning: (ae-missing-release-tag) "codeCoveragePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const codeCoveragePlugin: BackstagePlugin<
{
@@ -19,20 +17,12 @@ export const codeCoveragePlugin: BackstagePlugin<
{}
>;
// Warning: (ae-missing-release-tag) "EntityCodeCoverageContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export const EntityCodeCoverageContent: () => JSX.Element;
// Warning: (ae-missing-release-tag) "isCodeCoverageAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
const isCodeCoverageAvailable: (entity: Entity) => boolean;
export { isCodeCoverageAvailable };
export { isCodeCoverageAvailable as isPluginApplicableToEntity };
// @public
export function isCodeCoverageAvailable(entity: Entity): boolean;
// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const Router: () => JSX.Element;
// @public @deprecated (undocumented)
export const isPluginApplicableToEntity: typeof isCodeCoverageAvailable;
```
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'highlight.js/styles/atom-one-dark.css';
import highlight from 'highlight.js';
@@ -13,16 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { Entity } from '@backstage/catalog-model';
import { useEntity } from '@backstage/plugin-catalog-react';
import { CodeCoveragePage } from './CodeCoveragePage';
import { MissingAnnotationEmptyState } from '@backstage/core-components';
export const isCodeCoverageAvailable = (entity: Entity) =>
Boolean(entity.metadata.annotations?.['backstage.io/code-coverage']);
/**
* Returns true if the given entity has code coverage enabled.
*
* @public
*/
export function isCodeCoverageAvailable(entity: Entity) {
return Boolean(entity.metadata.annotations?.['backstage.io/code-coverage']);
}
export const Router = () => {
/**
* @public
*/
export const Router = (): JSX.Element => {
const { entity } = useEntity();
if (!isCodeCoverageAvailable(entity)) {
@@ -30,5 +40,6 @@ export const Router = () => {
<MissingAnnotationEmptyState annotation="backstage.io/code-coverage" />
);
}
return <CodeCoveragePage />;
};
+9 -5
View File
@@ -20,9 +20,13 @@
* @packageDocumentation
*/
import { isCodeCoverageAvailable } from './components/Router';
export { codeCoveragePlugin, EntityCodeCoverageContent } from './plugin';
export {
Router,
isCodeCoverageAvailable,
isCodeCoverageAvailable as isPluginApplicableToEntity,
} from './components/Router';
export { isCodeCoverageAvailable };
/**
* @public
* @deprecated Use `isPluginApplicableToEntity` instead.
*/
export const isPluginApplicableToEntity = isCodeCoverageAvailable;
+8
View File
@@ -23,6 +23,9 @@ import {
discoveryApiRef,
} from '@backstage/core-plugin-api';
/**
* @public
*/
export const codeCoveragePlugin = createPlugin({
id: 'code-coverage',
routes: {
@@ -37,6 +40,11 @@ export const codeCoveragePlugin = createPlugin({
],
});
/**
* An entity code coverage page.
*
* @public
*/
export const EntityCodeCoverageContent = codeCoveragePlugin.provide(
createRoutableExtension({
name: 'EntityCodeCoverageContent',
+4 -1
View File
@@ -43,9 +43,12 @@ proxy:
headers:
Authorization: token ${FOSSA_API_TOKEN}
# if you have a fossa organization, configure your id here
fossa:
# if you have a fossa organization, configure your id here
organizationId: <your-fossa-organization-id>
# if you have a self-managed fossa instance,
# configure the baseUrl to use for links to the fossa page here
externalLinkBaseUrl: <your fossa url>
```
4. Get an api-token and provide `FOSSA_AUTH_HEADER` as env variable (https://app.fossa.com/account/settings/integrations/api_tokens)
+7 -1
View File
@@ -20,6 +20,12 @@ export interface Config {
* The organization id in fossa.
* @visibility frontend
*/
organizationId: string;
organizationId?: string;
/**
* The base url to use for external links (from Backstage to Fossa).
* @visibility frontend
*/
externalLinkBaseUrl?: string;
};
}
+52
View File
@@ -175,6 +175,58 @@ describe('FossaClient', () => {
expect(summary).toBeUndefined();
});
it('should allow custom external link base url', async () => {
client = new FossaClient({
discoveryApi,
identityApi,
organizationId: '8736',
externalLinkBaseUrl: 'https://custom.fossa.com', // overrides the default app.fossa.com
});
server.use(
rest.get(`${mockBaseUrl}/fossa/projects`, (req, res, ctx) => {
const expectedQuery =
'count=1000&page=0&sort=title%2B&organizationId=8736&title=our-service';
if (req.url.searchParams.toString() !== expectedQuery) {
return res(
ctx.status(500),
ctx.body(
`${req.url.searchParams.toString()} !== ${expectedQuery}`,
),
);
}
return res(
ctx.json([
{
locator: 'custom+8736/our-service',
title: 'our-service',
default_branch: 'develop',
revisions: [
{
updatedAt: '2020-01-01T00:00:00Z',
dependency_count: 160,
unresolved_licensing_issue_count: 5,
unresolved_issue_count: 100,
},
],
},
]),
);
}),
);
const summary = await client.getFindingSummary('our-service');
expect(summary).toEqual({
timestamp: '2020-01-01T00:00:00Z',
issueCount: 5,
dependencyCount: 160,
projectDefaultBranch: 'develop',
projectUrl:
'https://custom.fossa.com/projects/custom%2B8736%2Four-service',
} as FindingSummary);
});
it('should handle 404 status', async () => {
server.use(
rest.get(`${mockBaseUrl}/fossa/projects`, (_req, res, ctx) => {
+7 -3
View File
@@ -36,20 +36,24 @@ export class FossaClient implements FossaApi {
discoveryApi: DiscoveryApi;
identityApi: IdentityApi;
organizationId?: string;
externalLinkBaseUrl: string;
private readonly limit = pLimit(5);
constructor({
discoveryApi,
identityApi,
organizationId,
externalLinkBaseUrl = 'https://app.fossa.com',
}: {
discoveryApi: DiscoveryApi;
identityApi: IdentityApi;
organizationId?: string;
externalLinkBaseUrl?: string;
}) {
this.discoveryApi = discoveryApi;
this.identityApi = identityApi;
this.organizationId = organizationId;
this.externalLinkBaseUrl = externalLinkBaseUrl;
}
private async callApi<T>(
@@ -104,9 +108,9 @@ export class FossaClient implements FossaApi {
revision.unresolved_issue_count,
dependencyCount: revision.dependency_count,
projectDefaultBranch: project.default_branch,
projectUrl: `https://app.fossa.com/projects/${encodeURIComponent(
project.locator,
)}`,
projectUrl: `${
this.externalLinkBaseUrl
}/projects/${encodeURIComponent(project.locator)}`,
},
};
}
+3
View File
@@ -39,6 +39,9 @@ export const fossaPlugin = createPlugin({
discoveryApi,
identityApi,
organizationId: configApi.getOptionalString('fossa.organizationId'),
externalLinkBaseUrl: configApi.getOptionalString(
'fossa.externalLinkBaseUrl',
),
}),
}),
],
+1 -1
View File
@@ -40,7 +40,7 @@
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"@react-hookz/web": "^14.0.0",
"@react-hookz/web": "^15.0.0",
"react-router-dom": "6.0.0-beta.0"
},
"peerDependencies": {
+6
View File
@@ -3,6 +3,7 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { CatalogApi } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import { Duration } from 'luxon';
import { Entity } from '@backstage/catalog-model';
@@ -108,6 +109,7 @@ export class KubernetesBuilder {
protected buildRouter(
objectsProvider: KubernetesObjectsProvider,
clusterSupplier: KubernetesClustersSupplier,
catalogApi: CatalogApi,
): express.Router;
// (undocumented)
protected buildServiceLocator(
@@ -155,6 +157,8 @@ export interface KubernetesClustersSupplier {
// @alpha (undocumented)
export interface KubernetesEnvironment {
// (undocumented)
catalogApi: CatalogApi;
// (undocumented)
config: Config;
// (undocumented)
@@ -268,6 +272,8 @@ export interface ObjectToFetch {
// @alpha (undocumented)
export interface RouterOptions {
// (undocumented)
catalogApi: CatalogApi;
// (undocumented)
clusterSupplier?: KubernetesClustersSupplier;
// (undocumented)
+4 -2
View File
@@ -37,12 +37,14 @@
"dependencies": {
"@azure/identity": "^2.0.4",
"@backstage/backend-common": "^0.14.1-next.2",
"@backstage/catalog-client": "^1.0.4-next.1",
"@backstage/catalog-model": "^1.1.0-next.2",
"@backstage/config": "^1.0.1",
"@backstage/errors": "^1.1.0-next.0",
"@backstage/plugin-kubernetes-common": "^0.4.0-next.1",
"@backstage/plugin-auth-node": "^0.2.3-next.1",
"@backstage/plugin-kubernetes-common": "^0.4.0-next.0",
"@google-cloud/container": "^4.0.0",
"@kubernetes/client-node": "^0.16.0",
"@kubernetes/client-node": "^0.17.0",
"@types/express": "^4.17.6",
"@types/luxon": "^2.0.4",
"aws-sdk": "^2.840.0",
@@ -0,0 +1,545 @@
/*
* 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 { errorHandler } from '@backstage/backend-common';
import express from 'express';
import request from 'supertest';
import Router from 'express-promise-router';
import { addResourceRoutesToRouter } from './resourcesRoutes';
import { Entity } from '@backstage/catalog-model';
describe('resourcesRoutes', () => {
let app: express.Express;
beforeAll(() => {
app = express();
app.use(express.json());
const router = Router();
addResourceRoutesToRouter(
router,
{
getEntityByRef: jest.fn().mockImplementation(entityRef => {
if (entityRef.name === 'noentity') {
return Promise.resolve(undefined);
}
return Promise.resolve({
kind: entityRef.kind,
metadata: {
name: entityRef.name,
namespace: entityRef.namespace,
},
} as Entity);
}),
} as any,
{
getKubernetesObjectsByEntity: jest.fn().mockImplementation(args => {
if (args.entity.metadata.name === 'inject500') {
return Promise.reject(new Error('some internal error'));
}
return Promise.resolve({
items: [
{
clusterOne: {
pods: [
{
metadata: {
name: 'pod1',
},
},
],
},
},
],
});
}),
getCustomResourcesByEntity: jest.fn().mockImplementation(args => {
if (args.entity.metadata.name === 'inject500') {
return Promise.reject(new Error('some internal error'));
}
return Promise.resolve({
items: [
{
clusterOne: {
pods: [
{
metadata: {
name: 'pod1',
},
},
],
},
},
],
});
}),
} as any,
);
app.use('/', router);
app.use(errorHandler());
});
describe('POST /resources/workloads/query', () => {
// eslint-disable-next-line jest/expect-expect
it('200 happy path', async () => {
await request(app)
.post('/resources/workloads/query')
.send({
entityRef: 'kind:namespacec/someComponent',
auth: {
google: 'something',
},
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(200, {
items: [
{
clusterOne: {
pods: [
{
metadata: {
name: 'pod1',
},
},
],
},
},
],
});
});
// eslint-disable-next-line jest/expect-expect
it('400 when missing entity ref', async () => {
await request(app)
.post('/resources/workloads/query')
.send({
auth: {
google: 'something',
},
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(400, {
error: { name: 'InputError', message: 'entity is a required field' },
request: {
method: 'POST',
url: '/resources/workloads/query',
},
response: { statusCode: 400 },
});
});
// eslint-disable-next-line jest/expect-expect
it('400 when bad entity ref', async () => {
await request(app)
.post('/resources/workloads/query')
.send({
entityRef: 'ffff',
auth: {
google: 'something',
},
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(400, {
error: {
name: 'InputError',
message:
'Invalid entity ref, Error: Entity reference "ffff" had missing or empty kind (e.g. did not start with "component:" or similar)',
},
request: {
method: 'POST',
url: '/resources/workloads/query',
},
response: { statusCode: 400 },
});
});
// eslint-disable-next-line jest/expect-expect
it('400 when no entity in catalog', async () => {
await request(app)
.post('/resources/workloads/query')
.send({
entityRef: 'noentity:noentity',
auth: {
google: 'something',
},
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(400, {
error: {
name: 'InputError',
message: 'Entity ref missing, noentity:default/noentity',
},
request: {
method: 'POST',
url: '/resources/workloads/query',
},
response: { statusCode: 400 },
});
});
// eslint-disable-next-line jest/expect-expect
it('401 when no Auth header', async () => {
await request(app)
.post('/resources/workloads/query')
.send({
entityRef: 'component:someComponent',
auth: {
google: 'something',
},
})
.set('Content-Type', 'application/json')
.expect(401, {
error: { name: 'AuthenticationError', message: 'No Backstage token' },
request: {
method: 'POST',
url: '/resources/workloads/query',
},
response: { statusCode: 401 },
});
});
// eslint-disable-next-line jest/expect-expect
it('401 when invalid Auth header', async () => {
await request(app)
.post('/resources/workloads/query')
.send({
entityRef: 'component:someComponent',
auth: {
google: 'something',
},
})
.set('Content-Type', 'application/json')
.set('Authorization', 'ffffff')
.expect(401, {
error: { name: 'AuthenticationError', message: 'No Backstage token' },
request: {
method: 'POST',
url: '/resources/workloads/query',
},
response: { statusCode: 401 },
});
});
// eslint-disable-next-line jest/expect-expect
it('500 handle gracefully', async () => {
await request(app)
.post('/resources/workloads/query')
.send({
entityRef: 'inject500:inject500/inject500',
auth: {
google: 'something',
},
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(500, {
error: {
name: 'Error',
message: 'some internal error',
level: 'error',
service: 'backstage',
},
request: { method: 'POST', url: '/resources/workloads/query' },
response: { statusCode: 500 },
});
});
});
describe('POST /resources/custom/query', () => {
// eslint-disable-next-line jest/expect-expect
it('200 happy path', async () => {
await request(app)
.post('/resources/custom/query')
.send({
entityRef: 'component:someComponent',
auth: {
google: 'something',
},
customResources: [
{
group: 'someGroup',
apiVersion: 'someApiVersion',
plural: 'somePlural',
},
],
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(200, {
items: [
{
clusterOne: {
pods: [
{
metadata: {
name: 'pod1',
},
},
],
},
},
],
});
});
// eslint-disable-next-line jest/expect-expect
it('400 when missing custom resources', async () => {
await request(app)
.post('/resources/custom/query')
.send({
entityRef: 'component:someComponent',
auth: {
google: 'something',
},
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(400, {
error: {
name: 'InputError',
message: 'customResources is a required field',
},
request: {
method: 'POST',
url: '/resources/custom/query',
},
response: { statusCode: 400 },
});
});
// eslint-disable-next-line jest/expect-expect
it('400 when custom resources not array', async () => {
await request(app)
.post('/resources/custom/query')
.send({
entityRef: 'component:someComponent',
auth: {
google: 'something',
},
customResources: 'somestring',
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(400, {
error: {
name: 'InputError',
message: 'customResources must be an array',
},
request: {
method: 'POST',
url: '/resources/custom/query',
},
response: { statusCode: 400 },
});
});
// eslint-disable-next-line jest/expect-expect
it('400 when custom resources empty', async () => {
await request(app)
.post('/resources/custom/query')
.send({
entityRef: 'component:someComponent',
auth: {
google: 'something',
},
customResources: [],
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(400, {
error: {
name: 'InputError',
message: 'at least 1 customResource is required',
},
request: {
method: 'POST',
url: '/resources/custom/query',
},
response: { statusCode: 400 },
});
});
// eslint-disable-next-line jest/expect-expect
it('400 when missing entity ref', async () => {
await request(app)
.post('/resources/custom/query')
.send({
auth: {
google: 'something',
},
customResources: [
{
group: 'someGroup',
apiVersion: 'someApiVersion',
plural: 'somePlural',
},
],
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(400, {
error: { name: 'InputError', message: 'entity is a required field' },
request: {
method: 'POST',
url: '/resources/custom/query',
},
response: { statusCode: 400 },
});
});
// eslint-disable-next-line jest/expect-expect
it('400 when bad entity ref', async () => {
await request(app)
.post('/resources/custom/query')
.send({
entityRef: 'ffff',
auth: {
google: 'something',
},
customResources: [
{
group: 'someGroup',
apiVersion: 'someApiVersion',
plural: 'somePlural',
},
],
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(400, {
error: {
name: 'InputError',
message:
'Invalid entity ref, Error: Entity reference "ffff" had missing or empty kind (e.g. did not start with "component:" or similar)',
},
request: {
method: 'POST',
url: '/resources/custom/query',
},
response: { statusCode: 400 },
});
});
// eslint-disable-next-line jest/expect-expect
it('400 when no entity in catalog', async () => {
await request(app)
.post('/resources/custom/query')
.send({
entityRef: 'noentity:noentity',
auth: {
google: 'something',
},
customResources: [
{
group: 'someGroup',
apiVersion: 'someApiVersion',
plural: 'somePlural',
},
],
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(400, {
error: {
name: 'InputError',
message: 'Entity ref missing, noentity:default/noentity',
},
request: {
method: 'POST',
url: '/resources/custom/query',
},
response: { statusCode: 400 },
});
});
// eslint-disable-next-line jest/expect-expect
it('401 when no Auth header', async () => {
await request(app)
.post('/resources/custom/query')
.send({
entityRef: 'component:someComponent',
auth: {
google: 'something',
},
customResources: [
{
group: 'someGroup',
apiVersion: 'someApiVersion',
plural: 'somePlural',
},
],
})
.set('Content-Type', 'application/json')
.expect(401, {
error: { name: 'AuthenticationError', message: 'No Backstage token' },
request: {
method: 'POST',
url: '/resources/custom/query',
},
response: { statusCode: 401 },
});
});
// eslint-disable-next-line jest/expect-expect
it('401 when invalid Auth header', async () => {
await request(app)
.post('/resources/custom/query')
.send({
entityRef: 'component:someComponent',
auth: {
google: 'something',
},
customResources: [
{
group: 'someGroup',
apiVersion: 'someApiVersion',
plural: 'somePlural',
},
],
})
.set('Content-Type', 'application/json')
.set('Authorization', 'ffffff')
.expect(401, {
error: { name: 'AuthenticationError', message: 'No Backstage token' },
request: {
method: 'POST',
url: '/resources/custom/query',
},
response: { statusCode: 401 },
});
});
// eslint-disable-next-line jest/expect-expect
it('500 handle gracefully', async () => {
await request(app)
.post('/resources/custom/query')
.send({
entityRef: 'inject500:inject500/inject500',
auth: {
google: 'something',
},
customResources: [
{
group: 'someGroup',
apiVersion: 'someApiVersion',
plural: 'somePlural',
},
],
})
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer Zm9vYmFy')
.expect(500, {
error: {
name: 'Error',
message: 'some internal error',
level: 'error',
service: 'backstage',
},
request: { method: 'POST', url: '/resources/custom/query' },
response: { statusCode: 500 },
});
});
});
});
@@ -0,0 +1,94 @@
/*
* 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 {
CompoundEntityRef,
parseEntityRef,
stringifyEntityRef,
} from '@backstage/catalog-model';
import { CatalogApi } from '@backstage/catalog-client';
import { InputError, AuthenticationError } from '@backstage/errors';
import express, { Request } from 'express';
import { KubernetesObjectsProvider } from '../types/types';
import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node';
export const addResourceRoutesToRouter = (
router: express.Router,
catalogApi: CatalogApi,
objectsProvider: KubernetesObjectsProvider,
) => {
const getEntityByReq = async (req: Request<any>) => {
const rawEntityRef = req.body.entityRef;
if (rawEntityRef && typeof rawEntityRef !== 'string') {
throw new InputError(`entity query must be a string`);
} else if (!rawEntityRef) {
throw new InputError('entity is a required field');
}
let entityRef: CompoundEntityRef | undefined = undefined;
try {
entityRef = parseEntityRef(rawEntityRef);
} catch (error) {
throw new InputError(`Invalid entity ref, ${error}`);
}
const token = getBearerTokenFromAuthorizationHeader(
req.headers.authorization,
);
if (!token) {
throw new AuthenticationError('No Backstage token');
}
const entity = await catalogApi.getEntityByRef(entityRef, {
token: token,
});
if (!entity) {
throw new InputError(
`Entity ref missing, ${stringifyEntityRef(entityRef)}`,
);
}
return entity;
};
router.post('/resources/workloads/query', async (req, res) => {
const entity = await getEntityByReq(req);
const response = await objectsProvider.getKubernetesObjectsByEntity({
entity,
auth: req.body.auth,
});
res.json(response);
});
router.post('/resources/custom/query', async (req, res) => {
const entity = await getEntityByReq(req);
if (!req.body.customResources) {
throw new InputError('customResources is a required field');
} else if (!Array.isArray(req.body.customResources)) {
throw new InputError('customResources must be an array');
} else if (req.body.customResources.length === 0) {
throw new InputError('at least 1 customResource is required');
}
const response = await objectsProvider.getCustomResourcesByEntity({
entity,
customResources: req.body.customResources,
auth: req.body.auth,
});
res.json(response);
});
};
@@ -31,11 +31,13 @@ import {
import { KubernetesBuilder } from './KubernetesBuilder';
import { KubernetesFanOutHandler } from './KubernetesFanOutHandler';
import { PodStatus } from '@kubernetes/client-node';
import { CatalogApi } from '@backstage/catalog-client';
describe('KubernetesBuilder', () => {
let app: express.Express;
let kubernetesFanOutHandler: jest.Mocked<KubernetesFanOutHandler>;
let config: Config;
let catalogApi: CatalogApi;
beforeAll(async () => {
const logger = getVoidLogger();
@@ -69,7 +71,13 @@ describe('KubernetesBuilder', () => {
getKubernetesObjectsByEntity: jest.fn(),
} as any;
const { router } = await KubernetesBuilder.createBuilder({ config, logger })
catalogApi = {} as CatalogApi;
const { router } = await KubernetesBuilder.createBuilder({
config,
logger,
catalogApi,
})
.setObjectsProvider(kubernetesFanOutHandler)
.setClusterSupplier(clusterSupplier)
.build();
@@ -240,6 +248,7 @@ describe('KubernetesBuilder', () => {
const { router } = await KubernetesBuilder.createBuilder({
logger,
config,
catalogApi,
})
.setClusterSupplier(clusterSupplier)
.setServiceLocator(serviceLocator)
@@ -37,6 +37,8 @@ import {
KubernetesFanOutHandler,
} from './KubernetesFanOutHandler';
import { KubernetesClientBasedFetcher } from './KubernetesFetcher';
import { addResourceRoutesToRouter } from '../routes/resourcesRoutes';
import { CatalogApi } from '@backstage/catalog-client';
/**
*
@@ -45,6 +47,7 @@ import { KubernetesClientBasedFetcher } from './KubernetesFetcher';
export interface KubernetesEnvironment {
logger: Logger;
config: Config;
catalogApi: CatalogApi;
}
/**
@@ -119,7 +122,11 @@ export class KubernetesBuilder {
objectTypesToFetch: this.getObjectTypesToFetch(),
});
const router = this.buildRouter(objectsProvider, clusterSupplier);
const router = this.buildRouter(
objectsProvider,
clusterSupplier,
this.env.catalogApi,
);
return {
clusterSupplier,
@@ -226,11 +233,13 @@ export class KubernetesBuilder {
protected buildRouter(
objectsProvider: KubernetesObjectsProvider,
clusterSupplier: KubernetesClustersSupplier,
catalogApi: CatalogApi,
): express.Router {
const logger = this.env.logger;
const router = Router();
router.use(express.json());
// @deprecated
router.post('/services/:serviceId', async (req, res) => {
const serviceId = req.params.serviceId;
const requestBody: ObjectsByEntityRequest = req.body;
@@ -259,6 +268,9 @@ export class KubernetesBuilder {
})),
});
});
addResourceRoutesToRouter(router, catalogApi, objectsProvider);
return router;
}
@@ -19,6 +19,7 @@ import { Logger } from 'winston';
import { KubernetesClustersSupplier } from '../types/types';
import express from 'express';
import { KubernetesBuilder } from './KubernetesBuilder';
import { CatalogApi } from '@backstage/catalog-client';
/**
*
@@ -27,6 +28,7 @@ import { KubernetesBuilder } from './KubernetesBuilder';
export interface RouterOptions {
logger: Logger;
config: Config;
catalogApi: CatalogApi;
clusterSupplier?: KubernetesClustersSupplier;
}
@@ -18,6 +18,7 @@ import {
errorHandler,
notFoundHandler,
requestLoggingHandler,
SingleHostDiscovery,
} from '@backstage/backend-common';
import compression from 'compression';
import cors from 'cors';
@@ -26,6 +27,7 @@ import helmet from 'helmet';
import { Logger } from 'winston';
import { createRouter } from './router';
import { ConfigReader } from '@backstage/config';
import { CatalogClient } from '@backstage/catalog-client';
export interface ApplicationOptions {
enableCors: boolean;
@@ -39,6 +41,10 @@ export async function createStandaloneApplication(
const config = new ConfigReader({});
const app = express();
const catalogApi = new CatalogClient({
discoveryApi: SingleHostDiscovery.fromConfig(config),
});
app.use(helmet());
if (enableCors) {
app.use(cors());
@@ -46,7 +52,7 @@ export async function createStandaloneApplication(
app.use(compression());
app.use(express.json());
app.use(requestLoggingHandler());
app.use('/', await createRouter({ logger, config }));
app.use('/', await createRouter({ logger, config, catalogApi }));
app.use(notFoundHandler());
app.use(errorHandler());
+2 -1
View File
@@ -38,8 +38,9 @@
"url": "https://github.com/backstage/backstage/issues"
},
"dependencies": {
"@backstage/catalog-model": "^1.1.0-next.1",
"@backstage/catalog-model": "^1.1.0-next.2",
"@kubernetes/client-node": "^0.16.0"
"@kubernetes/client-node": "^0.17.0"
},
"devDependencies": {
"@backstage/cli": "^0.18.0-next.2"
+1 -1
View File
@@ -41,7 +41,7 @@
"@backstage/plugin-catalog-react": "^1.1.2-next.2",
"@backstage/plugin-kubernetes-common": "^0.4.0-next.1",
"@backstage/theme": "^0.2.16-next.1",
"@kubernetes/client-node": "^0.16.0",
"@kubernetes/client-node": "^0.17.0",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
+11
View File
@@ -14,3 +14,14 @@
* limitations under the License.
*/
import '@testing-library/jest-dom';
// eslint-disable-next-line no-restricted-imports
import { TextDecoder, TextEncoder } from 'util';
// These are missing from jest-node, so not available on global.
Object.defineProperty(global, 'TextEncoder', {
value: TextEncoder,
});
Object.defineProperty(global, 'TextDecoder', {
value: TextDecoder,
});
+1
View File
@@ -388,6 +388,7 @@ export function createPublishGitlabAction(options: {
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
setUserAsOwner?: boolean | undefined;
}>;
// @public
@@ -32,6 +32,9 @@ const mockGitlabClient = {
Users: {
current: jest.fn(),
},
ProjectMembers: {
add: jest.fn(),
},
};
jest.mock('@gitbeaker/node', () => ({
Gitlab: class {
@@ -113,6 +116,7 @@ describe('publish:gitlab', () => {
});
it('should work when there is a token provided through ctx.input', async () => {
mockGitlabClient.Users.current.mockResolvedValue({ id: 12345 });
mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 });
mockGitlabClient.Projects.create.mockResolvedValue({
http_url_to_repo: 'http://mockurl.git',
@@ -135,6 +139,7 @@ describe('publish:gitlab', () => {
});
it('should call the correct Gitlab APIs when the owner is an organization', async () => {
mockGitlabClient.Users.current.mockResolvedValue({ id: 12345 });
mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 });
mockGitlabClient.Projects.create.mockResolvedValue({
http_url_to_repo: 'http://mockurl.git',
@@ -168,6 +173,7 @@ describe('publish:gitlab', () => {
});
it('should call initRepoAndPush with the correct values', async () => {
mockGitlabClient.Users.current.mockResolvedValue({ id: 12345 });
mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 });
mockGitlabClient.Projects.create.mockResolvedValue({
http_url_to_repo: 'http://mockurl.git',
@@ -187,6 +193,7 @@ describe('publish:gitlab', () => {
});
it('should call initRepoAndPush with the correct default branch', async () => {
mockGitlabClient.Users.current.mockResolvedValue({ id: 12345 });
mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 });
mockGitlabClient.Projects.create.mockResolvedValue({
http_url_to_repo: 'http://mockurl.git',
@@ -241,6 +248,7 @@ describe('publish:gitlab', () => {
config: customAuthorConfig,
});
mockGitlabClient.Users.current.mockResolvedValue({ id: 12345 });
mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 });
mockGitlabClient.Projects.create.mockResolvedValue({
http_url_to_repo: 'http://mockurl.git',
@@ -286,6 +294,7 @@ describe('publish:gitlab', () => {
config: customAuthorConfig,
});
mockGitlabClient.Users.current.mockResolvedValue({ id: 12345 });
mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 });
mockGitlabClient.Projects.create.mockResolvedValue({
http_url_to_repo: 'http://mockurl.git',
@@ -305,6 +314,7 @@ describe('publish:gitlab', () => {
});
it('should call output with the remoteUrl and repoContentsUrl', async () => {
mockGitlabClient.Users.current.mockResolvedValue({ id: 12345 });
mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 });
mockGitlabClient.Projects.create.mockResolvedValue({
http_url_to_repo: 'http://mockurl.git',
@@ -321,4 +331,54 @@ describe('publish:gitlab', () => {
'http://mockurl/-/blob/master',
);
});
it('should call the correct Gitlab APIs when setUserAsOwner option is true and integration config has a token', async () => {
const customAuthorConfig = new ConfigReader({
integrations: {
gitlab: [
{
host: 'gitlab.com',
token: 'tokenlols',
apiBaseUrl: 'https://api.gitlab.com',
},
],
},
});
const customAuthorIntegrations =
ScmIntegrations.fromConfig(customAuthorConfig);
const customAuthorAction = createPublishGitlabAction({
integrations: customAuthorIntegrations,
config: customAuthorConfig,
});
mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 });
mockGitlabClient.Users.current.mockResolvedValue({ id: 12345 });
mockGitlabClient.Projects.create.mockResolvedValue({
id: 123456,
http_url_to_repo: 'http://mockurl.git',
});
await customAuthorAction.handler({
...mockContext,
input: {
repoUrl: 'gitlab.com?repo=repo&owner=owner',
token: 'token',
setUserAsOwner: true,
},
});
expect(mockGitlabClient.Namespaces.show).toHaveBeenCalledWith('owner');
expect(mockGitlabClient.Users.current).toHaveBeenCalled();
expect(mockGitlabClient.ProjectMembers.add).toHaveBeenCalledWith(
123456,
12345,
50,
);
expect(mockGitlabClient.Projects.create).toHaveBeenCalledWith({
namespace_id: 1234,
name: 'repo',
visibility: 'private',
});
});
});
@@ -43,6 +43,7 @@ export function createPublishGitlabAction(options: {
gitCommitMessage?: string;
gitAuthorName?: string;
gitAuthorEmail?: string;
setUserAsOwner?: boolean;
}>({
id: 'publish:gitlab',
description:
@@ -92,6 +93,12 @@ export function createPublishGitlabAction(options: {
type: 'string',
description: 'The token to use for authorization to GitLab',
},
setUserAsOwner: {
title: 'Set User As Owner',
type: 'boolean',
description:
'Set the token user as owner of the newly created repository. Requires a token authorized to do the edit in the integration configuration for the matching host',
},
},
},
output: {
@@ -116,6 +123,7 @@ export function createPublishGitlabAction(options: {
gitCommitMessage = 'initial commit',
gitAuthorName,
gitAuthorEmail,
setUserAsOwner = false,
} = ctx.input;
const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);
@@ -149,19 +157,35 @@ export function createPublishGitlabAction(options: {
id: number;
};
const { id: userId } = (await client.Users.current()) as {
id: number;
};
if (!targetNamespace) {
const { id } = (await client.Users.current()) as {
id: number;
};
targetNamespace = id;
targetNamespace = userId;
}
const { http_url_to_repo } = await client.Projects.create({
const { id: projectId, http_url_to_repo } = await client.Projects.create({
namespace_id: targetNamespace,
name: repo,
visibility: repoVisibility,
});
// When setUserAsOwner is true the input token is expected to come from an unprivileged user GitLab
// OAuth flow. In this case GitLab works in a way that allows the unprivileged user to
// create the repository, but not to push the default protected branch (e.g. master).
// In order to set the user as owner of the newly created repository we need to check that the
// GitLab integration configuration for the matching host contains a token and use
// such token to bootstrap a new privileged client.
if (setUserAsOwner && integrationConfig.config.token) {
const adminClient = new Gitlab({
host: integrationConfig.config.baseUrl,
token: integrationConfig.config.token,
});
await adminClient.ProjectMembers.add(projectId, userId, 50);
}
const remoteUrl = (http_url_to_repo as string).replace(/\.git$/, '');
const repoContentsUrl = `${remoteUrl}/-/blob/${defaultBranch}`;
+1 -1
View File
@@ -55,7 +55,7 @@
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"@react-hookz/web": "^13.0.0",
"@react-hookz/web": "^15.0.0",
"@rjsf/core": "^3.2.1",
"@rjsf/material-ui": "^3.2.1",
"@types/json-schema": "^7.0.9",
+11 -3
View File
@@ -90,9 +90,17 @@ export class MyOwnClient implements TechRadarApi {
const data = await fetch('https://mydata.json').then(res => res.json());
// maybe you'll need to do some data transformation here to make it look like TechRadarLoaderResponse
return data;
// For example, this converts the timeline dates into date objects
return {
...data,
entries: data.entries.map(entry => ({
...entry,
timeline: entry.timeline.map(timeline => ({
...timeline,
date: new Date(timeline.date),
})),
})),
};
}
}
@@ -43,7 +43,7 @@
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"@react-hookz/web": "^14.0.0",
"@react-hookz/web": "^15.0.0",
"git-url-parse": "^12.0.0",
"react-use": "^17.2.4"
},
-2
View File
@@ -219,8 +219,6 @@ const ALLOW_WARNINGS = [
'plugins/circleci',
'plugins/cloudbuild',
'plugins/code-climate',
'plugins/code-coverage',
'plugins/code-coverage-backend',
'plugins/config-schema',
'plugins/cost-insights',
'plugins/dynatrace',
+70 -240
View File
@@ -344,17 +344,17 @@
dependencies:
debug "^4.1.1"
"@azure/msal-common@^7.0.0":
version "7.0.0"
resolved "https://registry.npmjs.org/@azure/msal-common/-/msal-common-7.0.0.tgz#f4b52c6d9591cf8720dcb24c1d21fce2d186f871"
integrity sha512-EkaHGjv0kw1RljhboeffM91b+v9d5VtmyG+0a/gvdqjbLu3kDzEfoaS5BNM9QqMzbxgZylsjAjQDtxdHLX/ziA==
"@azure/msal-common@^7.1.0":
version "7.1.0"
resolved "https://registry.npmjs.org/@azure/msal-common/-/msal-common-7.1.0.tgz#b77dbf9ae581f1ed254f81d56422e3cdd6664b32"
integrity sha512-WyfqE5mY/rggjqvq0Q5DxLnA33KSb0vfsUjxa95rycFknI03L5GPYI4HTU9D+g0PL5TtsQGnV3xzAGq9BFCVJQ==
"@azure/msal-node@^1.1.0", "@azure/msal-node@^1.3.0":
version "1.10.0"
resolved "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.10.0.tgz#ee3a26201c4899cc6928cc331c31d3568d3cb728"
integrity sha512-oSv9mg199FpRTe+fZ3o9NDYpKShOHqeceaNcCHJcKUaAaCojAbfbxD1Cvsti8BEsLKE6x0HcnjilnM1MKmZekA==
version "1.11.0"
resolved "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.11.0.tgz#d8bd3f15c1f05bf806ba6f9479c48c2eddd6a98d"
integrity sha512-KW/XEexfCrPzdYbjY7NVmhq9okZT3Jvck55CGXpz9W5asxeq3EtrP45p+ZXtQVEfko0YJdolpCNqWUyXvanWZg==
dependencies:
"@azure/msal-common" "^7.0.0"
"@azure/msal-common" "^7.1.0"
jsonwebtoken "^8.5.1"
uuid "^8.3.0"
@@ -2288,9 +2288,9 @@
xcase "^2.0.1"
"@google-cloud/container@^4.0.0":
version "4.0.1"
resolved "https://registry.npmjs.org/@google-cloud/container/-/container-4.0.1.tgz#a3206d3030539e09b7beee8f351fad86c77af79d"
integrity sha512-JpsOsdKnCQm1b5aVNnO/2Urdg+qbCLi/srEnTupRj73Pu5wXLHXOCe3vy8Z2GvyCqu7qr89fMREQ4kO4QijnHA==
version "4.1.0"
resolved "https://registry.npmjs.org/@google-cloud/container/-/container-4.1.0.tgz#cf929184f668fc40ca8b864b7f638228ae67fc98"
integrity sha512-LpVT3wxG73iX/5deF4a2KIEZ7sULF2mksrzyA25OVGtyxFclSZT2Gs5uho5USSLkoKg/Ou+l+ekGMrgBQbLDag==
dependencies:
google-gax "^3.0.1"
@@ -3528,10 +3528,10 @@
dependencies:
ioredis "^5.1.0"
"@kubernetes/client-node@^0.16.0":
version "0.16.3"
resolved "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.16.3.tgz#a26a5abbd6e45603b4f75f0baff00e19853e5be7"
integrity sha512-L7IckuyuPfhd+/Urib8MRas9D6sfKEq8IaITYcaE6LlU+Y8MeD7MTbuW6Yb2WdeRuFN8HPSS47mxPnOUNYBXEg==
"@kubernetes/client-node@^0.17.0":
version "0.17.0"
resolved "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.17.0.tgz#cbf69ff6c8a311c1461275169612ac46db00fe3f"
integrity sha512-oKQfRf7RPpJIF2Ft92g6jefbW3Mddf6IzcfpBNDWrAy66LmyAWds6fQTMsdutGPuXV6KD29u6RfM3rdzddGMIA==
dependencies:
"@types/js-yaml" "^4.0.1"
"@types/node" "^10.12.0"
@@ -3545,7 +3545,7 @@
isomorphic-ws "^4.0.1"
js-yaml "^4.1.0"
jsonpath-plus "^0.19.0"
openid-client "^4.1.1"
openid-client "^5.1.6"
request "^2.88.0"
rfc4648 "^1.3.0"
shelljs "^0.8.5"
@@ -4438,9 +4438,9 @@
react-is "^16.8.0 || ^17.0.0"
"@maxim_mazurok/gapi.client.calendar@^3.0.20220408":
version "3.0.20220624"
resolved "https://registry.npmjs.org/@maxim_mazurok/gapi.client.calendar/-/gapi.client.calendar-3.0.20220624.tgz#17817142e348ce811415dfb111299fbdac2450a0"
integrity sha512-5+x4A6l8GY+dojvXUBQGc0Y4JOm9lBY1YZfCAEEZPjBcXkx0vs/CenPnmAu/fwQgGSrQLipxFV+pS6RL3L28mg==
version "3.0.20220701"
resolved "https://registry.npmjs.org/@maxim_mazurok/gapi.client.calendar/-/gapi.client.calendar-3.0.20220701.tgz#4ee9089e8164269743e61ec36e79e095aa0926b6"
integrity sha512-3CjbVZysFdUmGWZwjYGsV3z+kViaVpiWvgR7/DX5nUtb1YWIaS0jNUCch1YVN9aDFaCWuI3UV/STveSPvUspgw==
dependencies:
"@types/gapi.client" "*"
@@ -5271,11 +5271,6 @@
resolved "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.4.tgz#a167e46c10d05a07ab299fc518793b0cff8f6924"
integrity sha512-BuJuXRSJNQ3QoKA6GWWDyuLpOUck+9hAXNMCnrloc1aWVoy6Xq6t9PUV08aBZ4Lutqq2LEHM486bpZqoViScog==
"@panva/asn1.js@^1.0.0":
version "1.0.0"
resolved "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6"
integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
version "1.1.2"
resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
@@ -5329,18 +5324,11 @@
resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=
"@react-hookz/deep-equal@^1.0.1", "@react-hookz/deep-equal@^1.0.2":
"@react-hookz/deep-equal@^1.0.2":
version "1.0.2"
resolved "https://registry.npmjs.org/@react-hookz/deep-equal/-/deep-equal-1.0.2.tgz#4e8bdeda027379dcf8b62a42e5f75f0351b11b35"
integrity sha512-cM5kPFb6EFH5q52WzRxfRX9+8g5kq78McWOYs6e1seo+nK6NpfLupT5uOCIJp37jU8ayd4Su8ni3HRFTN2C2kg==
"@react-hookz/web@^13.0.0":
version "13.3.0"
resolved "https://registry.npmjs.org/@react-hookz/web/-/web-13.3.0.tgz#257e31049e92a121912fe1e67bdd01dbec5a203b"
integrity sha512-KswgkmqBVVDo6UnBFfssrojmDisogxC4jGZmd976R8YHoS3zdQJxjqICpOBSRohbRyYhYS9Cprw7BuV/CZcMaw==
dependencies:
"@react-hookz/deep-equal" "^1.0.1"
"@react-hookz/web@^14.0.0":
version "14.7.1"
resolved "https://registry.npmjs.org/@react-hookz/web/-/web-14.7.1.tgz#5e39e6fc21331cc4ae95f36e8135ad763e6c29fb"
@@ -5348,6 +5336,13 @@
dependencies:
"@react-hookz/deep-equal" "^1.0.2"
"@react-hookz/web@^15.0.0":
version "15.0.1"
resolved "https://registry.npmjs.org/@react-hookz/web/-/web-15.0.1.tgz#a6e5460dd16e54ccc0b899e1eed4ae29e871060f"
integrity sha512-nvVLUsDFv3fpZcINoy3I4MeaX8+yoKU21m2Ey2g0VAVqOMp+0GBBC6OHkzT2lRa2fiPIVuJwlmboSKQt7segfQ==
dependencies:
"@react-hookz/deep-equal" "^1.0.2"
"@repeaterjs/repeater@^3.0.4":
version "3.0.4"
resolved "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca"
@@ -7496,9 +7491,9 @@
eslint-visitor-keys "^3.0.0"
"@uiw/react-codemirror@^4.9.3":
version "4.9.5"
resolved "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.9.5.tgz#0f66c09dfc355baef5a7020f206da8db90bae223"
integrity sha512-KHgP/PII9Gv4iEUzbdO95qpSSPy27iSHzxQ01mBPWC4UvzuA0eQY2h64gzi/ld68esbKMGYoevgOBPCRJwNN1A==
version "4.10.4"
resolved "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.10.4.tgz#f26833d5cfc4c63de1858c587218efd9f85f18e2"
integrity sha512-eMl/Xtmhhjo1Hmx0P1NgwVlviOUCdjSYPKHn0bgUG6aLgZjCo1l0MJDDuLikGp8YlDrXcDKgiuXPt4GykBaUmg==
dependencies:
"@babel/runtime" ">=7.11.0"
"@codemirror/theme-one-dark" "^6.0.0"
@@ -7853,13 +7848,6 @@ anafanafo@2.0.0:
dependencies:
char-width-table-consumer "^1.0.0"
ansi-align@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb"
integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==
dependencies:
string-width "^3.0.0"
ansi-colors@^4.1.1, ansi-colors@^4.1.3:
version "4.1.3"
resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
@@ -8423,9 +8411,9 @@ aws-sdk-mock@^5.2.1:
traverse "^0.6.6"
aws-sdk@^2.1122.0, aws-sdk@^2.840.0, aws-sdk@^2.948.0:
version "2.1167.0"
resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1167.0.tgz#e8614bce1a7355782734b6928792f14d11b39231"
integrity sha512-hUJzAqWVfNYpct1S+GjyPIc2s+GZcAhbWVqIG4qbLYZ3+sBTcjv3lLH5zx7K+qcTGINDU0g4EsMi6hIrAU+blg==
version "2.1168.0"
resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1168.0.tgz#7936459edee0c999065f1ecf2ef0bea89ea056c1"
integrity sha512-9+WYoYTHHjLqeWdSSLbNpmc/NgnZpX4LGiyYjXenh4WNRBXshXI0XioTK8BQgDscVzB978EJV8kG1nZGE3USzw==
dependencies:
buffer "4.9.2"
events "1.1.1"
@@ -8946,20 +8934,6 @@ bottleneck@^2.15.3:
resolved "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91"
integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==
boxen@^5.0.0:
version "5.1.2"
resolved "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50"
integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==
dependencies:
ansi-align "^3.0.0"
camelcase "^6.2.0"
chalk "^4.1.0"
cli-boxes "^2.2.1"
string-width "^4.2.2"
type-fest "^0.20.2"
widest-line "^3.1.0"
wrap-ansi "^7.0.0"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -9678,11 +9652,6 @@ clean-stack@^2.0.0:
resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
cli-boxes@^2.2.1:
version "2.2.1"
resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
cli-cursor@^2.0.0, cli-cursor@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
@@ -10231,18 +10200,6 @@ config-chain@^1.1.12:
ini "^1.3.4"
proto-list "~1.2.1"
configstore@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96"
integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==
dependencies:
dot-prop "^5.2.0"
graceful-fs "^4.1.2"
make-dir "^3.0.0"
unique-string "^2.0.0"
write-file-atomic "^3.0.0"
xdg-basedir "^4.0.0"
connect-history-api-fallback@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8"
@@ -10674,11 +10631,6 @@ crypto-browserify@^3.11.0:
randombytes "^2.0.0"
randomfill "^1.0.3"
crypto-random-string@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
css-box-model@^1.2.0:
version "1.2.1"
resolved "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1"
@@ -11779,7 +11731,7 @@ dot-case@^3.0.4:
no-case "^3.0.4"
tslib "^2.0.3"
dot-prop@^5.1.0, dot-prop@^5.2.0:
dot-prop@^5.1.0:
version "5.3.0"
resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==
@@ -11922,11 +11874,6 @@ emittery@^0.8.1:
resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860"
integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==
emoji-regex@^7.0.1:
version "7.0.3"
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
@@ -12299,11 +12246,6 @@ escalade@^3.1.1:
resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
escape-goat@^2.0.0:
version "2.1.1"
resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==
escape-html@^1.0.3, escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@@ -14199,23 +14141,6 @@ got@11.8.3:
p-cancelable "^2.0.0"
responselike "^2.0.0"
got@^11.8.0:
version "11.8.5"
resolved "https://registry.npmjs.org/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046"
integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==
dependencies:
"@sindresorhus/is" "^4.0.0"
"@szmarczak/http-timer" "^4.0.5"
"@types/cacheable-request" "^6.0.1"
"@types/responselike" "^1.0.0"
cacheable-lookup "^5.0.3"
cacheable-request "^7.0.2"
decompress-response "^6.0.0"
http2-wrapper "^1.0.0-beta.5.2"
lowercase-keys "^2.0.0"
p-cancelable "^2.0.0"
responselike "^2.0.0"
got@^9.6.0:
version "9.6.0"
resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
@@ -14517,11 +14442,6 @@ has-values@^1.0.0:
is-number "^3.0.0"
kind-of "^4.0.0"
has-yarn@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77"
integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==
has@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
@@ -15027,11 +14947,6 @@ import-from@^3.0.0:
dependencies:
resolve-from "^5.0.0"
import-lazy@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
import-lazy@~4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153"
@@ -15553,7 +15468,7 @@ is-in-browser@^1.0.2, is-in-browser@^1.1.3:
resolved "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=
is-installed-globally@^0.4.0, is-installed-globally@~0.4.0:
is-installed-globally@~0.4.0:
version "0.4.0"
resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520"
integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==
@@ -15593,11 +15508,6 @@ is-node-process@^1.0.1:
resolved "https://registry.npmjs.org/is-node-process/-/is-node-process-1.0.1.tgz#4fc7ac3a91e8aac58175fe0578abbc56f2831b23"
integrity sha512-5IcdXuf++TTNt3oGl9EBdkvndXA8gmc4bz/Y+mdEpWh3Mcn/+kOw6hI7LD5CocqJWMzeb0I0ClndRVNdEPuJXQ==
is-npm@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8"
integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==
is-number-object@^1.0.4:
version "1.0.7"
resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc"
@@ -15880,11 +15790,6 @@ is-wsl@^2.2.0:
dependencies:
is-docker "^2.0.0"
is-yarn-global@^0.3.0:
version "0.3.0"
resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232"
integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -16543,13 +16448,6 @@ joi@^17.4.0:
"@sideway/formula" "^3.0.0"
"@sideway/pinpoint" "^2.0.0"
jose@^2.0.5:
version "2.0.5"
resolved "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz#29746a18d9fff7dcf9d5d2a6f62cb0c7cd27abd3"
integrity sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==
dependencies:
"@panva/asn1.js" "^1.0.0"
jose@^4.1.4:
version "4.5.0"
resolved "https://registry.npmjs.org/jose/-/jose-4.5.0.tgz#92829d8cf846351eb55aaaf94f252fb1d191f2d5"
@@ -17196,7 +17094,7 @@ language-tags@^1.0.5:
dependencies:
language-subtag-registry "~0.3.2"
latest-version@5.1.0, latest-version@^5.1.0:
latest-version@5.1.0:
version "5.1.0"
resolved "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face"
integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==
@@ -17802,9 +17700,9 @@ log-update@^4.0.0:
wrap-ansi "^6.2.0"
logform@^2.3.2, logform@^2.4.0:
version "2.4.1"
resolved "https://registry.npmjs.org/logform/-/logform-2.4.1.tgz#512c9eaef738044d1c619790ba0f806c80d9d3a9"
integrity sha512-7XB/tqc3VRbri9pRjU6E97mQ8vC27ivJ3lct4jhyT+n0JNDd4YKldFl0D75NqDp46hk8RC7Ma1Vjv/UPf67S+A==
version "2.4.2"
resolved "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz#a617983ac0334d0c3b942c34945380062795b47c"
integrity sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==
dependencies:
"@colors/colors" "1.5.0"
fecha "^4.2.0"
@@ -17952,7 +17850,7 @@ make-dir@^3.0.0, make-dir@^3.1.0:
dependencies:
semver "^6.0.0"
make-error@^1, make-error@^1.1.1, make-error@^1.3.6:
make-error@^1, make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
@@ -19342,9 +19240,9 @@ node-releases@^2.0.1:
integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==
nodemon@^2.0.2:
version "2.0.18"
resolved "https://registry.npmjs.org/nodemon/-/nodemon-2.0.18.tgz#0f5a3aa7b4587f2626e6f01369deba89cb0462a2"
integrity sha512-uAvrKipi2zAz8E7nkSz4qW4F4zd5fs2wNGsTx+xXlP8KXqd9ucE0vY9wankOsPboeDyuUGN9vsXGV1pLn80l/A==
version "2.0.19"
resolved "https://registry.npmjs.org/nodemon/-/nodemon-2.0.19.tgz#cac175f74b9cb8b57e770d47841995eebe4488bd"
integrity sha512-4pv1f2bMDj0Eeg/MhGqxrtveeQ5/G/UVe9iO6uTZzjnRluSA4PVWf8CW99LUPwGB3eNIA7zUFoP77YuI7hOc0A==
dependencies:
chokidar "^3.5.2"
debug "^3.2.7"
@@ -19352,10 +19250,10 @@ nodemon@^2.0.2:
minimatch "^3.0.4"
pstree.remy "^1.1.8"
semver "^5.7.1"
simple-update-notifier "^1.0.7"
supports-color "^5.5.0"
touch "^3.1.0"
undefsafe "^2.0.5"
update-notifier "^5.1.0"
nopt@1.0.10, nopt@~1.0.10:
version "1.0.10"
@@ -19873,20 +19771,7 @@ openapi-types@^12.0.0:
resolved "https://registry.npmjs.org/openapi-types/-/openapi-types-12.0.0.tgz#458a99d048f9eae1c067e15d56a8bfb3726041f1"
integrity sha512-6Wd9k8nmGQHgCbehZCP6wwWcfXcvinhybUTBatuhjRsCxUIujuYFZc9QnGeae75CyHASewBtxs0HX/qwREReUw==
openid-client@^4.1.1:
version "4.9.0"
resolved "https://registry.npmjs.org/openid-client/-/openid-client-4.9.0.tgz#bdfc9194435316df419f759ce177635146b43074"
integrity sha512-ThBbvRUUZwxUKBVK2UpDNIZ3eJkvtqWI8s5Dm+naV+gJdL+yRhT+8ywqct1gy5uL+xVS5+A/nhFcpJIisH2x6Q==
dependencies:
aggregate-error "^3.1.0"
got "^11.8.0"
jose "^2.0.5"
lru-cache "^6.0.0"
make-error "^1.3.6"
object-hash "^2.0.1"
oidc-token-hash "^5.0.1"
openid-client@^5.1.3:
openid-client@^5.1.3, openid-client@^5.1.6:
version "5.1.8"
resolved "https://registry.npmjs.org/openid-client/-/openid-client-5.1.8.tgz#3a24910288b32c32f548fb6e391f44178ce6370f"
integrity sha512-EPxJY6bT7YIYQEXSGxRC5flQ3GUhLy98ufdto6+BVBrFGPmwjUpy4xBcYuU/Wt9nPkO/3EgljBrr6Ezx4lp1RQ==
@@ -20535,10 +20420,10 @@ path-case@^3.0.4:
dot-case "^3.0.4"
tslib "^2.0.3"
path-equal@1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/path-equal/-/path-equal-1.1.2.tgz#260e7c449c4c2022f68cc5fa6e617e892858250d"
integrity sha512-p5kxPPwCdbf5AdXzT1bUBJomhgBlEjRBavYNr1XUpMFIE4Hnf2roueCMXudZK5tnaAu1tTmp3GPzqwJK45IHEA==
path-equal@^1.1.2:
version "1.2.2"
resolved "https://registry.npmjs.org/path-equal/-/path-equal-1.2.2.tgz#fa2997f0a829de22ec8f5f86461ca5590d49b832"
integrity sha512-AUJvbcle1Zgb1TgtftHYknlrgrSYyI1ytrYgSbKUHSybwqUDnbD2cw9PIWivuMvsN+GTXmr/DRN4VBXpHG6aGg==
path-exists@^2.0.0:
version "2.1.0"
@@ -21581,17 +21466,10 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
pupa@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62"
integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==
dependencies:
escape-goat "^2.0.0"
puppeteer@^15.0.0:
version "15.2.0"
resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-15.2.0.tgz#9cd81334f9c6a2e1c972b5a7ecf3f18ab3bfb978"
integrity sha512-6Mzj5pbq4J4DxJE5o6V+arrOB9Gma0CxOLP1zKYMrMR7AYuNaPzsK7pBrpDwI64W6Mxk5G7NqiLSFTrgSzR1zg==
version "15.3.1"
resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-15.3.1.tgz#0ff9b433a8fc3798f5ec82ea4b31ec47857219cf"
integrity sha512-Z+SpYBiS1zUzMXV7Wnhe2pyuVCFAFRTq1UrUWHB2CkLos5v7bXvXYuZ3Fn5pSN5IObxijyx4opNYKTCRnGni6Q==
dependencies:
cross-fetch "3.1.5"
debug "4.3.4"
@@ -23227,13 +23105,6 @@ semver-compare@^1.0.0:
resolved "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
semver-diff@^3.1.1:
version "3.1.1"
resolved "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b"
integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==
dependencies:
semver "^6.3.0"
semver-store@^0.3.0:
version "0.3.0"
resolved "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz#ce602ff07df37080ec9f4fb40b29576547befbe9"
@@ -23244,7 +23115,7 @@ semver-store@^0.3.0:
resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
semver@7.0.0:
semver@7.0.0, semver@~7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
@@ -23515,6 +23386,13 @@ simple-swizzle@^0.2.2:
dependencies:
is-arrayish "^0.3.1"
simple-update-notifier@^1.0.7:
version "1.0.7"
resolved "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz#7edf75c5bdd04f88828d632f762b2bc32996a9cc"
integrity sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==
dependencies:
semver "~7.0.0"
sinon@^13.0.2:
version "13.0.2"
resolved "https://registry.npmjs.org/sinon/-/sinon-13.0.2.tgz#c6a8ddd655dc1415bbdc5ebf0e5b287806850c3a"
@@ -24186,7 +24064,7 @@ string-width@^1.0.1, string-width@^1.0.2:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -24203,15 +24081,6 @@ string-width@^2.1.1:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string-width@^3.0.0:
version "3.1.0"
resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
dependencies:
emoji-regex "^7.0.1"
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
string-width@^5.0.0:
version "5.1.2"
resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@@ -24267,7 +24136,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
strip-ansi@5.2.0, strip-ansi@^5.1.0:
strip-ansi@5.2.0:
version "5.2.0"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
@@ -25349,15 +25218,15 @@ typedarray@^0.0.6:
resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript-json-schema@^0.53.0:
version "0.53.1"
resolved "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.53.1.tgz#9204547f3e145169b40928998366ff6d28b81d32"
integrity sha512-Hg+RnOKUd38MOzC0rDft03a8xvwO+gCcj1F77smw2tCoZYQpFoLtrXWBGdvCX+REliko5WYel2kux17HPFqjLQ==
typescript-json-schema@^0.54.0:
version "0.54.0"
resolved "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.54.0.tgz#b3fc42ad90df6a0f6ab57571ebc8b4d41125df4f"
integrity sha512-/MNhm1pjdxXiVspjjyRCrQAA1B768cRzHU83aIqN5vQqQEW2NgyyKOfcguiRIMM64lseIZIelegnHOHEu7YDCg==
dependencies:
"@types/json-schema" "^7.0.9"
"@types/node" "^16.9.2"
glob "^7.1.7"
path-equal "1.1.2"
path-equal "^1.1.2"
safe-stable-stringify "^2.2.0"
ts-node "^10.2.1"
typescript "~4.6.0"
@@ -25510,13 +25379,6 @@ unique-slug@^2.0.0:
dependencies:
imurmurhash "^0.1.4"
unique-string@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d"
integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==
dependencies:
crypto-random-string "^2.0.0"
unist-builder@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/unist-builder/-/unist-builder-3.0.0.tgz#728baca4767c0e784e1e64bb44b5a5a753021a04"
@@ -25664,26 +25526,6 @@ upath@^2.0.1:
resolved "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b"
integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==
update-notifier@^5.1.0:
version "5.1.0"
resolved "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9"
integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==
dependencies:
boxen "^5.0.0"
chalk "^4.1.0"
configstore "^5.0.1"
has-yarn "^2.1.0"
import-lazy "^2.1.0"
is-ci "^2.0.0"
is-installed-globally "^0.4.0"
is-npm "^5.0.0"
is-yarn-global "^0.3.0"
latest-version "^5.1.0"
pupa "^2.1.1"
semver "^7.3.4"
semver-diff "^3.1.1"
xdg-basedir "^4.0.0"
upper-case-first@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324"
@@ -25995,9 +25837,9 @@ vm-browserify@^1.0.1:
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
vm2@^3.9.6:
version "3.9.9"
resolved "https://registry.npmjs.org/vm2/-/vm2-3.9.9.tgz#c0507bc5fbb99388fad837d228badaaeb499ddc5"
integrity sha512-xwTm7NLh/uOjARRBs8/95H0e8fT3Ukw5D/JJWhxMbhKzNh1Nu981jQKvkep9iKYNxzlVrdzD0mlBGkDKZWprlw==
version "3.9.10"
resolved "https://registry.npmjs.org/vm2/-/vm2-3.9.10.tgz#c66543096b5c44c8861a6465805c23c7cc996a44"
integrity sha512-AuECTSvwu2OHLAZYhG716YzwodKCIJxB6u1zG7PgSQwIgAlEaoXH52bxdcvT8GkGjnYK7r7yWDW0m0sOsPuBjQ==
dependencies:
acorn "^8.7.0"
acorn-walk "^8.2.0"
@@ -26311,13 +26153,6 @@ wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5:
dependencies:
string-width "^1.0.2 || 2 || 3 || 4"
widest-line@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"
integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==
dependencies:
string-width "^4.0.0"
window-size@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
@@ -26489,11 +26324,6 @@ xcase@^2.0.1:
resolved "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9"
integrity sha1-x/pyyqD0QNt4/VZzQyA4rJhEULk=
xdg-basedir@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
xhr@^2.0.1:
version "2.6.0"
resolved "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d"