From 24c509b9e2df5a6382929fa93824e91432a833a4 Mon Sep 17 00:00:00 2001 From: ebarrios Date: Tue, 14 Jul 2020 07:16:20 +0200 Subject: [PATCH 001/359] Created gcp-projects plugin --- .github/CODEOWNERS | 4 +- package.json | 3 + packages/app/package.json | 1 + packages/app/src/apis.ts | 2 + packages/app/src/plugins.ts | 1 + plugins/gcp-projects/.eslintrc.js | 3 + plugins/gcp-projects/README.md | 13 ++ plugins/gcp-projects/dev/index.tsx | 22 +++ plugins/gcp-projects/package.json | 47 ++++++ plugins/gcp-projects/src/api/GCPApi.ts | 34 ++++ plugins/gcp-projects/src/api/GCPClient.ts | 125 ++++++++++++++ plugins/gcp-projects/src/api/index.ts | 19 +++ plugins/gcp-projects/src/api/types.ts | 41 +++++ .../NewProjectPage/NewProjectPage.tsx | 119 ++++++++++++++ .../src/components/NewProjectPage/index.ts | 17 ++ .../ProjectDetailsPage/ProjectDetailsPage.tsx | 153 +++++++++++++++++ .../components/ProjectDetailsPage/index.ts | 17 ++ .../ProjectListPage/ProjectListPage.tsx | 155 ++++++++++++++++++ .../src/components/ProjectListPage/index.ts | 17 ++ plugins/gcp-projects/src/index.ts | 18 ++ plugins/gcp-projects/src/plugin.test.ts | 23 +++ plugins/gcp-projects/src/plugin.ts | 42 +++++ plugins/gcp-projects/src/setupTests.ts | 19 +++ yarn.lock | 83 +++++++++- 24 files changed, 970 insertions(+), 8 deletions(-) create mode 100644 plugins/gcp-projects/.eslintrc.js create mode 100644 plugins/gcp-projects/README.md create mode 100644 plugins/gcp-projects/dev/index.tsx create mode 100644 plugins/gcp-projects/package.json create mode 100644 plugins/gcp-projects/src/api/GCPApi.ts create mode 100644 plugins/gcp-projects/src/api/GCPClient.ts create mode 100644 plugins/gcp-projects/src/api/index.ts create mode 100644 plugins/gcp-projects/src/api/types.ts create mode 100644 plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx create mode 100644 plugins/gcp-projects/src/components/NewProjectPage/index.ts create mode 100644 plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx create mode 100644 plugins/gcp-projects/src/components/ProjectDetailsPage/index.ts create mode 100644 plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx create mode 100644 plugins/gcp-projects/src/components/ProjectListPage/index.ts create mode 100644 plugins/gcp-projects/src/index.ts create mode 100644 plugins/gcp-projects/src/plugin.test.ts create mode 100644 plugins/gcp-projects/src/plugin.ts create mode 100644 plugins/gcp-projects/src/setupTests.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 751ec4914d..7d941588c7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,6 +4,6 @@ # The last matching pattern takes precedence. # https://help.github.com/articles/about-codeowners/ -* @spotify/backstage-core -/plugins/techdocs @spotify/techdocs-core +* @spotify/backstage-core /packages/techdocs-cli @spotify/techdocs-core +/plugins/techdocs @spotify/techdocs-core diff --git a/package.json b/package.json index cc45d541d4..125dc285aa 100644 --- a/package.json +++ b/package.json @@ -53,5 +53,8 @@ "*.{json,md}": [ "prettier --write" ] + }, + "dependencies": { + "ts-node": "^8.10.2" } } diff --git a/packages/app/package.json b/packages/app/package.json index ed256e6c2d..88e2e630a7 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -8,6 +8,7 @@ "@backstage/plugin-catalog": "^0.1.1-alpha.13", "@backstage/plugin-circleci": "^0.1.1-alpha.13", "@backstage/plugin-explore": "^0.1.1-alpha.13", + "@backstage/plugin-gcp-projects": "^0.1.1-alpha.13", "@backstage/plugin-github-actions": "^0.1.1-alpha.13", "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.13", "@backstage/plugin-graphiql": "^0.1.1-alpha.13", diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index abc490b658..f99a88b345 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -58,6 +58,7 @@ import { import { scaffolderApiRef, ScaffolderApi } from '@backstage/plugin-scaffolder'; import { rollbarApiRef, RollbarClient } from '@backstage/plugin-rollbar'; +import { GCPClient, GCPApiRef } from '@backstage/plugin-gcp-projects'; export const apis = (config: ConfigApi) => { // eslint-disable-next-line no-console @@ -75,6 +76,7 @@ export const apis = (config: ConfigApi) => { builder.add(storageApiRef, WebStorage.create({ errorApi })); builder.add(circleCIApiRef, new CircleCIApi()); + builder.add(GCPApiRef, new GCPClient()); builder.add(featureFlagsApiRef, new FeatureFlags()); builder.add(lighthouseApiRef, new LighthouseRestApi('http://localhost:3003')); diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index 46c8f23ef0..19a359043b 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -27,3 +27,4 @@ export { plugin as TechDocs } from '@backstage/plugin-techdocs'; export { plugin as GraphiQL } from '@backstage/plugin-graphiql'; export { plugin as GithubActions } from '@backstage/plugin-github-actions'; export { plugin as Rollbar } from '@backstage/plugin-rollbar'; +export { plugin as GcpProjects } from '@backstage/plugin-gcp-projects'; diff --git a/plugins/gcp-projects/.eslintrc.js b/plugins/gcp-projects/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/gcp-projects/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/gcp-projects/README.md b/plugins/gcp-projects/README.md new file mode 100644 index 0000000000..6a2ab63c96 --- /dev/null +++ b/plugins/gcp-projects/README.md @@ -0,0 +1,13 @@ +# gcp-projects + +Welcome to the gcp-projects plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/gcp-projects](http://localhost:3000/gcp-projects). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](/dev) directory. diff --git a/plugins/gcp-projects/dev/index.tsx b/plugins/gcp-projects/dev/index.tsx new file mode 100644 index 0000000000..d97643057b --- /dev/null +++ b/plugins/gcp-projects/dev/index.tsx @@ -0,0 +1,22 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { createDevApp } from '@backstage/dev-utils'; +import { plugin } from '../src/plugin'; + +createDevApp() + .registerPlugin(plugin) + .render(); diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json new file mode 100644 index 0000000000..2bef7feef4 --- /dev/null +++ b/plugins/gcp-projects/package.json @@ -0,0 +1,47 @@ +{ + "name": "@backstage/plugin-gcp-projects", + "version": "0.1.1-alpha.13", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": true, + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "build": "backstage-cli plugin:build", + "start": "backstage-cli plugin:serve", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "diff": "backstage-cli plugin:diff", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/core": "^0.1.1-alpha.13", + "@backstage/theme": "^0.1.1-alpha.13", + "@material-ui/core": "^4.9.1", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.45", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "react-router-dom": "^5.2.0", + "react-use": "^14.2.0" + }, + "devDependencies": { + "@backstage/cli": "^0.1.1-alpha.13", + "@backstage/dev-utils": "^0.1.1-alpha.13", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^10.4.1", + "@testing-library/user-event": "^12.0.7", + "@types/jest": "^25.2.2", + "@types/node": "^12.0.0", + "jest-fetch-mock": "^3.0.3" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/gcp-projects/src/api/GCPApi.ts b/plugins/gcp-projects/src/api/GCPApi.ts new file mode 100644 index 0000000000..95b686815e --- /dev/null +++ b/plugins/gcp-projects/src/api/GCPApi.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { createApiRef } from '@backstage/core'; +import { Project, Operation } from './types'; + +export const GCPApiRef = createApiRef({ + id: 'plugin.gcpprojects.service', + description: 'Used by the GCP Projects plugin to make requests', +}); + +export type GCPApi = { + listProjects: ({ token }: { token: string }) => Promise; + getProject: (projectId: string, token: Promise) => Promise; + createProject: ( + projectName: string, + projectId: string, + owner: string, + token: string, + ) => Promise; +}; diff --git a/plugins/gcp-projects/src/api/GCPClient.ts b/plugins/gcp-projects/src/api/GCPClient.ts new file mode 100644 index 0000000000..fd8b708377 --- /dev/null +++ b/plugins/gcp-projects/src/api/GCPClient.ts @@ -0,0 +1,125 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { GCPApi } from './GCPApi'; +import { Project, Operation, Status } from './types'; + +export class GCPClient implements GCPApi { + async listProjects({ token }: { token: string }): Promise { + const url = `https://content-cloudresourcemanager.googleapis.com/v1/projects`; + + const response = await fetch(url, { + headers: new Headers({ + Accept: '*/*', + Authorization: `Bearer ${token}`, + }), + }); + + if (!response.ok) { + return [ + { + name: 'Error', + projectNumber: 'Response status is not OK', + projectId: 'Error', + lifecycleState: 'error', + createTime: 'Error', + }, + ]; + } + + const data = await response.json(); + + return data.projects; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async getProject( + projectId: string, + token: Promise, + ): Promise { + const url = `https://content-cloudresourcemanager.googleapis.com/v1/projects/${projectId}`; + const response = await fetch(url, { + headers: new Headers({ + Authorization: `Bearer ${await token}`, + }), + }); + + const dataBlank: Project = { + name: 'Error', + projectNumber: `Response status is${response.status}`, + projectId: 'Error', + lifecycleState: 'error', + createTime: 'Error', + }; + + if (!response.ok) { + return dataBlank; + } + + const data = await response.json(); + + const newData: Project = data; + + return newData; + } + + async createProject( + projectName: string, + projectId: string, + token: string, + ): Promise { + const status: Status = { + code: 0, + message: '', + details: [], + }; + + const op: Operation = { + name: '', + metadata: '', + done: true, + error: status, + response: '', + }; + + const newProject: Project = { + name: projectName, + projectId: projectId, + }; + + const body = JSON.stringify(newProject); + + const url = `https://content-cloudresourcemanager.googleapis.com/v1/projects`; + + const response = await fetch(url, { + headers: new Headers({ + Accept: '*/*', + Authorization: `Bearer ${token}`, + }), + body: body, + method: 'POST', + }); + + if (!response.ok) { + status.code = response.status; + return op; + } + + const data = await response.json(); + + return data; + } +} diff --git a/plugins/gcp-projects/src/api/index.ts b/plugins/gcp-projects/src/api/index.ts new file mode 100644 index 0000000000..51f617f19c --- /dev/null +++ b/plugins/gcp-projects/src/api/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './GCPApi'; +export * from './GCPClient'; +export * from './types'; diff --git a/plugins/gcp-projects/src/api/types.ts b/plugins/gcp-projects/src/api/types.ts new file mode 100644 index 0000000000..8dccaec5e2 --- /dev/null +++ b/plugins/gcp-projects/src/api/types.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 type Project = { + name: string; + projectNumber?: string; + projectId: string; + lifecycleState?: string; + createTime?: string; +}; + +export type ProjectDetails = { + details: string; +}; + +export type Operation = { + name: string; + metadata: string; + done: boolean; + error: Status; + response: string; +}; + +export type Status = { + code: number; + message: string; + details: string[]; +}; diff --git a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx new file mode 100644 index 0000000000..bfa2f158ce --- /dev/null +++ b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx @@ -0,0 +1,119 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React, { FC, useState } from 'react'; +import { Grid, Button, TextField } from '@material-ui/core'; + +import { + InfoCard, + Header, + Page, + pageTheme, + Content, + ContentHeader, + HeaderLabel, + SupportButton, + SimpleStepper, + SimpleStepperStep, + StructuredMetadataTable, +} from '@backstage/core'; + +export const NewProjectPage: FC<{}> = () => { + const [projectName, setProjectName] = useState(''); + const [projectId, setProjectId] = useState(''); + const [done, setDone] = useState(false); + + const metadata = { + ProjectName: projectName, + ProjectId: projectId, + }; + + return ( + +
+ + +
+ + + + This plugin will help you create a project in gcp. + + + + + + + + setProjectName(e.target.value)} + value={projectName} + fullWidth + /> + + + setProjectId(e.target.value)} + value={projectId} + fullWidth + /> + + + + + + + + {done === true ? ( + + + +
+
+
+ +
+
+ ) : ( +
+ )} +
+
+
+ ); +}; diff --git a/plugins/gcp-projects/src/components/NewProjectPage/index.ts b/plugins/gcp-projects/src/components/NewProjectPage/index.ts new file mode 100644 index 0000000000..1d2f023887 --- /dev/null +++ b/plugins/gcp-projects/src/components/NewProjectPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { NewProjectPage } from './NewProjectPage'; diff --git a/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx b/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx new file mode 100644 index 0000000000..d9721bf549 --- /dev/null +++ b/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx @@ -0,0 +1,153 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { + Button, + ButtonGroup, + LinearProgress, + makeStyles, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableRow, + Theme, + Typography, +} from '@material-ui/core'; +import React from 'react'; +import { useLocation } from 'react-router-dom'; +import { useAsync } from 'react-use'; +import { Link, useApi, googleAuthApiRef } from '@backstage/core'; +import { GCPApiRef } from '../../api'; + +const useStyles = makeStyles(theme => ({ + root: { + maxWidth: 720, + margin: theme.spacing(2), + }, + title: { + padding: theme.spacing(1, 0, 2, 0), + }, + table: { + padding: theme.spacing(1), + }, +})); + +export const ProjectDetailsPage = () => { + const api = useApi(GCPApiRef); + const googleApi = useApi(googleAuthApiRef); + const token = googleApi.getAccessToken( + 'https://www.googleapis.com/auth/cloud-platform.read-only', + ); + + const classes = useStyles(); + const location = useLocation(); + const status = useAsync( + () => + api.getProject( + decodeURIComponent(location.search.split('projectId=')[1]), + token, + ), + [location.search], + ); + + if (status.loading) { + return ; + } else if (status.error) { + return ( + + Failed to load build, {status.error.message} + + ); + } + + const details = status.value; + + return ( +
+ + + + < + + + Build Details + + + + + + + Name + + {details?.name} + + + + Project Number + + {details?.projectNumber} + + + + Project ID + + {details?.projectId} + + + + State + + + {details?.lifecycleState} + + + + + Creation Time + + {details?.createTime} + + + + Links + + + + {details?.name && ( + + )} + {details?.name && ( + + )} + + + + +
+
+
+ ); +}; diff --git a/plugins/gcp-projects/src/components/ProjectDetailsPage/index.ts b/plugins/gcp-projects/src/components/ProjectDetailsPage/index.ts new file mode 100644 index 0000000000..e9b6eb095a --- /dev/null +++ b/plugins/gcp-projects/src/components/ProjectDetailsPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ProjectDetailsPage } from './ProjectDetailsPage'; diff --git a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx new file mode 100644 index 0000000000..8be1b47857 --- /dev/null +++ b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx @@ -0,0 +1,155 @@ +/* + * Copyright 2020 Spotify AB + * + * 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. + */ + +// NEEDS WORK + +import { Link, useApi, googleAuthApiRef, InfoCard } from '@backstage/core'; +import { + LinearProgress, + makeStyles, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Theme, + Tooltip, + Typography, + Button, +} from '@material-ui/core'; +import React from 'react'; +import { useAsync } from 'react-use'; +import { GCPApiRef, Project } from '../../api'; + +const LongText = ({ text, max }: { text: string; max: number }) => { + if (text.length < max) { + return {text}; + } + return ( + + {text.slice(0, max)}... + + ); +}; + +const useStyles = makeStyles(theme => ({ + root: { + padding: theme.spacing(2), + }, + title: { + padding: theme.spacing(1, 0, 2, 0), + }, +})); + +const PageContents = () => { + const api = useApi(GCPApiRef); + const googleApi = useApi(googleAuthApiRef); + + const { loading, error, value } = useAsync(async () => { + const token = await googleApi.getAccessToken( + 'https://www.googleapis.com/auth/cloud-platform.read-only', + ); + + const projects = api.listProjects({ token }); + return projects; + }); + + if (loading) { + return ; + } + + if (error) { + return ( + + Failed to load projects, {error.message}{' '} + + ); + } + + return ( + + + + + Name + Project Number + Project ID + State + Creation Time + + + + {value?.map((project: Project) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ))} + +
+
+ ); +}; + +export const ProjectListPage = () => { + const classes = useStyles(); + + return ( +
+ + GCP Projects + + + + + + +
+ ); +}; diff --git a/plugins/gcp-projects/src/components/ProjectListPage/index.ts b/plugins/gcp-projects/src/components/ProjectListPage/index.ts new file mode 100644 index 0000000000..c2b0479cef --- /dev/null +++ b/plugins/gcp-projects/src/components/ProjectListPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ProjectListPage } from './ProjectListPage'; diff --git a/plugins/gcp-projects/src/index.ts b/plugins/gcp-projects/src/index.ts new file mode 100644 index 0000000000..d67bc6a864 --- /dev/null +++ b/plugins/gcp-projects/src/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { plugin } from './plugin'; +export * from './api'; diff --git a/plugins/gcp-projects/src/plugin.test.ts b/plugins/gcp-projects/src/plugin.test.ts new file mode 100644 index 0000000000..86e909995f --- /dev/null +++ b/plugins/gcp-projects/src/plugin.test.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { plugin } from './plugin'; + +describe('gcp-projects', () => { + it('should export plugin', () => { + expect(plugin).toBeDefined(); + }); +}); diff --git a/plugins/gcp-projects/src/plugin.ts b/plugins/gcp-projects/src/plugin.ts new file mode 100644 index 0000000000..29c7d74434 --- /dev/null +++ b/plugins/gcp-projects/src/plugin.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { createPlugin, createRouteRef } from '@backstage/core'; +import { ProjectListPage } from './components/ProjectListPage'; +import { ProjectDetailsPage } from './components/ProjectDetailsPage'; +import { NewProjectPage } from './components/NewProjectPage'; + +export const rootRouteRef = createRouteRef({ + path: '/gcp-projects', + title: 'GCP Projects', +}); +export const ProjectRouteRef = createRouteRef({ + path: '/gcp-projects/project', + title: 'GCP Project Page', +}); +export const NewProjectRouteRef = createRouteRef({ + path: '/gcp-projects/new', + title: 'GCP Project Page', +}); + +export const plugin = createPlugin({ + id: 'gcp-projects', + register({ router }) { + router.addRoute(rootRouteRef, ProjectListPage); + router.addRoute(ProjectRouteRef, ProjectDetailsPage); + router.addRoute(NewProjectRouteRef, NewProjectPage); + }, +}); diff --git a/plugins/gcp-projects/src/setupTests.ts b/plugins/gcp-projects/src/setupTests.ts new file mode 100644 index 0000000000..8553642152 --- /dev/null +++ b/plugins/gcp-projects/src/setupTests.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 '@testing-library/jest-dom'; + +require('jest-fetch-mock').enableMocks(); diff --git a/yarn.lock b/yarn.lock index 0fb5368314..7620f8832d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9942,6 +9942,18 @@ highlight.js@~9.15.0, highlight.js@~9.15.1: resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2" integrity sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw== +history@^4.9.0: + version "4.10.1" + resolved "https://registry.npmjs.org/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^3.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^1.0.1" + history@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/history/-/history-5.0.0.tgz#0cabbb6c4bbf835addb874f8259f6d25101efd08" @@ -9958,7 +9970,7 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -11090,6 +11102,11 @@ is-yarn-global@^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" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -12430,7 +12447,7 @@ loglevel@^1.6.8: resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -12889,6 +12906,14 @@ min-indent@^1.0.0: resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY= +mini-create-react-context@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz#df60501c83151db69e28eac0ef08b4002efab040" + integrity sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA== + dependencies: + "@babel/runtime" "^7.5.5" + tiny-warning "^1.0.3" + mini-css-extract-plugin@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz#5ba8290fbb4179a43dd27cca444ba150bee743a0" @@ -14369,6 +14394,13 @@ path-to-regexp@2.2.1: resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + path-type@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -15646,7 +15678,7 @@ react-inspector@^4.0.0: is-dom "^1.0.9" prop-types "^15.6.1" -react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -15725,6 +15757,35 @@ react-router-dom@6.0.0-beta.0: prop-types "^15.7.2" react-router "6.0.0-beta.0" +react-router-dom@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" + integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.2.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293" + integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + mini-create-react-context "^0.4.0" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + react-router@6.0.0-beta.0: version "6.0.0-beta.0" resolved "https://registry.npmjs.org/react-router/-/react-router-6.0.0-beta.0.tgz#3e11f39b6ded4412c2fed9e4f989dd4c8156724d" @@ -16342,6 +16403,11 @@ resolve-from@^5.0.0: resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve-pathname@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" + integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -18185,12 +18251,12 @@ tiny-emitter@^2.0.0: resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== -tiny-invariant@^1.0.6: +tiny-invariant@^1.0.2, tiny-invariant@^1.0.6: version "1.1.0" resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== -tiny-warning@^1.0.2: +tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== @@ -18406,7 +18472,7 @@ ts-loader@^7.0.4: micromatch "^4.0.0" semver "^6.0.0" -ts-node@^8.6.2: +ts-node@^8.10.2, ts-node@^8.6.2: version "8.10.2" resolved "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== @@ -18987,6 +19053,11 @@ validate.io-number@^1.0.3: resolved "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz#f63ffeda248bf28a67a8d48e0e3b461a1665baf8" integrity sha1-9j/+2iSL8opnqNSODjtGGhZluvg= +value-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" + integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" From 91dfb7d749dbbf43869098f17d59a8f1d7b9a28b Mon Sep 17 00:00:00 2001 From: ebarrios Date: Tue, 14 Jul 2020 07:19:04 +0200 Subject: [PATCH 002/359] Reverted order change in CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7d941588c7..1622f2dfcf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,5 +5,5 @@ # https://help.github.com/articles/about-codeowners/ * @spotify/backstage-core -/packages/techdocs-cli @spotify/techdocs-core /plugins/techdocs @spotify/techdocs-core +/packages/techdocs-cli @spotify/techdocs-core From e42c1ae02b7c368320aee28bd4e157a487312cef Mon Sep 17 00:00:00 2001 From: ebarrios Date: Tue, 14 Jul 2020 07:19:52 +0200 Subject: [PATCH 003/359] Removed space on CODEOWNERS file --- .github/CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1622f2dfcf..751ec4914d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,6 +4,6 @@ # The last matching pattern takes precedence. # https://help.github.com/articles/about-codeowners/ -* @spotify/backstage-core -/plugins/techdocs @spotify/techdocs-core +* @spotify/backstage-core +/plugins/techdocs @spotify/techdocs-core /packages/techdocs-cli @spotify/techdocs-core From 461f16250a348623257b55c6bdd91f4b9385e870 Mon Sep 17 00:00:00 2001 From: ebarrios Date: Tue, 14 Jul 2020 07:26:15 +0200 Subject: [PATCH 004/359] Removed trivago name on the title for creating a new GCP Project --- .../src/components/NewProjectPage/NewProjectPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx index bfa2f158ce..5588884a2a 100644 --- a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx +++ b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx @@ -48,7 +48,7 @@ export const NewProjectPage: FC<{}> = () => { - + This plugin will help you create a project in gcp. From 07128d499b4a47076693365ba890cb8bd43c2d96 Mon Sep 17 00:00:00 2001 From: ebarrios Date: Tue, 14 Jul 2020 07:40:08 +0200 Subject: [PATCH 005/359] Added or to not mandatory values in the project type for printing if they are not set on the response --- .../src/components/ProjectListPage/ProjectListPage.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx index 8be1b47857..00dac5c70e 100644 --- a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx +++ b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx @@ -103,7 +103,7 @@ const PageContents = () => { - + @@ -119,12 +119,15 @@ const PageContents = () => { - + - + From 8fbe0d32f4d2803f9105e3fb0af74ff1cbae9689 Mon Sep 17 00:00:00 2001 From: ebarrios Date: Tue, 14 Jul 2020 08:11:00 +0200 Subject: [PATCH 006/359] Added baseURL per recomendation from @stefanalund --- packages/backend/package.json | 2 +- plugins/gcp-projects/src/api/GCPClient.ts | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 2844f17744..9e42f6c4cb 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -18,7 +18,6 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/plugin-proxy-backend": "^0.1.1-alpha.13", "@backstage/backend-common": "^0.1.1-alpha.13", "@backstage/catalog-model": "^0.1.1-alpha.13", "@backstage/config": "^0.1.1-alpha.13", @@ -26,6 +25,7 @@ "@backstage/plugin-auth-backend": "^0.1.1-alpha.13", "@backstage/plugin-catalog-backend": "^0.1.1-alpha.13", "@backstage/plugin-identity-backend": "^0.1.1-alpha.13", + "@backstage/plugin-proxy-backend": "^0.1.1-alpha.13", "@backstage/plugin-rollbar-backend": "^0.1.1-alpha.13", "@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.13", "@backstage/plugin-sentry-backend": "^0.1.1-alpha.13", diff --git a/plugins/gcp-projects/src/api/GCPClient.ts b/plugins/gcp-projects/src/api/GCPClient.ts index fd8b708377..dc4fe21132 100644 --- a/plugins/gcp-projects/src/api/GCPClient.ts +++ b/plugins/gcp-projects/src/api/GCPClient.ts @@ -17,11 +17,12 @@ import { GCPApi } from './GCPApi'; import { Project, Operation, Status } from './types'; +const BaseURL = + 'https://content-cloudresourcemanager.googleapis.com/v1/projects'; + export class GCPClient implements GCPApi { async listProjects({ token }: { token: string }): Promise { - const url = `https://content-cloudresourcemanager.googleapis.com/v1/projects`; - - const response = await fetch(url, { + const response = await fetch(BaseURL, { headers: new Headers({ Accept: '*/*', Authorization: `Bearer ${token}`, @@ -50,7 +51,7 @@ export class GCPClient implements GCPApi { projectId: string, token: Promise, ): Promise { - const url = `https://content-cloudresourcemanager.googleapis.com/v1/projects/${projectId}`; + const url = `${BaseURL}/${projectId}`; const response = await fetch(url, { headers: new Headers({ Authorization: `Bearer ${await token}`, @@ -59,7 +60,7 @@ export class GCPClient implements GCPApi { const dataBlank: Project = { name: 'Error', - projectNumber: `Response status is${response.status}`, + projectNumber: `Response status is ${response.status}`, projectId: 'Error', lifecycleState: 'error', createTime: 'Error', @@ -102,9 +103,7 @@ export class GCPClient implements GCPApi { const body = JSON.stringify(newProject); - const url = `https://content-cloudresourcemanager.googleapis.com/v1/projects`; - - const response = await fetch(url, { + const response = await fetch(BaseURL, { headers: new Headers({ Accept: '*/*', Authorization: `Bearer ${token}`, From 18fc7c99f617d55a59b32c34b44f77175bf5ab54 Mon Sep 17 00:00:00 2001 From: ebarrios Date: Tue, 14 Jul 2020 08:27:22 +0200 Subject: [PATCH 007/359] Removed some extra header on the create new GCP Project --- .../NewProjectPage/NewProjectPage.tsx | 124 ++++++++---------- 1 file changed, 57 insertions(+), 67 deletions(-) diff --git a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx index 5588884a2a..57695dd6bc 100644 --- a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx +++ b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx @@ -42,78 +42,68 @@ export const NewProjectPage: FC<{}> = () => { }; return ( - -
- - -
- - - - This plugin will help you create a project in gcp. - - - - - - - - setProjectName(e.target.value)} - value={projectName} - fullWidth - /> - - - setProjectId(e.target.value)} - value={projectId} - fullWidth - /> - - - - - - - - {done === true ? ( - - - -
-
-
+ + + + + + + + setProjectName(e.target.value)} + value={projectName} + fullWidth + /> + + + setProjectId(e.target.value)} + value={projectId} + fullWidth + /> + + - - - ) : ( -
- )} + + +
-
-
+ {done === true ? ( + + + +
+
+
+ +
+
+ ) : ( +
+ )} + +
); }; From f9b4a2bd35c028f596673dde656a0ddd88d24dc2 Mon Sep 17 00:00:00 2001 From: ebarrios Date: Tue, 14 Jul 2020 09:37:38 +0200 Subject: [PATCH 008/359] Removed unsused imports from NewProjectPage --- .../src/components/NewProjectPage/NewProjectPage.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx index 57695dd6bc..0eddcf09e5 100644 --- a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx +++ b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx @@ -19,13 +19,8 @@ import { Grid, Button, TextField } from '@material-ui/core'; import { InfoCard, - Header, - Page, - pageTheme, Content, ContentHeader, - HeaderLabel, - SupportButton, SimpleStepper, SimpleStepperStep, StructuredMetadataTable, From 2c13894037705b392cb3d2c31f45c5b8ed1eb5ae Mon Sep 17 00:00:00 2001 From: ebarrios Date: Tue, 28 Jul 2020 15:48:14 +0200 Subject: [PATCH 009/359] Modify the UI per request of the backstage team --- .../NewProjectPage/NewProjectPage.tsx | 73 +++++--- .../ProjectDetailsPage/ProjectDetailsPage.tsx | 169 ++++++++++-------- .../ProjectListPage/ProjectListPage.tsx | 58 +++--- 3 files changed, 173 insertions(+), 127 deletions(-) diff --git a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx index 0eddcf09e5..f09044f3ab 100644 --- a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx +++ b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { FC, useState } from 'react'; +import React, { FC, useState, Fragment } from 'react'; import { Grid, Button, TextField } from '@material-ui/core'; import { @@ -24,9 +24,14 @@ import { SimpleStepper, SimpleStepperStep, StructuredMetadataTable, + HeaderLabel, + Page, + Header, + pageTheme, + SupportButton, } from '@backstage/core'; -export const NewProjectPage: FC<{}> = () => { +export const Project: FC<{}> = () => { const [projectName, setProjectName] = useState(''); const [projectId, setProjectId] = useState(''); const [done, setDone] = useState(false); @@ -38,10 +43,9 @@ export const NewProjectPage: FC<{}> = () => { return ( - - - + + = () => { /> + + - {done === true ? ( - - - -
-
-
- -
-
- ) : ( -
- )}
); }; + +const labels = ( + <> + + + +); + +export const NewProjectPage = () => { + return ( + +
+ {labels} +
+ + + Support Button + + + +
+ ); +}; diff --git a/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx b/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx index d9721bf549..713918d8a2 100644 --- a/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx +++ b/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx @@ -28,10 +28,19 @@ import { Theme, Typography, } from '@material-ui/core'; +import { + useApi, + googleAuthApiRef, + HeaderLabel, + Page, + Header, + pageTheme, + SupportButton, + Content, + ContentHeader, +} from '@backstage/core'; import React from 'react'; -import { useLocation } from 'react-router-dom'; import { useAsync } from 'react-use'; -import { Link, useApi, googleAuthApiRef } from '@backstage/core'; import { GCPApiRef } from '../../api'; const useStyles = makeStyles(theme => ({ @@ -47,7 +56,7 @@ const useStyles = makeStyles(theme => ({ }, })); -export const ProjectDetailsPage = () => { +const DetailsPage = () => { const api = useApi(GCPApiRef); const googleApi = useApi(googleAuthApiRef); const token = googleApi.getAccessToken( @@ -55,7 +64,6 @@ export const ProjectDetailsPage = () => { ); const classes = useStyles(); - const location = useLocation(); const status = useAsync( () => api.getProject( @@ -78,76 +86,87 @@ export const ProjectDetailsPage = () => { const details = status.value; return ( -
- - - - < - - - Build Details - - - - - - - Name - - {details?.name} - - - - Project Number - - {details?.projectNumber} - - - - Project ID - - {details?.projectId} - - - - State - - - {details?.lifecycleState} - - - - - Creation Time - - {details?.createTime} - - - - Links - - - - {details?.name && ( - - )} - {details?.name && ( - - )} - - - - -
-
-
+ + + + + + Name + + {details?.name} + + + + Project Number + + {details?.projectNumber} + + + + Project ID + + {details?.projectId} + + + + State + + {details?.lifecycleState} + + + + Creation Time + + {details?.createTime} + + + + Links + + + + {details?.name && ( + + )} + {details?.name && ( + + )} + + + + +
+
+ ); +}; + +const labels = ( + <> + + + +); + +export const ProjectDetailsPage = () => { + return ( + +
+ {labels} +
+ + + Support Button + + + +
); }; diff --git a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx index 00dac5c70e..163d8a54d8 100644 --- a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx +++ b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx @@ -16,7 +16,19 @@ // NEEDS WORK -import { Link, useApi, googleAuthApiRef, InfoCard } from '@backstage/core'; +import { + Link, + useApi, + googleAuthApiRef, + InfoCard, + HeaderLabel, + Page, + Header, + pageTheme, + SupportButton, + Content, + ContentHeader, +} from '@backstage/core'; import { LinearProgress, makeStyles, @@ -47,14 +59,12 @@ const LongText = ({ text, max }: { text: string; max: number }) => { ); }; -const useStyles = makeStyles(theme => ({ - root: { - padding: theme.spacing(2), - }, - title: { - padding: theme.spacing(1, 0, 2, 0), - }, -})); +const labels = ( + <> + + + +); const PageContents = () => { const api = useApi(GCPApiRef); @@ -76,7 +86,7 @@ const PageContents = () => { if (error) { return ( - Failed to load projects, {error.message}{' '} + {error.message}{' '} ); } @@ -139,20 +149,20 @@ const PageContents = () => { }; export const ProjectListPage = () => { - const classes = useStyles(); - return ( -
- - GCP Projects - - - - - - -
+ +
+ {labels} +
+ + + + All your software catalog entities + + + +
); }; From 082d667854ed763d3377980541638382ab67fcfd Mon Sep 17 00:00:00 2001 From: ebarrios Date: Tue, 28 Jul 2020 17:12:54 +0200 Subject: [PATCH 010/359] Clean up for yarn tsc and fix the simple settep for project creation --- .../NewProjectPage/NewProjectPage.tsx | 32 ++++++++++++------- .../ProjectListPage/ProjectListPage.tsx | 3 -- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx index f09044f3ab..4d439bbc0d 100644 --- a/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx +++ b/plugins/gcp-projects/src/components/NewProjectPage/NewProjectPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { FC, useState, Fragment } from 'react'; +import React, { FC, useState } from 'react'; import { Grid, Button, TextField } from '@material-ui/core'; import { @@ -34,7 +34,7 @@ import { export const Project: FC<{}> = () => { const [projectName, setProjectName] = useState(''); const [projectId, setProjectId] = useState(''); - const [done, setDone] = useState(false); + const [disabled, setDisabled] = useState(true); const metadata = { ProjectName: projectName, @@ -69,17 +69,15 @@ export const Project: FC<{}> = () => { fullWidth /> - + + setDisabled(false), + }} + > - + diff --git a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx index 163d8a54d8..8d612f75f8 100644 --- a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx +++ b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx @@ -20,7 +20,6 @@ import { Link, useApi, googleAuthApiRef, - InfoCard, HeaderLabel, Page, Header, @@ -31,7 +30,6 @@ import { } from '@backstage/core'; import { LinearProgress, - makeStyles, Paper, Table, TableBody, @@ -39,7 +37,6 @@ import { TableContainer, TableHead, TableRow, - Theme, Tooltip, Typography, Button, From 019b9b6dbc1fade2762a21ff89646fda9c4765a9 Mon Sep 17 00:00:00 2001 From: ebarrios Date: Wed, 29 Jul 2020 12:09:33 +0200 Subject: [PATCH 011/359] Readded package gcp-projects to packages.app.package.json --- packages/app/package.json | 1 + yarn.lock | 21 +++++++++------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 168506d498..164865cead 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -8,6 +8,7 @@ "@backstage/plugin-catalog": "^0.1.1-alpha.16", "@backstage/plugin-circleci": "^0.1.1-alpha.16", "@backstage/plugin-explore": "^0.1.1-alpha.16", + "@backstage/plugin-gcp-projects": "^0.1.1-alpha.16", "@backstage/plugin-github-actions": "^0.1.1-alpha.16", "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.16", "@backstage/plugin-graphiql": "^0.1.1-alpha.16", diff --git a/yarn.lock b/yarn.lock index cb354e71d4..f1bcff10a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3933,6 +3933,14 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" +"@types/jest@^25.2.2": + version "25.2.3" + resolved "https://registry.npmjs.org/@types/jest/-/jest-25.2.3.tgz#33d27e4c4716caae4eced355097a47ad363fdcaf" + integrity sha512-JXc1nK/tXHiDhV55dvfzqtmP4S3sy3T3ouV2tkViZgxY/zeUkcpQcQPGRlgF4KmWzWW5oiWYSZwtCB+2RsE4Fw== + dependencies: + jest-diff "^25.2.1" + pretty-format "^25.2.1" + "@types/jquery@^3.3.34": version "3.3.38" resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.38.tgz#6385f1e1b30bd2bff55ae8ee75ea42a999cc3608" @@ -13220,13 +13228,12 @@ loglevel@^1.6.7, loglevel@^1.6.8: resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA== - long@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -17358,11 +17365,6 @@ resolve-from@^4.0.0: resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - resolve-pathname@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" @@ -20372,11 +20374,6 @@ whatwg-fetch@^2.0.0, whatwg-fetch@^2.0.4: resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== -whatwg-fetch@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.2.0.tgz#8e134f701f0a4ab5fda82626f113e2b647fd16dc" - integrity sha512-SdGPoQMMnzVYThUbSrEvqTlkvC1Ux27NehaJ/GUHBfNrh5Mjg+1/uRyFMwVnxO2MrikMWvWAqUGgQOfVU4hT7w== - whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" From 840e708bf7a472d55a2daa7f3a79b30fb77b1e4a Mon Sep 17 00:00:00 2001 From: ebarrios Date: Wed, 29 Jul 2020 12:14:11 +0200 Subject: [PATCH 012/359] Rolled back plugin version to 0.1.1-alpha.13 --- packages/app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/package.json b/packages/app/package.json index 164865cead..495a375bd8 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -8,7 +8,7 @@ "@backstage/plugin-catalog": "^0.1.1-alpha.16", "@backstage/plugin-circleci": "^0.1.1-alpha.16", "@backstage/plugin-explore": "^0.1.1-alpha.16", - "@backstage/plugin-gcp-projects": "^0.1.1-alpha.16", + "@backstage/plugin-gcp-projects": "^0.1.1-alpha.13", "@backstage/plugin-github-actions": "^0.1.1-alpha.16", "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.16", "@backstage/plugin-graphiql": "^0.1.1-alpha.16", From c28527a469133141d94de71770a9da2cb6fdd8d5 Mon Sep 17 00:00:00 2001 From: ebarrios Date: Wed, 29 Jul 2020 12:42:25 +0200 Subject: [PATCH 013/359] Using version ^0.1.1-alpha.16 --- packages/app/package.json | 2 +- plugins/gcp-projects/package.json | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 495a375bd8..164865cead 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -8,7 +8,7 @@ "@backstage/plugin-catalog": "^0.1.1-alpha.16", "@backstage/plugin-circleci": "^0.1.1-alpha.16", "@backstage/plugin-explore": "^0.1.1-alpha.16", - "@backstage/plugin-gcp-projects": "^0.1.1-alpha.13", + "@backstage/plugin-gcp-projects": "^0.1.1-alpha.16", "@backstage/plugin-github-actions": "^0.1.1-alpha.16", "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.16", "@backstage/plugin-graphiql": "^0.1.1-alpha.16", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 2bef7feef4..1b4a031aac 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gcp-projects", - "version": "0.1.1-alpha.13", + "version": "0.1.1-alpha.16", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.13", - "@backstage/theme": "^0.1.1-alpha.13", + "@backstage/core": "^0.1.1-alpha.16", + "@backstage/theme": "^0.1.1-alpha.16", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,12 +32,12 @@ "react-use": "^14.2.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.13", - "@backstage/dev-utils": "^0.1.1-alpha.13", + "@backstage/cli": "^0.1.1-alpha.16", + "@backstage/dev-utils": "^0.1.1-alpha.16", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", - "@types/jest": "^25.2.2", + "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3" }, From b349720c7507d611aea7239d55cc220a3d20f325 Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Mon, 17 Aug 2020 17:06:28 +0200 Subject: [PATCH 014/359] =migrations https logic to new branch --- microsite/i18n/en.json | 3 + microsite/pages/en/background.js | 2 +- packages/backend-common/package.json | 3 +- .../src/service/lib/ServiceBuilderImpl.ts | 120 ++++++++++++++++-- .../backend-common/src/service/lib/config.ts | 70 ++++++++++ packages/backend-common/src/service/types.ts | 19 +++ yarn.lock | 2 +- 7 files changed, 204 insertions(+), 15 deletions(-) diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json index af891426db..5ddede3d97 100644 --- a/microsite/i18n/en.json +++ b/microsite/i18n/en.json @@ -50,6 +50,9 @@ "auth/add-auth-provider": { "title": "Adding authentication providers" }, + "auth/auth-backend-classes": { + "title": "auth/auth-backend-classes" + }, "auth/auth-backend": { "title": "Auth backend" }, diff --git a/microsite/pages/en/background.js b/microsite/pages/en/background.js index 70a15421ab..c5bf9bee94 100644 --- a/microsite/pages/en/background.js +++ b/microsite/pages/en/background.js @@ -112,7 +112,7 @@ const Background = props => { left: 0, right: 0, bottom: 0, - backgroundImage: `linear-gradient( to bottom, rgb(18, 18, 18), rgba(0, 0, 0, 0) ), url(../img/dot.svg);`, + backgroundImage: `linear-gradient( to bottom, rgb(18, 18, 18), rgba(0, 0, 0, 0) ), url(../img/dot.svg)`, }} /> diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 5cf2ee95e5..8d932cfd49 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -43,7 +43,8 @@ "lodash": "^4.17.15", "morgan": "^1.10.0", "stoppable": "^1.1.0", - "winston": "^3.2.1" + "winston": "^3.2.1", + "selfsigned": "^1.10.7" }, "peerDependencies": { "pg-connection-string": "^2.3.0" diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index cd8e445dd8..1182cc6388 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -19,7 +19,9 @@ import compression from 'compression'; import cors from 'cors'; import express, { Router } from 'express'; import helmet from 'helmet'; -import { Server } from 'http'; +import * as http from 'http'; +import * as https from 'https'; +import * as fs from 'fs'; import stoppable from 'stoppable'; import { Logger } from 'winston'; import { useHotCleanup } from '../../hot'; @@ -30,7 +32,12 @@ import { requestLoggingHandler, } from '../../middleware'; import { ServiceBuilder } from '../types'; -import { readBaseOptions, readCorsOptions } from './config'; +import { + readBaseOptions, + readCorsOptions, + readHttpsSettings, + HttpsSettings, +} from './config'; const DEFAULT_PORT = 7000; // '' is express default, which listens to all interfaces @@ -41,6 +48,7 @@ export class ServiceBuilderImpl implements ServiceBuilder { private host: string | undefined; private logger: Logger | undefined; private corsOptions: cors.CorsOptions | undefined; + private httpsSettings: HttpsSettings | undefined; private routers: [string, Router][]; // Reference to the module where builder is created - needed for hot module // reloading @@ -70,6 +78,11 @@ export class ServiceBuilderImpl implements ServiceBuilder { this.corsOptions = corsOptions; } + const httpsSettings = readHttpsSettings(backendConfig); + if (httpsSettings) { + this.httpsSettings = httpsSettings; + } + return this; } @@ -88,6 +101,11 @@ export class ServiceBuilderImpl implements ServiceBuilder { return this; } + setHttpsSettings(settings: HttpsSettings): ServiceBuilder { + this.httpsSettings = settings; + return this; + } + enableCors(options: cors.CorsOptions): ServiceBuilder { this.corsOptions = options; return this; @@ -98,9 +116,15 @@ export class ServiceBuilderImpl implements ServiceBuilder { return this; } - start(): Promise { + start(): Promise { const app = express(); - const { port, host, logger, corsOptions } = this.getOptions(); + const { + port, + host, + logger, + corsOptions, + httpsSettings, + } = this.getOptions(); app.use(helmet()); if (corsOptions) { @@ -121,20 +145,90 @@ export class ServiceBuilderImpl implements ServiceBuilder { reject(e); }); - const server = stoppable( - app.listen(port, host, () => { - logger.info(`Listening on ${host}:${port}`); - }), - 0, - ); + let server: http.Server; + + if (httpsSettings) { + logger.info('Initializing https server'); + + const credentials: { key: string; cert: string } = { + key: '', + cert: '', + }; + const signingOptions: any = httpsSettings?.certificate; + + if (signingOptions?.algorithm !== undefined) { + logger.info('Generating self-signed certificate with attributes'); + + const certificateAttributes: Array = Object.entries( + signingOptions.attributes, + ).map(([name, value]) => ({ name, value })); + + // TODO: Create a type def for selfsigned. + const signatures = require('selfsigned').generate( + certificateAttributes, + { + algorithm: signingOptions?.algorithm, + keySize: signingOptions?.size || 2048, + days: signingOptions?.days || 30, + }, + ); + + logger.info( + 'Bootstrapping key and cert from self-signed certificate', + ); + + credentials.key = signatures.private; + credentials.cert = signatures.cert; + } else { + if (fs.existsSync(signingOptions?.key)) { + if (fs.lstatSync(signingOptions?.key).isFile()) { + logger.info('Bootstrapping key from file'); + + credentials.key = fs.readFileSync(signingOptions?.key).toString(); + } + } else { + logger.info('Bootstrapping key from config'); + + credentials.key = signingOptions?.key; + } + + if (fs.existsSync(signingOptions?.cert)) { + if (fs.lstatSync(signingOptions?.cert).isFile()) { + logger.info('Bootstrapping cert from file'); + + credentials.cert = fs + .readFileSync(signingOptions?.cert) + .toString(); + } + } else { + logger.info('Bootstrapping cert from config'); + + credentials.cert = signingOptions?.cert; + } + } + + if (credentials.key === '' || credentials.cert === '') { + throw new Error('Invalid credentials'); + } + + server = https.createServer(credentials, app) as http.Server; + } else { + logger.info('Initializing http server'); + + server = http.createServer(app); + } + + const stoppableServer = stoppable(server, 0); + + stoppableServer.listen(port, host); useHotCleanup(this.module, () => - server.stop((e: any) => { + stoppableServer.stop((e: any) => { if (e) console.error(e); }), ); - resolve(server); + resolve(stoppableServer); }); } @@ -143,12 +237,14 @@ export class ServiceBuilderImpl implements ServiceBuilder { host: string; logger: Logger; corsOptions?: cors.CorsOptions; + httpsSettings?: HttpsSettings; } { return { port: this.port ?? DEFAULT_PORT, host: this.host ?? DEFAULT_HOST, logger: this.logger ?? getRootLogger(), corsOptions: this.corsOptions, + httpsSettings: this.httpsSettings, }; } } diff --git a/packages/backend-common/src/service/lib/config.ts b/packages/backend-common/src/service/lib/config.ts index 49f8dc4f04..e257bd4111 100644 --- a/packages/backend-common/src/service/lib/config.ts +++ b/packages/backend-common/src/service/lib/config.ts @@ -22,6 +22,41 @@ export type BaseOptions = { listenHost?: string; }; +export type CertificateOptions = { + key?: CertificateKeyOptions; + attributes?: CertificateAttributeOptions; +}; + +export type CertificateKeyOptions = { + size?: number; + algorithm?: string; + days?: number; +}; + +export type CertificateAttributeOptions = { + commonName?: string; +}; + +export type HttpsSettings = { + certificate: CertificateSigningOptions | CertificateReferenceOptions; +}; + +export type CertificateReferenceOptions = { + key: string; + cert: string; +}; + +export type CertificateSigningOptions = { + algorithm: string; + size?: number; + days?: number; + attributes?: CertificateAttributes; +}; + +export type CertificateAttributes = { + commonName?: string; +}; + /** * Reads some base options out of a config object. * @@ -40,6 +75,7 @@ export function readBaseOptions(config: ConfigReader): BaseOptions { if (typeof config.get('listen') === 'string') { // TODO(freben): Expand this to support more addresses and perhaps optional const { host, port } = parseListenAddress(config.getString('listen')); + return removeUnknown({ listenPort: port, listenHost: host, @@ -49,6 +85,7 @@ export function readBaseOptions(config: ConfigReader): BaseOptions { return removeUnknown({ listenPort: config.getOptionalNumber('listen.port'), listenHost: config.getOptionalString('listen.host'), + baseUrl: config.getOptionalString('baseUrl'), }); } @@ -86,6 +123,39 @@ export function readCorsOptions(config: ConfigReader): CorsOptions | undefined { }); } +/** + * Attempts to read a https settings object from the root of a config object. + * + * @param config The root of a backend config object + * @returns A https settings object, or undefined if not specified + * + * @example + * ```json + * { + * https: { + * certificate: ... + * } + * } + * ``` + */ +export function readHttpsSettings( + config: ConfigReader, +): HttpsSettings | undefined { + const cc = config.getOptionalConfig('https'); + + if (!cc) { + return undefined; + } + + const certificateConfig = cc.get('certificate'); + + const cfg = { + certificate: certificateConfig, + }; + + return removeUnknown(cfg as HttpsSettings); +} + function getOptionalStringOrStrings( config: ConfigReader, key: string, diff --git a/packages/backend-common/src/service/types.ts b/packages/backend-common/src/service/types.ts index 070794969f..d389f4b5ff 100644 --- a/packages/backend-common/src/service/types.ts +++ b/packages/backend-common/src/service/types.ts @@ -19,6 +19,7 @@ import cors from 'cors'; import { Router, RequestHandler } from 'express'; import { Server } from 'http'; import { Logger } from 'winston'; +import { HttpsSettings } from './lib/config'; export type ServiceBuilder = { /** @@ -39,6 +40,15 @@ export type ServiceBuilder = { */ setPort(port: number): ServiceBuilder; + /** + * Sets the host to listen on. + * + * '' is express default, which listens to all interfaces. + * + * @param host The host to listen on + */ + setHost(host: string): ServiceBuilder; + /** * Sets the logger to use for service-specific logging. * @@ -58,6 +68,15 @@ export type ServiceBuilder = { */ enableCors(options: cors.CorsOptions): ServiceBuilder; + /** + * Configure self-signed certificate generation options. + * + * If this method is not called, the resulting service will use sensible defaults + * + * @param options Standard certificate options + */ + setHttpsSettings(settings: HttpsSettings): ServiceBuilder; + /** * Adds a router (similar to the express .use call) to the service. * diff --git a/yarn.lock b/yarn.lock index 6a63b34141..9ab7af396b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9349,7 +9349,7 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -docusaurus@2.0.0-alpha.61: +docusaurus@^2.0.0-alpha.61: version "2.0.0-alpha.61" resolved "https://registry.npmjs.org/docusaurus/-/docusaurus-2.0.0-alpha.61.tgz#f347b81c98c66f1de3ecfccf63fa421ddff52fbb" integrity sha512-qDU3nOA4Xs95tIjjSETnEuRmTukTzgxyTZ5MgMyuG7y6h4oDHtpLcYf8F+xlXuuWHKv3VSxRJNzV8fZHPgnK3g== From d982278fc34450f59c5f237502413ae92c3cb4bc Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Tue, 18 Aug 2020 10:27:48 +0200 Subject: [PATCH 015/359] =fixed broken microsite --- microsite/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/package.json b/microsite/package.json index 6a10628b5a..dd8957245a 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -6,7 +6,7 @@ "scripts": { "examples": "docusaurus-examples", "start": "docusaurus-start", - "build": "docusaurus-build && cp static/img/*.svg build/backstage/img/", + "build": "docusaurus-build", "publish-gh-pages": "docusaurus-publish", "write-translations": "docusaurus-write-translations", "version": "docusaurus-version", From 44e09bd980afb1cf936ddbe6679eb7f34239dde9 Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Tue, 18 Aug 2020 10:37:38 +0200 Subject: [PATCH 016/359] =added disabled config for https w. self-signed cert --- app-config.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app-config.yaml b/app-config.yaml index a0233991f8..f3b3b8d00c 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -13,6 +13,13 @@ backend: database: client: sqlite3 connection: ':memory:' + # https: + # certificate: + # size: 2048 + # algorithm: sha256 + # days: 30 + # attributes: + # commonName: 'contoso.com' proxy: '/circleci/api': From 141bb0b9ab3c902b0c7715e606b251a2fd4ac7ef Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Tue, 18 Aug 2020 16:53:37 +0200 Subject: [PATCH 017/359] =triggering checks to see why CLI tests timed out --- app-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-config.yaml b/app-config.yaml index f3b3b8d00c..0e94367aa1 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -19,7 +19,7 @@ backend: # algorithm: sha256 # days: 30 # attributes: - # commonName: 'contoso.com' + # commonName: 'dfds.com' proxy: '/circleci/api': From 0ee6ba404905cce6ddcfcd052dbcb7b56704333d Mon Sep 17 00:00:00 2001 From: Tobias Andersen Date: Thu, 20 Aug 2020 11:11:53 +0200 Subject: [PATCH 018/359] =Fixed problem with the stoppable wrapper not triggering the listen method on the http server object --- .../backend-common/src/service/lib/ServiceBuilderImpl.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index 1182cc6388..806096915b 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -218,9 +218,12 @@ export class ServiceBuilderImpl implements ServiceBuilder { server = http.createServer(app); } - const stoppableServer = stoppable(server, 0); + const stoppableServer = stoppable( + server.listen(port, host, () => { + logger.info(`Listening on ${host}:${port}`); + }), 0); - stoppableServer.listen(port, host); + //stoppableServer.listen(port, host); useHotCleanup(this.module, () => stoppableServer.stop((e: any) => { From d263219c2b8aa55a82ead09c2adf2a79cc28678f Mon Sep 17 00:00:00 2001 From: Tobias Andersen Date: Thu, 20 Aug 2020 11:12:16 +0200 Subject: [PATCH 019/359] =Fixed problem with the stoppable wrapper not triggering the listen method on the http server object --- packages/backend-common/src/service/lib/ServiceBuilderImpl.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index 806096915b..0e0feb6a37 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -223,8 +223,6 @@ export class ServiceBuilderImpl implements ServiceBuilder { logger.info(`Listening on ${host}:${port}`); }), 0); - //stoppableServer.listen(port, host); - useHotCleanup(this.module, () => stoppableServer.stop((e: any) => { if (e) console.error(e); From 17233abfcbc37915b0d9158c3284811ef49c243a Mon Sep 17 00:00:00 2001 From: Tobias Andersen Date: Thu, 20 Aug 2020 14:55:42 +0200 Subject: [PATCH 020/359] =Partial commit, have to go home --- .../src/service/lib/ServiceBuilderImpl.ts | 6 ++- .../src/service/lib/hostFactory.ts | 41 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 packages/backend-common/src/service/lib/hostFactory.ts diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index 0e0feb6a37..e75ae8069f 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -38,6 +38,10 @@ import { readHttpsSettings, HttpsSettings, } from './config'; +import { + createHttpServer, + createHttpsServer, +} from './hostFactory'; const DEFAULT_PORT = 7000; // '' is express default, which listens to all interfaces @@ -215,7 +219,7 @@ export class ServiceBuilderImpl implements ServiceBuilder { } else { logger.info('Initializing http server'); - server = http.createServer(app); + server = createHttpServer(app); } const stoppableServer = stoppable( diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts new file mode 100644 index 0000000000..9b850431fb --- /dev/null +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 express from 'express'; +import * as http from 'http'; +import * as https from 'https'; + +/** + * Reads some base options out of a config object. + * + * @param config The root of a backend config object + * @returns A base options object + * + * @example + * ```json + * { + * baseUrl: "http://localhost:7000", + * listen: "0.0.0.0:7000" + * } + * ``` + */ +export function createHttpServer(app: express.Express): http.Server { + return http.createServer(app); +} + + +export function createHttpsServer(app: express.Express): http.Server { + return https.createServer(app); +} \ No newline at end of file From 34e72a7cf4af464ac0d027bdf0f246fd1f0f7684 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 20 Aug 2020 18:10:08 +0200 Subject: [PATCH 021/359] cli: grab the needed e2e test package dependencies from the create-app template --- packages/cli/e2e-test/cli-e2e-test.js | 34 +++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/cli/e2e-test/cli-e2e-test.js b/packages/cli/e2e-test/cli-e2e-test.js index 8a3285c544..c4004f7c46 100644 --- a/packages/cli/e2e-test/cli-e2e-test.js +++ b/packages/cli/e2e-test/cli-e2e-test.js @@ -17,6 +17,7 @@ const os = require('os'); const fs = require('fs-extra'); const fetch = require('node-fetch'); +const handlebars = require('handlebars'); const killTree = require('tree-kill'); const { resolve: resolvePath, join: joinPath } = require('path'); const Browser = require('zombie'); @@ -62,6 +63,35 @@ async function buildDistWorkspace(workspaceName, rootDir) { const workspaceDir = resolvePath(rootDir, workspaceName); await fs.ensureDir(workspaceDir); + // We grab the needed dependencies from the template packages + const appPkgTemplate = await fs.readFile( + resolvePath( + __dirname, + '../../create-app/templates/default-app/packages/app/package.json.hbs', + ), + 'utf8', + ); + const appPkg = JSON.parse( + handlebars.compile(appPkgTemplate)({ version: '0.0.0' }), + ); + const appDeps = Object.keys(appPkg.dependencies).filter(name => + name.startsWith('@backstage/'), + ); + + const backendPkgTemplate = await fs.readFile( + resolvePath( + __dirname, + '../../create-app/templates/default-app/packages/backend/package.json.hbs', + ), + 'utf8', + ); + const backendPkg = JSON.parse( + handlebars.compile(backendPkgTemplate)({ version: '0.0.0' }), + ); + const backendDeps = Object.keys(backendPkg.dependencies).filter(name => + name.startsWith('@backstage/'), + ); + print(`Preparing workspace`); await runPlain([ 'yarn', @@ -73,8 +103,8 @@ async function buildDistWorkspace(workspaceName, rootDir) { '@backstage/core', '@backstage/dev-utils', '@backstage/test-utils', - // We don't use the backend itself, but want all of its dependencies - 'example-backend', + ...appDeps, + ...backendDeps, ]); print('Pinning yarn version in workspace'); From 95596b812983f892411c32fa67a5ca7de4b22cd3 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 20 Aug 2020 21:01:13 +0200 Subject: [PATCH 022/359] chore(api-docs): the api docs plugin should be published (#2051) * chore(api-docs): the api docs plugin should be published * chore: add missing @types/react dependency * chore: move to dependency --- plugins/api-docs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 65f83a936d..c8a6b5732d 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -4,7 +4,6 @@ "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", - "private": true, "publishConfig": { "access": "public", "main": "dist/index.esm.js", @@ -34,6 +33,7 @@ "react-dom": "^16.13.1", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3", + "@types/react": "^16.9", "swagger-ui-react": "^3.31.1" }, "devDependencies": { From 89a0a2fc7b54ed82ccdf3dae43883963fa0d7d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 19 Aug 2020 15:52:18 +0200 Subject: [PATCH 023/359] docs: write call-existing-api.md --- docs/plugins/call-existing-api.md | 175 +++++++++++++++++++++++++++++- plugins/jenkins/README.md | 2 +- 2 files changed, 174 insertions(+), 3 deletions(-) diff --git a/docs/plugins/call-existing-api.md b/docs/plugins/call-existing-api.md index 075d4bdeb6..f6c9990b90 100644 --- a/docs/plugins/call-existing-api.md +++ b/docs/plugins/call-existing-api.md @@ -1,6 +1,177 @@ --- id: call-existing-api -title: Call existing API +title: Call Existing API --- -## TODO +This article describes the various options that Backstage frontend plugins have, +in communicating with service APIs that already exist. Each section below +describes a possible choice, and the circumstances under which it fits. + +In these examples, we will be ultimately requesting data from the fictional +FrobsCo API. + +## Issuing Requests Directly + +The most basic choice available is to issue requests directly from the plugin +frontend code to the FrobsCo API, using for example `fetch` or a support library +such as `axios`. + +Example: + +```ts +// Inside your component +fetch('https://api.frobsco.com/v1/list') + .then(response => response.json()) + .then(payload => setFrobs(payload as Frob[])); +``` + +Internally at Spotify, this has not been a very common choice. Third party APIs +are sometimes accessed like this. Just a handful of internal APIs also went +through the trouble of exposing themselves in a way that is useful directly from +a browser, but even then, often not from the public internet but only supporting +users that are already on the company VPN. + +This can be used when: + +- The API already does/exposes exactly what you need. +- The request/response patterns of the API match real world usage needs in + Backstage frontend plugins. For example, if the end use case is to show a + small summary in Backstage, but the only available API endpoint gives a 30 + megabyte blob with large amounts of redundant information, it would hurt the + end user experience. Particularly on mobile. The same goes for cases where you + want to show many individual pieces of information: if a common use case is to + show large tables where one API request per cell is necessary, the browser + will quickly become swamped and you may want to consider performing + aggregation elsewhere instead. +- The API can maintain interactive request/response times at your required peak + request rates. The end user experience will be degraded if they spend a lot of + time waiting for the data to arrive. +- The API endpoint is highly available. The browser does not have builtin + facilities for load balancing, service discovery, retries, health checks, + circuit breaking and similar. If the endpoint is occasionally down even for + short periods of time (e.g. during deploys), end users will quickly notice. +- The API is exposed over HTTPS (not just HTTP), and properly handles + [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). These are + requirements that the user's browser will impose for security reasons, and the + requests will be rejected otherwise. +- The API endpoint is easily reachable, in terms of network conditions, by end + users. This may be particularly relevant if your end users are outside of your + perimeter. +- The requests do not require secrets to be passed. This limitation does not + apply to OAuth tokens, which the frontend can negotiate and make proper use + of. + +## Using The Backstage Proxy + +Backstage has an optional proxy plugin for the backend, that can be used to +easily add proxy routes to downstream APIs. + +Example: + +```yaml +# In app-config.yaml +proxy: + '/frobs': + target: 'http://api.frobsco.com/v1' + changeOrigin: true + pathRewrite: + '^/proxy/frobs/': '/' +``` + +```ts +// Inside your component +const backendUrl = config.getString('backend.baseUrl'); +fetch(`${backendUrl}/proxy/frobs/list`) + .then(response => response.json()) + .then(payload => setFrobs(payload as Frob[])); +``` + +The proxy is powered by the `http-proxy-middleware` package, and supports all of +its +[configuration options](https://github.com/chimurai/http-proxy-middleware#options). + +Internally at Spotify, the proxy option has been the overwhelmingly most popular +choice for plugin makers. Since we have DNS based service discovery in place and +a microservices framework that made it trivial to expose plain HTTP, it has been +a matter of just adding a few lines of Backstage config to get the benefit of +being easily and robustly reachable from users' web browsers as well. + +This may be used instead of direct requests, when: + +- You need to perform HTTPS termination and/or CORS handling, because the API + itself is not supplying those. +- You need to inject a simple static secret into the requests, e.g. an + Authorization header that gets added to the request headers. +- You want to make use of other proxy facilities, such as retries, failover, + health checks, routing, request logging, rewrites, etc. +- You already have the Backstage backend itself exposed through your perimeter + and find it practical to have only one entry point to deal with, governing + ingress with just the Backstage config. + +## Creating a Backstage Backend Plugin + +Much like the Backstage frontend, the Backstage backend also has a plugin +system. The above mentioned proxy is actually one such plugin. If you were in +need of a more involved integration than just direct access to the FrobsCo API, +or if you needed to hold state, you may want to make such a plugin. + +Example: + +```ts +// Inside your component +const backendUrl = config.getString('backend.baseUrl'); +fetch(`${backendUrl}/frobs-aggregator/summary`) + .then(response => response.json()) + .then(payload => setSummary(payload as FrobSummary)); +``` + +```ts +// Inside a new frobs-aggregator backend plugin +router.use('/summary', async (req, res) => { + const agg = await Promise.all([ + fetch('https://api.frobsco.com/v1/list'), + fetch('http://flerps.partnercompany.com:8080/flerp-batch'), + database.currentThunk(), + ]).then(async ([frobs, flerps, thunk]) => { + return computeAggregate(await frobs.json(), await flerps.json(), thunk); + }); + res.status(200).send(agg); +}); +``` + +For a more detailed example, see +[the lighthouse plugin](https://github.com/spotify/backstage/tree/master/plugins/lighthouse) +that stores some state in a database and adds new capabilities to the underlying +API. + +Internally at Spotify, this has been a fairly popular choice for different +reasons. Commonly, the backend has been used as a caching and data massaging +layer for slow APIs or APIs whose request/response shapes or speeds were not +acceptable for direct use by frontends. For example, this has made it possible +to issue efficient batch queries from the frontend, e.g. in big lists or tables +that want to resolve a lot of sparse data from the larger list that an +underlying service supplies. + +This may be used instead of the above, when: + +- You need to perform complex model conversion, or protocol translation beyond + what the proxy handles. +- You want to perform aggregations or summaries on the backend instead of on the + frontend. +- You want to enable batching or caching of slower or more unreliable APIs. +- You need to maintain state for your plugin, perhaps using the builtin database + support in the backend. +- You need to inject secrets or in other ways negotiate with other parts of the + API or other services in order to perform your work. +- You want to enforce end user authentication / authorization for operations on + behalf of the API, have session handling, or similar. + +There is a balance to strike regarding when to make an entirely separate backend +for a purpose, and when to make a Backstage backend plugin that adapts something +that already exists. General advice is not easy to give, but contact us on +Discord if you have any questions, and we may be able to offer guidance. + +## Extending the GraphQL Model + +The extensible GraphQL backend layer is not built yet. This section will be +expanded when that happens. Stay tuned! diff --git a/plugins/jenkins/README.md b/plugins/jenkins/README.md index b8a844f5d8..a911704dc4 100644 --- a/plugins/jenkins/README.md +++ b/plugins/jenkins/README.md @@ -4,7 +4,7 @@ Website: [https://jenkins.io/](https://jenkins.io/) Last master build Folder results -Build detials +Build details ## Setup From 19db25eac700058c48ee28bc60f0e0adacfa9cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 20 Aug 2020 22:16:19 +0200 Subject: [PATCH 024/359] fix(core): chunk up tabs better --- packages/core/src/components/Tabs/Tabs.tsx | 8 ++++---- packages/core/src/components/Tabs/utils.ts | 21 +++++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/core/src/components/Tabs/Tabs.tsx b/packages/core/src/components/Tabs/Tabs.tsx index 54369de473..336ed2b93b 100644 --- a/packages/core/src/components/Tabs/Tabs.tsx +++ b/packages/core/src/components/Tabs/Tabs.tsx @@ -48,7 +48,7 @@ export interface TabsProps { tabs: TabProps[]; } -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ root: { flexGrow: 1, width: '100%', @@ -66,7 +66,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ export const Tabs: FC = ({ tabs }) => { const classes = useStyles(); - const [value, setValue] = useState([0, 0]); // [selectedChunckedNavIndex, selectedIndex] + const [value, setValue] = useState([0, 0]); // [selectedChunkedNavIndex, selectedIndex] const [navIndex, setNavIndex] = useState(0); const [numberOfChunkedElement, setNumberOfChunkedElement] = useState(0); const [chunkedTabs, setChunkedTabs] = useState([[]]); @@ -89,7 +89,7 @@ export const Tabs: FC = ({ tabs }) => { const hasNextNavIndex = () => navIndex + 1 < chunkedTabs.length; useEffect(() => { - // Each time the window is resized we calculate how many tabs wwe can render given the window width + // Each time the window is resized we calculate how many tabs we can render given the window width const padding = 20; // The AppBar padding const numberOfTabIcons = navIndex === 0 ? 1 : 2; @@ -99,7 +99,7 @@ export const Tabs: FC = ({ tabs }) => { const newChunkedElementSize = Math.floor(wrapperWidth / 170); setNumberOfChunkedElement(newChunkedElementSize); - setChunkedTabs(chunkArray([...tabs], newChunkedElementSize)); + setChunkedTabs(chunkArray(tabs, newChunkedElementSize)); setValue([ Math.floor(flattenIndex / newChunkedElementSize), flattenIndex % newChunkedElementSize, diff --git a/packages/core/src/components/Tabs/utils.ts b/packages/core/src/components/Tabs/utils.ts index 3e0ab6f2c3..8d6a3be5f3 100644 --- a/packages/core/src/components/Tabs/utils.ts +++ b/packages/core/src/components/Tabs/utils.ts @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { TabProps } from './Tabs'; -export const chunkArray = ( - myArray: TabProps[], - chunkSize: number, -): TabProps[][] => { - const results = []; - while (myArray.length) { - results.push(myArray.splice(0, chunkSize)); +export function chunkArray(array: T[], chunkSize: number): T[][] { + if (chunkSize <= 0) { + return [array]; } - return results; -}; + + const result: T[][] = []; + for (let i = 0; i < array.length; i += chunkSize) { + result.push(array.slice(i, i + chunkSize)); + } + + return result; +} From 96f1f522b9a47166bad78ea4b940ecd808eee3b5 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 20 Aug 2020 14:38:33 +0200 Subject: [PATCH 025/359] core-api: add DiscoveryApi + UrlPatternDiscovery implementation --- .../src/apis/definitions/DiscoveryApi.ts | 47 +++++++++++ .../core-api/src/apis/definitions/index.ts | 1 + .../DiscoveryApi/UrlPatternDiscovery.test.ts | 84 +++++++++++++++++++ .../DiscoveryApi/UrlPatternDiscovery.ts | 58 +++++++++++++ .../implementations/DiscoveryApi/index.ts | 21 +++++ .../src/apis/implementations/index.ts | 1 + 6 files changed, 212 insertions(+) create mode 100644 packages/core-api/src/apis/definitions/DiscoveryApi.ts create mode 100644 packages/core-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.test.ts create mode 100644 packages/core-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.ts create mode 100644 packages/core-api/src/apis/implementations/DiscoveryApi/index.ts diff --git a/packages/core-api/src/apis/definitions/DiscoveryApi.ts b/packages/core-api/src/apis/definitions/DiscoveryApi.ts new file mode 100644 index 0000000000..b0773086c7 --- /dev/null +++ b/packages/core-api/src/apis/definitions/DiscoveryApi.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { createApiRef } from '../ApiRef'; + +/** + * The discovery API is used to provide a mechanism for plugins to + * discover the endpoint to use to talk to their backend counterpart. + * + * The purpose of the discovery API is to allow for many different deployment + * setups and routing methods through a central configuration, instead + * of letting each individual plugin manage that configuration. + * + * Implementations of the discovery API can be a simple as a URL pattern + * using the pluginId, but could also have overrides for individual plugins, + * or query a separate discovery service. + */ +export type DiscoveryApi = { + /** + * Returns the HTTP base backend URL for a given plugin, without a trailing slash. + * + * This method must always be called just before making a request. as opposed to + * fetching the URL when constructing an API client. That is to ensure that more + * flexible routing patterns can be supported. + * + * For example, asking for the URL for `auth` may return something + * like `https://backstage.example.com/api/auth` + */ + getBaseUrl(pluginId: string): Promise; +}; + +export const discoveryApiRef = createApiRef({ + id: 'core.discovery', + description: 'Provides service discovery of backend plugins', +}); diff --git a/packages/core-api/src/apis/definitions/index.ts b/packages/core-api/src/apis/definitions/index.ts index c5d4a15117..678dce9e32 100644 --- a/packages/core-api/src/apis/definitions/index.ts +++ b/packages/core-api/src/apis/definitions/index.ts @@ -27,6 +27,7 @@ export * from './AppThemeApi'; export * from './ConfigApi'; export * from './ErrorApi'; export * from './FeatureFlagsApi'; +export * from './DiscoveryApi'; export * from './IdentityApi'; export * from './OAuthRequestApi'; export * from './StorageApi'; diff --git a/packages/core-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.test.ts b/packages/core-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.test.ts new file mode 100644 index 0000000000..9597443b98 --- /dev/null +++ b/packages/core-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.test.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { UrlPatternDiscovery } from './UrlPatternDiscovery'; + +describe('UrlPatternDiscovery', () => { + it('should not require interpolation', async () => { + const discoveryApi = UrlPatternDiscovery.compile('http://example.com'); + await expect(discoveryApi.getBaseUrl('my-plugin')).resolves.toBe( + 'http://example.com', + ); + }); + + it('should use a plain pattern', async () => { + const discoveryApi = UrlPatternDiscovery.compile( + 'http://localhost:7000/{{ pluginId }}', + ); + await expect(discoveryApi.getBaseUrl('my-plugin')).resolves.toBe( + 'http://localhost:7000/my-plugin', + ); + }); + + it('should allow for multiple interpolation points', async () => { + const discoveryApi = UrlPatternDiscovery.compile( + 'https://{{pluginId }}.example.com/api/{{ pluginId}}', + ); + await expect(discoveryApi.getBaseUrl('my-plugin')).resolves.toBe( + 'https://my-plugin.example.com/api/my-plugin', + ); + }); + + it('should validate that the pattern is a valid URL', () => { + expect(() => { + UrlPatternDiscovery.compile('example.com'); + }).toThrow('Invalid discovery URL pattern, Invalid URL: example.com'); + + expect(() => { + UrlPatternDiscovery.compile('http://'); + }).toThrow('Invalid discovery URL pattern, Invalid URL: http://'); + + expect(() => { + UrlPatternDiscovery.compile('abc123'); + }).toThrow('Invalid discovery URL pattern, Invalid URL: abc123'); + + expect(() => { + UrlPatternDiscovery.compile('http://example.com:{{pluginId}}'); + }).toThrow( + 'Invalid discovery URL pattern, Invalid URL: http://example.com:pluginId', + ); + + expect(() => { + UrlPatternDiscovery.compile('/{{pluginId}}'); + }).toThrow('Invalid discovery URL pattern, Invalid URL: /pluginId'); + + expect(() => { + UrlPatternDiscovery.compile('http://localhost/{{pluginId}}?forbidden'); + }).toThrow('Invalid discovery URL pattern, URL must not have a query'); + + expect(() => { + UrlPatternDiscovery.compile('http://localhost/{{pluginId}}#forbidden'); + }).toThrow('Invalid discovery URL pattern, URL must not have a hash'); + + expect(() => { + UrlPatternDiscovery.compile('http://localhost/{{pluginId}}/'); + }).toThrow('Invalid discovery URL pattern, URL must not end with a slash'); + + expect(() => { + UrlPatternDiscovery.compile('http://localhost/'); + }).toThrow('Invalid discovery URL pattern, URL must not end with a slash'); + }); +}); diff --git a/packages/core-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.ts b/packages/core-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.ts new file mode 100644 index 0000000000..ca48784584 --- /dev/null +++ b/packages/core-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { DiscoveryApi } from '../../definitions/DiscoveryApi'; + +/** + * UrlPatternDiscovery is a lightweight DiscoveryApi implementation. + * It uses a single template string to construct URLs for each plugin. + */ +export class UrlPatternDiscovery implements DiscoveryApi { + /** + * Creates a new UrlPatternDiscovery given a template. The the only + * interpolation done for the template is to replace instances of `{{pluginId}}` + * with the ID of the plugin being requested. + * + * Example pattern: `http://localhost:7000/api/{{ pluginId }}` + */ + static compile(pattern: string): UrlPatternDiscovery { + const parts = pattern.split(/\{\{\s*pluginId\s*\}\}/); + + try { + const urlStr = parts.join('pluginId'); + const url = new URL(urlStr); + if (url.hash) { + throw new Error('URL must not have a hash'); + } + if (url.search) { + throw new Error('URL must not have a query'); + } + if (urlStr.endsWith('/')) { + throw new Error('URL must not end with a slash'); + } + } catch (error) { + throw new Error(`Invalid discovery URL pattern, ${error.message}`); + } + + return new UrlPatternDiscovery(parts); + } + + private constructor(private readonly parts: string[]) {} + + async getBaseUrl(pluginId: string): Promise { + return this.parts.join(pluginId); + } +} diff --git a/packages/core-api/src/apis/implementations/DiscoveryApi/index.ts b/packages/core-api/src/apis/implementations/DiscoveryApi/index.ts new file mode 100644 index 0000000000..60a5b815e7 --- /dev/null +++ b/packages/core-api/src/apis/implementations/DiscoveryApi/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This folder contains implementations for all core APIs. +// +// Plugins should rely on these APIs for functionality as much as possible. + +export { UrlPatternDiscovery } from './UrlPatternDiscovery'; diff --git a/packages/core-api/src/apis/implementations/index.ts b/packages/core-api/src/apis/implementations/index.ts index e6d23fee21..30aeb81d44 100644 --- a/packages/core-api/src/apis/implementations/index.ts +++ b/packages/core-api/src/apis/implementations/index.ts @@ -24,5 +24,6 @@ export * from './AlertApi'; export * from './AppThemeApi'; export * from './ConfigApi'; export * from './ErrorApi'; +export * from './DiscoveryApi'; export * from './OAuthRequestApi'; export * from './StorageApi'; From ea1a36433d8b2b63e5a63e912f82f07d1dc336bf Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 20 Aug 2020 17:56:33 +0200 Subject: [PATCH 026/359] app,core-api: install DiscoveryApi in default app and use it for all auth providers --- packages/app/src/apis.ts | 29 ++++++----- .../implementations/auth/auth0/Auth0Auth.ts | 17 +++--- .../implementations/auth/github/GithubAuth.ts | 17 +++--- .../implementations/auth/gitlab/GitlabAuth.ts | 16 +++--- .../implementations/auth/google/GoogleAuth.ts | 17 +++--- .../implementations/auth/oauth2/OAuth2.ts | 16 +++--- .../implementations/auth/okta/OktaAuth.ts | 16 +++--- .../DefaultAuthConnector.test.ts | 11 ++-- .../lib/AuthConnector/DefaultAuthConnector.ts | 52 +++++++++---------- packages/dev-utils/src/devApp/apiFactories.ts | 37 +++++++------ .../ProfileCatalog/ProfileCatalog.test.tsx | 6 ++- 11 files changed, 120 insertions(+), 114 deletions(-) diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index df023b80cb..eb8cfb92af 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -24,6 +24,8 @@ import { ErrorAlerter, featureFlagsApiRef, FeatureFlags, + discoveryApiRef, + UrlPatternDiscovery, GoogleAuth, GithubAuth, OAuth2, @@ -74,7 +76,10 @@ import { TravisCIApi, travisCIApiRef, } from '@roadiehq/backstage-plugin-travis-ci'; -import { GithubPullRequestsClient, githubPullRequestsApiRef } from '@roadiehq/backstage-plugin-github-pull-requests'; +import { + GithubPullRequestsClient, + githubPullRequestsApiRef, +} from '@roadiehq/backstage-plugin-github-pull-requests'; export const apis = (config: ConfigApi) => { // eslint-disable-next-line no-console @@ -85,6 +90,10 @@ export const apis = (config: ConfigApi) => { const builder = ApiRegistry.builder(); + const discoveryApi = builder.add( + discoveryApiRef, + UrlPatternDiscovery.compile(`${backendUrl}/{{ pluginId }}`), + ); const alertApi = builder.add(alertApiRef, new AlertApiForwarder()); const errorApi = builder.add( errorApiRef, @@ -116,8 +125,7 @@ export const apis = (config: ConfigApi) => { builder.add( googleAuthApiRef, GoogleAuth.create({ - backendUrl, - basePath: '/auth/', + discoveryApi, oauthRequestApi, }), ); @@ -125,8 +133,7 @@ export const apis = (config: ConfigApi) => { const githubAuthApi = builder.add( githubAuthApiRef, GithubAuth.create({ - backendUrl, - basePath: '/auth/', + discoveryApi, oauthRequestApi, }), ); @@ -134,8 +141,7 @@ export const apis = (config: ConfigApi) => { builder.add( oktaAuthApiRef, OktaAuth.create({ - backendUrl, - basePath: '/auth/', + discoveryApi, oauthRequestApi, }), ); @@ -143,8 +149,7 @@ export const apis = (config: ConfigApi) => { builder.add( gitlabAuthApiRef, GitlabAuth.create({ - backendUrl, - basePath: '/auth/', + discoveryApi, oauthRequestApi, }), ); @@ -152,8 +157,7 @@ export const apis = (config: ConfigApi) => { builder.add( auth0AuthApiRef, Auth0Auth.create({ - backendUrl, - basePath: '/auth/', + discoveryApi, oauthRequestApi, }), ); @@ -161,8 +165,7 @@ export const apis = (config: ConfigApi) => { builder.add( oauth2ApiRef, OAuth2.create({ - backendUrl, - basePath: '/auth/', + discoveryApi, oauthRequestApi, }), ); diff --git a/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts b/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts index e69733741f..505c283b71 100644 --- a/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts +++ b/packages/core-api/src/apis/implementations/auth/auth0/Auth0Auth.ts @@ -27,16 +27,17 @@ import { AuthRequestOptions, BackstageIdentity, } from '../../../definitions/auth'; -import { OAuthRequestApi, AuthProvider } from '../../../definitions'; +import { + OAuthRequestApi, + AuthProvider, + DiscoveryApi, +} from '../../../definitions'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager'; import { Observable } from '../../../../types'; type CreateOptions = { - // TODO(Following the words of Rugvip): These two should be grabbed from global config when available, they're not unique to Auth0Auth - backendUrl: string; - basePath: string; - + discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; environment?: string; @@ -67,15 +68,13 @@ class Auth0Auth BackstageIdentityApi, SessionStateApi { static create({ - backendUrl, - basePath, + discoveryApi, environment = 'development', provider = DEFAULT_PROVIDER, oauthRequestApi, }: CreateOptions) { const connector = new DefaultAuthConnector({ - backendUrl, - basePath, + discoveryApi, environment, provider, oauthRequestApi: oauthRequestApi, diff --git a/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts b/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts index 1ad3e08699..8b9f807cd8 100644 --- a/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/github/GithubAuth.ts @@ -25,7 +25,11 @@ import { BackstageIdentity, AuthRequestOptions, } from '../../../definitions/auth'; -import { OAuthRequestApi, AuthProvider } from '../../../definitions'; +import { + OAuthRequestApi, + AuthProvider, + DiscoveryApi, +} from '../../../definitions'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; import { AuthSessionStore, @@ -34,10 +38,7 @@ import { import { Observable } from '../../../../types'; type CreateOptions = { - // TODO(Rugvip): These two should be grabbed from global config when available, they're not unique to GithubAuth - backendUrl: string; - basePath: string; - + discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; environment?: string; @@ -62,15 +63,13 @@ const DEFAULT_PROVIDER = { class GithubAuth implements OAuthApi, SessionStateApi { static create({ - backendUrl, - basePath, + discoveryApi, environment = 'development', provider = DEFAULT_PROVIDER, oauthRequestApi, }: CreateOptions) { const connector = new DefaultAuthConnector({ - backendUrl, - basePath, + discoveryApi, environment, provider, oauthRequestApi: oauthRequestApi, diff --git a/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts b/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts index e612c4ca61..1734e930fa 100644 --- a/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/gitlab/GitlabAuth.ts @@ -25,15 +25,17 @@ import { BackstageIdentity, AuthRequestOptions, } from '../../../definitions/auth'; -import { OAuthRequestApi, AuthProvider } from '../../../definitions'; +import { + OAuthRequestApi, + AuthProvider, + DiscoveryApi, +} from '../../../definitions'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; import { StaticAuthSessionManager } from '../../../../lib/AuthSessionManager'; import { Observable } from '../../../../types'; type CreateOptions = { - backendUrl: string; - basePath: string; - + discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; environment?: string; @@ -58,15 +60,13 @@ const DEFAULT_PROVIDER = { class GitlabAuth implements OAuthApi, SessionStateApi { static create({ - backendUrl, - basePath, + discoveryApi, environment = 'development', provider = DEFAULT_PROVIDER, oauthRequestApi, }: CreateOptions) { const connector = new DefaultAuthConnector({ - backendUrl, - basePath, + discoveryApi, environment, provider, oauthRequestApi, diff --git a/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts b/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts index c6a21dbb20..fdf9d46ba8 100644 --- a/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/google/GoogleAuth.ts @@ -28,16 +28,17 @@ import { AuthRequestOptions, BackstageIdentity, } from '../../../definitions/auth'; -import { OAuthRequestApi, AuthProvider } from '../../../definitions'; +import { + OAuthRequestApi, + AuthProvider, + DiscoveryApi, +} from '../../../definitions'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager'; import { Observable } from '../../../../types'; type CreateOptions = { - // TODO(Rugvip): These two should be grabbed from global config when available, they're not unique to GoogleAuth - backendUrl: string; - basePath: string; - + discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; environment?: string; @@ -71,15 +72,13 @@ class GoogleAuth BackstageIdentityApi, SessionStateApi { static create({ - backendUrl, - basePath, + discoveryApi, environment = 'development', provider = DEFAULT_PROVIDER, oauthRequestApi, }: CreateOptions) { const connector = new DefaultAuthConnector({ - backendUrl, - basePath, + discoveryApi, environment, provider, oauthRequestApi: oauthRequestApi, diff --git a/packages/core-api/src/apis/implementations/auth/oauth2/OAuth2.ts b/packages/core-api/src/apis/implementations/auth/oauth2/OAuth2.ts index 4b6177bed9..cf582b3a13 100644 --- a/packages/core-api/src/apis/implementations/auth/oauth2/OAuth2.ts +++ b/packages/core-api/src/apis/implementations/auth/oauth2/OAuth2.ts @@ -19,7 +19,11 @@ import { DefaultAuthConnector } from '../../../../lib/AuthConnector'; import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; import { Observable } from '../../../../types'; -import { AuthProvider, OAuthRequestApi } from '../../../definitions'; +import { + AuthProvider, + OAuthRequestApi, + DiscoveryApi, +} from '../../../definitions'; import { AuthRequestOptions, BackstageIdentity, @@ -33,9 +37,7 @@ import { import { OAuth2Session } from './types'; type CreateOptions = { - backendUrl: string; - basePath: string; - + discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; environment?: string; @@ -64,15 +66,13 @@ const SCOPE_PREFIX = ''; class OAuth2 implements OAuthApi, OpenIdConnectApi, ProfileInfoApi, SessionStateApi { static create({ - backendUrl, - basePath, + discoveryApi, environment = 'development', provider = DEFAULT_PROVIDER, oauthRequestApi, }: CreateOptions) { const connector = new DefaultAuthConnector({ - backendUrl, - basePath, + discoveryApi, environment, provider, oauthRequestApi: oauthRequestApi, diff --git a/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts b/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts index f60b182118..1f804748f0 100644 --- a/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts +++ b/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.ts @@ -28,15 +28,17 @@ import { AuthRequestOptions, BackstageIdentity, } from '../../../definitions/auth'; -import { OAuthRequestApi, AuthProvider } from '../../../definitions'; +import { + OAuthRequestApi, + AuthProvider, + DiscoveryApi, +} from '../../../definitions'; import { SessionManager } from '../../../../lib/AuthSessionManager/types'; import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager'; import { Observable } from '../../../../types'; type CreateOptions = { - backendUrl: string; - basePath: string; - + discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; environment?: string; @@ -80,15 +82,13 @@ class OktaAuth BackstageIdentityApi, SessionStateApi { static create({ - backendUrl, - basePath, + discoveryApi, environment = 'development', provider = DEFAULT_PROVIDER, oauthRequestApi, }: CreateOptions) { const connector = new DefaultAuthConnector({ - backendUrl, - basePath, + discoveryApi, environment, provider, oauthRequestApi: oauthRequestApi, diff --git a/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.test.ts b/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.test.ts index b6c31405ae..5781130799 100644 --- a/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.test.ts +++ b/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.test.ts @@ -18,11 +18,12 @@ import ProviderIcon from '@material-ui/icons/AcUnit'; import { DefaultAuthConnector } from './DefaultAuthConnector'; import MockOAuthApi from '../../apis/implementations/OAuthRequestApi/MockOAuthApi'; import * as loginPopup from '../loginPopup'; +import { UrlPatternDiscovery } from '../../apis'; const anyFetch = fetch as any; const defaultOptions = { - backendUrl: 'http://my-origin', + discoveryApi: UrlPatternDiscovery.compile('http://my-host/api/{{pluginId}}'), environment: 'production', provider: { id: 'my-provider', @@ -115,7 +116,7 @@ describe('DefaultAuthConnector', () => { expect(popupSpy).toBeCalledTimes(1); expect(popupSpy.mock.calls[0][0]).toMatchObject({ url: - 'http://my-origin/api/auth/my-provider/start?scope=a%20b&env=production', + 'http://my-host/api/auth/my-provider/start?scope=a%20b&env=production', }); await expect(sessionPromise).resolves.toEqual({ @@ -141,9 +142,9 @@ describe('DefaultAuthConnector', () => { instantPopup: true, }); - expect(popupSpy).toBeCalledTimes(1); - await expect(sessionPromise).resolves.toBe('my-session'); + + expect(popupSpy).toBeCalledTimes(1); }); it('should use join func to join scopes', async () => { @@ -164,7 +165,7 @@ describe('DefaultAuthConnector', () => { expect(popupSpy).toBeCalledTimes(1); expect(popupSpy.mock.calls[0][0]).toMatchObject({ url: - 'http://my-origin/api/auth/my-provider/start?scope=-ab-&env=production', + 'http://my-host/api/auth/my-provider/start?scope=-ab-&env=production', }); }); }); diff --git a/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.ts b/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.ts index 41af7f4670..1c7e92daa8 100644 --- a/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.ts +++ b/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.ts @@ -15,21 +15,19 @@ */ import { AuthRequester } from '../../apis'; -import { OAuthRequestApi, AuthProvider } from '../../apis/definitions'; +import { + OAuthRequestApi, + AuthProvider, + DiscoveryApi, +} from '../../apis/definitions'; import { showLoginPopup } from '../loginPopup'; import { AuthConnector, CreateSessionOptions } from './types'; -const DEFAULT_BASE_PATH = '/api/auth/'; - type Options = { /** - * The base URL of the auth backend. + * DiscoveryApi instance used to locate the auth backend endpoint. */ - backendUrl?: string; - /** - * Base path of the auth requests, defaults to /api/auth/ - */ - basePath?: string; + discoveryApi: DiscoveryApi; /** * Environment hint passed on to auth backend, for example 'production' or 'development' */ @@ -64,8 +62,7 @@ function defaultJoinScopes(scopes: Set) { */ export class DefaultAuthConnector implements AuthConnector { - private readonly backendUrl: string; - private readonly basePath: string; + private readonly discoveryApi: DiscoveryApi; private readonly environment: string; private readonly provider: AuthProvider & { id: string }; private readonly joinScopesFunc: (scopes: Set) => string; @@ -74,8 +71,7 @@ export class DefaultAuthConnector constructor(options: Options) { const { - backendUrl = window.location.origin, - basePath = DEFAULT_BASE_PATH, + discoveryApi, environment, provider, joinScopes = defaultJoinScopes, @@ -88,8 +84,7 @@ export class DefaultAuthConnector onAuthRequest: scopes => this.showPopup(scopes), }); - this.backendUrl = backendUrl; - this.basePath = basePath; + this.discoveryApi = discoveryApi; this.environment = environment; this.provider = provider; this.joinScopesFunc = joinScopes; @@ -104,12 +99,15 @@ export class DefaultAuthConnector } async refreshSession(): Promise { - const res = await fetch(this.buildUrl('/refresh', { optional: true }), { - headers: { - 'x-requested-with': 'XMLHttpRequest', + const res = await fetch( + await this.buildUrl('/refresh', { optional: true }), + { + headers: { + 'x-requested-with': 'XMLHttpRequest', + }, + credentials: 'include', }, - credentials: 'include', - }).catch(error => { + ).catch(error => { throw new Error(`Auth refresh request failed, ${error}`); }); @@ -134,7 +132,7 @@ export class DefaultAuthConnector } async removeSession(): Promise { - const res = await fetch(this.buildUrl('/logout'), { + const res = await fetch(await this.buildUrl('/logout'), { method: 'POST', headers: { 'x-requested-with': 'XMLHttpRequest', @@ -153,13 +151,12 @@ export class DefaultAuthConnector private async showPopup(scopes: Set): Promise { const scope = this.joinScopesFunc(scopes); - const popupUrl = this.buildUrl('/start', { scope }); - const { origin } = new URL(this.backendUrl); + const popupUrl = await this.buildUrl('/start', { scope }); const payload = await showLoginPopup({ url: popupUrl, name: `${this.provider.title} Login`, - origin, + origin: new URL(popupUrl).origin, width: 450, height: 730, }); @@ -167,16 +164,17 @@ export class DefaultAuthConnector return await this.sessionTransform(payload); } - private buildUrl( + private async buildUrl( path: string, query?: { [key: string]: string | boolean | undefined }, - ): string { + ): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('auth'); const queryString = this.buildQueryString({ ...query, env: this.environment, }); - return `${this.backendUrl}${this.basePath}${this.provider.id}${path}${queryString}`; + return `${baseUrl}/${this.provider.id}${path}${queryString}`; } private buildQueryString(query?: { diff --git a/packages/dev-utils/src/devApp/apiFactories.ts b/packages/dev-utils/src/devApp/apiFactories.ts index 5acf64e0d0..4cf208f5d2 100644 --- a/packages/dev-utils/src/devApp/apiFactories.ts +++ b/packages/dev-utils/src/devApp/apiFactories.ts @@ -24,6 +24,8 @@ import { AlertApiForwarder, oauthRequestApiRef, OAuthRequestManager, + UrlPatternDiscovery, + discoveryApiRef, GoogleAuth, googleAuthApiRef, GithubAuth, @@ -58,46 +60,49 @@ export const oauthRequestApiFactory = createApiFactory({ factory: () => new OAuthRequestManager(), }); +export const discoveryApiFactory = createApiFactory({ + implements: discoveryApiRef, + deps: {}, + factory: () => + UrlPatternDiscovery.compile(`http://localhost:7000/{{ pluginId }}`), +}); + export const googleAuthApiFactory = createApiFactory({ implements: googleAuthApiRef, - deps: { oauthRequestApi: oauthRequestApiRef }, - factory: ({ oauthRequestApi }) => + deps: { discoveryApi: discoveryApiRef, oauthRequestApi: oauthRequestApiRef }, + factory: ({ discoveryApi, oauthRequestApi }) => GoogleAuth.create({ - backendUrl: 'http://localhost:7000', - basePath: '/auth/', + discoveryApi, oauthRequestApi, }), }); export const githubAuthApiFactory = createApiFactory({ implements: githubAuthApiRef, - deps: { oauthRequestApi: oauthRequestApiRef }, - factory: ({ oauthRequestApi }) => + deps: { discoveryApi: discoveryApiRef, oauthRequestApi: oauthRequestApiRef }, + factory: ({ discoveryApi, oauthRequestApi }) => GithubAuth.create({ - backendUrl: 'http://localhost:7000', - basePath: '/auth/', + discoveryApi, oauthRequestApi, }), }); export const gitlabAuthApiFactory = createApiFactory({ implements: gitlabAuthApiRef, - deps: { oauthRequestApi: oauthRequestApiRef }, - factory: ({ oauthRequestApi }) => + deps: { discoveryApi: discoveryApiRef, oauthRequestApi: oauthRequestApiRef }, + factory: ({ discoveryApi, oauthRequestApi }) => GitlabAuth.create({ - backendUrl: 'http://localhost:7000', - basePath: '/auth/', + discoveryApi, oauthRequestApi, }), }); export const auth0AuthApiFactory = createApiFactory({ implements: auth0AuthApiRef, - deps: { oauthRequestApi: oauthRequestApiRef }, - factory: ({ oauthRequestApi }) => + deps: { discoveryApi: discoveryApiRef, oauthRequestApi: oauthRequestApiRef }, + factory: ({ discoveryApi, oauthRequestApi }) => Auth0Auth.create({ - backendUrl: 'http://localhost:7000', - basePath: '/auth/', + discoveryApi, oauthRequestApi, }), }); diff --git a/plugins/gitops-profiles/src/components/ProfileCatalog/ProfileCatalog.test.tsx b/plugins/gitops-profiles/src/components/ProfileCatalog/ProfileCatalog.test.tsx index a502f6fb4e..06074ff84d 100644 --- a/plugins/gitops-profiles/src/components/ProfileCatalog/ProfileCatalog.test.tsx +++ b/plugins/gitops-profiles/src/components/ProfileCatalog/ProfileCatalog.test.tsx @@ -26,6 +26,7 @@ import { githubAuthApiRef, GithubAuth, OAuthRequestManager, + UrlPatternDiscovery, } from '@backstage/core'; import { gitOpsApiRef, GitOpsRestApi } from '../../api'; @@ -37,8 +38,9 @@ describe('ProfileCatalog', () => { [ githubAuthApiRef, GithubAuth.create({ - backendUrl: 'http://localhost:7000', - basePath: '/auth/', + discoveryApi: UrlPatternDiscovery.compile( + 'http://example.com/{{pluginId}}', + ), oauthRequestApi, }), ], From b253c56526929b2515abb75798d0fa03a44a8663 Mon Sep 17 00:00:00 2001 From: Chris Simmons Date: Fri, 21 Aug 2020 15:46:17 +1200 Subject: [PATCH 027/359] add microsoft azure auth provider + add to example app --- app-config.yaml | 13 + packages/app/src/apis.ts | 16 +- packages/app/src/identityProviders.ts | 7 + .../core-api/src/apis/definitions/auth.ts | 18 ++ .../src/apis/implementations/auth/index.ts | 1 + .../auth/microsoft/MicrosoftAuth.ts | 172 ++++++++++++ .../implementations/auth/microsoft/index.ts | 18 ++ .../implementations/auth/microsoft/types.ts | 28 ++ .../core/src/layout/Sidebar/UserSettings.tsx | 8 + plugins/auth-backend/README.md | 35 +++ plugins/auth-backend/package.json | 3 + .../auth-backend/src/providers/factories.ts | 2 + .../src/providers/microsoft/index.ts | 17 ++ .../src/providers/microsoft/provider.ts | 257 ++++++++++++++++++ yarn.lock | 159 +++++++++++ 15 files changed, 753 insertions(+), 1 deletion(-) create mode 100644 packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts create mode 100644 packages/core-api/src/apis/implementations/auth/microsoft/index.ts create mode 100644 packages/core-api/src/apis/implementations/auth/microsoft/types.ts create mode 100644 plugins/auth-backend/src/providers/microsoft/index.ts create mode 100644 plugins/auth-backend/src/providers/microsoft/provider.ts diff --git a/app-config.yaml b/app-config.yaml index 01e2ff33fe..5792ea2eb5 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -122,3 +122,16 @@ auth: domain: $secret: env: AUTH_AUTH0_DOMAIN + microsoft: + development: + appOrigin: "http://localhost:3000/" + secure: false + clientId: + $secret: + env: AUTH_AZURE_CLIENT_ID + clientSecret: + $secret: + env: AUTH_AZURE_CLIENT_SECRET + tenantId: + $secret: + env: AUTH_AZURE_TENANT_ID \ No newline at end of file diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index df023b80cb..cb2a65b64a 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -30,6 +30,7 @@ import { OktaAuth, GitlabAuth, Auth0Auth, + MicrosoftAuth, oauthRequestApiRef, OAuthRequestManager, googleAuthApiRef, @@ -38,6 +39,7 @@ import { oktaAuthApiRef, gitlabAuthApiRef, auth0AuthApiRef, + microsoftAuthApiRef, storageApiRef, WebStorage, } from '@backstage/core'; @@ -74,7 +76,10 @@ import { TravisCIApi, travisCIApiRef, } from '@roadiehq/backstage-plugin-travis-ci'; -import { GithubPullRequestsClient, githubPullRequestsApiRef } from '@roadiehq/backstage-plugin-github-pull-requests'; +import { + GithubPullRequestsClient, + githubPullRequestsApiRef, +} from '@roadiehq/backstage-plugin-github-pull-requests'; export const apis = (config: ConfigApi) => { // eslint-disable-next-line no-console @@ -122,6 +127,15 @@ export const apis = (config: ConfigApi) => { }), ); + builder.add( + microsoftAuthApiRef, + MicrosoftAuth.create({ + backendUrl, + basePath: '/auth/', + oauthRequestApi, + }), + ); + const githubAuthApi = builder.add( githubAuthApiRef, GithubAuth.create({ diff --git a/packages/app/src/identityProviders.ts b/packages/app/src/identityProviders.ts index e80b1d6fea..0ae98cf971 100644 --- a/packages/app/src/identityProviders.ts +++ b/packages/app/src/identityProviders.ts @@ -19,6 +19,7 @@ import { gitlabAuthApiRef, oktaAuthApiRef, githubAuthApiRef, + microsoftAuthApiRef, } from '@backstage/core'; export const providers = [ @@ -28,6 +29,12 @@ export const providers = [ message: 'Sign In using Google', apiRef: googleAuthApiRef, }, + { + id: 'microsoft-auth-provider', + title: 'Microsoft', + message: 'Sign In using Microsoft Azure AD', + apiRef: microsoftAuthApiRef, + }, { id: 'gitlab-auth-provider', title: 'Gitlab', diff --git a/packages/core-api/src/apis/definitions/auth.ts b/packages/core-api/src/apis/definitions/auth.ts index a1a408fece..b26b23ce08 100644 --- a/packages/core-api/src/apis/definitions/auth.ts +++ b/packages/core-api/src/apis/definitions/auth.ts @@ -277,6 +277,24 @@ export const auth0AuthApiRef = createApiRef< description: 'Provides authentication towards Auth0 APIs', }); +/** + * Provides authentication towards Microsoft APIs and identities. + * + * For more info and a full list of supported scopes, see: + * - https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent + * - https://docs.microsoft.com/en-us/graph/permissions-reference + */ +export const microsoftAuthApiRef = createApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionStateApi +>({ + id: 'core.auth.microsoft', + description: 'Provides authentication towards Microsoft APIs and identities', +}); + /** * Provides authentication for custom identity providers. */ diff --git a/packages/core-api/src/apis/implementations/auth/index.ts b/packages/core-api/src/apis/implementations/auth/index.ts index ce6e0d8570..a6d7e2c989 100644 --- a/packages/core-api/src/apis/implementations/auth/index.ts +++ b/packages/core-api/src/apis/implementations/auth/index.ts @@ -20,3 +20,4 @@ export * from './google'; export * from './oauth2'; export * from './okta'; export * from './auth0'; +export * from './microsoft'; diff --git a/packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts b/packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts new file mode 100644 index 0000000000..d4a70995e3 --- /dev/null +++ b/packages/core-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts @@ -0,0 +1,172 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 MicrosoftIcon from '@material-ui/icons/AcUnit'; +import { DefaultAuthConnector } from '../../../../lib/AuthConnector'; +import { MicrosoftSession } from './types'; + +import { + OAuthApi, + OpenIdConnectApi, + ProfileInfoApi, + ProfileInfo, + SessionStateApi, + SessionState, + BackstageIdentityApi, + AuthRequestOptions, + BackstageIdentity, +} from '../../../definitions/auth'; + +import { OAuthRequestApi, AuthProvider } from '../../../definitions'; +import { SessionManager } from '../../../../lib/AuthSessionManager/types'; +import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager'; +import { Observable } from '../../../../types'; + +type CreateOptions = { + backendUrl: string; + basePath: string; + + oauthRequestApi: OAuthRequestApi; + + environment?: string; + provider?: AuthProvider & { id: string }; +}; + +export type MicrosoftAuthResponse = { + providerInfo: { + accessToken: string; + idToken: string; + scope: string; + expiresInSeconds: number; + }; + profile: ProfileInfo; + backstageIdentity: BackstageIdentity; +}; + +const DEFAULT_PROVIDER = { + id: 'microsoft', + title: 'Microsoft', + icon: MicrosoftIcon, +}; + +class MicrosoftAuth + implements + OAuthApi, + OpenIdConnectApi, + ProfileInfoApi, + BackstageIdentityApi, + SessionStateApi { + static create({ + backendUrl, + basePath, + environment = 'development', + provider = DEFAULT_PROVIDER, + oauthRequestApi, + }: CreateOptions) { + const connector = new DefaultAuthConnector({ + backendUrl, + basePath, + environment, + provider, + oauthRequestApi: oauthRequestApi, + sessionTransform(res: MicrosoftAuthResponse): MicrosoftSession { + return { + ...res, + providerInfo: { + idToken: res.providerInfo.idToken, + accessToken: res.providerInfo.accessToken, + scopes: MicrosoftAuth.normalizeScopes(res.providerInfo.scope), + expiresAt: new Date( + Date.now() + res.providerInfo.expiresInSeconds * 1000, + ), + }, + }; + }, + }); + + const sessionManager = new RefreshingAuthSessionManager({ + connector, + defaultScopes: new Set([ + 'openid', + 'offline_access', + 'profile', + 'email', + 'User.Read', + ]), + sessionScopes: (session: MicrosoftSession) => session.providerInfo.scopes, + sessionShouldRefresh: (session: MicrosoftSession) => { + const expiresInSec = + (session.providerInfo.expiresAt.getTime() - Date.now()) / 1000; + return expiresInSec < 60 * 5; + }, + }); + + return new MicrosoftAuth(sessionManager); + } + + sessionState$(): Observable { + return this.sessionManager.sessionState$(); + } + + constructor( + private readonly sessionManager: SessionManager, + ) {} + + async getAccessToken( + scope?: string | string[], + options?: AuthRequestOptions, + ) { + const session = await this.sessionManager.getSession({ + ...options, + scopes: MicrosoftAuth.normalizeScopes(scope), + }); + return session?.providerInfo.accessToken ?? ''; + } + + async getIdToken(options: AuthRequestOptions = {}) { + const session = await this.sessionManager.getSession(options); + return session?.providerInfo.idToken ?? ''; + } + + async logout() { + await this.sessionManager.removeSession(); + } + + async getBackstageIdentity( + options: AuthRequestOptions = {}, + ): Promise { + const session = await this.sessionManager.getSession(options); + return session?.backstageIdentity; + } + + async getProfile(options: AuthRequestOptions = {}) { + const session = await this.sessionManager.getSession(options); + return session?.profile; + } + + static normalizeScopes(scopes?: string | string[]): Set { + if (!scopes) { + return new Set(); + } + + const scopeList = Array.isArray(scopes) + ? scopes + : scopes.split(/[\s|,]/).filter(Boolean); + + return new Set(scopeList); + } +} +export default MicrosoftAuth; diff --git a/packages/core-api/src/apis/implementations/auth/microsoft/index.ts b/packages/core-api/src/apis/implementations/auth/microsoft/index.ts new file mode 100644 index 0000000000..e3ae4ee4f1 --- /dev/null +++ b/packages/core-api/src/apis/implementations/auth/microsoft/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './types'; +export { default as MicrosoftAuth } from './MicrosoftAuth'; diff --git a/packages/core-api/src/apis/implementations/auth/microsoft/types.ts b/packages/core-api/src/apis/implementations/auth/microsoft/types.ts new file mode 100644 index 0000000000..6eaf92808a --- /dev/null +++ b/packages/core-api/src/apis/implementations/auth/microsoft/types.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ProfileInfo, BackstageIdentity } from '../../../definitions'; + +export type MicrosoftSession = { + providerInfo: { + idToken: string; + accessToken: string; + scopes: Set; + expiresAt: Date; + }; + profile: ProfileInfo; + backstageIdentity: BackstageIdentity; +}; diff --git a/packages/core/src/layout/Sidebar/UserSettings.tsx b/packages/core/src/layout/Sidebar/UserSettings.tsx index e69d63186d..fbfdea9085 100644 --- a/packages/core/src/layout/Sidebar/UserSettings.tsx +++ b/packages/core/src/layout/Sidebar/UserSettings.tsx @@ -21,6 +21,7 @@ import { identityApiRef, oauth2ApiRef, oktaAuthApiRef, + microsoftAuthApiRef, useApi, configApiRef, } from '@backstage/core-api'; @@ -60,6 +61,13 @@ export function SidebarUserSettings() { icon={Star} /> )} + {providers.includes('microsoft') && ( + + )} {providers.includes('github') && ( , + ) => { + got + .get('https://graph.microsoft.com/v1.0/me/photos/48x48/$value', { + encoding: 'binary', + responseType: 'buffer', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }) + .then(photoData => { + const photoURL = `data:image/jpeg;base64,${Buffer.from( + photoData.body, + ).toString('base64')}`; + const authResponse = MicrosoftAuthProvider.transformAuthResponse( + accessToken, + params, + rawProfile, + photoURL, + ); + done(undefined, authResponse, { refreshToken }); + }) + .catch(error => { + console.log( + `Error retrieving user photo from Microsoft Graph API: ${error}`, + ); + const authResponse = MicrosoftAuthProvider.transformAuthResponse( + accessToken, + params, + rawProfile, + ); + done(undefined, authResponse, { refreshToken }); + }); + }, + ); + } + + async start( + req: express.Request, + options: Record, + ): Promise { + return await executeRedirectStrategy(req, this._strategy, options); + } + + async handler( + req: express.Request, + ): Promise<{ response: OAuthResponse; refreshToken: string }> { + const { response, privateInfo } = await executeFrameHandlerStrategy< + OAuthResponse, + PrivateInfo + >(req, this._strategy); + + return { + response: await this.populateIdentity(response), + refreshToken: privateInfo.refreshToken, + }; + } + + async refresh(refreshToken: string, scope: string): Promise { + const { accessToken, params } = await executeRefreshTokenStrategy( + this._strategy, + refreshToken, + scope, + ); + + const profile = await executeFetchUserProfileStrategy( + this._strategy, + accessToken, + params.id_token, + ); + const photo = await this.getUserPhoto(accessToken); + if (photo) { + profile.picture = photo; + } + + return this.populateIdentity({ + providerInfo: { + accessToken, + idToken: params.id_token, + expiresInSeconds: params.expires_in, + scope: params.scope, + }, + profile, + }); + } + + private getUserPhoto(accessToken: string): Promise { + return new Promise(resolve => { + got + .get('https://graph.microsoft.com/v1.0/me/photos/48x48/$value', { + encoding: 'binary', + responseType: 'buffer', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }) + .then(photoData => { + const photoURL = `data:image/jpeg;base64,${Buffer.from( + photoData.body, + ).toString('base64')}`; + resolve(photoURL); + }) + .catch(error => { + console.log( + `Error retrieving user photo from Microsoft Graph API: ${error}`, + ); + resolve(); + }); + }); + } + + private async populateIdentity( + response: OAuthResponse, + ): Promise { + const { profile } = response; + + if (!profile.email) { + throw new Error('Microsoft profile contained no email'); + } + + // Like Google implementation, setting this to local part of email for now + const id = profile.email.split('@')[0]; + + return { ...response, backstageIdentity: { id } }; + } +} + +export function createMicrosoftProvider( + config: AuthProviderConfig, + _: string, + envConfig: Config, + _logger: Logger, + tokenIssuer: TokenIssuer, +) { + const providerId = 'microsoft'; + + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); + const tenantID = envConfig.getString('tenantId'); + + const callbackUrl = `${config.baseUrl}/${providerId}/handler/frame`; + const authorizationUrl = `https://login.microsoftonline.com/${tenantID}/oauth2/v2.0/authorize`; + const tokenUrl = `https://login.microsoftonline.com/${tenantID}/oauth2/v2.0/token`; + + const provider = new MicrosoftAuthProvider({ + clientId, + clientSecret, + callbackUrl, + authorizationUrl, + tokenUrl, + }); + + return OAuthProvider.fromConfig(config, provider, { + disableRefresh: false, + providerId, + tokenIssuer, + }); +} diff --git a/yarn.lock b/yarn.lock index 1a56d05251..8548561577 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3419,6 +3419,11 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@sindresorhus/is@^3.0.0": + version "3.1.1" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.1.tgz#6e39e4222add8b362da35720c9dd5d53345d5851" + integrity sha512-tLnujxFtfH7F+i5ghUfgGlJsvyCKvUnSMFMlWybFdX9/DdX8svb4Zwx1gV0gkkVCHXtmPSetoAR3QlKfOld6Tw== + "@sinonjs/commons@^1.7.0": version "1.7.1" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1" @@ -4302,6 +4307,13 @@ dependencies: defer-to-connect "^1.0.1" +"@szmarczak/http-timer@^4.0.5": + version "4.0.5" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" + integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== + dependencies: + defer-to-connect "^2.0.0" + "@testing-library/cypress@^6.0.0": version "6.0.0" resolved "https://registry.npmjs.org/@testing-library/cypress/-/cypress-6.0.0.tgz#935f7716e0e495f02fd753a42621e4d350097dce" @@ -4467,6 +4479,16 @@ "@types/connect" "*" "@types/node" "*" +"@types/cacheable-request@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" + integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + "@types/cheerio@^0.22.8": version "0.22.21" resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3" @@ -4739,6 +4761,11 @@ resolved "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b" integrity sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ== +"@types/http-cache-semantics@*": + version "4.0.0" + resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" + integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== + "@types/http-errors@^1.6.3": version "1.8.0" resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz#682477dbbbd07cd032731cb3b0e7eaee3d026b69" @@ -4833,6 +4860,13 @@ resolved "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw== +"@types/keyv@*": + version "3.1.1" + resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" + integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== + dependencies: + "@types/node" "*" + "@types/koa-compose@*": version "3.2.5" resolved "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d" @@ -4976,6 +5010,13 @@ "@types/passport" "*" "@types/passport-oauth2" "*" +"@types/passport-microsoft@^0.0.0": + version "0.0.0" + resolved "https://registry.npmjs.org/@types/passport-microsoft/-/passport-microsoft-0.0.0.tgz#ba71bccdd793711239d6b02e8d5953c21abc1c8d" + integrity sha512-jfkltRosn+P/+RoFMTl+mCyBTgPTFhjDEF832j7fmlYpuf+5yuzPLz7Rm5XMKN/Gqpro6myCyGPTuCc4yBQ2jQ== + dependencies: + "@types/passport-oauth2" "*" + "@types/passport-oauth2@*": version "1.4.9" resolved "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.9.tgz#134007c4b505a82548c9cb19094c5baeb2205c92" @@ -5150,6 +5191,13 @@ dependencies: "@types/node" "*" +"@types/responselike@*", "@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/rollup-plugin-peer-deps-external@^2.2.0": version "2.2.0" resolved "https://registry.npmjs.org/@types/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.0.tgz#eae7d8b9d27fa037f5bcaded24e389f85b81973c" @@ -7389,6 +7437,11 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-lookup@^5.0.3: + version "5.0.3" + resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz#049fdc59dffdd4fc285e8f4f82936591bd59fec3" + integrity sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w== + cacheable-request@^2.1.1: version "2.1.4" resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" @@ -7415,6 +7468,19 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +cacheable-request@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" + integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^2.0.0" + cachedir@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" @@ -9100,6 +9166,13 @@ decompress-response@^4.2.0: dependencies: mimic-response "^2.0.0" +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" @@ -9215,6 +9288,11 @@ defer-to-connect@^1.0.1: resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +defer-to-connect@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1" + integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg== + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -11853,6 +11931,23 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" +got@^11.5.2: + version "11.5.2" + resolved "https://registry.npmjs.org/got/-/got-11.5.2.tgz#772e3f3a06d9c7589c7c94dc3c83cdb31ddbf742" + integrity sha512-yUhpEDLeuGiGJjRSzEq3kvt4zJtAcjKmhIiwNp/eUs75tRlXfWcHo5tcBaMQtnjHWC7nQYT5HkY/l0QOQTkVww== + dependencies: + "@sindresorhus/is" "^3.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.1" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.0" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + got@^7.0.0: version "7.1.0" resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" @@ -12554,6 +12649,14 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http2-wrapper@^1.0.0-beta.5.0: + version "1.0.0-beta.5.2" + resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz#8b923deb90144aea65cf834b016a340fc98556f3" + integrity sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -14292,6 +14395,11 @@ json-buffer@3.0.0: resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -14547,6 +14655,13 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +keyv@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz#9fe703cb4a94d6d11729d320af033307efd02ee6" + integrity sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw== + dependencies: + json-buffer "3.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -15738,6 +15853,11 @@ mimic-response@^2.0.0: resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + min-document@^2.19.0: version "2.19.0" resolved "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" @@ -16870,6 +16990,11 @@ p-cancelable@^1.0.0: resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +p-cancelable@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" + integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== + p-each-series@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" @@ -17253,6 +17378,14 @@ passport-google-oauth20@^2.0.0: dependencies: passport-oauth2 "1.x.x" +passport-microsoft@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/passport-microsoft/-/passport-microsoft-0.1.0.tgz#dc72c1a38b294d74f4dc55fe93f52e25cb9aa5b4" + integrity sha512-0giBDgE1fnR5X84zJZkQ11hnKVrzEgViwRO6RGsormK9zTxFQmN/UHMTDbIpvhk989VqALewB6Pk1R5vNr3GHw== + dependencies: + passport-oauth2 "1.2.0" + pkginfo "0.2.x" + passport-oauth1@1.x.x: version "1.1.0" resolved "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.1.0.tgz#a7de988a211f9cf4687377130ea74df32730c918" @@ -17262,6 +17395,15 @@ passport-oauth1@1.x.x: passport-strategy "1.x.x" utils-merge "1.x.x" +passport-oauth2@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.2.0.tgz#49613a3eca85c7a1e65bf1019e2b6b80a10c8ac2" + integrity sha1-SWE6PsqFx6HmW/EBnitrgKEMisI= + dependencies: + oauth "0.9.x" + passport-strategy "1.x.x" + uid2 "0.0.x" + passport-oauth2@1.x.x, passport-oauth2@^1.4.0, passport-oauth2@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.5.0.tgz#64babbb54ac46a4dcab35e7f266ed5294e3c4108" @@ -18539,6 +18681,11 @@ quick-lru@^4.0.1: resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + raf-schd@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0" @@ -19682,6 +19829,11 @@ resize-observer-polyfill@^1.5.1: resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== +resolve-alpn@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz#745ad60b3d6aff4b4a48e01b8c0bdc70959e0e8c" + integrity sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -19745,6 +19897,13 @@ responselike@1.0.2, responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" +responselike@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== + dependencies: + lowercase-keys "^2.0.0" + restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" From d4a6bc55042cc94c8f791b8f4e1ed13f63ba5786 Mon Sep 17 00:00:00 2001 From: Chris Simmons Date: Fri, 21 Aug 2020 16:32:04 +1200 Subject: [PATCH 028/359] udpated env vars to match provider name --- app-config.yaml | 6 +++--- plugins/auth-backend/README.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 5792ea2eb5..8a0199e30a 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -128,10 +128,10 @@ auth: secure: false clientId: $secret: - env: AUTH_AZURE_CLIENT_ID + env: AUTH_MICROSOFT_CLIENT_ID clientSecret: $secret: - env: AUTH_AZURE_CLIENT_SECRET + env: AUTH_MICROSOFT_CLIENT_SECRET tenantId: $secret: - env: AUTH_AZURE_TENANT_ID \ No newline at end of file + env: AUTH_MICROSOFT_TENANT_ID \ No newline at end of file diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index 3f8b793503..e1d144c1e0 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -103,9 +103,9 @@ The secret value will then be displayed on the screen. **You will not be able to ```bash cd packages/backend -export AUTH_AZURE_CLIENT_ID=x -export AUTH_AZURE_CLIENT_SECRET=x -export AUTH_AZURE_TENANT_ID=x +export AUTH_MICROSOFT_CLIENT_ID=x +export AUTH_MICROSOFT_CLIENT_SECRET=x +export AUTH_MICROSOFT_TENANT_ID=x yarn start ``` From 8a317bfa1c068baf068106d8aca7986b677ecf09 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 20 Aug 2020 16:59:01 +0200 Subject: [PATCH 029/359] feat: support passing --inspect to backend:dev This allows to debug the nodejs instance. --- packages/cli/src/commands/backend/dev.ts | 2 ++ packages/cli/src/index.ts | 1 + packages/cli/src/lib/bundler/backend.ts | 6 +++++- packages/cli/src/lib/bundler/config.ts | 5 ++++- packages/cli/src/lib/bundler/types.ts | 4 +++- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/backend/dev.ts b/packages/cli/src/commands/backend/dev.ts index 91c08af201..a0752b94ed 100644 --- a/packages/cli/src/commands/backend/dev.ts +++ b/packages/cli/src/commands/backend/dev.ts @@ -25,9 +25,11 @@ export default async (cmd: Command) => { env: 'development', rootPaths: [paths.targetRoot, paths.targetDir], }); + const waitForExit = await serveBackend({ entry: 'src/index', checksEnabled: cmd.check, + inspectEnabled: cmd.inspect, config: ConfigReader.fromConfigs(appConfigs), appConfigs, }); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 4b9dc4bab7..55ef639b2a 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -52,6 +52,7 @@ const main = (argv: string[]) => { .command('backend:dev') .description('Start local development server with HMR for the backend') .option('--check', 'Enable type checking and linting') + .option('--inspect', 'Enable debugger') .action(lazyAction(() => import('./commands/backend/dev'), 'default')); program diff --git a/packages/cli/src/lib/bundler/backend.ts b/packages/cli/src/lib/bundler/backend.ts index ce821a585b..f5f233854f 100644 --- a/packages/cli/src/lib/bundler/backend.ts +++ b/packages/cli/src/lib/bundler/backend.ts @@ -19,7 +19,11 @@ import { createBackendConfig } from './config'; import { resolveBundlingPaths } from './paths'; import { ServeOptions } from './types'; -export async function serveBackend(options: ServeOptions) { +export async function serveBackend( + options: ServeOptions & { + inspectEnabled: boolean; + }, +) { const paths = resolveBundlingPaths(options); const config = createBackendConfig(paths, { ...options, diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 8049ce436d..e0640b5133 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -200,7 +200,10 @@ export function createBackendConfig( : '[name].[chunkhash:8].chunk.js', }, plugins: [ - new StartServerPlugin('main.js'), + new StartServerPlugin({ + name: 'main.js', + nodeArgs: options.inspectEnabled ? ['--inspect'] : undefined, + }), new webpack.HotModuleReplacementPlugin(), ...(checksEnabled ? [ diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index 5d0ba66a51..03d68d9bb8 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -25,7 +25,9 @@ export type BundlingOptions = { baseUrl: URL; }; -export type BackendBundlingOptions = Omit; +export type BackendBundlingOptions = Omit & { + inspectEnabled: boolean; +}; export type ServeOptions = BundlingPathsOptions & { checksEnabled: boolean; From d63f777ee0d2f460e716873d7a3f9dabd5071658 Mon Sep 17 00:00:00 2001 From: Kat Zhou <61153904+katz95@users.noreply.github.com> Date: Fri, 21 Aug 2020 09:46:02 +0200 Subject: [PATCH 030/359] new design header for design documentation --- docs/assets/dls/designheader-updated.png | Bin 0 -> 155351 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/assets/dls/designheader-updated.png diff --git a/docs/assets/dls/designheader-updated.png b/docs/assets/dls/designheader-updated.png new file mode 100644 index 0000000000000000000000000000000000000000..56c5a56abbd65b857d61dbed59146ab428318196 GIT binary patch literal 155351 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

l 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%0DfCYtS)BFsPATpVa8`B_2Q; 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 uP#ZM`-f7k|3!GjcZ~cr4yW1Ai4dzQGd+R0~nx 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

%Qd|*XYssfv5ScpVB2`hNKtfBKx|x`vGZX( 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 p&(wb#lK Date: Fri, 21 Aug 2020 09:47:50 +0200 Subject: [PATCH 031/359] Update design.md --- docs/dls/design.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dls/design.md b/docs/dls/design.md index c8a94fad4d..23ba84024a 100644 --- a/docs/dls/design.md +++ b/docs/dls/design.md @@ -3,7 +3,7 @@ id: design title: Design --- -![header](../assets/dls/designheader.png) +![header](../assets/dls/designheader-updated.png) Much like Backstage Open Source, this is a _living_ document! We'll keep this updated as we evolve our practices! @@ -116,7 +116,7 @@ components. If you’d like to help build up our design system, you can also add components we’ve designed to the Storybook as well. **[Figma](https://www.figma.com/@backstage)** - we're stoked to be using Figma -Community to share our design assets. You can duplicate our component library +Community to share our design assets. You can duplicate our UI Kit and design your own plugin for Backstage. **[Discord](https://discord.gg/EBHEGzX)** - all design questions should be From 5bf548ac808428dcc530cc51625dd6d072d12b38 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Fri, 21 Aug 2020 10:31:30 +0200 Subject: [PATCH 032/359] Show progress bar when loading on techdocs (#2065) --- plugins/techdocs/src/reader/components/Reader.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index 8e1e36595a..8ae3273e12 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { useApi } from '@backstage/core'; +import { useApi, Progress } from '@backstage/core'; import { useShadowDom } from '..'; import { useAsync } from 'react-use'; import { techdocsStorageApiRef } from '../../api'; @@ -131,5 +131,10 @@ export const Reader = ({ entityId }: Props) => { return ; } - return

; + return ( + <> + {loading ? : null} +
+ + ); }; From 6e3cb6c874b66f30c22b019fef6b0cb8f1ded953 Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Fri, 21 Aug 2020 10:51:37 +0200 Subject: [PATCH 033/359] =removing support for loading certs via file paths, adding host factory module, removing config section --- app-config.yaml | 7 -- .../src/service/lib/ServiceBuilderImpl.ts | 87 ++----------------- .../src/service/lib/hostFactory.ts | 56 +++++++++++- 3 files changed, 60 insertions(+), 90 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 0e94367aa1..a0233991f8 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -13,13 +13,6 @@ backend: database: client: sqlite3 connection: ':memory:' - # https: - # certificate: - # size: 2048 - # algorithm: sha256 - # days: 30 - # attributes: - # commonName: 'dfds.com' proxy: '/circleci/api': diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index e75ae8069f..899db248d6 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -21,7 +21,6 @@ import express, { Router } from 'express'; import helmet from 'helmet'; import * as http from 'http'; import * as https from 'https'; -import * as fs from 'fs'; import stoppable from 'stoppable'; import { Logger } from 'winston'; import { useHotCleanup } from '../../hot'; @@ -38,10 +37,7 @@ import { readHttpsSettings, HttpsSettings, } from './config'; -import { - createHttpServer, - createHttpsServer, -} from './hostFactory'; +import { createHttpServer, createHttpsServer } from './hostFactory'; const DEFAULT_PORT = 7000; // '' is express default, which listens to all interfaces @@ -149,83 +145,16 @@ export class ServiceBuilderImpl implements ServiceBuilder { reject(e); }); - let server: http.Server; - - if (httpsSettings) { - logger.info('Initializing https server'); - - const credentials: { key: string; cert: string } = { - key: '', - cert: '', - }; - const signingOptions: any = httpsSettings?.certificate; - - if (signingOptions?.algorithm !== undefined) { - logger.info('Generating self-signed certificate with attributes'); - - const certificateAttributes: Array = Object.entries( - signingOptions.attributes, - ).map(([name, value]) => ({ name, value })); - - // TODO: Create a type def for selfsigned. - const signatures = require('selfsigned').generate( - certificateAttributes, - { - algorithm: signingOptions?.algorithm, - keySize: signingOptions?.size || 2048, - days: signingOptions?.days || 30, - }, - ); - - logger.info( - 'Bootstrapping key and cert from self-signed certificate', - ); - - credentials.key = signatures.private; - credentials.cert = signatures.cert; - } else { - if (fs.existsSync(signingOptions?.key)) { - if (fs.lstatSync(signingOptions?.key).isFile()) { - logger.info('Bootstrapping key from file'); - - credentials.key = fs.readFileSync(signingOptions?.key).toString(); - } - } else { - logger.info('Bootstrapping key from config'); - - credentials.key = signingOptions?.key; - } - - if (fs.existsSync(signingOptions?.cert)) { - if (fs.lstatSync(signingOptions?.cert).isFile()) { - logger.info('Bootstrapping cert from file'); - - credentials.cert = fs - .readFileSync(signingOptions?.cert) - .toString(); - } - } else { - logger.info('Bootstrapping cert from config'); - - credentials.cert = signingOptions?.cert; - } - } - - if (credentials.key === '' || credentials.cert === '') { - throw new Error('Invalid credentials'); - } - - server = https.createServer(credentials, app) as http.Server; - } else { - logger.info('Initializing http server'); - - server = createHttpServer(app); - } + const server: http.Server = httpsSettings + ? createHttpsServer(app, httpsSettings, logger) + : createHttpServer(app, logger); const stoppableServer = stoppable( server.listen(port, host, () => { - logger.info(`Listening on ${host}:${port}`); - }), 0); + logger.info(`Listening on ${host}:${port}`); + }), + 0, + ); useHotCleanup(this.module, () => stoppableServer.stop((e: any) => { diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index 9b850431fb..778601e3c4 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -16,6 +16,8 @@ import express from 'express'; import * as http from 'http'; import * as https from 'https'; +import { Logger } from 'winston'; +import { HttpsSettings } from './config'; /** * Reads some base options out of a config object. @@ -31,11 +33,57 @@ import * as https from 'https'; * } * ``` */ -export function createHttpServer(app: express.Express): http.Server { +export function createHttpServer( + app: express.Express, + logger: Logger, +): http.Server { + logger.info('Initializing http server'); + return http.createServer(app); } +export function createHttpsServer( + app: express.Express, + httpsSettings: HttpsSettings, + logger: Logger, +): http.Server { + logger.info('Initializing https server'); -export function createHttpsServer(app: express.Express): http.Server { - return https.createServer(app); -} \ No newline at end of file + const credentials: { key: string; cert: string } = { + key: '', + cert: '', + }; + + const signingOptions: any = httpsSettings?.certificate; + + if (signingOptions?.algorithm !== undefined) { + logger.info('Generating self-signed certificate with attributes'); + + const certificateAttributes: Array = Object.entries( + signingOptions.attributes, + ).map(([name, value]) => ({ name, value })); + + // TODO: Create a type def for selfsigned. + const signatures = require('selfsigned').generate(certificateAttributes, { + algorithm: signingOptions?.algorithm, + keySize: signingOptions?.size || 2048, + days: signingOptions?.days || 30, + }); + + logger.info('Bootstrapping self-signed certificate'); + + credentials.key = signatures.private; + credentials.cert = signatures.cert; + } else { + logger.info('Bootstrapping cert from config'); + + credentials.key = signingOptions?.key; + credentials.cert = signingOptions?.cert; + } + + if (credentials.key === '' || credentials.cert === '') { + throw new Error('Invalid credentials'); + } + + return https.createServer(credentials, app) as http.Server; +} From f66eb57c10bf63bee4eeebd70f89565142c8ece0 Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Fri, 21 Aug 2020 10:58:17 +0200 Subject: [PATCH 034/359] =add inline documentation --- .../src/service/lib/hostFactory.ts | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index 778601e3c4..8fab2fd4ab 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -20,34 +20,37 @@ import { Logger } from 'winston'; import { HttpsSettings } from './config'; /** - * Reads some base options out of a config object. + * Creates a Http server instance based on an Express application. * - * @param config The root of a backend config object - * @returns A base options object + * @param app The Express application object + * @param logger Optional Winston logger object + * @returns A Http server instance * - * @example - * ```json - * { - * baseUrl: "http://localhost:7000", - * listen: "0.0.0.0:7000" - * } - * ``` */ export function createHttpServer( app: express.Express, - logger: Logger, + logger?: Logger, ): http.Server { - logger.info('Initializing http server'); + logger?.info('Initializing http server'); return http.createServer(app); } +/** + * Creates a Https server instance based on an Express application. + * + * @param app The Express application object + * @param httpsSettings HttpsSettings for self-signed certificate generation + * @param logger Optional Winston logger object + * @returns A Https server instance + * + */ export function createHttpsServer( app: express.Express, httpsSettings: HttpsSettings, - logger: Logger, + logger?: Logger, ): http.Server { - logger.info('Initializing https server'); + logger?.info('Initializing https server'); const credentials: { key: string; cert: string } = { key: '', @@ -57,7 +60,7 @@ export function createHttpsServer( const signingOptions: any = httpsSettings?.certificate; if (signingOptions?.algorithm !== undefined) { - logger.info('Generating self-signed certificate with attributes'); + logger?.info('Generating self-signed certificate with attributes'); const certificateAttributes: Array = Object.entries( signingOptions.attributes, @@ -70,12 +73,12 @@ export function createHttpsServer( days: signingOptions?.days || 30, }); - logger.info('Bootstrapping self-signed certificate'); + logger?.info('Bootstrapping self-signed certificate'); credentials.key = signatures.private; credentials.cert = signatures.cert; } else { - logger.info('Bootstrapping cert from config'); + logger?.info('Bootstrapping cert from config'); credentials.key = signingOptions?.key; credentials.cert = signingOptions?.cert; From d8947239ad187725d002876c3375d3c48cc13607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 21 Aug 2020 11:01:46 +0200 Subject: [PATCH 035/359] fix(core): bump react-syntax-highlighter to unbreak build --- packages/core/package.json | 2 +- yarn.lock | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 7333e0ce2a..3e33fec7a1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -50,7 +50,7 @@ "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-sparklines": "^1.7.0", - "react-syntax-highlighter": "^13.2.1", + "react-syntax-highlighter": "^13.5.1", "react-use": "^15.3.3" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 1a56d05251..c3527d6df6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18209,14 +18209,14 @@ pretty-hrtime@^1.0.3: resolved "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= -prismjs@^1.17.1: +prismjs@^1.17.1, prismjs@^1.21.0, prismjs@~1.21.0: version "1.21.0" resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.21.0.tgz#36c086ec36b45319ec4218ee164c110f9fc015a3" integrity sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw== optionalDependencies: clipboard "^2.0.0" -prismjs@^1.20.0, prismjs@^1.8.4, prismjs@~1.20.0: +prismjs@^1.8.4: version "1.20.0" resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.20.0.tgz#9b685fc480a3514ee7198eac6a3bf5024319ff03" integrity sha512-AEDjSrVNkynnw6A+B1DsFkd6AVdTnp+/WoUixFRULlCLZVRZlVQMVWio/16jv7G1FscUxQxOQhWwApgbnxr6kQ== @@ -19070,16 +19070,16 @@ react-syntax-highlighter@^11.0.2: prismjs "^1.8.4" refractor "^2.4.1" -react-syntax-highlighter@^13.2.1: - version "13.2.1" - resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.2.1.tgz#3d5a3b655cd85ce06b9508e0365d01ef9b8560bb" - integrity sha512-O/AF/ll4I3Dp+2WuKbnF5yKG4b1BUncqcpTW6MIBDJPr2eGKqlNGS3Nv+lyCFtAIHx8N+/ktti8qH14Q5VjjcA== +react-syntax-highlighter@^13.5.1: + version "13.5.1" + resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.1.tgz#f21737cf6d582474a0f18b06b52613f4349c0e64" + integrity sha512-VVYTnFXF55WMRGdr3QNEzAzcypFZqH45kS7rqh90+AFeNGtui8/gV5AIOIJjwTsuP2UxcO9qvEq94Jq9BYFUhw== dependencies: "@babel/runtime" "^7.3.1" highlight.js "^10.1.1" lowlight "^1.14.0" - prismjs "^1.20.0" - refractor "^3.0.0" + prismjs "^1.21.0" + refractor "^3.1.0" react-test-renderer@^16.13.1: version "16.13.1" @@ -19411,14 +19411,14 @@ refractor@^2.4.1: parse-entities "^1.1.2" prismjs "~1.17.0" -refractor@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/refractor/-/refractor-3.0.0.tgz#7c8072eaf49dbc1b333e7acc64fb52a1c9b17c75" - integrity sha512-eCGK/oP4VuyW/ERqjMZRZHxl2QsztbkedkYy/SxqE/+Gh1gLaAF17tWIOcVJDiyGhar1NZy/0B9dFef7J0+FDw== +refractor@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/refractor/-/refractor-3.1.0.tgz#b05a43c8a1b4fccb30001ffcbd5cd781f7f06f78" + integrity sha512-bN8GvY6hpeXfC4SzWmYNQGLLF2ZakRDNBkgCL0vvl5hnpMrnyURk8Mv61v6pzn4/RBHzSWLp44SzMmVHqMGNww== dependencies: hastscript "^5.0.0" parse-entities "^2.0.0" - prismjs "~1.20.0" + prismjs "~1.21.0" regenerate-unicode-properties@^8.2.0: version "8.2.0" From 5bffcb108d36939fe43e54f9330ab560702b4e5f Mon Sep 17 00:00:00 2001 From: "toban@dfds.com" Date: Fri, 21 Aug 2020 11:03:07 +0200 Subject: [PATCH 036/359] =removing unused imports --- packages/backend-common/src/service/lib/ServiceBuilderImpl.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index 899db248d6..4669c00e68 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -20,7 +20,6 @@ import cors from 'cors'; import express, { Router } from 'express'; import helmet from 'helmet'; import * as http from 'http'; -import * as https from 'https'; import stoppable from 'stoppable'; import { Logger } from 'winston'; import { useHotCleanup } from '../../hot'; From 15e334a584013ee22e9f5b4335427edaf723473f Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Thu, 20 Aug 2020 16:40:25 +0200 Subject: [PATCH 037/359] Release v0.1.1-alpha.19 --- CHANGELOG.md | 2 ++ microsite/package.json | 2 +- packages/app/package.json | 44 ++++++++++++------------- packages/backend-common/package.json | 10 +++--- packages/backend/package.json | 28 ++++++++-------- packages/catalog-model/package.json | 6 ++-- packages/cli-common/package.json | 4 +-- packages/cli/package.json | 8 ++--- packages/config-loader/package.json | 4 +-- packages/config/package.json | 2 +- packages/core-api/package.json | 10 +++--- packages/core/package.json | 12 +++---- packages/create-app/package.json | 4 +-- packages/dev-utils/package.json | 10 +++--- packages/docgen/package.json | 2 +- packages/storybook/package.json | 4 +-- packages/techdocs-cli/package.json | 4 +-- packages/test-utils-core/package.json | 2 +- packages/test-utils/package.json | 10 +++--- packages/theme/package.json | 4 +-- plugins/api-docs/package.json | 16 ++++----- plugins/auth-backend/package.json | 8 ++--- plugins/catalog-backend/package.json | 10 +++--- plugins/catalog/package.json | 26 +++++++-------- plugins/circleci/package.json | 10 +++--- plugins/explore/package.json | 12 +++---- plugins/github-actions/package.json | 16 ++++----- plugins/gitops-profiles/package.json | 10 +++--- plugins/graphiql/package.json | 12 +++---- plugins/graphql/package.json | 6 ++-- plugins/identity-backend/package.json | 6 ++-- plugins/jenkins/package.json | 12 +++---- plugins/lighthouse/package.json | 12 +++---- plugins/newrelic/package.json | 10 +++--- plugins/proxy-backend/package.json | 8 ++--- plugins/register-component/package.json | 14 ++++---- plugins/rollbar-backend/package.json | 8 ++--- plugins/rollbar/package.json | 12 +++---- plugins/scaffolder-backend/package.json | 10 +++--- plugins/scaffolder/package.json | 16 ++++----- plugins/sentry-backend/package.json | 6 ++-- plugins/sentry/package.json | 10 +++--- plugins/tech-radar/package.json | 12 +++---- plugins/techdocs-backend/package.json | 10 +++--- plugins/techdocs/package.json | 18 +++++----- plugins/welcome/package.json | 10 +++--- 46 files changed, 237 insertions(+), 235 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99f03e3fa5..d295dc38e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re > Collect changes for the next release below +## v0.1.1-alpha.19 + ### @backstage/create-app - Many plugins have been added to the catalog and will for now be required to be added to separate apps as well. This will be solved as [#1536](https://github.com/spotify/backstage/issues/1536) gets sorted out, but for now you may need to install some plugins just to get pages to work. diff --git a/microsite/package.json b/microsite/package.json index dd8957245a..bcf28d7fe5 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -1,5 +1,5 @@ { - "version": "1.0.0", + "version": "0.1.1-alpha.19", "name": "backstage-microsite", "license": "Apache-2.0", "private": true, diff --git a/packages/app/package.json b/packages/app/package.json index 126913b32a..ad4aa3524c 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,29 +1,29 @@ { "name": "example-app", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": true, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/plugin-api-docs": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/plugin-circleci": "^0.1.1-alpha.18", - "@backstage/plugin-explore": "^0.1.1-alpha.18", - "@backstage/plugin-github-actions": "^0.1.1-alpha.18", - "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.18", - "@backstage/plugin-graphiql": "^0.1.1-alpha.18", - "@backstage/plugin-jenkins": "^0.1.1-alpha.18", - "@backstage/plugin-lighthouse": "^0.1.1-alpha.18", - "@backstage/plugin-newrelic": "^0.1.1-alpha.18", - "@backstage/plugin-register-component": "^0.1.1-alpha.18", - "@backstage/plugin-rollbar": "^0.1.1-alpha.18", - "@backstage/plugin-scaffolder": "^0.1.1-alpha.18", - "@backstage/plugin-sentry": "^0.1.1-alpha.18", - "@backstage/plugin-tech-radar": "^0.1.1-alpha.18", - "@backstage/plugin-techdocs": "^0.1.1-alpha.18", - "@backstage/plugin-welcome": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/plugin-api-docs": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/plugin-circleci": "^0.1.1-alpha.19", + "@backstage/plugin-explore": "^0.1.1-alpha.19", + "@backstage/plugin-github-actions": "^0.1.1-alpha.19", + "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.19", + "@backstage/plugin-graphiql": "^0.1.1-alpha.19", + "@backstage/plugin-jenkins": "^0.1.1-alpha.19", + "@backstage/plugin-lighthouse": "^0.1.1-alpha.19", + "@backstage/plugin-newrelic": "^0.1.1-alpha.19", + "@backstage/plugin-register-component": "^0.1.1-alpha.19", + "@backstage/plugin-rollbar": "^0.1.1-alpha.19", + "@backstage/plugin-scaffolder": "^0.1.1-alpha.19", + "@backstage/plugin-sentry": "^0.1.1-alpha.19", + "@backstage/plugin-tech-radar": "^0.1.1-alpha.19", + "@backstage/plugin-techdocs": "^0.1.1-alpha.19", + "@backstage/plugin-welcome": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.0", diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 5cf2ee95e5..018c7616f4 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", - "@backstage/config-loader": "^0.1.1-alpha.18", + "@backstage/cli-common": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", + "@backstage/config-loader": "^0.1.1-alpha.19", "@types/cors": "^2.8.6", "@types/express": "^4.17.6", "compression": "^1.7.4", @@ -54,7 +54,7 @@ } }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/compression": "^1.7.0", "@types/http-errors": "^1.6.3", "@types/morgan": "^1.9.0", diff --git a/packages/backend/package.json b/packages/backend/package.json index c80fbc7c38..99f0cf3ece 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "dist/index.cjs.js", "types": "src/index.ts", "private": true, @@ -18,18 +18,18 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", - "@backstage/plugin-auth-backend": "^0.1.1-alpha.18", - "@backstage/plugin-catalog-backend": "^0.1.1-alpha.18", - "@backstage/plugin-graphql-backend": "^0.1.1-alpha.18", - "@backstage/plugin-identity-backend": "^0.1.1-alpha.18", - "@backstage/plugin-proxy-backend": "^0.1.1-alpha.18", - "@backstage/plugin-rollbar-backend": "^0.1.1-alpha.18", - "@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.18", - "@backstage/plugin-sentry-backend": "^0.1.1-alpha.18", - "@backstage/plugin-techdocs-backend": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", + "@backstage/plugin-auth-backend": "^0.1.1-alpha.19", + "@backstage/plugin-catalog-backend": "^0.1.1-alpha.19", + "@backstage/plugin-graphql-backend": "^0.1.1-alpha.19", + "@backstage/plugin-identity-backend": "^0.1.1-alpha.19", + "@backstage/plugin-proxy-backend": "^0.1.1-alpha.19", + "@backstage/plugin-rollbar-backend": "^0.1.1-alpha.19", + "@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.19", + "@backstage/plugin-sentry-backend": "^0.1.1-alpha.19", + "@backstage/plugin-techdocs-backend": "^0.1.1-alpha.19", "@octokit/rest": "^18.0.0", "dockerode": "^3.2.0", "express": "^4.17.1", @@ -40,7 +40,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/dockerode": "^2.5.32", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5", diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 248f2765e4..54b3d3a621 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/config": "^0.1.1-alpha.19", "@types/json-schema": "^7.0.5", "@types/yup": "^0.28.2", "json-schema": "^0.2.5", @@ -29,7 +29,7 @@ "yup": "^0.29.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "@types/jest": "^26.0.7", "@types/lodash": "^4.14.151", diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index 092a2a5167..80cc665426 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli-common", "description": "Common functionality used by cli, backend, and create-app", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "main": "src/index.ts", "types": "src/index.ts", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/jest": "^26.0.7", "@types/node": "^12.0.0" }, diff --git a/packages/cli/package.json b/packages/cli/package.json index 3c9f85ac30..e5a8eafc81 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public" @@ -29,9 +29,9 @@ "backstage-cli": "bin/backstage-cli" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", - "@backstage/config-loader": "^0.1.1-alpha.18", + "@backstage/cli-common": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", + "@backstage/config-loader": "^0.1.1-alpha.19", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^3.18.5", "@lerna/project": "^3.18.0", diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index a2d7bbbc59..f5873c3c9d 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/config": "^0.1.1-alpha.19", "fs-extra": "^9.0.0", "yaml": "^1.9.2", "yup": "^0.29.1" diff --git a/packages/config/package.json b/packages/config/package.json index 9c8c81d31b..acbc690625 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config", "description": "Config API used by Backstage core, backend, and CLI", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-api/package.json b/packages/core-api/package.json index 9ce1290a73..7b2087630e 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-api", "description": "Internal Core API used by Backstage plugins and apps", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -29,8 +29,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/config": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@types/react": "^16.9", @@ -41,8 +41,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/test-utils-core": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/test-utils-core": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/packages/core/package.json b/packages/core/package.json index 3e33fec7a1..5b59ee3dff 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core", "description": "Core API used by Backstage plugins and apps", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.18", - "@backstage/core-api": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/config": "^0.1.1-alpha.19", + "@backstage/core-api": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -54,8 +54,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 6a1a3b60b5..ae62bc518a 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "Create app package for Backstage", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public" @@ -27,7 +27,7 @@ "start": "nodemon --" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.18", + "@backstage/cli-common": "^0.1.1-alpha.19", "chalk": "^4.0.0", "commander": "^4.1.1", "fs-extra": "^9.0.0", diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 7eab350990..ba82447ea4 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@testing-library/jest-dom": "^5.10.1", diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 4b832afdfd..446f57f3dc 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -1,7 +1,7 @@ { "name": "docgen", "description": "Tool for generating API Documentation for itself", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": true, "homepage": "https://backstage.io", "repository": { diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 3a928b044e..8660c0e8d8 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -1,6 +1,6 @@ { "name": "storybook", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "description": "Storybook build for core package", "private": true, "scripts": { @@ -14,7 +14,7 @@ ] }, "dependencies": { - "@backstage/theme": "^0.1.1-alpha.18" + "@backstage/theme": "^0.1.1-alpha.19" }, "devDependencies": { "@storybook/addon-actions": "^5.3.17", diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 2e5d1e3958..a5951fd770 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "CLI for running TechDocs locally.", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public" @@ -44,7 +44,7 @@ "ext": "ts" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "commander": "^5.1.0", "fs-extra": "^9.0.1", "http-proxy": "^1.18.1", diff --git a/packages/test-utils-core/package.json b/packages/test-utils-core/package.json index 638513af5c..821a94b1aa 100644 --- a/packages/test-utils-core/package.json +++ b/packages/test-utils-core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils-core", "description": "Utilities to test Backstage core", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 35bbb7d0b0..e336835592 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/core-api": "^0.1.1-alpha.18", - "@backstage/test-utils-core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/core-api": "^0.1.1-alpha.19", + "@backstage/test-utils-core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/packages/theme/package.json b/packages/theme/package.json index f4eafa58ff..f98a2cb762 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/theme", "description": "material-ui theme for use with Backstage.", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "@material-ui/core": "^4.9.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18" + "@backstage/cli": "^0.1.1-alpha.19" }, "files": [ "dist" diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index c8a6b5732d..342d0aa8ca 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@kyma-project/asyncapi-react": "^0.11.0", "@material-icons/font": "^1.0.2", "@material-ui/core": "^4.9.1", @@ -37,9 +37,9 @@ "swagger-ui-react": "^3.31.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 65111e599c..7771434750 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "body-parser": "^1.19.0", "compression": "^1.7.4", @@ -48,7 +48,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/jwt-decode": "2.2.1", diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 7dedbe62cd..5692ff992e 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ "mock-data:local": "./scripts/mock-data-local.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", @@ -41,7 +41,7 @@ "yup": "^0.29.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/lodash": "^4.14.151", "@types/node-fetch": "^2.5.7", "@types/supertest": "^2.0.8", diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 44b3a7b7ee..1e10e91478 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,15 +21,15 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/plugin-api-docs": "^0.1.1-alpha.18", - "@backstage/plugin-github-actions": "^0.1.1-alpha.18", - "@backstage/plugin-jenkins": "^0.1.1-alpha.18", - "@backstage/plugin-scaffolder": "^0.1.1-alpha.18", - "@backstage/plugin-sentry": "^0.1.1-alpha.18", - "@backstage/plugin-techdocs": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/plugin-api-docs": "^0.1.1-alpha.19", + "@backstage/plugin-github-actions": "^0.1.1-alpha.19", + "@backstage/plugin-jenkins": "^0.1.1-alpha.19", + "@backstage/plugin-scaffolder": "^0.1.1-alpha.19", + "@backstage/plugin-sentry": "^0.1.1-alpha.19", + "@backstage/plugin-techdocs": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -42,9 +42,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 4fb7a28124..b70230364f 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-circleci", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "postpack": "backstage-cli postpack" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,8 +36,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index e7783675ae..9b729d7a82 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 8fcc4591d2..db35022fae 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-actions", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,11 +22,11 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/core-api": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/core-api": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -39,8 +39,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 5815386014..a2a7900d52 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gitops-profiles", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,8 +32,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 27437d0318..94a755bb17 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "private": false, "publishConfig": { "access": "public", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -43,9 +43,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphql/package.json b/plugins/graphql/package.json index 02dc9d3608..f41a4540ec 100644 --- a/plugins/graphql/package.json +++ b/plugins/graphql/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-graphql-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "apollo-server": "^2.16.0", "apollo-server-express": "^2.16.0", @@ -32,7 +32,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/supertest": "^2.0.8", "eslint-plugin-graphql": "^4.0.0", "msw": "^0.20.5", diff --git a/plugins/identity-backend/package.json b/plugins/identity-backend/package.json index c5c3bffc9b..8b16d85827 100644 --- a/plugins/identity-backend/package.json +++ b/plugins/identity-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-identity-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "compression": "^1.7.4", "cors": "^2.8.5", @@ -33,7 +33,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "jest-fetch-mock": "^3.0.3" }, "files": [ diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index dce577801e..2e8b89bea3 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,8 +35,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index cbf4a77f7e..bcd34d32de 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-lighthouse", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -33,9 +33,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 5b1b5d54ce..b6e066bbc0 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -31,8 +31,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 023fc81cf2..4a37ab8a45 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,8 +19,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", @@ -34,7 +34,7 @@ "yup": "^0.29.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/node-fetch": "^2.5.7", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 02fd3fe0ed..c13121de66 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-register-component", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,8 +36,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index 4b4a45b9dd..a52abf91b9 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "axios": "^0.19.2", "camelcase-keys": "^6.2.2", @@ -37,7 +37,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/supertest": "^2.0.8", "jest-fetch-mock": "^3.0.3", "supertest": "^4.0.2" diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 1ca02cb528..5eeeacc505 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index fc956c9832..7112d0f49d 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@octokit/rest": "^18.0.0", "@types/dockerode": "^2.5.32", "@types/express": "^4.17.6", @@ -43,7 +43,7 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@octokit/types": "^5.0.1", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index c0376a3594..8dec795c3d 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,9 +41,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/sentry-backend/package.json b/plugins/sentry-backend/package.json index e199dd2f1d..8850886493 100644 --- a/plugins/sentry-backend/package.json +++ b/plugins/sentry-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", "@types/express": "^4.17.6", "axios": "^0.19.2", "compression": "^1.7.4", @@ -34,7 +34,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "jest-fetch-mock": "^3.0.3" }, "files": [ diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 3a6cf54199..365a394331 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -34,8 +34,8 @@ "timeago.js": "^4.0.2" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index b2a7be7341..2e788e31e8 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-radar", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/test-utils-core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/test-utils-core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,8 +35,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 8a5a9fd10a..6a24ce3e8f 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.18", - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/config": "^0.1.1-alpha.18", + "@backstage/backend-common": "^0.1.1-alpha.19", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/config": "^0.1.1-alpha.19", "@types/dockerode": "^2.5.34", "@types/express": "^4.17.6", "dockerode": "^3.2.1", @@ -35,7 +35,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", "@types/node-fetch": "^2.5.7", "supertest": "^4.0.2" }, diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 03f1d6053b..d5416c7329 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,12 +22,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.18", - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/core-api": "^0.1.1-alpha.18", - "@backstage/plugin-catalog": "^0.1.1-alpha.18", - "@backstage/test-utils": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/catalog-model": "^0.1.1-alpha.19", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/core-api": "^0.1.1-alpha.19", + "@backstage/plugin-catalog": "^0.1.1-alpha.19", + "@backstage/test-utils": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -40,8 +40,8 @@ "sanitize-html": "^1.27.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 2c0c838de8..b832c5d37a 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-welcome", - "version": "0.1.1-alpha.18", + "version": "0.1.1-alpha.19", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.18", - "@backstage/theme": "^0.1.1-alpha.18", + "@backstage/core": "^0.1.1-alpha.19", + "@backstage/theme": "^0.1.1-alpha.19", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,8 +32,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.18", - "@backstage/dev-utils": "^0.1.1-alpha.18", + "@backstage/cli": "^0.1.1-alpha.19", + "@backstage/dev-utils": "^0.1.1-alpha.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", From 613c06bdd6101b45ce3feb3329cde22796f631d9 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Fri, 21 Aug 2020 12:11:03 +0200 Subject: [PATCH 038/359] Techdocs: Updates preparers to make it work in actual usecases (#1957) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature(techdocs): JIT generation of techdocs * Fix linting issues * Add Techdocs tab to entity page * Added missing dep * Added missing dep * Removed duplicate dep * Better tab navigation * Update label on entity page to say docs * Fix lint issue * Move building of docs from entity to a function * feature(techdocs): JIT generation of techdocs * Fix linting issues * Add Techdocs tab to entity page * Added missing dep * Added missing dep * Removed duplicate dep * Better tab navigation * Update label on entity page to say docs * Fix lint issue * Move building of docs from entity to a function * attempt to add back react as a dep * Fixed failing test * fix(techdocs): Hopefully fixed tests * Fixed another failing test * Initial apiRef for techdocs storage * Cleaned up some code in reader * test(headertabs): add tests to HeaderTabs component * fix(headertabs): change tab mock id * fix(techdocs-generator): fix problem with macOS symlink to tmp folder * fix(lint): remove unused configApiRef * wip * Ongoing cleanups * Cleanups and fix tests * Initial github preparer. Doesn't work properly yet * Updated dir preparer to handle github managed-by-location and added github preparer * Removed old file * Fixed feedback and added better logging to techdocs build process * Updated create-app template Co-authored-by: Fredrik Adelöw Co-authored-by: Emma Indal --- packages/backend/src/plugins/techdocs.ts | 17 +- .../packages/backend/src/plugins/techdocs.ts | 6 +- .../documented-component.yaml | 2 +- plugins/techdocs-backend/package.json | 2 + .../src/service/router.test.ts | 3 +- .../techdocs-backend/src/service/router.ts | 18 +- .../src/service/standaloneServer.ts | 6 +- .../src/techdocs/stages/generate/techdocs.ts | 44 +++-- .../src/techdocs/stages/prepare/dir.test.ts | 7 +- .../src/techdocs/stages/prepare/dir.ts | 79 ++++++++- .../src/techdocs/stages/prepare/github.ts | 71 ++++++++ .../src/techdocs/stages/prepare/helpers.ts | 12 +- .../src/techdocs/stages/prepare/index.ts | 1 + .../src/techdocs/stages/prepare/preparers.ts | 11 +- .../src/techdocs/stages/publish/local.test.ts | 7 +- .../src/techdocs/stages/publish/local.ts | 21 ++- yarn.lock | 160 +++++++++++++++++- 17 files changed, 413 insertions(+), 54 deletions(-) create mode 100644 plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts diff --git a/packages/backend/src/plugins/techdocs.ts b/packages/backend/src/plugins/techdocs.ts index 812a56cd9f..7364cc4d83 100644 --- a/packages/backend/src/plugins/techdocs.ts +++ b/packages/backend/src/plugins/techdocs.ts @@ -20,22 +20,27 @@ import { Preparers, Generators, LocalPublish, - TechdocsGenerator + TechdocsGenerator, + GithubPreparer, } from '@backstage/plugin-techdocs-backend'; import { PluginEnvironment } from '../types'; import Docker from 'dockerode'; -export default async function createPlugin({ logger, config }: PluginEnvironment) { +export default async function createPlugin({ + logger, + config, +}: PluginEnvironment) { const generators = new Generators(); - const techdocsGenerator = new TechdocsGenerator(); + const techdocsGenerator = new TechdocsGenerator(logger); generators.register('techdocs', techdocsGenerator); - const directoryPreparer = new DirectoryPreparer(); const preparers = new Preparers(); - + const githubPreparer = new GithubPreparer(logger); + const directoryPreparer = new DirectoryPreparer(logger); preparers.register('dir', directoryPreparer); + preparers.register('github', githubPreparer); - const publisher = new LocalPublish(); + const publisher = new LocalPublish(logger); const dockerClient = new Docker(); diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts index 8c5144f50c..a7b713fcb6 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts @@ -14,15 +14,15 @@ export default async function createPlugin({ config, }: PluginEnvironment) { const generators = new Generators(); - const techdocsGenerator = new TechdocsGenerator(); + const techdocsGenerator = new TechdocsGenerator(logger); generators.register('techdocs', techdocsGenerator); - const directoryPreparer = new DirectoryPreparer(); + const directoryPreparer = new DirectoryPreparer(logger); const preparers = new Preparers(); preparers.register('dir', directoryPreparer); - const publisher = new LocalPublish(); + const publisher = new LocalPublish(logger); const dockerClient = new Docker(); diff --git a/plugins/techdocs-backend/examples/documented-component/documented-component.yaml b/plugins/techdocs-backend/examples/documented-component/documented-component.yaml index d5a1214b15..045ff89013 100644 --- a/plugins/techdocs-backend/examples/documented-component/documented-component.yaml +++ b/plugins/techdocs-backend/examples/documented-component/documented-component.yaml @@ -4,7 +4,7 @@ metadata: name: documented-component description: A Service with TechDocs documentation annotations: - backstage.io/techdocs-ref: 'dir:./' + backstage.io/techdocs-ref: 'github:https://github.com/spotify/backstage/blob/master/plugins/techdocs-backend/examples/documented-component' spec: type: service lifecycle: experimental diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 8a5a9fd10a..0ce09ba473 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -30,8 +30,10 @@ "express": "^4.17.1", "express-promise-router": "^3.0.3", "fs-extra": "^9.0.1", + "git-url-parse": "^11.1.3", "knex": "^0.21.1", "node-fetch": "^2.6.0", + "nodegit": "^0.27.0", "winston": "^3.2.1" }, "devDependencies": { diff --git a/plugins/techdocs-backend/src/service/router.test.ts b/plugins/techdocs-backend/src/service/router.test.ts index b28010e059..c266f98677 100644 --- a/plugins/techdocs-backend/src/service/router.test.ts +++ b/plugins/techdocs-backend/src/service/router.test.ts @@ -24,12 +24,13 @@ import { createRouter } from './router'; describe('createRouter', () => { let app: express.Express; + const logger = getVoidLogger(); beforeAll(async () => { const router = await createRouter({ preparers: new Preparers(), generators: new Generators(), - publisher: new LocalPublish(), + publisher: new LocalPublish(logger), logger: getVoidLogger(), dockerClient: new Docker(), config: ConfigReader.fromConfigs([]), diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index e11e3bb57f..f93d182edc 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -45,18 +45,34 @@ export async function createRouter({ publisher, config, dockerClient, + logger, }: RouterOptions): Promise { const router = Router(); + const getEntityId = (entity: Entity) => { + return `${entity.kind}:${entity.metadata.namespace ?? ''}:${ + entity.metadata.name + }`; + }; + const buildDocsForEntity = async (entity: Entity) => { const preparer = preparers.get(entity); const generator = generators.get(entity); + logger.info(`[TechDocs] Running preparer on entity ${getEntityId(entity)}`); + const preparedDir = await preparer.prepare(entity); + + logger.info( + `[TechDocs] Running generator on entity ${getEntityId(entity)}`, + ); const { resultDir } = await generator.run({ - directory: await preparer.prepare(entity), + directory: preparedDir, dockerClient, }); + logger.info( + `[TechDocs] Running publisher on entity ${getEntityId(entity)}`, + ); await publisher.publish({ entity, directory: resultDir, diff --git a/plugins/techdocs-backend/src/service/standaloneServer.ts b/plugins/techdocs-backend/src/service/standaloneServer.ts index 76de973875..93233af4c0 100644 --- a/plugins/techdocs-backend/src/service/standaloneServer.ts +++ b/plugins/techdocs-backend/src/service/standaloneServer.ts @@ -41,14 +41,14 @@ export async function startStandaloneServer( logger.debug('Creating application...'); const preparers = new Preparers(); - const directoryPreparer = new DirectoryPreparer(); + const directoryPreparer = new DirectoryPreparer(logger); preparers.register('dir', directoryPreparer); const generators = new Generators(); - const techdocsGenerator = new TechdocsGenerator(); + const techdocsGenerator = new TechdocsGenerator(logger); generators.register('techdocs', techdocsGenerator); - const publisher = new LocalPublish(); + const publisher = new LocalPublish(logger); const dockerClient = new Docker(); diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts index 32b811af78..96449a0bd6 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts @@ -13,17 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import fs from 'fs-extra'; +import path from 'path'; +import os from 'os'; +import { Logger } from 'winston'; + import { GeneratorBase, GeneratorRunOptions, GeneratorRunResult, } from './types'; import { runDockerContainer } from './helpers'; -import fs from 'fs-extra'; -import path from 'path'; -import os from 'os'; export class TechdocsGenerator implements GeneratorBase { + private readonly logger: Logger; + + constructor(logger: Logger) { + this.logger = logger; + } + public async run({ directory, logStream, @@ -36,18 +45,23 @@ export class TechdocsGenerator implements GeneratorBase { path.join(tmpdirResolvedPath, 'techdocs-tmp-'), ); - await runDockerContainer({ - imageName: 'spotify/techdocs', - args: ['build', '-d', '/result'], - logStream, - docsDir: directory, - resultDir, - dockerClient, - }); - - console.log( - `[TechDocs]: Successfully generated docs from ${directory} into ${resultDir}`, - ); + try { + await runDockerContainer({ + imageName: 'spotify/techdocs', + args: ['build', '-d', '/result'], + logStream, + docsDir: directory, + resultDir, + dockerClient, + }); + this.logger.info( + `[TechDocs]: Successfully generated docs from ${directory} into ${resultDir}`, + ); + } catch (error) { + this.logger.debug( + `[TechDocs]: Failed to generate docs from ${directory} into ${resultDir}`, + ); + } return { resultDir }; } diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts index 8a99a8b59a..fed7a0e107 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts @@ -14,6 +14,9 @@ * limitations under the License. */ import { DirectoryPreparer } from './dir'; +import { getVoidLogger } from '@backstage/backend-common'; + +const logger = getVoidLogger(); const createMockEntity = (annotations: {}) => { return { @@ -30,7 +33,7 @@ const createMockEntity = (annotations: {}) => { describe('directory preparer', () => { it('should merge managed-by-location and techdocs-ref when techdocs-ref is relative', async () => { - const directoryPreparer = new DirectoryPreparer(); + const directoryPreparer = new DirectoryPreparer(logger); const mockEntity = createMockEntity({ 'backstage.io/managed-by-location': @@ -44,7 +47,7 @@ describe('directory preparer', () => { }); it('should merge managed-by-location and techdocs-ref when techdocs-ref is absolute', async () => { - const directoryPreparer = new DirectoryPreparer(); + const directoryPreparer = new DirectoryPreparer(logger); const mockEntity = createMockEntity({ 'backstage.io/managed-by-location': diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts index e61a3e48ac..1560c2ea8e 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts @@ -13,26 +13,95 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import fs from 'fs-extra'; +import os from 'os'; import { PreparerBase } from './types'; import { Entity } from '@backstage/catalog-model'; import path from 'path'; import { parseReferenceAnnotation } from './helpers'; +import { InputError } from '@backstage/backend-common'; +import { Clone } from 'nodegit'; +import parseGitUrl from 'git-url-parse'; +import { Logger } from 'winston'; export class DirectoryPreparer implements PreparerBase { - prepare(entity: Entity): Promise { - const { location: managedByLocation } = parseReferenceAnnotation( + private readonly logger: Logger; + + constructor(logger: Logger) { + this.logger = logger; + } + + private async cloneGithubRepo(entity: Entity) { + const { type, target } = parseReferenceAnnotation( 'backstage.io/managed-by-location', entity, ); - const { location: techdocsLocation } = parseReferenceAnnotation( + + if (type !== 'github') { + throw new InputError(`Wrong target type: ${type}, should be 'github'`); + } + + const parsedGitLocation = parseGitUrl(target); + const repositoryTmpPath = path.join( + os.tmpdir(), + 'backstage-repo', + parsedGitLocation.source, + parsedGitLocation.owner, + parsedGitLocation.name, + parsedGitLocation.ref, + ); + if (fs.existsSync(repositoryTmpPath)) { + return repositoryTmpPath; + } + const repositoryCheckoutUrl = parsedGitLocation.toString('https'); + + this.logger.debug( + `[TechDocs] Checking out repository ${repositoryCheckoutUrl} to ${repositoryTmpPath}`, + ); + + fs.mkdirSync(repositoryTmpPath, { recursive: true }); + await Clone.clone(repositoryCheckoutUrl, repositoryTmpPath, {}); + + return repositoryTmpPath; + } + + private async resolveManagedByLocationToDir(entity: Entity) { + const { type, target } = parseReferenceAnnotation( + 'backstage.io/managed-by-location', + entity, + ); + + this.logger.debug( + `[TechDocs] Building docs for entity with type 'dir' and managed-by-location '${type}'`, + ); + switch (type) { + case 'github': { + const parsedGitLocation = parseGitUrl(target); + const repoLocation = await this.cloneGithubRepo(entity); + + return path.dirname( + path.join(repoLocation, parsedGitLocation.filepath), + ); + } + case 'file': + return path.dirname(target); + default: + throw new InputError(`Unable to resolve location type ${type}`); + } + } + + async prepare(entity: Entity): Promise { + const { target } = parseReferenceAnnotation( 'backstage.io/techdocs-ref', entity, ); - const managedByLocationDirectory = path.dirname(managedByLocation); + const managedByLocationDirectory = await this.resolveManagedByLocationToDir( + entity, + ); return new Promise(resolve => { - resolve(path.resolve(managedByLocationDirectory, techdocsLocation)); + resolve(path.resolve(managedByLocationDirectory, target)); }); } } diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts new file mode 100644 index 0000000000..27ee277f02 --- /dev/null +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts @@ -0,0 +1,71 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 fs from 'fs-extra'; +import path from 'path'; +import os from 'os'; +import { Entity } from '@backstage/catalog-model'; +import { InputError } from '@backstage/backend-common'; +import { PreparerBase } from './types'; +import parseGitUrl from 'git-url-parse'; +import { Clone } from 'nodegit'; +import { parseReferenceAnnotation } from './helpers'; +import { Logger } from 'winston'; + +export class GithubPreparer implements PreparerBase { + private readonly logger: Logger; + + constructor(logger: Logger) { + this.logger = logger; + } + + async prepare(entity: Entity): Promise { + const { type, target } = parseReferenceAnnotation( + 'backstage.io/techdocs-ref', + entity, + ); + + if (type !== 'github') { + throw new InputError(`Wrong target type: ${type}, should be 'github'`); + } + + const parsedGitLocation = parseGitUrl(target); + const repositoryTmpPath = path.join( + os.tmpdir(), + 'backstage-repo', + parsedGitLocation.source, + parsedGitLocation.owner, + parsedGitLocation.name, + parsedGitLocation.ref, + ); + + if (fs.existsSync(repositoryTmpPath)) { + this.logger.debug( + `[TechDocs] Found repository already checked out at ${repositoryTmpPath}`, + ); + return path.join(repositoryTmpPath, parsedGitLocation.filepath); + } + const repositoryCheckoutUrl = parsedGitLocation.toString('https'); + + this.logger.debug( + `[TechDocs] Checking out repository ${repositoryCheckoutUrl} to ${repositoryTmpPath}`, + ); + + fs.mkdirSync(repositoryTmpPath, { recursive: true }); + await Clone.clone(repositoryCheckoutUrl, repositoryTmpPath, {}); + + return path.join(repositoryTmpPath, parsedGitLocation.filepath); + } +} diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts index 1fd86805ca..3b3388d58d 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts @@ -18,8 +18,8 @@ import { InputError } from '@backstage/backend-common'; import { RemoteProtocol } from './types'; export type ParsedLocationAnnotation = { - protocol: RemoteProtocol; - location: string; + type: RemoteProtocol; + target: string; }; export const parseReferenceAnnotation = ( @@ -36,19 +36,19 @@ export const parseReferenceAnnotation = ( // split on the first colon for the protocol and the rest after the first split // is the location. - const [protocol, location] = annotation.split(/:(.+)/) as [ + const [type, target] = annotation.split(/:(.+)/) as [ RemoteProtocol?, string?, ]; - if (!protocol || !location) { + if (!type || !target) { throw new InputError( `Failure to parse either protocol or location for entity: ${entity.metadata.name}`, ); } return { - protocol, - location, + type, + target, }; }; diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts index 9f928e7413..535b56f327 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/index.ts @@ -14,5 +14,6 @@ * limitations under the License. */ export { DirectoryPreparer } from './dir'; +export { GithubPreparer } from './github'; export { Preparers } from './preparers'; export type { PreparerBuilder } from './types'; diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/preparers.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/preparers.ts index 1d5b689d8b..1c8fadd145 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/preparers.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/preparers.ts @@ -26,13 +26,16 @@ export class Preparers implements PreparerBuilder { } get(entity: Entity): PreparerBase { - const { protocol } = parseReferenceAnnotation('backstage.io/techdocs-ref', entity); - const preparer = this.preparerMap.get(protocol); + const { type } = parseReferenceAnnotation( + 'backstage.io/techdocs-ref', + entity, + ); + const preparer = this.preparerMap.get(type); if (!preparer) { - throw new Error(`No preparer registered for type: "${protocol}"`); + throw new Error(`No preparer registered for type: "${type}"`); } return preparer; } -} \ No newline at end of file +} diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts index 1ed8be3898..55e0a547e9 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.test.ts @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { LocalPublish } from './local'; import fs from 'fs-extra'; import path from 'path'; +import { getVoidLogger } from '@backstage/backend-common'; +import { LocalPublish } from './local'; const createMockEntity = (annotations = {}) => { return { @@ -30,9 +31,11 @@ const createMockEntity = (annotations = {}) => { }; }; +const logger = getVoidLogger(); + describe('local publisher', () => { it('should publish generated documentation dir', async () => { - const publisher = new LocalPublish(); + const publisher = new LocalPublish(logger); const mockEntity = createMockEntity(); diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts index 73a7d5a758..be9e1876d8 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts @@ -13,12 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { PublisherBase } from './types'; -import { Entity } from '@backstage/catalog-model'; -import path from 'path'; import fs from 'fs-extra'; +import path from 'path'; +import { Logger } from 'winston'; +import { Entity } from '@backstage/catalog-model'; +import { PublisherBase } from './types'; export class LocalPublish implements PublisherBase { + private readonly logger: Logger; + + constructor(logger: Logger) { + this.logger = logger; + } + publish({ entity, directory, @@ -37,16 +44,22 @@ export class LocalPublish implements PublisherBase { '../../../../static/docs/', entity.kind, entityNamespace, - entity.metadata.name + entity.metadata.name, ); if (!fs.existsSync(publishDir)) { + this.logger.info( + `[TechDocs]: Could not find ${publishDir}, creates the directory.`, + ); fs.mkdirSync(publishDir, { recursive: true }); } return new Promise((resolve, reject) => { fs.copy(directory, publishDir, err => { if (err) { + this.logger.debug( + `[TechDocs]: Failed to copy docs from ${directory} to ${publishDir}`, + ); reject(err); } diff --git a/yarn.lock b/yarn.lock index c3527d6df6..f9e0a21696 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3419,6 +3419,11 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@sindresorhus/is@^2.0.0": + version "2.1.1" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz#ceff6a28a5b4867c2dd4a1ba513de278ccbe8bb1" + integrity sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg== + "@sinonjs/commons@^1.7.0": version "1.7.1" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1" @@ -4302,6 +4307,13 @@ dependencies: defer-to-connect "^1.0.1" +"@szmarczak/http-timer@^4.0.0": + version "4.0.5" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" + integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== + dependencies: + defer-to-connect "^2.0.0" + "@testing-library/cypress@^6.0.0": version "6.0.0" resolved "https://registry.npmjs.org/@testing-library/cypress/-/cypress-6.0.0.tgz#935f7716e0e495f02fd753a42621e4d350097dce" @@ -4467,6 +4479,16 @@ "@types/connect" "*" "@types/node" "*" +"@types/cacheable-request@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" + integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + "@types/cheerio@^0.22.8": version "0.22.21" resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3" @@ -4739,6 +4761,11 @@ resolved "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b" integrity sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ== +"@types/http-cache-semantics@*": + version "4.0.0" + resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" + integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== + "@types/http-errors@^1.6.3": version "1.8.0" resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz#682477dbbbd07cd032731cb3b0e7eaee3d026b69" @@ -4833,6 +4860,13 @@ resolved "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw== +"@types/keyv@*", "@types/keyv@^3.1.1": + version "3.1.1" + resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" + integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== + dependencies: + "@types/node" "*" + "@types/koa-compose@*": version "3.2.5" resolved "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d" @@ -5150,6 +5184,13 @@ dependencies: "@types/node" "*" +"@types/responselike@*": + version "1.0.0" + resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/rollup-plugin-peer-deps-external@^2.2.0": version "2.2.0" resolved "https://registry.npmjs.org/@types/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.0.tgz#eae7d8b9d27fa037f5bcaded24e389f85b81973c" @@ -7389,6 +7430,14 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-lookup@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz#87be64a18b925234875e10a9bb1ebca4adce6b38" + integrity sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg== + dependencies: + "@types/keyv" "^3.1.1" + keyv "^4.0.0" + cacheable-request@^2.1.1: version "2.1.4" resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" @@ -7415,6 +7464,19 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +cacheable-request@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" + integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^2.0.0" + cachedir@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" @@ -9100,6 +9162,13 @@ decompress-response@^4.2.0: dependencies: mimic-response "^2.0.0" +decompress-response@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz#7849396e80e3d1eba8cb2f75ef4930f76461cb0f" + integrity sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw== + dependencies: + mimic-response "^2.0.0" + decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" @@ -9215,6 +9284,11 @@ defer-to-connect@^1.0.1: resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +defer-to-connect@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1" + integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg== + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -11630,6 +11704,13 @@ git-url-parse@^11.1.2: dependencies: git-up "^4.0.0" +git-url-parse@^11.1.3: + version "11.1.3" + resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.1.3.tgz#03625b6fc09905e9ad1da7bb2b84be1bf9123143" + integrity sha512-GPsfwticcu52WQ+eHp0IYkAyaOASgYdtsQDIt4rUp6GbiNt1P9ddrh3O0kQB0eD4UJZszVqNT3+9Zwcg40fywA== + dependencies: + git-up "^4.0.0" + gitconfiglocal@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" @@ -11853,6 +11934,27 @@ good-listener@^1.2.2: dependencies: delegate "^3.1.2" +got@^10.7.0: + version "10.7.0" + resolved "https://registry.npmjs.org/got/-/got-10.7.0.tgz#62889dbcd6cca32cd6a154cc2d0c6895121d091f" + integrity sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg== + dependencies: + "@sindresorhus/is" "^2.0.0" + "@szmarczak/http-timer" "^4.0.0" + "@types/cacheable-request" "^6.0.1" + cacheable-lookup "^2.0.0" + cacheable-request "^7.0.1" + decompress-response "^5.0.0" + duplexer3 "^0.1.4" + get-stream "^5.0.0" + lowercase-keys "^2.0.0" + mimic-response "^2.1.0" + p-cancelable "^2.0.0" + p-event "^4.0.0" + responselike "^2.0.0" + to-readable-stream "^2.0.0" + type-fest "^0.10.0" + got@^7.0.0: version "7.1.0" resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" @@ -14292,6 +14394,11 @@ json-buffer@3.0.0: resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -14547,6 +14654,13 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +keyv@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz#9fe703cb4a94d6d11729d320af033307efd02ee6" + integrity sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw== + dependencies: + json-buffer "3.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -15733,7 +15847,7 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -mimic-response@^2.0.0: +mimic-response@^2.0.0, mimic-response@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== @@ -16323,6 +16437,21 @@ nodegit@0.26.5: request-promise-native "^1.0.5" tar-fs "^1.16.3" +nodegit@^0.27.0: + version "0.27.0" + resolved "https://registry.npmjs.org/nodegit/-/nodegit-0.27.0.tgz#4e8cc236f60e1c97324a5acff99056fe116a6ebe" + integrity sha512-E9K4gPjWiA0b3Tx5lfWCzG7Cvodi2idl3V5UD2fZrOrHikIfrN7Fc2kWLtMUqqomyoToYJLeIC8IV7xb1CYRLA== + dependencies: + fs-extra "^7.0.0" + got "^10.7.0" + json5 "^2.1.0" + lodash "^4.17.14" + nan "^2.14.0" + node-gyp "^4.0.0" + node-pre-gyp "^0.13.0" + ramda "^0.25.0" + tar-fs "^1.16.3" + nodemon@^2.0.2: version "2.0.4" resolved "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416" @@ -16870,6 +16999,11 @@ p-cancelable@^1.0.0: resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +p-cancelable@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" + integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== + p-each-series@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" @@ -16889,6 +17023,13 @@ p-event@^2.1.0: dependencies: p-timeout "^2.0.1" +p-event@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" + integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== + dependencies: + p-timeout "^3.1.0" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -19745,6 +19886,13 @@ responselike@1.0.2, responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" +responselike@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== + dependencies: + lowercase-keys "^2.0.0" + restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -21906,6 +22054,11 @@ to-readable-stream@^1.0.0: resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== +to-readable-stream@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz#82880316121bea662cdc226adb30addb50cb06e8" + integrity sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w== + to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" @@ -22204,6 +22357,11 @@ type-detect@4.0.8: resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.10.0: + version "0.10.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz#7f06b2b9fbfc581068d1341ffabd0349ceafc642" + integrity sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw== + type-fest@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" From b9683dc2369b5fde8a2d8bf54703d748be081f62 Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Fri, 21 Aug 2020 12:27:03 +0200 Subject: [PATCH 039/359] Commit lock file --- yarn.lock | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index c3527d6df6..140bd5dcdc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1544,6 +1544,42 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@backstage/core@0.1.1-alpha.18": + version "0.1.1-alpha.18" + resolved "https://registry.npmjs.org/@backstage/core/-/core-0.1.1-alpha.18.tgz#a8384b6fa65af17ee76166f2e0e4922356856ec1" + integrity sha512-7I0eaTfF30xyX4CD/bFUnXXy22zQzQbqSFFYIGLm5j/XFi3IRb6WANoFMs1hvB/BPI/rojX9oN51PfuHBS6xHg== + dependencies: + "@backstage/config" "^0.1.1-alpha.18" + "@backstage/core-api" "^0.1.1-alpha.18" + "@backstage/theme" "^0.1.1-alpha.18" + "@material-ui/core" "^4.9.1" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.45" + "@types/react" "^16.9" + "@types/react-sparklines" "^1.7.0" + classnames "^2.2.6" + clsx "^1.1.0" + lodash "^4.17.15" + material-table "1.62.x" + prop-types "^15.7.2" + rc-progress "^3.0.0" + react "^16.12.0" + react-dom "^16.12.0" + react-helmet "6.1.0" + react-hook-form "^5.7.2" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-sparklines "^1.7.0" + react-syntax-highlighter "^13.2.1" + react-use "^15.3.3" + +"@backstage/theme@0.1.1-alpha.18": + version "0.1.1-alpha.18" + resolved "https://registry.npmjs.org/@backstage/theme/-/theme-0.1.1-alpha.18.tgz#40adb3e798054d75ccf57c81e5fe3cb6e32cfac5" + integrity sha512-dDTUGH/fxRGtwnR6NCK44ojlfNyFQJKVlax2CNIB/JA43+2c5G2ipDvJ8MhxDeTwwNeO+HJj1AF0AqoJcc1QdQ== + dependencies: + "@material-ui/core" "^4.9.1" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -15446,6 +15482,22 @@ markdown-toc@^1.2.0: repeat-string "^1.6.1" strip-color "^0.1.0" +material-table@1.62.x: + version "1.62.0" + resolved "https://registry.npmjs.org/material-table/-/material-table-1.62.0.tgz#117793ebf16ab0fccbb6f8a670d849a7be0b5995" + integrity sha512-+3tnk32lXtkXeKM7k/hZ82jpSzlXU5CsWXqJHq4Tl0Un7ycjK2Kef6EMPqeE3i58vKNqbIvbrFf/ESH0D/Qwig== + dependencies: + "@date-io/date-fns" "^1.1.0" + "@material-ui/pickers" "^3.2.2" + classnames "^2.2.6" + date-fns "^2.0.0-alpha.27" + debounce "^1.2.0" + fast-deep-equal "2.0.1" + filefy "0.1.10" + prop-types "^15.6.2" + react-beautiful-dnd "^13.0.0" + react-double-scrollbar "0.0.15" + material-table@1.68.0: version "1.68.0" resolved "https://registry.npmjs.org/material-table/-/material-table-1.68.0.tgz#275c3d9a885c40ae4bc5a7461c00e877f92397b9" @@ -19070,7 +19122,7 @@ react-syntax-highlighter@^11.0.2: prismjs "^1.8.4" refractor "^2.4.1" -react-syntax-highlighter@^13.5.1: +react-syntax-highlighter@^13.2.1, react-syntax-highlighter@^13.5.1: version "13.5.1" resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.1.tgz#f21737cf6d582474a0f18b06b52613f4349c0e64" integrity sha512-VVYTnFXF55WMRGdr3QNEzAzcypFZqH45kS7rqh90+AFeNGtui8/gV5AIOIJjwTsuP2UxcO9qvEq94Jq9BYFUhw== From 4d17c1fd94666c8114899d357c044755107d6b60 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 21 Aug 2020 13:24:43 +0200 Subject: [PATCH 040/359] cli: fix jest transform pattern termination --- packages/cli/config/jest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/config/jest.js b/packages/cli/config/jest.js index 77d3a84e82..d95dd21489 100644 --- a/packages/cli/config/jest.js +++ b/packages/cli/config/jest.js @@ -37,8 +37,8 @@ async function getConfig() { // TODO: jest is working on module support, it's possible that we can remove this in the future transform: { '\\.esm\\.js$': require.resolve('jest-esm-transformer'), - '\\.(js|jsx|ts|tsx)': require.resolve('ts-jest'), - '\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg)': require.resolve( + '\\.(js|jsx|ts|tsx)$': require.resolve('ts-jest'), + '\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg)$': require.resolve( './jestFileTransform.js', ), }, From f43063d463f30bf66c021b3c95e76fe8f50f4d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 21 Aug 2020 13:28:54 +0200 Subject: [PATCH 041/359] fix dependency and lockfile --- packages/app/package.json | 2 +- yarn.lock | 66 +++++---------------------------------- 2 files changed, 8 insertions(+), 60 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index ad4aa3524c..d6598b9954 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -28,7 +28,7 @@ "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.0", "@roadiehq/backstage-plugin-github-pull-requests": "0.3.0", - "@roadiehq/backstage-plugin-travis-ci": "^0.1.3", + "@roadiehq/backstage-plugin-travis-ci": "^0.1.4", "history": "^5.0.0", "prop-types": "^15.7.2", "react": "^16.12.0", diff --git a/yarn.lock b/yarn.lock index 140bd5dcdc..ec6343107a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1544,42 +1544,6 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@backstage/core@0.1.1-alpha.18": - version "0.1.1-alpha.18" - resolved "https://registry.npmjs.org/@backstage/core/-/core-0.1.1-alpha.18.tgz#a8384b6fa65af17ee76166f2e0e4922356856ec1" - integrity sha512-7I0eaTfF30xyX4CD/bFUnXXy22zQzQbqSFFYIGLm5j/XFi3IRb6WANoFMs1hvB/BPI/rojX9oN51PfuHBS6xHg== - dependencies: - "@backstage/config" "^0.1.1-alpha.18" - "@backstage/core-api" "^0.1.1-alpha.18" - "@backstage/theme" "^0.1.1-alpha.18" - "@material-ui/core" "^4.9.1" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@types/react" "^16.9" - "@types/react-sparklines" "^1.7.0" - classnames "^2.2.6" - clsx "^1.1.0" - lodash "^4.17.15" - material-table "1.62.x" - prop-types "^15.7.2" - rc-progress "^3.0.0" - react "^16.12.0" - react-dom "^16.12.0" - react-helmet "6.1.0" - react-hook-form "^5.7.2" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-sparklines "^1.7.0" - react-syntax-highlighter "^13.2.1" - react-use "^15.3.3" - -"@backstage/theme@0.1.1-alpha.18": - version "0.1.1-alpha.18" - resolved "https://registry.npmjs.org/@backstage/theme/-/theme-0.1.1-alpha.18.tgz#40adb3e798054d75ccf57c81e5fe3cb6e32cfac5" - integrity sha512-dDTUGH/fxRGtwnR6NCK44ojlfNyFQJKVlax2CNIB/JA43+2c5G2ipDvJ8MhxDeTwwNeO+HJj1AF0AqoJcc1QdQ== - dependencies: - "@material-ui/core" "^4.9.1" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -3367,13 +3331,13 @@ react-router-dom "6.0.0-beta.0" react-use "^15.3.3" -"@roadiehq/backstage-plugin-travis-ci@^0.1.3": - version "0.1.3" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.1.3.tgz#597882bb6db119b9c8970f15cc506c5d70ba2c41" - integrity sha512-xTnc71VtQSp7qfiE/p92U2/FEAvsG32BHIELebYZGCIt+ZqY/fSiXZSGsOXFDlg9adTu0e8ro6HWnknXCdo2pw== +"@roadiehq/backstage-plugin-travis-ci@^0.1.4": + version "0.1.4" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.1.4.tgz#ddbb2ba5b9d47f474462ecbc8c769759578e8526" + integrity sha512-T1eJ0huhwZcRHjrDKWoAzlKBv8UbhXgrvE7wuHD7dCcmfQdln0jbn7cskOpTnVmrzGXWKQ6mDyKX3LfdEPYbAw== dependencies: - "@backstage/core" "0.1.1-alpha.18" - "@backstage/theme" "0.1.1-alpha.18" + "@backstage/core" "^0.1.1-alpha.18" + "@backstage/theme" "^0.1.1-alpha.18" "@material-ui/core" "^4.9.1" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" @@ -15482,22 +15446,6 @@ markdown-toc@^1.2.0: repeat-string "^1.6.1" strip-color "^0.1.0" -material-table@1.62.x: - version "1.62.0" - resolved "https://registry.npmjs.org/material-table/-/material-table-1.62.0.tgz#117793ebf16ab0fccbb6f8a670d849a7be0b5995" - integrity sha512-+3tnk32lXtkXeKM7k/hZ82jpSzlXU5CsWXqJHq4Tl0Un7ycjK2Kef6EMPqeE3i58vKNqbIvbrFf/ESH0D/Qwig== - dependencies: - "@date-io/date-fns" "^1.1.0" - "@material-ui/pickers" "^3.2.2" - classnames "^2.2.6" - date-fns "^2.0.0-alpha.27" - debounce "^1.2.0" - fast-deep-equal "2.0.1" - filefy "0.1.10" - prop-types "^15.6.2" - react-beautiful-dnd "^13.0.0" - react-double-scrollbar "0.0.15" - material-table@1.68.0: version "1.68.0" resolved "https://registry.npmjs.org/material-table/-/material-table-1.68.0.tgz#275c3d9a885c40ae4bc5a7461c00e877f92397b9" @@ -19122,7 +19070,7 @@ react-syntax-highlighter@^11.0.2: prismjs "^1.8.4" refractor "^2.4.1" -react-syntax-highlighter@^13.2.1, react-syntax-highlighter@^13.5.1: +react-syntax-highlighter@^13.5.1: version "13.5.1" resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.1.tgz#f21737cf6d582474a0f18b06b52613f4349c0e64" integrity sha512-VVYTnFXF55WMRGdr3QNEzAzcypFZqH45kS7rqh90+AFeNGtui8/gV5AIOIJjwTsuP2UxcO9qvEq94Jq9BYFUhw== From 9b655c8f713c031092ee744922347b1112527205 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2020 14:47:17 +0200 Subject: [PATCH 042/359] chore(deps): bump @material-icons/font from 1.0.3 to 1.0.4 (#2063) Bumps [@material-icons/font](https://github.com/material-icons/material-icons) from 1.0.3 to 1.0.4. - [Release notes](https://github.com/material-icons/material-icons/releases) - [Commits](https://github.com/material-icons/material-icons/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4329ae46ca..38855bb50d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2874,9 +2874,9 @@ write-file-atomic "^2.3.0" "@material-icons/font@^1.0.2": - version "1.0.3" - resolved "https://registry.npmjs.org/@material-icons/font/-/font-1.0.3.tgz#f722e5a69a03f20ef47d015cb69420bebeeaabe5" - integrity sha512-aIRd0Z9b/HJ/O24KOaP7dNsXypMnXhpWrpLVYvQB/JesVqXYSbioYND200sR+C14a0LSCp+qWnWCnSXmN1hWGw== + version "1.0.4" + resolved "https://registry.npmjs.org/@material-icons/font/-/font-1.0.4.tgz#4bfc59dcaba90f6d71e4aeffd1ebb2ea6119c844" + integrity sha512-tt80HS8dDtx/jj6fXWi6CycCZWC1AY3YOXjHwxShwe/jrW/fRVdw0C9JOGo0ij5/7y+uG0Rp0c4plu7J4Cq7EA== "@material-ui/core@^4.9.1": version "4.9.7" From 307106588491ffcd1008f07359d672168793a091 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2020 14:47:45 +0200 Subject: [PATCH 043/359] chore(deps): bump swagger-ui-react from 3.31.1 to 3.32.4 (#2059) Bumps [swagger-ui-react](https://github.com/swagger-api/swagger-ui) from 3.31.1 to 3.32.4. - [Release notes](https://github.com/swagger-api/swagger-ui/releases) - [Commits](https://github.com/swagger-api/swagger-ui/compare/v3.31.1...v3.32.4) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 168 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 104 insertions(+), 64 deletions(-) diff --git a/yarn.lock b/yarn.lock index 38855bb50d..06324a67a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1415,7 +1415,7 @@ pirates "^4.0.0" source-map-support "^0.5.16" -"@babel/runtime-corejs2@^7.10.4": +"@babel/runtime-corejs2@^7.10.4", "@babel/runtime-corejs2@^7.8.7": version "7.11.2" resolved "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.11.2.tgz#700a03945ebad0d31ba6690fc8a6bcc9040faa47" integrity sha512-AC/ciV28adSSpEkBglONBWq4/Lvm6GAZuxIoyVtsnUpZMl0bxLtoChEnYAkP+47KyOCayZanojtflUEUJtR/6Q== @@ -1423,14 +1423,6 @@ core-js "^2.6.5" regenerator-runtime "^0.13.4" -"@babel/runtime-corejs2@^7.8.7": - version "7.10.3" - resolved "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.10.3.tgz#81bc99a96bfcb6db3f0dcf73fdc577cc554d341b" - integrity sha512-enKvnR/kKFbZFgXYo165wtSA5OeiTlgsnU4jV3vpKRhfWUJjLS6dfVcjIPeRcgJbgEgdgu0I+UyBWqu6c0GumQ== - dependencies: - core-js "^2.6.5" - regenerator-runtime "^0.13.4" - "@babel/runtime-corejs3@^7.10.2": version "7.10.3" resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.10.3.tgz#931ed6941d3954924a7aa967ee440e60c507b91a" @@ -8484,6 +8476,11 @@ core-js-pure@^3.0.0, core-js-pure@^3.0.1: resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= + core-js@^2.4.0, core-js@^2.5.7, core-js@^2.6.11, core-js@^2.6.5: version "2.6.11" resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" @@ -8567,6 +8564,15 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-react-class@^15.5.1: + version "15.6.3" + resolved "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036" + integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg== + dependencies: + fbjs "^0.8.9" + loose-envify "^1.3.1" + object-assign "^4.1.1" + create-react-context@0.3.0, create-react-context@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz#546dede9dc422def0d3fc2fe03afe0bc0f4f7d8c" @@ -10919,6 +10925,19 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fbjs@^0.8.9: + version "0.8.17" + resolved "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" + integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -12377,11 +12396,6 @@ highlight.js@~9.13.0: resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== -highlight.js@~9.15.0, highlight.js@~9.15.1: - version "9.15.10" - resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2" - integrity sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw== - history@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/history/-/history-5.0.0.tgz#0cabbb6c4bbf835addb874f8259f6d25101efd08" @@ -13085,7 +13099,7 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: +invariant@^2.0.0, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -13579,7 +13593,7 @@ is-ssh@^1.3.0: dependencies: protocols "^1.1.0" -is-stream@^1.0.0, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -13720,6 +13734,14 @@ isobject@^4.0.0: resolved "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + isomorphic-form-data@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz#9f6adf1c4c61ae3aefd8f110ab60fb9b143d6cec" @@ -14271,15 +14293,7 @@ js-tokens@^3.0.2: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@^3.13.1, js-yaml@^3.8.3: - version "3.13.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@^3.14.0, js-yaml@^3.8.1: +js-yaml@^3.13.1, js-yaml@^3.14.0, js-yaml@^3.8.1, js-yaml@^3.8.3: version "3.14.0" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== @@ -15058,7 +15072,7 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash-es@^4.17.11: +lodash-es@^4.17.11, lodash-es@^4.2.1: version "4.17.15" resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== @@ -15302,7 +15316,7 @@ longest@^1.0.0: resolved "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -15339,14 +15353,6 @@ lowercase-keys@^2.0.0: resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lowlight@1.12.1: - version "1.12.1" - resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.12.1.tgz#014acf8dd73a370e02ff1cc61debcde3bb1681eb" - integrity sha512-OqaVxMGIESnawn+TU/QMV5BJLbUghUfjDWPAtFqDYDmDtr4FnB+op8xM+pR7nKlauHNUHXGt0VgWatFB8voS5w== - dependencies: - fault "^1.0.2" - highlight.js "~9.15.0" - lowlight@^1.14.0: version "1.14.0" resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.14.0.tgz#83ebc143fec0f9e6c0d3deffe01be129ce56b108" @@ -16261,6 +16267,14 @@ node-fetch@2.6.0, node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.3.0, node- resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + node-forge@0.9.0: version "0.9.0" resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" @@ -18439,6 +18453,13 @@ promise.series@^0.2.0: resolved "https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd" integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70= +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + promise@^8.0.3: version "8.1.0" resolved "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e" @@ -19052,7 +19073,7 @@ react-inspector@^4.0.0: is-dom "^1.0.9" prop-types "^15.6.1" -react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -19072,7 +19093,7 @@ react-lazylog@^4.5.2: text-encoding-utf-8 "^1.0.1" whatwg-fetch "^2.0.4" -react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== @@ -19121,18 +19142,17 @@ react-popper@^1.3.6: typed-styles "^0.0.7" warning "^4.0.2" -react-redux@^5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/react-redux/-/react-redux-5.1.2.tgz#b19cf9e21d694422727bf798e934a916c4080f57" - integrity sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q== +react-redux@=4.4.10: + version "4.4.10" + resolved "https://registry.npmjs.org/react-redux/-/react-redux-4.4.10.tgz#ad57bd1db00c2d0aa7db992b360ce63dd0b80ec5" + integrity sha512-tjL0Bmpkj75Td0k+lXlF8Fc8a9GuXFv/3ahUOCXExWs/jhsKiQeTffdH0j5byejCGCRL4tvGFYlrwBF1X/Aujg== dependencies: - "@babel/runtime" "^7.1.2" + create-react-class "^15.5.1" hoist-non-react-statics "^3.3.0" - invariant "^2.2.4" - loose-envify "^1.1.0" - prop-types "^15.6.1" - react-is "^16.6.0" - react-lifecycles-compat "^3.0.0" + invariant "^2.0.0" + lodash "^4.17.11" + loose-envify "^1.4.0" + prop-types "^15.7.2" react-redux@^7.1.1: version "7.2.0" @@ -19189,16 +19209,16 @@ react-string-replace@^0.4.1: dependencies: lodash "^4.17.4" -react-syntax-highlighter@=12.2.1: - version "12.2.1" - resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-12.2.1.tgz#14d78352da1c1c3f93c6698b70ec7c706b83493e" - integrity sha512-CTsp0ZWijwKRYFg9xhkWD4DSpQqE4vb2NKVMdPAkomnILSmsNBHE0n5GuI5zB+PU3ySVvXvdt9jo+ViD9XibCA== +react-syntax-highlighter@=13.5.0: + version "13.5.0" + resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.0.tgz#c0f3fd826a52b2a6ce5786d4ce60c3e0665b65c0" + integrity sha512-2nKo8spFxe9shcjbdUiqxkrf/IMDqKUZLx7JVIxEJ17P+fYFGL4CRsZZC66UPeQ2o/f29eKu31CrkKGCK1RHuA== dependencies: "@babel/runtime" "^7.3.1" - highlight.js "~9.15.1" - lowlight "1.12.1" - prismjs "^1.8.4" - refractor "^2.4.1" + highlight.js "^10.1.1" + lowlight "^1.14.0" + prismjs "^1.21.0" + refractor "^3.1.0" react-syntax-highlighter@^11.0.2: version "11.0.2" @@ -19535,7 +19555,17 @@ redux-immutable@3.1.0: dependencies: immutable "^3.8.1" -redux@^4.0.4, redux@^4.0.5: +redux@=3.7.2: + version "3.7.2" + resolved "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" + integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== + dependencies: + lodash "^4.2.1" + lodash-es "^4.2.1" + loose-envify "^1.1.0" + symbol-observable "^1.0.3" + +redux@^4.0.4: version "4.0.5" resolved "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== @@ -20411,7 +20441,7 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -setimmediate@^1.0.4: +setimmediate@^1.0.4, setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= @@ -21568,9 +21598,9 @@ swagger-client@=3.10.12: url "~0.11.0" swagger-ui-react@^3.31.1: - version "3.31.1" - resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-3.31.1.tgz#1770cfa1dc390f79887d641ea35ee654f4bf7d80" - integrity sha512-bGpNzizMT/7N4LTdw6yx+t3rpsEoalrn0VIfhUzv9zXVrUTl+3AKPLHpLBr0r4NvzvaZLrYuSjkatnLfngJ4vg== + version "3.32.4" + resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-3.32.4.tgz#cc2cef0e2b9faff7c0dab111e9b4b9c87d0a01a2" + integrity sha512-AbdeBVIV6oM4pXKEsGY2Ijs1ySmdVYP4cHdZYu0kWghxKVqskocsfB+SsvRTh6mOxdZ2YNAEhc7RMdSqaWnGgw== dependencies: "@babel/runtime-corejs2" "^7.10.4" "@braintree/sanitize-url" "^4.0.0" @@ -21596,9 +21626,9 @@ swagger-ui-react@^3.31.1: react-immutable-pure-component "^1.1.1" react-inspector "^2.3.0" react-motion "^0.5.2" - react-redux "^5.1.2" - react-syntax-highlighter "=12.2.1" - redux "^4.0.5" + react-redux "=4.4.10" + react-syntax-highlighter "=13.5.0" + redux "=3.7.2" redux-immutable "3.1.0" remarkable "^2.0.1" reselect "^4.0.0" @@ -21616,7 +21646,7 @@ swr@^0.3.0: dependencies: fast-deep-equal "2.0.1" -symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: +symbol-observable@^1.0.3, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== @@ -22422,6 +22452,11 @@ typescript@^3.9.3: resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== +ua-parser-js@^0.7.18: + version "0.7.21" + resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" + integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" @@ -23202,6 +23237,11 @@ whatwg-fetch@3.0.0, whatwg-fetch@^3.0.0: resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== +whatwg-fetch@>=0.10.0: + version "3.4.0" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.0.tgz#e11de14f4878f773fbebcde8871b2c0699af8b30" + integrity sha512-rsum2ulz2iuZH08mJkT0Yi6JnKhwdw4oeyMjokgxd+mmqYSd9cPpOQf01TIWgjxG/U4+QR+AwKq6lSbXVxkyoQ== + whatwg-fetch@^2.0.0, whatwg-fetch@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" From c6c3c94c18f73d72de35d3f1e5ac258b5a2bb5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Fri, 21 Aug 2020 14:48:16 +0200 Subject: [PATCH 044/359] Camelcase GitHub and GitLab (#2034) * Camelcase GitHub and GitLab * GitLab * Code GitLab * GitHub in code * Fix auth naming * Update docs/features/software-templates/adding-templates.md Co-authored-by: Raghunandan Balachandran * Update docs/features/software-templates/extending/create-your-own-preparer.md Co-authored-by: Raghunandan Balachandran Co-authored-by: Raghunandan Balachandran --- docs/features/software-templates/adding-templates.md | 2 +- .../extending/create-your-own-preparer.md | 2 +- docs/features/software-templates/index.md | 2 +- docs/features/software-templates/installation.md | 2 +- docs/reference/utility-apis/README.md | 4 ++-- microsite/blog/2020-03-18-what-is-backstage.md | 2 +- packages/app/src/identityProviders.ts | 8 ++++---- packages/core-api/src/apis/definitions/auth.ts | 8 ++++---- packages/core/src/layout/Sidebar/UserSettings.tsx | 4 ++-- plugins/auth-backend/src/providers/github/provider.ts | 2 +- .../ingestion/processors/GitlabApiReaderProcessor.test.ts | 2 +- .../src/ingestion/processors/GitlabApiReaderProcessor.ts | 4 ++-- .../src/ingestion/processors/GitlabReaderProcessor.ts | 2 +- plugins/github-actions/src/api/GithubActionsApi.ts | 2 +- plugins/graphiql/src/lib/api/GraphQLEndpoints.ts | 2 +- .../src/scaffolder/stages/publish/github.test.ts | 2 +- 16 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index 2ec4bdd6f3..b0405cab79 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -54,7 +54,7 @@ contains more information about the required fields. Once we have a `template.yaml` ready, we can then add it to the service catalog for use by the scaffolder. -Currently the catalog supports loading definitions from Github + Local Files. To +Currently the catalog supports loading definitions from GitHub + Local Files. To load from other places, not only will there need to be another preparer, but the support to load the location will also need to be added to the Catalog. diff --git a/docs/features/software-templates/extending/create-your-own-preparer.md b/docs/features/software-templates/extending/create-your-own-preparer.md index 739c516658..0b3fb31323 100644 --- a/docs/features/software-templates/extending/create-your-own-preparer.md +++ b/docs/features/software-templates/extending/create-your-own-preparer.md @@ -54,7 +54,7 @@ The `protocol` is set on the when added to the service catalog. You can see more about this `PreparerKey` here in [Register your own template](../adding-templates.md) -**note:** Currently the catalog supports loading definitions from Github + Local +**note:** Currently the catalog supports loading definitions from GitHub + Local Files, which translate into the two `PreparerKeys` `file` and `github`. To load from other places, not only will there need to be another preparer, but the support to load the location will also need to be added to the Catalog. diff --git a/docs/features/software-templates/index.md b/docs/features/software-templates/index.md index d9a86ee7aa..503fbb7a3d 100644 --- a/docs/features/software-templates/index.md +++ b/docs/features/software-templates/index.md @@ -34,7 +34,7 @@ internally. After filling in these variables, you'll get some more fields to fill out which are required for backstage usage. The owner, which is a `user` in the backstage -system, and the `storePath` which right now must be a Github Organisation and a +system, and the `storePath` which right now must be a GitHub Organisation and a non-existing github repository name in the format `organisation/reponame`. ![Enter backstage vars](../../assets/software-templates/template-picked-2.png) diff --git a/docs/features/software-templates/installation.md b/docs/features/software-templates/installation.md index 0508d1f3f7..73b4f20359 100644 --- a/docs/features/software-templates/installation.md +++ b/docs/features/software-templates/installation.md @@ -109,7 +109,7 @@ export default async function createPlugin({ logger }: PluginEnvironment) { preparers.register('file', filePreparer); preparers.register('github', githubPreparer); - // Create Github client with your access token from environment variables + // Create GitHub client with your access token from environment variables const githubClient = new Octokit({ auth: process.env.GITHUB_ACCESS_TOKEN }); const publisher = new GithubPublisher({ client: githubClient }); diff --git a/docs/reference/utility-apis/README.md b/docs/reference/utility-apis/README.md index fbfbbc475e..521628fd33 100644 --- a/docs/reference/utility-apis/README.md +++ b/docs/reference/utility-apis/README.md @@ -55,7 +55,7 @@ ApiRef: ## githubAuth -Provides authentication towards Github APIs +Provides authentication towards GitHub APIs Implemented types: [OAuthApi](./OAuthApi.md), [ProfileInfoApi](./ProfileInfoApi.md), @@ -67,7 +67,7 @@ ApiRef: ## gitlabAuth -Provides authentication towards Gitlab APIs +Provides authentication towards GitLab APIs Implemented types: [OAuthApi](./OAuthApi.md), [ProfileInfoApi](./ProfileInfoApi.md), diff --git a/microsite/blog/2020-03-18-what-is-backstage.md b/microsite/blog/2020-03-18-what-is-backstage.md index 942adc607f..8b0a0fcbe6 100644 --- a/microsite/blog/2020-03-18-what-is-backstage.md +++ b/microsite/blog/2020-03-18-what-is-backstage.md @@ -13,7 +13,7 @@ Two days ago, we released the open source version of [Backstage](https://backsta ## What’s the big infrastructure problem? -As companies grow, their infrastructure systems get messier. Consider a team that wants to deploy something to the cloud. While Spotify has many awesome engineers, not every engineer is well-versed in our chosen cloud-provider tooling. Yet everyone is required to know and understand Terraform, GCP/AWS/Azure CLIs, Gitlab CI, Prometheus, Kubernetes, Docker, various monitoring and alerting tools, and much, much more. Once other resources come into play (databases, queueing, etc.), each engineer requires even more tools and domain-specific knowledge (or “disciplines”), from backend to machine learning, to mobile and data. +As companies grow, their infrastructure systems get messier. Consider a team that wants to deploy something to the cloud. While Spotify has many awesome engineers, not every engineer is well-versed in our chosen cloud-provider tooling. Yet everyone is required to know and understand Terraform, GCP/AWS/Azure CLIs, GitLab CI, Prometheus, Kubernetes, Docker, various monitoring and alerting tools, and much, much more. Once other resources come into play (databases, queueing, etc.), each engineer requires even more tools and domain-specific knowledge (or “disciplines”), from backend to machine learning, to mobile and data. ## What’s the fix? diff --git a/packages/app/src/identityProviders.ts b/packages/app/src/identityProviders.ts index e80b1d6fea..ea5af8b505 100644 --- a/packages/app/src/identityProviders.ts +++ b/packages/app/src/identityProviders.ts @@ -30,14 +30,14 @@ export const providers = [ }, { id: 'gitlab-auth-provider', - title: 'Gitlab', - message: 'Sign In using Gitlab', + title: 'GitLab', + message: 'Sign In using GitLab', apiRef: gitlabAuthApiRef, }, { id: 'github-auth-provider', - title: 'Github', - message: 'Sign In using Github', + title: 'GitHub', + message: 'Sign In using GitHub', apiRef: githubAuthApiRef, }, { diff --git a/packages/core-api/src/apis/definitions/auth.ts b/packages/core-api/src/apis/definitions/auth.ts index a1a408fece..ffe44afb8a 100644 --- a/packages/core-api/src/apis/definitions/auth.ts +++ b/packages/core-api/src/apis/definitions/auth.ts @@ -222,7 +222,7 @@ export const googleAuthApiRef = createApiRef< }); /** - * Provides authentication towards Github APIs. + * Provides authentication towards GitHub APIs. * * See https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/ * for a full list of supported scopes. @@ -231,7 +231,7 @@ export const githubAuthApiRef = createApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionStateApi >({ id: 'core.auth.github', - description: 'Provides authentication towards Github APIs', + description: 'Provides authentication towards GitHub APIs', }); /** @@ -252,7 +252,7 @@ export const oktaAuthApiRef = createApiRef< }); /** - * Provides authentication towards Gitlab APIs. + * Provides authentication towards GitLab APIs. * * See https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#limiting-scopes-of-a-personal-access-token * for a full list of supported scopes. @@ -261,7 +261,7 @@ export const gitlabAuthApiRef = createApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionStateApi >({ id: 'core.auth.gitlab', - description: 'Provides authentication towards Gitlab APIs', + description: 'Provides authentication towards GitLab APIs', }); /** diff --git a/packages/core/src/layout/Sidebar/UserSettings.tsx b/packages/core/src/layout/Sidebar/UserSettings.tsx index e69d63186d..74fa5e173a 100644 --- a/packages/core/src/layout/Sidebar/UserSettings.tsx +++ b/packages/core/src/layout/Sidebar/UserSettings.tsx @@ -62,14 +62,14 @@ export function SidebarUserSettings() { )} {providers.includes('github') && ( )} {providers.includes('gitlab') && ( diff --git a/plugins/auth-backend/src/providers/github/provider.ts b/plugins/auth-backend/src/providers/github/provider.ts index e6ba56d2c3..b63acdfb20 100644 --- a/plugins/auth-backend/src/providers/github/provider.ts +++ b/plugins/auth-backend/src/providers/github/provider.ts @@ -74,7 +74,7 @@ export class GithubAuthProvider implements OAuthProviderHandlers { idToken: params.id_token, }; - // Github provides an id numeric value (123) + // GitHub provides an id numeric value (123) // as a fallback const id = passportProfile!.id; diff --git a/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.test.ts index cd46d97d13..c43c68591e 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.test.ts @@ -50,7 +50,7 @@ describe('GitlabApiReaderProcessor', () => { 'https://gitlab.com/groupA/teams/teamA/repoA/-/blob/branch/my/path/', url: null, err: - 'Incorrect url: https://gitlab.com/groupA/teams/teamA/repoA/-/blob/branch/my/path/, Error: Gitlab url does not end in .ya?ml', + 'Incorrect url: https://gitlab.com/groupA/teams/teamA/repoA/-/blob/branch/my/path/, Error: GitLab url does not end in .ya?ml', }, ]; diff --git a/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.ts index 6964640eb4..3e585bf2b3 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GitlabApiReaderProcessor.ts @@ -77,7 +77,7 @@ export class GitlabApiReaderProcessor implements LocationProcessor { const branchAndfilePath = url.pathname.split('/-/blob/')[1]; if (!branchAndfilePath.match(/\.ya?ml$/)) { - throw new Error('Gitlab url does not end in .ya?ml'); + throw new Error('GitLab url does not end in .ya?ml'); } const [branch, ...filePath] = branchAndfilePath.split('/'); @@ -127,7 +127,7 @@ export class GitlabApiReaderProcessor implements LocationProcessor { return projectID; } catch (e) { - throw new Error(`Could not get Gitlab ProjectID for: ${target}, ${e}`); + throw new Error(`Could not get GitLab ProjectID for: ${target}, ${e}`); } } } diff --git a/plugins/catalog-backend/src/ingestion/processors/GitlabReaderProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/GitlabReaderProcessor.ts index 211f325afe..9f308ee184 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GitlabReaderProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GitlabReaderProcessor.ts @@ -77,7 +77,7 @@ export class GitlabReaderProcessor implements LocationProcessor { blobKeyword !== 'blob' || !restOfPath.join('/').match(/\.yaml$/) ) { - throw new Error('Wrong Gitlab URL'); + throw new Error('Wrong GitLab URL'); } // Replace 'blob' with 'raw' diff --git a/plugins/github-actions/src/api/GithubActionsApi.ts b/plugins/github-actions/src/api/GithubActionsApi.ts index acdfb90cf3..9a95f21860 100644 --- a/plugins/github-actions/src/api/GithubActionsApi.ts +++ b/plugins/github-actions/src/api/GithubActionsApi.ts @@ -23,7 +23,7 @@ import { export const githubActionsApiRef = createApiRef({ id: 'plugin.githubactions.service', - description: 'Used by the Github Actions plugin to make requests', + description: 'Used by the GitHub Actions plugin to make requests', }); export type GithubActionsApi = { diff --git a/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts b/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts index ee0b4bfd4e..23a9e9e697 100644 --- a/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts +++ b/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts @@ -33,7 +33,7 @@ export type GithubEndpointConfig = { id: string; title: string; /** - * Github GraphQL API url, defaults to https://api.github.com/graphql + * GitHub GraphQL API url, defaults to https://api.github.com/graphql */ url?: string; /** diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts index 164ec316df..93493ab2cc 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts @@ -45,7 +45,7 @@ const { mockRemote: jest.Mocked; }; -describe('Github Publisher', () => { +describe('GitHub Publisher', () => { const publisher = new GithubPublisher({ client: new Octokit() }); beforeEach(() => { From f6f56aa7484b50f9c2293eddd91fc3d6ccaa73f7 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Fri, 21 Aug 2020 14:56:46 +0200 Subject: [PATCH 045/359] TechDocs: documentation template (#1954) * feat(docs-template): Add docs template to create a standalone docs project * fix(docs-template): add docs template to mock data * fix(docs-template): owner of template * add techdocs-ref to cookiecutter docs project * delete unused cookiecutter json file * update docs in default index.md file * rename from .yaml to .yml to be consistent to docs * update techdocs-ref to use github protocol --- .../docs-template/template.yaml | 29 +++++++++++++++++++ .../component-info.yaml | 12 ++++++++ .../docs/index.md | 28 ++++++++++++++++++ .../{{cookiecutter.component_id}}/mkdocs.yml | 8 +++++ .../scaffolder-backend/scripts/mock-data.sh | 1 + .../src/techdocs/stages/prepare/dir.ts | 3 +- .../src/techdocs/stages/prepare/github.ts | 3 +- 7 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/template.yaml create mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/component-info.yaml create mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/docs/index.md create mode 100644 plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/mkdocs.yml diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml new file mode 100644 index 0000000000..01ebdadbce --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml @@ -0,0 +1,29 @@ +apiVersion: backstage.io/v1alpha1 +kind: Template +metadata: + name: docs-template + title: Documentation Template + description: Create a new standalone documentation project + tags: + - Experimental + - TechDocs + - MkDocs +spec: + owner: spotify/techdocs-core + templater: cookiecutter + type: documentation + path: '.' + + schema: + required: + - component_id + properties: + component_id: + title: Name + type: string + description: Unique name of the component + description: + title: Description + type: string + description: Description of the component + \ No newline at end of file diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/component-info.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/component-info.yaml new file mode 100644 index 0000000000..854b692fcf --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/component-info.yaml @@ -0,0 +1,12 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: {{cookiecutter.component_id}} + description: {{cookiecutter.description}} + annotations: + github.com/project-slug: {{cookiecutter.storePath}} + backstage.io/techdocs-ref: github:https://github.com/{{cookiecutter.storePath}} +spec: + type: documentation + lifecycle: experimental + owner: {{cookiecutter.owner}} diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/docs/index.md b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/docs/index.md new file mode 100644 index 0000000000..5352ef7801 --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/docs/index.md @@ -0,0 +1,28 @@ +## {{ cookiecutter.component_id }} + +{{ cookiecutter.description }} + +## Getting started + +Start write your documentation by adding more markdown (.md) files to this folder (/docs) or replace the content in this file. + +## Table of Contents + +The Table of Contents on the right is generated automatically based on the hierarchy +of headings. Only use one H1 (`#` in Markdown) per file. + +## Site navigation + +For new pages to appear in the left hand navigation you need edit the `mkdocs.yml` +file in root of your repo. The navigation can also link out to other sites. + +Alternatively, if there is no `nav` section in `mkdocs.yml`, a navigation section +will be created for you. However, you will not be able to use alternate titles for +pages, or include links to other sites. + +Note that MkDocs uses `mkdocs.yml`, not `mkdocs.yaml`, although both appear to work. +See also . + +## Support + +That's it. If you need support, reach out in [#docs-like-code](https://discord.com/channels/687207715902193673/714754240933003266) on Discord. diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/mkdocs.yml b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/mkdocs.yml new file mode 100644 index 0000000000..2126971502 --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/mkdocs.yml @@ -0,0 +1,8 @@ +site_name: {{cookiecutter.component_id}} +site_description: {{cookiecutter.description}} + +nav: + - Introduction: index.md + +plugins: + - techdocs-core diff --git a/plugins/scaffolder-backend/scripts/mock-data.sh b/plugins/scaffolder-backend/scripts/mock-data.sh index f18d4b4838..9a9b0b186b 100755 --- a/plugins/scaffolder-backend/scripts/mock-data.sh +++ b/plugins/scaffolder-backend/scripts/mock-data.sh @@ -4,6 +4,7 @@ for URL in \ 'react-ssr-template' \ 'springboot-grpc-template' \ 'create-react-app' \ + 'docs-template' \ ; do \ curl \ --location \ diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts index 1560c2ea8e..e108ac4ab9 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.ts @@ -43,7 +43,8 @@ export class DirectoryPreparer implements PreparerBase { const parsedGitLocation = parseGitUrl(target); const repositoryTmpPath = path.join( - os.tmpdir(), + // fs.realpathSync fixes a problem with macOS returning a path that is a symlink + fs.realpathSync(os.tmpdir()), 'backstage-repo', parsedGitLocation.source, parsedGitLocation.owner, diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts index 27ee277f02..aa8a4bb228 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/github.ts @@ -43,7 +43,8 @@ export class GithubPreparer implements PreparerBase { const parsedGitLocation = parseGitUrl(target); const repositoryTmpPath = path.join( - os.tmpdir(), + // fs.realpathSync fixes a problem with macOS returning a path that is a symlink + fs.realpathSync(os.tmpdir()), 'backstage-repo', parsedGitLocation.source, parsedGitLocation.owner, From 382491dc5af1ca2567a93a1be4b218b4b08a93e6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 21 Aug 2020 14:25:09 +0200 Subject: [PATCH 046/359] plugins/catalog: switch CatalogClient to use DiscoveryApi --- packages/app/src/apis.ts | 8 +---- plugins/catalog/src/api/CatalogClient.test.ts | 33 ++++++++----------- plugins/catalog/src/api/CatalogClient.ts | 23 +++++-------- 3 files changed, 22 insertions(+), 42 deletions(-) diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index eb8cfb92af..c787ffc589 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -178,13 +178,7 @@ export const apis = (config: ConfigApi) => { }), ); - builder.add( - catalogApiRef, - new CatalogClient({ - apiOrigin: backendUrl, - basePath: '/catalog', - }), - ); + builder.add(catalogApiRef, new CatalogClient({ discoveryApi })); builder.add( scaffolderApiRef, diff --git a/plugins/catalog/src/api/CatalogClient.test.ts b/plugins/catalog/src/api/CatalogClient.test.ts index 7803f2e173..18f062db77 100644 --- a/plugins/catalog/src/api/CatalogClient.test.ts +++ b/plugins/catalog/src/api/CatalogClient.test.ts @@ -18,25 +18,21 @@ import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { CatalogClient } from './CatalogClient'; import { Entity } from '@backstage/catalog-model'; +import { UrlPatternDiscovery } from '@backstage/core'; const server = setupServer(); +const mockBaseUrl = 'http://backstage:9191/i-am-a-mock-base'; +const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl); describe('CatalogClient', () => { beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close()); - const mockApiOrigin = 'http://backstage:9191'; - const mockBasePath = '/i-am-a-mock-base'; - let client = new CatalogClient({ - apiOrigin: mockApiOrigin, - basePath: mockBasePath, - }); + + let client = new CatalogClient({ discoveryApi }); beforeEach(() => { - client = new CatalogClient({ - apiOrigin: mockApiOrigin, - basePath: mockBasePath, - }); + client = new CatalogClient({ discoveryApi }); }); describe('getEntiies', () => { @@ -61,7 +57,7 @@ describe('CatalogClient', () => { beforeEach(() => { server.use( - rest.get(`${mockApiOrigin}${mockBasePath}/entities`, (_, res, ctx) => { + rest.get(`${mockBaseUrl}/entities`, (_, res, ctx) => { return res(ctx.json(defaultResponse)); }), ); @@ -75,15 +71,12 @@ describe('CatalogClient', () => { it('builds entity search filters properly', async () => { expect.assertions(2); server.use( - rest.get( - `${mockApiOrigin}${mockBasePath}/entities`, - (req, res, ctx) => { - expect(req.url.searchParams.toString()).toBe( - 'a=1&b=2&b=3&%C3%B6=%3D', - ); - return res(ctx.json([])); - }, - ), + rest.get(`${mockBaseUrl}/entities`, (req, res, ctx) => { + expect(req.url.searchParams.toString()).toBe( + 'a=1&b=2&b=3&%C3%B6=%3D', + ); + return res(ctx.json([])); + }), ); const entities = await client.getEntities({ diff --git a/plugins/catalog/src/api/CatalogClient.ts b/plugins/catalog/src/api/CatalogClient.ts index 3804315ace..d5ff033caa 100644 --- a/plugins/catalog/src/api/CatalogClient.ts +++ b/plugins/catalog/src/api/CatalogClient.ts @@ -20,24 +20,17 @@ import { LOCATION_ANNOTATION, } from '@backstage/catalog-model'; import { CatalogApi, EntityCompoundName } from './types'; +import { DiscoveryApi } from '@backstage/core'; export class CatalogClient implements CatalogApi { - private apiOrigin: string; - private basePath: string; + private readonly discoveryApi: DiscoveryApi; - constructor({ - apiOrigin, - basePath, - }: { - apiOrigin: string; - basePath: string; - }) { - this.apiOrigin = apiOrigin; - this.basePath = basePath; + constructor(options: { discoveryApi: DiscoveryApi }) { + this.discoveryApi = options.discoveryApi; } private async getRequired(path: string): Promise { - const url = `${this.apiOrigin}${this.basePath}${path}`; + const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`; const response = await fetch(url); if (!response.ok) { @@ -50,7 +43,7 @@ export class CatalogClient implements CatalogApi { } private async getOptional(path: string): Promise { - const url = `${this.apiOrigin}${this.basePath}${path}`; + const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`; const response = await fetch(url); if (!response.ok) { @@ -100,7 +93,7 @@ export class CatalogClient implements CatalogApi { async addLocation(type: string, target: string) { const response = await fetch( - `${this.apiOrigin}${this.basePath}/locations`, + `${await this.discoveryApi.getBaseUrl('catalog')}/locations`, { headers: { 'Content-Type': 'application/json', @@ -135,7 +128,7 @@ export class CatalogClient implements CatalogApi { async removeEntityByUid(uid: string): Promise { const response = await fetch( - `${this.apiOrigin}${this.basePath}/entities/by-uid/${uid}`, + `${await this.discoveryApi.getBaseUrl('catalog')}/entities/by-uid/${uid}`, { method: 'DELETE', }, From be837b7b361965aee91becb5e56ed2bda5127291 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 21 Aug 2020 14:28:23 +0200 Subject: [PATCH 047/359] plugins/scaffolder: switch ScaffolderApi to use DiscoveryApi --- packages/app/src/apis.ts | 8 +------- plugins/scaffolder/src/api.ts | 24 ++++++++---------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index c787ffc589..72dad858c7 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -180,13 +180,7 @@ export const apis = (config: ConfigApi) => { builder.add(catalogApiRef, new CatalogClient({ discoveryApi })); - builder.add( - scaffolderApiRef, - new ScaffolderApi({ - apiOrigin: backendUrl, - basePath: '/scaffolder/v1', - }), - ); + builder.add(scaffolderApiRef, new ScaffolderApi({ discoveryApi })); builder.add(gitOpsApiRef, new GitOpsRestApi('http://localhost:3008')); diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index 4a0d508a2b..3c42ed2ca4 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createApiRef } from '@backstage/core'; +import { createApiRef, DiscoveryApi } from '@backstage/core'; import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; export const scaffolderApiRef = createApiRef({ @@ -23,18 +23,10 @@ export const scaffolderApiRef = createApiRef({ }); export class ScaffolderApi { - private apiOrigin: string; - private basePath: string; + private readonly discoveryApi: DiscoveryApi; - constructor({ - apiOrigin, - basePath, - }: { - apiOrigin: string; - basePath: string; - }) { - this.apiOrigin = apiOrigin; - this.basePath = basePath; + constructor(options: { discoveryApi: DiscoveryApi }) { + this.discoveryApi = options.discoveryApi; } /** @@ -46,7 +38,7 @@ export class ScaffolderApi { template: TemplateEntityV1alpha1, values: Record, ) { - const url = `${this.apiOrigin}${this.basePath}/jobs`; + const url = `${await this.discoveryApi.getBaseUrl('scaffolder')}/v1/jobs`; const response = await fetch(url, { method: 'POST', headers: { @@ -65,9 +57,9 @@ export class ScaffolderApi { } async getJob(jobId: string) { - const url = `${this.apiOrigin}${this.basePath}/job/${encodeURIComponent( - jobId, - )}`; + const url = `${await this.discoveryApi.getBaseUrl( + 'scaffolder', + )}/v1/job/${encodeURIComponent(jobId)}`; return fetch(url).then(x => x.json()); } } From 6f0c438519b7b3deae60440090d163a317d90740 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 21 Aug 2020 15:13:52 +0200 Subject: [PATCH 048/359] plugins/rollbar: switch RollbarClient to use DiscoveryApi --- packages/app/src/apis.ts | 8 +------- plugins/rollbar/src/api/RollbarClient.ts | 17 +++++------------ 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 72dad858c7..0c409994cb 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -201,13 +201,7 @@ export const apis = (config: ConfigApi) => { ]), ); - builder.add( - rollbarApiRef, - new RollbarClient({ - apiOrigin: backendUrl, - basePath: '/rollbar', - }), - ); + builder.add(rollbarApiRef, new RollbarClient({ discoveryApi })); builder.add( techdocsStorageApiRef, diff --git a/plugins/rollbar/src/api/RollbarClient.ts b/plugins/rollbar/src/api/RollbarClient.ts index 5cabbcbc24..1862ad8270 100644 --- a/plugins/rollbar/src/api/RollbarClient.ts +++ b/plugins/rollbar/src/api/RollbarClient.ts @@ -20,20 +20,13 @@ import { RollbarProject, RollbarTopActiveItem, } from './types'; +import { DiscoveryApi } from '@backstage/core'; export class RollbarClient implements RollbarApi { - private apiOrigin: string; - private basePath: string; + private readonly discoveryApi: DiscoveryApi; - constructor({ - apiOrigin, - basePath, - }: { - apiOrigin: string; - basePath: string; - }) { - this.apiOrigin = apiOrigin; - this.basePath = basePath; + constructor(options: { discoveryApi: DiscoveryApi }) { + this.discoveryApi = options.discoveryApi; } async getAllProjects(): Promise { @@ -59,7 +52,7 @@ export class RollbarClient implements RollbarApi { } private async get(path: string): Promise { - const url = `${this.apiOrigin}${this.basePath}${path}`; + const url = `${await this.discoveryApi.getBaseUrl('rollbar')}${path}`; const response = await fetch(url); if (!response.ok) { From 2e50876160e54a2a622e56affec09c270f1dd133 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Fri, 21 Aug 2020 15:30:50 +0200 Subject: [PATCH 049/359] Throw error when failing to generate docs to prevent request loop (#2073) --- .../techdocs-backend/src/techdocs/stages/generate/techdocs.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts index 96449a0bd6..40cd7590b6 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts @@ -61,6 +61,9 @@ export class TechdocsGenerator implements GeneratorBase { this.logger.debug( `[TechDocs]: Failed to generate docs from ${directory} into ${resultDir}`, ); + throw new Error( + `Failed to generate docs from ${directory} into ${resultDir} with error ${error.message}`, + ); } return { resultDir }; From e9c937dd9f7c2ac4957ad0c31822b02d35f8a257 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Fri, 21 Aug 2020 15:46:10 +0200 Subject: [PATCH 050/359] Add docs template to default app templates (#2072) --- packages/create-app/templates/default-app/app-config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/create-app/templates/default-app/app-config.yaml b/packages/create-app/templates/default-app/app-config.yaml index 49c3dc3691..0c3ec3ed80 100644 --- a/packages/create-app/templates/default-app/app-config.yaml +++ b/packages/create-app/templates/default-app/app-config.yaml @@ -54,3 +54,5 @@ catalog: target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml - type: github target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml + - type: github + target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml From 0afdba6ac175205d1040348bd0a4a5d930471483 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2020 15:47:45 +0200 Subject: [PATCH 051/359] chore(deps): bump @rjsf/core from 2.2.2 to 2.3.0 (#2062) Bumps [@rjsf/core](https://github.com/rjsf-team/react-jsonschema-form) from 2.2.2 to 2.3.0. - [Release notes](https://github.com/rjsf-team/react-jsonschema-form/releases) - [Commits](https://github.com/rjsf-team/react-jsonschema-form/compare/v2.2.2...v2.3.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index 06324a67a3..5a42953413 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3279,9 +3279,9 @@ react-lifecycles-compat "^3.0.4" "@rjsf/core@^2.1.0": - version "2.2.2" - resolved "https://registry.npmjs.org/@rjsf/core/-/core-2.2.2.tgz#1ebb6fe47448998f3b54e2dea8d58de8a46014dc" - integrity sha512-4d6DHIiTJEkUq5vyl4LIxLGIYYKKnHcprf94oVchUtGQvRFjNUDFxeFQoyr90oaxcBMs2WDDcCgjcFaKVyfErg== + version "2.3.0" + resolved "https://registry.npmjs.org/@rjsf/core/-/core-2.3.0.tgz#334c73d2262ef1a8cda477e238067af7336c5599" + integrity sha512-OZKYHt9tjKhzOH4CvsPiCwepuIacqI++cNmnL2fsxh1IF+uEWGlo3NLDWhhSaBbOv9jps6a5YQcLbLtjNuSwug== dependencies: "@babel/runtime-corejs2" "^7.8.7" "@types/json-schema" "^7.0.4" @@ -5845,17 +5845,7 @@ ajv@^5.0.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.5.5, ajv@^6.7.0: - version "6.12.2" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" - integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ajv@^6.10.1: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.1, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.5.5, ajv@^6.7.0: version "6.12.3" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== From 3e10e5e3d8665edd3529615b1e4af4949dcb1f8f Mon Sep 17 00:00:00 2001 From: Nikita Dudnik Date: Fri, 21 Aug 2020 15:48:08 +0200 Subject: [PATCH 052/359] fix(microsite): animation for techdocs (#2070) --- microsite/pages/en/index.js | 18 ++++++++++++++---- microsite/static/img/techdocs2.gif | Bin 0 -> 89705 bytes 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 microsite/static/img/techdocs2.gif diff --git a/microsite/pages/en/index.js b/microsite/pages/en/index.js index d140a2036b..986db4b732 100644 --- a/microsite/pages/en/index.js +++ b/microsite/pages/en/index.js @@ -353,10 +353,20 @@ class Index extends React.Component { + + + + } /> diff --git a/microsite/static/img/techdocs2.gif b/microsite/static/img/techdocs2.gif new file mode 100644 index 0000000000000000000000000000000000000000..ba937581b3f71e6516d401b25f4260223a570a17 GIT binary patch literal 89705 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 m5vSPW?qKP{i8$?5eY^t&TNc Date: Fri, 21 Aug 2020 17:26:39 -0300 Subject: [PATCH 053/359] cli: Check "NODE_ENV" env var value for config files location --- packages/cli/src/commands/app/build.ts | 2 +- packages/cli/src/commands/app/serve.ts | 2 +- packages/cli/src/commands/backend/dev.ts | 2 +- packages/cli/src/commands/plugin/export.ts | 2 +- packages/cli/src/commands/plugin/serve.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/commands/app/build.ts b/packages/cli/src/commands/app/build.ts index 21d6924b15..5d499cbc2d 100644 --- a/packages/cli/src/commands/app/build.ts +++ b/packages/cli/src/commands/app/build.ts @@ -22,7 +22,7 @@ import { buildBundle } from '../../lib/bundler'; export default async (cmd: Command) => { const appConfigs = await loadConfig({ - env: 'production', + env: process.env.NODE_ENV ?? 'production', rootPaths: [paths.targetRoot, paths.targetDir], }); await buildBundle({ diff --git a/packages/cli/src/commands/app/serve.ts b/packages/cli/src/commands/app/serve.ts index a04e73dceb..c748d12a75 100644 --- a/packages/cli/src/commands/app/serve.ts +++ b/packages/cli/src/commands/app/serve.ts @@ -22,7 +22,7 @@ import { serveBundle } from '../../lib/bundler'; export default async (cmd: Command) => { const appConfigs = await loadConfig({ - env: 'development', + env: process.env.NODE_ENV ?? 'development', rootPaths: [paths.targetRoot, paths.targetDir], }); const waitForExit = await serveBundle({ diff --git a/packages/cli/src/commands/backend/dev.ts b/packages/cli/src/commands/backend/dev.ts index 91c08af201..08a1d74276 100644 --- a/packages/cli/src/commands/backend/dev.ts +++ b/packages/cli/src/commands/backend/dev.ts @@ -22,7 +22,7 @@ import { serveBackend } from '../../lib/bundler/backend'; export default async (cmd: Command) => { const appConfigs = await loadConfig({ - env: 'development', + env: process.env.NODE_ENV ?? 'development', rootPaths: [paths.targetRoot, paths.targetDir], }); const waitForExit = await serveBackend({ diff --git a/packages/cli/src/commands/plugin/export.ts b/packages/cli/src/commands/plugin/export.ts index 8f3c132285..8cbbe5b3ee 100644 --- a/packages/cli/src/commands/plugin/export.ts +++ b/packages/cli/src/commands/plugin/export.ts @@ -22,7 +22,7 @@ import { buildBundle } from '../../lib/bundler'; export default async (cmd: Command) => { const appConfigs = await loadConfig({ - env: 'production', + env: process.env.NODE_ENV ?? 'production', rootPaths: [paths.targetRoot, paths.targetDir], }); await buildBundle({ diff --git a/packages/cli/src/commands/plugin/serve.ts b/packages/cli/src/commands/plugin/serve.ts index a677bec918..8a04df8837 100644 --- a/packages/cli/src/commands/plugin/serve.ts +++ b/packages/cli/src/commands/plugin/serve.ts @@ -22,7 +22,7 @@ import { serveBundle } from '../../lib/bundler'; export default async (cmd: Command) => { const appConfigs = await loadConfig({ - env: 'development', + env: process.env.NODE_ENV ?? 'development', rootPaths: [paths.targetRoot, paths.targetDir], }); const waitForExit = await serveBundle({ From 9414db5ee7206aa5072c430e4497ae0fc53da22f Mon Sep 17 00:00:00 2001 From: Paul Pacheco Date: Fri, 21 Aug 2020 15:38:12 -0500 Subject: [PATCH 054/359] show how to use config when setting up api (#2075) Most API will require some configuration. So it would be more useful to show an example where the config is available when the API's are being wired up --- docs/api/utility-apis.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/api/utility-apis.md b/docs/api/utility-apis.md index ff8c91e178..b4c2fd161e 100644 --- a/docs/api/utility-apis.md +++ b/docs/api/utility-apis.md @@ -71,18 +71,23 @@ import { AlertApiForwarder, ErrorApiForwarder, ErrorAlerter, + ConfigApi } from '@backstage/core'; -const builder = ApiRegistry.builder(); -// The alert API is a self-contained implementation that shows alerts to the user. -const alertApi = builder.add(alertApiRef, new AlertApiForwarder()); +const apis = (config: ConfigApi) => { + const builder = ApiRegistry.builder(); -// The error API uses the alert API to send error notifications to the user. -builder.add(errorApiRef, new ErrorAlerter(alertApi, new ErrorApiForwarder())); + // The alert API is a self-contained implementation that shows alerts to the user. + const alertApi = builder.add(alertApiRef, new AlertApiForwarder()); + + // The error API uses the alert API to send error notifications to the user. + builder.add(errorApiRef, new ErrorAlerter(alertApi, new ErrorApiForwarder())); + return builder.build(); +} const app = createApp({ - apis: apiBuilder.build(), + apis, // ... other config }); ``` From d3db3a784997765295e9d72f3112b5c4688ea54c Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Fri, 21 Aug 2020 23:04:51 +0200 Subject: [PATCH 055/359] feat: IoC, DI, indirection, routes, tabs, catalog, app, github-actions --- packages/app/src/App.tsx | 15 +- packages/app/src/components/Root/Root.tsx | 2 +- .../app/src/components/catalog/Component.tsx | 82 +++++++++ .../app/src/components/catalog/index.tsx | 15 +- plugins/catalog/package.json | 1 + plugins/catalog/src/Router.tsx | 164 ++++++++++++++++++ .../EntityPageTabs/EntityPageTabs.tsx | 94 ++++++++++ .../src/components/EntityPageTabs}/index.ts | 3 +- plugins/catalog/src/hooks/useEntity.ts | 55 ++++++ plugins/catalog/src/index.ts | 3 + plugins/catalog/src/routes.ts | 6 +- plugins/github-actions/package.json | 1 + plugins/github-actions/src/Router.tsx | 34 ++++ .../WorkflowRunDetails/WorkflowRunDetails.tsx | 28 ++- .../WorkflowRunDetailsPage.tsx | 65 ------- .../WorkflowRunsPage/WorkflowRunsPage.tsx | 56 ------ .../WorkflowRunsTable/WorkflowRunsTable.tsx | 19 +- .../src/components/useProjectName.ts | 10 +- plugins/github-actions/src/index.ts | 2 + plugins/github-actions/src/plugin.ts | 17 +- 20 files changed, 494 insertions(+), 178 deletions(-) create mode 100644 packages/app/src/components/catalog/Component.tsx rename plugins/github-actions/src/components/WorkflowRunDetailsPage/index.ts => packages/app/src/components/catalog/index.tsx (60%) create mode 100644 plugins/catalog/src/Router.tsx create mode 100644 plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx rename plugins/{github-actions/src/components/WorkflowRunsPage => catalog/src/components/EntityPageTabs}/index.ts (91%) create mode 100644 plugins/catalog/src/hooks/useEntity.ts create mode 100644 plugins/github-actions/src/Router.tsx delete mode 100644 plugins/github-actions/src/components/WorkflowRunDetailsPage/WorkflowRunDetailsPage.tsx delete mode 100644 plugins/github-actions/src/components/WorkflowRunsPage/WorkflowRunsPage.tsx diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 993fd77978..cb0853a013 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -26,6 +26,11 @@ import * as plugins from './plugins'; import { apis } from './apis'; import { hot } from 'react-hot-loader/root'; import { providers } from './identityProviders'; +import { CatalogPlugin } from '@backstage/plugin-catalog'; +// import { ExplorePlugin } from '@backstage/plugin-explore'; +import { Route, Routes } from 'react-router'; + +import { EntityPage } from './components/catalog'; const app = createApp({ apis, @@ -46,8 +51,16 @@ const app = createApp({ const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); -const AppRoutes = app.getRoutes(); +const AppRoutes = () => ( + + } + /> + {/* } /> */} + +); const App: FC<{}> = () => ( diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 1d32b6e16e..c6ce87444c 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -89,7 +89,7 @@ const Root: FC<{}> = ({ children }) => ( {/* Global nav, not org-specific */} - + diff --git a/packages/app/src/components/catalog/Component.tsx b/packages/app/src/components/catalog/Component.tsx new file mode 100644 index 0000000000..2d2aef9f79 --- /dev/null +++ b/packages/app/src/components/catalog/Component.tsx @@ -0,0 +1,82 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { + GitHubActionsPlugin, + GITHUB_ACTIONS_ANNOTATION, +} from '@backstage/plugin-github-actions'; +import { + useEntity, + EntityPageTabs as Tabs, + EntityMetadataCard, +} from '@backstage/plugin-catalog'; +import { Grid } from '@material-ui/core'; + +const OverviewPage = () => { + const entity = useEntity(); + return ( + + + + ); +}; + +// Just for illustration purposes, gonna live in plugin-circle-ci +const CIRCLE_CI_ANNOTATION = 'circle-ci-slug-dummy'; + +const DefaultEntity = () => { + const entity = useEntity(); + + const isCIAvailable = [ + entity!.metadata!.annotations?.[GITHUB_ACTIONS_ANNOTATION], + entity!.metadata!.annotations?.[CIRCLE_CI_ANNOTATION], + ].some(Boolean); + + return ( + + + + + {isCIAvailable && ( + + {entity!.metadata!.annotations?.[GITHUB_ACTIONS_ANNOTATION] && ( + + )} + + )} + + {/* eslint-disable-next-line no-console */} +
{console.log('was here /docs')}Docs tab contents
+
+
+ ); +}; + +const Service = () => ; +const Website = () => ; +const UnkownEntity = () =>
Unknown entity!
; + +export const ComponentEntity = () => { + const entity = useEntity(); + switch (entity.spec!.type) { + case 'service': + return ; + case 'website': + return ; + default: + return ; + } +}; diff --git a/plugins/github-actions/src/components/WorkflowRunDetailsPage/index.ts b/packages/app/src/components/catalog/index.tsx similarity index 60% rename from plugins/github-actions/src/components/WorkflowRunDetailsPage/index.ts rename to packages/app/src/components/catalog/index.tsx index 443bcacc88..9aaf1a568b 100644 --- a/plugins/github-actions/src/components/WorkflowRunDetailsPage/index.ts +++ b/packages/app/src/components/catalog/index.tsx @@ -13,5 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import React from 'react'; +import { useEntity } from '@backstage/plugin-catalog'; +import { ComponentEntity } from './Component'; -export { WorkflowRunDetailsPage } from './WorkflowRunDetailsPage'; +const UnkownEntityKind = () =>
Unknown entity kind!
; + +export const EntityPage = () => { + const entity = useEntity(); + switch (entity.kind) { + case 'Component': + return ; + default: + return ; + } +}; diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 1e10e91478..4768fdf985 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -36,6 +36,7 @@ "moment": "^2.26.0", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-helmet": "6.1.0", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3", diff --git a/plugins/catalog/src/Router.tsx b/plugins/catalog/src/Router.tsx new file mode 100644 index 0000000000..701b9d1045 --- /dev/null +++ b/plugins/catalog/src/Router.tsx @@ -0,0 +1,164 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React, { FC, useState } from 'react'; +import { CatalogPage } from './components/CatalogPage/CatalogPage'; +// import { EntityPage } from './components/EntityPage/EntityPage'; +import { Route, Routes, useParams, useNavigate } from 'react-router'; +import { entityRoute, rootRoute, entityRouteDefault } from './routes'; +import { useEntityFromUrl, EntityContext, useEntity } from './hooks/useEntity'; +import { + pageTheme, + PageTheme, + Page, + Header, + HeaderLabel, + Content, + Progress, +} from '@backstage/core'; +import { Entity } from '@backstage/catalog-model'; +import { FavouriteEntity } from './components/FavouriteEntity/FavouriteEntity'; +import { Box } from '@material-ui/core'; +import { EntityContextMenu } from './components/EntityContextMenu/EntityContextMenu'; +import { UnregisterEntityDialog } from './components/UnregisterEntityDialog/UnregisterEntityDialog'; +import { Alert } from '@material-ui/lab'; +// const EntityContext = React.createContext(null); +export const getPageTheme = (entity?: Entity): PageTheme => { + const themeKey = entity?.spec?.type?.toString() ?? 'home'; + return pageTheme[themeKey] ?? pageTheme.home; +}; + +const EntityPageTitle = ({ + entity, + title, +}: { + title: string; + entity: Entity | undefined; +}) => ( + + {title} + {entity && } + +); + +function headerProps( + kind: string, + namespace: string | undefined, + name: string, + entity: Entity | undefined, +): { headerTitle: string; headerType: string } { + return { + headerTitle: `${name}${namespace ? ` in ${namespace}` : ''}`, + headerType: (() => { + let t = kind.toLowerCase(); + if (entity && entity.spec && 'type' in entity.spec) { + t += ' — '; + t += (entity.spec as { type: string }).type.toLowerCase(); + } + return t; + })(), + }; +} +const EntityPageLayout = ({ children }: { children: React.ReactNode }) => { + const { entity, loading, error } = useEntityFromUrl(); + const { optionalNamespaceAndName, kind } = useParams() as { + optionalNamespaceAndName: string; + kind: string; + }; + const [name, namespace] = optionalNamespaceAndName.split(':').reverse(); + + const { headerTitle, headerType } = headerProps( + kind, + namespace, + name, + entity!, + ); + + const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); + const navigate = useNavigate(); + const cleanUpAfterRemoval = async () => { + setConfirmationDialogOpen(false); + navigate('/'); + }; + + const showRemovalDialog = () => setConfirmationDialogOpen(true); + + return ( + +
} + pageTitleOverride={headerTitle} + type={headerType} + > + {entity && ( + <> + + + + + )} +
+ + {loading && } + + {entity && ( + + {children} + + )} + {error && ( + + {error.toString()} + + )} + setConfirmationDialogOpen(false)} + /> +
+ ); +}; + +export const CatalogPlugin = ({ EntityPage }) => ( + + } /> + + + + } + /> + + + + } + /> + +); + +export { EntityMetadataCard } from './components/EntityMetadataCard/EntityMetadataCard'; diff --git a/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx b/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx new file mode 100644 index 0000000000..f4f9308669 --- /dev/null +++ b/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx @@ -0,0 +1,94 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { + useParams, + useNavigate, + PartialRouteObject, + matchRoutes, + RouteObject, + useRoutes, + Navigate, + RouteMatch, +} from 'react-router'; +import { Tab, HeaderTabs, Content } from '@backstage/core'; +import { Grid } from '@material-ui/core'; +import { Helmet } from 'react-helmet'; + +const getSelectedIndex = (matchedRoute: RouteMatch, tabs: Tab[]) => { + if (!matchedRoute) return 0; + const tabIndex = tabs.findIndex(t => t.id === matchedRoute.route.path); + return ~tabIndex ? tabIndex : 0; +}; +export const EntityPageTabs = ({ children }: { children: React.ReactNode }) => { + const routes: PartialRouteObject[] = []; + const tabs: Tab[] = []; + const params = useParams(); + const navigate = useNavigate(); + React.Children.forEach(children, child => { + if (!React.isValidElement(child)) { + // Skip conditionals resolved to falses/nulls/undefineds etc + return; + } + const pathAndId = + (child as JSX.Element).props.path + + ((child as JSX.Element).props.exact ? '' : '/*'); + routes.push({ + path: pathAndId, + element: (child as JSX.Element).props.children, + }); + tabs.push({ + id: pathAndId, + label: (child as JSX.Element).props.title, + }); + routes.push({ + path: '/*', + element: , + }); + }); + const [matchedRoute] = + matchRoutes(routes as RouteObject[], `/${params['*']}`) ?? []; + const selectedIndex = getSelectedIndex(matchedRoute, tabs); + const currentTab = tabs[selectedIndex]; + const title = currentTab.label; + + const onTabChange = (index: number) => navigate(tabs[index].id.slice(1, -2)); + + const currentRouteElement = useRoutes(routes); + + return ( + <> + + + + + {currentRouteElement} + + + + ); +}; +type TabProps = { + children: React.ReactNode; + title: string; + path: string; + exact?: boolean; +}; +EntityPageTabs.Tab = (_props: TabProps) => null; diff --git a/plugins/github-actions/src/components/WorkflowRunsPage/index.ts b/plugins/catalog/src/components/EntityPageTabs/index.ts similarity index 91% rename from plugins/github-actions/src/components/WorkflowRunsPage/index.ts rename to plugins/catalog/src/components/EntityPageTabs/index.ts index ef511763f7..83fc3d1095 100644 --- a/plugins/github-actions/src/components/WorkflowRunsPage/index.ts +++ b/plugins/catalog/src/components/EntityPageTabs/index.ts @@ -13,5 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -export { WorkflowRunsPage } from './WorkflowRunsPage'; +export { EntityPageTabs } from './EntityPageTabs'; diff --git a/plugins/catalog/src/hooks/useEntity.ts b/plugins/catalog/src/hooks/useEntity.ts new file mode 100644 index 0000000000..022be1126f --- /dev/null +++ b/plugins/catalog/src/hooks/useEntity.ts @@ -0,0 +1,55 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { useEffect, createContext, useContext } from 'react'; +import { useNavigate, useParams } from 'react-router'; +import { useApi, errorApiRef } from '@backstage/core'; +import { catalogApiRef } from '../api/types'; +import { useAsync } from 'react-use'; +import { Entity } from '@backstage/catalog-model'; + +const REDIRECT_DELAY = 2000; + +export const EntityContext = createContext((null as any) as Entity); +export const useEntityFromUrl = () => { + const { optionalNamespaceAndName, kind } = useParams(); + const [name, namespace] = optionalNamespaceAndName.split(':').reverse(); + const navigate = useNavigate(); + const errorApi = useApi(errorApiRef); + const catalogApi = useApi(catalogApiRef); + + const { value: entity, error, loading } = useAsync( + () => catalogApi.getEntityByName({ kind, namespace, name }), + [catalogApi, kind, namespace, name], + ); + + useEffect(() => { + if (!error && !loading && !entity) { + errorApi.post(new Error('Entity not found!')); + setTimeout(() => { + navigate('/'); + }, REDIRECT_DELAY); + } + }, [errorApi, navigate, error, loading, entity]); + + if (!name) { + navigate('/catalog'); + return { entity: null, loading: null, error: new Error('No name in url') }; + } + + return { entity, loading, error }; +}; + +export const useEntity = () => useContext(EntityContext); diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index 36fd69971d..ba8826aaa9 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -19,3 +19,6 @@ export * from './api/CatalogClient'; export * from './api/types'; export * from './routes'; export { useEntityCompoundName } from './components/useEntityCompoundName'; +export * from './Router'; +export { useEntity } from './hooks/useEntity'; +export { EntityPageTabs } from './components/EntityPageTabs'; diff --git a/plugins/catalog/src/routes.ts b/plugins/catalog/src/routes.ts index a8ff8df685..c3fd97564a 100644 --- a/plugins/catalog/src/routes.ts +++ b/plugins/catalog/src/routes.ts @@ -20,16 +20,16 @@ const NoIcon = () => null; export const rootRoute = createRouteRef({ icon: NoIcon, - path: '/', + path: '', title: 'Catalog', }); export const entityRoute = createRouteRef({ icon: NoIcon, - path: '/catalog/:kind/:optionalNamespaceAndName/:selectedTabId/*', + path: ':kind/:optionalNamespaceAndName/*', title: 'Entity', }); export const entityRouteDefault = createRouteRef({ icon: NoIcon, - path: '/catalog/:kind/:optionalNamespaceAndName', + path: ':kind/:optionalNamespaceAndName', title: 'Entity', }); diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index db35022fae..3386ed0db2 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -35,6 +35,7 @@ "moment": "^2.27.0", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3" }, diff --git a/plugins/github-actions/src/Router.tsx b/plugins/github-actions/src/Router.tsx new file mode 100644 index 0000000000..b843c594b9 --- /dev/null +++ b/plugins/github-actions/src/Router.tsx @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { Entity } from '@backstage/catalog-model'; +import { Routes, Route } from 'react-router'; +import { rootRouteRef, buildRouteRef } from './plugin'; +import { WorkflowRunDetails } from './components/WorkflowRunDetails'; +import { WorkflowRunsTable } from './components/WorkflowRunsTable'; + +export const GitHubActionsPlugin = ({ entity }: { entity: Entity }) => ( + + } + /> + } + /> + +); diff --git a/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx index 8c445ca9f6..d8e3b02524 100644 --- a/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx +++ b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ import React from 'react'; -import { useEntityCompoundName } from '@backstage/plugin-catalog'; import { useWorkflowRunsDetails } from './useWorkflowRunsDetails'; import { useWorkflowRunJobs } from './useWorkflowRunJobs'; import { useProjectName } from '../useProjectName'; @@ -35,13 +34,16 @@ import { LinearProgress, CircularProgress, Theme, - Link, + Breadcrumbs, + Link as MaterialLink, } from '@material-ui/core'; import { Jobs, Job, Step } from '../../api'; import moment from 'moment'; import { WorkflowRunStatus } from '../WorkflowRunStatus'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import ExternalLinkIcon from '@material-ui/icons/Launch'; +import { Entity } from '@backstage/catalog-model'; +import { Link } from '@backstage/core'; const useStyles = makeStyles(theme => ({ root: { @@ -140,18 +142,8 @@ const JobListItem = ({ job, className }: { job: Job; className: string }) => { ); }; -export const WorkflowRunDetails = () => { - let entityCompoundName = useEntityCompoundName(); - if (!entityCompoundName.name) { - // TODO(shmidt-i): remove when is fully integrated - // into the entity view - entityCompoundName = { - kind: 'Component', - name: 'backstage', - namespace: 'default', - }; - } - const projectName = useProjectName(entityCompoundName); +export const WorkflowRunDetails = ({ entity }: { entity: Entity }) => { + const projectName = useProjectName(entity); const [owner, repo] = projectName.value ? projectName.value.split('/') : []; const details = useWorkflowRunsDetails(repo, owner); @@ -170,6 +162,10 @@ export const WorkflowRunDetails = () => { } return (
+ + Workflow runs + Workflow run details + @@ -211,10 +207,10 @@ export const WorkflowRunDetails = () => { {details.value?.html_url && ( - + Workflow runs on GitHub{' '} - + )} diff --git a/plugins/github-actions/src/components/WorkflowRunDetailsPage/WorkflowRunDetailsPage.tsx b/plugins/github-actions/src/components/WorkflowRunDetailsPage/WorkflowRunDetailsPage.tsx deleted file mode 100644 index ec9a3f484d..0000000000 --- a/plugins/github-actions/src/components/WorkflowRunDetailsPage/WorkflowRunDetailsPage.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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 { Typography, Grid, Breadcrumbs } from '@material-ui/core'; - -import React from 'react'; -import { - Link, - Page, - Header, - HeaderLabel, - Content, - ContentHeader, - SupportButton, - pageTheme, -} from '@backstage/core'; - -import { WorkflowRunDetails } from '../WorkflowRunDetails'; - -/** - * A component for Jobs visualization. Jobs are a property of a Workflow Run. - */ -export const WorkflowRunDetailsPage = () => { - return ( - -
- - -
- - - - This plugin allows you to view and interact with your builds within - the GitHub Actions environment. - - - - Workflow runs - Workflow run details - - - - - - - -
- ); -}; diff --git a/plugins/github-actions/src/components/WorkflowRunsPage/WorkflowRunsPage.tsx b/plugins/github-actions/src/components/WorkflowRunsPage/WorkflowRunsPage.tsx deleted file mode 100644 index b75a70f326..0000000000 --- a/plugins/github-actions/src/components/WorkflowRunsPage/WorkflowRunsPage.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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 { - Header, - HeaderLabel, - pageTheme, - Page, - Content, - ContentHeader, - SupportButton, -} from '@backstage/core'; -import { Grid } from '@material-ui/core'; -import React from 'react'; - -import { WorkflowRunsTable } from '../WorkflowRunsTable'; - -export const WorkflowRunsPage = () => { - return ( - -
- - -
- - - - This plugin allows you to view and interact with your builds within - the GitHub Actions environment. - - - - - - - - -
- ); -}; diff --git a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index a2a6ead9b5..7e971118fc 100644 --- a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -25,6 +25,7 @@ import SyncIcon from '@material-ui/icons/Sync'; import { buildRouteRef } from '../../plugin'; import { useEntityCompoundName } from '@backstage/plugin-catalog'; import { useProjectName } from '../useProjectName'; +import { Entity } from '@backstage/catalog-model'; export type WorkflowRun = { id: string; @@ -134,6 +135,7 @@ export const WorkflowRunsTableView: FC = ({ data={runs ?? []} onChangePage={onChangePage} onChangeRowsPerPage={onChangePageSize} + style={{ width: '100%' }} title={ @@ -146,25 +148,14 @@ export const WorkflowRunsTableView: FC = ({ ); }; -export const WorkflowRunsTable = () => { - let entityCompoundName = useEntityCompoundName(); - - if (!entityCompoundName.name) { - // TODO(shmidt-i): remove when is fully integrated - // into the entity view - entityCompoundName = { - kind: 'Component', - name: 'backstage', - namespace: 'default', - }; - } - - const { value: projectName, loading } = useProjectName(entityCompoundName); +export const WorkflowRunsTable = ({ entity }: { entity: Entity }) => { + const { value: projectName, loading } = useProjectName(entity); const [owner, repo] = (projectName ?? '/').split('/'); const [tableProps, { retry, setPage, setPageSize }] = useWorkflowRuns({ owner, repo, }); + return ( { - const catalogApi = useApi(catalogApiRef); +export const GITHUB_ACTIONS_ANNOTATION = 'github.com/project-slug'; +export const useProjectName = (entity: Entity) => { const { value, loading, error } = useAsync(async () => { - const entity = await catalogApi.getEntityByName(name); - return entity?.metadata.annotations?.['github.com/project-slug'] ?? ''; + return entity?.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION] ?? ''; }); return { value, loading, error }; }; diff --git a/plugins/github-actions/src/index.ts b/plugins/github-actions/src/index.ts index 4a69c363cd..f500685507 100644 --- a/plugins/github-actions/src/index.ts +++ b/plugins/github-actions/src/index.ts @@ -17,3 +17,5 @@ export { plugin } from './plugin'; export * from './api'; export { Widget } from './components/Widget'; +export { GitHubActionsPlugin } from './Router'; +export { GITHUB_ACTIONS_ANNOTATION } from './components/useProjectName'; diff --git a/plugins/github-actions/src/plugin.ts b/plugins/github-actions/src/plugin.ts index dbc366bd71..ead33b7959 100644 --- a/plugins/github-actions/src/plugin.ts +++ b/plugins/github-actions/src/plugin.ts @@ -15,28 +15,19 @@ */ import { createPlugin, createRouteRef } from '@backstage/core'; -import { WorkflowRunDetailsPage } from './components/WorkflowRunDetailsPage'; -import { WorkflowRunsPage } from './components/WorkflowRunsPage'; // TODO(freben): This is just a demo route for now export const rootRouteRef = createRouteRef({ - path: '/github-actions', + path: '', title: 'GitHub Actions', }); -export const projectRouteRef = createRouteRef({ - path: '/github-actions/:kind/:optionalNamespaceAndName/', - title: 'GitHub Actions for project', -}); + export const buildRouteRef = createRouteRef({ - path: '/github-actions/workflow-run/:id', + path: ':id', title: 'GitHub Actions Workflow Run', }); export const plugin = createPlugin({ id: 'github-actions', - register({ router }) { - router.addRoute(rootRouteRef, WorkflowRunsPage); - router.addRoute(projectRouteRef, WorkflowRunsPage); - router.addRoute(buildRouteRef, WorkflowRunDetailsPage); - }, + register() {}, }); From d224f9bba7c96a652e767043af3beea79fc8181d Mon Sep 17 00:00:00 2001 From: Mustansar Anwar ul Samad Date: Sat, 22 Aug 2020 10:04:17 +1200 Subject: [PATCH 056/359] Fix webpack sourcemap path for debugging --- packages/cli/src/lib/bundler/config.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 8049ce436d..be81b4bb59 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -198,6 +198,11 @@ export function createBackendConfig( chunkFilename: isDev ? '[name].chunk.js' : '[name].[chunkhash:8].chunk.js', + ...(isDev + ? { + devtoolModuleFilenameTemplate: 'file:///[absolute-resource-path]', + } + : {}), }, plugins: [ new StartServerPlugin('main.js'), From 5ed8e5d05cbbdab15a48d60af0b01243bde421fb Mon Sep 17 00:00:00 2001 From: Chris Simmons Date: Sat, 22 Aug 2020 12:26:06 +1200 Subject: [PATCH 057/359] simplified and cleaned up duplicate code --- .../src/providers/microsoft/provider.ts | 39 +++++-------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index 92f7a10dd8..5997d8e1a8 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -58,15 +58,13 @@ export class MicrosoftAuthProvider implements OAuthProviderHandlers { accessToken: string, params: any, rawProfile: any, - photoURL?: any, + photoURL: any, ): OAuthResponse { let passportProfile: passport.Profile = rawProfile; - if (photoURL) { - passportProfile = { - ...passportProfile, - photos: [{ value: photoURL }], - }; - } + passportProfile = { + ...passportProfile, + photos: [{ value: photoURL }], + }; const profile = makeProfileInfo(passportProfile, params.id_token); const providerInfo = { @@ -99,18 +97,8 @@ export class MicrosoftAuthProvider implements OAuthProviderHandlers { rawProfile: passport.Profile, done: PassportDoneCallback, ) => { - got - .get('https://graph.microsoft.com/v1.0/me/photos/48x48/$value', { - encoding: 'binary', - responseType: 'buffer', - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }) - .then(photoData => { - const photoURL = `data:image/jpeg;base64,${Buffer.from( - photoData.body, - ).toString('base64')}`; + this.getUserPhoto(accessToken) + .then(photoURL => { const authResponse = MicrosoftAuthProvider.transformAuthResponse( accessToken, params, @@ -120,15 +108,7 @@ export class MicrosoftAuthProvider implements OAuthProviderHandlers { done(undefined, authResponse, { refreshToken }); }) .catch(error => { - console.log( - `Error retrieving user photo from Microsoft Graph API: ${error}`, - ); - const authResponse = MicrosoftAuthProvider.transformAuthResponse( - accessToken, - params, - rawProfile, - ); - done(undefined, authResponse, { refreshToken }); + throw new Error(`Error processing auth response: ${error}`); }); }, ); @@ -201,8 +181,9 @@ export class MicrosoftAuthProvider implements OAuthProviderHandlers { }) .catch(error => { console.log( - `Error retrieving user photo from Microsoft Graph API: ${error}`, + `Could not retrieve user profile photo from Microsoft Graph API: ${error}`, ); + // User profile photo is optional, ignore errors and resolve undefined resolve(); }); }); From 290d28d856095ae8b39f0111b40d4ef70cc6c3fc Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Sat, 22 Aug 2020 10:05:21 +0200 Subject: [PATCH 058/359] Update microsite i18n json --- microsite/i18n/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json index d2593a13ce..698d86a26a 100644 --- a/microsite/i18n/en.json +++ b/microsite/i18n/en.json @@ -199,7 +199,7 @@ "title": "Backend plugin" }, "plugins/call-existing-api": { - "title": "Call existing API" + "title": "Call Existing API" }, "plugins/create-a-plugin": { "title": "Create a Backstage Plugin" From 950b316820c04518d30cf21ba10058052891df82 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 22 Aug 2020 10:08:06 +0200 Subject: [PATCH 059/359] cli: switch ts-jest to use isolatedModules --- packages/cli/config/jest.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/cli/config/jest.js b/packages/cli/config/jest.js index d95dd21489..cc7d45c139 100644 --- a/packages/cli/config/jest.js +++ b/packages/cli/config/jest.js @@ -37,7 +37,10 @@ async function getConfig() { // TODO: jest is working on module support, it's possible that we can remove this in the future transform: { '\\.esm\\.js$': require.resolve('jest-esm-transformer'), - '\\.(js|jsx|ts|tsx)$': require.resolve('ts-jest'), + '\\.(js|jsx|ts|tsx)$': [ + require.resolve('ts-jest'), + { isolatedModules: true }, + ], '\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg)$': require.resolve( './jestFileTransform.js', ), From e76d33e97df7195b521c13df22dde5ff05a99376 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Sat, 22 Aug 2020 10:37:02 +0200 Subject: [PATCH 060/359] fix: move catch-all route out of loop --- .../src/components/EntityPageTabs/EntityPageTabs.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx b/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx index f4f9308669..2053c05b91 100644 --- a/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx +++ b/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx @@ -38,6 +38,7 @@ export const EntityPageTabs = ({ children }: { children: React.ReactNode }) => { const tabs: Tab[] = []; const params = useParams(); const navigate = useNavigate(); + React.Children.forEach(children, child => { if (!React.isValidElement(child)) { // Skip conditionals resolved to falses/nulls/undefineds etc @@ -54,11 +55,14 @@ export const EntityPageTabs = ({ children }: { children: React.ReactNode }) => { id: pathAndId, label: (child as JSX.Element).props.title, }); - routes.push({ - path: '/*', - element: , - }); }); + + // Add catch-all for incorrect sub-routes + routes.push({ + path: '/*', + element: , + }); + const [matchedRoute] = matchRoutes(routes as RouteObject[], `/${params['*']}`) ?? []; const selectedIndex = getSelectedIndex(matchedRoute, tabs); From a21cfcd062c2f2944197d4a36fbefb90b5971c22 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 22 Aug 2020 11:11:10 +0200 Subject: [PATCH 061/359] create-app: update template to use DiscoveryApi --- .../default-app/packages/app/src/apis.ts | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/packages/create-app/templates/default-app/packages/app/src/apis.ts b/packages/create-app/templates/default-app/packages/app/src/apis.ts index 35f3e076d6..4cc2ebe03a 100644 --- a/packages/create-app/templates/default-app/packages/app/src/apis.ts +++ b/packages/create-app/templates/default-app/packages/app/src/apis.ts @@ -6,6 +6,8 @@ import { ConfigApi, ErrorApiForwarder, ErrorAlerter, + discoveryApiRef, + UrlPatternDiscovery, oauthRequestApiRef, OAuthRequestManager, storageApiRef, @@ -24,6 +26,10 @@ export const apis = (config: ConfigApi) => { const builder = ApiRegistry.builder(); + const discoveryApi = builder.add( + discoveryApiRef, + UrlPatternDiscovery.compile(`${backendUrl}/{{ pluginId }}`), + ); const alertApi = builder.add(alertApiRef, new AlertApiForwarder()); const errorApi = builder.add( errorApiRef, @@ -33,21 +39,9 @@ export const apis = (config: ConfigApi) => { builder.add(storageApiRef, WebStorage.create({ errorApi })); builder.add(oauthRequestApiRef, new OAuthRequestManager()); - builder.add( - catalogApiRef, - new CatalogClient({ - apiOrigin: backendUrl, - basePath: '/catalog', - }), - ); + builder.add(catalogApiRef, new CatalogClient({ discoveryApi })); - builder.add( - scaffolderApiRef, - new ScaffolderApi({ - apiOrigin: backendUrl, - basePath: '/scaffolder/v1', - }), - ); + builder.add(scaffolderApiRef, new ScaffolderApi({ discoveryApi })); return builder.build(); }; From 257a3b52ed64d82230b270a2c04a9543fc1f42ec Mon Sep 17 00:00:00 2001 From: Omer Farooq Date: Sun, 23 Aug 2020 00:17:57 +1200 Subject: [PATCH 062/359] Add Azure ingestion processor --- .../src/ingestion/LocationReaders.ts | 2 + .../AzureApiReaderProcessor.test.ts | 84 +++++++++++ .../processors/AzureApiReaderProcessor.ts | 134 ++++++++++++++++++ .../RegisterComponentForm.tsx | 2 +- .../RegisterComponentPage.tsx | 13 +- 5 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.test.ts create mode 100644 plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.ts diff --git a/plugins/catalog-backend/src/ingestion/LocationReaders.ts b/plugins/catalog-backend/src/ingestion/LocationReaders.ts index 05f93815b8..29892bb2dc 100644 --- a/plugins/catalog-backend/src/ingestion/LocationReaders.ts +++ b/plugins/catalog-backend/src/ingestion/LocationReaders.ts @@ -31,6 +31,7 @@ import { GithubApiReaderProcessor } from './processors/GithubApiReaderProcessor' import { GitlabApiReaderProcessor } from './processors/GitlabApiReaderProcessor'; import { GitlabReaderProcessor } from './processors/GitlabReaderProcessor'; import { BitbucketApiReaderProcessor } from './processors/BitbucketApiReaderProcessor'; +import { AzureApiReaderProcessor } from './processors/AzureApiReaderProcessor'; import { UrlReaderProcessor } from './processors/UrlReaderProcessor'; import { LocationRefProcessor } from './processors/LocationEntityProcessor'; import { StaticLocationProcessor } from './processors/StaticLocationProcessor'; @@ -79,6 +80,7 @@ export class LocationReaders implements LocationReader { new GitlabApiReaderProcessor(), new GitlabReaderProcessor(), new BitbucketApiReaderProcessor(), + new AzureApiReaderProcessor(), new UrlReaderProcessor(), new YamlProcessor(), new EntityPolicyProcessor(entityPolicy), diff --git a/plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.test.ts new file mode 100644 index 0000000000..35101b2607 --- /dev/null +++ b/plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.test.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { AzureApiReaderProcessor } from './AzureApiReaderProcessor'; + +describe('BitbucketApiReaderProcessor', () => { + it('should build raw api', () => { + const processor = new AzureApiReaderProcessor(); + const tests = [ + { + target: + 'https://dev.azure.com/org-name/project-name/_git/repo-name?path=my-template.yaml&version=GBmaster', + url: new URL( + 'https://dev.azure.com/org-name/project-name/_apis/sourceProviders/TfsGit/filecontents?repository=repo-name&commitOrBranch=master&path=my-template.yaml&api-version=6.0-preview.1', + ), + err: undefined, + }, + { + target: 'https://api.com/a/b/blob/master/path/to/c.yaml', + url: null, + err: + 'Incorrect url: https://api.com/a/b/blob/master/path/to/c.yaml, Error: Wrong Azure Devops URL or Invalid file path', + }, + { + target: 'com/a/b/blob/master/path/to/c.yaml', + url: null, + err: + 'Incorrect url: com/a/b/blob/master/path/to/c.yaml, TypeError: Invalid URL: com/a/b/blob/master/path/to/c.yaml', + }, + ]; + + for (const test of tests) { + if (test.err) { + expect(() => processor.buildRawUrl(test.target)).toThrowError(test.err); + } else if (test.url) { + expect(processor.buildRawUrl(test.target).toString()).toEqual( + test.url.toString(), + ); + } else { + throw new Error( + 'This should not have happened. Either err or url should have matched.', + ); + } + } + }); + + it('should return request options', () => { + const tests = [ + { + token: '0123456789', + expect: { + headers: { + Authorization: 'Basic OjAxMjM0NTY3ODk=', + }, + }, + }, + { + token: '', + expect: { + headers: {}, + }, + }, + ]; + + for (const test of tests) { + process.env.AZURE_PRIVATE_TOKEN = test.token; + const processor = new AzureApiReaderProcessor(); + expect(processor.getRequestOptions()).toEqual(test.expect); + } + }); +}); diff --git a/plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.ts new file mode 100644 index 0000000000..41b4336b4f --- /dev/null +++ b/plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.ts @@ -0,0 +1,134 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { LocationSpec } from '@backstage/catalog-model'; +import fetch, { RequestInit, HeadersInit } from 'node-fetch'; +import * as result from './results'; +import { LocationProcessor, LocationProcessorEmit } from './types'; + +export class AzureApiReaderProcessor implements LocationProcessor { + private privateToken: string = process.env.AZURE_PRIVATE_TOKEN || ''; + + getRequestOptions(): RequestInit { + const headers: HeadersInit = {}; + + if (this.privateToken !== '') { + headers.Authorization = `Basic ${Buffer.from( + `:${this.privateToken}`, + 'utf8', + ).toString('base64')}`; + } + + const requestOptions: RequestInit = { + headers, + }; + + return requestOptions; + } + + async readLocation( + location: LocationSpec, + optional: boolean, + emit: LocationProcessorEmit, + ): Promise { + if (location.type !== 'azure/api') { + return false; + } + + try { + const url = this.buildRawUrl(location.target); + + const response = await fetch(url.toString(), this.getRequestOptions()); + + if (response.ok) { + const data = await response.buffer(); + emit(result.data(location, data)); + } else { + const message = `${location.target} could not be read as ${url}, ${response.status} ${response.statusText}`; + if (response.status === 404) { + if (!optional) { + emit(result.notFoundError(location, message)); + } + } else { + emit(result.generalError(location, message)); + } + } + } catch (e) { + const message = `Unable to read ${location.type} ${location.target}, ${e}`; + emit(result.generalError(location, message)); + } + return true; + } + + // Converts + // from: https://dev.azure.com/{organization}/{project}/_git/reponame?path={path}&version=GB{commitOrBranch}&_a=contents + // to: https://dev.azure.com/{organization}/{project}/_apis/sourceProviders/{providerName}/filecontents?repository={repository}&commitOrBranch={commitOrBranch}&path={path}&api-version=6.0-preview.1 + + buildRawUrl(target: string): URL { + try { + const url = new URL(target); + + const [ + empty, + userOrOrg, + project, + srcKeyword, + repoName, + ] = url.pathname.split('/'); + + const path = url.searchParams.get('path') || ''; + const ref = url.searchParams.get('version')?.substr(2); + + if ( + url.hostname !== 'dev.azure.com' || + empty !== '' || + userOrOrg === '' || + project === '' || + srcKeyword !== '_git' || + repoName === '' || + path === '' || + ref === '' || + !path.match(/\.yaml$/) + ) { + throw new Error('Wrong Azure Devops URL or Invalid file path'); + } + + // transform to api + url.pathname = [ + empty, + userOrOrg, + project, + '_apis', + 'sourceProviders', + 'TfsGit', + 'filecontents', + ].join('/'); + + url.search = [ + `repository=${repoName}`, + `commitOrBranch=${ref}`, + `path=${path}`, + 'api-version=6.0-preview.1', + ].join('&'); + + url.protocol = 'https'; + + return url; + } catch (e) { + throw new Error(`Incorrect url: ${target}, ${e}`); + } + } +} diff --git a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx index c1ea9c454d..a61469e969 100644 --- a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx +++ b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx @@ -71,7 +71,7 @@ const RegisterComponentForm: FC = ({ onSubmit, submitting }) => { name="componentLocation" required margin="normal" - helperText="Enter the full path to the component.yaml file in GitHub to start tracking your component. It must be in a public repo." + helperText="Enter the full path to the component.yaml file in Github, Gitlab, Bitbucket or Azure to start tracking your component. For private repo provide authentication information via config." inputRef={register({ required: true, validate: ComponentIdValidators, diff --git a/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx b/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx index 85d63fe2cb..be497e9aa7 100644 --- a/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx +++ b/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx @@ -79,7 +79,18 @@ const RegisterComponentPage: FC<{}> = () => { setFormState(FormStates.Submitting); const { componentLocation: target } = formData; try { - const data = await catalogApi.addLocation('github', target); + var typeMapping = [ + { url: /https:\/\/gitlab\.com\/.*/, type: 'gitlab' }, + { url: /https:\/\/bitbucket\.org\/.*/, type: 'bitbucket/api' }, + { url: /https:\/\/dev\.azure\.com\/.*/, type: 'azure/api' }, + { url: /.*/, type: 'github' }, + ]; + + var type = typeMapping.filter(function (item) { + return new RegExp(item.url).test(target); + })[0].type; + + const data = await catalogApi.addLocation(type, target); if (!isMounted()) return; From c33fc6c356227a1f5b034350630a879c413e85be Mon Sep 17 00:00:00 2001 From: Omer Farooq Date: Sun, 23 Aug 2020 00:41:58 +1200 Subject: [PATCH 063/359] Fix linting errors --- .../RegisterComponentPage/RegisterComponentPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx b/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx index be497e9aa7..1f0eb4fead 100644 --- a/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx +++ b/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx @@ -79,14 +79,14 @@ const RegisterComponentPage: FC<{}> = () => { setFormState(FormStates.Submitting); const { componentLocation: target } = formData; try { - var typeMapping = [ + const typeMapping = [ { url: /https:\/\/gitlab\.com\/.*/, type: 'gitlab' }, { url: /https:\/\/bitbucket\.org\/.*/, type: 'bitbucket/api' }, { url: /https:\/\/dev\.azure\.com\/.*/, type: 'azure/api' }, { url: /.*/, type: 'github' }, ]; - var type = typeMapping.filter(function (item) { + const type = typeMapping.filter(item => { return new RegExp(item.url).test(target); })[0].type; From 7e454952f7e623fde4ea0437ed98e08c9a6ac0b8 Mon Sep 17 00:00:00 2001 From: Omer Farooq Date: Sun, 23 Aug 2020 00:59:03 +1200 Subject: [PATCH 064/359] fix broken UI test --- .../RegisterComponentForm/RegisterComponentForm.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx index 01402ca19b..fd7a1cc0f8 100644 --- a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx +++ b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx @@ -30,14 +30,14 @@ const setup = (props?: Partial) => { ), }; }; -describe('RegisterComponentForm', () => { +fdescribe('RegisterComponentForm', () => { afterEach(() => cleanup()); it('should initially render a disabled button', async () => { const { rendered } = setup(); expect( await rendered.findByText( - 'Enter the full path to the component.yaml file in GitHub to start tracking your component. It must be in a public repo.', + 'Enter the full path to the component.yaml file in Github, Gitlab, Bitbucket or Azure to start tracking your component. For private repo provide authentication information via config.', ), ).toBeInTheDocument(); From 54b6a9cc06d17e3365096c3314c4e78e4bb87cca Mon Sep 17 00:00:00 2001 From: Omer Farooq Date: Sun, 23 Aug 2020 01:02:32 +1200 Subject: [PATCH 065/359] undo test focus --- .../RegisterComponentForm/RegisterComponentForm.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx index fd7a1cc0f8..d4d2cf4789 100644 --- a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx +++ b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx @@ -30,7 +30,7 @@ const setup = (props?: Partial) => { ), }; }; -fdescribe('RegisterComponentForm', () => { +describe('RegisterComponentForm', () => { afterEach(() => cleanup()); it('should initially render a disabled button', async () => { From f57f6a7402965d7c95eb0b73af2608f779770e99 Mon Sep 17 00:00:00 2001 From: Omer Farooq Date: Sun, 23 Aug 2020 14:26:39 +1200 Subject: [PATCH 066/359] modify ends with .yaml check to contains .yaml --- .../processors/AzureApiReaderProcessor.test.ts | 2 +- .../ingestion/processors/YamlProcessor.test.ts | 16 ++++++++++++++++ .../src/ingestion/processors/YamlProcessor.ts | 2 +- .../RegisterComponentPage.tsx | 4 +--- .../register-component/src/util/validate.test.ts | 3 ++- plugins/register-component/src/util/validate.ts | 4 ++-- 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.test.ts index 35101b2607..0ef111fcb1 100644 --- a/plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/AzureApiReaderProcessor.test.ts @@ -16,7 +16,7 @@ import { AzureApiReaderProcessor } from './AzureApiReaderProcessor'; -describe('BitbucketApiReaderProcessor', () => { +describe('AzureApiReaderProcessor', () => { it('should build raw api', () => { const processor = new AzureApiReaderProcessor(); const tests = [ diff --git a/plugins/catalog-backend/src/ingestion/processors/YamlProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/YamlProcessor.test.ts index 587793a6de..9f1ede3f9e 100644 --- a/plugins/catalog-backend/src/ingestion/processors/YamlProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/YamlProcessor.test.ts @@ -51,6 +51,22 @@ describe('YamlProcessor', () => { expect(never).not.toBeCalled(); }); + it('should process url that contains yaml', async () => { + const containsYamlLocationSpec = { + type: 'url', + target: 'http://example.com/component?path=test.yaml&c=1&d=2', + }; + + const buffer = Buffer.from([]); + const emit = jest.fn(); + + expect( + await processor.parseData(buffer, containsYamlLocationSpec, emit), + ).toBe(true); + + expect(emit).toBeCalled(); + }); + it('should process entity with yaml', async () => { const entity = { apiVersion: 'backstage.io/v1alpha1', diff --git a/plugins/catalog-backend/src/ingestion/processors/YamlProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/YamlProcessor.ts index 6a2b5cf419..79ae55fae1 100644 --- a/plugins/catalog-backend/src/ingestion/processors/YamlProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/YamlProcessor.ts @@ -26,7 +26,7 @@ export class YamlProcessor implements LocationProcessor { location: LocationSpec, emit: LocationProcessorEmit, ): Promise { - if (!location.target.match(/\.ya?ml$/)) { + if (!location.target.match(/\.ya?ml/)) { return false; } diff --git a/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx b/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx index 1f0eb4fead..e61f904347 100644 --- a/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx +++ b/plugins/register-component/src/components/RegisterComponentPage/RegisterComponentPage.tsx @@ -86,9 +86,7 @@ const RegisterComponentPage: FC<{}> = () => { { url: /.*/, type: 'github' }, ]; - const type = typeMapping.filter(item => { - return new RegExp(item.url).test(target); - })[0].type; + const type = typeMapping.filter(item => item.url.test(target))[0].type; const data = await catalogApi.addLocation(type, target); diff --git a/plugins/register-component/src/util/validate.test.ts b/plugins/register-component/src/util/validate.test.ts index d062655bd2..e4fc90699b 100644 --- a/plugins/register-component/src/util/validate.test.ts +++ b/plugins/register-component/src/util/validate.test.ts @@ -31,11 +31,12 @@ describe('ComponentIdValidators', () => { }); }); describe('yamlValidator', () => { - const errorMessage = "Must end with '.yaml'."; + const errorMessage = "Must contain '.yaml'."; test.each([ [true, '.yaml'], [true, 'http://example.com/blob/master/service.yaml'], [true, 'https://example.yaml'], + [true, 'https://example.com?path=abc.yaml&c=1'], [errorMessage, '.yml'], [errorMessage, 'http://example.com/blob/master/service'], [errorMessage, undefined], diff --git a/plugins/register-component/src/util/validate.ts b/plugins/register-component/src/util/validate.ts index 78d20995f5..8552872015 100644 --- a/plugins/register-component/src/util/validate.ts +++ b/plugins/register-component/src/util/validate.ts @@ -19,6 +19,6 @@ export const ComponentIdValidators = { (typeof value === 'string' && value.match(/^https:\/\//) !== null) || 'Must start with https://.', yamlValidator: (value: any) => - (typeof value === 'string' && value.match(/.yaml$/) !== null) || - "Must end with '.yaml'.", + (typeof value === 'string' && value.match(/.yaml/) !== null) || + "Must contain '.yaml'.", }; From d7a10bcf127788d797e2ee2a469d2a76100434f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Sun, 23 Aug 2020 09:39:53 +0200 Subject: [PATCH 067/359] Update README.md (#2083) --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index ee9e85fd7f..b5e8704548 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,6 @@ For more information go to [backstage.io](https://backstage.io) or join our [Dis A detailed project roadmap, including already delivered milestones, is available [here](https://backstage.io/docs/overview/roadmap). -## Overview - -The Backstage platform consists of a number of different components: - -- **app** - Main web application that users interact with. It's built up by a number of different _Plugins_. This repo contains an example implementation of an app (located in `packages/app`) and you can easily get started with your own app by [creating one](docs/getting-started/create-an-app.md). -- [**plugins**](https://github.com/spotify/backstage/tree/master/plugins) - Each plugin is treated as a self-contained web app and can include almost any type of content. Plugins all use a common set of platform API's and reusable UI components. Plugins can fetch data either from the _backend_ or through any RESTful API exposed through the _proxy_. -- [**service catalog**](https://github.com/spotify/backstage/tree/master/packages/backend) - Service that holds the model of your software ecosystem, including organisational information and what team owns what software. The backend also has a Plugin model for extending its graph. -- [**proxy**](https://github.com/spotify/backstage/tree/master/plugins/proxy-backend) - Terminates HTTPS and exposes any RESTful API to Plugins. -- **identity** - A backend service that holds your organisation's metadata. - ## Getting Started There are two different ways to get started with Backstage, either by creating a standalone app, or by cloning this repo. Which method you use depends on what you're planning to do. From 5f1c74452acbb294e32e21f8e2889343253155e0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 16:18:48 +0200 Subject: [PATCH 068/359] workflows: rename Frontend CI to just CI --- .github/workflows/{frontend.yml => ci.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{frontend.yml => ci.yml} (98%) diff --git a/.github/workflows/frontend.yml b/.github/workflows/ci.yml similarity index 98% rename from .github/workflows/frontend.yml rename to .github/workflows/ci.yml index dc65b987a8..105642e284 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/ci.yml @@ -1,11 +1,11 @@ -name: Frontend CI +name: CI on: pull_request: paths-ignore: - 'microsite/**' jobs: - build: + verify: runs-on: ubuntu-latest strategy: From 1358739063eae124dce5ba11064b891a1ade68ed Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 11:25:10 +0200 Subject: [PATCH 069/359] cli: move out e2e-test to separate package --- packages/cli/package.json | 7 +--- packages/e2e-test/.eslintrc.js | 7 ++++ packages/e2e-test/README.md | 24 +++++++++++++ packages/e2e-test/package.json | 35 +++++++++++++++++++ .../e2e-test => e2e-test/src}/.eslintrc.js | 0 .../{cli/e2e-test => e2e-test/src}/helpers.js | 0 .../cli-e2e-test.js => e2e-test/src/index.js} | 0 7 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 packages/e2e-test/.eslintrc.js create mode 100644 packages/e2e-test/README.md create mode 100644 packages/e2e-test/package.json rename packages/{cli/e2e-test => e2e-test/src}/.eslintrc.js (100%) rename packages/{cli/e2e-test => e2e-test/src}/helpers.js (100%) rename packages/{cli/e2e-test/cli-e2e-test.js => e2e-test/src/index.js} (100%) diff --git a/packages/cli/package.json b/packages/cli/package.json index e5a8eafc81..0291e720d2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -21,7 +21,6 @@ "build": "backstage-cli build --outputs cjs", "lint": "backstage-cli lint", "test": "backstage-cli test", - "test:e2e": "node e2e-test/cli-e2e-test.js", "clean": "backstage-cli clean", "start": "nodemon --" }, @@ -69,9 +68,7 @@ "jest-css-modules": "^2.1.0", "jest-esm-transformer": "^1.0.0", "mini-css-extract-plugin": "^0.9.0", - "node-fetch": "^2.6.0", "ora": "^4.0.3", - "pgtools": "^0.3.0", "raw-loader": "^4.0.1", "react": "^16.0.0", "react-dev-utils": "^10.2.1", @@ -118,9 +115,7 @@ "@types/webpack-dev-server": "^3.10.0", "del": "^5.1.0", "nodemon": "^2.0.2", - "tree-kill": "^1.2.2", - "ts-node": "^8.6.2", - "zombie": "^6.1.4" + "ts-node": "^8.6.2" }, "files": [ "asset-types", diff --git a/packages/e2e-test/.eslintrc.js b/packages/e2e-test/.eslintrc.js new file mode 100644 index 0000000000..69bec6cd2a --- /dev/null +++ b/packages/e2e-test/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], + ignorePatterns: ['templates/**'], + rules: { + 'no-console': 0, + }, +}; diff --git a/packages/e2e-test/README.md b/packages/e2e-test/README.md new file mode 100644 index 0000000000..6cf50283c5 --- /dev/null +++ b/packages/e2e-test/README.md @@ -0,0 +1,24 @@ +# e2e-test + +End-to-end test for verifying Backstage packages. + +## Usage + +This package is only meant for usage within the Backstage monorepo. + +All packages need to be installed and built before running the test. In a fresh clone of this repo you first need to run the following from the repo root: + +```sh +yarn install +yarn tsc +yarn build +``` + +Once those tasks have completed, you can now run the test using `yarn start` inside this package. + +If you make changes to other packages you will need to rerun `yarn tsc && yarn build`. Changes to this package do not require a rebuild. + +## Documentation + +- [Backstage Readme](https://github.com/spotify/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/spotify/backstage/blob/master/docs/README.md) diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json new file mode 100644 index 0000000000..d2f9dc9784 --- /dev/null +++ b/packages/e2e-test/package.json @@ -0,0 +1,35 @@ +{ + "name": "e2e-test", + "description": "E2E test for verifying Backstage packages", + "version": "0.0.0", + "private": true, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/spotify/backstage", + "directory": "packages/e2e-test" + }, + "keywords": [ + "backstage" + ], + "license": "Apache-2.0", + "main": "src/index.js", + "scripts": { + "start": "ts-node .", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "test:e2e": "yarn start" + }, + "devDependencies": { + "@backstage/cli-common": "^0.1.1-alpha.19", + "@types/fs-extra": "^9.0.1", + "@types/node": "^13.7.2", + "fs-extra": "^9.0.0", + "handlebars": "^4.7.3", + "node-fetch": "^2.6.0", + "pgtools": "^0.3.0", + "tree-kill": "^1.2.2", + "ts-node": "^8.6.2", + "zombie": "^6.1.4" + } +} diff --git a/packages/cli/e2e-test/.eslintrc.js b/packages/e2e-test/src/.eslintrc.js similarity index 100% rename from packages/cli/e2e-test/.eslintrc.js rename to packages/e2e-test/src/.eslintrc.js diff --git a/packages/cli/e2e-test/helpers.js b/packages/e2e-test/src/helpers.js similarity index 100% rename from packages/cli/e2e-test/helpers.js rename to packages/e2e-test/src/helpers.js diff --git a/packages/cli/e2e-test/cli-e2e-test.js b/packages/e2e-test/src/index.js similarity index 100% rename from packages/cli/e2e-test/cli-e2e-test.js rename to packages/e2e-test/src/index.js From 9052dfc5bf7d4fba51ca17fd271a2b3885c6e14a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 11:28:59 +0200 Subject: [PATCH 070/359] e2e-test: merge eslint rules --- packages/e2e-test/.eslintrc.js | 9 +++++++++ packages/e2e-test/src/.eslintrc.js | 29 ----------------------------- 2 files changed, 9 insertions(+), 29 deletions(-) delete mode 100644 packages/e2e-test/src/.eslintrc.js diff --git a/packages/e2e-test/.eslintrc.js b/packages/e2e-test/.eslintrc.js index 69bec6cd2a..ce2592e03b 100644 --- a/packages/e2e-test/.eslintrc.js +++ b/packages/e2e-test/.eslintrc.js @@ -3,5 +3,14 @@ module.exports = { ignorePatterns: ['templates/**'], rules: { 'no-console': 0, + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: true, + optionalDependencies: false, + peerDependencies: false, + bundledDependencies: false, + }, + ], }, }; diff --git a/packages/e2e-test/src/.eslintrc.js b/packages/e2e-test/src/.eslintrc.js deleted file mode 100644 index 274c7426b8..0000000000 --- a/packages/e2e-test/src/.eslintrc.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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. - */ - -module.exports = { - rules: { - 'import/no-extraneous-dependencies': [ - 'error', - { - devDependencies: true, - optionalDependencies: true, - peerDependencies: true, - bundledDependencies: true, - }, - ], - }, -}; From 0d240696ebad85b2e2e78b346d08d9e283dab565 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 12:00:37 +0200 Subject: [PATCH 071/359] e2e-test: port to typscript --- packages/e2e-test/package.json | 4 +- .../e2e-test/src/{helpers.js => helpers.ts} | 48 ++++++------- packages/e2e-test/src/{index.js => index.ts} | 68 +++++++++++-------- packages/e2e-test/src/types.d.ts | 18 +++++ 4 files changed, 82 insertions(+), 56 deletions(-) rename packages/e2e-test/src/{helpers.js => helpers.ts} (84%) rename packages/e2e-test/src/{index.js => index.ts} (86%) create mode 100644 packages/e2e-test/src/types.d.ts diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index d2f9dc9784..389f4b6f93 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -13,9 +13,9 @@ "backstage" ], "license": "Apache-2.0", - "main": "src/index.js", + "main": "src/index.ts", "scripts": { - "start": "ts-node .", + "start": "yarn ts-node --transpile-only --compiler-options '{\"module\":\"CommonJS\"}' .", "lint": "backstage-cli lint", "test": "backstage-cli test", "test:e2e": "yarn start" diff --git a/packages/e2e-test/src/helpers.js b/packages/e2e-test/src/helpers.ts similarity index 84% rename from packages/e2e-test/src/helpers.js rename to packages/e2e-test/src/helpers.ts index 580f53cbe8..783b7e3988 100644 --- a/packages/e2e-test/src/helpers.js +++ b/packages/e2e-test/src/helpers.ts @@ -14,16 +14,21 @@ * limitations under the License. */ -const { spawn, execFile: execFileCb } = require('child_process'); -const { promisify } = require('util'); +import { + spawn, + execFile as execFileCb, + SpawnOptions, + ChildProcess, +} from 'child_process'; +import { promisify } from 'util'; const execFile = promisify(execFileCb); const EXPECTED_LOAD_ERRORS = /ECONNREFUSED|ECONNRESET|did not get to load all resources/; -function spawnPiped(cmd, options) { - function pipeWithPrefix(stream, prefix = '') { - return data => { +export function spawnPiped(cmd: string[], options?: SpawnOptions) { + function pipeWithPrefix(stream: NodeJS.WriteStream, prefix = '') { + return (data: Buffer) => { const prefixedMsg = data .toString('utf8') .trimRight() @@ -40,11 +45,11 @@ function spawnPiped(cmd, options) { child.on('error', handleError); const logPrefix = cmd.map(s => s.replace(/.+\//, '')).join(' '); - child.stdout.on( + child.stdout?.on( 'data', pipeWithPrefix(process.stdout, `[${logPrefix}].out: `), ); - child.stderr.on( + child.stderr?.on( 'data', pipeWithPrefix(process.stderr, `[${logPrefix}].err: `), ); @@ -52,7 +57,7 @@ function spawnPiped(cmd, options) { return child; } -async function runPlain(cmd, options) { +export async function runPlain(cmd: string[], options?: SpawnOptions) { try { const { stdout } = await execFile(cmd[0], cmd.slice(1), { ...options, @@ -70,8 +75,9 @@ async function runPlain(cmd, options) { } } -function handleError(err) { +export function handleError(err: Error & { code?: unknown }) { process.stdout.write(`${err.name}: ${err.stack || err.message}\n`); + if (typeof err.code === 'number') { process.exit(err.code); } else { @@ -85,7 +91,7 @@ function handleError(err) { * .cancel() is available * @returns {Promise} Promise of resolution */ -function waitFor(fn) { +export function waitFor(fn: () => boolean) { return new Promise(resolve => { const handle = setInterval(() => { if (fn()) { @@ -97,7 +103,7 @@ function waitFor(fn) { }); } -async function waitForExit(child) { +export async function waitForExit(child: ChildProcess) { if (child.exitCode !== null) { throw new Error(`Child already exited with code ${child.exitCode}`); } @@ -113,10 +119,10 @@ async function waitForExit(child) { ); } -async function waitForPageWithText( - browser, - path, - text, +export async function waitForPageWithText( + browser: any, + path: string, + text: string, { intervalMs = 1000, maxLoadAttempts = 240, maxFindTextAttempts = 3 } = {}, ) { let loadAttempts = 0; @@ -163,16 +169,6 @@ async function waitForPageWithText( } } -function print(msg) { +export function print(msg: string) { return process.stdout.write(`${msg}\n`); } - -module.exports = { - spawnPiped, - runPlain, - handleError, - waitFor, - waitForExit, - waitForPageWithText, - print, -}; diff --git a/packages/e2e-test/src/index.js b/packages/e2e-test/src/index.ts similarity index 86% rename from packages/e2e-test/src/index.js rename to packages/e2e-test/src/index.ts index c4004f7c46..a455964f44 100644 --- a/packages/e2e-test/src/index.js +++ b/packages/e2e-test/src/index.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -const os = require('os'); -const fs = require('fs-extra'); -const fetch = require('node-fetch'); -const handlebars = require('handlebars'); -const killTree = require('tree-kill'); -const { resolve: resolvePath, join: joinPath } = require('path'); -const Browser = require('zombie'); -const { +import os from 'os'; +import fs from 'fs-extra'; +import fetch from 'node-fetch'; +import handlebars from 'handlebars'; +import killTree from 'tree-kill'; +import { resolve as resolvePath, join as joinPath } from 'path'; +import Browser from 'zombie'; +import { spawnPiped, runPlain, handleError, @@ -29,8 +29,8 @@ const { waitFor, waitForExit, print, -} = require('./helpers'); -const pgtools = require('pgtools'); +} from './helpers'; +import pgtools from 'pgtools'; async function main() { const rootDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'backstage-e2e-')); @@ -59,7 +59,7 @@ async function main() { /** * Builds a dist workspace that contains the cli and core packages */ -async function buildDistWorkspace(workspaceName, rootDir) { +async function buildDistWorkspace(workspaceName: string, rootDir: string) { const workspaceDir = resolvePath(rootDir, workspaceName); await fs.ensureDir(workspaceDir); @@ -121,13 +121,20 @@ async function buildDistWorkspace(workspaceName, rootDir) { /** * Pin the yarn version in a directory to the one we're using in the Backstage repo */ -async function pinYarnVersion(dir) { +async function pinYarnVersion(dir: string) { const repoRoot = resolvePath(__dirname, '../../..'); const yarnRc = await fs.readFile(resolvePath(repoRoot, '.yarnrc'), 'utf8'); const yarnRcLines = yarnRc.split('\n'); const yarnPathLine = yarnRcLines.find(line => line.startsWith('yarn-path')); - const [, localYarnPath] = yarnPathLine.match(/"(.*)"/); + if (!yarnPathLine) { + throw new Error(`Unable to find 'yarn-path' in ${yarnRc}`); + } + const match = yarnPathLine.match(/"(.*)"/); + if (!match) { + throw new Error(`Invalid 'yarn-path' in ${yarnRc}`); + } + const [, localYarnPath] = match; const yarnPath = resolvePath(repoRoot, localYarnPath); await fs.writeFile(resolvePath(dir, '.yarnrc'), `yarn-path "${yarnPath}"\n`); @@ -136,7 +143,12 @@ async function pinYarnVersion(dir) { /** * Creates a new app inside rootDir called test-app, using packages from the workspaceDir */ -async function createApp(appName, isPostgres, workspaceDir, rootDir) { +async function createApp( + appName: string, + isPostgres: boolean, + workspaceDir: string, + rootDir: string, +) { const child = spawnPiped( [ 'node', @@ -150,20 +162,20 @@ async function createApp(appName, isPostgres, workspaceDir, rootDir) { try { let stdout = ''; - child.stdout.on('data', data => { + child.stdout?.on('data', data => { stdout = stdout + data.toString('utf8'); }); await waitFor(() => stdout.includes('Enter a name for the app')); - child.stdin.write(`${appName}\n`); + child.stdin?.write(`${appName}\n`); await waitFor(() => stdout.includes('Select database for the backend')); if (!isPostgres) { // Simulate down arrow press - child.stdin.write(`\u001B\u005B\u0042`); + child.stdin?.write(`\u001B\u005B\u0042`); } - child.stdin.write(`\n`); + child.stdin?.write(`\n`); print('Waiting for app create script to be done'); await waitForExit(child); @@ -205,7 +217,7 @@ async function createApp(appName, isPostgres, workspaceDir, rootDir) { /** * This points dependency resolutions into the workspace for each package that is present there */ -async function overrideModuleResolutions(appDir, workspaceDir) { +async function overrideModuleResolutions(appDir: string, workspaceDir: string) { const pkgJsonPath = resolvePath(appDir, 'package.json'); const pkgJson = await fs.readJson(pkgJsonPath); @@ -231,19 +243,19 @@ async function overrideModuleResolutions(appDir, workspaceDir) { /** * Uses create-plugin command to create a new plugin in the app */ -async function createPlugin(pluginName, appDir) { +async function createPlugin(pluginName: string, appDir: string) { const child = spawnPiped(['yarn', 'create-plugin'], { cwd: appDir, }); try { let stdout = ''; - child.stdout.on('data', data => { + child.stdout?.on('data', (data: Buffer) => { stdout = stdout + data.toString('utf8'); }); await waitFor(() => stdout.includes('Enter an ID for the plugin')); - child.stdin.write(`${pluginName}\n`); + child.stdin?.write(`${pluginName}\n`); // await waitFor(() => stdout.includes('Enter the owner(s) of the plugin')); // child.stdin.write('@someuser\n'); @@ -266,7 +278,7 @@ async function createPlugin(pluginName, appDir) { /** * Start serving the newly created app and make sure that the create plugin is rendering correctly */ -async function testAppServe(pluginName, appDir) { +async function testAppServe(pluginName: string, appDir: string) { const startApp = spawnPiped(['yarn', 'start'], { cwd: appDir, }); @@ -302,7 +314,7 @@ async function testAppServe(pluginName, appDir) { } /** Creates PG databases (drops if exists before) */ -async function createDB(database) { +async function createDB(database: string) { const config = { host: process.env.POSTGRES_HOST, port: process.env.POSTGRES_PORT, @@ -321,7 +333,7 @@ async function createDB(database) { /** * Start serving the newly created backend and make sure that all db migrations works correctly */ -async function testBackendStart(appDir, isPostgres) { +async function testBackendStart(appDir: string, isPostgres: boolean) { if (isPostgres) { print('Creating DBs'); await Promise.all( @@ -343,10 +355,10 @@ async function testBackendStart(appDir, isPostgres) { let stdout = ''; let stderr = ''; - child.stdout.on('data', data => { + child.stdout?.on('data', (data: Buffer) => { stdout = stdout + data.toString('utf8'); }); - child.stderr.on('data', data => { + child.stderr?.on('data', (data: Buffer) => { stderr = stderr + data.toString('utf8'); }); let successful = false; @@ -384,4 +396,4 @@ async function testBackendStart(appDir, isPostgres) { } process.on('unhandledRejection', handleError); -main(process.argv.slice(2)).catch(handleError); +main().catch(handleError); diff --git a/packages/e2e-test/src/types.d.ts b/packages/e2e-test/src/types.d.ts new file mode 100644 index 0000000000..0ae86490ee --- /dev/null +++ b/packages/e2e-test/src/types.d.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * 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. + */ + +declare module 'zombie'; +declare module 'pgtools'; From 831ce12b7d3b31325688fc001f4737aa3da078b9 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 12:21:17 +0200 Subject: [PATCH 072/359] e2e-test: refactor dep detection and use cli-common to resolve paths --- packages/e2e-test/src/index.ts | 60 +++++++++++++--------------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/packages/e2e-test/src/index.ts b/packages/e2e-test/src/index.ts index a455964f44..46992ab966 100644 --- a/packages/e2e-test/src/index.ts +++ b/packages/e2e-test/src/index.ts @@ -31,6 +31,9 @@ import { print, } from './helpers'; import pgtools from 'pgtools'; +import { findPaths } from '@backstage/cli-common'; + +const paths = findPaths(__dirname); async function main() { const rootDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'backstage-e2e-')); @@ -63,34 +66,22 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) { const workspaceDir = resolvePath(rootDir, workspaceName); await fs.ensureDir(workspaceDir); - // We grab the needed dependencies from the template packages - const appPkgTemplate = await fs.readFile( - resolvePath( - __dirname, - '../../create-app/templates/default-app/packages/app/package.json.hbs', - ), - 'utf8', - ); - const appPkg = JSON.parse( - handlebars.compile(appPkgTemplate)({ version: '0.0.0' }), - ); - const appDeps = Object.keys(appPkg.dependencies).filter(name => - name.startsWith('@backstage/'), - ); - - const backendPkgTemplate = await fs.readFile( - resolvePath( - __dirname, - '../../create-app/templates/default-app/packages/backend/package.json.hbs', - ), - 'utf8', - ); - const backendPkg = JSON.parse( - handlebars.compile(backendPkgTemplate)({ version: '0.0.0' }), - ); - const backendDeps = Object.keys(backendPkg.dependencies).filter(name => - name.startsWith('@backstage/'), - ); + // We grab the needed dependencies from the create app template + const createAppDeps: string[] = []; + for (const pkgPath of ['packages/app', 'packages/backend']) { + const path = paths.resolveOwnRoot( + 'packages/create-app/templates/default-app', + pkgPath, + 'package.json.hbs', + ); + const appPkgTemplate = await fs.readFile(path, 'utf8'); + const { dependencies: allDeps } = JSON.parse( + handlebars.compile(appPkgTemplate)({ version: '0.0.0' }), + ); + createAppDeps.push( + ...Object.keys(allDeps).filter(name => name.startsWith('@backstage/')), + ); + } print(`Preparing workspace`); await runPlain([ @@ -98,13 +89,8 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) { 'backstage-cli', 'build-workspace', workspaceDir, - '@backstage/cli', '@backstage/create-app', - '@backstage/core', - '@backstage/dev-utils', - '@backstage/test-utils', - ...appDeps, - ...backendDeps, + ...createAppDeps, ]); print('Pinning yarn version in workspace'); @@ -122,9 +108,7 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) { * Pin the yarn version in a directory to the one we're using in the Backstage repo */ async function pinYarnVersion(dir: string) { - const repoRoot = resolvePath(__dirname, '../../..'); - - const yarnRc = await fs.readFile(resolvePath(repoRoot, '.yarnrc'), 'utf8'); + const yarnRc = await fs.readFile(paths.resolveOwnRoot('.yarnrc'), 'utf8'); const yarnRcLines = yarnRc.split('\n'); const yarnPathLine = yarnRcLines.find(line => line.startsWith('yarn-path')); if (!yarnPathLine) { @@ -135,7 +119,7 @@ async function pinYarnVersion(dir: string) { throw new Error(`Invalid 'yarn-path' in ${yarnRc}`); } const [, localYarnPath] = match; - const yarnPath = resolvePath(repoRoot, localYarnPath); + const yarnPath = paths.resolveOwnRoot(localYarnPath); await fs.writeFile(resolvePath(dir, '.yarnrc'), `yarn-path "${yarnPath}"\n`); } From 0d09f12c4d51a0cbab012c4c86f0699c71993a2f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 13:28:16 +0200 Subject: [PATCH 073/359] workflows: update e2e test workflows --- .github/workflows/{cli-win.yml => e2e-win.yml} | 14 ++++++++------ .github/workflows/{cli.yml => e2e.yml} | 14 +++++++------- 2 files changed, 15 insertions(+), 13 deletions(-) rename .github/workflows/{cli-win.yml => e2e-win.yml} (77%) rename .github/workflows/{cli.yml => e2e.yml} (87%) diff --git a/.github/workflows/cli-win.yml b/.github/workflows/e2e-win.yml similarity index 77% rename from .github/workflows/cli-win.yml rename to .github/workflows/e2e-win.yml index 4e7a186282..5f75f3d9c8 100644 --- a/.github/workflows/cli-win.yml +++ b/.github/workflows/e2e-win.yml @@ -1,12 +1,13 @@ -name: CLI Test Windows +name: E2E Test Windows -# Building on windows is really slow, so this workflow is separate from cli.yml and only builds on changes +# Building on windows is really slow, so this workflow is separate from e2e.yml and only builds on changes # to the cli itself. They're more likely to introduce issues on windows, compared to changes to core and yarn.lock. on: pull_request: paths: - - '.github/workflows/cli-win.yml' + - '.github/workflows/e2e-win.yml' - 'packages/cli/**' + - 'packages/e2e/**' - 'packages/create-app/**' jobs: @@ -42,6 +43,7 @@ jobs: - name: yarn install run: yarn install --frozen-lockfile - run: yarn tsc - - run: yarn build - - name: verify app and plugin creation - run: node ${{ github.workspace }}/packages/cli/e2e-test/cli-e2e-test.js + - name: yarn build + run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite + - name: run E2E test + run: yarn workspace e2e-test start diff --git a/.github/workflows/cli.yml b/.github/workflows/e2e.yml similarity index 87% rename from .github/workflows/cli.yml rename to .github/workflows/e2e.yml index 158310b5ea..45b9d7e2d8 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/e2e.yml @@ -1,11 +1,10 @@ -name: CLI Test +name: E2E Test Linux on: pull_request: paths-ignore: - 'microsite/**' - jobs: build: runs-on: ${{ matrix.os }} @@ -51,13 +50,14 @@ jobs: - name: yarn install run: yarn install --frozen-lockfile - run: yarn tsc - - run: yarn build - - name: verify app and plugin creation + - name: yarn build + run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite + - name: run E2E test + run: | + sudo sysctl fs.inotify.max_user_watches=524288 + yarn workspace e2e-test start env: POSTGRES_HOST: localhost POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }} POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - run: | - sudo sysctl fs.inotify.max_user_watches=524288 - node ${{ github.workspace }}/packages/cli/e2e-test/cli-e2e-test.js From 5fc0ce3e375b62704dd92d2bd66abdd447eac75b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 13:43:18 +0200 Subject: [PATCH 074/359] e2e-test: make waitFor time out --- packages/e2e-test/src/helpers.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/e2e-test/src/helpers.ts b/packages/e2e-test/src/helpers.ts index 783b7e3988..b59de75860 100644 --- a/packages/e2e-test/src/helpers.ts +++ b/packages/e2e-test/src/helpers.ts @@ -91,9 +91,14 @@ export function handleError(err: Error & { code?: unknown }) { * .cancel() is available * @returns {Promise} Promise of resolution */ -export function waitFor(fn: () => boolean) { - return new Promise(resolve => { +export function waitFor(fn: () => boolean, maxSeconds: number = 120) { + let count = 0; + return new Promise((resolve, reject) => { const handle = setInterval(() => { + if (count++ > maxSeconds * 10) { + reject(new Error('Timed out while waiting for condition')); + return; + } if (fn()) { clearInterval(handle); resolve(); From b73612df6476122043a9da08a29b2eba7b7d73fb Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 14:56:05 +0200 Subject: [PATCH 075/359] e2e-test: add minimal test --- packages/e2e-test/src/helpers.test.ts | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 packages/e2e-test/src/helpers.test.ts diff --git a/packages/e2e-test/src/helpers.test.ts b/packages/e2e-test/src/helpers.test.ts new file mode 100644 index 0000000000..196241f9e0 --- /dev/null +++ b/packages/e2e-test/src/helpers.test.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { waitFor } from './helpers'; + +describe('waitFor', () => { + it('should wait for true', async () => { + const fn = jest.fn().mockReturnValue(true); + await waitFor(fn); + expect(fn).toHaveBeenCalledTimes(1); + }); + + it('should time out', async () => { + const fn = jest.fn().mockReturnValue(false); + await expect(waitFor(fn, 1)).rejects.toThrow( + 'Timed out while waiting for condition', + ); + expect(fn).toHaveBeenCalled(); + }); +}); From e8e6deb0e46c48585a702837379900a133d7131f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 15:33:15 +0200 Subject: [PATCH 076/359] e2e-test: switch to using ts-node register to pass options --- packages/e2e-test/package.json | 4 +-- .../e2e-test/src/{index.ts => e2e-test.ts} | 0 packages/e2e-test/src/index.js | 25 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) rename packages/e2e-test/src/{index.ts => e2e-test.ts} (100%) create mode 100644 packages/e2e-test/src/index.js diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 389f4b6f93..8e6c730431 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -13,9 +13,9 @@ "backstage" ], "license": "Apache-2.0", - "main": "src/index.ts", + "main": "src/index.js", "scripts": { - "start": "yarn ts-node --transpile-only --compiler-options '{\"module\":\"CommonJS\"}' .", + "start": "node .", "lint": "backstage-cli lint", "test": "backstage-cli test", "test:e2e": "yarn start" diff --git a/packages/e2e-test/src/index.ts b/packages/e2e-test/src/e2e-test.ts similarity index 100% rename from packages/e2e-test/src/index.ts rename to packages/e2e-test/src/e2e-test.ts diff --git a/packages/e2e-test/src/index.js b/packages/e2e-test/src/index.js new file mode 100644 index 0000000000..90f8030a0e --- /dev/null +++ b/packages/e2e-test/src/index.js @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Spotify AB + * + * 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. + */ + +require('ts-node').register({ + transpileOnly: true, + project: require('path').resolve(__dirname, '../../../tsconfig.json'), + compilerOptions: { + module: 'CommonJS', + }, +}); + +require('./e2e-test'); From 5b722c179fb43932cf6109a389dc8d6a57e209dd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 15:48:34 +0200 Subject: [PATCH 077/359] e2e-test: include local deps from plugin template --- packages/e2e-test/src/e2e-test.ts | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/e2e-test/src/e2e-test.ts b/packages/e2e-test/src/e2e-test.ts index 46992ab966..7377655497 100644 --- a/packages/e2e-test/src/e2e-test.ts +++ b/packages/e2e-test/src/e2e-test.ts @@ -35,6 +35,12 @@ import { findPaths } from '@backstage/cli-common'; const paths = findPaths(__dirname); +const templatePackagePaths = [ + 'packages/cli/templates/default-plugin/package.json.hbs', + 'packages/create-app/templates/default-app/packages/app/package.json.hbs', + 'packages/create-app/templates/default-app/packages/backend/package.json.hbs', +]; + async function main() { const rootDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'backstage-e2e-')); print(`CLI E2E test root: ${rootDir}\n`); @@ -67,20 +73,18 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) { await fs.ensureDir(workspaceDir); // We grab the needed dependencies from the create app template - const createAppDeps: string[] = []; - for (const pkgPath of ['packages/app', 'packages/backend']) { - const path = paths.resolveOwnRoot( - 'packages/create-app/templates/default-app', - pkgPath, - 'package.json.hbs', - ); - const appPkgTemplate = await fs.readFile(path, 'utf8'); - const { dependencies: allDeps } = JSON.parse( - handlebars.compile(appPkgTemplate)({ version: '0.0.0' }), - ); - createAppDeps.push( - ...Object.keys(allDeps).filter(name => name.startsWith('@backstage/')), + const createAppDeps = new Set(); + for (const pkgJsonPath of templatePackagePaths) { + const path = paths.resolveOwnRoot(pkgJsonPath); + const pkgTemplate = await fs.readFile(path, 'utf8'); + const { dependencies = {}, devDependencies = {} } = JSON.parse( + handlebars.compile(pkgTemplate)({ version: '0.0.0' }), ); + + Array() + .concat(Object.keys(dependencies), Object.keys(devDependencies)) + .filter(name => name.startsWith('@backstage/')) + .forEach(dep => createAppDeps.add(dep)); } print(`Preparing workspace`); From 4e5422bd34275f82a479dbc48c5ba8e46b3b62d0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 15:55:29 +0200 Subject: [PATCH 078/359] workflows: add Windows master build --- .github/workflows/master-win.yml | 47 ++++++++++++++++++++++++++++++++ .github/workflows/master.yml | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/master-win.yml diff --git a/.github/workflows/master-win.yml b/.github/workflows/master-win.yml new file mode 100644 index 0000000000..074f944250 --- /dev/null +++ b/.github/workflows/master-win.yml @@ -0,0 +1,47 @@ +name: Master Build Windows + +on: + push: + branches: [master] + +jobs: + build: + runs-on: windows-latest + + strategy: + matrix: + node-version: [12.x] + + env: + CI: true + NODE_OPTIONS: --max-old-space-size=4096 + + steps: + - uses: actions/checkout@v2 + - name: find location of global yarn cache + id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v2 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + - name: use node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + + - name: yarn install + run: yarn install --frozen-lockfile + +# Tests are broken on Windows, disabled for now + # - name: test + # run: yarn lerna -- run test diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 3ef0c1fee3..805095ec53 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -1,4 +1,4 @@ -name: Master Build +name: Main Master Build on: push: From ed733cf02967d29a549c77d887f1276a83868bc6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 23 Aug 2020 18:01:13 +0200 Subject: [PATCH 079/359] docsgen: fix link generation on windows --- packages/docgen/src/docgen/ApiDocGenerator.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/docgen/src/docgen/ApiDocGenerator.ts b/packages/docgen/src/docgen/ApiDocGenerator.ts index bfe449155b..3edcfedb9c 100644 --- a/packages/docgen/src/docgen/ApiDocGenerator.ts +++ b/packages/docgen/src/docgen/ApiDocGenerator.ts @@ -15,7 +15,11 @@ */ import ts from 'typescript'; -import { relative } from 'path'; +import { + relative as relativePath, + sep as pathSep, + posix as posixPath, +} from 'path'; import { ExportedInstance, ApiDoc, @@ -25,6 +29,12 @@ import { TypeLink, } from './types'; +// Always use unix path separators for the relative file +// paths, since we'll be using those for HTTP URLs. +function relativeLink(basePath: string, filePath: string) { + return relativePath(basePath, filePath).split(pathSep).join(posixPath.sep); +} + /** * The ApiDocGenerator uses the typescript compiler API to build the data structure that * describes a Backstage API and all of it's related types. @@ -58,7 +68,7 @@ export default class ApiDocGenerator { const id = this.getObjectPropertyLiteral(info, 'id'); const description = this.getObjectPropertyLiteral(info, 'description'); - const file = relative(this.basePath, source.fileName); + const file = relativeLink(this.basePath, source.fileName); const { line } = source.getLineAndCharacterOfPosition( apiInstance.node.getStart(), ); @@ -111,7 +121,7 @@ export default class ApiDocGenerator { const name = (type.aliasSymbol || type.symbol).name; const [declaration] = (type.aliasSymbol || type.symbol).declarations; const sourceFile = declaration.getSourceFile(); - const file = relative(this.basePath, sourceFile.fileName); + const file = relativeLink(this.basePath, sourceFile.fileName); const { line } = sourceFile.getLineAndCharacterOfPosition( declaration.getStart(), ); @@ -234,7 +244,7 @@ export default class ApiDocGenerator { const { line } = sourceFile.getLineAndCharacterOfPosition( declaration.getStart(), ); - const file = relative(this.basePath, sourceFile.fileName); + const file = relativeLink(this.basePath, sourceFile.fileName); const typeInfo = { id: (symbol as any).id, name: symbol.name, From ed5a0e702821d36ef6ffec0ef0582c37869f90ec Mon Sep 17 00:00:00 2001 From: Andrew Thauer <6507159+andrewthauer@users.noreply.github.com> Date: Mon, 24 Aug 2020 00:34:16 -0400 Subject: [PATCH 080/359] docs: add wealthsimple to adopters list (#2090) --- ADOPTERS.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index 7a96198529..dc02ad3b85 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,13 +1,14 @@ -| Organization | Contact | Description of Use | -| --------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | -| [Spotify](https://www.spotify.com) | [@stefanalund](https://github.com/stefanalund) | Main interface towards all of Spotify's infrastructure and technical documentation. | -| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. | -| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | -| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | -| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | -| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | -| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | -| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | -| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | -| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | +| Organization | Contact | Description of Use | +| -------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | +| [Spotify](https://www.spotify.com) | [@stefanalund](https://github.com/stefanalund) | Main interface towards all of Spotify's infrastructure and technical documentation. | +| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. | +| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | +| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | +| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | +| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | +| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | +| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | +| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | +| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | +| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | From 9939dace2f2de5bcb0685f5d84bd472187d6020a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 24 Aug 2020 14:54:54 +0200 Subject: [PATCH 081/359] fix(e2e): attempt to unbreak e2e tests due to bad rollup-plugin-dts release --- packages/cli/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 0291e720d2..b57ee8cb67 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -76,7 +76,7 @@ "recursive-readdir": "^2.2.2", "replace-in-file": "^6.0.0", "rollup": "2.23.x", - "rollup-plugin-dts": "^1.4.6", + "rollup-plugin-dts": "1.4.11", "rollup-plugin-esbuild": "^2.0.0", "rollup-plugin-image-files": "^1.4.2", "rollup-plugin-peer-deps-external": "^2.2.2", diff --git a/yarn.lock b/yarn.lock index 5a42953413..2ad2111e29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20008,10 +20008,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rollup-plugin-dts@^1.4.6: - version "1.4.10" - resolved "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-1.4.10.tgz#0373c4284a2ba4d2d72df69c289271a816bc2736" - integrity sha512-bL6MBXc8lK7D5b/tYbHaglxs4ZxMQTQilGA6Xm9KQBEj4h9ZwIDlAsvDooGjJ/cOw23r3POTRtSCEyTHxtzHJg== +rollup-plugin-dts@1.4.11: + version "1.4.11" + resolved "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-1.4.11.tgz#aedf0b7bb91d51e20b755e2c18e840edfc7af7a1" + integrity sha512-yiScAMKgwH77b44a/IFGgjLsmwSlNfQhEM+eCb2uMrupQMPE1n/12wrnT431+v1u6wYMF1XuHqldh+v/7mTvYA== optionalDependencies: "@babel/code-frame" "^7.10.4" From 9bacbf14cb6eea3abb0a5fc38d7efe938d2297a5 Mon Sep 17 00:00:00 2001 From: Marc Bruggmann Date: Mon, 24 Aug 2020 15:12:33 +0200 Subject: [PATCH 082/359] Update the system model to focus more on the core entities. (#2011) * Update the system model to focus more on the core entities. Now that we have a way of labeling and annotating catalogue entities, we can focus the system model more on the entities themselves. I believe that also makes more sense as a model that is shared across many companies - it's easier to standardize on the core entities, and leave some room to implement further abstraction or categories to individual implementations. * Re-add system and domain conepts. --- .../software-model-core-entities.png | Bin 0 -> 17277 bytes .../features/software-catalog/system-model.md | 129 ++++++++++-------- 2 files changed, 71 insertions(+), 58 deletions(-) create mode 100644 docs/features/software-catalog/software-model-core-entities.png diff --git a/docs/features/software-catalog/software-model-core-entities.png b/docs/features/software-catalog/software-model-core-entities.png new file mode 100644 index 0000000000000000000000000000000000000000..b718b7527c3e5a8ac723f09d294457f8bda6ce44 GIT binary patch literal 17277 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 literal 0 HcmV?d00001 diff --git a/docs/features/software-catalog/system-model.md b/docs/features/software-catalog/system-model.md index 4b2884e097..435fe05b48 100644 --- a/docs/features/software-catalog/system-model.md +++ b/docs/features/software-catalog/system-model.md @@ -3,55 +3,24 @@ id: system-model title: System Model --- -We believe that a strong shared understanding and terminology around systems, -software and resources leads to a better Backstage experience. +We believe that a strong shared understanding and terminology around software +and resources leads to a better Backstage experience. _This description originates from [this RFC](https://github.com/spotify/backstage/issues/390). Note that some of the concepts are not yet supported in Backstage._ -## Concepts +## Core Entities -We model our technology using these five concepts (further explained below): +We model software in the Backstage catalogue using these three core entities +(further explained below): -- **Domains** are a high-level grouping of systems -- **Systems** encapsulate the implementation of APIs -- **APIs** are the boundaries between different components and systems -- **Components** are pieces of software +- **Components** are individual pieces of software +- **APIs** are the boundaries between different components - **Resources** are physical or virtual infrastructure needed to operate a - system + component -![Software Ecosystem Model_ Public Github version](https://user-images.githubusercontent.com/24575/77633084-39bcde80-6f4f-11ea-8251-f8df561a3652.png) - -### Domain - -While systems are the basic level of encapsulation for resources, components and -APIs, it is often useful to group a collection of systems that share -terminology, domain models, business purpose, or documentation, i.e. they form a -bounded context. - -For example, it would make sense if the different systems in the “Payments” -domain would come with some documentation on how to accept payments for a new -product or use-case, share the same entity types in their APIs, and integrate -well with each other. - -### System - -With increasing complexity in software, we believe that systems form an -important abstraction level to help us reason about software ecosystems. Systems -are a useful concept in that they allow us to ignore the implementation details -of a certain functionality for consumers, while allowing the owning team to make -changes as they see fit (leading to low coupling). - -A system, in this sense, is a collection of resources and components that -exposes one or several APIs. Components and resources in a system are typically -owned by the same team and are expected to co-evolve. As such, systems usually -consist of at most a handful of components. - -For example, a playlist management system might encapsulate a backend service to -update playlists, a backend service to query them, and a database to store them. -It could expose an RPC API, a daily snapshots dataset, and an event stream of -playlist updates. +![](system-model-core-entities.png) ### Component @@ -60,34 +29,78 @@ backend service or data pipeline (list not exhaustive). A component can be tracked in source control, or use some existing open source or commercial software. -A component can implement APIs for other components to consume. It might depend -on the resources of the system it belongs to, and APIs from other components or -other systems. All other aspects of the component, e.g. any code dependencies, -must be encapsulated. +A component can implement APIs for other components to consume. In turn it +might depend on APIs implemented by other components, or resources that are +attached to it at runtime. ### API -We believe APIs form an important (maybe the most important) abstraction that -allows large software ecosystems to scale. Thus, APIs are a first class citizen -in the Backstage model and the primary way to discover existing functionality in +APIs form an important (maybe the most important) abstraction that allows large +software ecosystems to scale. Thus, APIs are a first class citizen in the +Backstage model and the primary way to discover existing functionality in the ecosystem. -APIs are implemented by components and form boundaries between components and -systems. They might be defined using an RPC IDL (eg Protobuf, GraphQL, ...), a -data schema (eg Avro, TFRecord, ...), or as code interfaces. In any case, APIs -exposed by components need to be in a known machine-readable format so we can +APIs are implemented by components and form boundaries between components. They +might be defined using an RPC IDL (eg Protobuf, GraphQL, ...), a data schema +(eg Avro, TFRecord, ...), or as code interfaces. In any case, APIs exposed by +components need to be in a known machine-readable format so we can build further tooling and analysis on top. -Some APIs might be exposed by the system, making them available for any other -Spotify component to consume. Those public APIs must be documented and humanly -discoverable in Backstage. +APIs have a visibility: they are either public (making them available for any +other component to consume), restricted (only available to a whitelisted set of +consumers), or private (only available within their system). As public APIs are +going to be the primary way interaction between components, Backstage supports +documenting, indexing and searching all APIs so we can browse them as +developers. ### Resource -Resources are the infrastructure a system needs to operate, like BigTable -databases, Pub/Sub topics, S3 buckets or CDNs. Modelling them together with -components and systems will better allow us to visualize resource footprint, and -create tooling around them. +Resources are the infrastructure a component needs to operate at runtime, like +BigTable databases, Pub/Sub topics, S3 buckets or CDNs. Modelling them together +with components and systems will better allow us to visualize resource +footprint, and create tooling around them. + +## Ecosystem Modeling + +A large catalogue of components, APIs and resources can be highly granular +and hard to understand as a whole. It might thus be convenient to further +categorize these entities using the following (optional) concepts: +* **Systems** are a collection of entities that cooperate to perform some + function +* **Domains** relate entities and systems to part of the business + +### System + +With increasing complexity in software, systems form an important abstraction +level to help us reason about software ecosystems. Systems are a useful concept +in that they allow us to ignore the implementation details of a certain +functionality for consumers, while allowing the owning team to make changes as +they see fit (leading to low coupling). + +A system, in this sense, is a collection of resources and components that +exposes one or several public APIs. The main benefit of modelling a system is +that it hides its resources and private APIs between the components for any +consumers. This means that as the owner, you can evolve the implementation, in +terms of components and resources, without your consumers being able to notice. +Typically, a system will consist of at most a handful of components (see +Domain for a grouping of systems). + +For example, a playlist management system might encapsulate a backend service +to update playlists, a backend service to query them, and a database to store +them. It could expose an RPC API, a daily snapshots dataset, and an event +stream of playlist updates. + +### Domain +While systems are the basic level of encapsulation for related entities, it is +often useful to group a collection of systems that share terminology, domain +models, metrics, KPIs, business purpose, or documentation, i.e. they form a +bounded context. + +For example, it would make sense if the different systems in the “Payments” +domain would come with some documentation on how to accept payments for a new +product or use-case, share the same entity types in their APIs, and integrate +well with each other. Other domains could be “Content Ingestion”, “Ads” or +“Search”. ## Current status From b5ada28f261f0de18a07aa6821d9749dcb441573 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Mon, 24 Aug 2020 15:52:44 +0200 Subject: [PATCH 083/359] Added techdocs-ref to backstage entity --- catalog-info.yaml | 1 + plugins/github-actions/scripts/sample.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/catalog-info.yaml b/catalog-info.yaml index 86b67f95cd..4221ee6dc6 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -7,6 +7,7 @@ metadata: annotations: github.com/project-slug: spotify/backstage backstage.io/github-actions-id: spotify/backstage + backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git spec: type: library owner: Spotify diff --git a/plugins/github-actions/scripts/sample.yaml b/plugins/github-actions/scripts/sample.yaml index d582bf9c35..7d9de50463 100644 --- a/plugins/github-actions/scripts/sample.yaml +++ b/plugins/github-actions/scripts/sample.yaml @@ -5,6 +5,7 @@ metadata: description: backstage.io annotations: github.com/project-slug: 'spotify/backstage' + backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git spec: type: website lifecycle: production From 889e1fd4bb929228aedb04f9bf34768acbf87145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Mon, 24 Aug 2020 16:37:51 +0200 Subject: [PATCH 084/359] Docs: strategies for adopting Backstage (#2092) * Adopting guidelines * More updates * Tactics * Add metrics * Add MTTR --- docs/README.md | 1 + docs/assets/pop.png | Bin 0 -> 103686 bytes docs/overview/adopting.md | 144 ++++++++++++++++++++++++++++++++++++++ docs/plugins/index.md | 2 +- microsite/i18n/en.json | 5 +- microsite/sidebars.json | 3 +- mkdocs.yml | 1 + 7 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 docs/assets/pop.png create mode 100644 docs/overview/adopting.md diff --git a/docs/README.md b/docs/README.md index b9e79a9e0a..55e178b691 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,6 +12,7 @@ better yet, a pull request. - [Architecture and terminology](overview/architecture-terminology.md) - [Roadmap](overview/roadmap.md) - [Vision](overview/vision.md) + - [Strategies for adopting](overview/adopting.md) - Getting started - [Running Backstage locally](getting-started/index.md) - [Installation](getting-started/installation.md) diff --git a/docs/assets/pop.png b/docs/assets/pop.png new file mode 100644 index 0000000000000000000000000000000000000000..441126f33c08cda5006dcfd913f76b027904bbd5 GIT binary patch literal 103686 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ðYy$A#7{R;Q|C`ENYI;6OPe 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 Case study: Inside Spotify we have a team that owns our CI platform. They +> don't only maintain the pipelines and build servers, but also expose their +> product in Backstage through a plugin. Since they also +> [maintain their own API](../plugins/call-existing-api.md), they can improve +> their product by iterating on API and UI in lockstep. Because the plugin +> follows our [platform design guidelines](../dls/design.md) their customers get +> a CI experience that is consistent with other tools on the platform (and users +> don't have to become experts in Jenkins). + +### Tactics + +Example of tactics we have used to evangelize Backstage internally: + +- Arrange "Lunch & Learns" and seminars. Frequently offer teams interested in + Backstage development to come to a seminar where you show, for example, how to + build a plugin from scratch. + +- Embedding. As contributing teams start development of their first plugin it is + often very appreciated to have one person from the central team come over and + "embed" for a Sprint or two. + +- Hack days. Backstage-focused Hackathons or hack days is a fun way to get + people into plugin development. + +- Show & tell meetings. In order to build an internal community around Backstage + we have quarterly meetings where anyone working on Backstage is invited to + present their work. This is a not only a great way to get early feedback, but + also helps coordination between teams that are building overlapping + experiences. + +- Provide metrics. Add instrumentation to your Backstage deployment and make + metrics available to contributing teams. At Spotify we have even gone so far + as sending out weekly digest email showing how usage metrics have changed for + individual plugins. + +- Pro-actively identify new plugins. Reach out to teams that own internal UIs or + platforms that you think would make sense to consolidate into Backstage. + +## Metrics + +These are some of the metrics that you can use to verify if Backstage has a +successful impact on your software development process: + +- **Onboarding time** Time until new engineers are productive. At Spotify we + measure this as the time until the employee has merged their 10th PR (this + metric was down 55% two years after deploying Backstage). + +- **Number of merges per developer/day** Less time spent jumping between + different tools and looking for information means more time to focus on + shipping code. A second level of bottlenecks can be identified if you + categorize contributions by domain (services, web, data, etc). + +- **Deploys to production** Cousin to the metric above: How many times does an + engineer push changes into production. + +- **MTTR** With clear ownership of all the pieces in your micro services + ecosystem and all tools integrated into one place, Backstage makes it quicker + for teams to find the root cause of failures, and fix them. + +- **Context switching** Reducing context switching can help engineers stay in + the "zone". We measure the number of different tools an engineer have to + interact with in order to get a certain job done (e.g. push a change, follow + it into production and validate it did not break anything). + +- **T-shapedness** A + [T-shaped](https://medium.com/@jchyip/why-t-shaped-people-e8706198e437) + engineer is someone that is able to contribute to different domains of + engineering. Teams with T-shaped people have fewer bottlenecks and can + therefore deliver more consistently. Backstage makes it easier to be T-shaped + since tools and infrastructure is consistent between domains, and information + is available centrally. + +- **eNPS** Surveys asking about how productive people feel, how easy it is to + find information and overall satisfaction with internal tools. + +- **Fragmentation** _(Experimental)_ Backstage + [Software Templates](../features/software-templates/index.md) helps drive + standardization in your software ecosystem. By measuring the variance in + technology between different software components it is possible to get a sense + of the overall fragmentation in your ecosystem. Examples could include: + framework versions, languages, deployment methods and various code quality + measurements. + +Additionally, these proxy metrics can be used to validate the success of +Backstage as _the_ platform: + +- Nr of teams that have contributed at least one plugin (currently 63 inside + Spotify) + +- Nr of total plugins (currently 135 inside Spotify) + +- % of contributions coming from outside the central Backstage team (currently + 85% inside Spotify) diff --git a/docs/plugins/index.md b/docs/plugins/index.md index bb8951f959..c5b67bcec4 100644 --- a/docs/plugins/index.md +++ b/docs/plugins/index.md @@ -1,6 +1,6 @@ --- id: index -title: Intro +title: Intro to plugins --- Backstage is a single-page application composed of a set of plugins. diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json index 698d86a26a..96c874f496 100644 --- a/microsite/i18n/en.json +++ b/microsite/i18n/en.json @@ -177,6 +177,9 @@ "journey": { "title": "journey" }, + "overview/adopting": { + "title": "Strategies for adopting" + }, "overview/architecture-overview": { "title": "Architecture overview" }, @@ -208,7 +211,7 @@ "title": "Existing plugins" }, "plugins/index": { - "title": "Intro" + "title": "Intro to plugins" }, "plugins/plugin-development": { "title": "Plugin Development in Backstage" diff --git a/microsite/sidebars.json b/microsite/sidebars.json index b07f326086..efb76397aa 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -5,7 +5,8 @@ "overview/architecture-overview", "overview/architecture-terminology", "overview/roadmap", - "overview/vision" + "overview/vision", + "overview/adopting" ], "Getting Started": [ "getting-started/index", diff --git a/mkdocs.yml b/mkdocs.yml index 085d83b7cc..e227ab8f9d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,6 +8,7 @@ nav: - Architecture and terminology: 'overview/architecture-terminology.md' - Roadmap: 'overview/roadmap.md' - Vision: 'overview/vision.md' + - Strategies for adopting: 'overview/adopting.md' - Getting started: - Running Backstage locally: 'getting-started/index.md' - Installation: 'getting-started/installation.md' From edab8b2ad89e7a90f61b9d6cff82371f20d2d00f Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Mon, 24 Aug 2020 17:01:27 +0200 Subject: [PATCH 085/359] fix(template tags): fixes issues with tags specified with uppercase (#2049) * fix(template tags): fixes issues with tags specified with uppercase * adds new validator for metadata tags * fix(template tags): change tags to be lowercase * fix(template tags docs): update docs to include lowercased tags * fix(validator): change back to using isValidDnsLabel for tags * fix(software catalog docs): use lowercase tags in docs --- docs/features/software-catalog/descriptor-format.md | 4 ++-- docs/features/software-templates/adding-templates.md | 4 ++-- .../extending/create-your-own-templater.md | 4 ++-- .../sample-templates/create-react-app/template.yaml | 6 +++--- .../sample-templates/react-ssr-template/template.yaml | 4 ++-- .../sample-templates/springboot-grpc-template/template.yaml | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index a7d57890dc..78dfcf365c 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -370,8 +370,8 @@ metadata: description: Next.js application skeleton for creating isomorphic web applications. tags: - - Recommended - - React + - recommended + - react spec: owner: web@example.com templater: cookiecutter diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index b0405cab79..67e9c23605 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -22,8 +22,8 @@ metadata: Next.js application skeleton for creating isomorphic web applications. # some tags to display in the frontend tags: - - Recommended - - React + - recommended + - react spec: # which templater key to use in the templaters builder templater: cookiecutter diff --git a/docs/features/software-templates/extending/create-your-own-templater.md b/docs/features/software-templates/extending/create-your-own-templater.md index ac05eed3b0..a39f87924b 100644 --- a/docs/features/software-templates/extending/create-your-own-templater.md +++ b/docs/features/software-templates/extending/create-your-own-templater.md @@ -116,8 +116,8 @@ metadata: description: Next.js application skeleton for creating isomorphic web applications. tags: - - Recommended - - React + - recommended + - react spec: owner: web@example.com templater: handlebars diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml b/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml index 958882e9fd..a377d60ccc 100644 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml @@ -5,9 +5,9 @@ metadata: title: Create React App Template description: Create a new CRA website project tags: - - Experimental - - React - - CRA + - experimental + - react + - cra spec: owner: web@example.com templater: cra diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml index 5594a3a919..ee051de6e0 100644 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml @@ -5,8 +5,8 @@ metadata: title: React SSR Template description: Create a website powered with Next.js tags: - - Recommended - - React + - recommended + - react spec: owner: web@example.com templater: cookiecutter diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml index 520d0a62ac..c6189fe7c8 100644 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml @@ -5,8 +5,8 @@ metadata: title: Spring Boot GRPC Service description: Create a simple microservice using gRPC and Spring Boot Java tags: - - Recommended - - Java + - recommended + - java spec: owner: service@example.com templater: cookiecutter From e94c1e321eeec57d93f42f6c5664758edbbb7363 Mon Sep 17 00:00:00 2001 From: Jeff Feng <46946747+fengypants@users.noreply.github.com> Date: Mon, 24 Aug 2020 12:54:18 -0400 Subject: [PATCH 086/359] Update siteConfig.js (#2100) Now that we have a Docs tab on backstage.io, seems like the GitHub menu item can go back to linking to the top of the repo. --- microsite/siteConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/siteConfig.js b/microsite/siteConfig.js index f0c8ce5fa5..73a25cd931 100644 --- a/microsite/siteConfig.js +++ b/microsite/siteConfig.js @@ -35,7 +35,7 @@ const siteConfig = { // For no header links in the top nav bar -> headerLinks: [], headerLinks: [ { - href: 'https://github.com/spotify/backstage#backstage', + href: 'https://github.com/spotify/backstage', label: 'GitHub', }, { From ba8bef38a637a3544b0aaae56a839fefcb393e56 Mon Sep 17 00:00:00 2001 From: Omer Farooq Date: Tue, 25 Aug 2020 18:30:12 +1200 Subject: [PATCH 087/359] update casing --- .../RegisterComponentForm/RegisterComponentForm.test.tsx | 2 +- .../components/RegisterComponentForm/RegisterComponentForm.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx index d4d2cf4789..e1226bbf32 100644 --- a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx +++ b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.test.tsx @@ -37,7 +37,7 @@ describe('RegisterComponentForm', () => { const { rendered } = setup(); expect( await rendered.findByText( - 'Enter the full path to the component.yaml file in Github, Gitlab, Bitbucket or Azure to start tracking your component. For private repo provide authentication information via config.', + 'Enter the full path to the component.yaml file in GitHub, GitLab, Bitbucket or Azure to start tracking your component. For private repo provide authentication information via config.', ), ).toBeInTheDocument(); diff --git a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx index a61469e969..de3c54610d 100644 --- a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx +++ b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx @@ -71,7 +71,7 @@ const RegisterComponentForm: FC = ({ onSubmit, submitting }) => { name="componentLocation" required margin="normal" - helperText="Enter the full path to the component.yaml file in Github, Gitlab, Bitbucket or Azure to start tracking your component. For private repo provide authentication information via config." + helperText="Enter the full path to the component.yaml file in GitHub, GitLab, Bitbucket or Azure to start tracking your component. For private repo provide authentication information via config." inputRef={register({ required: true, validate: ComponentIdValidators, From b6ba9ed08af63e4c4d7fb503fbed7dbb48b649b6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 09:03:07 +0200 Subject: [PATCH 088/359] workflows: cache node_modules in e2e tests --- .github/workflows/e2e-win.yml | 5 +++++ .github/workflows/e2e.yml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/e2e-win.yml b/.github/workflows/e2e-win.yml index 5f75f3d9c8..448a61678c 100644 --- a/.github/workflows/e2e-win.yml +++ b/.github/workflows/e2e-win.yml @@ -36,6 +36,11 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v2 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 45b9d7e2d8..e58299929d 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -43,6 +43,11 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v2 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: From a4a9777b61188bd62f4885cd854b4406c6e7a6ea Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 09:04:00 +0200 Subject: [PATCH 089/359] workflows: remove duplicate storybook build already done by chromatic --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 105642e284..0a0a335203 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,6 +79,3 @@ jobs: - name: verify plugin template run: yarn lerna -- run diff -- --check - - - name: verify storybook - run: yarn workspace storybook build-storybook From 9b514b1875ba8e6083befb1e316101fa3c181fe0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 09:05:59 +0200 Subject: [PATCH 090/359] workflows: add yarn cache and frozen lockfile install for chromatic --- .../workflows/chromatic-storybook-test.yml | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/chromatic-storybook-test.yml b/.github/workflows/chromatic-storybook-test.yml index e38b494a73..f7edd68ba9 100644 --- a/.github/workflows/chromatic-storybook-test.yml +++ b/.github/workflows/chromatic-storybook-test.yml @@ -14,7 +14,31 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 # Required to retrieve git history - - run: yarn install && yarn build-storybook + + - name: find location of global yarn cache + id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: cache node_modules + uses: actions/cache@v2 + with: + path: node_modules + key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + - name: use node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - run: yarn install --frozen-lockfile + + - run: yarn build-storybook + - uses: chromaui/action@v1 with: token: ${{ secrets.GITHUB_TOKEN }} From 7cd7e22e54301ec90a960e94d6767c45bc7b0c8e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2020 09:48:44 +0200 Subject: [PATCH 091/359] chore(deps): bump @testing-library/jest-dom from 5.10.1 to 5.11.4 (#2095) Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.10.1 to 5.11.4. - [Release notes](https://github.com/testing-library/jest-dom/releases) - [Changelog](https://github.com/testing-library/jest-dom/blob/master/CHANGELOG.md) - [Commits](https://github.com/testing-library/jest-dom/compare/v5.10.1...v5.11.4) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 53 ++++++++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2ad2111e29..ed00552d62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4326,17 +4326,16 @@ pretty-format "^25.5.0" "@testing-library/jest-dom@^5.10.1": - version "5.10.1" - resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.10.1.tgz#6508a9f007bd74e5d3c0b3135b668027ab663989" - integrity sha512-uv9lLAnEFRzwUTN/y9lVVXVXlEzazDkelJtM5u92PsGkEasmdI+sfzhZHxSDzlhZVTrlLfuMh2safMr8YmzXLg== + version "5.11.4" + resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.4.tgz#f325c600db352afb92995c2576022b35621ddc99" + integrity sha512-6RRn3epuweBODDIv3dAlWjOEHQLpGJHB2i912VS3JQtsD22+ENInhdDNl4ZZQiViLlIfFinkSET/J736ytV9sw== dependencies: "@babel/runtime" "^7.9.2" "@types/testing-library__jest-dom" "^5.9.1" + aria-query "^4.2.2" chalk "^3.0.0" - css "^2.2.4" + css "^3.0.0" css.escape "^1.5.1" - jest-diff "^25.1.0" - jest-matcher-utils "^25.1.0" lodash "^4.17.15" redent "^3.0.0" @@ -8801,15 +8800,14 @@ css.escape@1.5.1, css.escape@^1.5.1: resolved "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= -css@^2.2.4: - version "2.2.4" - resolved "https://registry.npmjs.org/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" - integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== +css@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" + integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== dependencies: - inherits "^2.0.3" + inherits "^2.0.4" source-map "^0.6.1" - source-map-resolve "^0.5.2" - urix "^0.1.0" + source-map-resolve "^0.6.0" cssesc@^3.0.0: version "3.0.0" @@ -13880,7 +13878,7 @@ jest-css-modules@^2.1.0: dependencies: identity-obj-proxy "3.0.0" -jest-diff@^25.1.0, jest-diff@^25.2.1: +jest-diff@^25.2.1: version "25.5.0" resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9" integrity sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A== @@ -13957,11 +13955,6 @@ jest-fetch-mock@^3.0.3: cross-fetch "^3.0.4" promise-polyfill "^8.1.3" -jest-get-type@^25.1.0: - version "25.1.0" - resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz#1cfe5fc34f148dc3a8a3b7275f6b9ce9e2e8a876" - integrity sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw== - jest-get-type@^25.2.6: version "25.2.6" resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" @@ -14023,16 +14016,6 @@ jest-leak-detector@^26.0.1: jest-get-type "^26.0.0" pretty-format "^26.0.1" -jest-matcher-utils@^25.1.0: - version "25.1.0" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz#fa5996c45c7193a3c24e73066fc14acdee020220" - integrity sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ== - dependencies: - chalk "^3.0.0" - jest-diff "^25.1.0" - jest-get-type "^25.1.0" - pretty-format "^25.1.0" - jest-matcher-utils@^26.0.1: version "26.0.1" resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.0.1.tgz#12e1fc386fe4f14678f4cc8dbd5ba75a58092911" @@ -18329,7 +18312,7 @@ pretty-format@^24.3.0: ansi-styles "^3.2.0" react-is "^16.8.4" -pretty-format@^25.1.0, pretty-format@^25.2.1, pretty-format@^25.5.0: +pretty-format@^25.2.1, pretty-format@^25.5.0: version "25.5.0" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ== @@ -20764,7 +20747,7 @@ source-list-map@^2.0.0: resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: +source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== @@ -20775,6 +20758,14 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" +source-map-resolve@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" + integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" From 80089ed32eb9909f9965ab48543bb550d55f89fc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2020 09:49:13 +0200 Subject: [PATCH 092/359] chore(deps-dev): bump @types/supertest from 2.0.9 to 2.0.10 (#2094) Bumps [@types/supertest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/supertest) from 2.0.9 to 2.0.10. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/supertest) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index ed00552d62..857b8b2cc0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5264,9 +5264,9 @@ "@types/node" "*" "@types/supertest@^2.0.8": - version "2.0.9" - resolved "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.9.tgz#049bddbcb0ee0d60a9b836ccc977d813a1c32325" - integrity sha512-0BTpWWWAO1+uXaP/oA0KW1eOZv4hc0knhrWowV06Gwwz3kqQxNO98fUFM2e15T+PdPRmOouNFrYvaBgdojPJ3g== + version "2.0.10" + resolved "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.10.tgz#630d79b4d82c73e043e43ff777a9ca98d457cab7" + integrity sha512-Xt8TbEyZTnD5Xulw95GLMOkmjGICrOQyJ2jqgkSjAUR3mm7pAIzSR0NFBaMcwlzVvlpCjNwbATcWWwjNiZiFrQ== dependencies: "@types/superagent" "*" From ce6205146134d92741d0986f119d747ac381be6d Mon Sep 17 00:00:00 2001 From: Joel Low Date: Mon, 24 Aug 2020 16:59:09 +0800 Subject: [PATCH 093/359] feat: support specifying lint output format --- packages/cli/src/commands/lint.ts | 2 +- packages/cli/src/index.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/lint.ts b/packages/cli/src/commands/lint.ts index b355939e55..7b4532ffa7 100644 --- a/packages/cli/src/commands/lint.ts +++ b/packages/cli/src/commands/lint.ts @@ -22,7 +22,7 @@ export default async (cmd: Command, cmdArgs: string[]) => { const args = [ '--ext=js,jsx,ts,tsx', '--max-warnings=0', - '--format=eslint-formatter-friendly', + `--format=${cmd.format}`, ...(cmdArgs ?? [paths.targetDir]), ]; if (cmd.fix) { diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 4b9dc4bab7..742e05f34a 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -113,6 +113,11 @@ const main = (argv: string[]) => { program .command('lint') + .option( + '--format ', + 'Lint report output format', + 'eslint-formatter-friendly', + ) .option('--fix', 'Attempt to automatically fix violations') .description('Lint a package') .action(lazyAction(() => import('./commands/lint'), 'default')); From e323e1691b03f869253e2429aae8f5b77054641b Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Tue, 25 Aug 2020 12:07:12 +0200 Subject: [PATCH 094/359] update tags to be lowercased in docs template (#2104) * update tags to be lowercased in docs template * Update template.yaml --- .../sample-templates/docs-template/template.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml index 01ebdadbce..da2152280d 100644 --- a/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml @@ -5,9 +5,9 @@ metadata: title: Documentation Template description: Create a new standalone documentation project tags: - - Experimental - - TechDocs - - MkDocs + - experimental + - techdocs + - mkdocs spec: owner: spotify/techdocs-core templater: cookiecutter @@ -26,4 +26,4 @@ spec: title: Description type: string description: Description of the component - \ No newline at end of file + From d42c09c0586b4dded79c1d5798a91001a8b3eb0b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 12:13:09 +0200 Subject: [PATCH 095/359] workflows: use new multi-path caching to completely cache node_modules + prep for composite action --- .../workflows/chromatic-storybook-test.yml | 40 ++++++++------ .github/workflows/ci.yml | 54 ++++++++++++------- .github/workflows/e2e-win.yml | 38 +++++++------ .github/workflows/e2e.yml | 38 +++++++------ .github/workflows/master-win.yml | 38 +++++++------ .github/workflows/master.yml | 37 +++++++------ .github/workflows/microsite-build-check.yml | 38 ++++++------- .../microsite-with-storybook-deploy.yml | 38 ++++++------- 8 files changed, 188 insertions(+), 133 deletions(-) diff --git a/.github/workflows/chromatic-storybook-test.yml b/.github/workflows/chromatic-storybook-test.yml index f7edd68ba9..0bcaf400d0 100644 --- a/.github/workflows/chromatic-storybook-test.yml +++ b/.github/workflows/chromatic-storybook-test.yml @@ -15,27 +15,33 @@ jobs: with: fetch-depth: 0 # Required to retrieve git history - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - - - run: yarn install --frozen-lockfile + registry-url: https://registry.npmjs.org/ # Needed for auth + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: yarn install + run: yarn install --frozen-lockfile + # End of yarn setup - run: yarn build-storybook diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a0a335203..9d0a37cd1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,34 +20,52 @@ jobs: - uses: actions/checkout@v2 - name: fetch branch master run: git fetch origin master - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + + # Beginning of yarn setup, keep in sync between all workflows. + # TODO(Rugvip): move this to composite action once all features we use are supported - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + + # Cache every node_modules folder inside the monorepo + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + # We use both yarn.lock and package.json as cache keys to ensure that + # changes to local monorepo packages bust the cache. + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + + # If we get a cache hit for node_modules, there's no need to bring in the global + # yarn cache or run yarn install, as all dependencies will be installed already. + + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: yarn install + if: steps.cache-modules.outputs.cache-hit != 'true' + run: yarn install --frozen-lockfile + # End of yarn setup - name: check for yarn.lock changes id: yarn-lock run: git diff --quiet origin/master HEAD -- yarn.lock continue-on-error: true - - name: yarn install - run: yarn install --frozen-lockfile - - name: verify doc links run: node docs/verify-links.js diff --git a/.github/workflows/e2e-win.yml b/.github/workflows/e2e-win.yml index 448a61678c..37cb13ffbe 100644 --- a/.github/workflows/e2e-win.yml +++ b/.github/workflows/e2e-win.yml @@ -26,27 +26,35 @@ jobs: name: Node ${{ matrix.node-version }} on ${{ matrix.os }} steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup + - run: yarn tsc - name: yarn build run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index e58299929d..401fe7297a 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -33,27 +33,35 @@ jobs: name: Node ${{ matrix.node-version }} on ${{ matrix.os }} steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ # Needed for auth + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup + - run: yarn tsc - name: yarn build run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite diff --git a/.github/workflows/master-win.yml b/.github/workflows/master-win.yml index 074f944250..b5a042a236 100644 --- a/.github/workflows/master-win.yml +++ b/.github/workflows/master-win.yml @@ -18,29 +18,35 @@ jobs: steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup + # Tests are broken on Windows, disabled for now # - name: test diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 805095ec53..04b50c3e60 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -18,29 +18,34 @@ jobs: steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup - name: lint run: yarn lerna -- run lint diff --git a/.github/workflows/microsite-build-check.yml b/.github/workflows/microsite-build-check.yml index 41f43399ca..dc86ca2630 100644 --- a/.github/workflows/microsite-build-check.yml +++ b/.github/workflows/microsite-build-check.yml @@ -21,32 +21,34 @@ jobs: steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup - name: build microsite run: yarn workspace backstage-microsite build diff --git a/.github/workflows/microsite-with-storybook-deploy.yml b/.github/workflows/microsite-with-storybook-deploy.yml index 84ac6f9490..2c8b8af40c 100644 --- a/.github/workflows/microsite-with-storybook-deploy.yml +++ b/.github/workflows/microsite-with-storybook-deploy.yml @@ -25,32 +25,34 @@ jobs: steps: - uses: actions/checkout@v2 - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - - name: cache global yarn cache - uses: actions/cache@v2 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: cache node_modules - uses: actions/cache@v2 - with: - path: node_modules - key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} + # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - + - name: cache all node_modules + id: cache-modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} + - name: find location of global yarn cache + id: yarn-cache + if: steps.cache-modules.outputs.cache-hit != 'true' + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v2 + if: steps.cache-modules.outputs.cache-hit != 'true' + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- - name: yarn install run: yarn install --frozen-lockfile + # End of yarn setup - name: build microsite run: yarn workspace backstage-microsite build From ff888ebc8c667a5e5eead8c21f12c81a1d928f79 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 13:19:39 +0200 Subject: [PATCH 096/359] cli-common: remove dev dep on cli --- packages/cli-common/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index 80cc665426..5e050210da 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -29,7 +29,6 @@ "clean": "backstage-cli clean" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.19", "@types/jest": "^26.0.7", "@types/node": "^12.0.0" }, From bc883377fd1f4352b78c3e0e4bfbba196c2af32e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 13:22:54 +0200 Subject: [PATCH 097/359] workflows: no need to include deps in build for tests --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 105642e284..610a1cd7a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,8 +59,7 @@ jobs: - name: build changed packages if: ${{ steps.yarn-lock.outcome == 'success' }} - # Need to build all dependencies as well to be able to run tests later - run: yarn lerna -- run build --since origin/master --include-dependencies + run: yarn lerna -- run build --since origin/master - name: build all packages if: ${{ steps.yarn-lock.outcome == 'failure' }} From 4665a2f5d46335b23d36bb54ebbba886808ce278 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Tue, 25 Aug 2020 13:57:57 +0200 Subject: [PATCH 098/359] Added selectedTabId param to generatePath in JobStatusModal (#2109) * Added selectedTabId param to generatePath in JobStatusModal * Added selectedTabId to generatePath in ApiCatalogTable * Link to default entity tab in JobStatusModal --- .../src/components/ApiCatalogTable/ApiCatalogTable.tsx | 1 + .../src/components/JobStatusModal/JobStatusModal.tsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/api-docs/src/components/ApiCatalogTable/ApiCatalogTable.tsx b/plugins/api-docs/src/components/ApiCatalogTable/ApiCatalogTable.tsx index e30583caed..99848637b6 100644 --- a/plugins/api-docs/src/components/ApiCatalogTable/ApiCatalogTable.tsx +++ b/plugins/api-docs/src/components/ApiCatalogTable/ApiCatalogTable.tsx @@ -37,6 +37,7 @@ const columns: TableColumn[] = [ .filter(Boolean) .join(':'), kind: entity.kind, + selectedTabId: 'overview', })} > {entity.metadata.name} diff --git a/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx b/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx index c6ed55c92c..f5a592b7b7 100644 --- a/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx +++ b/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx @@ -26,7 +26,7 @@ import { useJobPolling } from './useJobPolling'; import { Job } from '../../types'; import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { Button } from '@backstage/core'; -import { entityRoute } from '@backstage/plugin-catalog'; +import { entityRouteDefault } from '@backstage/plugin-catalog'; import { generatePath } from 'react-router-dom'; type Props = { @@ -72,7 +72,7 @@ export const JobStatusModal = ({ {entity && ( + ) : null} - ) : null} diff --git a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx index 1b84b79234..4090b2af9c 100644 --- a/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx +++ b/plugins/register-component/src/components/RegisterComponentForm/RegisterComponentForm.tsx @@ -49,7 +49,7 @@ const RegisterComponentForm: FC = ({ onSubmit, submitting }) => { }); const classes = useStyles(); const hasErrors = !!errors.componentLocation; - const dirty = formState?.dirty; + const dirty = formState?.isDirty; return submitting ? ( From 6f3b0b8c20e9d1c5309f20470f2c3acc2a1b0ed3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 25 Aug 2020 18:25:56 +0200 Subject: [PATCH 186/359] catalog-backend: initial catalog ingestion rules implementation --- .../src/ingestion/CatalogRules.test.ts | 114 +++++++++++ .../src/ingestion/CatalogRules.ts | 187 ++++++++++++++++++ .../src/ingestion/LocationReaders.ts | 24 ++- 3 files changed, 320 insertions(+), 5 deletions(-) create mode 100644 plugins/catalog-backend/src/ingestion/CatalogRules.test.ts create mode 100644 plugins/catalog-backend/src/ingestion/CatalogRules.ts diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts new file mode 100644 index 0000000000..7f2ccdc254 --- /dev/null +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts @@ -0,0 +1,114 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { LocationSpec, Entity } from '@backstage/catalog-model'; +import { CatalogRulesEnforcer } from './CatalogRules'; + +const entity = { + user: { + kind: 'User', + } as Entity, + group: { + kind: 'Group', + } as Entity, + component: { + kind: 'component', + } as Entity, +}; + +const location: Record = { + x: { + type: 'github', + target: 'https://github.com/a/b/blob/master/x.yaml', + }, + y: { + type: 'github', + target: 'https://github.com/a/b/blob/master/y.yaml', + }, + z: { + type: 'file', + target: '/root/z.yaml', + }, +}; + +describe('CatalogRulesEnforcer', () => { + it('should allow by default', () => { + const enforcer = new CatalogRulesEnforcer([]); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(true); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + }); + + it('should deny all', () => { + const enforcer = new CatalogRulesEnforcer([{ allow: [] }]); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); + }); + + it('should allow all with override', () => { + const enforcer = new CatalogRulesEnforcer([{ allow: [] }, { deny: [] }]); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(true); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + }); + + it('should deny groups', () => { + const enforcer = new CatalogRulesEnforcer([ + { allow: [], deny: [{ kind: 'Group' }] }, + ]); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.x)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.z)).toBe(false); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + }); + + it('should deny groups from github', () => { + const enforcer = new CatalogRulesEnforcer([ + { allow: [], deny: [{ kind: 'Group' }], locations: [{ type: 'github' }] }, + ]); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.x)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.z)).toBe(true); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + }); + + it('should override to allow groups from files', () => { + const enforcer = new CatalogRulesEnforcer([ + { allow: [], deny: [{ kind: 'Group' }] }, + { allow: [{ kind: 'Group' }], deny: [], locations: [{ type: 'file' }] }, + ]); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.x)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.z)).toBe(true); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + }); + + it('should not be sensitive to kind case', () => { + const enforcer = new CatalogRulesEnforcer([ + { allow: [], deny: [{ kind: 'group' }] }, + { allow: [], deny: [{ kind: 'Component' }] }, + ]); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.x)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.z)).toBe(false); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); + }); +}); diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.ts new file mode 100644 index 0000000000..a03d93852a --- /dev/null +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.ts @@ -0,0 +1,187 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { Config } from '@backstage/config'; +import { LocationSpec, Entity } from '@backstage/catalog-model'; + +/** + * A structure for matching entities to a given rule. + */ +type EntityMatcher = { + kind: string; +}; + +/** + * A structure for matching locations to a given rule. + */ +type LocationMatcher = { + target?: string; + type: string; +}; + +/** + * Rules to apply to catalog entities + * + * An undefined list of matchers means match all, an empty list of matchers means match none + */ +type CatalogRule = { + deny?: EntityMatcher[]; + allow?: EntityMatcher[]; + locations?: LocationMatcher[]; +}; + +export class CatalogRulesEnforcer { + /** + * Default rules used by the catalog. + * + * Denies any location from specifying user or group entities. + */ + static readonly defaultRules: CatalogRule[] = [ + { + deny: [{ kind: 'User' }, { kind: 'Group' }], + allow: [], + }, + ]; + + /** + * Loads catalog rules from config. + * + * This reads `catalog.rules` and defaults to the default rules if no value is present. + * The value of the config should be a list of config objects, each with a single `deny` + * field which in turn is a list of entity kind to deny. + * + * It also reads in rules from `catalog.locations`, where each location can have a list + * of allowed entity for the location, specified in an `allow` field. + * + * For example: + * + * ```yaml + * catalog: + * rules: + * - deny: [User, Group, System] + * + * locations: + * - type: github + * target: https://github.com/org/repo/blob/master/users.yaml + * allow: [User, Group] + * - type: github + * target: https://github.com/org/repo/blob/master/systems.yaml + * allow: [System] + * ``` + */ + static fromConfig(config: Config) { + const rules = new Array(); + + if (config.has('catalog.rules')) { + const globalRules = config.getConfigArray('catalog.rules').map(sub => ({ + deny: sub.getStringArray('deny').map(kind => ({ kind })), + allow: [], + })); + rules.push(...globalRules); + } else { + rules.push(...CatalogRulesEnforcer.defaultRules); + } + + if (config.has('catalog.locations')) { + const locationRules = config + .getConfigArray('catalog.locations') + .flatMap(sub => { + if (!sub.has('allow')) { + return []; + } + + return [ + { + deny: [], + allow: sub.getStringArray('allow').map(kind => ({ kind })), + locations: [ + { + type: sub.getString('type'), + target: sub.getString('target'), + }, + ], + }, + ]; + }); + + rules.push(...locationRules); + } + + return new CatalogRulesEnforcer(rules); + } + + constructor(private readonly rules: CatalogRule[]) {} + + /** + * Checks wether a specific entity/location combination is allowed + * according to the configured rules. + */ + isAllowed(entity: Entity, location: LocationSpec) { + let result = true; + + for (const rule of this.rules) { + if (!this.matchLocation(location, rule.locations)) { + continue; + } + + if (this.matchEntity(entity, rule.allow)) { + result = true; + } + if (this.matchEntity(entity, rule.deny)) { + result = false; + } + } + + return result; + } + + private matchLocation( + location: LocationSpec, + matchers?: LocationMatcher[], + ): boolean { + if (!matchers) { + return true; + } + + for (const matcher of matchers) { + if (matcher.type !== location.type) { + continue; + } + if (matcher.target && matcher.target !== location.target) { + continue; + } + return true; + } + + return false; + } + + private matchEntity(entity: Entity, matchers?: EntityMatcher[]): boolean { + if (!matchers) { + return true; + } + + for (const matcher of matchers) { + if (entity.kind.toLowerCase() !== matcher.kind.toLowerCase()) { + continue; + } + + return true; + } + + return false; + } +} diff --git a/plugins/catalog-backend/src/ingestion/LocationReaders.ts b/plugins/catalog-backend/src/ingestion/LocationReaders.ts index 46fa6781f1..bddef6b4de 100644 --- a/plugins/catalog-backend/src/ingestion/LocationReaders.ts +++ b/plugins/catalog-backend/src/ingestion/LocationReaders.ts @@ -47,6 +47,7 @@ import { } from './processors/types'; import { YamlProcessor } from './processors/YamlProcessor'; import { LocationReader, ReadLocationResult } from './types'; +import { CatalogRulesEnforcer } from './CatalogRules'; // The max amount of nesting depth of generated work items const MAX_DEPTH = 10; @@ -63,6 +64,7 @@ type Options = { export class LocationReaders implements LocationReader { private readonly logger: Logger; private readonly processors: LocationProcessor[]; + private readonly rulesEnforcer: CatalogRulesEnforcer; static defaultProcessors(options: { config?: Config; @@ -96,6 +98,9 @@ export class LocationReaders implements LocationReader { }: Options) { this.logger = logger; this.processors = processors; + this.rulesEnforcer = config + ? CatalogRulesEnforcer.fromConfig(config) + : new CatalogRulesEnforcer(CatalogRulesEnforcer.defaultRules); } async read(location: LocationSpec): Promise { @@ -112,11 +117,20 @@ export class LocationReaders implements LocationReader { } else if (item.type === 'data') { await this.handleData(item, emit); } else if (item.type === 'entity') { - const entity = await this.handleEntity(item, emit); - output.entities.push({ - entity, - location: item.location, - }); + if (this.rulesEnforcer.isAllowed(item.entity, item.location)) { + const entity = await this.handleEntity(item, emit); + output.entities.push({ + entity, + location: item.location, + }); + } else { + output.errors.push({ + location: item.location, + error: new Error( + `Entity of kind ${item.entity.kind} is not allowed from location ${item.location.target}:${item.location.type}`, + ), + }); + } } else if (item.type === 'error') { await this.handleError(item, emit); output.errors.push({ From 8d07b541d7eb037cea5a8a79c01135a02b98cab9 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 27 Aug 2020 20:54:56 +0200 Subject: [PATCH 187/359] catalog-backend: switch catalog rules to deny by default --- .../src/ingestion/CatalogRules.test.ts | 42 ++++++++++--------- .../src/ingestion/CatalogRules.ts | 27 +++++------- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts index 7f2ccdc254..1168db1080 100644 --- a/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts @@ -45,11 +45,11 @@ const location: Record = { }; describe('CatalogRulesEnforcer', () => { - it('should allow by default', () => { + it('should deny by default', () => { const enforcer = new CatalogRulesEnforcer([]); - expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); - expect(enforcer.isAllowed(entity.group, location.y)).toBe(true); - expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); }); it('should deny all', () => { @@ -59,8 +59,10 @@ describe('CatalogRulesEnforcer', () => { expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); }); - it('should allow all with override', () => { - const enforcer = new CatalogRulesEnforcer([{ allow: [] }, { deny: [] }]); + it('should allow all', () => { + const enforcer = new CatalogRulesEnforcer([ + { allow: [{ kind: 'User' }, { kind: 'Group' }, { kind: 'Component' }] }, + ]); expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); expect(enforcer.isAllowed(entity.group, location.y)).toBe(true); expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); @@ -68,7 +70,7 @@ describe('CatalogRulesEnforcer', () => { it('should deny groups', () => { const enforcer = new CatalogRulesEnforcer([ - { allow: [], deny: [{ kind: 'Group' }] }, + { allow: [{ kind: 'User' }, { kind: 'Component' }] }, ]); expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); expect(enforcer.isAllowed(entity.group, location.x)).toBe(false); @@ -79,7 +81,8 @@ describe('CatalogRulesEnforcer', () => { it('should deny groups from github', () => { const enforcer = new CatalogRulesEnforcer([ - { allow: [], deny: [{ kind: 'Group' }], locations: [{ type: 'github' }] }, + { allow: [{ kind: 'User' }, { kind: 'Component' }] }, + { allow: [{ kind: 'Group' }], locations: [{ type: 'file' }] }, ]); expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); expect(enforcer.isAllowed(entity.group, location.x)).toBe(false); @@ -88,27 +91,26 @@ describe('CatalogRulesEnforcer', () => { expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); }); - it('should override to allow groups from files', () => { + it('should allow groups from files', () => { const enforcer = new CatalogRulesEnforcer([ - { allow: [], deny: [{ kind: 'Group' }] }, - { allow: [{ kind: 'Group' }], deny: [], locations: [{ type: 'file' }] }, + { allow: [{ kind: 'Group' }], locations: [{ type: 'file' }] }, ]); - expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(false); expect(enforcer.isAllowed(entity.group, location.x)).toBe(false); expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); expect(enforcer.isAllowed(entity.group, location.z)).toBe(true); - expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); }); it('should not be sensitive to kind case', () => { const enforcer = new CatalogRulesEnforcer([ - { allow: [], deny: [{ kind: 'group' }] }, - { allow: [], deny: [{ kind: 'Component' }] }, + { allow: [{ kind: 'group' }] }, + { allow: [{ kind: 'Component' }] }, ]); - expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); - expect(enforcer.isAllowed(entity.group, location.x)).toBe(false); - expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); - expect(enforcer.isAllowed(entity.group, location.z)).toBe(false); - expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.z)).toBe(true); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); }); }); diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.ts index a03d93852a..eda93b552f 100644 --- a/plugins/catalog-backend/src/ingestion/CatalogRules.ts +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.ts @@ -38,8 +38,7 @@ type LocationMatcher = { * An undefined list of matchers means match all, an empty list of matchers means match none */ type CatalogRule = { - deny?: EntityMatcher[]; - allow?: EntityMatcher[]; + allow: EntityMatcher[]; locations?: LocationMatcher[]; }; @@ -51,8 +50,7 @@ export class CatalogRulesEnforcer { */ static readonly defaultRules: CatalogRule[] = [ { - deny: [{ kind: 'User' }, { kind: 'Group' }], - allow: [], + allow: [{ kind: 'Component' }, { kind: 'API' }], }, ]; @@ -60,8 +58,10 @@ export class CatalogRulesEnforcer { * Loads catalog rules from config. * * This reads `catalog.rules` and defaults to the default rules if no value is present. - * The value of the config should be a list of config objects, each with a single `deny` - * field which in turn is a list of entity kind to deny. + * The value of the config should be a list of config objects, each with a single `allow` + * field which in turn is a list of entity kinds to allow. + * + * If there is no matching rule to allow an ingested entity, it will be rejected by the catalog. * * It also reads in rules from `catalog.locations`, where each location can have a list * of allowed entity for the location, specified in an `allow` field. @@ -71,7 +71,7 @@ export class CatalogRulesEnforcer { * ```yaml * catalog: * rules: - * - deny: [User, Group, System] + * - allow: [Component, API] * * locations: * - type: github @@ -87,8 +87,7 @@ export class CatalogRulesEnforcer { if (config.has('catalog.rules')) { const globalRules = config.getConfigArray('catalog.rules').map(sub => ({ - deny: sub.getStringArray('deny').map(kind => ({ kind })), - allow: [], + allow: sub.getStringArray('allow').map(kind => ({ kind })), })); rules.push(...globalRules); } else { @@ -105,7 +104,6 @@ export class CatalogRulesEnforcer { return [ { - deny: [], allow: sub.getStringArray('allow').map(kind => ({ kind })), locations: [ { @@ -130,22 +128,17 @@ export class CatalogRulesEnforcer { * according to the configured rules. */ isAllowed(entity: Entity, location: LocationSpec) { - let result = true; - for (const rule of this.rules) { if (!this.matchLocation(location, rule.locations)) { continue; } if (this.matchEntity(entity, rule.allow)) { - result = true; - } - if (this.matchEntity(entity, rule.deny)) { - result = false; + return true; } } - return result; + return false; } private matchLocation( From 903cbdcfb5120f3414662c51b839e89892404af3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 31 Aug 2020 00:24:18 +0200 Subject: [PATCH 188/359] catalog-backend: add tests for config rules --- .../src/ingestion/CatalogRules.test.ts | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts index 1168db1080..4a403f8dfa 100644 --- a/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts @@ -16,6 +16,7 @@ import { LocationSpec, Entity } from '@backstage/catalog-model'; import { CatalogRulesEnforcer } from './CatalogRules'; +import { ConfigReader } from '@backstage/config'; const entity = { user: { @@ -113,4 +114,70 @@ describe('CatalogRulesEnforcer', () => { expect(enforcer.isAllowed(entity.group, location.z)).toBe(true); expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); }); + + describe('fromConfig', () => { + it('should allow components by default', () => { + const enforcer = CatalogRulesEnforcer.fromConfig(new ConfigReader({})); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + }); + + it('should deny all', () => { + const enforcer = CatalogRulesEnforcer.fromConfig( + new ConfigReader({ catalog: { rules: [] } }), + ); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); + }); + + it('should allow all', () => { + const enforcer = CatalogRulesEnforcer.fromConfig( + new ConfigReader({ + catalog: { + rules: [{ allow: ['User', 'Group'] }, { allow: ['Component'] }], + }, + }), + ); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(true); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + }); + + it('should deny groups', () => { + const enforcer = CatalogRulesEnforcer.fromConfig( + new ConfigReader({ + catalog: { rules: [{ allow: ['User'] }, { allow: ['Component'] }] }, + }), + ); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.x)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.z)).toBe(false); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + }); + + it('should allow groups from a specific github location', () => { + const enforcer = CatalogRulesEnforcer.fromConfig( + new ConfigReader({ + catalog: { + rules: [{ allow: ['user'] }], + locations: [ + { + type: 'github', + target: 'https://github.com/a/b/blob/master/x.yaml', + allow: ['Group'], + }, + ], + }, + }), + ); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.z)).toBe(false); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); + }); + }); }); From f546bb3862893d85a7f3cfdf84005d2fd603065e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 31 Aug 2020 01:05:54 +0200 Subject: [PATCH 189/359] docs: added catalog configuration docs --- .../software-catalog/configuration.md | 60 +++++++++++++++++++ docs/features/software-catalog/index.md | 16 +++++ mkdocs.yml | 1 + 3 files changed, 77 insertions(+) create mode 100644 docs/features/software-catalog/configuration.md diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md new file mode 100644 index 0000000000..77b630d347 --- /dev/null +++ b/docs/features/software-catalog/configuration.md @@ -0,0 +1,60 @@ +--- +id: software-catalog-configuration +title: Catalog Configuration +--- + +## Static Location Configuration + +To enable declarative catalog setups, it is possible to add locations to the +catalog via [static configuration](../../conf/index.md). Locations are added to +the catalog under the `catalog.locations` key, for example: + +```yaml +catalog: + locations: + - type: github + target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml +``` + +The locations added through static configuration can not be removed through the +catalog locations API. To remove the locations, you have to remove them from the +configuration. + +## Catalog Rules + +By default the catalog will only allow ingestion of entities with the kind +`Component` and `API`. In order to allow entities of other kinds to be added, +you need to add rules to the catalog. Rules are added either in a separate +`catalog.rules` key, or added to statically configured locations. + +For example, given the following configuration: + +```yaml +catalog: + rules: + - allow: [Component, API] + - allow: [System] + locations: + type: github + + locations: + - type: github + target: https://github.com/org/example/blob/master/org-data.yaml + allow: [Group] +``` + +We are able to add entities of kind `Component` or `API` from any location, +entities of kind `System` from any `github` location, and `Group` entities from +the `org-data.yaml`, which will also be read as statically configured location. + +Note that if the `catalog.rules` key is present it will replace the default +value, meaning that you need to add rules for `Component` and `API` kinds if you +want those to be allowed. + +The following configuration will reject any kind of entities from being added to +the catalog: + +```yaml +catalog: + rules: [] +``` diff --git a/docs/features/software-catalog/index.md b/docs/features/software-catalog/index.md index db8c183390..bfc3d53d4d 100644 --- a/docs/features/software-catalog/index.md +++ b/docs/features/software-catalog/index.md @@ -79,6 +79,22 @@ All software created through the [Backstage Software Templates](../software-templates/index.md) are automatically registered in the catalog. +### Static catalog configuration + +In addition to manually registering components, it is also possible to register +components though [static configuration](../../conf/index.md). For example, the +above example can be added using the following configuration: + +```yaml +catalog: + locations: + - type: github + target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml +``` + +More information about catalog configuration can be found +[here](configuration.md). + ### Updating component metadata Teams owning the components are responsible for maintaining the metadata about diff --git a/mkdocs.yml b/mkdocs.yml index 637e45c0f0..b12c8f81c7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,6 +28,7 @@ nav: - Overview: 'features/software-catalog/index.md' - System model: 'features/software-catalog/system-model.md' - YAML File Format: 'features/software-catalog/descriptor-format.md' + - Configuration: 'features/software-catalog/configuration.md' - Extending the model: 'features/software-catalog/extending-the-model.md' - External integrations: 'features/software-catalog/external-integrations.md' - API: 'features/software-catalog/api.md' From 74e55ef32f173e2e89656bbe03802426453df5f2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 31 Aug 2020 13:40:05 +0200 Subject: [PATCH 190/359] catalog-backend: updated docs to not include location in catalog.rules + test --- docs/features/software-catalog/configuration.md | 11 ++++------- .../src/ingestion/CatalogRules.test.ts | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md index 77b630d347..d4cc7fc0c2 100644 --- a/docs/features/software-catalog/configuration.md +++ b/docs/features/software-catalog/configuration.md @@ -32,10 +32,7 @@ For example, given the following configuration: ```yaml catalog: rules: - - allow: [Component, API] - - allow: [System] - locations: - type: github + - allow: [Component, API, System] locations: - type: github @@ -43,9 +40,9 @@ catalog: allow: [Group] ``` -We are able to add entities of kind `Component` or `API` from any location, -entities of kind `System` from any `github` location, and `Group` entities from -the `org-data.yaml`, which will also be read as statically configured location. +We are able to add entities of kind `Component`, `API`, or `System` from any +location, and `Group` entities from the `org-data.yaml`, which will also be read +as statically configured location. Note that if the `catalog.rules` key is present it will replace the default value, meaning that you need to add rules for `Component` and `API` kinds if you diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts index 4a403f8dfa..34484f6b1e 100644 --- a/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts @@ -179,5 +179,20 @@ describe('CatalogRulesEnforcer', () => { expect(enforcer.isAllowed(entity.group, location.z)).toBe(false); expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); }); + + it('should not care about location configuration in catalog.rules', () => { + const enforcer = CatalogRulesEnforcer.fromConfig( + new ConfigReader({ + catalog: { + rules: [{ allow: ['Group'], locations: [{ type: 'github' }] }], + }, + }), + ); + expect(enforcer.isAllowed(entity.user, location.x)).toBe(false); + expect(enforcer.isAllowed(entity.group, location.x)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.y)).toBe(true); + expect(enforcer.isAllowed(entity.group, location.z)).toBe(true); + expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); + }); }); }); From 6a22efc31b3994bd04db93fc21f75c82bf6eefe6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 31 Aug 2020 14:06:10 +0200 Subject: [PATCH 191/359] .github: update issue template to label with enhancement instead of help wanted (#2184) --- .github/ISSUE_TEMPLATE/feature_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_template.md b/.github/ISSUE_TEMPLATE/feature_template.md index 012b8d7a06..d70622bf52 100644 --- a/.github/ISSUE_TEMPLATE/feature_template.md +++ b/.github/ISSUE_TEMPLATE/feature_template.md @@ -1,7 +1,7 @@ --- name: 'Feature Request' about: 'Suggest new features and changes' -labels: help wanted +labels: enhancement --- From b284cb38e345c87fcfd5ccf5a468668cc1fd3e36 Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Mon, 31 Aug 2020 13:30:43 +0100 Subject: [PATCH 192/359] Add Plugins Page to microsite (#2144) * Add Plugins Page to microsite * Updates to microsite Plugins Page * Correct plugin authors and documentation links * Add category to UI * Move documentation from README to the docs website * Use external urls for logos * Updates to microsite Plugins Page * trailing whitespace * Run prettier on new markdown * Updates to microsite Plugins Page * Move plugins link * Clarify category field * Updates to microsite Plugins Page * Add authorUrl field to plugin config * Render author as a muted link like those in the site map * Updates to microsite Plugins Page * Add authorUrl field to example in docs * Updates to microsite Plugins Page * Add npmPackageName field - intended for future use * Updates to microsite Plugins Page * Add npmPackageName field - intended for future use * Updates to microsite Plugins Page * Use correct docs link --- docs/plugins/add-to-marketplace.md | 23 ++++++ microsite/data/plugins/rollbar.yaml | 10 +++ microsite/data/plugins/sentry.yaml | 10 +++ microsite/data/plugins/travis-ci.yaml | 10 +++ microsite/i18n/en.json | 4 + microsite/package.json | 3 +- microsite/pages/en/plugins.js | 84 +++++++++++++++++++ microsite/sidebars.json | 2 +- microsite/siteConfig.js | 4 + microsite/static/css/plugins.css | 111 ++++++++++++++++++++++++++ 10 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 docs/plugins/add-to-marketplace.md create mode 100644 microsite/data/plugins/rollbar.yaml create mode 100644 microsite/data/plugins/sentry.yaml create mode 100644 microsite/data/plugins/travis-ci.yaml create mode 100644 microsite/pages/en/plugins.js create mode 100644 microsite/static/css/plugins.css diff --git a/docs/plugins/add-to-marketplace.md b/docs/plugins/add-to-marketplace.md new file mode 100644 index 0000000000..23cfecd5d0 --- /dev/null +++ b/docs/plugins/add-to-marketplace.md @@ -0,0 +1,23 @@ +--- +id: add-to-marketplace +title: Add to Marketplace +--- + +## Adding a Plugin to the Marketplace + +To add a new plugin to the [plugin marketplace](https://backstage.io/plugins) +create a file in `data/plugins` with your plugin's information. Example: + +```yaml +--- +title: Your Plugin +author: Your Name +authorUrl: # A link to information about the author E.g. Company url, github user profile, etc +category: Monitoring # A single category e.g. CI, Machine Learning, Services, Monitoring +description: A brief description of the plugin. # Max 170 characters +documentation: # A link to your documentation E.g. Your github README +iconUrl: # Used as the src attribute for your logo. +# You can provide an external url or add your logo under static/img and provide a path +# relative to static/ e.g. img/my-logo.png +npmPackageName: # Your npm package name E.g. '@backstage/plugin-' quotes are required +``` diff --git a/microsite/data/plugins/rollbar.yaml b/microsite/data/plugins/rollbar.yaml new file mode 100644 index 0000000000..0118fab391 --- /dev/null +++ b/microsite/data/plugins/rollbar.yaml @@ -0,0 +1,10 @@ +--- +title: Rollbar +author: '@andrewthauer' +authorUrl: https://github.com/andrewthauer +category: Monitoring +description: View Rollbar errors for your services in Backstage. +documentation: https://github.com/spotify/backstage/tree/master/plugins/rollbar +iconUrl: https://rollbar.com/assets/media/rollbar-mark-color.png +npmPackageName: '@backstage/plugin-rollbar' + diff --git a/microsite/data/plugins/sentry.yaml b/microsite/data/plugins/sentry.yaml new file mode 100644 index 0000000000..7ca291acea --- /dev/null +++ b/microsite/data/plugins/sentry.yaml @@ -0,0 +1,10 @@ +--- +title: Sentry +author: Spotify +authorUrl: https://www.spotify.com/ +category: Monitoring +description: View Sentry issues in Backstage. +documentation: https://github.com/spotify/backstage/tree/master/plugins/sentry +iconUrl: https://sentry-brand.storage.googleapis.com/sentry-glyph-white.png +npmPackageName: '@backstage/plugin-sentry' + diff --git a/microsite/data/plugins/travis-ci.yaml b/microsite/data/plugins/travis-ci.yaml new file mode 100644 index 0000000000..48c0a4cb86 --- /dev/null +++ b/microsite/data/plugins/travis-ci.yaml @@ -0,0 +1,10 @@ +--- +title: Travis CI +author: roadie.io +authorUrl: https://roadie.io/ +category: CI +description: View Travis CI builds for your service in Backstage. +documentation: https://roadie.io/backstage/plugins/travis-ci +iconUrl: https://roadie.io/static/af2941eaf0af675facb281d566f42e14/45f2b/travis-ci-mascot-200x200.png +npmPackageName: '@roadiehq/backstage-plugin-travis-ci' + diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json index 9d83dfcef9..f61442e938 100644 --- a/microsite/i18n/en.json +++ b/microsite/i18n/en.json @@ -199,6 +199,9 @@ "overview/what-is-backstage": { "title": "What is Backstage?" }, + "plugins/add-to-marketplace": { + "title": "Add to Marketplace" + }, "plugins/backend-plugin": { "title": "Backend plugin" }, @@ -295,6 +298,7 @@ "Docs": "Docs", "Blog": "Blog", "Demos": "Demos", + "Plugins": "Plugins", "Newsletter": "Newsletter" }, "categories": { diff --git a/microsite/package.json b/microsite/package.json index 035c7438a4..baaf9ccaf4 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -13,6 +13,7 @@ "rename-version": "docusaurus-rename-version" }, "devDependencies": { - "docusaurus": "^2.0.0-alpha.61" + "docusaurus": "^2.0.0-alpha.61", + "js-yaml": "^3.14.0" } } diff --git a/microsite/pages/en/plugins.js b/microsite/pages/en/plugins.js new file mode 100644 index 0000000000..916f34c21e --- /dev/null +++ b/microsite/pages/en/plugins.js @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +const fs = require('fs'); +const yaml = require('js-yaml'); +const React = require('react'); +const Components = require(`${process.cwd()}/core/Components.js`); +const { + Block: { Container }, + BulletLine, +} = Components; + +const pluginsDirectory = require('path').join(process.cwd(), 'data/plugins'); +const pluginMetadata = fs + .readdirSync(pluginsDirectory) + .map(file => + yaml.safeLoad(fs.readFileSync(`./data/plugins/${file}`, 'utf8')), + ); +const truncate = text => + text.length > 170 ? text.substr(0, 170) + '...' : text; + +const addPluginDocsLink = '/docs/plugins/add-to-marketplace'; +const defaultIconUrl = 'img/logo-gradient-on-dark.svg'; + +const Plugins = () => ( + +); + +module.exports = Plugins; diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 72a9388818..5d8952ac41 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -91,7 +91,7 @@ { "type": "subcategory", "label": "Publishing", - "ids": ["plugins/publishing", "plugins/publish-private"] + "ids": ["plugins/publishing", "plugins/publish-private", "plugins/add-to-marketplace"] } ], "Configuration": [ diff --git a/microsite/siteConfig.js b/microsite/siteConfig.js index 73852d6ddd..cc5ad89e09 100644 --- a/microsite/siteConfig.js +++ b/microsite/siteConfig.js @@ -38,6 +38,10 @@ const siteConfig = { href: '/docs', label: 'Docs', }, + { + page: 'plugins', + label: 'Plugins', + }, { page: 'blog', blog: true, diff --git a/microsite/static/css/plugins.css b/microsite/static/css/plugins.css new file mode 100644 index 0000000000..042e2954e1 --- /dev/null +++ b/microsite/static/css/plugins.css @@ -0,0 +1,111 @@ +.PluginCard { + background-color: #272822; + height: 100%; + padding: 16px; + display: flex; + flex-direction: column; +} + +.grid { + display: grid; + grid-gap: 1rem; + grid-template-columns: repeat(4, 1fr); + grid-auto-rows: 1fr; + padding-top: 32px; +} + +@media (max-width: 1200px) { + .grid { + grid-template-columns: repeat(3, 1fr); + } +} + +@media only screen and (max-width: 815px) { + .grid { + grid-template-columns: repeat(2, 1fr); + } +} + +.PluginCard img { + float: left; + margin: 0px 16px 8px 0px; + height: 100px; + width: 100px; +} + +.PluginCardHeader { + max-height: fit-content; + min-height: fit-content; +} + +.PluginCardTitle { + color: white; + vertical-align: top; + margin: 8px 0px 0px 16px; +} + +.PluginAddNewButton { + position: absolute; + bottom: 16px; + right: 0px; +} + +.ButtonFilled { + padding: 4px 8px; + border-radius: 4px; + background-color: #36BAA2; + color: white; + margin-top: 36px; +} + +.ButtonFilled:hover { + border: 1px solid #36BAA2; + background-color: transparent; +} + +.ChipOutlined { + font-size: small; + border-radius: 16px; + padding: 2px 8px; + border: 1px solid #36BAA2; + color: #36BAA2; +} + +.PluginCardLink { + padding: 2px 8px; + position: absolute; + bottom: 0; + right: 0; +} + +.PluginPageLayout { + margin: auto; + max-width: 1430px; + padding: 20px; +} + +.PluginPageHeader { + position: relative; +} + +.PluginPageHeader h2 { + display: inline-block; +} + +.PluginCardBody { + padding-top: 8px; +} + +.PluginCardFooter { + position: relative; + min-height: 2em; +} + +.Author, .Author a { + margin-bottom: 0.25em; + color: rgba(255,255,255, 0.6); +} + + .Author a:hover { + color: white; +} From b602a7b729f8a3a388a8f6b8d61336bcbc0a6e82 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Mon, 31 Aug 2020 15:13:42 +0200 Subject: [PATCH 193/359] Remove WIP (#2185) --- packages/techdocs-container/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/techdocs-container/README.md b/packages/techdocs-container/README.md index 2bfa9a1d26..aa1bf15974 100644 --- a/packages/techdocs-container/README.md +++ b/packages/techdocs-container/README.md @@ -2,8 +2,6 @@ This is the Docker container that powers the creation of static documentation sites that are supported by [TechDocs](https://github.com/spotify/backstage/blob/master/plugins/techdocs). -**WIP: This is a work in progress. It is not ready for use yet. Follow our progress on [the Backstage Discord](https://discord.gg/MUpMjP2) under #docs-like-code or on [our GitHub Milestone](https://github.com/spotify/backstage/milestone/15).** - ## Getting Started Using the TechDocs CLI, we can invoke the latest version of `techdocs-container` via Docker Hub: From 0c29e3d410472c40ab939e1b73a934bebb410a3f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 31 Aug 2020 14:51:34 +0200 Subject: [PATCH 194/359] workflows: add discord notification --- .github/workflows/master.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 04b50c3e60..354838037e 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -74,3 +74,11 @@ jobs: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" package_root: "packages/core" tag_prefix: "v" + + - name: Discord notification + if: ${{ failure() }} + uses: Ilshidur/action-discord@0.2.0 + env: + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} + with: + args: 'Master build failed https://github.com/{{GITHUB_REPOSITORY}}/actions/runs/{{GITHUB_RUN_ID}}' From 43ac2c0af7f32c80188fd44468bbae279bf08069 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 31 Aug 2020 14:30:01 +0200 Subject: [PATCH 195/359] feat(catalog, app): new api 4 plugins integration --- packages/app/package.json | 1 + packages/app/src/App.tsx | 7 +- packages/app/src/apis.ts | 5 +- .../app/src/components/catalog/Component.tsx | 82 ------- .../app/src/components/catalog/EntityPage.tsx | 89 +++++++ plugins/catalog/src/Router.tsx | 164 +++---------- .../{EntityPageTabs => CatalogPage}/index.ts | 2 +- .../components/EntityPage/EntityPage.test.tsx | 100 -------- .../src/components/EntityPage/EntityPage.tsx | 231 ------------------ .../EntityPageLayout/EntityPageLayout.tsx | 147 +++++++++++ .../EntityPageLayout/Tabbed/Tabbed.tsx | 98 ++++++++ .../EntityPageLayout/Tabbed/index.ts | 16 ++ .../src/components/EntityPageLayout/index.ts | 16 ++ .../EntityPageTabs/EntityPageTabs.tsx | 98 -------- .../EntityProvider/EntityProvider.tsx | 21 +- .../src/components/EntityProvider/index.ts | 16 ++ plugins/catalog/src/hooks/useEntity.ts | 32 ++- plugins/catalog/src/index.ts | 3 +- plugins/catalog/src/plugin.ts | 8 - plugins/catalog/src/routes.ts | 2 +- plugins/github-actions/src/Router.tsx | 37 ++- plugins/github-actions/src/index.ts | 2 +- plugins/github-actions/src/plugin.ts | 1 - 23 files changed, 492 insertions(+), 686 deletions(-) delete mode 100644 packages/app/src/components/catalog/Component.tsx create mode 100644 packages/app/src/components/catalog/EntityPage.tsx rename plugins/catalog/src/components/{EntityPageTabs => CatalogPage}/index.ts (92%) delete mode 100644 plugins/catalog/src/components/EntityPage/EntityPage.test.tsx delete mode 100644 plugins/catalog/src/components/EntityPage/EntityPage.tsx create mode 100644 plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx create mode 100644 plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx create mode 100644 plugins/catalog/src/components/EntityPageLayout/Tabbed/index.ts create mode 100644 plugins/catalog/src/components/EntityPageLayout/index.ts delete mode 100644 plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx rename packages/app/src/components/catalog/index.tsx => plugins/catalog/src/components/EntityProvider/EntityProvider.tsx (60%) create mode 100644 plugins/catalog/src/components/EntityProvider/index.ts diff --git a/packages/app/package.json b/packages/app/package.json index d6598b9954..4b9b40466b 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -5,6 +5,7 @@ "dependencies": { "@backstage/cli": "^0.1.1-alpha.19", "@backstage/core": "^0.1.1-alpha.19", + "@backstage/catalog-model": "^0.1.1-alpha.19", "@backstage/plugin-api-docs": "^0.1.1-alpha.19", "@backstage/plugin-catalog": "^0.1.1-alpha.19", "@backstage/plugin-circleci": "^0.1.1-alpha.19", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index cb0853a013..5abc058920 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -26,11 +26,11 @@ import * as plugins from './plugins'; import { apis } from './apis'; import { hot } from 'react-hot-loader/root'; import { providers } from './identityProviders'; -import { CatalogPlugin } from '@backstage/plugin-catalog'; +import { CatalogRouter } from '@backstage/plugin-catalog'; // import { ExplorePlugin } from '@backstage/plugin-explore'; import { Route, Routes } from 'react-router'; -import { EntityPage } from './components/catalog'; +import { EntityPage } from './components/catalog/EntityPage'; const app = createApp({ apis, @@ -56,11 +56,12 @@ const AppRoutes = () => ( } + element={} /> {/* } /> */} ); + const App: FC<{}> = () => ( diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index df023b80cb..f8a28ca85a 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -74,7 +74,10 @@ import { TravisCIApi, travisCIApiRef, } from '@roadiehq/backstage-plugin-travis-ci'; -import { GithubPullRequestsClient, githubPullRequestsApiRef } from '@roadiehq/backstage-plugin-github-pull-requests'; +import { + GithubPullRequestsClient, + githubPullRequestsApiRef, +} from '@roadiehq/backstage-plugin-github-pull-requests'; export const apis = (config: ConfigApi) => { // eslint-disable-next-line no-console diff --git a/packages/app/src/components/catalog/Component.tsx b/packages/app/src/components/catalog/Component.tsx deleted file mode 100644 index 2d2aef9f79..0000000000 --- a/packages/app/src/components/catalog/Component.tsx +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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 React from 'react'; -import { - GitHubActionsPlugin, - GITHUB_ACTIONS_ANNOTATION, -} from '@backstage/plugin-github-actions'; -import { - useEntity, - EntityPageTabs as Tabs, - EntityMetadataCard, -} from '@backstage/plugin-catalog'; -import { Grid } from '@material-ui/core'; - -const OverviewPage = () => { - const entity = useEntity(); - return ( - - - - ); -}; - -// Just for illustration purposes, gonna live in plugin-circle-ci -const CIRCLE_CI_ANNOTATION = 'circle-ci-slug-dummy'; - -const DefaultEntity = () => { - const entity = useEntity(); - - const isCIAvailable = [ - entity!.metadata!.annotations?.[GITHUB_ACTIONS_ANNOTATION], - entity!.metadata!.annotations?.[CIRCLE_CI_ANNOTATION], - ].some(Boolean); - - return ( - - - - - {isCIAvailable && ( - - {entity!.metadata!.annotations?.[GITHUB_ACTIONS_ANNOTATION] && ( - - )} - - )} - - {/* eslint-disable-next-line no-console */} -
{console.log('was here /docs')}Docs tab contents
-
-
- ); -}; - -const Service = () => ; -const Website = () => ; -const UnkownEntity = () =>
Unknown entity!
; - -export const ComponentEntity = () => { - const entity = useEntity(); - switch (entity.spec!.type) { - case 'service': - return ; - case 'website': - return ; - default: - return ; - } -}; diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx new file mode 100644 index 0000000000..4368d8e26b --- /dev/null +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -0,0 +1,89 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { GitHubActionsPage } from '@backstage/plugin-github-actions'; +import React from 'react'; +import { + EntityPageLayout, + useEntity, + EntityMetadataCard, +} from '@backstage/plugin-catalog'; +import { Entity } from '@backstage/catalog-model'; + +export const OverviewPage = ({ entity }: { entity: Entity }) => ( + +); + +const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + } + /> + Much docs, such wow ({entity.metadata.name})🐶} + /> + +); + +const WebsiteEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + } + /> + Much docs, such wow ({entity.metadata.name})🐶} + /> + +); + +const DefaultEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + +); + +export const EntityPage = () => { + const { entity } = useEntity(); + switch (entity?.spec?.type) { + case 'service': + return ; + case 'website': + return ; + default: + return ; + } +}; diff --git a/plugins/catalog/src/Router.tsx b/plugins/catalog/src/Router.tsx index 701b9d1045..161536c714 100644 --- a/plugins/catalog/src/Router.tsx +++ b/plugins/catalog/src/Router.tsx @@ -13,152 +13,58 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { FC, useState } from 'react'; -import { CatalogPage } from './components/CatalogPage/CatalogPage'; -// import { EntityPage } from './components/EntityPage/EntityPage'; -import { Route, Routes, useParams, useNavigate } from 'react-router'; +import React, { ComponentType } from 'react'; +import { CatalogPage } from './components/CatalogPage'; +import { EntityPageLayout } from './components/EntityPageLayout'; +import { Route, Routes } from 'react-router'; import { entityRoute, rootRoute, entityRouteDefault } from './routes'; -import { useEntityFromUrl, EntityContext, useEntity } from './hooks/useEntity'; -import { - pageTheme, - PageTheme, - Page, - Header, - HeaderLabel, - Content, - Progress, -} from '@backstage/core'; -import { Entity } from '@backstage/catalog-model'; -import { FavouriteEntity } from './components/FavouriteEntity/FavouriteEntity'; -import { Box } from '@material-ui/core'; -import { EntityContextMenu } from './components/EntityContextMenu/EntityContextMenu'; -import { UnregisterEntityDialog } from './components/UnregisterEntityDialog/UnregisterEntityDialog'; -import { Alert } from '@material-ui/lab'; -// const EntityContext = React.createContext(null); -export const getPageTheme = (entity?: Entity): PageTheme => { - const themeKey = entity?.spec?.type?.toString() ?? 'home'; - return pageTheme[themeKey] ?? pageTheme.home; -}; +import { Content } from '@backstage/core'; +import { Typography, Link } from '@material-ui/core'; +import { EntityProvider } from './components/EntityProvider'; -const EntityPageTitle = ({ - entity, - title, -}: { - title: string; - entity: Entity | undefined; -}) => ( - - {title} - {entity && } - +const DefaultEntityPage = () => ( + + + This is default entity page. + + To override this component with your custom implementation, read + docs on{' '} + + backstage.io/docs + + + + } + /> + ); -function headerProps( - kind: string, - namespace: string | undefined, - name: string, - entity: Entity | undefined, -): { headerTitle: string; headerType: string } { - return { - headerTitle: `${name}${namespace ? ` in ${namespace}` : ''}`, - headerType: (() => { - let t = kind.toLowerCase(); - if (entity && entity.spec && 'type' in entity.spec) { - t += ' — '; - t += (entity.spec as { type: string }).type.toLowerCase(); - } - return t; - })(), - }; -} -const EntityPageLayout = ({ children }: { children: React.ReactNode }) => { - const { entity, loading, error } = useEntityFromUrl(); - const { optionalNamespaceAndName, kind } = useParams() as { - optionalNamespaceAndName: string; - kind: string; - }; - const [name, namespace] = optionalNamespaceAndName.split(':').reverse(); - - const { headerTitle, headerType } = headerProps( - kind, - namespace, - name, - entity!, - ); - - const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); - const navigate = useNavigate(); - const cleanUpAfterRemoval = async () => { - setConfirmationDialogOpen(false); - navigate('/'); - }; - - const showRemovalDialog = () => setConfirmationDialogOpen(true); - - return ( - -
} - pageTitleOverride={headerTitle} - type={headerType} - > - {entity && ( - <> - - - - - )} -
- - {loading && } - - {entity && ( - - {children} - - )} - {error && ( - - {error.toString()} - - )} - setConfirmationDialogOpen(false)} - /> -
- ); -}; - -export const CatalogPlugin = ({ EntityPage }) => ( +export const CatalogRouter = ({ + EntityPage = DefaultEntityPage, +}: { + EntityPage?: ComponentType; +}) => ( } /> + - + } /> + - + } /> ); - -export { EntityMetadataCard } from './components/EntityMetadataCard/EntityMetadataCard'; diff --git a/plugins/catalog/src/components/EntityPageTabs/index.ts b/plugins/catalog/src/components/CatalogPage/index.ts similarity index 92% rename from plugins/catalog/src/components/EntityPageTabs/index.ts rename to plugins/catalog/src/components/CatalogPage/index.ts index 83fc3d1095..1adb8866c8 100644 --- a/plugins/catalog/src/components/EntityPageTabs/index.ts +++ b/plugins/catalog/src/components/CatalogPage/index.ts @@ -13,4 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { EntityPageTabs } from './EntityPageTabs'; +export { CatalogPage } from './CatalogPage'; diff --git a/plugins/catalog/src/components/EntityPage/EntityPage.test.tsx b/plugins/catalog/src/components/EntityPage/EntityPage.test.tsx deleted file mode 100644 index 1b966c0bde..0000000000 --- a/plugins/catalog/src/components/EntityPage/EntityPage.test.tsx +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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. - */ - -jest.mock('react-router-dom', () => { - const actual = jest.requireActual('react-router-dom'); - const mockNavigate = jest.fn(); - return { - ...actual, - useNavigate: jest.fn(() => mockNavigate), - useParams: jest.fn(), - }; -}); - -import { ApiProvider, ApiRegistry, errorApiRef } from '@backstage/core'; -import { Entity } from '@backstage/catalog-model'; -import { wrapInTestApp } from '@backstage/test-utils'; -import { render, waitFor } from '@testing-library/react'; -import * as React from 'react'; -import { CatalogApi, catalogApiRef } from '../../api/types'; -import { EntityPage, getPageTheme } from './EntityPage'; - -const { - useParams, - useNavigate, -}: { useParams: jest.Mock; useNavigate: () => jest.Mock } = jest.requireMock( - 'react-router-dom', -); - -const errorApi = { post: () => {} }; - -describe('EntityPage', () => { - it('should redirect to catalog page when name is not provided', async () => { - useParams.mockReturnValue({ - kind: 'Component', - optionalNamespaceAndName: '', - }); - - render( - wrapInTestApp( - ) as CatalogApi, - ], - ])} - > - - , - ), - ); - - await waitFor(() => expect(useNavigate()).toHaveBeenCalledWith('/catalog')); - }); -}); - -describe('getPageTheme', () => { - const defaultPageTheme = getPageTheme(); - it.each(['service', 'app', 'library', 'tool', 'documentation', 'website'])( - 'should select right theme for predefined type: %p ̰ ', - type => { - const theme = getPageTheme(({ - spec: { - type, - }, - } as any) as Entity); - expect(theme).toBeDefined(); - expect(theme).not.toBe(defaultPageTheme); - }, - ); - - it('should select default theme for unknown/unspecified types', () => { - const theme1 = getPageTheme(({ - spec: { - type: 'unknown-type', - }, - } as any) as Entity); - const theme2 = getPageTheme(({ - spec: {}, - } as any) as Entity); - expect(theme1).toBe(defaultPageTheme); - expect(theme2).toBe(defaultPageTheme); - }); -}); diff --git a/plugins/catalog/src/components/EntityPage/EntityPage.tsx b/plugins/catalog/src/components/EntityPage/EntityPage.tsx deleted file mode 100644 index adbc758067..0000000000 --- a/plugins/catalog/src/components/EntityPage/EntityPage.tsx +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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 { Entity } from '@backstage/catalog-model'; -import { - Content, - errorApiRef, - Header, - HeaderLabel, - Page, - pageTheme, - PageTheme, - Progress, - useApi, - HeaderTabs, -} from '@backstage/core'; -import { Box } from '@material-ui/core'; -import { Alert } from '@material-ui/lab'; -import React, { FC, useEffect, useState } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; -import { useAsync } from 'react-use'; -import { catalogApiRef } from '../..'; -import { EntityContextMenu } from '../EntityContextMenu/EntityContextMenu'; -import { EntityPageDocs } from '../EntityPageDocs/EntityDocsPage'; -import { EntityPageApi } from '../EntityPageApi/EntityPageApi'; -import { EntityPageOverview } from '../EntityPageOverview/EntityPageOverview'; -import { FavouriteEntity } from '../FavouriteEntity/FavouriteEntity'; -import { UnregisterEntityDialog } from '../UnregisterEntityDialog/UnregisterEntityDialog'; - -const REDIRECT_DELAY = 1000; -function headerProps( - kind: string, - namespace: string | undefined, - name: string, - entity: Entity | undefined, -): { headerTitle: string; headerType: string } { - return { - headerTitle: `${name}${namespace ? ` in ${namespace}` : ''}`, - headerType: (() => { - let t = kind.toLowerCase(); - if (entity && entity.spec && 'type' in entity.spec) { - t += ' — '; - t += (entity.spec as { type: string }).type.toLowerCase(); - } - return t; - })(), - }; -} - -export const getPageTheme = (entity?: Entity): PageTheme => { - const themeKey = entity?.spec?.type?.toString() ?? 'home'; - return pageTheme[themeKey] ?? pageTheme.home; -}; - -const EntityPageTitle: FC<{ title: string; entity: Entity | undefined }> = ({ - entity, - title, -}) => ( - - {title} - {entity && } - -); - -export const EntityPage: FC<{}> = () => { - const { - optionalNamespaceAndName, - kind, - selectedTabId = 'overview', - } = useParams() as { - optionalNamespaceAndName: string; - kind: string; - selectedTabId: string; - }; - const navigate = useNavigate(); - - const [name, namespace] = optionalNamespaceAndName.split(':').reverse(); - - const errorApi = useApi(errorApiRef); - const catalogApi = useApi(catalogApiRef); - - const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); - const { value: entity, error, loading } = useAsync( - () => catalogApi.getEntityByName({ kind, namespace, name }), - [catalogApi, kind, namespace, name], - ); - - useEffect(() => { - if (!error && !loading && !entity) { - errorApi.post(new Error('Entity not found!')); - setTimeout(() => { - navigate('/'); - }, REDIRECT_DELAY); - } - }, [errorApi, navigate, error, loading, entity]); - - if (!name) { - navigate('/catalog'); - return null; - } - - const cleanUpAfterRemoval = async () => { - setConfirmationDialogOpen(false); - navigate('/'); - }; - - const showRemovalDialog = () => setConfirmationDialogOpen(true); - - // TODO - Replace with proper tabs implementation - const tabs = [ - { - id: 'overview', - label: 'Overview', - content: (e: Entity) => , - }, - { - id: 'ci', - label: 'CI/CD', - }, - { - id: 'tests', - label: 'Tests', - }, - { - id: 'api', - label: 'API', - show: (e: Entity) => !!e?.spec?.implementsApis, - content: (e: Entity) => , - }, - { - id: 'monitoring', - label: 'Monitoring', - }, - { - id: 'quality', - label: 'Quality', - }, - { - id: 'docs', - label: 'Docs', - show: (e: Entity) => - !!e.metadata.annotations?.['backstage.io/techdocs-ref'], - content: (e: Entity) => , - }, - ]; - - const { headerTitle, headerType } = headerProps( - kind, - namespace, - name, - entity, - ); - - const selectedTab = tabs.find(tab => tab.id === selectedTabId); - - const filteredHeaderTabs = entity - ? tabs.filter(tab => (tab.show ? tab.show(entity) : true)) - : []; - - return ( - -
} - pageTitleOverride={headerTitle} - type={headerType} - > - {entity && ( - <> - - - - - )} -
- - {loading && } - - {error && ( - - {error.toString()} - - )} - - {entity && ( - <> - { - navigate( - `/catalog/${kind}/${optionalNamespaceAndName}/${filteredHeaderTabs[idx].id}`, - ); - }} - selectedIndex={filteredHeaderTabs.findIndex( - tab => tab.id === selectedTabId, - )} - /> - - {selectedTab && selectedTab.content - ? selectedTab.content(entity) - : null} - - setConfirmationDialogOpen(false)} - /> - - )} -
- ); -}; diff --git a/plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx b/plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx new file mode 100644 index 0000000000..532a9e3b0b --- /dev/null +++ b/plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx @@ -0,0 +1,147 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React, { useState } from 'react'; +import { useParams, useNavigate } from 'react-router'; + +import { + useEntityFromUrl, + EntityContext, + useEntity, +} from '../../hooks/useEntity'; +import { + pageTheme, + PageTheme, + Page, + Header, + HeaderLabel, + Content, + Progress, +} from '@backstage/core'; +import { Entity } from '@backstage/catalog-model'; +import { FavouriteEntity } from '../FavouriteEntity/FavouriteEntity'; +import { Box } from '@material-ui/core'; +import { EntityContextMenu } from '../EntityContextMenu/EntityContextMenu'; +import { UnregisterEntityDialog } from '../UnregisterEntityDialog/UnregisterEntityDialog'; +import { Alert } from '@material-ui/lab'; +import { Tabbed } from './Tabbed'; + +const getPageTheme = (entity?: Entity): PageTheme => { + const themeKey = entity?.spec?.type?.toString() ?? 'home'; + return pageTheme[themeKey] ?? pageTheme.home; +}; + +const EntityPageTitle = ({ + entity, + title, +}: { + title: string; + entity: Entity | undefined; +}) => ( + + {title} + {entity && } + +); + +function headerProps( + kind: string, + namespace: string | undefined, + name: string, + entity: Entity | undefined, +): { headerTitle: string; headerType: string } { + return { + headerTitle: `${name}${namespace ? ` in ${namespace}` : ''}`, + headerType: (() => { + let t = kind.toLowerCase(); + if (entity && entity.spec && 'type' in entity.spec) { + t += ' — '; + t += (entity.spec as { type: string }).type.toLowerCase(); + } + return t; + })(), + }; +} + +export const EntityPageLayout = ({ + children, +}: { + children: React.ReactNode; +}) => { + const { optionalNamespaceAndName, kind } = useParams() as { + optionalNamespaceAndName: string; + kind: string; + }; + const [name, namespace] = optionalNamespaceAndName.split(':').reverse(); + + const { entity, loading, error } = useEntity(); + const { headerTitle, headerType } = headerProps( + kind, + namespace, + name, + entity!, + ); + + const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); + const navigate = useNavigate(); + const cleanUpAfterRemoval = async () => { + setConfirmationDialogOpen(false); + navigate('/'); + }; + + const showRemovalDialog = () => setConfirmationDialogOpen(true); + + return ( + +
} + pageTitleOverride={headerTitle} + type={headerType} + > + {entity && ( + <> + + + + + )} +
+ + {loading && } + + {entity && {children}} + + {error && ( + + {error.toString()} + + )} + setConfirmationDialogOpen(false)} + /> +
+ ); +}; + +EntityPageLayout.Content = Tabbed.Content; diff --git a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx new file mode 100644 index 0000000000..6e32dd1c80 --- /dev/null +++ b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx @@ -0,0 +1,98 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { + useParams, + useNavigate, + PartialRouteObject, + matchRoutes, + RouteObject, + useRoutes, + Navigate, + RouteMatch, +} from 'react-router'; +import { Tab, HeaderTabs, Content } from '@backstage/core'; +import { Grid } from '@material-ui/core'; +import { Helmet } from 'react-helmet'; + +const getSelectedIndex = (matchedRoute: RouteMatch, tabs: Tab[]) => { + if (!matchedRoute) return 0; + const tabIndex = tabs.findIndex(t => t.id === matchedRoute.route.path); + return ~tabIndex ? tabIndex : 0; +}; + +export const Tabbed = { + Layout: ({ children }: { children: React.ReactNode }) => { + const routes: PartialRouteObject[] = []; + const tabs: Tab[] = []; + const params = useParams(); + const navigate = useNavigate(); + + React.Children.forEach(children, child => { + if (!React.isValidElement(child)) { + // Skip conditionals resolved to falses/nulls/undefineds etc + return; + } + const pathAndId = (child as JSX.Element).props.path; + + // Child here must be then always a functional component without any wrappers + tabs.push({ + id: pathAndId, + label: (child as JSX.Element).props.title, + }); + + routes.push({ + path: pathAndId, + element: child.props.element, + }); + }); + + // Add catch-all for incorrect sub-routes + routes.push({ + path: '/*', + element: , + }); + + const [matchedRoute] = + matchRoutes(routes as RouteObject[], `/${params['*']}`) ?? []; + const selectedIndex = getSelectedIndex(matchedRoute, tabs); + const currentTab = tabs[selectedIndex]; + const title = currentTab.label; + + const onTabChange = (index: number) => + navigate(tabs[index].id.slice(1, -2)); + + const currentRouteElement = useRoutes(routes); + + return ( + <> + + + + + {currentRouteElement} + + + + ); + }, + Content: (_props: { path: string; title: string; element: JSX.Element }) => + null, +}; diff --git a/plugins/catalog/src/components/EntityPageLayout/Tabbed/index.ts b/plugins/catalog/src/components/EntityPageLayout/Tabbed/index.ts new file mode 100644 index 0000000000..d58cd626ba --- /dev/null +++ b/plugins/catalog/src/components/EntityPageLayout/Tabbed/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { Tabbed } from './Tabbed'; diff --git a/plugins/catalog/src/components/EntityPageLayout/index.ts b/plugins/catalog/src/components/EntityPageLayout/index.ts new file mode 100644 index 0000000000..acf6f948f1 --- /dev/null +++ b/plugins/catalog/src/components/EntityPageLayout/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { EntityPageLayout } from './EntityPageLayout'; diff --git a/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx b/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx deleted file mode 100644 index 2053c05b91..0000000000 --- a/plugins/catalog/src/components/EntityPageTabs/EntityPageTabs.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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 React from 'react'; -import { - useParams, - useNavigate, - PartialRouteObject, - matchRoutes, - RouteObject, - useRoutes, - Navigate, - RouteMatch, -} from 'react-router'; -import { Tab, HeaderTabs, Content } from '@backstage/core'; -import { Grid } from '@material-ui/core'; -import { Helmet } from 'react-helmet'; - -const getSelectedIndex = (matchedRoute: RouteMatch, tabs: Tab[]) => { - if (!matchedRoute) return 0; - const tabIndex = tabs.findIndex(t => t.id === matchedRoute.route.path); - return ~tabIndex ? tabIndex : 0; -}; -export const EntityPageTabs = ({ children }: { children: React.ReactNode }) => { - const routes: PartialRouteObject[] = []; - const tabs: Tab[] = []; - const params = useParams(); - const navigate = useNavigate(); - - React.Children.forEach(children, child => { - if (!React.isValidElement(child)) { - // Skip conditionals resolved to falses/nulls/undefineds etc - return; - } - const pathAndId = - (child as JSX.Element).props.path + - ((child as JSX.Element).props.exact ? '' : '/*'); - routes.push({ - path: pathAndId, - element: (child as JSX.Element).props.children, - }); - tabs.push({ - id: pathAndId, - label: (child as JSX.Element).props.title, - }); - }); - - // Add catch-all for incorrect sub-routes - routes.push({ - path: '/*', - element: , - }); - - const [matchedRoute] = - matchRoutes(routes as RouteObject[], `/${params['*']}`) ?? []; - const selectedIndex = getSelectedIndex(matchedRoute, tabs); - const currentTab = tabs[selectedIndex]; - const title = currentTab.label; - - const onTabChange = (index: number) => navigate(tabs[index].id.slice(1, -2)); - - const currentRouteElement = useRoutes(routes); - - return ( - <> - - - - - {currentRouteElement} - - - - ); -}; -type TabProps = { - children: React.ReactNode; - title: string; - path: string; - exact?: boolean; -}; -EntityPageTabs.Tab = (_props: TabProps) => null; diff --git a/packages/app/src/components/catalog/index.tsx b/plugins/catalog/src/components/EntityProvider/EntityProvider.tsx similarity index 60% rename from packages/app/src/components/catalog/index.tsx rename to plugins/catalog/src/components/EntityProvider/EntityProvider.tsx index 9aaf1a568b..e328b64d44 100644 --- a/packages/app/src/components/catalog/index.tsx +++ b/plugins/catalog/src/components/EntityProvider/EntityProvider.tsx @@ -13,18 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; -import { useEntity } from '@backstage/plugin-catalog'; -import { ComponentEntity } from './Component'; +import React, { ReactNode } from 'react'; +import { useEntityFromUrl, EntityContext } from '../../hooks/useEntity'; -const UnkownEntityKind = () =>
Unknown entity kind!
; +export const EntityProvider = ({ children }: { children: ReactNode }) => { + const { entity, loading, error } = useEntityFromUrl(); -export const EntityPage = () => { - const entity = useEntity(); - switch (entity.kind) { - case 'Component': - return ; - default: - return ; - } + return ( + + {children} + + ); }; diff --git a/plugins/catalog/src/components/EntityProvider/index.ts b/plugins/catalog/src/components/EntityProvider/index.ts new file mode 100644 index 0000000000..01eae4737f --- /dev/null +++ b/plugins/catalog/src/components/EntityProvider/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { EntityProvider } from './EntityProvider'; diff --git a/plugins/catalog/src/hooks/useEntity.ts b/plugins/catalog/src/hooks/useEntity.ts index 022be1126f..ede45efeef 100644 --- a/plugins/catalog/src/hooks/useEntity.ts +++ b/plugins/catalog/src/hooks/useEntity.ts @@ -22,8 +22,19 @@ import { Entity } from '@backstage/catalog-model'; const REDIRECT_DELAY = 2000; -export const EntityContext = createContext((null as any) as Entity); -export const useEntityFromUrl = () => { +type EntityLoadingStatus = { + entity?: Entity; + loading: boolean | null; + error?: Error; +}; + +export const EntityContext = createContext({ + entity: undefined as any, + loading: null, + error: undefined, +}); + +export const useEntityFromUrl = (): EntityLoadingStatus => { const { optionalNamespaceAndName, kind } = useParams(); const [name, namespace] = optionalNamespaceAndName.split(':').reverse(); const navigate = useNavigate(); @@ -36,7 +47,7 @@ export const useEntityFromUrl = () => { ); useEffect(() => { - if (!error && !loading && !entity) { + if (error || (!loading && !entity)) { errorApi.post(new Error('Entity not found!')); setTimeout(() => { navigate('/'); @@ -46,10 +57,21 @@ export const useEntityFromUrl = () => { if (!name) { navigate('/catalog'); - return { entity: null, loading: null, error: new Error('No name in url') }; + return { + entity: undefined, + loading: null, + error: new Error('No name in url'), + } as never; } return { entity, loading, error }; }; -export const useEntity = () => useContext(EntityContext); +/** + * Always going to return an entity, or throw an error if not a descendant of a EntityProvider. + * Otherwise the useEntityFromUrl will take care of the `undefined` entity + */ +export const useEntity = () => + useContext & { entity: Entity }>( + EntityContext as any, + ); diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index ba8826aaa9..d0e9778a00 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -21,4 +21,5 @@ export * from './routes'; export { useEntityCompoundName } from './components/useEntityCompoundName'; export * from './Router'; export { useEntity } from './hooks/useEntity'; -export { EntityPageTabs } from './components/EntityPageTabs'; +export { EntityMetadataCard } from './components/EntityMetadataCard/EntityMetadataCard'; +export { EntityPageLayout } from './components/EntityPageLayout'; diff --git a/plugins/catalog/src/plugin.ts b/plugins/catalog/src/plugin.ts index 483e1f8464..4bfbf2453b 100644 --- a/plugins/catalog/src/plugin.ts +++ b/plugins/catalog/src/plugin.ts @@ -15,15 +15,7 @@ */ import { createPlugin } from '@backstage/core'; -import { CatalogPage } from './components/CatalogPage/CatalogPage'; -import { EntityPage } from './components/EntityPage/EntityPage'; -import { entityRoute, rootRoute, entityRouteDefault } from './routes'; export const plugin = createPlugin({ id: 'catalog', - register({ router }) { - router.addRoute(rootRoute, CatalogPage); - router.addRoute(entityRoute, EntityPage); - router.addRoute(entityRouteDefault, EntityPage); - }, }); diff --git a/plugins/catalog/src/routes.ts b/plugins/catalog/src/routes.ts index c3fd97564a..a9606760fb 100644 --- a/plugins/catalog/src/routes.ts +++ b/plugins/catalog/src/routes.ts @@ -30,6 +30,6 @@ export const entityRoute = createRouteRef({ }); export const entityRouteDefault = createRouteRef({ icon: NoIcon, - path: ':kind/:optionalNamespaceAndName', + path: ':kind/:optionalNamespaceAndName/*', title: 'Entity', }); diff --git a/plugins/github-actions/src/Router.tsx b/plugins/github-actions/src/Router.tsx index b843c594b9..c508302e6d 100644 --- a/plugins/github-actions/src/Router.tsx +++ b/plugins/github-actions/src/Router.tsx @@ -19,16 +19,29 @@ import { Routes, Route } from 'react-router'; import { rootRouteRef, buildRouteRef } from './plugin'; import { WorkflowRunDetails } from './components/WorkflowRunDetails'; import { WorkflowRunsTable } from './components/WorkflowRunsTable'; +import { GITHUB_ACTIONS_ANNOTATION } from './components/useProjectName'; +import { WarningPanel } from '@backstage/core'; -export const GitHubActionsPlugin = ({ entity }: { entity: Entity }) => ( - - } - /> - } - /> - -); +const isPluginApplicableToEntity = (entity: Entity) => + Boolean(entity?.metadata?.annotations?.[GITHUB_ACTIONS_ANNOTATION]) && + entity?.metadata?.annotations?.[GITHUB_ACTIONS_ANNOTATION] !== ''; + +export const GitHubActionsPage = ({ entity }: { entity: Entity }) => + !isPluginApplicableToEntity(entity) ? ( + + `entity.metadata.annotations[' + {GITHUB_ACTIONS_ANNOTATION}']` key is missing on the entity.{' '} + + ) : ( + + } + /> + } + /> + ) + + ); diff --git a/plugins/github-actions/src/index.ts b/plugins/github-actions/src/index.ts index f500685507..0bb5862d8b 100644 --- a/plugins/github-actions/src/index.ts +++ b/plugins/github-actions/src/index.ts @@ -17,5 +17,5 @@ export { plugin } from './plugin'; export * from './api'; export { Widget } from './components/Widget'; -export { GitHubActionsPlugin } from './Router'; +export { GitHubActionsPage } from './Router'; export { GITHUB_ACTIONS_ANNOTATION } from './components/useProjectName'; diff --git a/plugins/github-actions/src/plugin.ts b/plugins/github-actions/src/plugin.ts index ead33b7959..7e08229d6d 100644 --- a/plugins/github-actions/src/plugin.ts +++ b/plugins/github-actions/src/plugin.ts @@ -29,5 +29,4 @@ export const buildRouteRef = createRouteRef({ export const plugin = createPlugin({ id: 'github-actions', - register() {}, }); From f14ebd6d186b85311347feda95df1f7ef9cbc7a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Mon, 31 Aug 2020 17:29:29 +0200 Subject: [PATCH 196/359] Add more plugins to Marketplace (#2192) --- microsite/data/plugins/circleci.yaml | 9 +++++++++ microsite/data/plugins/gitops-cluster.yaml | 14 ++++++++++++++ microsite/data/plugins/graphiql.yaml | 13 +++++++++++++ microsite/data/plugins/lighthouse.yaml | 14 ++++++++++++++ microsite/data/plugins/new-relic.yaml | 14 ++++++++++++++ microsite/data/plugins/tech-radar.yaml | 9 +++++++++ microsite/i18n/en.json | 2 +- 7 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 microsite/data/plugins/circleci.yaml create mode 100644 microsite/data/plugins/gitops-cluster.yaml create mode 100644 microsite/data/plugins/graphiql.yaml create mode 100644 microsite/data/plugins/lighthouse.yaml create mode 100644 microsite/data/plugins/new-relic.yaml create mode 100644 microsite/data/plugins/tech-radar.yaml diff --git a/microsite/data/plugins/circleci.yaml b/microsite/data/plugins/circleci.yaml new file mode 100644 index 0000000000..c8a3677614 --- /dev/null +++ b/microsite/data/plugins/circleci.yaml @@ -0,0 +1,9 @@ +--- +title: CircleCI +author: Spotify +authorUrl: https://www.spotify.com/ +category: CI +description: Automate your development process with CI hosted in the cloud or on a private server. +documentation: https://github.com/spotify/backstage/tree/master/plugins/circleci +iconUrl: https://d3r49iyjzglexf.cloudfront.net/logo-wordmark-26f8eaea9b0f6e13b90d3f4a8fd8fda31490f5af41daab98bbede45037682576.svg +npmPackageName: '@backstage/plugin-circleci' diff --git a/microsite/data/plugins/gitops-cluster.yaml b/microsite/data/plugins/gitops-cluster.yaml new file mode 100644 index 0000000000..6f8ab6b097 --- /dev/null +++ b/microsite/data/plugins/gitops-cluster.yaml @@ -0,0 +1,14 @@ +--- +title: GitOps Clusters +author: Weaveworks +authorUrl: https://www.weave.works/ +category: Kubernetes +description: Create GitOps-managed Kubernetes clusters. Currently, it supports provisioning EKS clusters on GitHub via GitHub Actions. +documentation: https://github.com/spotify/backstage/tree/master/plugins/gitops-profiles +iconUrl: https://res-5.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_256,w_256,f_auto,q_auto:eco/v1462316670/i9d3delzvx1erzjhmcws.png +npmPackageName: '@backstage/plugin-gitops-profiles' +tags: + - kubernetes + - gitops + - github + - eks diff --git a/microsite/data/plugins/graphiql.yaml b/microsite/data/plugins/graphiql.yaml new file mode 100644 index 0000000000..73733c84fd --- /dev/null +++ b/microsite/data/plugins/graphiql.yaml @@ -0,0 +1,13 @@ +--- +title: GraphiQL +author: Spotify +authorUrl: https://www.spotify.com/ +category: Debugging +description: Integrates GraphiQL as a tool to browse GraphiQL endpoints inside Backstage. +documentation: https://github.com/spotify/backstage/tree/master/plugins/lighthouse +iconUrl: https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/GraphQL_Logo.svg/1024px-GraphQL_Logo.svg.png +npmPackageName: '@backstage/plugin-graphiql' +tags: + - graphql + - github + - gitlab diff --git a/microsite/data/plugins/lighthouse.yaml b/microsite/data/plugins/lighthouse.yaml new file mode 100644 index 0000000000..15a650f0bd --- /dev/null +++ b/microsite/data/plugins/lighthouse.yaml @@ -0,0 +1,14 @@ +--- +title: Lighthouse +author: Spotify +authorUrl: https://www.spotify.com/ +category: Accessibility +description: Google's Lighthouse tool is a great resource for benchmarking and improving the accessibility, performance, SEO, and best practices of your website. +documentation: https://github.com/spotify/backstage/tree/master/plugins/lighthouse +iconUrl: https://seeklogo.com/images/G/google-lighthouse-logo-1C7FA08580-seeklogo.com.png +npmPackageName: '@backstage/plugin-lighthouse' +tags: + - web + - seo + - accessibility + - performance diff --git a/microsite/data/plugins/new-relic.yaml b/microsite/data/plugins/new-relic.yaml new file mode 100644 index 0000000000..e3ddf18652 --- /dev/null +++ b/microsite/data/plugins/new-relic.yaml @@ -0,0 +1,14 @@ +--- +title: New Relic +author: '@timwheelercom' +authorUrl: https://github.com/timwheelercom +category: Monitoring +description: Observability platform built to help engineers create and monitor their software. +documentation: https://github.com/spotify/backstage/tree/master/plugins/newrelic +iconUrl: https://www.mulesoft.com/sites/default/files/2018-10/New_relic.png +npmPackageName: '@backstage/plugin-newrelic' +tags: + - performance + - monitoring + - errors + - alerting diff --git a/microsite/data/plugins/tech-radar.yaml b/microsite/data/plugins/tech-radar.yaml new file mode 100644 index 0000000000..a20667e8f0 --- /dev/null +++ b/microsite/data/plugins/tech-radar.yaml @@ -0,0 +1,9 @@ +--- +title: Tech Radar +author: Spotify +authorUrl: https://www.spotify.com/ +category: Discovery +description: Visualize the your company's official guidelines of different areas of software development. +documentation: https://github.com/spotify/backstage/tree/master/plugins/tech-radar +iconUrl: https://github.com/spotify/backstage/raw/master/plugins/tech-radar/docs/screenshot.png +npmPackageName: '@backstage/plugin-tech-radar' diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json index f61442e938..96ffb780dc 100644 --- a/microsite/i18n/en.json +++ b/microsite/i18n/en.json @@ -296,9 +296,9 @@ "links": { "GitHub": "GitHub", "Docs": "Docs", + "Plugins": "Plugins", "Blog": "Blog", "Demos": "Demos", - "Plugins": "Plugins", "Newsletter": "Newsletter" }, "categories": { From 0d40a302ff0410c84bff96a9d8136db8e6058bdb Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 31 Aug 2020 17:30:47 +0200 Subject: [PATCH 197/359] cli: define BUILD_INFO in builds --- packages/cli/src/lib/bundler/bundle.ts | 2 +- packages/cli/src/lib/bundler/config.ts | 45 ++++++++++++++++++++++++-- packages/cli/src/lib/bundler/server.ts | 2 +- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index 1193ed505e..8746c807e7 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -36,7 +36,7 @@ export async function buildBundle(options: BuildOptions) { const { statsJsonEnabled } = options; const paths = resolveBundlingPaths(options); - const config = createConfig(paths, { + const config = await createConfig(paths, { ...options, checksEnabled: false, isDev: false, diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index d432d6b64e..61f4448dd9 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import fs from 'fs-extra'; import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin'; import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin'; @@ -25,6 +26,9 @@ import { Config } from '@backstage/config'; import { BundlingPaths } from './paths'; import { transforms } from './transforms'; import { BundlingOptions, BackendBundlingOptions } from './types'; +import { version } from '../../lib/version'; +import { paths as cliPaths } from '../../lib/paths'; +import { runPlain } from '../run'; export function resolveBaseUrl(config: Config): URL { const baseUrl = config.getString('app.baseUrl'); @@ -35,10 +39,40 @@ export function resolveBaseUrl(config: Config): URL { } } -export function createConfig( +async function readBuildInfo() { + const timestamp = Date.now(); + + let commit = 'unknown'; + try { + commit = await runPlain('git', 'rev-parse', 'HEAD'); + } catch (error) { + console.warn(`WARNING: Failed to read git commit, ${error}`); + } + + let gitVersion = 'unknown'; + try { + gitVersion = await runPlain('git', 'describe', '--always'); + } catch (error) { + console.warn(`WARNING: Failed to describe git version, ${error}`); + } + + const { version: packageVersion } = await fs.readJson( + cliPaths.resolveTarget('package.json'), + ); + + return { + cliVersion: version, + gitVersion, + packageVersion, + timestamp, + commit, + }; +} + +export async function createConfig( paths: BundlingPaths, options: BundlingOptions, -): webpack.Configuration { +): Promise { const { checksEnabled, isDev } = options; const { plugins, loaders } = transforms(options); @@ -81,6 +115,13 @@ export function createConfig( }), ); + const buildInfo = await readBuildInfo(); + plugins.push( + new webpack.DefinePlugin({ + 'process.env.BUILD_INFO': JSON.stringify(buildInfo), + }), + ); + return { mode: isDev ? 'development' : 'production', profile: false, diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index c7bacc0c7d..4c4a424d08 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -30,7 +30,7 @@ export async function serveBundle(options: ServeOptions) { const paths = resolveBundlingPaths(options); const pkgPath = paths.targetPackageJson; const pkg = await fs.readJson(pkgPath); - const config = createConfig(paths, { + const config = await createConfig(paths, { ...options, isDev: true, baseUrl: url, From 4788ee073ac4ebeef288ffb9d3b16e1fe25d20a3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 31 Aug 2020 20:36:05 +0200 Subject: [PATCH 198/359] cli: use regexps to match paths for rollup plugins --- packages/cli/src/lib/builder/config.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/lib/builder/config.ts b/packages/cli/src/lib/builder/config.ts index e62e1fc6cd..bc689a3eb0 100644 --- a/packages/cli/src/lib/builder/config.ts +++ b/packages/cli/src/lib/builder/config.ts @@ -88,15 +88,25 @@ export const makeConfigs = async ( }), resolve({ mainFields }), commonjs({ - include: ['node_modules/**', '../../node_modules/**'], - exclude: ['**/*.stories.*', '**/*.test.*'], + include: /node_modules/, + exclude: [/\/[^/]+\.(?:stories|test)\.[^/]+$/], }), postcss(), - imageFiles({ exclude: '**/*.icon.svg' }), + imageFiles({ + exclude: /\.icon\.svg$/, + include: [ + /\.css$/, + /\.svg$/, + /\.png$/, + /\.gif$/, + /\.jpg$/, + /\.jpeg$/, + ], + }), json(), yaml(), svgr({ - include: '**/*.icon.svg', + include: /\.icon\.svg$/, template: svgrTemplate, }), esbuild({ From e57798cd882131feda647c7a4ec2f796535e574e Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 31 Aug 2020 18:01:43 +0200 Subject: [PATCH 199/359] test(catalog): test Tabbed implementation --- .../EntityPageLayout/Tabbed/Tabbed.test.tsx | 183 ++++++++++++++++++ .../EntityPageLayout/Tabbed/Tabbed.tsx | 19 +- 2 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.test.tsx diff --git a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.test.tsx b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.test.tsx new file mode 100644 index 0000000000..3eccafbf7a --- /dev/null +++ b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.test.tsx @@ -0,0 +1,183 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { Tabbed } from './Tabbed'; +import { renderInTestApp } from '@backstage/test-utils'; +import { fireEvent } from '@testing-library/react'; +import { act } from 'react-dom/test-utils'; +import { Routes, Route } from 'react-router'; + +describe('Tabbed layout', () => { + it('renders simplest case', async () => { + const rendered = await renderInTestApp( + + tabbed-test-content} + /> + , + ); + + expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument(); + expect(rendered.getByText('tabbed-test-content')).toBeInTheDocument(); + }); + + it('navigates when user clicks different tab', async () => { + const rendered = await renderInTestApp( + + + tabbed-test-content} + /> + tabbed-test-content-2} + /> + + } + /> + , + ); + + const secondTab = rendered.queryAllByRole('tab')[1]; + act(() => { + fireEvent.click(secondTab); + }); + + expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument(); + expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument(); + + expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument(); + expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument(); + }); + + describe('correctly delegates nested links', () => { + const renderRoute = (route: string) => + renderInTestApp( + + + tabbed-test-content} + /> + + tabbed-test-content-2 + + tabbed-test-nested-content-2} + /> + + + } + /> + + } + /> + , + { routeEntries: [route] }, + ); + + it('works for nested content', async () => { + const rendered = await renderRoute('/some-other-path/nested'); + + expect( + rendered.queryByText('tabbed-test-content'), + ).not.toBeInTheDocument(); + expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument(); + expect( + rendered.queryByText('tabbed-test-nested-content-2'), + ).toBeInTheDocument(); + }); + + it('works for non-nested content', async () => { + const rendered = await renderRoute('/some-other-path/'); + + expect( + rendered.queryByText('tabbed-test-content'), + ).not.toBeInTheDocument(); + expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument(); + expect( + rendered.queryByText('tabbed-test-nested-content-2'), + ).not.toBeInTheDocument(); + }); + }); + + it('shows only one tab contents at a time', async () => { + const rendered = await renderInTestApp( + + tabbed-test-content} + /> + tabbed-test-content-2} + /> + , + { routeEntries: ['/some-other-path'] }, + ); + + expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument(); + expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument(); + + expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument(); + expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument(); + }); + + it('redirects to the top level when no route is matching the url', async () => { + const rendered = await renderInTestApp( + + tabbed-test-content} + /> + tabbed-test-content-2} + /> + , + { routeEntries: ['/non-existing-path'] }, + ); + + expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument(); + expect(rendered.getByText('tabbed-test-content')).toBeInTheDocument(); + expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument(); + + expect( + rendered.queryByText('tabbed-test-content-2'), + ).not.toBeInTheDocument(); + }); +}); diff --git a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx index 6e32dd1c80..6f3e5e1fcf 100644 --- a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx +++ b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx @@ -34,6 +34,23 @@ const getSelectedIndex = (matchedRoute: RouteMatch, tabs: Tab[]) => { return ~tabIndex ? tabIndex : 0; }; +/** + * Compound component, which allows you to define layout + * for EntityPage using Tabs as a subnavigation mechanism + * Constists of 2 parts: Tabbed.Layout and Tabbed.Content. + * Takes care of: tabs, routes, document titles, spacing around content + * + * @example + * ```jsx + * + * This is rendered under /example/anything-here route} + * /> + * + * ``` + */ export const Tabbed = { Layout: ({ children }: { children: React.ReactNode }) => { const routes: PartialRouteObject[] = []; @@ -73,7 +90,7 @@ export const Tabbed = { const title = currentTab.label; const onTabChange = (index: number) => - navigate(tabs[index].id.slice(1, -2)); + navigate(tabs[index].id.replace(/\/\*$/g, '')); const currentRouteElement = useRoutes(routes); From 660de68e7e41b4c855ee084973ceabf96583fc0f Mon Sep 17 00:00:00 2001 From: nikek Date: Mon, 31 Aug 2020 23:01:40 +0200 Subject: [PATCH 200/359] Add AboutCard component to EntityPageOverview --- packages/core/src/layout/Header/Header.tsx | 2 +- .../components/AboutCard/AboutCard.test.tsx | 46 +++++ .../src/components/AboutCard/AboutCard.tsx | 182 ++++++++++++++++++ .../IconLinkVertical/IconLinkVertical.tsx | 53 +++++ .../IconLinkVertical/index.ts} | 14 +- .../index.ts} | 17 +- .../EntityPageOverview/EntityPageOverview.tsx | 4 +- 7 files changed, 286 insertions(+), 32 deletions(-) create mode 100644 plugins/catalog/src/components/AboutCard/AboutCard.test.tsx create mode 100644 plugins/catalog/src/components/AboutCard/AboutCard.tsx create mode 100644 plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx rename plugins/catalog/src/components/{EntityMetadataCard/EntityMetadataCard.tsx => AboutCard/IconLinkVertical/index.ts} (61%) rename plugins/catalog/src/components/{EntityMetadataCard/EntityMetadataCard.test.tsx => AboutCard/index.ts} (50%) diff --git a/packages/core/src/layout/Header/Header.tsx b/packages/core/src/layout/Header/Header.tsx index 7aafc2730e..6b9ea05245 100644 --- a/packages/core/src/layout/Header/Header.tsx +++ b/packages/core/src/layout/Header/Header.tsx @@ -38,7 +38,7 @@ const useStyles = makeStyles( alignItems: 'center', backgroundImage: props => props.backgroundImage, backgroundPosition: 'center', - backgroundSize: '100% 400px', + backgroundSize: 'cover', }, leftItemsBox: { flex: '1 1 auto', diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx new file mode 100644 index 0000000000..bf69818995 --- /dev/null +++ b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx @@ -0,0 +1,46 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { render } from '@testing-library/react'; +import { AboutCard } from './AboutCard'; + +describe('', () => { + it('renders info and "view source" link', () => { + const entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'software', + annotations: { + 'backstage.io/managed-by-location': + 'github:https://github.com/spotify/backstage/blob/master/software.yaml', + }, + }, + spec: { + owner: 'guest', + type: 'service', + lifecycle: 'production', + }, + }; + const { getByText } = render(); + expect(getByText('service')).toBeInTheDocument(); + expect(getByText('View Source').closest('a')).toHaveAttribute( + 'href', + 'https://github.com/spotify/backstage/blob/master/software.yaml', + ); + }); +}); diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx new file mode 100644 index 0000000000..d27175fcf3 --- /dev/null +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -0,0 +1,182 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { + Grid, + Typography, + makeStyles, + Chip, + IconButton, + Card, + CardContent, + CardHeader, + Divider, +} from '@material-ui/core'; +import { Entity } from '@backstage/catalog-model'; + +import GitHubIcon from '@material-ui/icons/GitHub'; +import { IconLinkVertical } from './IconLinkVertical'; +import EditIcon from '@material-ui/icons/Edit'; +import DocsIcon from '@material-ui/icons/Description'; + +const useStyles = makeStyles(theme => ({ + links: { + margin: theme.spacing(2, 0, 2), + display: 'grid', + gridAutoFlow: 'column', + gridAutoColumns: 'min-content', + gridGap: theme.spacing(2), + }, + label: { + color: '#9e9e9e', + textTransform: 'uppercase', + fontSize: '12px', + fontWeight: 'bold', + overflow: 'hidden', + whiteSpace: 'nowrap', + }, + value: { + fontWeight: 'bold', + overflow: 'hidden', + lineHeight: '24px', + wordBreak: 'break-word', + }, + description: { + wordBreak: 'break-word', + }, +})); + +const iconMap: Record = { + github: , +}; + +type CodeLinkInfo = { icon?: React.ReactNode; href?: string }; + +function getCodeLinkInfo(entity: Entity): CodeLinkInfo { + const location = + entity?.metadata?.annotations?.['backstage.io/managed-by-location']; + + if (location) { + // split by first `:` + // e.g. "github:https://github.com/spotify/backstage/blob/master/software.yaml" + const [type, target] = location.split(/:(.+)/); + + return { icon: iconMap[type], href: target }; + } + return {}; +} + +type AboutCardProps = { + entity: Entity; +}; + +export function AboutCard({ entity }: AboutCardProps) { + const classes = useStyles(); + const codeLink = getCodeLinkInfo(entity); + + return ( + + + + + } + subheader={ + + } + /> + + + + + + {entity?.metadata?.description || 'No description'} + + + + + + + {(entity?.metadata?.tags || []).map(t => ( + + ))} + + + + + ); +} + +function AboutField({ + label, + value, + gridSizes, + children, +}: { + label: string; + value?: string; + gridSizes?: Record; + children?: React.ReactNode; +}) { + const classes = useStyles(); + + // Content is either children or a string prop `value` + const content = React.Children.count(children) ? ( + children + ) : ( + + {value || `unknown`} + + ); + return ( + + + {label} + + {content} + + ); +} diff --git a/plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx b/plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx new file mode 100644 index 0000000000..91a5211925 --- /dev/null +++ b/plugins/catalog/src/components/AboutCard/IconLinkVertical/IconLinkVertical.tsx @@ -0,0 +1,53 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 * as React from 'react'; +import { makeStyles, Link } from '@material-ui/core'; +import LinkIcon from '@material-ui/icons/Link'; + +export type IconLinkVerticalProps = { + icon?: React.ReactNode; + href?: string; + label: string; +}; + +const useIconStyles = makeStyles({ + link: { + display: 'grid', + justifyItems: 'center', + gridGap: 4, + textAlign: 'center', + }, + label: { + fontSize: '0.7rem', + textTransform: 'uppercase', + fontWeight: 600, + letterSpacing: 1.2, + }, +}); + +export function IconLinkVertical({ + icon = , + href = '#', + ...props +}: IconLinkVerticalProps) { + const classes = useIconStyles(); + return ( + + {icon} + {props.label} + + ); +} diff --git a/plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.tsx b/plugins/catalog/src/components/AboutCard/IconLinkVertical/index.ts similarity index 61% rename from plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.tsx rename to plugins/catalog/src/components/AboutCard/IconLinkVertical/index.ts index 57082a9c91..ddc146dbd8 100644 --- a/plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.tsx +++ b/plugins/catalog/src/components/AboutCard/IconLinkVertical/index.ts @@ -14,16 +14,4 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; -import { InfoCard, StructuredMetadataTable } from '@backstage/core'; -import React, { FC } from 'react'; - -type Props = { - entity: Entity; -}; - -export const EntityMetadataCard: FC = ({ entity }) => ( - - - -); +export { IconLinkVertical } from './IconLinkVertical'; diff --git a/plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.test.tsx b/plugins/catalog/src/components/AboutCard/index.ts similarity index 50% rename from plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.test.tsx rename to plugins/catalog/src/components/AboutCard/index.ts index 4bb64f221d..93765ff942 100644 --- a/plugins/catalog/src/components/EntityMetadataCard/EntityMetadataCard.test.tsx +++ b/plugins/catalog/src/components/AboutCard/index.ts @@ -14,19 +14,4 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; -import { render } from '@testing-library/react'; -import React from 'react'; -import { EntityMetadataCard } from './EntityMetadataCard'; - -describe('EntityMetadataCard component', () => { - it('should display entity name if provided', async () => { - const testEntity: Entity = { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { name: 'test' }, - }; - const rendered = await render(); - expect(await rendered.findByText('test')).toBeInTheDocument(); - }); -}); +export { AboutCard } from './AboutCard'; diff --git a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx index 45a62198c7..4067fef29a 100644 --- a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx +++ b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx @@ -24,14 +24,14 @@ import { } from '@backstage/plugin-jenkins'; import { Grid } from '@material-ui/core'; import React, { FC } from 'react'; -import { EntityMetadataCard } from '../EntityMetadataCard/EntityMetadataCard'; +import { AboutCard } from '../AboutCard'; export const EntityPageOverview: FC<{ entity: Entity }> = ({ entity }) => { return ( - + {entity.metadata?.annotations?.[ 'backstage.io/jenkins-github-folder' From 0ff78005cd23e9d54e313b95548c0b7138b0dd5c Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 31 Aug 2020 23:07:16 +0200 Subject: [PATCH 201/359] fix(docs): code block background --- microsite/static/css/custom.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/static/css/custom.css b/microsite/static/css/custom.css index 3c0184a07b..5e078582cc 100644 --- a/microsite/static/css/custom.css +++ b/microsite/static/css/custom.css @@ -216,7 +216,7 @@ td { code { font-family: IBM Plex Mono, Menlo, Monaco, Consolas, Courier New, monospace; font-weight: 500; - background-color: #0e0e0e; + background-color: #272822; } /* .stripe { From 713060d1f320280300dbf9742c5a8cbe22941cdd Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 31 Aug 2020 23:18:06 +0200 Subject: [PATCH 202/359] docs: plugin integration into catalog --- docs/plugins/index.md | 7 + ...integrating-plugin-into-service-catalog.md | 125 ++++++++++++++++++ microsite/i18n/en.json | 5 +- microsite/sidebars.json | 1 + .../app/src/components/catalog/EntityPage.tsx | 20 +-- .../EntityPageLayout/Tabbed/Tabbed.tsx | 9 +- .../src/{ => components}/Router.tsx | 11 +- plugins/github-actions/src/index.ts | 3 +- 8 files changed, 154 insertions(+), 27 deletions(-) create mode 100644 docs/plugins/integrating-plugin-into-service-catalog.md rename plugins/github-actions/src/{ => components}/Router.tsx (80%) diff --git a/docs/plugins/index.md b/docs/plugins/index.md index bb8951f959..857b8f7fcf 100644 --- a/docs/plugins/index.md +++ b/docs/plugins/index.md @@ -26,3 +26,10 @@ This helps the community know what plugins are in development. You can also use this process if you have an idea for a good plugin but you hope that someone else will pick up the work. + +## Integrate into the catalog service + +If your plugin isn't supposed to live as a standalone page, but rather needs to +be presented as a part of a catalog service (e.g. a separate tab or a card on an +"Overview" tab), then check out +[the instruction](integrating-plugin-into-service-catalog.md). on how to do it. diff --git a/docs/plugins/integrating-plugin-into-service-catalog.md b/docs/plugins/integrating-plugin-into-service-catalog.md new file mode 100644 index 0000000000..7819737c52 --- /dev/null +++ b/docs/plugins/integrating-plugin-into-service-catalog.md @@ -0,0 +1,125 @@ +--- +id: integrating-plugin-into-service-catalog +title: Integrate into the catalog service +--- + +> This is an advanced use case and currently is an experimental feature. Expect +> API to change over time + +## Steps + +1. [Create a plugin](#create-a-plugin) +1. [Export a router with relative routes](#export-a-router) +1. [Import and use router in the APP](#import-and-use-router-in-the-app) + +### Create a plugin + +Follow the [same process](create-a-plugin.md) as for standalone plugin. You +should have a separate package in a folder, which represents your plugin. + +Example: + +``` +$ yarn create-plugin +> ? Enter an ID for the plugin [required] my-plugin +> ? Enter the owner(s) of the plugin. If specified, this will be added to CODEOWNERS for the plugin path. [optional] + +Creating the plugin... +``` + +### Export a router + +Now in the plugin you have a `Router.tsx` file in the `src` folder. By default +it contains only one example route. Create a routing structure needed for your +plugin, keeping in mind that the whole set of routes defined here are going to +be mounted under some different route in the App. + +Example: + +`my-plugin` consists of 2 different views - `/me` and `/about`. I envision +people integrating it into plugin catalog as a tab named "MyPlugin". Then, my +`Routes.tsx` for the plugin is going to look like: + +```tsx + + } /> + } /> + +``` + +(where MePage and AboutPage are 2 components defined in your plugin and imported +accordingly inside `Router.tsx`) + +> Pay attention, if your `MePage` references the `AboutPage` it needs to do it +> through link to `about`, not `/about`. This allows react-router v6 to enable +> its relative routing mechanism. Read more - +> https://reacttraining.com/blog/react-router-v6-pre/#relative-route-path-and-link-to + +### Import and use router in the APP + +In the `app/src/components/catalog/EntityPage.tsx` (app === your folder, +containing backstage app) import your created Router: + +```tsx +import { Router as MyPluginRouter } from '@backstage/plugin-my-plugin; +``` + +Now, you need to mount `MyPluginRouter` onto some route, for example if you had: + +```tsx +const DefaultEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + +); +``` + +after you add your code it becomes: + +```tsx +const DefaultEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + } + /> + +); +``` + +All of magic happens thanks to the `EntityPageLayout` component, which comes as +an export from `@backstage/plugin-catalog` package. + +```tsx +type EntityPageLayoutContentProps = { + /** + * Going to be transformed into react-router v6 + * path under the hood. Read more at https://reacttraining.com/blog/react-router-v6-pre + */ + path: string; + /** + * Gets transformed into the title for the tab + */ + title: string; + /** + * Element that is rendered when the location + * matches the path provided + */ + element: JSX.Element; +}; +``` + +> The recommended pattern is to get the `entity` in the App and then pass it to +> the plugin's Router component as a prop. However if it inconvenient for you, +> use `useEntity` hook from `@backstage/plugin-catalog` directly inside your +> plugin. diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json index d2593a13ce..1f04f7430b 100644 --- a/microsite/i18n/en.json +++ b/microsite/i18n/en.json @@ -199,7 +199,7 @@ "title": "Backend plugin" }, "plugins/call-existing-api": { - "title": "Call existing API" + "title": "Call Existing API" }, "plugins/create-a-plugin": { "title": "Create a Backstage Plugin" @@ -210,6 +210,9 @@ "plugins/index": { "title": "Intro" }, + "plugins/integrating-plugin-into-service-catalog": { + "title": "Integrate into the catalog service" + }, "plugins/plugin-development": { "title": "Plugin Development in Backstage" }, diff --git a/microsite/sidebars.json b/microsite/sidebars.json index b07f326086..7576296b7a 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -73,6 +73,7 @@ "plugins/create-a-plugin", "plugins/plugin-development", "plugins/structure-of-a-plugin", + "plugins/integrating-plugin-into-service-catalog", { "type": "subcategory", "label": "Backends and APIs", diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 4368d8e26b..1c7a08e362 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { GitHubActionsPage } from '@backstage/plugin-github-actions'; +import { Router as GitHubActionsRouter } from '@backstage/plugin-github-actions'; import React from 'react'; import { EntityPageLayout, @@ -22,7 +22,7 @@ import { } from '@backstage/plugin-catalog'; import { Entity } from '@backstage/catalog-model'; -export const OverviewPage = ({ entity }: { entity: Entity }) => ( +const OverviewPage = ({ entity }: { entity: Entity }) => ( ); @@ -36,12 +36,7 @@ const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( } - /> - Much docs, such wow ({entity.metadata.name})🐶} + element={} /> ); @@ -56,12 +51,7 @@ const WebsiteEntityPage = ({ entity }: { entity: Entity }) => ( } - /> - Much docs, such wow ({entity.metadata.name})🐶} + element={} /> ); @@ -69,7 +59,7 @@ const WebsiteEntityPage = ({ entity }: { entity: Entity }) => ( const DefaultEntityPage = ({ entity }: { entity: Entity }) => ( } /> diff --git a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx index 6f3e5e1fcf..ad0b9d067c 100644 --- a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx +++ b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx @@ -78,10 +78,11 @@ export const Tabbed = { }); // Add catch-all for incorrect sub-routes - routes.push({ - path: '/*', - element: , - }); + if ((routes?.[0]?.path ?? '') !== '') + routes.push({ + path: '/*', + element: , + }); const [matchedRoute] = matchRoutes(routes as RouteObject[], `/${params['*']}`) ?? []; diff --git a/plugins/github-actions/src/Router.tsx b/plugins/github-actions/src/components/Router.tsx similarity index 80% rename from plugins/github-actions/src/Router.tsx rename to plugins/github-actions/src/components/Router.tsx index c508302e6d..80637b75f0 100644 --- a/plugins/github-actions/src/Router.tsx +++ b/plugins/github-actions/src/components/Router.tsx @@ -16,17 +16,18 @@ import React from 'react'; import { Entity } from '@backstage/catalog-model'; import { Routes, Route } from 'react-router'; -import { rootRouteRef, buildRouteRef } from './plugin'; -import { WorkflowRunDetails } from './components/WorkflowRunDetails'; -import { WorkflowRunsTable } from './components/WorkflowRunsTable'; -import { GITHUB_ACTIONS_ANNOTATION } from './components/useProjectName'; +import { rootRouteRef, buildRouteRef } from '../plugin'; +import { WorkflowRunDetails } from './WorkflowRunDetails'; +import { WorkflowRunsTable } from './WorkflowRunsTable'; +import { GITHUB_ACTIONS_ANNOTATION } from './useProjectName'; import { WarningPanel } from '@backstage/core'; const isPluginApplicableToEntity = (entity: Entity) => Boolean(entity?.metadata?.annotations?.[GITHUB_ACTIONS_ANNOTATION]) && entity?.metadata?.annotations?.[GITHUB_ACTIONS_ANNOTATION] !== ''; -export const GitHubActionsPage = ({ entity }: { entity: Entity }) => +export const Router = ({ entity }: { entity: Entity }) => + // TODO(shmidt-i): move warning to a separate standardized component !isPluginApplicableToEntity(entity) ? ( `entity.metadata.annotations[' diff --git a/plugins/github-actions/src/index.ts b/plugins/github-actions/src/index.ts index 0bb5862d8b..7f8b968c16 100644 --- a/plugins/github-actions/src/index.ts +++ b/plugins/github-actions/src/index.ts @@ -16,6 +16,5 @@ export { plugin } from './plugin'; export * from './api'; -export { Widget } from './components/Widget'; -export { GitHubActionsPage } from './Router'; +export { Router } from './components/Router'; export { GITHUB_ACTIONS_ANNOTATION } from './components/useProjectName'; From e8259d15150b8fc1f0a1fb9fc4ffdfc7406e97c5 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 31 Aug 2020 23:34:32 +0200 Subject: [PATCH 203/359] fix: adjust code after rebasing --- packages/app/src/App.tsx | 3 +- .../EntityPageLayout/Tabbed/Tabbed.tsx | 18 ++++++--- yarn.lock | 37 +++++++++++++++++-- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 5abc058920..642eb7b004 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -28,7 +28,7 @@ import { hot } from 'react-hot-loader/root'; import { providers } from './identityProviders'; import { CatalogRouter } from '@backstage/plugin-catalog'; // import { ExplorePlugin } from '@backstage/plugin-explore'; -import { Route, Routes } from 'react-router'; +import { Route, Routes, Navigate } from 'react-router'; import { EntityPage } from './components/catalog/EntityPage'; @@ -58,6 +58,7 @@ const AppRoutes = () => ( path="/catalog/*" element={} /> + } /> {/* } /> */} ); diff --git a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx index ad0b9d067c..693ee10c78 100644 --- a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx +++ b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx @@ -28,10 +28,14 @@ import { Tab, HeaderTabs, Content } from '@backstage/core'; import { Grid } from '@material-ui/core'; import { Helmet } from 'react-helmet'; -const getSelectedIndex = (matchedRoute: RouteMatch, tabs: Tab[]) => { - if (!matchedRoute) return 0; +const getSelectedIndexOrDefault = ( + matchedRoute: RouteMatch, + tabs: Tab[], + defaultIndex = 0, +) => { + if (!matchedRoute) return defaultIndex; const tabIndex = tabs.findIndex(t => t.id === matchedRoute.route.path); - return ~tabIndex ? tabIndex : 0; + return ~tabIndex ? tabIndex : defaultIndex; }; /** @@ -86,12 +90,16 @@ export const Tabbed = { const [matchedRoute] = matchRoutes(routes as RouteObject[], `/${params['*']}`) ?? []; - const selectedIndex = getSelectedIndex(matchedRoute, tabs); + const selectedIndex = getSelectedIndexOrDefault(matchedRoute, tabs); const currentTab = tabs[selectedIndex]; const title = currentTab.label; const onTabChange = (index: number) => - navigate(tabs[index].id.replace(/\/\*$/g, '')); + // Remove trailing /* + // And remove leading / for relative navigation + // Note! route resolves relative to the position in the React tree, + // not relative to current location + navigate(tabs[index].id.replace(/\/\*$/, '').replace(/^\//, '')); const currentRouteElement = useRoutes(routes); diff --git a/yarn.lock b/yarn.lock index d90056f47d..aa85370465 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2724,7 +2724,7 @@ dependencies: "@types/node" ">= 8" -"@open-draft/until@^1.0.3": +"@open-draft/until@^1.0.0", "@open-draft/until@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== @@ -4106,6 +4106,11 @@ dependencies: "@types/express" "*" +"@types/cookie@^0.3.3": + version "0.3.3" + resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" + integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== + "@types/cookie@^0.4.0": version "0.4.0" resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz#14f854c0f93d326e39da6e3b6f34f7d37513d108" @@ -11650,7 +11655,7 @@ graphql-upload@^8.0.2: http-errors "^1.7.3" object-path "^0.11.4" -graphql@15.3.0, graphql@^15.3.0: +graphql@15.3.0, graphql@^15.0.0, graphql@^15.3.0: version "15.3.0" resolved "https://registry.npmjs.org/graphql/-/graphql-15.3.0.tgz#3ad2b0caab0d110e3be4a5a9b2aa281e362b5278" integrity sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w== @@ -11858,7 +11863,7 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -headers-utils@^1.2.0: +headers-utils@^1.1.9, headers-utils@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/headers-utils/-/headers-utils-1.2.0.tgz#5e10d1bc9d2bccf789547afca5b991a3167241e8" integrity sha512-4/BMXcWrJErw7JpM87gF8MNEXcIMLzepYZjNRv/P9ctgupl2Ywa3u1PgHtNhSRq84bHH9Ndlkdy7bSi+bZ9I9A== @@ -15561,6 +15566,22 @@ ms@^2.0.0, ms@^2.1.1: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +msw@^0.19.5: + version "0.19.5" + resolved "https://registry.npmjs.org/msw/-/msw-0.19.5.tgz#7d2a1a852ccf1644d3db6735d69fff6777aac33f" + integrity sha512-J5eQ++gDVZoHPC8gVXtWcakLjgmPipvFj/sEnlRV/WViXuiq2CamSqO3Wbh6H8bAmj+k2vUWCfcVT1HjMdKB2Q== + dependencies: + "@open-draft/until" "^1.0.0" + "@types/cookie" "^0.3.3" + chalk "^4.0.0" + cookie "^0.4.1" + graphql "^15.0.0" + headers-utils "^1.1.9" + node-match-path "^0.4.2" + node-request-interceptor "^0.2.5" + statuses "^2.0.0" + yargs "^15.3.1" + msw@^0.20.5: version "0.20.5" resolved "https://registry.npmjs.org/msw/-/msw-0.20.5.tgz#b6141080c0d8b17c451d9ca36c28cc47b4ac487a" @@ -15807,7 +15828,7 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-match-path@^0.4.4: +node-match-path@^0.4.2, node-match-path@^0.4.4: version "0.4.4" resolved "https://registry.npmjs.org/node-match-path/-/node-match-path-0.4.4.tgz#516a10926093c0cc6f237d020685b593b19baebb" integrity sha512-pBq9gp7TG0r0VXuy/oeZmQsjBSnYQo7G886Ly/B3azRwZuEtHCY155dzmfoKWcDPGgyfIGD8WKVC7h3+6y7yTg== @@ -15866,6 +15887,14 @@ node-releases@^1.1.29, node-releases@^1.1.52, node-releases@^1.1.58: resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== +node-request-interceptor@^0.2.5: + version "0.2.6" + resolved "https://registry.npmjs.org/node-request-interceptor/-/node-request-interceptor-0.2.6.tgz#541278d7033bb6a8befb5dd793f83428cf6446a2" + integrity sha512-aJW1tPSM7nzuZFRe+C/KSz22GJO3CVFMxHHmMGX8Z+tjP7TCIVbzeckLFVfJG68BdVgrdOOP7Ejc57ag820eyA== + dependencies: + debug "^4.1.1" + headers-utils "^1.2.0" + node-request-interceptor@^0.3.5: version "0.3.5" resolved "https://registry.npmjs.org/node-request-interceptor/-/node-request-interceptor-0.3.5.tgz#4b26159617829c9a70643012c0fdc3ae4c78ae43" From 8539b14a7e9b9099f131d8dfcd57d45ac488c53c Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 31 Aug 2020 23:44:33 +0200 Subject: [PATCH 204/359] refactor: simplify optional chaining --- plugins/github-actions/src/components/Router.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/github-actions/src/components/Router.tsx b/plugins/github-actions/src/components/Router.tsx index 80637b75f0..d0745f67d4 100644 --- a/plugins/github-actions/src/components/Router.tsx +++ b/plugins/github-actions/src/components/Router.tsx @@ -23,8 +23,8 @@ import { GITHUB_ACTIONS_ANNOTATION } from './useProjectName'; import { WarningPanel } from '@backstage/core'; const isPluginApplicableToEntity = (entity: Entity) => - Boolean(entity?.metadata?.annotations?.[GITHUB_ACTIONS_ANNOTATION]) && - entity?.metadata?.annotations?.[GITHUB_ACTIONS_ANNOTATION] !== ''; + Boolean(entity.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION]) && + entity.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION] !== ''; export const Router = ({ entity }: { entity: Entity }) => // TODO(shmidt-i): move warning to a separate standardized component From 5127da1f68facd2409dd2b566133a8885691ec30 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 00:13:46 +0200 Subject: [PATCH 205/359] fix(github-actions): cards --- .../components/EntityPageCi/EntityPageCi.tsx | 5 +- .../EntityPageLayout/EntityPageLayout.tsx | 6 +-- .../EntityPageOverview/EntityPageOverview.tsx | 7 +-- .../{Widget/Widget.tsx => Cards/Cards.tsx} | 50 +++---------------- .../src/components/{Widget => Cards}/index.ts | 2 +- .../WorkflowRunsTable/WorkflowRunsTable.tsx | 10 +++- plugins/github-actions/src/index.ts | 1 + 7 files changed, 22 insertions(+), 59 deletions(-) rename plugins/github-actions/src/components/{Widget/Widget.tsx => Cards/Cards.tsx} (75%) rename plugins/github-actions/src/components/{Widget => Cards}/index.ts (88%) diff --git a/plugins/catalog/src/components/EntityPageCi/EntityPageCi.tsx b/plugins/catalog/src/components/EntityPageCi/EntityPageCi.tsx index d39617da25..3261764d11 100644 --- a/plugins/catalog/src/components/EntityPageCi/EntityPageCi.tsx +++ b/plugins/catalog/src/components/EntityPageCi/EntityPageCi.tsx @@ -14,9 +14,10 @@ * limitations under the License. */ +// TODO(shmidt-i): move to the app import { Entity } from '@backstage/catalog-model'; import { Content } from '@backstage/core'; -import { RecentWorkflowRunsCard as GithubActionsListWidget } from '@backstage/plugin-github-actions'; +import { LatestWorkflowsForBranchCard } from '@backstage/plugin-github-actions'; import { Grid } from '@material-ui/core'; import React, { FC } from 'react'; @@ -26,7 +27,7 @@ export const EntityPageCi: FC<{ entity: Entity }> = ({ entity }) => { {entity.metadata?.annotations?.['backstage.io/github-actions-id'] && ( - + )} diff --git a/plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx b/plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx index 532a9e3b0b..17f9f46830 100644 --- a/plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx +++ b/plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx @@ -16,11 +16,7 @@ import React, { useState } from 'react'; import { useParams, useNavigate } from 'react-router'; -import { - useEntityFromUrl, - EntityContext, - useEntity, -} from '../../hooks/useEntity'; +import { useEntity } from '../../hooks/useEntity'; import { pageTheme, PageTheme, diff --git a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx index 45a62198c7..26a1e08d42 100644 --- a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx +++ b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx @@ -14,10 +14,10 @@ * limitations under the License. */ +// TODO(shmidt-i): move to the app import { Entity } from '@backstage/catalog-model'; import { Content } from '@backstage/core'; import { SentryIssuesWidget } from '@backstage/plugin-sentry'; -import { Widget as GithubActionsWidget } from '@backstage/plugin-github-actions'; import { JenkinsBuildsWidget, JenkinsLastBuildWidget, @@ -47,11 +47,6 @@ export const EntityPageOverview: FC<{ entity: Entity }> = ({ entity }) => { )} - {entity.metadata?.annotations?.['backstage.io/github-actions-id'] && ( - - - - )} { - if (error) return Couldn't fetch {branch} runs; - if (loading) return ; - return ; -}; - -export const RecentWorkflowRunsCard = ({ +export const LatestWorkflowsForBranchCard = ({ entity, branch = 'master', }: { entity: Entity; branch: string; -}) => { - const errorApi = useApi(errorApiRef); - const [owner, repo] = ( - entity?.metadata.annotations?.['backstage.io/github-actions-id'] ?? '/' - ).split('/'); - const [{ loading, error }] = useWorkflowRuns({ - owner, - repo, - branch, - }); - - useEffect(() => { - if (error) { - errorApi.post(error); - } - }, [error, errorApi]); - - return ( - - - - ); -}; +}) => ( + + + +); diff --git a/plugins/github-actions/src/components/Widget/index.ts b/plugins/github-actions/src/components/Cards/index.ts similarity index 88% rename from plugins/github-actions/src/components/Widget/index.ts rename to plugins/github-actions/src/components/Cards/index.ts index 0bd5dad98a..8c987ea1d5 100644 --- a/plugins/github-actions/src/components/Widget/index.ts +++ b/plugins/github-actions/src/components/Cards/index.ts @@ -13,4 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { Widget, RecentWorkflowRunsCard } from './Widget'; +export { LatestWorkflowRunCard, LatestWorkflowsForBranchCard } from './Cards'; diff --git a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index 7e971118fc..cc834f09ee 100644 --- a/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/github-actions/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -23,7 +23,6 @@ import { useWorkflowRuns } from '../useWorkflowRuns'; import { WorkflowRunStatus } from '../WorkflowRunStatus'; import SyncIcon from '@material-ui/icons/Sync'; import { buildRouteRef } from '../../plugin'; -import { useEntityCompoundName } from '@backstage/plugin-catalog'; import { useProjectName } from '../useProjectName'; import { Entity } from '@backstage/catalog-model'; @@ -148,12 +147,19 @@ export const WorkflowRunsTableView: FC = ({ ); }; -export const WorkflowRunsTable = ({ entity }: { entity: Entity }) => { +export const WorkflowRunsTable = ({ + entity, + branch, +}: { + entity: Entity; + branch?: string; +}) => { const { value: projectName, loading } = useProjectName(entity); const [owner, repo] = (projectName ?? '/').split('/'); const [tableProps, { retry, setPage, setPageSize }] = useWorkflowRuns({ owner, repo, + branch, }); return ( diff --git a/plugins/github-actions/src/index.ts b/plugins/github-actions/src/index.ts index 7f8b968c16..17c1fa2dd7 100644 --- a/plugins/github-actions/src/index.ts +++ b/plugins/github-actions/src/index.ts @@ -17,4 +17,5 @@ export { plugin } from './plugin'; export * from './api'; export { Router } from './components/Router'; +export * from './components/Cards'; export { GITHUB_ACTIONS_ANNOTATION } from './components/useProjectName'; From 8a67a8df844d234fc1bcb007c2fa1b21b0674257 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 00:21:25 +0200 Subject: [PATCH 206/359] fix(docs): add i18n to gitignore to avoid lerna errors in master --- .gitignore | 1 + microsite/i18n/en.json | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 743561c3b6..aee80f4c10 100644 --- a/.gitignore +++ b/.gitignore @@ -92,6 +92,7 @@ dist # Microsite build output microsite/build +microsite/i18n # Gatsby files .cache/ diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json index 96ffb780dc..e9f63d5737 100644 --- a/microsite/i18n/en.json +++ b/microsite/i18n/en.json @@ -92,6 +92,9 @@ "features/software-catalog/software-catalog-api": { "title": "API" }, + "features/software-catalog/software-catalog-configuration": { + "title": "Catalog Configuration" + }, "features/software-catalog/descriptor-format": { "title": "Descriptor Format of Catalog Entities", "sidebar_label": "YAML File Format" From 329b663c93e28003b999271b4b35a044eddc2a01 Mon Sep 17 00:00:00 2001 From: Andrew Thauer <6507159+andrewthauer@users.noreply.github.com> Date: Sat, 29 Aug 2020 16:59:14 -0400 Subject: [PATCH 207/359] refactor: cleanup & renames to common db code --- .../src/database/connection.test.ts | 16 +- .../backend-common/src/database/connection.ts | 26 ++- .../src/database/postgres.test.ts | 176 ++++++++++-------- .../backend-common/src/database/postgres.ts | 45 +++-- .../src/database/sqlite3.test.ts | 17 +- .../backend-common/src/database/sqlite3.ts | 12 +- packages/backend/src/index.ts | 13 +- .../default-app/packages/backend/src/index.ts | 13 +- 8 files changed, 184 insertions(+), 134 deletions(-) diff --git a/packages/backend-common/src/database/connection.test.ts b/packages/backend-common/src/database/connection.test.ts index 814503fda1..2cc3a54e06 100644 --- a/packages/backend-common/src/database/connection.test.ts +++ b/packages/backend-common/src/database/connection.test.ts @@ -15,7 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; -import { createDatabase } from './connection'; +import { createDatabaseClient } from './connection'; describe('database connection', () => { const createConfig = (data: any) => @@ -26,10 +26,10 @@ describe('database connection', () => { }, ]); - describe(createDatabase, () => { + describe(createDatabaseClient, () => { it('returns a postgres connection', () => { expect( - createDatabase( + createDatabaseClient( createConfig({ client: 'pg', connection: { @@ -45,7 +45,7 @@ describe('database connection', () => { it('returns an sqlite connection', () => { expect( - createDatabase( + createDatabaseClient( createConfig({ client: 'sqlite3', connection: ':memory:', @@ -56,7 +56,7 @@ describe('database connection', () => { it('tries to create a mysql connection as a passthrough', () => { expect(() => - createDatabase( + createDatabaseClient( createConfig({ client: 'mysql', connection: { @@ -72,7 +72,7 @@ describe('database connection', () => { it('accepts overrides', () => { expect( - createDatabase( + createDatabaseClient( createConfig({ client: 'pg', connection: { @@ -93,7 +93,7 @@ describe('database connection', () => { it('throws an error without a client', () => { expect(() => - createDatabase( + createDatabaseClient( createConfig({ connection: '', }), @@ -103,7 +103,7 @@ describe('database connection', () => { it('throws an error without a connection', () => { expect(() => - createDatabase( + createDatabaseClient( createConfig({ client: 'pg', }), diff --git a/packages/backend-common/src/database/connection.ts b/packages/backend-common/src/database/connection.ts index d968275482..009a85239f 100644 --- a/packages/backend-common/src/database/connection.ts +++ b/packages/backend-common/src/database/connection.ts @@ -15,30 +15,36 @@ */ import knex from 'knex'; -import { ConfigReader } from '@backstage/config'; +import { Config } from '@backstage/config'; import { mergeDatabaseConfig } from './config'; -import { createPgDatabase } from './postgres'; -import { createSqlite3Database } from './sqlite3'; +import { createPgDatabaseClient } from './postgres'; +import { createSqliteDatabaseClient } from './sqlite3'; type DatabaseClient = 'pg' | 'sqlite3' | string; /** * Creates a knex database connection * - * @param config The database config + * @param dbConfig The database config * @param overrides Additional options to merge with the config */ -export function createDatabase( - config: ConfigReader, +export function createDatabaseClient( + dbConfig: Config, overrides?: Partial, ) { - const client: DatabaseClient = config.getString('client'); + const client: DatabaseClient = dbConfig.getString('client'); if (client === 'pg') { - return createPgDatabase(config, overrides); + return createPgDatabaseClient(dbConfig, overrides); } else if (client === 'sqlite3') { - return createSqlite3Database(config); + return createSqliteDatabaseClient(dbConfig); } - return knex(mergeDatabaseConfig(config.get(), overrides)); + return knex(mergeDatabaseConfig(dbConfig.get(), overrides)); } + +/** + * Alias for createDatabaseClient + * @deprecated Use createDatabaseClient instead + */ +export const createDatabase = createDatabaseClient; diff --git a/packages/backend-common/src/database/postgres.test.ts b/packages/backend-common/src/database/postgres.test.ts index 8edfc2c15d..82161dc53c 100644 --- a/packages/backend-common/src/database/postgres.test.ts +++ b/packages/backend-common/src/database/postgres.test.ts @@ -14,15 +14,26 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/config'; +import { Config, ConfigReader } from '@backstage/config'; import { - parsePgConnectionString, + createPgDatabaseClient, buildPgDatabaseConfig, - createPgDatabase, + getPgConnectionConfig, + parsePgConnectionString, } from './postgres'; describe('postgres', () => { - const createConfig = (connection: any) => + const createMockConnection = () => ({ + host: 'acme', + user: 'foo', + password: 'bar', + database: 'foodb', + }); + + const createMockConnectionString = () => + 'postgresql://foo:bar@acme:5432/foodb'; + + const createConfig = (connection: any): Config => ConfigReader.fromConfigs([ { context: '', @@ -35,86 +46,58 @@ describe('postgres', () => { describe(buildPgDatabaseConfig, () => { it('builds a postgres config', () => { - expect( - buildPgDatabaseConfig( - createConfig({ - host: 'acme', - user: 'foo', - password: 'bar', - port: '5432', - database: 'foodb', - }), - ), - ).toEqual({ + const mockConnection = createMockConnection(); + + expect(buildPgDatabaseConfig(createConfig(mockConnection))).toEqual({ client: 'pg', - connection: { - host: 'acme', - user: 'foo', - password: 'bar', - port: '5432', - database: 'foodb', - }, + connection: mockConnection, useNullAsDefault: true, }); }); it('builds a connection string config', () => { - expect( - buildPgDatabaseConfig( - createConfig('postgresql://foo:bar@acme:5432/foodb'), - ), - ).toEqual({ - client: 'pg', - connection: 'postgresql://foo:bar@acme:5432/foodb', - useNullAsDefault: true, - }); + const mockConnectionString = createMockConnectionString(); + + expect(buildPgDatabaseConfig(createConfig(mockConnectionString))).toEqual( + { + client: 'pg', + connection: mockConnectionString, + useNullAsDefault: true, + }, + ); }); it('overrides the database name', () => { + const mockConnection = createMockConnection(); + expect( - buildPgDatabaseConfig( - createConfig({ - host: 'somehost', - user: 'postgres', - password: 'pass', - database: 'foo', - }), - { connection: { database: 'foodb' } }, - ), + buildPgDatabaseConfig(createConfig(mockConnection), { + connection: { database: 'other_db' }, + }), ).toEqual({ client: 'pg', connection: { - host: 'somehost', - user: 'postgres', - password: 'pass', - database: 'foodb', + ...mockConnection, + database: 'other_db', }, useNullAsDefault: true, }); }); it('adds additional config settings', () => { + const mockConnection = createMockConnection(); + expect( - buildPgDatabaseConfig( - createConfig({ - host: 'somehost', - user: 'postgres', - password: 'pass', - database: 'foo', - }), - { - connection: { database: 'foodb' }, - pool: { min: 0, max: 7 }, - debug: true, - }, - ), + buildPgDatabaseConfig(createConfig(mockConnection), { + connection: { database: 'other_db' }, + pool: { min: 0, max: 7 }, + debug: true, + }), ).toEqual({ client: 'pg', connection: { - host: 'somehost', - user: 'postgres', - password: 'pass', - database: 'foodb', + ...mockConnection, + database: 'other_db', }, useNullAsDefault: true, pool: { min: 0, max: 7 }, @@ -123,37 +106,72 @@ describe('postgres', () => { }); it('overrides the database from connection string', () => { + const mockConnectionString = createMockConnectionString(); + const mockConnection = createMockConnection(); + expect( - buildPgDatabaseConfig( - createConfig('postgresql://postgres:pass@localhost:5432/dbname'), - { connection: { database: 'foodb' } }, - ), + buildPgDatabaseConfig(createConfig(mockConnectionString), { + connection: { database: 'other_db' }, + }), ).toEqual({ client: 'pg', connection: { - host: 'localhost', - user: 'postgres', - password: 'pass', + ...mockConnection, port: '5432', - database: 'foodb', + database: 'other_db', }, useNullAsDefault: true, }); }); }); - describe(createPgDatabase, () => { + describe(getPgConnectionConfig, () => { + it('returns the connection object back', () => { + const mockConnection = createMockConnection(); + const config = createConfig(mockConnection); + + expect(getPgConnectionConfig(config)).toEqual(mockConnection); + }); + + it('does not parse the connection string', () => { + const mockConnection = createMockConnection(); + const config = createConfig(mockConnection); + + expect(getPgConnectionConfig(config, true)).toEqual(mockConnection); + }); + + it('automatically parses the connection string', () => { + const mockConnection = createMockConnection(); + const mockConnectionString = createMockConnectionString(); + const config = createConfig(mockConnectionString); + + expect(getPgConnectionConfig(config)).toEqual({ + ...mockConnection, + port: '5432', + }); + }); + + it('parses the connection string', () => { + const mockConnection = createMockConnection(); + const mockConnectionString = createMockConnectionString(); + const config = createConfig(mockConnectionString); + + expect(getPgConnectionConfig(config, true)).toEqual({ + ...mockConnection, + port: '5432', + }); + }); + }); + + describe(createPgDatabaseClient, () => { it('creates a postgres knex instance', () => { expect( - createPgDatabase( + createPgDatabaseClient( createConfig({ - client: 'pg', - connection: { - host: 'acme', - user: 'foo', - password: 'bar', - database: 'foodb', - }, + host: 'acme', + user: 'foo', + password: 'bar', + database: 'foodb', }), ), ).toBeTruthy(); @@ -161,7 +179,7 @@ describe('postgres', () => { it('attempts to read an ssl cert', () => { expect(() => - createPgDatabase( + createPgDatabaseClient( createConfig( 'postgresql://postgres:pass@localhost:5432/dbname?sslrootcert=/path/to/file', ), diff --git a/packages/backend-common/src/database/postgres.ts b/packages/backend-common/src/database/postgres.ts index 09b6d0653b..03a74156c7 100644 --- a/packages/backend-common/src/database/postgres.ts +++ b/packages/backend-common/src/database/postgres.ts @@ -14,18 +14,18 @@ * limitations under the License. */ -import knex from 'knex'; -import { ConfigReader } from '@backstage/config'; +import knex, { PgConnectionConfig } from 'knex'; +import { Config } from '@backstage/config'; import { mergeDatabaseConfig } from './config'; /** - * Creates a knex sqlite3 database connection + * Creates a knex postgres database connection * * @param dbConfig The database config * @param overrides Additional options to merge with the config */ -export function createPgDatabase( - dbConfig: ConfigReader, +export function createPgDatabaseClient( + dbConfig: Config, overrides?: knex.Config, ) { const knexConfig = buildPgDatabaseConfig(dbConfig, overrides); @@ -40,26 +40,43 @@ export function createPgDatabase( * @param overrides Additional options to merge with the config */ export function buildPgDatabaseConfig( - dbConfig: ConfigReader, + dbConfig: Config, overrides?: knex.Config, ) { - const connection = dbConfig.get('connection') as any; - return mergeDatabaseConfig( dbConfig.get(), { - // Only parse the connection string when overrides are provided - connection: - overrides && - (typeof connection === 'string' || connection instanceof String) - ? parsePgConnectionString(connection as string) - : connection, + connection: getPgConnectionConfig(dbConfig, !!overrides), useNullAsDefault: true, }, overrides, ); } +/** + * Gets the postgres connection config + * + * @param dbConfig The database config + * @param parseConnectionString Flag to explictly control connection string parsing + */ +export function getPgConnectionConfig( + dbConfig: Config, + parseConnectionString?: boolean, +): PgConnectionConfig | string { + const connection = dbConfig.get('connection') as any; + const isConnectionString = + typeof connection === 'string' || connection instanceof String; + const autoParse = typeof parseConnectionString !== 'boolean'; + + const shouldParseConnectionString = autoParse + ? isConnectionString + : parseConnectionString && isConnectionString; + + return shouldParseConnectionString + ? parsePgConnectionString(connection as string) + : connection; +} + /** * Parses a connection string using pg-connection-string * diff --git a/packages/backend-common/src/database/sqlite3.test.ts b/packages/backend-common/src/database/sqlite3.test.ts index a25720b81c..dbff5ee354 100644 --- a/packages/backend-common/src/database/sqlite3.test.ts +++ b/packages/backend-common/src/database/sqlite3.test.ts @@ -15,7 +15,10 @@ */ import { ConfigReader } from '@backstage/config'; -import { buildSqlite3DatabaseConfig, createSqlite3Database } from './sqlite3'; +import { + buildSqliteDatabaseConfig, + createSqliteDatabaseClient, +} from './sqlite3'; describe('sqlite3', () => { const createConfig = (connection: any) => @@ -29,9 +32,9 @@ describe('sqlite3', () => { }, ]); - describe(buildSqlite3DatabaseConfig, () => { + describe(buildSqliteDatabaseConfig, () => { it('buidls a string connection', () => { - expect(buildSqlite3DatabaseConfig(createConfig(':memory:'))).toEqual({ + expect(buildSqliteDatabaseConfig(createConfig(':memory:'))).toEqual({ client: 'sqlite3', connection: ':memory:', useNullAsDefault: true, @@ -40,7 +43,7 @@ describe('sqlite3', () => { it('builds a filename connection', () => { expect( - buildSqlite3DatabaseConfig( + buildSqliteDatabaseConfig( createConfig({ filename: '/path/to/foo', }), @@ -56,7 +59,7 @@ describe('sqlite3', () => { it('replaces the connection with an override', () => { expect( - buildSqlite3DatabaseConfig(createConfig(':memory:'), { + buildSqliteDatabaseConfig(createConfig(':memory:'), { connection: { filename: '/path/to/foo' }, }), ).toEqual({ @@ -69,10 +72,10 @@ describe('sqlite3', () => { }); }); - describe(createSqlite3Database, () => { + describe(createSqliteDatabaseClient, () => { it('creates an in memory knex instance', () => { expect( - createSqlite3Database( + createSqliteDatabaseClient( createConfig({ client: 'sqlite3', connection: ':memory:', diff --git a/packages/backend-common/src/database/sqlite3.ts b/packages/backend-common/src/database/sqlite3.ts index 6249394d05..7bdc4380a1 100644 --- a/packages/backend-common/src/database/sqlite3.ts +++ b/packages/backend-common/src/database/sqlite3.ts @@ -15,7 +15,7 @@ */ import knex from 'knex'; -import { ConfigReader } from '@backstage/config'; +import { Config } from '@backstage/config'; import { mergeDatabaseConfig } from './config'; /** @@ -24,11 +24,11 @@ import { mergeDatabaseConfig } from './config'; * @param dbConfig The database config * @param overrides Additional options to merge with the config */ -export function createSqlite3Database( - dbConfig: ConfigReader, +export function createSqliteDatabaseClient( + dbConfig: Config, overrides?: knex.Config, ) { - const knexConfig = buildSqlite3DatabaseConfig(dbConfig, overrides); + const knexConfig = buildSqliteDatabaseConfig(dbConfig, overrides); const database = knex(knexConfig); database.client.pool.on('createSuccess', (_eventId: any, resource: any) => { @@ -44,8 +44,8 @@ export function createSqlite3Database( * @param dbConfig The database config * @param overrides Additional options to merge with the config */ -export function buildSqlite3DatabaseConfig( - dbConfig: ConfigReader, +export function buildSqliteDatabaseConfig( + dbConfig: Config, overrides?: knex.Config, ) { return mergeDatabaseConfig( diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 6df593182d..0423047cf7 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -23,7 +23,7 @@ */ import { - createDatabase, + createDatabaseClient, createServiceBuilder, loadBackendConfig, getRootLogger, @@ -48,11 +48,14 @@ function makeCreateEnv(loadedConfigs: AppConfig[]) { return (plugin: string): PluginEnvironment => { const logger = getRootLogger().child({ type: 'plugin', plugin }); - const database = createDatabase(config.getConfig('backend.database'), { - connection: { - database: `backstage_plugin_${plugin}`, + const database = createDatabaseClient( + config.getConfig('backend.database'), + { + connection: { + database: `backstage_plugin_${plugin}`, + }, }, - }); + ); return { logger, database, config }; }; } diff --git a/packages/create-app/templates/default-app/packages/backend/src/index.ts b/packages/create-app/templates/default-app/packages/backend/src/index.ts index d6fba2478a..6a014727e9 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/index.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/index.ts @@ -7,7 +7,7 @@ */ import { - createDatabase, + createDatabaseClient, createServiceBuilder, loadBackendConfig, getRootLogger, @@ -27,11 +27,14 @@ function makeCreateEnv(loadedConfigs: AppConfig[]) { return (plugin: string): PluginEnvironment => { const logger = getRootLogger().child({ type: 'plugin', plugin }); - const database = createDatabase(config.getConfig('backend.database'), { - connection: { - database: `backstage_plugin_${plugin}`, + const database = createDatabaseClient( + config.getConfig('backend.database'), + { + connection: { + database: `backstage_plugin_${plugin}`, + }, }, - }); + ); return { logger, database, config }; }; } From 6b1e63c2fc0a0f5b37a52b8edea8a41f4818e08c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20DOREAU?= Date: Sun, 23 Aug 2020 00:32:20 +0200 Subject: [PATCH 208/359] feat(create-app): add missing plugins --- .../default-app/packages/app/package.json.hbs | 6 +++ .../default-app/packages/app/src/apis.ts | 38 ++++++++++++++++++- .../default-app/packages/app/src/plugins.ts | 6 +++ .../default-app/packages/app/src/sidebar.tsx | 9 +++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs index 7d34a94c7b..704b04ceca 100644 --- a/packages/create-app/templates/default-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs @@ -11,6 +11,12 @@ "@backstage/plugin-register-component": "^{{version}}", "@backstage/plugin-scaffolder": "^{{version}}", "@backstage/plugin-techdocs": "^{{version}}", + "@backstage/plugin-circleci": "^{{version}}", + "@backstage/plugin-explore": "^{{version}}", + "@backstage/plugin-lighthouse": "^{{version}}", + "@backstage/plugin-tech-radar": "^{{version}}", + "@backstage/plugin-github-actions": "^{{version}}", + "@backstage/plugin-sentry": "^{{version}}", "@backstage/test-utils": "^{{version}}", "@backstage/theme": "^{{version}}", "history": "^5.0.0", diff --git a/packages/create-app/templates/default-app/packages/app/src/apis.ts b/packages/create-app/templates/default-app/packages/app/src/apis.ts index 7fc59cc9d7..02b5898884 100644 --- a/packages/create-app/templates/default-app/packages/app/src/apis.ts +++ b/packages/create-app/templates/default-app/packages/app/src/apis.ts @@ -14,15 +14,33 @@ import { WebStorage, } from '@backstage/core'; -import { catalogApiRef, CatalogClient } from '@backstage/plugin-catalog'; +import { + lighthouseApiRef, + LighthouseRestApi, +} from '@backstage/plugin-lighthouse'; -import { scaffolderApiRef, ScaffolderApi } from '@backstage/plugin-scaffolder'; +import { + GithubActionsClient, + githubActionsApiRef, +} from '@backstage/plugin-github-actions'; import { techdocsStorageApiRef, TechDocsStorageApi, + + techdocsStorageApiRef, + TechDocsStorageApi, } from '@backstage/plugin-techdocs'; +import { techRadarApiRef, TechRadar } from '@backstage/plugin-tech-radar'; + +import { catalogApiRef, CatalogClient } from '@backstage/plugin-catalog'; +import { CircleCIApi, circleCIApiRef } from '@backstage/plugin-circleci'; + +import { scaffolderApiRef, ScaffolderApi } from '@backstage/plugin-scaffolder'; + + + export const apis = (config: ConfigApi) => { // eslint-disable-next-line no-console console.log(`Creating APIs for ${config.getString('app.title')}`); @@ -46,9 +64,25 @@ export const apis = (config: ConfigApi) => { builder.add(oauthRequestApiRef, new OAuthRequestManager()); builder.add(catalogApiRef, new CatalogClient({ discoveryApi })); + builder.add(githubActionsApiRef, new GithubActionsClient()); + + builder.add(lighthouseApiRef, new LighthouseRestApi('http://localhost:3003')); + + builder.add( + circleCIApiRef, + new CircleCIApi(`${backendUrl}/proxy/circleci/api`), + ); builder.add(scaffolderApiRef, new ScaffolderApi({ discoveryApi })); + builder.add( + techRadarApiRef, + new TechRadar({ + width: 1500, + height: 800, + }), + ); + builder.add( techdocsStorageApiRef, new TechDocsStorageApi({ apiOrigin: techdocsStorageUrl }), diff --git a/packages/create-app/templates/default-app/packages/app/src/plugins.ts b/packages/create-app/templates/default-app/packages/app/src/plugins.ts index f6bef70188..0d54c55649 100644 --- a/packages/create-app/templates/default-app/packages/app/src/plugins.ts +++ b/packages/create-app/templates/default-app/packages/app/src/plugins.ts @@ -2,3 +2,9 @@ export { plugin as CatalogPlugin } from '@backstage/plugin-catalog'; export { plugin as RegisterComponent } from '@backstage/plugin-register-component'; export { plugin as ScaffolderPlugin } from '@backstage/plugin-scaffolder'; export { plugin as TechDocsPlugin } from '@backstage/plugin-techdocs'; +export { plugin as Explore } from '@backstage/plugin-explore'; +export { plugin as Circleci } from '@backstage/plugin-circleci'; +export { plugin as LighthousePlugin } from '@backstage/plugin-lighthouse'; +export { plugin as TechRadar } from '@backstage/plugin-tech-radar'; +export { plugin as GithubActions } from '@backstage/plugin-github-actions'; +export { plugin as Sentry } from '@backstage/plugin-sentry'; diff --git a/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx b/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx index 6520dfbe6e..28a43b2fb7 100644 --- a/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx @@ -2,6 +2,10 @@ import React from 'react'; import HomeIcon from '@material-ui/icons/Home'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; +import ExploreIcon from '@material-ui/icons/Explore'; +import BuildIcon from '@material-ui/icons/BuildRounded'; +import RuleIcon from '@material-ui/icons/AssignmentTurnedIn'; +import MapIcon from '@material-ui/icons/MyLocation'; import { Sidebar, SidebarItem, @@ -20,6 +24,11 @@ export const AppSidebar = () => ( + + + + + {/* End global nav */} From 1637987b2a88e1b1e3c47cf83d097ed1a82f7d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20DOREAU?= Date: Sun, 23 Aug 2020 13:28:27 +0200 Subject: [PATCH 209/359] feat(create-app): add tech-docs plugin --- .../templates/default-app/packages/app/package.json.hbs | 1 + .../templates/default-app/packages/app/src/sidebar.tsx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs index 704b04ceca..8ea7d44f76 100644 --- a/packages/create-app/templates/default-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs @@ -16,6 +16,7 @@ "@backstage/plugin-lighthouse": "^{{version}}", "@backstage/plugin-tech-radar": "^{{version}}", "@backstage/plugin-github-actions": "^{{version}}", + "@backstage/plugin-techdocs": "^{{version}}", "@backstage/plugin-sentry": "^{{version}}", "@backstage/test-utils": "^{{version}}", "@backstage/theme": "^{{version}}", diff --git a/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx b/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx index 28a43b2fb7..fa9a397751 100644 --- a/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx @@ -6,6 +6,7 @@ import ExploreIcon from '@material-ui/icons/Explore'; import BuildIcon from '@material-ui/icons/BuildRounded'; import RuleIcon from '@material-ui/icons/AssignmentTurnedIn'; import MapIcon from '@material-ui/icons/MyLocation'; + import { Sidebar, SidebarItem, @@ -22,9 +23,9 @@ export const AppSidebar = () => ( {/* Global nav, not org-specific */} + - From bb92d909ea70fde11f1cc72c91e1510fa91ac296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20DOREAU?= Date: Tue, 1 Sep 2020 08:19:10 +0200 Subject: [PATCH 210/359] feat(create-app): sentry backend + lighthouse config --- .../templates/default-app/app-config.yaml.hbs | 6 +++++ .../default-app/packages/backend/src/index.ts | 3 +++ .../packages/backend/src/plugins/sentry.ts | 22 +++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 packages/create-app/templates/default-app/packages/backend/src/plugins/sentry.ts diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index aff375ad29..33f7f5dc60 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -44,6 +44,12 @@ proxy: techdocs: storageUrl: http://localhost:7000/techdocs/static/docs +sentry: + organization: spotify + +lighthouse: + baseUrl: http://localhost:3003 + auth: providers: {} diff --git a/packages/create-app/templates/default-app/packages/backend/src/index.ts b/packages/create-app/templates/default-app/packages/backend/src/index.ts index 6a014727e9..6ba6c4dc8e 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/index.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/index.ts @@ -20,6 +20,7 @@ import identity from './plugins/identity'; import scaffolder from './plugins/scaffolder'; import proxy from './plugins/proxy'; import techdocs from './plugins/techdocs'; +import sentry from './plugins/sentry'; import { PluginEnvironment } from './types'; function makeCreateEnv(loadedConfigs: AppConfig[]) { @@ -50,11 +51,13 @@ async function main() { const identityEnv = useHotMemoize(module, () => createEnv('identity')); const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); + const sentryEnv = useHotMemoize(module, () => createEnv('sentry')); const service = createServiceBuilder(module) .loadConfig(configReader) .addRouter('/catalog', await catalog(catalogEnv)) .addRouter('/scaffolder', await scaffolder(scaffolderEnv)) + .addRouter('/sentry', await sentry(sentryEnv)) .addRouter('/auth', await auth(authEnv)) .addRouter('/identity', await identity(identityEnv)) .addRouter('/techdocs', await techdocs(techdocsEnv)) diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/sentry.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/sentry.ts new file mode 100644 index 0000000000..5cd0e55761 --- /dev/null +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/sentry.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { createRouter } from '@backstage/plugin-sentry-backend'; +import type { PluginEnvironment } from '../types'; + +export default async function createPlugin({ logger }: PluginEnvironment) { + return await createRouter(logger); +} From 055b8baa6de8c5c5208f6d016510934b823c068f Mon Sep 17 00:00:00 2001 From: Iain Billett Date: Tue, 1 Sep 2020 07:55:31 +0100 Subject: [PATCH 211/359] Add Pull Request Plugin to the marketplace (#2190) * Add Pull Request Plugin to the marketplace * Add Pull Request Plugin to the marketplace * typo --- microsite/data/plugins/github-pull-requests.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 microsite/data/plugins/github-pull-requests.yaml diff --git a/microsite/data/plugins/github-pull-requests.yaml b/microsite/data/plugins/github-pull-requests.yaml new file mode 100644 index 0000000000..917198a221 --- /dev/null +++ b/microsite/data/plugins/github-pull-requests.yaml @@ -0,0 +1,10 @@ +--- +title: GitHub Pull Requests +author: roadie.io +authorUrl: https://roadie.io/ +category: CI +description: View GitHub pull requests for your service in Backstage. +documentation: https://roadie.io/backstage/plugins/github-pull-requests +iconUrl: https://roadie.io/static/7f13bb8d861d8dedc5112fb939d215f9/351f2/GitHub-Mark-Light-120px-plus.png +npmPackageName: '@roadiehq/backstage-plugin-github-pull-requests' + From 94da78133f16b1a5a346be445426633e07492870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 1 Sep 2020 09:03:44 +0200 Subject: [PATCH 212/359] fix(deps): yarn.lock update again --- yarn.lock | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index d90056f47d..aa85370465 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2724,7 +2724,7 @@ dependencies: "@types/node" ">= 8" -"@open-draft/until@^1.0.3": +"@open-draft/until@^1.0.0", "@open-draft/until@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== @@ -4106,6 +4106,11 @@ dependencies: "@types/express" "*" +"@types/cookie@^0.3.3": + version "0.3.3" + resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" + integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== + "@types/cookie@^0.4.0": version "0.4.0" resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz#14f854c0f93d326e39da6e3b6f34f7d37513d108" @@ -11650,7 +11655,7 @@ graphql-upload@^8.0.2: http-errors "^1.7.3" object-path "^0.11.4" -graphql@15.3.0, graphql@^15.3.0: +graphql@15.3.0, graphql@^15.0.0, graphql@^15.3.0: version "15.3.0" resolved "https://registry.npmjs.org/graphql/-/graphql-15.3.0.tgz#3ad2b0caab0d110e3be4a5a9b2aa281e362b5278" integrity sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w== @@ -11858,7 +11863,7 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -headers-utils@^1.2.0: +headers-utils@^1.1.9, headers-utils@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/headers-utils/-/headers-utils-1.2.0.tgz#5e10d1bc9d2bccf789547afca5b991a3167241e8" integrity sha512-4/BMXcWrJErw7JpM87gF8MNEXcIMLzepYZjNRv/P9ctgupl2Ywa3u1PgHtNhSRq84bHH9Ndlkdy7bSi+bZ9I9A== @@ -15561,6 +15566,22 @@ ms@^2.0.0, ms@^2.1.1: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +msw@^0.19.5: + version "0.19.5" + resolved "https://registry.npmjs.org/msw/-/msw-0.19.5.tgz#7d2a1a852ccf1644d3db6735d69fff6777aac33f" + integrity sha512-J5eQ++gDVZoHPC8gVXtWcakLjgmPipvFj/sEnlRV/WViXuiq2CamSqO3Wbh6H8bAmj+k2vUWCfcVT1HjMdKB2Q== + dependencies: + "@open-draft/until" "^1.0.0" + "@types/cookie" "^0.3.3" + chalk "^4.0.0" + cookie "^0.4.1" + graphql "^15.0.0" + headers-utils "^1.1.9" + node-match-path "^0.4.2" + node-request-interceptor "^0.2.5" + statuses "^2.0.0" + yargs "^15.3.1" + msw@^0.20.5: version "0.20.5" resolved "https://registry.npmjs.org/msw/-/msw-0.20.5.tgz#b6141080c0d8b17c451d9ca36c28cc47b4ac487a" @@ -15807,7 +15828,7 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-match-path@^0.4.4: +node-match-path@^0.4.2, node-match-path@^0.4.4: version "0.4.4" resolved "https://registry.npmjs.org/node-match-path/-/node-match-path-0.4.4.tgz#516a10926093c0cc6f237d020685b593b19baebb" integrity sha512-pBq9gp7TG0r0VXuy/oeZmQsjBSnYQo7G886Ly/B3azRwZuEtHCY155dzmfoKWcDPGgyfIGD8WKVC7h3+6y7yTg== @@ -15866,6 +15887,14 @@ node-releases@^1.1.29, node-releases@^1.1.52, node-releases@^1.1.58: resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== +node-request-interceptor@^0.2.5: + version "0.2.6" + resolved "https://registry.npmjs.org/node-request-interceptor/-/node-request-interceptor-0.2.6.tgz#541278d7033bb6a8befb5dd793f83428cf6446a2" + integrity sha512-aJW1tPSM7nzuZFRe+C/KSz22GJO3CVFMxHHmMGX8Z+tjP7TCIVbzeckLFVfJG68BdVgrdOOP7Ejc57ag820eyA== + dependencies: + debug "^4.1.1" + headers-utils "^1.2.0" + node-request-interceptor@^0.3.5: version "0.3.5" resolved "https://registry.npmjs.org/node-request-interceptor/-/node-request-interceptor-0.3.5.tgz#4b26159617829c9a70643012c0fdc3ae4c78ae43" From 0627c246373f9073883a6808817e262e98fbaad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Tue, 1 Sep 2020 09:26:21 +0200 Subject: [PATCH 213/359] Change templates to use Home theme (#2202) * Change templates to use Home theme * Update docs URL --- .../src/components/ScaffolderPage/ScaffolderPage.tsx | 4 ++-- .../scaffolder/src/components/TemplatePage/TemplatePage.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index 12b3e303dd..6f8bf0d550 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -64,7 +64,7 @@ export const ScaffolderPage: React.FC<{}> = () => { }, [error, errorApi]); return ( - +
= () => { Shoot! Looks like you don't have any templates. Check out the documentation{' '} - + here! diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index 05cc249e41..3c22e8d50c 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -136,7 +136,7 @@ export const TemplatePage = () => { } return ( - +
Date: Tue, 1 Sep 2020 09:44:22 +0200 Subject: [PATCH 214/359] chore: remove gitignored files --- microsite/i18n/en.json | 328 ----------------------------------------- 1 file changed, 328 deletions(-) delete mode 100644 microsite/i18n/en.json diff --git a/microsite/i18n/en.json b/microsite/i18n/en.json deleted file mode 100644 index e9f63d5737..0000000000 --- a/microsite/i18n/en.json +++ /dev/null @@ -1,328 +0,0 @@ -{ - "_comment": "This file is auto-generated by write-translations.js", - "localized-strings": { - "next": "Next", - "previous": "Previous", - "tagline": "An open platform for building developer portals", - "docs": { - "api/backend": { - "title": "Backend" - }, - "api/utility-apis": { - "title": "Utility APIs" - }, - "architecture-decisions/adrs-adr001": { - "title": "ADR001: Architecture Decision Record (ADR) log", - "sidebar_label": "ADR001" - }, - "architecture-decisions/adrs-adr002": { - "title": "ADR002: Default Software Catalog File Format", - "sidebar_label": "ADR002" - }, - "architecture-decisions/adrs-adr003": { - "title": "ADR003: Avoid Default Exports and Prefer Named Exports", - "sidebar_label": "ADR003" - }, - "architecture-decisions/adrs-adr004": { - "title": "ADR004: Module Export Structure", - "sidebar_label": "ADR004" - }, - "architecture-decisions/adrs-adr005": { - "title": "ADR005: Catalog Core Entities", - "sidebar_label": "ADR005" - }, - "architecture-decisions/adrs-adr006": { - "title": "ADR006: Avoid React.FC and React.SFC", - "sidebar_label": "ADR006" - }, - "architecture-decisions/adrs-adr007": { - "title": "ADR007: Use MSW to mock http requests", - "sidebar_label": "ADR007" - }, - "architecture-decisions/adrs-adr008": { - "title": "ADR008: Default Catalog File Name", - "sidebar_label": "ADR008" - }, - "architecture-decisions/adrs-overview": { - "title": "Architecture Decision Records (ADR)", - "sidebar_label": "Overview" - }, - "auth/add-auth-provider": { - "title": "Adding authentication providers" - }, - "auth/auth-backend-classes": { - "title": "Auth backend classes" - }, - "auth/auth-backend": { - "title": "Auth backend" - }, - "auth/glossary": { - "title": "Glossary" - }, - "auth/index": { - "title": "User Authentication and Authorization in Backstage" - }, - "auth/oauth": { - "title": "OAuth and OpenID Connect" - }, - "conf/defining": { - "title": "Defining Configuration for your Plugin" - }, - "conf/index": { - "title": "Static Configuration in Backstage" - }, - "conf/reading": { - "title": "Reading Backstage Configuration" - }, - "conf/writing": { - "title": "Writing Backstage Configuration Files" - }, - "dls/contributing-to-storybook": { - "title": "Contributing to Storybook" - }, - "dls/design": { - "title": "Design" - }, - "dls/figma": { - "title": "Figma" - }, - "FAQ": { - "title": "FAQ" - }, - "features/software-catalog/software-catalog-api": { - "title": "API" - }, - "features/software-catalog/software-catalog-configuration": { - "title": "Catalog Configuration" - }, - "features/software-catalog/descriptor-format": { - "title": "Descriptor Format of Catalog Entities", - "sidebar_label": "YAML File Format" - }, - "features/software-catalog/extending-the-model": { - "title": "Extending the model" - }, - "features/software-catalog/external-integrations": { - "title": "External integrations" - }, - "features/software-catalog/software-catalog-overview": { - "title": "Backstage Service Catalog (alpha)", - "sidebar_label": "Backstage Service Catalog" - }, - "features/software-catalog/installation": { - "title": "features/software-catalog/installation" - }, - "features/software-catalog/system-model": { - "title": "System Model" - }, - "features/software-templates/adding-templates": { - "title": "Adding your own Templates" - }, - "features/software-templates/extending/extending-preparer": { - "title": "Create your own Preparer" - }, - "features/software-templates/extending/extending-publisher": { - "title": "Create your own Publisher" - }, - "features/software-templates/extending/extending-templater": { - "title": "Creating your own Templater" - }, - "features/software-templates/extending/extending-index": { - "title": "Extending the Scaffolder" - }, - "features/software-templates/software-templates-index": { - "title": "Software Templates" - }, - "features/software-templates/installation": { - "title": "features/software-templates/installation" - }, - "features/techdocs/concepts": { - "title": "Concepts" - }, - "features/techdocs/creating-and-publishing": { - "title": "Creating and publishing your docs", - "sidebar_label": "Creating and Publishing Documentation" - }, - "features/techdocs/faqs": { - "title": "TechDocs FAQ", - "sidebar_label": "FAQ" - }, - "features/techdocs/getting-started": { - "title": "Getting Started" - }, - "features/techdocs/techdocs-overview": { - "title": "TechDocs Documentation", - "sidebar_label": "Overview" - }, - "getting-started/app-custom-theme": { - "title": "Customize the look-and-feel of your App" - }, - "getting-started/configure-app-with-plugins": { - "title": "Configuring App with plugins" - }, - "getting-started/create-an-app": { - "title": "Create an App" - }, - "getting-started/deployment-k8s": { - "title": "Kubernetes" - }, - "getting-started/deployment-other": { - "title": "Other" - }, - "getting-started/development-environment": { - "title": "Development Environment" - }, - "getting-started/index": { - "title": "Running Backstage Locally" - }, - "getting-started/installation": { - "title": "Installation" - }, - "overview/adopting": { - "title": "Strategies for adopting" - }, - "overview/architecture-overview": { - "title": "Architecture overview" - }, - "overview/architecture-terminology": { - "title": "Architecture terminology" - }, - "overview/background": { - "title": "The Spotify Story" - }, - "overview/roadmap": { - "title": "Project roadmap" - }, - "overview/support": { - "title": "Support and community" - }, - "overview/vision": { - "title": "Vision" - }, - "overview/what-is-backstage": { - "title": "What is Backstage?" - }, - "plugins/add-to-marketplace": { - "title": "Add to Marketplace" - }, - "plugins/backend-plugin": { - "title": "Backend plugin" - }, - "plugins/call-existing-api": { - "title": "Call Existing API" - }, - "plugins/create-a-plugin": { - "title": "Create a Backstage Plugin" - }, - "plugins/existing-plugins": { - "title": "Existing plugins" - }, - "plugins/index": { - "title": "Intro to plugins" - }, - "plugins/plugin-development": { - "title": "Plugin Development" - }, - "plugins/proxying": { - "title": "Proxying" - }, - "plugins/publish-private": { - "title": "Publish private" - }, - "plugins/publishing": { - "title": "Publishing" - }, - "plugins/structure-of-a-plugin": { - "title": "Structure of a Plugin" - }, - "plugins/testing": { - "title": "Testing with Jest" - }, - "README": { - "title": "README" - }, - "reference/createPlugin-feature-flags": { - "title": "createPlugin - feature flags" - }, - "reference/createPlugin-router": { - "title": "createPlugin - router" - }, - "reference/createPlugin": { - "title": "createPlugin" - }, - "reference/utility-apis/AlertApi": { - "title": "reference/utility-apis/AlertApi" - }, - "reference/utility-apis/AppThemeApi": { - "title": "reference/utility-apis/AppThemeApi" - }, - "reference/utility-apis/BackstageIdentityApi": { - "title": "reference/utility-apis/BackstageIdentityApi" - }, - "reference/utility-apis/Config": { - "title": "reference/utility-apis/Config" - }, - "reference/utility-apis/ErrorApi": { - "title": "reference/utility-apis/ErrorApi" - }, - "reference/utility-apis/FeatureFlagsApi": { - "title": "reference/utility-apis/FeatureFlagsApi" - }, - "reference/utility-apis/IdentityApi": { - "title": "reference/utility-apis/IdentityApi" - }, - "reference/utility-apis/OAuthApi": { - "title": "reference/utility-apis/OAuthApi" - }, - "reference/utility-apis/OAuthRequestApi": { - "title": "reference/utility-apis/OAuthRequestApi" - }, - "reference/utility-apis/OpenIdConnectApi": { - "title": "reference/utility-apis/OpenIdConnectApi" - }, - "reference/utility-apis/ProfileInfoApi": { - "title": "reference/utility-apis/ProfileInfoApi" - }, - "reference/utility-apis/README": { - "title": "Utility API References" - }, - "reference/utility-apis/SessionStateApi": { - "title": "reference/utility-apis/SessionStateApi" - }, - "reference/utility-apis/StorageApi": { - "title": "reference/utility-apis/StorageApi" - }, - "tutorials/journey": { - "title": "Future developer journey" - } - }, - "links": { - "GitHub": "GitHub", - "Docs": "Docs", - "Plugins": "Plugins", - "Blog": "Blog", - "Demos": "Demos", - "Newsletter": "Newsletter" - }, - "categories": { - "Overview": "Overview", - "Getting Started": "Getting Started", - "Features": "Features", - "Plugins": "Plugins", - "Configuration": "Configuration", - "Auth and identity": "Auth and identity", - "Designing for Backstage": "Designing for Backstage", - "API references": "API references", - "Tutorials": "Tutorials", - "Architecture Decision Records (ADRs)": "Architecture Decision Records (ADRs)", - "Contribute": "Contribute", - "Support": "Support", - "FAQ": "FAQ" - } - }, - "pages-strings": { - "Help Translate|recruit community translators for your project": "Help Translate", - "Edit this Doc|recruitment message asking to edit the doc source": "Edit", - "Translate this Doc|recruitment message asking to translate the docs": "Translate" - } -} From bcbeb0b657945b55965260c67524621c17fb823e Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Tue, 1 Sep 2020 09:46:46 +0200 Subject: [PATCH 215/359] Add api-docs to marketplace (#2204) * docs: improve documentation about adding plugins to the marketplace The relative path is misleading as one has to come from root. * docs: add api-docs plugin to marketplace --- docs/plugins/add-to-marketplace.md | 4 +++- microsite/data/plugins/api-docs.yaml | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 microsite/data/plugins/api-docs.yaml diff --git a/docs/plugins/add-to-marketplace.md b/docs/plugins/add-to-marketplace.md index 23cfecd5d0..d85807a226 100644 --- a/docs/plugins/add-to-marketplace.md +++ b/docs/plugins/add-to-marketplace.md @@ -6,7 +6,9 @@ title: Add to Marketplace ## Adding a Plugin to the Marketplace To add a new plugin to the [plugin marketplace](https://backstage.io/plugins) -create a file in `data/plugins` with your plugin's information. Example: +create a file in +[`microsite/data/plugins`](https://github.com/spotify/backstage/tree/master/microsite/data/plugins) +with your plugin's information. Example: ```yaml --- diff --git a/microsite/data/plugins/api-docs.yaml b/microsite/data/plugins/api-docs.yaml new file mode 100644 index 0000000000..b7f85b62b3 --- /dev/null +++ b/microsite/data/plugins/api-docs.yaml @@ -0,0 +1,9 @@ +--- +title: API Docs +author: SDA SE +authorUrl: https://sda.se/ +category: Discovery +description: Components to discover and display API entities as an extension to the catalog plugin. +documentation: https://github.com/spotify/backstage/blob/master/plugins/api-docs/README.md +iconUrl: https://github.com/spotify/backstage/raw/master/plugins/api-docs/docs/entity_tab_api.png +npmPackageName: '@backstage/plugin-api-docs' From b9ed0d3088a6c3634d16a08d7bce4c992b4477dd Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Tue, 1 Sep 2020 09:51:00 +0200 Subject: [PATCH 216/359] feat(catalog): change the tags in the entity list to be gray (#2205) --- .../catalog/src/components/CatalogTable/CatalogTable.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index c68eb10e66..cd4df13775 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -75,12 +75,7 @@ const columns: TableColumn[] = [ <> {entity.metadata.tags && entity.metadata.tags.map(t => ( - + ))} ), From ff5da01280cc79a7792b58a18de5bd604db431e1 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Tue, 1 Sep 2020 09:54:05 +0200 Subject: [PATCH 217/359] TechDocs: techdocs backend tests (#2196) * add tests for generators class * add tests for helpers * renaming * fixup --- .../stages/generate/generators.test.ts | 47 ++++++++ .../techdocs/stages/generate/generators.ts | 53 +++++---- .../techdocs/stages/generate/helpers.test.ts | 103 ++++++++++++++++++ 3 files changed, 175 insertions(+), 28 deletions(-) create mode 100644 plugins/techdocs-backend/src/techdocs/stages/generate/generators.test.ts create mode 100644 plugins/techdocs-backend/src/techdocs/stages/generate/helpers.test.ts diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/generators.test.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/generators.test.ts new file mode 100644 index 0000000000..b339aef9f8 --- /dev/null +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/generators.test.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { Generators, TechdocsGenerator } from './'; +import { getVoidLogger } from '@backstage/backend-common'; + +const logger = getVoidLogger(); + +const mockEntity = { + apiVersion: 'version', + kind: 'TestKind', + metadata: { + name: 'testName', + }, +}; + +describe('generators', () => { + it('should return error if no generator is registered', async () => { + const generators = new Generators(); + + expect(() => generators.get(mockEntity)).toThrowError( + 'No generator registered for entity: "techdocs"', + ); + }); + + it('should return correct registered generator', async () => { + const generators = new Generators(); + const techdocs = new TechdocsGenerator(logger); + + generators.register('techdocs', techdocs); + + expect(generators.get(mockEntity)).toBe(techdocs); + }); +}); diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/generators.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/generators.ts index da9d3c418b..f28a169942 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/generate/generators.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/generators.ts @@ -15,32 +15,29 @@ */ import { - GeneratorBase, - SupportedGeneratorKey, - GeneratorBuilder, - } from './types'; - - import { Entity } from '@backstage/catalog-model'; - import { getGeneratorKey } from './helpers'; - - export class Generators implements GeneratorBuilder { - private generatorMap = new Map(); - - register(templaterKey: SupportedGeneratorKey, templater: GeneratorBase) { - this.generatorMap.set(templaterKey, templater); - } - - get(entity: Entity): GeneratorBase { - const generatorKey = getGeneratorKey(entity); - const generator = this.generatorMap.get(generatorKey); - - if (!generator) { - throw new Error( - `No generator registered for entity: "${generatorKey}"`, - ); - } - - return generator; - } + GeneratorBase, + SupportedGeneratorKey, + GeneratorBuilder, +} from './types'; + +import { Entity } from '@backstage/catalog-model'; +import { getGeneratorKey } from './helpers'; + +export class Generators implements GeneratorBuilder { + private generatorMap = new Map(); + + register(generatorKey: SupportedGeneratorKey, generator: GeneratorBase) { + this.generatorMap.set(generatorKey, generator); } - \ No newline at end of file + + get(entity: Entity): GeneratorBase { + const generatorKey = getGeneratorKey(entity); + const generator = this.generatorMap.get(generatorKey); + + if (!generator) { + throw new Error(`No generator registered for entity: "${generatorKey}"`); + } + + return generator; + } +} diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.test.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.test.ts new file mode 100644 index 0000000000..799e79ef67 --- /dev/null +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.test.ts @@ -0,0 +1,103 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 Stream, { PassThrough } from 'stream'; +import os from 'os'; +import Docker from 'dockerode'; +import { runDockerContainer, getGeneratorKey } from './helpers'; + +const mockEntity = { + apiVersion: 'version', + kind: 'TestKind', + metadata: { + name: 'testName', + }, +}; + +const mockDocker = new Docker() as jest.Mocked; + +describe('helpers', () => { + describe('getGeneratorKey', () => { + it('should return techdocs as the only generator key', () => { + const key = getGeneratorKey(mockEntity); + expect(key).toBe('techdocs'); + }); + }); + + describe('runDockerContainer', () => { + beforeEach(() => { + jest.spyOn(mockDocker, 'pull').mockImplementation((async ( + _image: string, + _something: any, + handler: (err: Error | undefined, stream: PassThrough) => void, + ) => { + const mockStream = new PassThrough(); + handler(undefined, mockStream); + mockStream.end(); + }) as any); + + jest + .spyOn(mockDocker, 'run') + .mockResolvedValue([{ Error: null, StatusCode: 0 }]); + }); + + const imageName = 'spotify/techdocs'; + const args = ['build', '-d', '/result']; + const docsDir = os.tmpdir(); + const resultDir = os.tmpdir(); + + it('should pull the techdocs docker container', async () => { + await runDockerContainer({ + imageName, + args, + docsDir, + resultDir, + dockerClient: mockDocker, + }); + + expect(mockDocker.pull).toHaveBeenCalledWith( + imageName, + {}, + expect.any(Function), + ); + }); + + it('should run the techdocs docker container', async () => { + await runDockerContainer({ + imageName, + args, + docsDir, + resultDir, + dockerClient: mockDocker, + }); + + expect(mockDocker.run).toHaveBeenCalledWith( + imageName, + args, + expect.any(Stream), + { + Volumes: { + '/content': {}, + '/result': {}, + }, + WorkingDir: '/content', + HostConfig: { + Binds: [`${docsDir}:/content`, `${resultDir}:/result`], + }, + }, + ); + }); + }); +}); From f7ac665b75ef63951a60be700dbede728806d6aa Mon Sep 17 00:00:00 2001 From: nikek Date: Tue, 1 Sep 2020 10:26:16 +0200 Subject: [PATCH 218/359] Add semantic color to AboutCard field titles --- plugins/catalog/src/components/AboutCard/AboutCard.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx index d27175fcf3..d83beaedb5 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -35,17 +35,18 @@ import DocsIcon from '@material-ui/icons/Description'; const useStyles = makeStyles(theme => ({ links: { - margin: theme.spacing(2, 0, 2), + margin: theme.spacing(2, 0), display: 'grid', gridAutoFlow: 'column', gridAutoColumns: 'min-content', - gridGap: theme.spacing(2), + gridGap: theme.spacing(3), }, label: { - color: '#9e9e9e', + color: theme.palette.text.secondary, textTransform: 'uppercase', - fontSize: '12px', + fontSize: '10px', fontWeight: 'bold', + letterSpacing: 0.5, overflow: 'hidden', whiteSpace: 'nowrap', }, From a549ad53cea7f0b1ee9c3e28b4741e50ee8adcef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20DOREAU?= Date: Tue, 1 Sep 2020 10:37:04 +0200 Subject: [PATCH 219/359] fix(create-app): remove duplicate import --- .../create-app/templates/default-app/packages/app/src/apis.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/create-app/templates/default-app/packages/app/src/apis.ts b/packages/create-app/templates/default-app/packages/app/src/apis.ts index 02b5898884..14351eaba7 100644 --- a/packages/create-app/templates/default-app/packages/app/src/apis.ts +++ b/packages/create-app/templates/default-app/packages/app/src/apis.ts @@ -27,9 +27,6 @@ import { import { techdocsStorageApiRef, TechDocsStorageApi, - - techdocsStorageApiRef, - TechDocsStorageApi, } from '@backstage/plugin-techdocs'; import { techRadarApiRef, TechRadar } from '@backstage/plugin-tech-radar'; From 81cb294dacbf23bc863afb3b287a24ff9a68d61b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 10:36:21 +0200 Subject: [PATCH 220/359] config,docs: add templates as allowed kind and document process --- app-config.yaml | 2 ++ .../software-catalog/configuration.md | 4 ++-- .../software-templates/adding-templates.md | 22 +++++++++++++++++++ .../templates/default-app/app-config.yaml.hbs | 5 +++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 3b3733cf54..ba556873aa 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -48,6 +48,8 @@ lighthouse: baseUrl: http://localhost:3003 catalog: + rules: + allow: [Component, API, Group, Template] processors: githubApi: privateToken: diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md index d4cc7fc0c2..acfaf115de 100644 --- a/docs/features/software-catalog/configuration.md +++ b/docs/features/software-catalog/configuration.md @@ -32,7 +32,7 @@ For example, given the following configuration: ```yaml catalog: rules: - - allow: [Component, API, System] + - allow: [Component, API, Template] locations: - type: github @@ -40,7 +40,7 @@ catalog: allow: [Group] ``` -We are able to add entities of kind `Component`, `API`, or `System` from any +We are able to add entities of kind `Component`, `API`, or `Template` from any location, and `Group` entities from the `org-data.yaml`, which will also be read as statically configured location. diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index 67467bd98e..16419b4dfe 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -58,6 +58,28 @@ Currently the catalog supports loading definitions from GitHub + Local Files. To load from other places, not only will there need to be another preparer, but the support to load the location will also need to be added to the Catalog. +You can add the template files to the catalog through +[static location configuration](../software-catalog/configuration.md#static-location-configuration), +for example + +```yaml +catalog: + locations: + - type: github + target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml + allow: [Template] +``` + +Templates can also be added by posting the to the catalog directly. Note that if +you're doing this, you need to configure the catalog to allow template entities +to be ingested from any source, for example: + +```yaml +catalog: + rules: + allow: [Component, API, Template] +``` + For loading from a file, the following command should work when the backend is running: diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index aff375ad29..e194295df3 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -69,11 +69,16 @@ catalog: # Backstage example templates - type: github target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml + allow: [Template] - type: github target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml + allow: [Template] - type: github target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml + allow: [Template] - type: github target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml + allow: [Template] - type: github target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml + allow: [Template] From a85ca3123c8a00b590be214cb3f992965d2bf598 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 1 Sep 2020 10:46:52 +0200 Subject: [PATCH 221/359] Add non-legacy routes when in isolation --- packages/dev-utils/src/devApp/render.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index 0e28a5bf8b..410f7592a5 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -199,8 +199,17 @@ class DevAppBuilder { for (const plugin of plugins) { for (const output of plugin.output()) { - if (output.type === 'legacy-route') { - paths.push(output.path); + switch (output.type) { + case 'legacy-route': { + paths.push(output.path); + break; + } + case 'route': { + paths.push(output.target.path); + break; + } + default: + break; } } } From 042f2cbb44faeb2a92ad43841a84776681947c49 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 1 Sep 2020 10:53:55 +0200 Subject: [PATCH 222/359] Fix formatting in plugin template --- .../cli/templates/default-plugin/src/plugin.ts.hbs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cli/templates/default-plugin/src/plugin.ts.hbs b/packages/cli/templates/default-plugin/src/plugin.ts.hbs index 6a410a6a28..1bf4d07cdb 100644 --- a/packages/cli/templates/default-plugin/src/plugin.ts.hbs +++ b/packages/cli/templates/default-plugin/src/plugin.ts.hbs @@ -2,13 +2,13 @@ import { createPlugin, createRouteRef } from '@backstage/core'; import ExampleComponent from './components/ExampleComponent'; export const rootRouteRef = createRouteRef({ -path: '/{{ id }}', -title: '{{ id }}', + path: '/{{ id }}', + title: '{{ id }}', }); export const plugin = createPlugin({ -id: '{{ id }}', -register({ router }) { -router.addRoute(rootRouteRef, ExampleComponent); -}, + id: '{{ id }}', + register({ router }) { + router.addRoute(rootRouteRef, ExampleComponent); + }, }); From a62d0af2c8c8374eb1a2f809eb4824aba87d2761 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 11:12:43 +0200 Subject: [PATCH 223/359] chore(eslint): adjust no-unused-vars rule --- packages/cli/config/eslint.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/cli/config/eslint.js b/packages/cli/config/eslint.js index 2416a250e1..792cd467d5 100644 --- a/packages/cli/config/eslint.js +++ b/packages/cli/config/eslint.js @@ -56,7 +56,12 @@ module.exports = { '@typescript-eslint/no-unused-expressions': 'error', '@typescript-eslint/no-unused-vars': [ 'warn', - { vars: 'all', args: 'after-used', ignoreRestSiblings: true }, + { + vars: 'all', + args: 'after-used', + ignoreRestSiblings: true, + argsIgnorePattern: '^_', + }, ], 'no-restricted-imports': [ 2, From 542ee253df7432e14b861a214399a730a53d8d87 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 11:13:24 +0200 Subject: [PATCH 224/359] fix: update create-app to match example-app --- .../default-app/packages/app/src/App.tsx | 2 +- .../packages/app/src/components/AppRoutes.tsx | 14 ++++ .../app/src/components/catalog/EntityPage.tsx | 79 +++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 packages/create-app/templates/default-app/packages/app/src/components/AppRoutes.tsx create mode 100644 packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index c96bd9d05e..9b431ca441 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -8,6 +8,7 @@ import { import { apis } from './apis'; import * as plugins from './plugins'; import { AppSidebar } from './sidebar'; +import { AppRoutes } from './components/AppRoutes'; const app = createApp({ apis, @@ -16,7 +17,6 @@ const app = createApp({ const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); -const AppRoutes = app.getRoutes(); const App: FC<{}> = () => ( diff --git a/packages/create-app/templates/default-app/packages/app/src/components/AppRoutes.tsx b/packages/create-app/templates/default-app/packages/app/src/components/AppRoutes.tsx new file mode 100644 index 0000000000..b689052a4a --- /dev/null +++ b/packages/create-app/templates/default-app/packages/app/src/components/AppRoutes.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { Routes, Route, Navigate } from 'react-router'; +import { CatalogRouter } from '@backstage/plugin-catalog'; +import { EntityPage } from './catalog/EntityPage'; + +export const AppRoutes = () => ( + + } + /> + } /> + +); diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx new file mode 100644 index 0000000000..1c7a08e362 --- /dev/null +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { Router as GitHubActionsRouter } from '@backstage/plugin-github-actions'; +import React from 'react'; +import { + EntityPageLayout, + useEntity, + EntityMetadataCard, +} from '@backstage/plugin-catalog'; +import { Entity } from '@backstage/catalog-model'; + +const OverviewPage = ({ entity }: { entity: Entity }) => ( + +); + +const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + } + /> + +); + +const WebsiteEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + } + /> + +); + +const DefaultEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + +); + +export const EntityPage = () => { + const { entity } = useEntity(); + switch (entity?.spec?.type) { + case 'service': + return ; + case 'website': + return ; + default: + return ; + } +}; From 7dc5425119af6bdc1246c6cf161ea55a246398c7 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 11:22:27 +0200 Subject: [PATCH 225/359] fix: typedep --- plugins/catalog/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 0a8b01b63f..1c830d74f6 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -52,6 +52,7 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", + "@types/react": "^16.9", "jest-fetch-mock": "^3.0.3", "msw": "^0.20.5", "react-test-renderer": "^16.13.1", From 6e8f4ca44a0c1ee429198a05f420867774c19172 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 11:30:59 +0200 Subject: [PATCH 226/359] catalog-backend: wrap location-specific rules in an additional rules field --- .../software-catalog/configuration.md | 3 +- .../software-templates/adding-templates.md | 3 +- .../templates/default-app/app-config.yaml.hbs | 15 ++++++---- .../src/ingestion/CatalogRules.test.ts | 6 +++- .../src/ingestion/CatalogRules.ts | 29 +++++++++---------- 5 files changed, 32 insertions(+), 24 deletions(-) diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md index acfaf115de..44e998d5a9 100644 --- a/docs/features/software-catalog/configuration.md +++ b/docs/features/software-catalog/configuration.md @@ -37,7 +37,8 @@ catalog: locations: - type: github target: https://github.com/org/example/blob/master/org-data.yaml - allow: [Group] + rules: + - allow: [Group] ``` We are able to add entities of kind `Component`, `API`, or `Template` from any diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index 16419b4dfe..6e6e22dd9a 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -67,7 +67,8 @@ catalog: locations: - type: github target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml - allow: [Template] + rules: + - allow: [Template] ``` Templates can also be added by posting the to the catalog directly. Note that if diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index e194295df3..16729e00e4 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -69,16 +69,21 @@ catalog: # Backstage example templates - type: github target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml - allow: [Template] + rules: + - allow: [Template] - type: github target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml - allow: [Template] + rules: + - allow: [Template] - type: github target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml - allow: [Template] + rules: + - allow: [Template] - type: github target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml - allow: [Template] + rules: + - allow: [Template] - type: github target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml - allow: [Template] + rules: + - allow: [Template] diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts index 34484f6b1e..794416786c 100644 --- a/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts @@ -167,7 +167,11 @@ describe('CatalogRulesEnforcer', () => { { type: 'github', target: 'https://github.com/a/b/blob/master/x.yaml', - allow: ['Group'], + rules: [ + { + allow: ['Group'], + }, + ], }, ], }, diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.ts index eda93b552f..2b55ebef32 100644 --- a/plugins/catalog-backend/src/ingestion/CatalogRules.ts +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.ts @@ -64,7 +64,7 @@ export class CatalogRulesEnforcer { * If there is no matching rule to allow an ingested entity, it will be rejected by the catalog. * * It also reads in rules from `catalog.locations`, where each location can have a list - * of allowed entity for the location, specified in an `allow` field. + * of rules for that specific location, specified in a `rules` field. * * For example: * @@ -76,10 +76,12 @@ export class CatalogRulesEnforcer { * locations: * - type: github * target: https://github.com/org/repo/blob/master/users.yaml - * allow: [User, Group] + * rules: + * - allow: [User, Group] * - type: github * target: https://github.com/org/repo/blob/master/systems.yaml - * allow: [System] + * rules: + * - allow: [System] * ``` */ static fromConfig(config: Config) { @@ -97,22 +99,17 @@ export class CatalogRulesEnforcer { if (config.has('catalog.locations')) { const locationRules = config .getConfigArray('catalog.locations') - .flatMap(sub => { - if (!sub.has('allow')) { + .flatMap(locConf => { + if (!locConf.has('rules')) { return []; } + const type = locConf.getString('type'); + const target = locConf.getString('target'); - return [ - { - allow: sub.getStringArray('allow').map(kind => ({ kind })), - locations: [ - { - type: sub.getString('type'), - target: sub.getString('target'), - }, - ], - }, - ]; + return locConf.getConfigArray('rules').map(ruleConf => ({ + allow: ruleConf.getStringArray('allow').map(kind => ({ kind })), + locations: [{ type, target }], + })); }); rules.push(...locationRules); From 7b098885c51d6cdf2040079f323592c598338414 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 11:41:33 +0200 Subject: [PATCH 227/359] fix: AboutCard --- packages/app/src/components/catalog/EntityPage.tsx | 4 ++-- .../packages/app/src/components/catalog/EntityPage.tsx | 4 ++-- plugins/catalog/src/index.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 1c7a08e362..b56268036f 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -18,12 +18,12 @@ import React from 'react'; import { EntityPageLayout, useEntity, - EntityMetadataCard, + AboutCard, } from '@backstage/plugin-catalog'; import { Entity } from '@backstage/catalog-model'; const OverviewPage = ({ entity }: { entity: Entity }) => ( - + ); const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx index 1c7a08e362..b56268036f 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -18,12 +18,12 @@ import React from 'react'; import { EntityPageLayout, useEntity, - EntityMetadataCard, + AboutCard, } from '@backstage/plugin-catalog'; import { Entity } from '@backstage/catalog-model'; const OverviewPage = ({ entity }: { entity: Entity }) => ( - + ); const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index d0e9778a00..26285b4814 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -21,5 +21,5 @@ export * from './routes'; export { useEntityCompoundName } from './components/useEntityCompoundName'; export * from './Router'; export { useEntity } from './hooks/useEntity'; -export { EntityMetadataCard } from './components/EntityMetadataCard/EntityMetadataCard'; +export { AboutCard } from './components/AboutCard'; export { EntityPageLayout } from './components/EntityPageLayout'; From 39f739408f8bc4f213528acbf1e93668447e4c76 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 11:54:59 +0200 Subject: [PATCH 228/359] fix: typedeps again --- plugins/catalog/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 1c830d74f6..ec4ad4999a 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -40,7 +40,8 @@ "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3", - "swr": "^0.3.0" + "swr": "^0.3.0", + "@types/react": "^16.9" }, "devDependencies": { "@backstage/cli": "^0.1.1-alpha.20", @@ -52,7 +53,6 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "@types/react": "^16.9", "jest-fetch-mock": "^3.0.3", "msw": "^0.20.5", "react-test-renderer": "^16.13.1", From 568f398aa944fa2b87369d68da8fd428890db84d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 1 Sep 2020 11:59:31 +0200 Subject: [PATCH 229/359] BREAKING CHANGE: no longer add express.json() by default to backend --- CHANGELOG.md | 9 +++++++++ .../backend-common/src/service/lib/ServiceBuilderImpl.ts | 1 - plugins/auth-backend/package.json | 1 - plugins/auth-backend/src/service/router.ts | 5 ++--- plugins/graphql/src/service/router.ts | 4 ++-- plugins/identity-backend/src/service/router.ts | 4 +++- plugins/rollbar-backend/src/service/router.ts | 1 + plugins/scaffolder-backend/src/service/router.ts | 1 + plugins/sentry-backend/src/service/router.ts | 2 ++ yarn.lock | 2 +- 10 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce39a9f8d2..20ae9a414d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,15 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re > Collect changes for the next release below +- The backend plugin + [service builder](https://github.com/spotify/backstage/blob/master/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts) + no longer adds `express.json()` automatically to all routes. While convenient + in a lot of cases, it also led to problems where for example the proxy + middleware could hang because the body had already been altered and could not + be streamed. Also, plugins that rather wanted to handle e.g. form encoded data + still had to cater to that manually. We therefore decided to let plugins add + `express.json()` themselves if they happen to deal with JSON data. + ## v0.1.1-alpha.20 - Includes https://github.com/spotify/backstage/pull/2097 to resolve issues with create-plugin command. diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index df69387e40..10f9ca4d9b 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -135,7 +135,6 @@ export class ServiceBuilderImpl implements ServiceBuilder { app.use(cors(corsOptions)); } app.use(compression()); - app.use(express.json()); if (this.enableMetrics) { app.use(metricsHandler()); } diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index e5838c9011..107a594c4d 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -23,7 +23,6 @@ "@backstage/backend-common": "^0.1.1-alpha.20", "@backstage/config": "^0.1.1-alpha.20", "@types/express": "^4.17.6", - "body-parser": "^1.19.0", "compression": "^1.7.4", "cookie-parser": "^1.4.5", "cors": "^2.8.5", diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index 8ea1d412cb..b5451cad6c 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -17,7 +17,6 @@ import express from 'express'; import Router from 'express-promise-router'; import cookieParser from 'cookie-parser'; -import bodyParser from 'body-parser'; import Knex from 'knex'; import { Logger } from 'winston'; import { createAuthProviderRouter } from '../providers'; @@ -53,8 +52,8 @@ export async function createRouter( }); router.use(cookieParser()); - router.use(bodyParser.urlencoded({ extended: false })); - router.use(bodyParser.json()); + router.use(express.urlencoded({ extended: false })); + router.use(express.json()); const providersConfig = options.config.getConfig('auth.providers'); const providers = providersConfig.keys(); diff --git a/plugins/graphql/src/service/router.ts b/plugins/graphql/src/service/router.ts index 69e2394f11..0b91d7307a 100644 --- a/plugins/graphql/src/service/router.ts +++ b/plugins/graphql/src/service/router.ts @@ -39,8 +39,8 @@ export async function createRouter( const server = new ApolloServer({ typeDefs, logger: options.logger }); const router = Router(); - const apolloMiddlware = server.getMiddleware({ path: '/' }); - router.use(apolloMiddlware); + const apolloMiddleware = server.getMiddleware({ path: '/' }); + router.use(apolloMiddleware); router.get('/health', (_, response) => { response.send({ status: 'ok' }); diff --git a/plugins/identity-backend/src/service/router.ts b/plugins/identity-backend/src/service/router.ts index 9c6515ac04..9d16e8f332 100644 --- a/plugins/identity-backend/src/service/router.ts +++ b/plugins/identity-backend/src/service/router.ts @@ -27,13 +27,15 @@ export interface RouterOptions { const makeRouter = (adapter: IdentityApi): express.Router => { const router = Router(); + router.use(express.json()); + router.get('/users/:user/groups', async (req, res) => { const user = req.params.user; const type = req.query.type?.toString() ?? ''; - const response = await adapter.getUserGroups({ user, type }); res.send(response); }); + return router; }; diff --git a/plugins/rollbar-backend/src/service/router.ts b/plugins/rollbar-backend/src/service/router.ts index 0a8c58a9d7..5ee63b5b05 100644 --- a/plugins/rollbar-backend/src/service/router.ts +++ b/plugins/rollbar-backend/src/service/router.ts @@ -31,6 +31,7 @@ export async function createRouter( options: RouterOptions, ): Promise { const router = Router(); + const logger = options.logger.child({ plugin: 'rollbar' }); const config = options.config.getConfig('rollbar'); const accessToken = !options.rollbarApi diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index fcfd5765a1..1b96158fa4 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -42,6 +42,7 @@ export async function createRouter( options: RouterOptions, ): Promise { const router = Router(); + router.use(express.json()); const { preparers, diff --git a/plugins/sentry-backend/src/service/router.ts b/plugins/sentry-backend/src/service/router.ts index e7978042d3..153a8325b5 100644 --- a/plugins/sentry-backend/src/service/router.ts +++ b/plugins/sentry-backend/src/service/router.ts @@ -20,6 +20,8 @@ import { getSentryApiForwarder } from './sentry-api'; export async function createRouter(logger: Logger): Promise { const router = Router(); + router.use(express.json()); + const SENTRY_TOKEN = process.env.SENTRY_TOKEN; if (!SENTRY_TOKEN) { if (process.env.NODE_ENV !== 'development') { diff --git a/yarn.lock b/yarn.lock index aa85370465..84e4c4cbb4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6562,7 +6562,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== -body-parser@1.19.0, body-parser@^1.18.3, body-parser@^1.19.0: +body-parser@1.19.0, body-parser@^1.18.3: version "1.19.0" resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== From 4f1bb23e6a6a6a6a6b725f6d437e61bc1aa932de Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 12:23:06 +0200 Subject: [PATCH 230/359] fix: package deps for create-app template --- .../templates/default-app/packages/app/package.json.hbs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs index 7d34a94c7b..9a59785388 100644 --- a/packages/create-app/templates/default-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs @@ -11,6 +11,8 @@ "@backstage/plugin-register-component": "^{{version}}", "@backstage/plugin-scaffolder": "^{{version}}", "@backstage/plugin-techdocs": "^{{version}}", + "@backstage/catalog-model": "^{{version}}", + "@backstage/plugin-github-actions": "^{{version}}", "@backstage/test-utils": "^{{version}}", "@backstage/theme": "^{{version}}", "history": "^5.0.0", From 56412628699114d4fe732c349a3b48bd202a2481 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 12:44:06 +0200 Subject: [PATCH 231/359] fix: disable plugin verification in browser [temporary] --- packages/e2e-test/src/e2e-test.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/e2e-test/src/e2e-test.ts b/packages/e2e-test/src/e2e-test.ts index 7377655497..6dab8caafb 100644 --- a/packages/e2e-test/src/e2e-test.ts +++ b/packages/e2e-test/src/e2e-test.ts @@ -277,11 +277,12 @@ async function testAppServe(pluginName: string, appDir: string) { const browser = new Browser(); await waitForPageWithText(browser, '/', 'Backstage Service Catalog'); - await waitForPageWithText( - browser, - `/${pluginName}`, - `Welcome to ${pluginName}!`, - ); + // TODO(shmidt-i): adjust the plugin creation flow with new routing patterns + // await waitForPageWithText( + // browser, + // `/${pluginName}`, + // `Welcome to ${pluginName}!`, + // ); print('Both App and Plugin loaded correctly'); successful = true; From 1b658f4bc349018c06a96f6e1ac8cc29bcbba597 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Tue, 1 Sep 2020 13:31:45 +0200 Subject: [PATCH 232/359] fix: use correct rules syntax --- app-config.yaml | 2 +- docs/features/software-templates/adding-templates.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index ba556873aa..7a8863495e 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -49,7 +49,7 @@ lighthouse: catalog: rules: - allow: [Component, API, Group, Template] + - allow: [Component, API, Group, Template, Location] processors: githubApi: privateToken: diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index 16419b4dfe..58dbe847a5 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -77,7 +77,7 @@ to be ingested from any source, for example: ```yaml catalog: rules: - allow: [Component, API, Template] + - allow: [Component, API, Template] ``` For loading from a file, the following command should work when the backend is From 3b545791cec1ccdd9ae7ae6716b056104087e3c9 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Mon, 31 Aug 2020 15:08:24 +0200 Subject: [PATCH 233/359] new release 31 aug 20 --- microsite/package.json | 2 +- packages/app/package.json | 44 ++++++++++++------------- packages/backend-common/package.json | 10 +++--- packages/backend/package.json | 32 +++++++++--------- packages/catalog-model/package.json | 6 ++-- packages/cli-common/package.json | 2 +- packages/cli/package.json | 8 ++--- packages/config-loader/package.json | 4 +-- packages/config/package.json | 2 +- packages/core-api/package.json | 10 +++--- packages/core/package.json | 12 +++---- packages/create-app/package.json | 4 +-- packages/dev-utils/package.json | 10 +++--- packages/docgen/package.json | 2 +- packages/e2e-test/package.json | 4 +-- packages/storybook/package.json | 4 +-- packages/techdocs-cli/package.json | 4 +-- packages/test-utils-core/package.json | 2 +- packages/test-utils/package.json | 10 +++--- packages/theme/package.json | 4 +-- plugins/api-docs/package.json | 16 ++++----- plugins/app-backend/package.json | 8 ++--- plugins/auth-backend/package.json | 8 ++--- plugins/catalog-backend/package.json | 10 +++--- plugins/catalog/package.json | 26 +++++++-------- plugins/circleci/package.json | 10 +++--- plugins/explore/package.json | 12 +++---- plugins/github-actions/package.json | 16 ++++----- plugins/gitops-profiles/package.json | 10 +++--- plugins/graphiql/package.json | 12 +++---- plugins/graphql/package.json | 6 ++-- plugins/identity-backend/package.json | 6 ++-- plugins/jenkins/package.json | 12 +++---- plugins/lighthouse/package.json | 14 ++++---- plugins/newrelic/package.json | 10 +++--- plugins/proxy-backend/package.json | 8 ++--- plugins/register-component/package.json | 14 ++++---- plugins/rollbar-backend/package.json | 8 ++--- plugins/rollbar/package.json | 12 +++---- plugins/scaffolder-backend/package.json | 10 +++--- plugins/scaffolder/package.json | 16 ++++----- plugins/sentry-backend/package.json | 6 ++-- plugins/sentry/package.json | 10 +++--- plugins/tech-radar/package.json | 12 +++---- plugins/techdocs-backend/package.json | 10 +++--- plugins/techdocs/package.json | 18 +++++----- plugins/welcome/package.json | 10 +++--- 47 files changed, 243 insertions(+), 243 deletions(-) diff --git a/microsite/package.json b/microsite/package.json index baaf9ccaf4..ee3c7f8dc2 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -1,5 +1,5 @@ { - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "name": "backstage-microsite", "license": "Apache-2.0", "private": true, diff --git a/packages/app/package.json b/packages/app/package.json index d32808b48b..13f2b83440 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,29 +1,29 @@ { "name": "example-app", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": true, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/plugin-api-docs": "^0.1.1-alpha.20", - "@backstage/plugin-catalog": "^0.1.1-alpha.20", - "@backstage/plugin-circleci": "^0.1.1-alpha.20", - "@backstage/plugin-explore": "^0.1.1-alpha.20", - "@backstage/plugin-github-actions": "^0.1.1-alpha.20", - "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.20", - "@backstage/plugin-graphiql": "^0.1.1-alpha.20", - "@backstage/plugin-jenkins": "^0.1.1-alpha.20", - "@backstage/plugin-lighthouse": "^0.1.1-alpha.20", - "@backstage/plugin-newrelic": "^0.1.1-alpha.20", - "@backstage/plugin-register-component": "^0.1.1-alpha.20", - "@backstage/plugin-rollbar": "^0.1.1-alpha.20", - "@backstage/plugin-scaffolder": "^0.1.1-alpha.20", - "@backstage/plugin-sentry": "^0.1.1-alpha.20", - "@backstage/plugin-tech-radar": "^0.1.1-alpha.20", - "@backstage/plugin-techdocs": "^0.1.1-alpha.20", - "@backstage/plugin-welcome": "^0.1.1-alpha.20", - "@backstage/test-utils": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/plugin-api-docs": "^0.1.1-alpha.21", + "@backstage/plugin-catalog": "^0.1.1-alpha.21", + "@backstage/plugin-circleci": "^0.1.1-alpha.21", + "@backstage/plugin-explore": "^0.1.1-alpha.21", + "@backstage/plugin-github-actions": "^0.1.1-alpha.21", + "@backstage/plugin-gitops-profiles": "^0.1.1-alpha.21", + "@backstage/plugin-graphiql": "^0.1.1-alpha.21", + "@backstage/plugin-jenkins": "^0.1.1-alpha.21", + "@backstage/plugin-lighthouse": "^0.1.1-alpha.21", + "@backstage/plugin-newrelic": "^0.1.1-alpha.21", + "@backstage/plugin-register-component": "^0.1.1-alpha.21", + "@backstage/plugin-rollbar": "^0.1.1-alpha.21", + "@backstage/plugin-scaffolder": "^0.1.1-alpha.21", + "@backstage/plugin-sentry": "^0.1.1-alpha.21", + "@backstage/plugin-tech-radar": "^0.1.1-alpha.21", + "@backstage/plugin-techdocs": "^0.1.1-alpha.21", + "@backstage/plugin-welcome": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.0", diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 92d301d7e3..2f82634a2e 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.20", - "@backstage/config": "^0.1.1-alpha.20", - "@backstage/config-loader": "^0.1.1-alpha.20", + "@backstage/cli-common": "^0.1.1-alpha.21", + "@backstage/config": "^0.1.1-alpha.21", + "@backstage/config-loader": "^0.1.1-alpha.21", "@types/cors": "^2.8.6", "@types/express": "^4.17.6", "compression": "^1.7.4", @@ -57,7 +57,7 @@ } }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "@types/compression": "^1.7.0", "@types/http-errors": "^1.6.3", "@types/morgan": "^1.9.0", diff --git a/packages/backend/package.json b/packages/backend/package.json index 7393ede81e..2509c006d7 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "dist/index.cjs.js", "types": "src/index.ts", "private": true, @@ -18,22 +18,22 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "example-app": "^0.1.1-alpha.20", - "@backstage/backend-common": "^0.1.1-alpha.20", - "@backstage/catalog-model": "^0.1.1-alpha.20", - "@backstage/config": "^0.1.1-alpha.20", - "@backstage/plugin-app-backend": "^0.1.1-alpha.20", - "@backstage/plugin-auth-backend": "^0.1.1-alpha.20", - "@backstage/plugin-catalog-backend": "^0.1.1-alpha.20", - "@backstage/plugin-graphql-backend": "^0.1.1-alpha.20", - "@backstage/plugin-identity-backend": "^0.1.1-alpha.20", - "@backstage/plugin-proxy-backend": "^0.1.1-alpha.20", - "@backstage/plugin-rollbar-backend": "^0.1.1-alpha.20", - "@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.20", - "@backstage/plugin-sentry-backend": "^0.1.1-alpha.20", - "@backstage/plugin-techdocs-backend": "^0.1.1-alpha.20", + "@backstage/backend-common": "^0.1.1-alpha.21", + "@backstage/catalog-model": "^0.1.1-alpha.21", + "@backstage/config": "^0.1.1-alpha.21", + "@backstage/plugin-app-backend": "^0.1.1-alpha.21", + "@backstage/plugin-auth-backend": "^0.1.1-alpha.21", + "@backstage/plugin-catalog-backend": "^0.1.1-alpha.21", + "@backstage/plugin-graphql-backend": "^0.1.1-alpha.21", + "@backstage/plugin-identity-backend": "^0.1.1-alpha.21", + "@backstage/plugin-proxy-backend": "^0.1.1-alpha.21", + "@backstage/plugin-rollbar-backend": "^0.1.1-alpha.21", + "@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.21", + "@backstage/plugin-sentry-backend": "^0.1.1-alpha.21", + "@backstage/plugin-techdocs-backend": "^0.1.1-alpha.21", "@octokit/rest": "^18.0.0", "dockerode": "^3.2.0", + "example-app": "^0.1.1-alpha.21", "express": "^4.17.1", "knex": "^0.21.1", "pg": "^8.3.0", @@ -42,7 +42,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "@types/dockerode": "^2.5.32", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5", diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 69c91dbe8f..0d18125d43 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.20", + "@backstage/config": "^0.1.1-alpha.21", "@types/json-schema": "^7.0.5", "@types/yup": "^0.28.2", "json-schema": "^0.2.5", @@ -29,7 +29,7 @@ "yup": "^0.29.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "@types/express": "^4.17.6", "@types/jest": "^26.0.7", "@types/lodash": "^4.14.151", diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index f76fcbb1da..4132907e70 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli-common", "description": "Common functionality used by cli, backend, and create-app", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "main": "src/index.ts", "types": "src/index.ts", diff --git a/packages/cli/package.json b/packages/cli/package.json index 6a2b02e9d4..9d6b1c054f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public" @@ -28,9 +28,9 @@ "backstage-cli": "bin/backstage-cli" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.20", - "@backstage/config": "^0.1.1-alpha.20", - "@backstage/config-loader": "^0.1.1-alpha.20", + "@backstage/cli-common": "^0.1.1-alpha.21", + "@backstage/config": "^0.1.1-alpha.21", + "@backstage/config-loader": "^0.1.1-alpha.21", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^3.18.5", "@lerna/project": "^3.18.0", diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 4eda5f8ba8..92d20ef1e7 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public", @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.20", + "@backstage/config": "^0.1.1-alpha.21", "fs-extra": "^9.0.0", "yaml": "^1.9.2", "yup": "^0.29.1" diff --git a/packages/config/package.json b/packages/config/package.json index a991a73621..236964f2bd 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config", "description": "Config API used by Backstage core, backend, and CLI", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-api/package.json b/packages/core-api/package.json index e557719a4f..a5e23796cd 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-api", "description": "Internal Core API used by Backstage plugins and apps", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public", @@ -29,8 +29,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/config": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@types/react": "^16.9", @@ -41,8 +41,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/test-utils-core": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/test-utils-core": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/packages/core/package.json b/packages/core/package.json index b51f0dd554..d6731aca9a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core", "description": "Core API used by Backstage plugins and apps", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.20", - "@backstage/core-api": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/config": "^0.1.1-alpha.21", + "@backstage/core-api": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -54,8 +54,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/test-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 7de2a9559c..2d0d4a5758 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "Create app package for Backstage", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public" @@ -27,7 +27,7 @@ "start": "nodemon --" }, "dependencies": { - "@backstage/cli-common": "^0.1.1-alpha.20", + "@backstage/cli-common": "^0.1.1-alpha.21", "chalk": "^4.0.0", "commander": "^6.1.0", "fs-extra": "^9.0.0", diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 5b9c1da6fb..c917130cbd 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/test-utils": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@testing-library/jest-dom": "^5.10.1", diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 46f0e32902..c75ea53faf 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -1,7 +1,7 @@ { "name": "docgen", "description": "Tool for generating API Documentation for itself", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": true, "homepage": "https://backstage.io", "repository": { diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index a32595b165..0af233ec8e 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,7 +1,7 @@ { "name": "e2e-test", "description": "E2E test for verifying Backstage packages", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": true, "homepage": "https://backstage.io", "repository": { @@ -21,7 +21,7 @@ "test:e2e": "yarn start" }, "devDependencies": { - "@backstage/cli-common": "^0.1.1-alpha.20", + "@backstage/cli-common": "^0.1.1-alpha.21", "@types/fs-extra": "^9.0.1", "@types/node": "^13.7.2", "fs-extra": "^9.0.0", diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 8ade556ee8..35b609eebf 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -1,6 +1,6 @@ { "name": "storybook", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "description": "Storybook build for core package", "private": true, "scripts": { @@ -14,7 +14,7 @@ ] }, "dependencies": { - "@backstage/theme": "^0.1.1-alpha.20" + "@backstage/theme": "^0.1.1-alpha.21" }, "devDependencies": { "@storybook/addon-actions": "^5.3.17", diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 4dcdfa4d20..222620f976 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "CLI for running TechDocs locally.", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public" @@ -44,7 +44,7 @@ "ext": "ts" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "commander": "^6.1.0", "fs-extra": "^9.0.1", "http-proxy": "^1.18.1", diff --git a/packages/test-utils-core/package.json b/packages/test-utils-core/package.json index 172228759a..8439bee779 100644 --- a/packages/test-utils-core/package.json +++ b/packages/test-utils-core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils-core", "description": "Utilities to test Backstage core", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public", diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index fbc3c1e57a..d28b66a9ed 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/core-api": "^0.1.1-alpha.20", - "@backstage/test-utils-core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/core-api": "^0.1.1-alpha.21", + "@backstage/test-utils-core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/packages/theme/package.json b/packages/theme/package.json index dbe6f9a366..e946482f32 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/theme", "description": "material-ui theme for use with Backstage.", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "@material-ui/core": "^4.9.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20" + "@backstage/cli": "^0.1.1-alpha.21" }, "files": [ "dist" diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index a2ec96ee67..644c6ed63a 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.20", - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/plugin-catalog": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/catalog-model": "^0.1.1-alpha.21", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/plugin-catalog": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@kyma-project/asyncapi-react": "^0.11.0", "@material-icons/font": "^1.0.2", "@material-ui/core": "^4.9.1", @@ -36,9 +36,9 @@ "swagger-ui-react": "^3.31.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", - "@backstage/test-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 46cd8dcf25..e6c3dde7b6 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.20", - "@backstage/config-loader": "^0.1.1-alpha.20", + "@backstage/backend-common": "^0.1.1-alpha.21", + "@backstage/config-loader": "^0.1.1-alpha.21", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", @@ -30,7 +30,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "@types/supertest": "^2.0.8", "msw": "^0.19.5", "supertest": "^4.0.2" diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index e5838c9011..4a03ef29a0 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.20", - "@backstage/config": "^0.1.1-alpha.20", + "@backstage/backend-common": "^0.1.1-alpha.21", + "@backstage/config": "^0.1.1-alpha.21", "@types/express": "^4.17.6", "body-parser": "^1.19.0", "compression": "^1.7.4", @@ -50,7 +50,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/jwt-decode": "2.2.1", diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 82b3074c54..c1b20ed7bb 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ "mock-data:local": "./scripts/mock-data-local.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.20", - "@backstage/catalog-model": "^0.1.1-alpha.20", - "@backstage/config": "^0.1.1-alpha.20", + "@backstage/backend-common": "^0.1.1-alpha.21", + "@backstage/catalog-model": "^0.1.1-alpha.21", + "@backstage/config": "^0.1.1-alpha.21", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", @@ -41,7 +41,7 @@ "yup": "^0.29.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "@types/lodash": "^4.14.151", "@types/node-fetch": "^2.5.7", "@types/supertest": "^2.0.8", diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 0f82add554..0b775e84ba 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,15 +21,15 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.20", - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/plugin-api-docs": "^0.1.1-alpha.20", - "@backstage/plugin-github-actions": "^0.1.1-alpha.20", - "@backstage/plugin-jenkins": "^0.1.1-alpha.20", - "@backstage/plugin-scaffolder": "^0.1.1-alpha.20", - "@backstage/plugin-sentry": "^0.1.1-alpha.20", - "@backstage/plugin-techdocs": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/catalog-model": "^0.1.1-alpha.21", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/plugin-api-docs": "^0.1.1-alpha.21", + "@backstage/plugin-github-actions": "^0.1.1-alpha.21", + "@backstage/plugin-jenkins": "^0.1.1-alpha.21", + "@backstage/plugin-scaffolder": "^0.1.1-alpha.21", + "@backstage/plugin-sentry": "^0.1.1-alpha.21", + "@backstage/plugin-techdocs": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -42,9 +42,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", - "@backstage/test-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 8515e12a8e..b5824d3869 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-circleci", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "postpack": "backstage-cli postpack" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,8 +36,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 12d6e8073e..42176c45cf 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", - "@backstage/test-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 686f85e23e..2dc3c9fa61 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-actions", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,11 +22,11 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.20", - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/core-api": "^0.1.1-alpha.20", - "@backstage/plugin-catalog": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/catalog-model": "^0.1.1-alpha.21", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/core-api": "^0.1.1-alpha.21", + "@backstage/plugin-catalog": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -39,8 +39,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index bd26f51a3e..dc32a4df14 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gitops-profiles", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,8 +32,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 0c0ae53171..cc1a0a7204 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "private": false, "publishConfig": { "access": "public", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -43,9 +43,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", - "@backstage/test-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphql/package.json b/plugins/graphql/package.json index 045cd471a1..1612398ea9 100644 --- a/plugins/graphql/package.json +++ b/plugins/graphql/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-graphql-backend", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.20", + "@backstage/backend-common": "^0.1.1-alpha.21", "@types/express": "^4.17.6", "apollo-server": "^2.16.0", "apollo-server-express": "^2.16.0", @@ -32,7 +32,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "@types/supertest": "^2.0.8", "eslint-plugin-graphql": "^4.0.0", "msw": "^0.20.5", diff --git a/plugins/identity-backend/package.json b/plugins/identity-backend/package.json index 554e51aea0..18db7c9220 100644 --- a/plugins/identity-backend/package.json +++ b/plugins/identity-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-identity-backend", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.20", + "@backstage/backend-common": "^0.1.1-alpha.21", "@types/express": "^4.17.6", "compression": "^1.7.4", "cors": "^2.8.5", @@ -33,7 +33,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "jest-fetch-mock": "^3.0.3" }, "files": [ diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 73ac0344e4..da1754749f 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.20", - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/catalog-model": "^0.1.1-alpha.21", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,8 +35,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 12cff879c0..647215f7f8 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-lighthouse", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/config": "^0.1.1-alpha.20", - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/config": "^0.1.1-alpha.21", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -34,9 +34,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", - "@backstage/test-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 007535a832..84f066edae 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -31,8 +31,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index f924242ef0..9f3bc16103 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,8 +19,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.20", - "@backstage/config": "^0.1.1-alpha.20", + "@backstage/backend-common": "^0.1.1-alpha.21", + "@backstage/config": "^0.1.1-alpha.21", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", @@ -34,7 +34,7 @@ "yup": "^0.29.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "@types/node-fetch": "^2.5.7", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 106f55714f..0064d50aaa 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-register-component", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.20", - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/plugin-catalog": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/catalog-model": "^0.1.1-alpha.21", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/plugin-catalog": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -36,8 +36,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index 9bc35185ce..7fc1b27e64 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar-backend", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.20", - "@backstage/config": "^0.1.1-alpha.20", + "@backstage/backend-common": "^0.1.1-alpha.21", + "@backstage/config": "^0.1.1-alpha.21", "@types/express": "^4.17.6", "axios": "^0.19.2", "camelcase-keys": "^6.2.2", @@ -37,7 +37,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "@types/supertest": "^2.0.8", "jest-fetch-mock": "^3.0.3", "supertest": "^4.0.2" diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 1971dc77a9..9c5343247c 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", - "@backstage/test-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 67ca4e4c61..ef429cf94b 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.20", - "@backstage/catalog-model": "^0.1.1-alpha.20", - "@backstage/config": "^0.1.1-alpha.20", + "@backstage/backend-common": "^0.1.1-alpha.21", + "@backstage/catalog-model": "^0.1.1-alpha.21", + "@backstage/config": "^0.1.1-alpha.21", "@octokit/rest": "^18.0.0", "@types/dockerode": "^2.5.32", "@types/express": "^4.17.6", @@ -44,7 +44,7 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "@octokit/types": "^5.0.1", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index ba0e01ac1b..7497cbe024 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.20", - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/plugin-catalog": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/catalog-model": "^0.1.1-alpha.21", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/plugin-catalog": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,9 +41,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", - "@backstage/test-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/sentry-backend/package.json b/plugins/sentry-backend/package.json index 0bc4ee59af..ea91839bfb 100644 --- a/plugins/sentry-backend/package.json +++ b/plugins/sentry-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry-backend", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.20", + "@backstage/backend-common": "^0.1.1-alpha.21", "@types/express": "^4.17.6", "axios": "^0.19.2", "compression": "^1.7.4", @@ -34,7 +34,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "jest-fetch-mock": "^3.0.3" }, "files": [ diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 721619509f..d5b29cc307 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -34,8 +34,8 @@ "timeago.js": "^4.0.2" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 428ff929fb..4039f40878 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-radar", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/test-utils-core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/test-utils-core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,8 +35,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 928a0659b1..00650ccca8 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "mock-data": "./scripts/mock-data.sh" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.20", - "@backstage/catalog-model": "^0.1.1-alpha.20", - "@backstage/config": "^0.1.1-alpha.20", + "@backstage/backend-common": "^0.1.1-alpha.21", + "@backstage/catalog-model": "^0.1.1-alpha.21", + "@backstage/config": "^0.1.1-alpha.21", "@types/dockerode": "^2.5.34", "@types/express": "^4.17.6", "dockerode": "^3.2.1", @@ -37,7 +37,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", "@types/node-fetch": "^2.5.7", "supertest": "^4.0.2" }, diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 4654213f8c..2d63c9dd53 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,12 +22,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.1.1-alpha.20", - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/core-api": "^0.1.1-alpha.20", - "@backstage/plugin-catalog": "^0.1.1-alpha.20", - "@backstage/test-utils": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/catalog-model": "^0.1.1-alpha.21", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/core-api": "^0.1.1-alpha.21", + "@backstage/plugin-catalog": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -40,8 +40,8 @@ "sanitize-html": "^1.27.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 972e081ae1..2d754a51a6 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-welcome", - "version": "0.1.1-alpha.20", + "version": "0.1.1-alpha.21", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -21,8 +21,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.1.1-alpha.20", - "@backstage/theme": "^0.1.1-alpha.20", + "@backstage/core": "^0.1.1-alpha.21", + "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -32,8 +32,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.20", - "@backstage/dev-utils": "^0.1.1-alpha.20", + "@backstage/cli": "^0.1.1-alpha.21", + "@backstage/dev-utils": "^0.1.1-alpha.21", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", From aa2eeb8c49aae07d2bf67df6864c432c4f48e0a7 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 14:19:29 +0200 Subject: [PATCH 234/359] fix: e2e tsc --- packages/e2e-test/src/e2e-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/e2e-test/src/e2e-test.ts b/packages/e2e-test/src/e2e-test.ts index 6dab8caafb..4942a5abf5 100644 --- a/packages/e2e-test/src/e2e-test.ts +++ b/packages/e2e-test/src/e2e-test.ts @@ -266,7 +266,7 @@ async function createPlugin(pluginName: string, appDir: string) { /** * Start serving the newly created app and make sure that the create plugin is rendering correctly */ -async function testAppServe(pluginName: string, appDir: string) { +async function testAppServe(_pluginName: string, appDir: string) { const startApp = spawnPiped(['yarn', 'start'], { cwd: appDir, }); From ee0310213c41e385ec9c6bbab927392b8f66f676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Tue, 1 Sep 2020 14:27:33 +0200 Subject: [PATCH 235/359] Tweaks to plugin marketplace (#2209) * Tweaks to plugin marketplace * Add plugin details to roadmap * Update README.md * Link to plugins from home * Update circleci.yaml --- CONTRIBUTING.md | 7 +- README.md | 2 +- docs/overview/roadmap.md | 21 ++-- microsite/core/Footer.js | 3 + microsite/data/plugins/api-docs.yaml | 2 +- microsite/data/plugins/circleci.yaml | 2 +- microsite/data/plugins/github-actions.yaml | 13 +++ microsite/data/plugins/tech-radar.yaml | 2 +- microsite/pages/en/index.js | 10 +- microsite/pages/en/plugins.js | 38 ++++++- microsite/static/css/plugins.css | 115 +++++++++++---------- 11 files changed, 133 insertions(+), 82 deletions(-) create mode 100644 microsite/data/plugins/github-actions.yaml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b8febd1d2..853332f74d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,4 @@ ---- -id: CONTRIBUTING -title: Contributing ---- +# Contributing to Backstage Our vision for Backstage is for it to become the trusted standard toolbox (read: UX layer) for the open source infrastructure landscape. Think of it like Kubernetes for developer experience. We realize this is an ambitious goal. We can’t do it alone. @@ -31,7 +28,7 @@ What kind of plugins should/could be created? Some inspiration from the 120+ plu ## Suggesting a plugin -If you start developing a plugin that you aim to release as open source, we suggest that you create a new [new Issue](https://github.com/spotify/backstage/issues/new?template=plugin_template.md). This helps the community know what plugins are in development. +If you start developing a plugin that you aim to release as open source, we suggest that you create a new [new Issue](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). This helps the community know what plugins are in development. You can also use this process if you have an idea for a good plugin but you hope that someone else will pick up the work. diff --git a/README.md b/README.md index b5e8704548..4009864edc 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Take a look at the [Getting Started](https://backstage.io/docs/getting-started/i ## Documentation -- [Main documentation](https://backstage.io/docs/overview/what-is-backstage) +- [Main documentation](https://backstage.io/docs) - [Service Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview) - [Architecture](https://backstage.io/docs/overview/architecture-terminology) ([Decisions](https://backstage.io/docs/architecture-decisions/adrs-overview)) - [Designing for Backstage](https://backstage.io/docs/dls/design) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index a84bc525c9..5a06f4544f 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -79,18 +79,26 @@ guidelines to get started. - Further improvements to platform documentation +### Plugins + +Building and maintaining [plugins](https://backstage.io/plugins) is the work of +the entire Backstage community. + +A list of plugins that are in development is +[available here](https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+label%3Aplugin+sort%3Areactions-%2B1-desc). +We strongly recommend to upvote 👍 plugins you are interested in. This helps us +and the community prioritize what plugins to build. + +Are you missing a plugin for your favorite tool? Please +[suggest a new one](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). +Chances are that someone will jump in and help build it. + ### Future work 🔮 - **[Backstage platform is stable](https://github.com/spotify/backstage/milestone/19)** - The platform APIs and features are stable and can be depended on for production use. After this plugins will require little to no maintenance. -- **[Plugin marketplace](https://github.com/spotify/backstage/issues/2009)** - - As the ecosystem of Backstage plugins continues to grow it is becoming - increasingly hard to keep track of what plugins are available. To solve this - we imagine a "Plugin marketplace" that helps with discovery and installation - of plugins. - - **Deploy a product demo at `demo.backstage.io`** - Deploy a typical Backstage deployment available publicly so that people can click around and get a feel for the product without having to install anything. @@ -111,6 +119,7 @@ guidelines to get started. ### Completed milestones ✅ +- [Plugin marketplace](https://backstage.io/plugins) - [Improved and move documentation to backstage.io](https://backstage.io/docs/overview/what-is-backstage) - [Backstage Service Catalog (alpha)](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha) - [Backstage Software Templates (alpha)](https://backstage.io/blog/2020/08/05/announcing-backstage-software-templates) diff --git a/microsite/core/Footer.js b/microsite/core/Footer.js index 407bebca84..761862c1fc 100644 --- a/microsite/core/Footer.js +++ b/microsite/core/Footer.js @@ -45,6 +45,9 @@ class Footer extends React.Component {
Community
Support chatroom + + Contributing + Subscribe to our newsletter diff --git a/microsite/data/plugins/api-docs.yaml b/microsite/data/plugins/api-docs.yaml index b7f85b62b3..babcbf74f1 100644 --- a/microsite/data/plugins/api-docs.yaml +++ b/microsite/data/plugins/api-docs.yaml @@ -5,5 +5,5 @@ authorUrl: https://sda.se/ category: Discovery description: Components to discover and display API entities as an extension to the catalog plugin. documentation: https://github.com/spotify/backstage/blob/master/plugins/api-docs/README.md -iconUrl: https://github.com/spotify/backstage/raw/master/plugins/api-docs/docs/entity_tab_api.png +iconUrl: https://thecoders.io/wp-content/uploads/2019/11/tech-swagger.svg npmPackageName: '@backstage/plugin-api-docs' diff --git a/microsite/data/plugins/circleci.yaml b/microsite/data/plugins/circleci.yaml index c8a3677614..6937064815 100644 --- a/microsite/data/plugins/circleci.yaml +++ b/microsite/data/plugins/circleci.yaml @@ -5,5 +5,5 @@ authorUrl: https://www.spotify.com/ category: CI description: Automate your development process with CI hosted in the cloud or on a private server. documentation: https://github.com/spotify/backstage/tree/master/plugins/circleci -iconUrl: https://d3r49iyjzglexf.cloudfront.net/logo-wordmark-26f8eaea9b0f6e13b90d3f4a8fd8fda31490f5af41daab98bbede45037682576.svg +iconUrl: https://www.saaves.com/storage/brochure/logo-circleci-icon1583764538.png npmPackageName: '@backstage/plugin-circleci' diff --git a/microsite/data/plugins/github-actions.yaml b/microsite/data/plugins/github-actions.yaml new file mode 100644 index 0000000000..b384980b2a --- /dev/null +++ b/microsite/data/plugins/github-actions.yaml @@ -0,0 +1,13 @@ +--- +title: GitHub Actions +author: Spotify +authorUrl: https://www.spotify.com/ +category: CI +description: GitHub Actions makes it easy to automate all your software workflows, now with world-class CI/CD. Build, test, and deploy your code right from GitHub. +documentation: https://github.com/spotify/backstage/tree/master/plugins/github-actions +iconUrl: https://avatars2.githubusercontent.com/u/44036562?s=400&v=4 +npmPackageName: '@backstage/plugin-github-actions' +tags: + - ci + - cd + - github diff --git a/microsite/data/plugins/tech-radar.yaml b/microsite/data/plugins/tech-radar.yaml index a20667e8f0..585e204b12 100644 --- a/microsite/data/plugins/tech-radar.yaml +++ b/microsite/data/plugins/tech-radar.yaml @@ -5,5 +5,5 @@ authorUrl: https://www.spotify.com/ category: Discovery description: Visualize the your company's official guidelines of different areas of software development. documentation: https://github.com/spotify/backstage/tree/master/plugins/tech-radar -iconUrl: https://github.com/spotify/backstage/raw/master/plugins/tech-radar/docs/screenshot.png +iconUrl: https://img.icons8.com/officel/2x/radar.png npmPackageName: '@backstage/plugin-tech-radar' diff --git a/microsite/pages/en/index.js b/microsite/pages/en/index.js index 986db4b732..7a58ede25f 100644 --- a/microsite/pages/en/index.js +++ b/microsite/pages/en/index.js @@ -455,12 +455,8 @@ class Index extends React.Component { Share with the community - Building{' '} - - open source plugins - {' '} - contributes to the entire Backstage ecosystem, which benefits - everyone + Building open source plugins contributes + to the entire Backstage ecosystem, which benefits everyone @@ -472,7 +468,7 @@ class Index extends React.Component { Build a plugin - + Contribute diff --git a/microsite/pages/en/plugins.js b/microsite/pages/en/plugins.js index 916f34c21e..8c5f017e24 100644 --- a/microsite/pages/en/plugins.js +++ b/microsite/pages/en/plugins.js @@ -29,13 +29,14 @@ const Plugins = () => (
-

Plugins

+

Plugin marketplace

+

Open source plugins that you can add to your Backstage deployment

- Add Plugin + Add to marketplace
@@ -69,13 +70,44 @@ const Plugins = () => ( className="PluginCardLink ButtonFilled" href={documentation} > - docs + Explore
), )} +
+
+

+ Do you have an existing plugin that you want to add to the + Marketplace? +

+

+ + Add to marketplace + +

+
+ +

+ See what plugins are already{' '} + + in progress + {' '} + and 👍. Missing a plugin for your favorite tool? Please{' '} + + suggest + {' '} + a new one. +

+
+
diff --git a/microsite/static/css/plugins.css b/microsite/static/css/plugins.css index 042e2954e1..96fe3dac35 100644 --- a/microsite/static/css/plugins.css +++ b/microsite/static/css/plugins.css @@ -1,111 +1,112 @@ .PluginCard { - background-color: #272822; - height: 100%; - padding: 16px; - display: flex; - flex-direction: column; + background-color: #272822; + height: 100%; + padding: 16px; + display: flex; + flex-direction: column; } .grid { - display: grid; - grid-gap: 1rem; - grid-template-columns: repeat(4, 1fr); - grid-auto-rows: 1fr; - padding-top: 32px; + display: grid; + grid-gap: 1rem; + grid-template-columns: repeat(4, 1fr); + grid-auto-rows: 1fr; + padding-top: 32px; } @media (max-width: 1200px) { - .grid { - grid-template-columns: repeat(3, 1fr); - } + .grid { + grid-template-columns: repeat(3, 1fr); + } } @media only screen and (max-width: 815px) { - .grid { - grid-template-columns: repeat(2, 1fr); - } + .grid { + grid-template-columns: repeat(2, 1fr); + } } .PluginCard img { - float: left; - margin: 0px 16px 8px 0px; - height: 100px; - width: 100px; + float: left; + margin: 0px 16px 8px 0px; + height: 80px; + width: 80px; } .PluginCardHeader { - max-height: fit-content; - min-height: fit-content; + max-height: fit-content; + min-height: fit-content; } .PluginCardTitle { - color: white; - vertical-align: top; - margin: 8px 0px 0px 16px; + color: white; + vertical-align: top; + margin: 8px 0px 0px 16px; } .PluginAddNewButton { - position: absolute; - bottom: 16px; - right: 0px; + position: absolute; + bottom: 16px; + right: 0px; } .ButtonFilled { - padding: 4px 8px; - border-radius: 4px; - background-color: #36BAA2; - color: white; - margin-top: 36px; + padding: 4px 8px; + border-radius: 4px; + background-color: #36baa2; + color: white; + margin-top: 36px; } .ButtonFilled:hover { - border: 1px solid #36BAA2; - background-color: transparent; + border: 1px solid #36baa2; + background-color: transparent; } .ChipOutlined { - font-size: small; - border-radius: 16px; - padding: 2px 8px; - border: 1px solid #36BAA2; - color: #36BAA2; + font-size: small; + border-radius: 16px; + padding: 2px 8px; + border: 1px solid #36baa2; + color: #36baa2; } .PluginCardLink { - padding: 2px 8px; - position: absolute; - bottom: 0; - right: 0; + padding: 2px 8px; + position: absolute; + bottom: 0; + right: 0; } .PluginPageLayout { - margin: auto; - max-width: 1430px; - padding: 20px; + margin: auto; + max-width: 1430px; + padding: 20px; } .PluginPageHeader { - position: relative; + position: relative; } .PluginPageHeader h2 { - display: inline-block; + display: inline-block; } .PluginCardBody { - padding-top: 8px; + padding-top: 8px; } .PluginCardFooter { - position: relative; - min-height: 2em; + position: relative; + min-height: 2em; } -.Author, .Author a { - margin-bottom: 0.25em; - color: rgba(255,255,255, 0.6); +.Author, +.Author a { + margin-bottom: 0.25em; + color: rgba(255, 255, 255, 0.6); } - .Author a:hover { - color: white; +.Author a:hover { + color: white; } From 379e5186bd7cebf60b7c72d15aed333325090fb4 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 14:42:19 +0200 Subject: [PATCH 236/359] cli: set NODE_ENV to test when running tests if not already set --- packages/cli/src/commands/testCommand.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/cli/src/commands/testCommand.ts b/packages/cli/src/commands/testCommand.ts index 9969aee3b4..2c3f9fe895 100644 --- a/packages/cli/src/commands/testCommand.ts +++ b/packages/cli/src/commands/testCommand.ts @@ -57,6 +57,12 @@ export default async (cmd: Command) => { } } + // This is the only thing that is not implemented by jest.run(), so we do it here instead + // https://github.com/facebook/jest/blob/cd8828f7bbec6e55b4df5e41e853a5133c4a3ee1/packages/jest-cli/bin/jest.js#L12 + if (!process.env.NODE_ENV) { + (process.env as any).NODE_ENV = 'test'; + } + // eslint-disable-next-line jest/no-jest-import await require('jest').run(args); }; From f234f17853b5421bfb77b7b11d3950d523fae8f0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 31 Aug 2020 16:44:36 +0200 Subject: [PATCH 237/359] cli: fix version management in create-app --- packages/cli/src/commands/create-plugin/createPlugin.ts | 7 ++++++- packages/cli/src/commands/plugin/diff.ts | 5 ++++- packages/cli/templates/default-plugin/package.json.hbs | 8 ++++---- .../templates/default-app/{lerna.json.hbs => lerna.json} | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) rename packages/create-app/templates/default-app/{lerna.json.hbs => lerna.json} (77%) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 47b883882b..5e6882a4a0 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -28,6 +28,7 @@ import { } from '../../lib/codeowners'; import { paths } from '../../lib/paths'; import { Task, templatingTask } from '../../lib/tasks'; +import { version as backstageVersion } from '../../lib/version'; const exec = promisify(execCb); @@ -239,7 +240,11 @@ export default async () => { await createTemporaryPluginFolder(tempDir); Task.section('Preparing files'); - await templatingTask(templateDir, tempDir, { ...answers, version }); + await templatingTask(templateDir, tempDir, { + ...answers, + version, + backstageVersion, + }); Task.section('Moving to final location'); await movePlugin(tempDir, pluginDir, answers.id); diff --git a/packages/cli/src/commands/plugin/diff.ts b/packages/cli/src/commands/plugin/diff.ts index 66db692ab4..99af093f2c 100644 --- a/packages/cli/src/commands/plugin/diff.ts +++ b/packages/cli/src/commands/plugin/diff.ts @@ -25,7 +25,7 @@ import { yesPromptFunc, } from '../../lib/diff'; import { paths } from '../../lib/paths'; -import { version } from '../../lib/version'; +import { version as backstageVersion } from '../../lib/version'; export type PluginData = { id: string; @@ -62,9 +62,12 @@ export default async (cmd: Command) => { promptFunc = yesPromptFunc; } + const { version } = await fs.readJson(paths.resolveTargetRoot('lerna.json')); + const data = await readPluginData(); const templateFiles = await diffTemplateFiles('default-plugin', { version, + backstageVersion, ...data, }); await handleAllFiles(fileHandlers, templateFiles, promptFunc); diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 4e85bf39bd..38d4e41b5b 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^{{version}}", - "@backstage/theme": "^{{version}}", + "@backstage/core": "^{{backstageVersion}}", + "@backstage/theme": "^{{backstageVersion}}", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -31,8 +31,8 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^{{version}}", - "@backstage/dev-utils": "^{{version}}", + "@backstage/cli": "^{{backstageVersion}}", + "@backstage/dev-utils": "^{{backstageVersion}}", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/packages/create-app/templates/default-app/lerna.json.hbs b/packages/create-app/templates/default-app/lerna.json similarity index 77% rename from packages/create-app/templates/default-app/lerna.json.hbs rename to packages/create-app/templates/default-app/lerna.json index ba627eaf80..322929db1d 100644 --- a/packages/create-app/templates/default-app/lerna.json.hbs +++ b/packages/create-app/templates/default-app/lerna.json @@ -2,5 +2,5 @@ "packages": ["packages/*", "plugins/*"], "npmClient": "yarn", "useWorkspaces": true, - "version": "{{version}}" + "version": "0.1.0" } From 9a99a92f8462c8ac7fc27eb09fe352918a106de6 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 1 Sep 2020 15:53:12 +0200 Subject: [PATCH 238/359] fix: e2e hanging --- packages/e2e-test/src/e2e-test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/e2e-test/src/e2e-test.ts b/packages/e2e-test/src/e2e-test.ts index 4942a5abf5..f74c3f7166 100644 --- a/packages/e2e-test/src/e2e-test.ts +++ b/packages/e2e-test/src/e2e-test.ts @@ -63,6 +63,9 @@ async function main() { print('All tests successful, removing test dir'); await fs.remove(rootDir); + + // Just in case some child process was left hanging + process.exit(0); } /** From eee4283c65274db86ee2a47b93f6fb4d9246671f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 17:32:37 +0200 Subject: [PATCH 239/359] catalog-backend: add Locations to the set of default allowed kinds --- .../features/software-catalog/configuration.md | 18 +++++++++--------- .../src/ingestion/CatalogRules.test.ts | 17 ++++++++++++++++- .../src/ingestion/CatalogRules.ts | 2 +- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md index 44e998d5a9..be6314e494 100644 --- a/docs/features/software-catalog/configuration.md +++ b/docs/features/software-catalog/configuration.md @@ -23,16 +23,16 @@ configuration. ## Catalog Rules By default the catalog will only allow ingestion of entities with the kind -`Component` and `API`. In order to allow entities of other kinds to be added, -you need to add rules to the catalog. Rules are added either in a separate -`catalog.rules` key, or added to statically configured locations. +`Component`, `API` and `Location`. In order to allow entities of other kinds to +be added, you need to add rules to the catalog. Rules are added either in a +separate `catalog.rules` key, or added to statically configured locations. For example, given the following configuration: ```yaml catalog: rules: - - allow: [Component, API, Template] + - allow: [Component, API, Location, Template] locations: - type: github @@ -41,13 +41,13 @@ catalog: - allow: [Group] ``` -We are able to add entities of kind `Component`, `API`, or `Template` from any -location, and `Group` entities from the `org-data.yaml`, which will also be read -as statically configured location. +We are able to add entities of kind `Component`, `API`, `Location`, or +`Template` from any location, and `Group` entities from the `org-data.yaml`, +which will also be read as statically configured location. Note that if the `catalog.rules` key is present it will replace the default -value, meaning that you need to add rules for `Component` and `API` kinds if you -want those to be allowed. +value, meaning that you need to add rules for the default kinds if you want +those to still be allowed. The following configuration will reject any kind of entities from being added to the catalog: diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts index 794416786c..bb5c025c28 100644 --- a/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.test.ts @@ -28,6 +28,9 @@ const entity = { component: { kind: 'component', } as Entity, + location: { + kind: 'Location', + } as Entity, }; const location: Record = { @@ -51,6 +54,7 @@ describe('CatalogRulesEnforcer', () => { expect(enforcer.isAllowed(entity.user, location.x)).toBe(false); expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); + expect(enforcer.isAllowed(entity.location, location.z)).toBe(false); }); it('should deny all', () => { @@ -58,15 +62,21 @@ describe('CatalogRulesEnforcer', () => { expect(enforcer.isAllowed(entity.user, location.x)).toBe(false); expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); + expect(enforcer.isAllowed(entity.location, location.z)).toBe(false); }); it('should allow all', () => { const enforcer = new CatalogRulesEnforcer([ - { allow: [{ kind: 'User' }, { kind: 'Group' }, { kind: 'Component' }] }, + { + allow: ['User', 'Group', 'Component', 'Location'].map(kind => ({ + kind, + })), + }, ]); expect(enforcer.isAllowed(entity.user, location.x)).toBe(true); expect(enforcer.isAllowed(entity.group, location.y)).toBe(true); expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + expect(enforcer.isAllowed(entity.location, location.z)).toBe(true); }); it('should deny groups', () => { @@ -121,6 +131,7 @@ describe('CatalogRulesEnforcer', () => { expect(enforcer.isAllowed(entity.user, location.x)).toBe(false); expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + expect(enforcer.isAllowed(entity.location, location.z)).toBe(true); }); it('should deny all', () => { @@ -130,6 +141,7 @@ describe('CatalogRulesEnforcer', () => { expect(enforcer.isAllowed(entity.user, location.x)).toBe(false); expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); + expect(enforcer.isAllowed(entity.location, location.z)).toBe(false); }); it('should allow all', () => { @@ -156,6 +168,7 @@ describe('CatalogRulesEnforcer', () => { expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); expect(enforcer.isAllowed(entity.group, location.z)).toBe(false); expect(enforcer.isAllowed(entity.component, location.z)).toBe(true); + expect(enforcer.isAllowed(entity.location, location.z)).toBe(false); }); it('should allow groups from a specific github location', () => { @@ -182,6 +195,7 @@ describe('CatalogRulesEnforcer', () => { expect(enforcer.isAllowed(entity.group, location.y)).toBe(false); expect(enforcer.isAllowed(entity.group, location.z)).toBe(false); expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); + expect(enforcer.isAllowed(entity.location, location.z)).toBe(false); }); it('should not care about location configuration in catalog.rules', () => { @@ -197,6 +211,7 @@ describe('CatalogRulesEnforcer', () => { expect(enforcer.isAllowed(entity.group, location.y)).toBe(true); expect(enforcer.isAllowed(entity.group, location.z)).toBe(true); expect(enforcer.isAllowed(entity.component, location.z)).toBe(false); + expect(enforcer.isAllowed(entity.location, location.z)).toBe(false); }); }); }); diff --git a/plugins/catalog-backend/src/ingestion/CatalogRules.ts b/plugins/catalog-backend/src/ingestion/CatalogRules.ts index 2b55ebef32..e964524bc7 100644 --- a/plugins/catalog-backend/src/ingestion/CatalogRules.ts +++ b/plugins/catalog-backend/src/ingestion/CatalogRules.ts @@ -50,7 +50,7 @@ export class CatalogRulesEnforcer { */ static readonly defaultRules: CatalogRule[] = [ { - allow: [{ kind: 'Component' }, { kind: 'API' }], + allow: ['Component', 'API', 'Location'].map(kind => ({ kind })), }, ]; From c8e77cfc45c69bae3b8dd91bb153b0654f44042c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 18:28:39 +0200 Subject: [PATCH 240/359] app: mark as bundled --- packages/app/package.json | 1 + .../templates/default-app/packages/app/package.json.hbs | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/app/package.json b/packages/app/package.json index d32808b48b..6d9595ad1b 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -2,6 +2,7 @@ "name": "example-app", "version": "0.1.1-alpha.20", "private": true, + "bundled": true, "dependencies": { "@backstage/cli": "^0.1.1-alpha.20", "@backstage/core": "^0.1.1-alpha.20", diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs index 7d34a94c7b..ecdc8a7f96 100644 --- a/packages/create-app/templates/default-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs @@ -2,6 +2,7 @@ "name": "app", "version": "0.0.0", "private": true, + "bundled": true, "dependencies": { "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", From c319de51a7373ae7d057465d41ff22c321aaa82b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 18:58:33 +0200 Subject: [PATCH 241/359] backend-common: add resolvePackagePath --- packages/backend-common/src/index.ts | 1 + packages/backend-common/src/paths.ts | 36 +++++++++++++++++++ plugins/app-backend/src/service/router.ts | 13 +++---- .../src/identity/DatabaseKeyStore.ts | 8 ++--- .../src/database/DatabaseManager.ts | 9 +++-- plugins/graphql/src/service/router.ts | 9 +++-- 6 files changed, 53 insertions(+), 23 deletions(-) create mode 100644 packages/backend-common/src/paths.ts diff --git a/packages/backend-common/src/index.ts b/packages/backend-common/src/index.ts index f499613851..c527f5fea1 100644 --- a/packages/backend-common/src/index.ts +++ b/packages/backend-common/src/index.ts @@ -20,4 +20,5 @@ export * from './errors'; export * from './logging'; export * from './middleware'; export * from './service'; +export * from './paths'; export * from './hot'; diff --git a/packages/backend-common/src/paths.ts b/packages/backend-common/src/paths.ts new file mode 100644 index 0000000000..402c0e2252 --- /dev/null +++ b/packages/backend-common/src/paths.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2020 Spotify AB + * + * 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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import { resolve as resolvePath } from 'path'; + +/** + * Resolve a path relative to the root of a package directory. + * Additional path arguments are resolved relative to the package dir. + * + * This is particularly useful when you want to access assets shipped with + * your backend plugin package. When doing so, do not forget to include the assets + * in your published package by adding them to `files` in your `package.json`. + */ +export function resolvePackagePath(name: string, ...paths: string[]) { + const req = + typeof __non_webpack_require__ === 'undefined' + ? require + : __non_webpack_require__; + + return resolvePath(req.resolve(`${name}/package.json`), '..', ...paths); +} diff --git a/plugins/app-backend/src/service/router.ts b/plugins/app-backend/src/service/router.ts index ecc5b4fa4b..c9a06e4dda 100644 --- a/plugins/app-backend/src/service/router.ts +++ b/plugins/app-backend/src/service/router.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { resolve as resolvePath, dirname } from 'path'; -import { notFoundHandler } from '@backstage/backend-common'; +import { resolve as resolvePath } from 'path'; +import { notFoundHandler, resolvePackagePath } from '@backstage/backend-common'; import express from 'express'; import Router from 'express-promise-router'; import { Logger } from 'winston'; @@ -23,19 +23,14 @@ import { injectEnvConfig } from '../lib/config'; export interface RouterOptions { logger: Logger; - appPackageName?: string; + appPackageName: string; staticFallbackHandler?: express.Handler; } export async function createRouter( options: RouterOptions, ): Promise { - const appDistDir = resolvePath( - dirname( - __non_webpack_require__.resolve(`${options.appPackageName}/package.json`), - ), - 'dist', - ); + const appDistDir = resolvePackagePath(options.appPackageName, 'dist'); options.logger.info(`Serving static app content from ${appDistDir}`); await injectEnvConfig({ diff --git a/plugins/auth-backend/src/identity/DatabaseKeyStore.ts b/plugins/auth-backend/src/identity/DatabaseKeyStore.ts index c175cd6e12..c24cac55be 100644 --- a/plugins/auth-backend/src/identity/DatabaseKeyStore.ts +++ b/plugins/auth-backend/src/identity/DatabaseKeyStore.ts @@ -15,13 +15,13 @@ */ import Knex from 'knex'; -import path from 'path'; import { utc } from 'moment'; +import { resolvePackagePath } from '@backstage/backend-common'; import { AnyJWK, KeyStore, StoredKey } from './types'; -const migrationsDir = path.resolve( - require.resolve('@backstage/plugin-auth-backend/package.json'), - '../migrations', +const migrationsDir = resolvePackagePath( + '@backstage/plugin-auth-backend', + 'migrations', ); const TABLE = 'signing_keys'; diff --git a/plugins/catalog-backend/src/database/DatabaseManager.ts b/plugins/catalog-backend/src/database/DatabaseManager.ts index 28751f4830..ce91cc737f 100644 --- a/plugins/catalog-backend/src/database/DatabaseManager.ts +++ b/plugins/catalog-backend/src/database/DatabaseManager.ts @@ -14,17 +14,16 @@ * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; +import { getVoidLogger, resolvePackagePath } from '@backstage/backend-common'; import { makeValidator } from '@backstage/catalog-model'; import Knex from 'knex'; -import path from 'path'; import { Logger } from 'winston'; import { CommonDatabase } from './CommonDatabase'; import { Database } from './types'; -const migrationsDir = path.resolve( - require.resolve('@backstage/plugin-catalog-backend/package.json'), - '../migrations', +const migrationsDir = resolvePackagePath( + '@backstage/plugin-catalog-backend', + 'migrations', ); export type CreateDatabaseOptions = { diff --git a/plugins/graphql/src/service/router.ts b/plugins/graphql/src/service/router.ts index 0b91d7307a..df5185c1b1 100644 --- a/plugins/graphql/src/service/router.ts +++ b/plugins/graphql/src/service/router.ts @@ -14,17 +14,16 @@ * limitations under the License. */ -import { errorHandler } from '@backstage/backend-common'; +import { errorHandler, resolvePackagePath } from '@backstage/backend-common'; import express from 'express'; import Router from 'express-promise-router'; import { Logger } from 'winston'; import fs from 'fs'; -import path from 'path'; import { ApolloServer } from 'apollo-server-express'; -const schemaPath = path.resolve( - require.resolve('@backstage/plugin-graphql-backend/package.json'), - '../schema.gql', +const schemaPath = resolvePackagePath( + '@backstage/plugin-graphql-backend', + 'schema.gql', ); export interface RouterOptions { From 39b087bd0e6728260a545c47b2e804dd5cfeb752 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 19:51:02 +0200 Subject: [PATCH 242/359] cli: remove dependencies from bundled packages to avoid install --- packages/cli/src/lib/packager/index.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/cli/src/lib/packager/index.ts b/packages/cli/src/lib/packager/index.ts index d65d1af304..fda85dd68a 100644 --- a/packages/cli/src/lib/packager/index.ts +++ b/packages/cli/src/lib/packager/index.ts @@ -26,6 +26,7 @@ type LernaPackage = { private: boolean; location: string; scripts: Record; + get(key: string): any; }; type FileEntry = @@ -107,6 +108,26 @@ async function moveToDistWorkspace( strip: 1, }); await fs.remove(archivePath); + + // We remove the dependencies from package.json of packages that are marked + // as bundled, so that yarn doesn't try to install them. + if (target.get('bundled')) { + const pkgJson = await fs.readJson( + resolvePath(absoluteOutputPath, 'package.json'), + ); + delete pkgJson.dependencies; + delete pkgJson.devDependencies; + delete pkgJson.peerDependencies; + delete pkgJson.optionalDependencies; + + await fs.writeJson( + resolvePath(absoluteOutputPath, 'package.json'), + pkgJson, + { + spaces: 2, + }, + ); + } }), ); } From 8e94bffbcc9f2f962782c9ea507f96982f36eb77 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 19:56:16 +0200 Subject: [PATCH 243/359] cli: mark all monorepo packages as external, instead of @backstage ones --- packages/cli/src/lib/bundler/backend.ts | 2 +- packages/cli/src/lib/bundler/config.ts | 23 +++++++++++++++-------- packages/cli/src/lib/bundler/paths.ts | 1 - 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/lib/bundler/backend.ts b/packages/cli/src/lib/bundler/backend.ts index f5f233854f..e3cc8af8d1 100644 --- a/packages/cli/src/lib/bundler/backend.ts +++ b/packages/cli/src/lib/bundler/backend.ts @@ -25,7 +25,7 @@ export async function serveBackend( }, ) { const paths = resolveBundlingPaths(options); - const config = createBackendConfig(paths, { + const config = await createBackendConfig(paths, { ...options, isDev: true, }); diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 61f4448dd9..118cb1b40c 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -15,6 +15,7 @@ */ import fs from 'fs-extra'; +import { resolve as resolvePath } from 'path'; import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin'; import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin'; @@ -171,14 +172,23 @@ export async function createConfig( }; } -export function createBackendConfig( +export async function createBackendConfig( paths: BundlingPaths, options: BackendBundlingOptions, -): webpack.Configuration { +): Promise { const { checksEnabled, isDev } = options; const { loaders } = transforms(options); + // Find all local monorepo packages and their node_modules, and mark them as external. + const LernaProject = require('@lerna/project'); + const project = new LernaProject(cliPaths.targetDir); + const packages = await project.getPackages(); + const localPackageNames = packages.map((p: any) => p.name); + const moduleDirs = packages.map((p: any) => + resolvePath(p.location, 'node_modules'), + ); + return { mode: isDev ? 'development' : 'production', profile: false, @@ -193,11 +203,8 @@ export function createBackendConfig( externals: [ nodeExternals({ modulesDir: paths.rootNodeModules, - allowlist: ['webpack/hot/poll?100', /\@backstage\/.*/], - }), - nodeExternals({ - modulesDir: paths.targetNodeModules, - allowlist: ['webpack/hot/poll?100', /\@backstage\/.*/], + additionalModuleDirs: moduleDirs, + allowlist: ['webpack/hot/poll?100', ...localPackageNames], }), ], target: 'node' as const, @@ -219,7 +226,7 @@ export function createBackendConfig( resolve: { extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'], mainFields: ['browser', 'module', 'main'], - modules: [paths.targetNodeModules, paths.rootNodeModules], + modules: [paths.rootNodeModules, ...moduleDirs], plugins: [ new ModuleScopePlugin( [paths.targetSrc, paths.targetDev], diff --git a/packages/cli/src/lib/bundler/paths.ts b/packages/cli/src/lib/bundler/paths.ts index 70e82d8d48..390e41a345 100644 --- a/packages/cli/src/lib/bundler/paths.ts +++ b/packages/cli/src/lib/bundler/paths.ts @@ -63,7 +63,6 @@ export function resolveBundlingPaths(options: BundlingPathsOptions) { targetDev: paths.resolveTarget('dev'), targetEntry: resolveTargetModule(entry), targetTsConfig: paths.resolveTargetRoot('tsconfig.json'), - targetNodeModules: paths.resolveTarget('node_modules'), targetPackageJson: paths.resolveTarget('package.json'), rootNodeModules: paths.resolveTargetRoot('node_modules'), root: paths.targetRoot, From 86124cc9e362b951d69262484a824e8288da0cae Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 07:49:28 +0200 Subject: [PATCH 244/359] chore(deps-dev): bump @types/helmet from 0.0.47 to 0.0.48 (#2230) Bumps [@types/helmet](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/helmet) from 0.0.47 to 0.0.48. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/helmet) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- packages/backend/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 7393ede81e..a2a9e41a92 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -46,6 +46,6 @@ "@types/dockerode": "^2.5.32", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5", - "@types/helmet": "^0.0.47" + "@types/helmet": "^0.0.48" } } diff --git a/yarn.lock b/yarn.lock index 84e4c4cbb4..1553a3e006 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4264,10 +4264,10 @@ "@types/koa" "*" graphql "^14.5.3" -"@types/helmet@^0.0.47": - version "0.0.47" - resolved "https://registry.npmjs.org/@types/helmet/-/helmet-0.0.47.tgz#ec5161541b649142205b7c558bca14801c5ce129" - integrity sha512-TcHA/djjdUtrMtq/QAayVLrsgjNNZ1Uhtz0KhfH01mrmjH44E54DA1A0HNbwW0H/NBFqV+tGMo85ACuEhMXcdg== +"@types/helmet@^0.0.48": + version "0.0.48" + resolved "https://registry.npmjs.org/@types/helmet/-/helmet-0.0.48.tgz#e754399d2f4672ba63962e8490efd3edd31d9799" + integrity sha512-C7MpnvSDrunS1q2Oy1VWCY7CDWHozqSnM8P4tFeRTuzwqni+PYOjEredwcqWG+kLpYcgLsgcY3orHB54gbx2Jw== dependencies: "@types/express" "*" From b3c4f0c85bc47a44d7724f519c3cd247b91b15f2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 07:49:55 +0200 Subject: [PATCH 245/359] chore(deps-dev): bump @testing-library/cypress from 6.0.0 to 6.0.1 (#2229) Bumps [@testing-library/cypress](https://github.com/kentcdodds/cypress-testing-library) from 6.0.0 to 6.0.1. - [Release notes](https://github.com/kentcdodds/cypress-testing-library/releases) - [Changelog](https://github.com/testing-library/cypress-testing-library/blob/master/CHANGELOG.md) - [Commits](https://github.com/kentcdodds/cypress-testing-library/compare/v6.0.0...v6.0.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 115 +++++++++++++++++++++++++++--------------------------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1553a3e006..1853507e63 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1056,7 +1056,7 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.10.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.11.2" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== @@ -1677,15 +1677,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@^24.9.0": - version "24.9.0" - resolved "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" - integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^13.0.0" - "@jest/types@^25.5.0": version "25.5.0" resolved "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d" @@ -1706,6 +1697,17 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@jest/types@^26.3.0": + version "26.3.0" + resolved "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz#97627bf4bdb72c55346eef98e3b3f7ddc4941f71" + integrity sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + "@jsdevtools/ono@^7.1.3": version "7.1.3" resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" @@ -3853,23 +3855,24 @@ defer-to-connect "^2.0.0" "@testing-library/cypress@^6.0.0": - version "6.0.0" - resolved "https://registry.npmjs.org/@testing-library/cypress/-/cypress-6.0.0.tgz#935f7716e0e495f02fd753a42621e4d350097dce" - integrity sha512-vWPQtPsIDk5STOH2XdJbJoYq9gxOSAItP0ail+MlylK230zNkf3ODKd6eqWnDdruuqrhTF3CyqvPNMA8Xks/UQ== + version "6.0.1" + resolved "https://registry.npmjs.org/@testing-library/cypress/-/cypress-6.0.1.tgz#c924a69617db6a403c498abcb63759b79318fc76" + integrity sha512-hcPu2OnVuSTX1yDubEe7TBRD6mP2VgyopP1WTBIrJP79INPZdgOAX+TMNH68uZ/r5b4C7100IB4hjPIEQ+/Xog== dependencies: - "@babel/runtime" "^7.8.7" - "@testing-library/dom" "^7.0.2" - "@types/testing-library__cypress" "^5.0.3" + "@babel/runtime" "^7.11.2" + "@testing-library/dom" "^7.22.2" + "@types/testing-library__cypress" "^5.0.6" -"@testing-library/dom@^7.0.2", "@testing-library/dom@^7.17.1": - version "7.17.2" - resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-7.17.2.tgz#d062e41336b885107bca8ffc7022eaf37cccaee1" - integrity sha512-TQAoIzoI9g64oNVJ+13i4cCh/DQp/n7fJOyMqlFB1oQVusPtSgiPImyb52CwtvPO7J0Im0+/4YcmxU9a+cVxxw== +"@testing-library/dom@^7.11.0", "@testing-library/dom@^7.17.1", "@testing-library/dom@^7.22.2": + version "7.23.0" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-7.23.0.tgz#c54c0fa53705ad867bcefb52fc0c96487fbc10f6" + integrity sha512-H5m090auYH+obdZmsaYLrSWC5OauWD2CvNbz88KBxQJoXgkJzbU0DpAG8BS7Evj5WqCC3nAAKrLS6vw0ljUYLg== dependencies: "@babel/runtime" "^7.10.3" + "@types/aria-query" "^4.2.0" aria-query "^4.2.2" - dom-accessibility-api "^0.4.5" - pretty-format "^25.5.0" + dom-accessibility-api "^0.5.1" + pretty-format "^26.4.2" "@testing-library/jest-dom@^5.10.1": version "5.10.1" @@ -3976,6 +3979,11 @@ resolved "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== +"@types/aria-query@^4.2.0": + version "4.2.0" + resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0" + integrity sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A== + "@types/babel__core@^7.1.7": version "7.1.7" resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" @@ -4363,6 +4371,13 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" +"@types/istanbul-reports@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" + integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + dependencies: + "@types/istanbul-lib-report" "*" + "@types/jest@*", "@types/jest@^26.0.7": version "26.0.9" resolved "https://registry.npmjs.org/@types/jest/-/jest-26.0.9.tgz#0543b57da5f0cd949c5f423a00c56c492289c989" @@ -4848,21 +4863,14 @@ dependencies: "@types/estree" "*" -"@types/testing-library__cypress@^5.0.3": - version "5.0.3" - resolved "https://registry.npmjs.org/@types/testing-library__cypress/-/testing-library__cypress-5.0.3.tgz#94969b7c1eea96e09d8e023a1d225590fa75a1fe" - integrity sha512-efMwaVnsWEBDz0Ikm84Kh/oiUxMfz5LQtWx/Y6s2Bj0KkQ6+569/101X3Gz2bJy+otaXWPzOUOwDeAfzOJWoeQ== +"@types/testing-library__cypress@^5.0.6": + version "5.0.6" + resolved "https://registry.npmjs.org/@types/testing-library__cypress/-/testing-library__cypress-5.0.6.tgz#9015f575c1a98f05996a4fe769071134ee488c26" + integrity sha512-TUp5wfanU7zUZigKqIeQDChnHQ1MEzbYqrI5iCQMFiesWNOASWm/el1lFBh1JPqmd6GkdDdDiHYJnkqd9le2ww== dependencies: - "@types/testing-library__dom" "*" + "@testing-library/dom" "^7.11.0" cypress "*" -"@types/testing-library__dom@*": - version "6.14.0" - resolved "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-6.14.0.tgz#1aede831cb4ed4a398448df5a2c54b54a365644e" - integrity sha512-sMl7OSv0AvMOqn1UJ6j1unPMIHRXen0Ita1ujnMX912rrOcawe4f7wu0Zt9GIQhBhJvH2BaibqFgQ3lP+Pj2hA== - dependencies: - pretty-format "^24.3.0" - "@types/testing-library__jest-dom@^5.9.1": version "5.9.1" resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.1.tgz#aba5ee062b7880f69c212ef769389f30752806e5" @@ -4971,13 +4979,6 @@ resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== -"@types/yargs@^13.0.0": - version "13.0.8" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz#a38c22def2f1c2068f8971acb3ea734eb3c64a99" - integrity sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA== - dependencies: - "@types/yargs-parser" "*" - "@types/yargs@^15.0.0": version "15.0.4" resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz#7e5d0f8ca25e9d5849f2ea443cf7c402decd8299" @@ -5451,7 +5452,7 @@ ansi-regex@^3.0.0: resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-regex@^4.0.0, ansi-regex@^4.1.0: +ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== @@ -9149,10 +9150,10 @@ docusaurus@^2.0.0-alpha.61: tree-node-cli "^1.2.5" truncate-html "^1.0.3" -dom-accessibility-api@^0.4.5: - version "0.4.5" - resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.4.5.tgz#d9c1cefa89f509d8cf132ab5d250004d755e76e3" - integrity sha512-HcPDilI95nKztbVikaN2vzwvmv0sE8Y2ZJFODy/m15n7mGXLeOKGiys9qWVbFbh+aq/KYj2lqMLybBOkYAEXqg== +dom-accessibility-api@^0.5.1: + version "0.5.2" + resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.2.tgz#ef3cdb5d3f0d599d8f9c8b18df2fb63c9793739d" + integrity sha512-k7hRNKAiPJXD2aBqfahSo4/01cTsKWXf+LqJgglnkN2Nz8TsxXKQBXHhKe0Ye9fEfHEZY49uSA5Sr3AqP/sWKA== dom-converter@^0.2: version "0.2.0" @@ -17806,16 +17807,6 @@ pretty-error@^2.1.1: renderkid "^2.0.1" utila "~0.4" -pretty-format@^24.3.0: - version "24.9.0" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" - integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== - dependencies: - "@jest/types" "^24.9.0" - ansi-regex "^4.0.0" - ansi-styles "^3.2.0" - react-is "^16.8.4" - pretty-format@^25.1.0, pretty-format@^25.2.1, pretty-format@^25.5.0: version "25.5.0" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" @@ -17836,6 +17827,16 @@ pretty-format@^26.0.1: ansi-styles "^4.0.0" react-is "^16.12.0" +pretty-format@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz#d081d032b398e801e2012af2df1214ef75a81237" + integrity sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA== + dependencies: + "@jest/types" "^26.3.0" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^16.12.0" + pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -18543,7 +18544,7 @@ react-inspector@^4.0.0: is-dom "^1.0.9" prop-types "^15.6.1" -react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.6, react-is@^16.9.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== From e9527a7796dce17c2600eb61d716f9adc6dca580 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 07:50:19 +0200 Subject: [PATCH 246/359] chore(deps): bump rollup-plugin-postcss from 3.1.1 to 3.1.8 (#2231) Bumps [rollup-plugin-postcss](https://github.com/egoist/rollup-plugin-postcss) from 3.1.1 to 3.1.8. - [Release notes](https://github.com/egoist/rollup-plugin-postcss/releases) - [Changelog](https://github.com/egoist/rollup-plugin-postcss/blob/master/CHANGELOG-OLD.md) - [Commits](https://github.com/egoist/rollup-plugin-postcss/compare/v3.1.1...v3.1.8) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1853507e63..3301f94f83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19513,9 +19513,9 @@ rollup-plugin-peer-deps-external@^2.2.2: integrity sha512-W6IePXTExGXVDAlfZbNUUrx3GxUOZP248u5n4a4ID1XZMrbQ+uGeNiEfapvdzwx0qZi5DNH/hDLiPUP+pzFIxg== rollup-plugin-postcss@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/rollup-plugin-postcss/-/rollup-plugin-postcss-3.1.1.tgz#eb895bd919285aaf6200324071103b4d3ea68607" - integrity sha512-4/FO5/2O5kv2uWRd7PPTN4mBCWoHwwFTnpEGokfPKfj6kygvTORqkBWNgVPXi7bBefNKtMA3FqQ10se6/J8kKw== + version "3.1.8" + resolved "https://registry.npmjs.org/rollup-plugin-postcss/-/rollup-plugin-postcss-3.1.8.tgz#d1bcaf8eb0fcb0936e3684c22dd8628d13a82fd1" + integrity sha512-JHnGfW8quNc6ePxEkZ05HEZ1YiRxDgY9RKEetMfsrwxR2kh/d90OVScTc6b1c2Q17Cs/5TRYL+1uddG21lQe3w== dependencies: chalk "^4.0.0" concat-with-sourcemaps "^1.1.0" From ab7d6e89a8f2614dfb595283f041bb6a23cec1fb Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 2 Sep 2020 07:52:12 +0200 Subject: [PATCH 247/359] Add "Example Plugin" to Storybook (#2212) * Add Page Story as Plugin Example * Change settings button to primary and clean up code * Simplfiy content header and remove unnecessary styling --- .../core/src/layout/Page/Page.stories.tsx | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 packages/core/src/layout/Page/Page.stories.tsx diff --git a/packages/core/src/layout/Page/Page.stories.tsx b/packages/core/src/layout/Page/Page.stories.tsx new file mode 100644 index 0000000000..786b4740fd --- /dev/null +++ b/packages/core/src/layout/Page/Page.stories.tsx @@ -0,0 +1,114 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { + Header, + Page, + HeaderLabel, + ContentHeader, + Content, + pageTheme, +} from '../'; +import { SupportButton, Table, StatusOK, TableColumn } from '../../components'; +import { Box, Typography, Link, Chip, Button } from '@material-ui/core'; + +export default { + title: 'Example Plugin', + component: Page, +}; + +interface TableData { + id: number; + branch: string; + hash: string; + status: string; +} + +const generateTestData = (rows = 10) => { + const data: Array = []; + while (data.length <= rows) { + data.push({ + id: data.length + 18534, + branch: 'techdocs: modify documentation header', + hash: 'techdocs/docs-header 5749c98e3f61f8bb116e5cb87b0e4e1 ', + status: 'Success', + }); + } + return data; +}; + +const columns: TableColumn[] = [ + { + title: 'ID', + field: 'id', + highlight: true, + type: 'numeric', + width: '80px', + }, + { + title: 'Message/Source', + highlight: true, + render: (row: Partial) => ( + <> + {row.branch} + {row.hash} + + ), + }, + { + title: 'Status', + render: (row: Partial) => ( + + + {row.status} + + ), + }, + { + title: 'Tags', + render: () => , + width: '10%', + }, +]; + +export const PluginWithTable = () => { + return ( + +
+ + +
+ + + + + This Plugin is an example. This text could provide usefull + information for the user. + + +
+
+ + + + {pluginMetadata.map( + ({ + iconUrl, + title, + description, + author, + authorUrl, + documentation, + category, + }) => ( +
+
+ {title} +

{title}

+

+ by {author} +

+ {category} +
+
+

{truncate(description)}

+
+ + + + docs + + + +
+ ), + )} +
+
+
+ + + ); +}; From 7f4064975141b1b61a3d1800f3171f2350cac24f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2020 05:55:52 +0000 Subject: [PATCH 248/359] chore(deps): bump jest from 26.0.1 to 26.4.2 Bumps [jest](https://github.com/facebook/jest) from 26.0.1 to 26.4.2. - [Release notes](https://github.com/facebook/jest/releases) - [Changelog](https://github.com/facebook/jest/blob/master/CHANGELOG.md) - [Commits](https://github.com/facebook/jest/compare/v26.0.1...v26.4.2) Signed-off-by: dependabot-preview[bot] --- yarn.lock | 776 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 406 insertions(+), 370 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3301f94f83..bea32a92aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -536,6 +536,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings@^7.8.0", "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" @@ -1521,156 +1528,160 @@ resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jest/console@^26.0.1": - version "26.0.1" - resolved "https://registry.npmjs.org/@jest/console/-/console-26.0.1.tgz#62b3b2fa8990f3cbffbef695c42ae9ddbc8f4b39" - integrity sha512-9t1KUe/93coV1rBSxMmBAOIK3/HVpwxArCA1CxskKyRiv6o8J70V8C/V3OJminVCTa2M0hQI9AWRd5wxu2dAHw== +"@jest/console@^26.3.0": + version "26.3.0" + resolved "https://registry.npmjs.org/@jest/console/-/console-26.3.0.tgz#ed04063efb280c88ba87388b6f16427c0a85c856" + integrity sha512-/5Pn6sJev0nPUcAdpJHMVIsA8sKizL2ZkcKPE5+dJrCccks7tcM7c9wbgHudBJbxXLoTbqsHkG1Dofoem4F09w== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" + "@types/node" "*" chalk "^4.0.0" - jest-message-util "^26.0.1" - jest-util "^26.0.1" + jest-message-util "^26.3.0" + jest-util "^26.3.0" slash "^3.0.0" -"@jest/core@^26.0.1": - version "26.0.1" - resolved "https://registry.npmjs.org/@jest/core/-/core-26.0.1.tgz#aa538d52497dfab56735efb00e506be83d841fae" - integrity sha512-Xq3eqYnxsG9SjDC+WLeIgf7/8KU6rddBxH+SCt18gEpOhAGYC/Mq+YbtlNcIdwjnnT+wDseXSbU0e5X84Y4jTQ== +"@jest/core@^26.4.2": + version "26.4.2" + resolved "https://registry.npmjs.org/@jest/core/-/core-26.4.2.tgz#85d0894f31ac29b5bab07aa86806d03dd3d33edc" + integrity sha512-sDva7YkeNprxJfepOctzS8cAk9TOekldh+5FhVuXS40+94SHbiicRO1VV2tSoRtgIo+POs/Cdyf8p76vPTd6dg== dependencies: - "@jest/console" "^26.0.1" - "@jest/reporters" "^26.0.1" - "@jest/test-result" "^26.0.1" - "@jest/transform" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/console" "^26.3.0" + "@jest/reporters" "^26.4.1" + "@jest/test-result" "^26.3.0" + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" - jest-changed-files "^26.0.1" - jest-config "^26.0.1" - jest-haste-map "^26.0.1" - jest-message-util "^26.0.1" + jest-changed-files "^26.3.0" + jest-config "^26.4.2" + jest-haste-map "^26.3.0" + jest-message-util "^26.3.0" jest-regex-util "^26.0.0" - jest-resolve "^26.0.1" - jest-resolve-dependencies "^26.0.1" - jest-runner "^26.0.1" - jest-runtime "^26.0.1" - jest-snapshot "^26.0.1" - jest-util "^26.0.1" - jest-validate "^26.0.1" - jest-watcher "^26.0.1" + jest-resolve "^26.4.0" + jest-resolve-dependencies "^26.4.2" + jest-runner "^26.4.2" + jest-runtime "^26.4.2" + jest-snapshot "^26.4.2" + jest-util "^26.3.0" + jest-validate "^26.4.2" + jest-watcher "^26.3.0" micromatch "^4.0.2" p-each-series "^2.1.0" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^26.0.1": - version "26.0.1" - resolved "https://registry.npmjs.org/@jest/environment/-/environment-26.0.1.tgz#82f519bba71959be9b483675ee89de8c8f72a5c8" - integrity sha512-xBDxPe8/nx251u0VJ2dFAFz2H23Y98qdIaNwnMK6dFQr05jc+Ne/2np73lOAx+5mSBO/yuQldRrQOf6hP1h92g== +"@jest/environment@^26.3.0": + version "26.3.0" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-26.3.0.tgz#e6953ab711ae3e44754a025f838bde1a7fd236a0" + integrity sha512-EW+MFEo0DGHahf83RAaiqQx688qpXgl99wdb8Fy67ybyzHwR1a58LHcO376xQJHfmoXTu89M09dH3J509cx2AA== dependencies: - "@jest/fake-timers" "^26.0.1" - "@jest/types" "^26.0.1" - jest-mock "^26.0.1" + "@jest/fake-timers" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + jest-mock "^26.3.0" -"@jest/fake-timers@^26.0.1": - version "26.0.1" - resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.0.1.tgz#f7aeff13b9f387e9d0cac9a8de3bba538d19d796" - integrity sha512-Oj/kCBnTKhm7CR+OJSjZty6N1bRDr9pgiYQr4wY221azLz5PHi08x/U+9+QpceAYOWheauLP8MhtSVFrqXQfhg== +"@jest/fake-timers@^26.3.0": + version "26.3.0" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.3.0.tgz#f515d4667a6770f60ae06ae050f4e001126c666a" + integrity sha512-ZL9ytUiRwVP8ujfRepffokBvD2KbxbqMhrXSBhSdAhISCw3gOkuntisiSFv+A6HN0n0fF4cxzICEKZENLmW+1A== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" "@sinonjs/fake-timers" "^6.0.1" - jest-message-util "^26.0.1" - jest-mock "^26.0.1" - jest-util "^26.0.1" + "@types/node" "*" + jest-message-util "^26.3.0" + jest-mock "^26.3.0" + jest-util "^26.3.0" -"@jest/globals@^26.0.1": - version "26.0.1" - resolved "https://registry.npmjs.org/@jest/globals/-/globals-26.0.1.tgz#3f67b508a7ce62b6e6efc536f3d18ec9deb19a9c" - integrity sha512-iuucxOYB7BRCvT+TYBzUqUNuxFX1hqaR6G6IcGgEqkJ5x4htNKo1r7jk1ji9Zj8ZMiMw0oB5NaA7k5Tx6MVssA== +"@jest/globals@^26.4.2": + version "26.4.2" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-26.4.2.tgz#73c2a862ac691d998889a241beb3dc9cada40d4a" + integrity sha512-Ot5ouAlehhHLRhc+sDz2/9bmNv9p5ZWZ9LE1pXGGTCXBasmi5jnYjlgYcYt03FBwLmZXCZ7GrL29c33/XRQiow== dependencies: - "@jest/environment" "^26.0.1" - "@jest/types" "^26.0.1" - expect "^26.0.1" + "@jest/environment" "^26.3.0" + "@jest/types" "^26.3.0" + expect "^26.4.2" -"@jest/reporters@^26.0.1": - version "26.0.1" - resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-26.0.1.tgz#14ae00e7a93e498cec35b0c00ab21c375d9b078f" - integrity sha512-NWWy9KwRtE1iyG/m7huiFVF9YsYv/e+mbflKRV84WDoJfBqUrNRyDbL/vFxQcYLl8IRqI4P3MgPn386x76Gf2g== +"@jest/reporters@^26.4.1": + version "26.4.1" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-26.4.1.tgz#3b4d6faf28650f3965f8b97bc3d114077fb71795" + integrity sha512-aROTkCLU8++yiRGVxLsuDmZsQEKO6LprlrxtAuzvtpbIFl3eIjgIf3EUxDKgomkS25R9ZzwGEdB5weCcBZlrpQ== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^26.0.1" - "@jest/test-result" "^26.0.1" - "@jest/transform" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/console" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" graceful-fs "^4.2.4" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.0" + istanbul-lib-instrument "^4.0.3" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.0.2" - jest-haste-map "^26.0.1" - jest-resolve "^26.0.1" - jest-util "^26.0.1" - jest-worker "^26.0.0" + jest-haste-map "^26.3.0" + jest-resolve "^26.4.0" + jest-util "^26.3.0" + jest-worker "^26.3.0" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" terminal-link "^2.0.0" - v8-to-istanbul "^4.1.3" + v8-to-istanbul "^5.0.1" optionalDependencies: - node-notifier "^7.0.0" + node-notifier "^8.0.0" -"@jest/source-map@^26.0.0": - version "26.0.0" - resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-26.0.0.tgz#fd7706484a7d3faf7792ae29783933bbf48a4749" - integrity sha512-S2Z+Aj/7KOSU2TfW0dyzBze7xr95bkm5YXNUqqCek+HE0VbNNSNzrRwfIi5lf7wvzDTSS0/ib8XQ1krFNyYgbQ== +"@jest/source-map@^26.3.0": + version "26.3.0" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-26.3.0.tgz#0e646e519883c14c551f7b5ae4ff5f1bfe4fc3d9" + integrity sha512-hWX5IHmMDWe1kyrKl7IhFwqOuAreIwHhbe44+XH2ZRHjrKIh0LO5eLQ/vxHFeAfRwJapmxuqlGAEYLadDq6ZGQ== dependencies: callsites "^3.0.0" graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@^26.0.1": - version "26.0.1" - resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-26.0.1.tgz#1ffdc1ba4bc289919e54b9414b74c9c2f7b2b718" - integrity sha512-oKwHvOI73ICSYRPe8WwyYPTtiuOAkLSbY8/MfWF3qDEd/sa8EDyZzin3BaXTqufir/O/Gzea4E8Zl14XU4Mlyg== +"@jest/test-result@^26.3.0": + version "26.3.0" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-26.3.0.tgz#46cde01fa10c0aaeb7431bf71e4a20d885bc7fdb" + integrity sha512-a8rbLqzW/q7HWheFVMtghXV79Xk+GWwOK1FrtimpI5n1la2SY0qHri3/b0/1F0Ve0/yJmV8pEhxDfVwiUBGtgg== dependencies: - "@jest/console" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/console" "^26.3.0" + "@jest/types" "^26.3.0" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^26.0.1": - version "26.0.1" - resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.0.1.tgz#b0563424728f3fe9e75d1442b9ae4c11da73f090" - integrity sha512-ssga8XlwfP8YjbDcmVhwNlrmblddMfgUeAkWIXts1V22equp2GMIHxm7cyeD5Q/B0ZgKPK/tngt45sH99yLLGg== +"@jest/test-sequencer@^26.4.2": + version "26.4.2" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.4.2.tgz#58a3760a61eec758a2ce6080201424580d97cbba" + integrity sha512-83DRD8N3M0tOhz9h0bn6Kl6dSp+US6DazuVF8J9m21WAp5x7CqSMaNycMP0aemC/SH/pDQQddbsfHRTBXVUgog== dependencies: - "@jest/test-result" "^26.0.1" + "@jest/test-result" "^26.3.0" graceful-fs "^4.2.4" - jest-haste-map "^26.0.1" - jest-runner "^26.0.1" - jest-runtime "^26.0.1" + jest-haste-map "^26.3.0" + jest-runner "^26.4.2" + jest-runtime "^26.4.2" -"@jest/transform@^26.0.1": - version "26.0.1" - resolved "https://registry.npmjs.org/@jest/transform/-/transform-26.0.1.tgz#0e3ecbb34a11cd4b2080ed0a9c4856cf0ceb0639" - integrity sha512-pPRkVkAQ91drKGbzCfDOoHN838+FSbYaEAvBXvKuWeeRRUD8FjwXkqfUNUZL6Ke48aA/1cqq/Ni7kVMCoqagWA== +"@jest/transform@^26.3.0": + version "26.3.0" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-26.3.0.tgz#c393e0e01459da8a8bfc6d2a7c2ece1a13e8ba55" + integrity sha512-Isj6NB68QorGoFWvcOjlUhpkT56PqNIsXKR7XfvoDlCANn/IANlh8DrKAA2l2JKC3yWSMH5wS0GwuQM20w3b2A== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" babel-plugin-istanbul "^6.0.0" chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.2.4" - jest-haste-map "^26.0.1" + jest-haste-map "^26.3.0" jest-regex-util "^26.0.0" - jest-util "^26.0.1" + jest-util "^26.3.0" micromatch "^4.0.2" pirates "^4.0.1" slash "^3.0.0" @@ -1687,16 +1698,6 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" -"@jest/types@^26.0.1": - version "26.0.1" - resolved "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz#b78333fbd113fa7aec8d39de24f88de8686dac67" - integrity sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - "@jest/types@^26.3.0": version "26.3.0" resolved "https://registry.npmjs.org/@jest/types/-/types-26.3.0.tgz#97627bf4bdb72c55346eef98e3b3f7ddc4941f71" @@ -3984,6 +3985,17 @@ resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0" integrity sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A== +"@types/babel__core@^7.0.0": + version "7.1.9" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d" + integrity sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + "@types/babel__core@^7.1.7": version "7.1.7" resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" @@ -6088,16 +6100,16 @@ babel-helper-to-multiple-sequence-expressions@^0.5.0: resolved "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz#a3f924e3561882d42fcf48907aa98f7979a4588d" integrity sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA== -babel-jest@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-26.0.1.tgz#450139ce4b6c17174b136425bda91885c397bc46" - integrity sha512-Z4GGmSNQ8pX3WS1O+6v3fo41YItJJZsVxG5gIQ+HuB/iuAQBJxMTHTwz292vuYws1LnHfwSRgoqI+nxdy/pcvw== +babel-jest@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-26.3.0.tgz#10d0ca4b529ca3e7d1417855ef7d7bd6fc0c3463" + integrity sha512-sxPnQGEyHAOPF8NcUsD0g7hDCnvLL2XyblRBcgrzTWBB/mAIpWow3n1bEL+VghnnZfreLhFSBsFluRoK2tRK4g== dependencies: - "@jest/transform" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" "@types/babel__core" "^7.1.7" babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^26.0.0" + babel-preset-jest "^26.3.0" chalk "^4.0.0" graceful-fs "^4.2.4" slash "^3.0.0" @@ -6141,13 +6153,14 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^26.0.0: - version "26.0.0" - resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.0.0.tgz#fd1d35f95cf8849fc65cb01b5e58aedd710b34a8" - integrity sha512-+AuoehOrjt9irZL7DOt2+4ZaTM6dlu1s5TTS46JBa0/qem4dy7VNW3tMb96qeEqcIh20LD73TVNtmVEeymTG7w== +babel-plugin-jest-hoist@^26.2.0: + version "26.2.0" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.2.0.tgz#bdd0011df0d3d513e5e95f76bd53b51147aca2dd" + integrity sha512-B/hVMRv8Nh1sQ1a3EY8I0n4Y1Wty3NrR5ebOyVT302op+DOAau+xNEImGMsUWOC3++ZlMooCytKz+NgN8aKGbA== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.7.0: @@ -6313,14 +6326,15 @@ babel-plugin-transform-undefined-to-void@^6.9.4: resolved "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz#be241ca81404030678b748717322b89d0c8fe280" integrity sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA= -babel-preset-current-node-syntax@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz#fb4a4c51fe38ca60fede1dc74ab35eb843cb41d6" - integrity sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw== +babel-preset-current-node-syntax@^0.1.3: + version "0.1.3" + resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.3.tgz#b4b547acddbf963cba555ba9f9cbbb70bfd044da" + integrity sha512-uyexu1sVwcdFnyq9o8UQYsXwXflIh8LvrF5+cKrYam93ned1CStffB3+BEcsxGSgagoA3GEyjDqO4a/58hyPYQ== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-bigint" "^7.8.3" "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -6329,13 +6343,13 @@ babel-preset-current-node-syntax@^0.1.2: "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -babel-preset-jest@^26.0.0: - version "26.0.0" - resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.0.0.tgz#1eac82f513ad36c4db2e9263d7c485c825b1faa6" - integrity sha512-9ce+DatAa31DpR4Uir8g4Ahxs5K4W4L8refzt+qHWQANb6LhGcAEfIFgLUwk67oya2cCUd6t4eUMtO/z64ocNw== +babel-preset-jest@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.3.0.tgz#ed6344506225c065fd8a0b53e191986f74890776" + integrity sha512-5WPdf7nyYi2/eRxCbVrE1kKCWxgWY4RsPEbdJWFm7QsesFGqjdkyLeu1zRkwM1cxK6EPIlNd6d2AxLk7J+t4pw== dependencies: - babel-plugin-jest-hoist "^26.0.0" - babel-preset-current-node-syntax "^0.1.2" + babel-plugin-jest-hoist "^26.2.0" + babel-preset-current-node-syntax "^0.1.3" "babel-preset-minify@^0.5.0 || 0.6.0-alpha.5": version "0.5.1" @@ -8997,10 +9011,10 @@ diff-sequences@^25.2.6: resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== -diff-sequences@^26.0.0: - version "26.0.0" - resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz#0760059a5c287637b842bd7085311db7060e88a6" - integrity sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg== +diff-sequences@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.3.0.tgz#62a59b1b29ab7fd27cef2a33ae52abe73042d0a2" + integrity sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig== diff@^4.0.1, diff@^4.0.2: version "4.0.2" @@ -9435,6 +9449,11 @@ emitter-component@^1.1.1: resolved "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz#065e2dbed6959bf470679edabeaf7981d1003ab6" integrity sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY= +emittery@^0.7.1: + version "0.7.1" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.7.1.tgz#c02375a927a40948c0345cc903072597f5270451" + integrity sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ== + "emoji-regex@>=6.0.0 <=6.1.1": version "6.1.1" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz#c6cd0ec1b0642e2a3c67a1137efc5e796da4f88e" @@ -10163,16 +10182,16 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/expect/-/expect-26.0.1.tgz#18697b9611a7e2725e20ba3ceadda49bc9865421" - integrity sha512-QcCy4nygHeqmbw564YxNbHTJlXh47dVID2BUP52cZFpLU9zHViMFK6h07cC1wf7GYCTIigTdAXhVua8Yl1FkKg== +expect@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/expect/-/expect-26.4.2.tgz#36db120928a5a2d7d9736643032de32f24e1b2a1" + integrity sha512-IlJ3X52Z0lDHm7gjEp+m76uX46ldH5VpqmU0006vqDju/285twh7zaWMRhs67VpQhBwjjMchk+p5aA0VkERCAA== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" ansi-styles "^4.0.0" - jest-get-type "^26.0.0" - jest-matcher-utils "^26.0.1" - jest-message-util "^26.0.1" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.4.2" + jest-message-util "^26.3.0" jest-regex-util "^26.0.0" express-prom-bundle@^6.1.0: @@ -13173,6 +13192,13 @@ is-wsl@^2.1.1: resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + 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" @@ -13249,6 +13275,16 @@ istanbul-lib-instrument@^4.0.0: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" +istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -13308,57 +13344,57 @@ jenkins@^0.28.0: dependencies: papi "^0.29.0" -jest-changed-files@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.0.1.tgz#1334630c6a1ad75784120f39c3aa9278e59f349f" - integrity sha512-q8LP9Sint17HaE2LjxQXL+oYWW/WeeXMPE2+Op9X3mY8IEGFVc14xRxFjUuXUbcPAlDLhtWdIEt59GdQbn76Hw== +jest-changed-files@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.3.0.tgz#68fb2a7eb125f50839dab1f5a17db3607fe195b1" + integrity sha512-1C4R4nijgPltX6fugKxM4oQ18zimS7LqQ+zTTY8lMCMFPrxqBFb7KJH0Z2fRQJvw2Slbaipsqq7s1mgX5Iot+g== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" execa "^4.0.0" throat "^5.0.0" -jest-cli@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-26.0.1.tgz#3a42399a4cbc96a519b99ad069a117d955570cac" - integrity sha512-pFLfSOBcbG9iOZWaMK4Een+tTxi/Wcm34geqZEqrst9cZDkTQ1LZ2CnBrTlHWuYAiTMFr0EQeK52ScyFU8wK+w== +jest-cli@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-26.4.2.tgz#24afc6e4dfc25cde4c7ec4226fb7db5f157c21da" + integrity sha512-zb+lGd/SfrPvoRSC/0LWdaWCnscXc1mGYW//NP4/tmBvRPT3VntZ2jtKUONsRi59zc5JqmsSajA9ewJKFYp8Cw== dependencies: - "@jest/core" "^26.0.1" - "@jest/test-result" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/core" "^26.4.2" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" import-local "^3.0.2" is-ci "^2.0.0" - jest-config "^26.0.1" - jest-util "^26.0.1" - jest-validate "^26.0.1" + jest-config "^26.4.2" + jest-util "^26.3.0" + jest-validate "^26.4.2" prompts "^2.0.1" yargs "^15.3.1" -jest-config@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-config/-/jest-config-26.0.1.tgz#096a3d4150afadf719d1fab00e9a6fb2d6d67507" - integrity sha512-9mWKx2L1LFgOXlDsC4YSeavnblN6A4CPfXFiobq+YYLaBMymA/SczN7xYTSmLaEYHZOcB98UdoN4m5uNt6tztg== +jest-config@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-26.4.2.tgz#da0cbb7dc2c131ffe831f0f7f2a36256e6086558" + integrity sha512-QBf7YGLuToiM8PmTnJEdRxyYy3mHWLh24LJZKVdXZ2PNdizSe1B/E8bVm+HYcjbEzGuVXDv/di+EzdO/6Gq80A== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^26.0.1" - "@jest/types" "^26.0.1" - babel-jest "^26.0.1" + "@jest/test-sequencer" "^26.4.2" + "@jest/types" "^26.3.0" + babel-jest "^26.3.0" chalk "^4.0.0" deepmerge "^4.2.2" glob "^7.1.1" graceful-fs "^4.2.4" - jest-environment-jsdom "^26.0.1" - jest-environment-node "^26.0.1" - jest-get-type "^26.0.0" - jest-jasmine2 "^26.0.1" + jest-environment-jsdom "^26.3.0" + jest-environment-node "^26.3.0" + jest-get-type "^26.3.0" + jest-jasmine2 "^26.4.2" jest-regex-util "^26.0.0" - jest-resolve "^26.0.1" - jest-util "^26.0.1" - jest-validate "^26.0.1" + jest-resolve "^26.4.0" + jest-util "^26.3.0" + jest-validate "^26.4.2" micromatch "^4.0.2" - pretty-format "^26.0.1" + pretty-format "^26.4.2" jest-css-modules@^2.1.0: version "2.1.0" @@ -13377,15 +13413,15 @@ jest-diff@^25.1.0, jest-diff@^25.2.1: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-diff@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz#c44ab3cdd5977d466de69c46929e0e57f89aa1de" - integrity sha512-odTcHyl5X+U+QsczJmOjWw5tPvww+y9Yim5xzqxVl/R1j4z71+fHW4g8qu1ugMmKdFdxw+AtQgs5mupPnzcIBQ== +jest-diff@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-26.4.2.tgz#a1b7b303bcc534aabdb3bd4a7caf594ac059f5aa" + integrity sha512-6T1XQY8U28WH0Z5rGpQ+VqZSZz8EN8rZcBtfvXaOkbwxIEeRre6qnuZQlbY1AJ4MKDxQF8EkrCvK+hL/VkyYLQ== dependencies: chalk "^4.0.0" - diff-sequences "^26.0.0" - jest-get-type "^26.0.0" - pretty-format "^26.0.1" + diff-sequences "^26.3.0" + jest-get-type "^26.3.0" + pretty-format "^26.4.2" jest-docblock@^26.0.0: version "26.0.0" @@ -13394,39 +13430,41 @@ jest-docblock@^26.0.0: dependencies: detect-newline "^3.0.0" -jest-each@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-each/-/jest-each-26.0.1.tgz#633083061619302fc90dd8f58350f9d77d67be04" - integrity sha512-OTgJlwXCAR8NIWaXFL5DBbeS4QIYPuNASkzSwMCJO+ywo9BEa6TqkaSWsfR7VdbMLdgYJqSfQcIyjJCNwl5n4Q== +jest-each@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-26.4.2.tgz#bb14f7f4304f2bb2e2b81f783f989449b8b6ffae" + integrity sha512-p15rt8r8cUcRY0Mvo1fpkOGYm7iI8S6ySxgIdfh3oOIv+gHwrHTy5VWCGOecWUhDsit4Nz8avJWdT07WLpbwDA== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" chalk "^4.0.0" - jest-get-type "^26.0.0" - jest-util "^26.0.1" - pretty-format "^26.0.1" + jest-get-type "^26.3.0" + jest-util "^26.3.0" + pretty-format "^26.4.2" -jest-environment-jsdom@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.0.1.tgz#217690852e5bdd7c846a4e3b50c8ffd441dfd249" - integrity sha512-u88NJa3aptz2Xix2pFhihRBAatwZHWwSiRLBDBQE1cdJvDjPvv7ZGA0NQBxWwDDn7D0g1uHqxM8aGgfA9Bx49g== +jest-environment-jsdom@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.3.0.tgz#3b749ba0f3a78e92ba2c9ce519e16e5dd515220c" + integrity sha512-zra8He2btIMJkAzvLaiZ9QwEPGEetbxqmjEBQwhH3CA+Hhhu0jSiEJxnJMbX28TGUvPLxBt/zyaTLrOPF4yMJA== dependencies: - "@jest/environment" "^26.0.1" - "@jest/fake-timers" "^26.0.1" - "@jest/types" "^26.0.1" - jest-mock "^26.0.1" - jest-util "^26.0.1" + "@jest/environment" "^26.3.0" + "@jest/fake-timers" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + jest-mock "^26.3.0" + jest-util "^26.3.0" jsdom "^16.2.2" -jest-environment-node@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.0.1.tgz#584a9ff623124ff6eeb49e0131b5f7612b310b13" - integrity sha512-4FRBWcSn5yVo0KtNav7+5NH5Z/tEgDLp7VRQVS5tCouWORxj+nI+1tOLutM07Zb2Qi7ja+HEDoOUkjBSWZg/IQ== +jest-environment-node@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.3.0.tgz#56c6cfb506d1597f94ee8d717072bda7228df849" + integrity sha512-c9BvYoo+FGcMj5FunbBgtBnbR5qk3uky8PKyRVpSfe2/8+LrNQMiXX53z6q2kY+j15SkjQCOSL/6LHnCPLVHNw== dependencies: - "@jest/environment" "^26.0.1" - "@jest/fake-timers" "^26.0.1" - "@jest/types" "^26.0.1" - jest-mock "^26.0.1" - jest-util "^26.0.1" + "@jest/environment" "^26.3.0" + "@jest/fake-timers" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + jest-mock "^26.3.0" + jest-util "^26.3.0" jest-esm-transformer@^1.0.0: version "1.0.0" @@ -13449,61 +13487,63 @@ jest-get-type@^25.1.0, jest-get-type@^25.2.6: resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== -jest-get-type@^26.0.0: - version "26.0.0" - resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz#381e986a718998dbfafcd5ec05934be538db4039" - integrity sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg== +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-haste-map@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.0.1.tgz#40dcc03c43ac94d25b8618075804d09cd5d49de7" - integrity sha512-J9kBl/EdjmDsvyv7CiyKY5+DsTvVOScenprz/fGqfLg/pm1gdjbwwQ98nW0t+OIt+f+5nAVaElvn/6wP5KO7KA== +jest-haste-map@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.3.0.tgz#c51a3b40100d53ab777bfdad382d2e7a00e5c726" + integrity sha512-DHWBpTJgJhLLGwE5Z1ZaqLTYqeODQIZpby0zMBsCU9iRFHYyhklYqP4EiG73j5dkbaAdSZhgB938mL51Q5LeZA== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" "@types/graceful-fs" "^4.1.2" + "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.4" - jest-serializer "^26.0.0" - jest-util "^26.0.1" - jest-worker "^26.0.0" + jest-regex-util "^26.0.0" + jest-serializer "^26.3.0" + jest-util "^26.3.0" + jest-worker "^26.3.0" micromatch "^4.0.2" sane "^4.0.3" walker "^1.0.7" - which "^2.0.2" optionalDependencies: fsevents "^2.1.2" -jest-jasmine2@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.0.1.tgz#947c40ee816636ba23112af3206d6fa7b23c1c1c" - integrity sha512-ILaRyiWxiXOJ+RWTKupzQWwnPaeXPIoLS5uW41h18varJzd9/7I0QJGqg69fhTT1ev9JpSSo9QtalriUN0oqOg== +jest-jasmine2@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.4.2.tgz#18a9d5bec30904267ac5e9797570932aec1e2257" + integrity sha512-z7H4EpCldHN1J8fNgsja58QftxBSL+JcwZmaXIvV9WKIM+x49F4GLHu/+BQh2kzRKHAgaN/E82od+8rTOBPyPA== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.0.1" - "@jest/source-map" "^26.0.0" - "@jest/test-result" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/environment" "^26.3.0" + "@jest/source-map" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^26.0.1" + expect "^26.4.2" is-generator-fn "^2.0.0" - jest-each "^26.0.1" - jest-matcher-utils "^26.0.1" - jest-message-util "^26.0.1" - jest-runtime "^26.0.1" - jest-snapshot "^26.0.1" - jest-util "^26.0.1" - pretty-format "^26.0.1" + jest-each "^26.4.2" + jest-matcher-utils "^26.4.2" + jest-message-util "^26.3.0" + jest-runtime "^26.4.2" + jest-snapshot "^26.4.2" + jest-util "^26.3.0" + pretty-format "^26.4.2" throat "^5.0.0" -jest-leak-detector@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.0.1.tgz#79b19ab3f41170e0a78eb8fa754a116d3447fb8c" - integrity sha512-93FR8tJhaYIWrWsbmVN1pQ9ZNlbgRpfvrnw5LmgLRX0ckOJ8ut/I35CL7awi2ecq6Ca4lL59bEK9hr7nqoHWPA== +jest-leak-detector@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.4.2.tgz#c73e2fa8757bf905f6f66fb9e0070b70fa0f573f" + integrity sha512-akzGcxwxtE+9ZJZRW+M2o+nTNnmQZxrHJxX/HjgDaU5+PLmY1qnQPnMjgADPGCRPhB+Yawe1iij0REe+k/aHoA== dependencies: - jest-get-type "^26.0.0" - pretty-format "^26.0.1" + jest-get-type "^26.3.0" + pretty-format "^26.4.2" jest-matcher-utils@^25.1.0: version "25.1.0" @@ -13515,23 +13555,23 @@ jest-matcher-utils@^25.1.0: jest-get-type "^25.1.0" pretty-format "^25.1.0" -jest-matcher-utils@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.0.1.tgz#12e1fc386fe4f14678f4cc8dbd5ba75a58092911" - integrity sha512-PUMlsLth0Azen8Q2WFTwnSkGh2JZ8FYuwijC8NR47vXKpsrKmA1wWvgcj1CquuVfcYiDEdj985u5Wmg7COEARw== +jest-matcher-utils@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.4.2.tgz#fa81f3693f7cb67e5fc1537317525ef3b85f4b06" + integrity sha512-KcbNqWfWUG24R7tu9WcAOKKdiXiXCbMvQYT6iodZ9k1f7065k0keUOW6XpJMMvah+hTfqkhJhRXmA3r3zMAg0Q== dependencies: chalk "^4.0.0" - jest-diff "^26.0.1" - jest-get-type "^26.0.0" - pretty-format "^26.0.1" + jest-diff "^26.4.2" + jest-get-type "^26.3.0" + pretty-format "^26.4.2" -jest-message-util@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz#07af1b42fc450b4cc8e90e4c9cef11b33ce9b0ac" - integrity sha512-CbK8uQREZ8umUfo8+zgIfEt+W7HAHjQCoRaNs4WxKGhAYBGwEyvxuK81FXa7VeB9pwDEXeeKOB2qcsNVCAvB7Q== +jest-message-util@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.3.0.tgz#3bdb538af27bb417f2d4d16557606fd082d5841a" + integrity sha512-xIavRYqr4/otGOiLxLZGj3ieMmjcNE73Ui+LdSW/Y790j5acqCsAdDiLIbzHCZMpN07JOENRWX5DcU+OQ+TjTA== dependencies: "@babel/code-frame" "^7.0.0" - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" "@types/stack-utils" "^1.0.1" chalk "^4.0.0" graceful-fs "^4.2.4" @@ -13539,164 +13579,169 @@ jest-message-util@^26.0.1: slash "^3.0.0" stack-utils "^2.0.2" -jest-mock@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-26.0.1.tgz#7fd1517ed4955397cf1620a771dc2d61fad8fd40" - integrity sha512-MpYTBqycuPYSY6xKJognV7Ja46/TeRbAZept987Zp+tuJvMN0YBWyyhG9mXyYQaU3SBI0TUlSaO5L3p49agw7Q== +jest-mock@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-26.3.0.tgz#ee62207c3c5ebe5f35b760e1267fee19a1cfdeba" + integrity sha512-PeaRrg8Dc6mnS35gOo/CbZovoDPKAeB1FICZiuagAgGvbWdNNyjQjkOaGUa/3N3JtpQ/Mh9P4A2D4Fv51NnP8Q== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" + "@types/node" "*" -jest-pnp-resolver@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" - integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== jest-regex-util@^26.0.0: version "26.0.0" resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-resolve-dependencies@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.0.1.tgz#607ba7ccc32151d185a477cff45bf33bce417f0b" - integrity sha512-9d5/RS/ft0vB/qy7jct/qAhzJsr6fRQJyGAFigK3XD4hf9kIbEH5gks4t4Z7kyMRhowU6HWm/o8ILqhaHdSqLw== +jest-resolve-dependencies@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.4.2.tgz#739bdb027c14befb2fe5aabbd03f7bab355f1dc5" + integrity sha512-ADHaOwqEcVc71uTfySzSowA/RdxUpCxhxa2FNLiin9vWLB1uLPad3we+JSSROq5+SrL9iYPdZZF8bdKM7XABTQ== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" jest-regex-util "^26.0.0" - jest-snapshot "^26.0.1" + jest-snapshot "^26.4.2" -jest-resolve@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.0.1.tgz#21d1ee06f9ea270a343a8893051aeed940cde736" - integrity sha512-6jWxk0IKZkPIVTvq6s72RH735P8f9eCJW3IM5CX/SJFeKq1p2cZx0U49wf/SdMlhaB/anann5J2nCJj6HrbezQ== +jest-resolve@^26.4.0: + version "26.4.0" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.4.0.tgz#6dc0af7fb93e65b73fec0368ca2b76f3eb59a6d7" + integrity sha512-bn/JoZTEXRSlEx3+SfgZcJAVuTMOksYq9xe9O6s4Ekg84aKBObEaVXKOEilULRqviSLAYJldnoWV9c07kwtiCg== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" chalk "^4.0.0" graceful-fs "^4.2.4" - jest-pnp-resolver "^1.2.1" - jest-util "^26.0.1" + jest-pnp-resolver "^1.2.2" + jest-util "^26.3.0" read-pkg-up "^7.0.1" resolve "^1.17.0" slash "^3.0.0" -jest-runner@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-26.0.1.tgz#ea03584b7ae4bacfb7e533d680a575a49ae35d50" - integrity sha512-CApm0g81b49Znm4cZekYQK67zY7kkB4umOlI2Dx5CwKAzdgw75EN+ozBHRvxBzwo1ZLYZ07TFxkaPm+1t4d8jA== +jest-runner@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-26.4.2.tgz#c3ec5482c8edd31973bd3935df5a449a45b5b853" + integrity sha512-FgjDHeVknDjw1gRAYaoUoShe1K3XUuFMkIaXbdhEys+1O4bEJS8Avmn4lBwoMfL8O5oFTdWYKcf3tEJyyYyk8g== dependencies: - "@jest/console" "^26.0.1" - "@jest/environment" "^26.0.1" - "@jest/test-result" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/console" "^26.3.0" + "@jest/environment" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" chalk "^4.0.0" + emittery "^0.7.1" exit "^0.1.2" graceful-fs "^4.2.4" - jest-config "^26.0.1" + jest-config "^26.4.2" jest-docblock "^26.0.0" - jest-haste-map "^26.0.1" - jest-jasmine2 "^26.0.1" - jest-leak-detector "^26.0.1" - jest-message-util "^26.0.1" - jest-resolve "^26.0.1" - jest-runtime "^26.0.1" - jest-util "^26.0.1" - jest-worker "^26.0.0" + jest-haste-map "^26.3.0" + jest-leak-detector "^26.4.2" + jest-message-util "^26.3.0" + jest-resolve "^26.4.0" + jest-runtime "^26.4.2" + jest-util "^26.3.0" + jest-worker "^26.3.0" source-map-support "^0.5.6" throat "^5.0.0" -jest-runtime@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.0.1.tgz#a121a6321235987d294168e282d52b364d7d3f89" - integrity sha512-Ci2QhYFmANg5qaXWf78T2Pfo6GtmIBn2rRaLnklRyEucmPccmCKvS9JPljcmtVamsdMmkyNkVFb9pBTD6si9Lw== +jest-runtime@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.4.2.tgz#94ce17890353c92e4206580c73a8f0c024c33c42" + integrity sha512-4Pe7Uk5a80FnbHwSOk7ojNCJvz3Ks2CNQWT5Z7MJo4tX0jb3V/LThKvD9tKPNVNyeMH98J/nzGlcwc00R2dSHQ== dependencies: - "@jest/console" "^26.0.1" - "@jest/environment" "^26.0.1" - "@jest/fake-timers" "^26.0.1" - "@jest/globals" "^26.0.1" - "@jest/source-map" "^26.0.0" - "@jest/test-result" "^26.0.1" - "@jest/transform" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/console" "^26.3.0" + "@jest/environment" "^26.3.0" + "@jest/fake-timers" "^26.3.0" + "@jest/globals" "^26.4.2" + "@jest/source-map" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" "@types/yargs" "^15.0.0" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.2.4" - jest-config "^26.0.1" - jest-haste-map "^26.0.1" - jest-message-util "^26.0.1" - jest-mock "^26.0.1" + jest-config "^26.4.2" + jest-haste-map "^26.3.0" + jest-message-util "^26.3.0" + jest-mock "^26.3.0" jest-regex-util "^26.0.0" - jest-resolve "^26.0.1" - jest-snapshot "^26.0.1" - jest-util "^26.0.1" - jest-validate "^26.0.1" + jest-resolve "^26.4.0" + jest-snapshot "^26.4.2" + jest-util "^26.3.0" + jest-validate "^26.4.2" slash "^3.0.0" strip-bom "^4.0.0" yargs "^15.3.1" -jest-serializer@^26.0.0: - version "26.0.0" - resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.0.0.tgz#f6c521ddb976943b93e662c0d4d79245abec72a3" - integrity sha512-sQGXLdEGWFAE4wIJ2ZaIDb+ikETlUirEOBsLXdoBbeLhTHkZUJwgk3+M8eyFizhM6le43PDCCKPA1hzkSDo4cQ== +jest-serializer@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.3.0.tgz#1c9d5e1b74d6e5f7e7f9627080fa205d976c33ef" + integrity sha512-IDRBQBLPlKa4flg77fqg0n/pH87tcRKwe8zxOVTWISxGpPHYkRZ1dXKyh04JOja7gppc60+soKVZ791mruVdow== dependencies: + "@types/node" "*" graceful-fs "^4.2.4" -jest-snapshot@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.0.1.tgz#1baa942bd83d47b837a84af7fcf5fd4a236da399" - integrity sha512-jxd+cF7+LL+a80qh6TAnTLUZHyQoWwEHSUFJjkw35u3Gx+BZUNuXhYvDqHXr62UQPnWo2P6fvQlLjsU93UKyxA== +jest-snapshot@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.4.2.tgz#87d3ac2f2bd87ea8003602fbebd8fcb9e94104f6" + integrity sha512-N6Uub8FccKlf5SBFnL2Ri/xofbaA68Cc3MGjP/NuwgnsvWh+9hLIR/DhrxbSiKXMY9vUW5dI6EW1eHaDHqe9sg== dependencies: "@babel/types" "^7.0.0" - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" "@types/prettier" "^2.0.0" chalk "^4.0.0" - expect "^26.0.1" + expect "^26.4.2" graceful-fs "^4.2.4" - jest-diff "^26.0.1" - jest-get-type "^26.0.0" - jest-matcher-utils "^26.0.1" - jest-message-util "^26.0.1" - jest-resolve "^26.0.1" - make-dir "^3.0.0" + jest-diff "^26.4.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.3.0" + jest-matcher-utils "^26.4.2" + jest-message-util "^26.3.0" + jest-resolve "^26.4.0" natural-compare "^1.4.0" - pretty-format "^26.0.1" + pretty-format "^26.4.2" semver "^7.3.2" -jest-util@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-26.0.1.tgz#72c4c51177b695fdd795ca072a6f94e3d7cef00a" - integrity sha512-byQ3n7ad1BO/WyFkYvlWQHTsomB6GIewBh8tlGtusiylAlaxQ1UpS0XYH0ngOyhZuHVLN79Qvl6/pMiDMSSG1g== +jest-util@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-26.3.0.tgz#a8974b191df30e2bf523ebbfdbaeb8efca535b3e" + integrity sha512-4zpn6bwV0+AMFN0IYhH/wnzIQzRaYVrz1A8sYnRnj4UXDXbOVtWmlaZkO9mipFqZ13okIfN87aDoJWB7VH6hcw== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" + "@types/node" "*" chalk "^4.0.0" graceful-fs "^4.2.4" is-ci "^2.0.0" - make-dir "^3.0.0" + micromatch "^4.0.2" -jest-validate@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-26.0.1.tgz#a62987e1da5b7f724130f904725e22f4e5b2e23c" - integrity sha512-u0xRc+rbmov/VqXnX3DlkxD74rHI/CfS5xaV2VpeaVySjbb1JioNVOyly5b56q2l9ZKe7bVG5qWmjfctkQb0bA== +jest-validate@^26.4.2: + version "26.4.2" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-26.4.2.tgz#e871b0dfe97747133014dcf6445ee8018398f39c" + integrity sha512-blft+xDX7XXghfhY0mrsBCYhX365n8K5wNDC4XAcNKqqjEzsRUSXP44m6PL0QJEW2crxQFLLztVnJ4j7oPlQrQ== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.3.0" camelcase "^6.0.0" chalk "^4.0.0" - jest-get-type "^26.0.0" + jest-get-type "^26.3.0" leven "^3.1.0" - pretty-format "^26.0.1" + pretty-format "^26.4.2" -jest-watcher@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.0.1.tgz#5b5e3ebbdf10c240e22a98af66d645631afda770" - integrity sha512-pdZPydsS8475f89kGswaNsN3rhP6lnC3/QDCppP7bg1L9JQz7oU9Mb/5xPETk1RHDCWeqmVC47M4K5RR7ejxFw== +jest-watcher@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.3.0.tgz#f8ef3068ddb8af160ef868400318dc4a898eed08" + integrity sha512-XnLdKmyCGJ3VoF6G/p5ohbJ04q/vv5aH9ENI+i6BL0uu9WWB6Z7Z2lhQQk0d2AVZcRGp1yW+/TsoToMhBFPRdQ== dependencies: - "@jest/test-result" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^26.0.1" + jest-util "^26.3.0" string-length "^4.0.1" jest-worker@^25.1.0: @@ -13707,22 +13752,23 @@ jest-worker@^25.1.0: merge-stream "^2.0.0" supports-color "^7.0.0" -jest-worker@^26.0.0: - version "26.0.0" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.0.0.tgz#4920c7714f0a96c6412464718d0c58a3df3fb066" - integrity sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw== +jest-worker@^26.3.0: + version "26.3.0" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz#7c8a97e4f4364b4f05ed8bca8ca0c24de091871f" + integrity sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw== dependencies: + "@types/node" "*" merge-stream "^2.0.0" supports-color "^7.0.0" jest@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/jest/-/jest-26.0.1.tgz#5c51a2e58dff7525b65f169721767173bf832694" - integrity sha512-29Q54kn5Bm7ZGKIuH2JRmnKl85YRigp0o0asTc6Sb6l2ch1DCXIeZTLLFy9ultJvhkTqbswF5DEx4+RlkmCxWg== + version "26.4.2" + resolved "https://registry.npmjs.org/jest/-/jest-26.4.2.tgz#7e8bfb348ec33f5459adeaffc1a25d5752d9d312" + integrity sha512-LLCjPrUh98Ik8CzW8LLVnSCfLaiY+wbK53U7VxnFSX7Q+kWC4noVeDvGWIFw0Amfq1lq2VfGm7YHWSLBV62MJw== dependencies: - "@jest/core" "^26.0.1" + "@jest/core" "^26.4.2" import-local "^3.0.2" - jest-cli "^26.0.1" + jest-cli "^26.4.2" jose@^1.27.1: version "1.27.1" @@ -15839,16 +15885,16 @@ node-modules-regexp@^1.0.0: resolved "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-notifier@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.0.tgz#513bc42f2aa3a49fce1980a7ff375957c71f718a" - integrity sha512-y8ThJESxsHcak81PGpzWwQKxzk+5YtP3IxR8AYdpXQ1IB6FmcVzFdZXrkPin49F/DKUCfeeiziB8ptY9npzGuA== +node-notifier@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz#a7eee2d51da6d0f7ff5094bc7108c911240c1620" + integrity sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA== dependencies: growly "^1.3.0" - is-wsl "^2.1.1" - semver "^7.2.1" + is-wsl "^2.2.0" + semver "^7.3.2" shellwords "^0.1.1" - uuid "^7.0.3" + uuid "^8.3.0" which "^2.0.2" node-pre-gyp@^0.11.0: @@ -17817,16 +17863,6 @@ pretty-format@^25.1.0, pretty-format@^25.2.1, pretty-format@^25.5.0: ansi-styles "^4.0.0" react-is "^16.12.0" -pretty-format@^26.0.1: - version "26.0.1" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.0.1.tgz#a4fe54fe428ad2fd3413ca6bbd1ec8c2e277e197" - integrity sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw== - dependencies: - "@jest/types" "^26.0.1" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^16.12.0" - pretty-format@^26.4.2: version "26.4.2" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.2.tgz#d081d032b398e801e2012af2df1214ef75a81237" @@ -22312,7 +22348,7 @@ uuid@^7.0.3: resolved "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== -uuid@^8.0.0, uuid@^8.2.0: +uuid@^8.0.0, uuid@^8.2.0, uuid@^8.3.0: version "8.3.0" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== @@ -22322,10 +22358,10 @@ v8-compile-cache@^2.0.3: resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== -v8-to-istanbul@^4.1.3: - version "4.1.4" - resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz#b97936f21c0e2d9996d4985e5c5156e9d4e49cd6" - integrity sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ== +v8-to-istanbul@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-5.0.1.tgz#0608f5b49a481458625edb058488607f25498ba5" + integrity sha512-mbDNjuDajqYe3TXFk5qxcQy8L1msXNE37WTlLoqqpBfRsimbNcrlhQlDPntmECEcUvdC+AQ8CyMMf6EUx1r74Q== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" From a7e922250aa1a63798a07a1798896b02793461ad Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 1 Sep 2020 14:21:05 +0200 Subject: [PATCH 249/359] Add instructions on how to refresh existing release PR --- docs/plugins/publishing.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/plugins/publishing.md b/docs/plugins/publishing.md index 1d16ebb047..6fee4f9b1d 100644 --- a/docs/plugins/publishing.md +++ b/docs/plugins/publishing.md @@ -39,4 +39,18 @@ $ git push origin -u new-release And then create a PR. Once the PR is approved and merged into master, the master build will publish new versions of all bumped packages. +### Include new changes in existing release PR + +If you want to include some last minute changes to an existing release PR, +follow these instructions: + +```sh +$ git checkout master +$ git pull +$ git checkout new-release +$ git reset --hard master +$ yarn release +$ git push --force +``` + [Back to Docs](../README.md) From 9f59ee821a98ad66d7193d88dca22894100006e7 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 2 Sep 2020 11:07:33 +0200 Subject: [PATCH 250/359] Add new log format --- packages/backend-common/package.json | 3 +- .../backend-common/src/logging/formats.ts | 35 +++++++++++++++++++ .../backend-common/src/logging/rootLogger.ts | 7 ++-- 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 packages/backend-common/src/logging/formats.ts diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 92d301d7e3..248e638ead 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -46,7 +46,8 @@ "prom-client": "^12.0.0", "selfsigned": "^1.10.7", "stoppable": "^1.1.0", - "winston": "^3.2.1" + "winston": "^3.2.1", + "logform": "^2.1.1" }, "peerDependencies": { "pg-connection-string": "^2.3.0" diff --git a/packages/backend-common/src/logging/formats.ts b/packages/backend-common/src/logging/formats.ts new file mode 100644 index 0000000000..5f00448744 --- /dev/null +++ b/packages/backend-common/src/logging/formats.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 * as winston from 'winston'; +import { TransformableInfo } from 'logform'; + +const coloredTemplate = (info: TransformableInfo) => { + const { timestamp, level, message, plugin, service } = info; + const colorizer = winston.format.colorize(); + const prefix = plugin || service; + const timestampColor = colorizer.colorize('timestamp', timestamp); + const prefixColor = colorizer.colorize('prefix', prefix); + + return `${timestampColor} ${prefixColor} ${level} ${message}`; +}; + +export const coloredFormat = winston.format.combine( + winston.format.timestamp(), + winston.format.colorize({ + colors: { timestamp: 'dim', prefix: 'blue' }, + }), + winston.format.printf(coloredTemplate), +); diff --git a/packages/backend-common/src/logging/rootLogger.ts b/packages/backend-common/src/logging/rootLogger.ts index fd2f3a1c8b..306ed23444 100644 --- a/packages/backend-common/src/logging/rootLogger.ts +++ b/packages/backend-common/src/logging/rootLogger.ts @@ -14,17 +14,14 @@ * limitations under the License. */ import * as winston from 'winston'; +import { coloredFormat } from './formats'; let rootLogger: winston.Logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: process.env.NODE_ENV === 'production' ? winston.format.json() - : winston.format.combine( - winston.format.colorize(), - winston.format.timestamp(), - winston.format.simple(), - ), + : coloredFormat, defaultMeta: { service: 'backstage' }, transports: [ new winston.transports.Console({ From 7d8058a1ef0fab41a6801e8f3ecbf616098a2faf Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 2 Sep 2020 11:08:40 +0200 Subject: [PATCH 251/359] Make http-proxy-middleware log to supplied logger --- plugins/proxy-backend/src/service/router.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/proxy-backend/src/service/router.ts b/plugins/proxy-backend/src/service/router.ts index 5273b2c806..5c5e17b872 100644 --- a/plugins/proxy-backend/src/service/router.ts +++ b/plugins/proxy-backend/src/service/router.ts @@ -34,6 +34,7 @@ export interface RouterOptions { // given config. function buildMiddleware( pathPrefix: string, + logger: Logger, route: string, config: string | ProxyConfig, ): Proxy { @@ -54,6 +55,9 @@ function buildMiddleware( fullConfig.changeOrigin = true; } + // Attach the logger to the proxy config + fullConfig.logProvider = () => logger; + return createProxyMiddleware(fullConfig); } @@ -66,7 +70,12 @@ export async function createRouter( Object.entries(proxyConfig).forEach(([route, proxyRouteConfig]) => { router.use( route, - buildMiddleware(options.pathPrefix, route, proxyRouteConfig), + buildMiddleware( + options.pathPrefix, + options.logger, + route, + proxyRouteConfig, + ), ); }); From dac617b436090565a89a0f5f55b0a7471e3284f3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 2 Sep 2020 11:39:59 +0200 Subject: [PATCH 252/359] create-app: remove duplicate techdocs plugin from app package --- .../templates/default-app/packages/app/package.json.hbs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs index 57ec119ed5..c432f27a3a 100644 --- a/packages/create-app/templates/default-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs @@ -17,7 +17,6 @@ "@backstage/plugin-lighthouse": "^{{version}}", "@backstage/plugin-tech-radar": "^{{version}}", "@backstage/plugin-github-actions": "^{{version}}", - "@backstage/plugin-techdocs": "^{{version}}", "@backstage/plugin-sentry": "^{{version}}", "@backstage/test-utils": "^{{version}}", "@backstage/theme": "^{{version}}", From 992dfd545212f82cdeb83e2fc48921a4faa45e99 Mon Sep 17 00:00:00 2001 From: "althaf.hameez" Date: Wed, 2 Sep 2020 18:13:25 +0800 Subject: [PATCH 253/359] rebase --- ADOPTERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index dc02ad3b85..0ba3e3196b 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -12,3 +12,4 @@ | [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | | [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | | [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | +| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | From 0af1ce25caf544271e186f74f9429eecc99e1cd6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 2 Sep 2020 13:01:27 +0200 Subject: [PATCH 254/359] techdocs-backend: use resolvePackagePath instead of __dirname --- plugins/techdocs-backend/src/service/router.ts | 12 +++++++----- .../src/techdocs/stages/publish/local.ts | 8 ++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index f93d182edc..bb26ea19b8 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -19,7 +19,6 @@ import express from 'express'; import Knex from 'knex'; import fetch from 'node-fetch'; import { Config } from '@backstage/config'; -import path from 'path'; import Docker from 'dockerode'; import { GeneratorBuilder, @@ -27,6 +26,7 @@ import { PublisherBase, LocalPublish, } from '../techdocs'; +import { resolvePackagePath } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; type RouterOptions = { @@ -39,6 +39,11 @@ type RouterOptions = { dockerClient: Docker; }; +const staticDocsDir = resolvePackagePath( + '@backstage/plugin-techdocs-backend', + 'static/docs', +); + export async function createRouter({ preparers, generators, @@ -102,10 +107,7 @@ export async function createRouter({ }); if (publisher instanceof LocalPublish) { - router.use( - '/static/docs/', - express.static(path.resolve(__dirname, `../../static/docs`)), - ); + router.use('/static/docs/', express.static(staticDocsDir)); router.use( '/static/docs/:kind/:namespace/:name', async (req, res, next) => { diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts index be9e1876d8..e33c6fa17d 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts @@ -14,10 +14,10 @@ * limitations under the License. */ import fs from 'fs-extra'; -import path from 'path'; import { Logger } from 'winston'; import { Entity } from '@backstage/catalog-model'; import { PublisherBase } from './types'; +import { resolvePackagePath } from '@backstage/backend-common'; export class LocalPublish implements PublisherBase { private readonly logger: Logger; @@ -39,9 +39,9 @@ export class LocalPublish implements PublisherBase { | { remoteUrl: string } { const entityNamespace = entity.metadata.namespace ?? 'default'; - const publishDir = path.resolve( - __dirname, - '../../../../static/docs/', + const publishDir = resolvePackagePath( + '@backstage/plugin-techdocs-backend', + 'static/docs', entity.kind, entityNamespace, entity.metadata.name, From 8ac23713029bfbab7d36261f62de8468a5ef1f9b Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 2 Sep 2020 14:03:35 +0200 Subject: [PATCH 255/359] Remove sentry from create-app --- .../templates/default-app/app-config.yaml.hbs | 3 --- .../default-app/packages/app/package.json.hbs | 1 - .../default-app/packages/app/src/plugins.ts | 1 - .../packages/backend/package.json.hbs | 1 - .../default-app/packages/backend/src/index.ts | 3 --- .../packages/backend/src/plugins/sentry.ts | 22 ------------------- 6 files changed, 31 deletions(-) delete mode 100644 packages/create-app/templates/default-app/packages/backend/src/plugins/sentry.ts diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index 64185e43d3..d5a8e9b200 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -44,9 +44,6 @@ proxy: techdocs: storageUrl: http://localhost:7000/techdocs/static/docs -sentry: - organization: spotify - lighthouse: baseUrl: http://localhost:3003 diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs index c432f27a3a..9d4993c4dc 100644 --- a/packages/create-app/templates/default-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs @@ -17,7 +17,6 @@ "@backstage/plugin-lighthouse": "^{{version}}", "@backstage/plugin-tech-radar": "^{{version}}", "@backstage/plugin-github-actions": "^{{version}}", - "@backstage/plugin-sentry": "^{{version}}", "@backstage/test-utils": "^{{version}}", "@backstage/theme": "^{{version}}", "history": "^5.0.0", diff --git a/packages/create-app/templates/default-app/packages/app/src/plugins.ts b/packages/create-app/templates/default-app/packages/app/src/plugins.ts index 0d54c55649..c787ac2166 100644 --- a/packages/create-app/templates/default-app/packages/app/src/plugins.ts +++ b/packages/create-app/templates/default-app/packages/app/src/plugins.ts @@ -7,4 +7,3 @@ export { plugin as Circleci } from '@backstage/plugin-circleci'; export { plugin as LighthousePlugin } from '@backstage/plugin-lighthouse'; export { plugin as TechRadar } from '@backstage/plugin-tech-radar'; export { plugin as GithubActions } from '@backstage/plugin-github-actions'; -export { plugin as Sentry } from '@backstage/plugin-sentry'; diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs index a978349b02..53bfd6d990 100644 --- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs @@ -26,7 +26,6 @@ "@backstage/plugin-proxy-backend": "^{{version}}", "@backstage/plugin-rollbar-backend": "^{{version}}", "@backstage/plugin-scaffolder-backend": "^{{version}}", - "@backstage/plugin-sentry-backend": "^{{version}}", "@backstage/plugin-techdocs-backend": "^{{version}}", "@octokit/rest": "^18.0.0", "dockerode": "^3.2.0", diff --git a/packages/create-app/templates/default-app/packages/backend/src/index.ts b/packages/create-app/templates/default-app/packages/backend/src/index.ts index 6ba6c4dc8e..6a014727e9 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/index.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/index.ts @@ -20,7 +20,6 @@ import identity from './plugins/identity'; import scaffolder from './plugins/scaffolder'; import proxy from './plugins/proxy'; import techdocs from './plugins/techdocs'; -import sentry from './plugins/sentry'; import { PluginEnvironment } from './types'; function makeCreateEnv(loadedConfigs: AppConfig[]) { @@ -51,13 +50,11 @@ async function main() { const identityEnv = useHotMemoize(module, () => createEnv('identity')); const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); - const sentryEnv = useHotMemoize(module, () => createEnv('sentry')); const service = createServiceBuilder(module) .loadConfig(configReader) .addRouter('/catalog', await catalog(catalogEnv)) .addRouter('/scaffolder', await scaffolder(scaffolderEnv)) - .addRouter('/sentry', await sentry(sentryEnv)) .addRouter('/auth', await auth(authEnv)) .addRouter('/identity', await identity(identityEnv)) .addRouter('/techdocs', await techdocs(techdocsEnv)) diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/sentry.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/sentry.ts deleted file mode 100644 index 5cd0e55761..0000000000 --- a/packages/create-app/templates/default-app/packages/backend/src/plugins/sentry.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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 { createRouter } from '@backstage/plugin-sentry-backend'; -import type { PluginEnvironment } from '../types'; - -export default async function createPlugin({ logger }: PluginEnvironment) { - return await createRouter(logger); -} From c08a83260e7da58b4458965e4e6022756f1ba6f6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 2 Sep 2020 14:21:58 +0200 Subject: [PATCH 256/359] auth-backend: use correct interface in factory return type --- plugins/auth-backend/src/providers/types.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/auth-backend/src/providers/types.ts b/plugins/auth-backend/src/providers/types.ts index 80b8899d94..b3b7e518cc 100644 --- a/plugins/auth-backend/src/providers/types.ts +++ b/plugins/auth-backend/src/providers/types.ts @@ -18,8 +18,6 @@ import express from 'express'; import { Logger } from 'winston'; import { TokenIssuer } from '../identity'; import { Config } from '@backstage/config'; -import { OAuthProvider } from '../lib/OAuthProvider'; -import { SamlAuthProvider } from './saml/provider'; export type OAuthProviderOptions = { /** @@ -174,7 +172,7 @@ export type AuthProviderFactory = ( envConfig: Config, logger: Logger, issuer: TokenIssuer, -) => OAuthProvider | SamlAuthProvider | undefined; +) => AuthProviderRouteHandlers | undefined; export type AuthResponse = { providerInfo: ProviderInfo; From 07c9a9474a111f8e8dcd1787734135dd52f19b29 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Wed, 2 Sep 2020 15:30:35 +0200 Subject: [PATCH 257/359] Get branchname from repo instance instead of location url (#2242) --- .../techdocs-backend/src/techdocs/stages/prepare/helpers.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts index b2f04bbbbf..27e746590a 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/helpers.ts @@ -79,9 +79,10 @@ export const checkoutGitRepository = async ( if (fs.existsSync(repositoryTmpPath)) { const repository = await Repository.open(repositoryTmpPath); + const currentBranchName = (await repository.getCurrentBranch()).shorthand(); await repository.mergeBranches( - parsedGitLocation.ref, - `origin/${parsedGitLocation.ref}`, + currentBranchName, + `origin/${currentBranchName}`, ); return repositoryTmpPath; } From 9988479c61cfac34e6e5089797d5316790858b51 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Wed, 2 Sep 2020 15:56:14 +0200 Subject: [PATCH 258/359] refactor: address PR comments --- docs/plugins/index.md | 4 +-- ...integrating-plugin-into-service-catalog.md | 5 ++- packages/app/src/App.tsx | 8 ++--- .../app/src/components/catalog/EntityPage.tsx | 7 ++++- packages/core-api/src/app/App.tsx | 11 ++----- packages/core-api/src/app/types.ts | 2 +- packages/dev-utils/src/devApp/render.tsx | 4 +-- packages/e2e-test/src/e2e-test.ts | 13 ++++---- .../EntityPageLayout/EntityPageLayout.tsx | 9 +++--- .../EntityPageLayout/Tabbed/Tabbed.test.tsx | 15 +++++++++ .../EntityPageLayout/Tabbed/Tabbed.tsx | 15 +++++---- .../catalog/src/{ => components}/Router.tsx | 31 ++++++++++--------- plugins/catalog/src/hooks/useEntity.ts | 29 +++++++---------- plugins/catalog/src/index.ts | 2 +- plugins/catalog/src/routes.ts | 5 --- .../JobStatusModal/JobStatusModal.tsx | 4 +-- 16 files changed, 86 insertions(+), 78 deletions(-) rename plugins/catalog/src/{ => components}/Router.tsx (71%) diff --git a/docs/plugins/index.md b/docs/plugins/index.md index 6941ff3e18..41ad082d7c 100644 --- a/docs/plugins/index.md +++ b/docs/plugins/index.md @@ -27,9 +27,9 @@ This helps the community know what plugins are in development. You can also use this process if you have an idea for a good plugin but you hope that someone else will pick up the work. -## Integrate into the catalog service +## Integrate into the Service Catalog If your plugin isn't supposed to live as a standalone page, but rather needs to -be presented as a part of a catalog service (e.g. a separate tab or a card on an +be presented as a part of a Service Catalog (e.g. a separate tab or a card on an "Overview" tab), then check out [the instruction](integrating-plugin-into-service-catalog.md). on how to do it. diff --git a/docs/plugins/integrating-plugin-into-service-catalog.md b/docs/plugins/integrating-plugin-into-service-catalog.md index 7819737c52..4408c27ccc 100644 --- a/docs/plugins/integrating-plugin-into-service-catalog.md +++ b/docs/plugins/integrating-plugin-into-service-catalog.md @@ -1,6 +1,6 @@ --- id: integrating-plugin-into-service-catalog -title: Integrate into the catalog service +title: Integrate into the Service Catalog --- > This is an advanced use case and currently is an experimental feature. Expect @@ -119,7 +119,6 @@ type EntityPageLayoutContentProps = { }; ``` -> The recommended pattern is to get the `entity` in the App and then pass it to -> the plugin's Router component as a prop. However if it inconvenient for you, +> You can either pass the entity from App to the plugin's router as a prop or > use `useEntity` hook from `@backstage/plugin-catalog` directly inside your > plugin. diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 642eb7b004..cb38726716 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -26,8 +26,7 @@ import * as plugins from './plugins'; import { apis } from './apis'; import { hot } from 'react-hot-loader/root'; import { providers } from './identityProviders'; -import { CatalogRouter } from '@backstage/plugin-catalog'; -// import { ExplorePlugin } from '@backstage/plugin-explore'; +import { Router as CatalogRouter } from '@backstage/plugin-catalog'; import { Route, Routes, Navigate } from 'react-router'; import { EntityPage } from './components/catalog/EntityPage'; @@ -51,6 +50,7 @@ const app = createApp({ const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); +const deprecatedAppRoutes = app.getRoutes(); const AppRoutes = () => ( @@ -58,8 +58,8 @@ const AppRoutes = () => ( path="/catalog/*" element={} /> - } /> - {/* } /> */} + + {...deprecatedAppRoutes} ); diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index b56268036f..913c063f90 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -21,9 +21,14 @@ import { AboutCard, } from '@backstage/plugin-catalog'; import { Entity } from '@backstage/catalog-model'; +import { Grid } from '@material-ui/core'; const OverviewPage = ({ entity }: { entity: Entity }) => ( - + + + + + ); const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( diff --git a/packages/core-api/src/app/App.tsx b/packages/core-api/src/app/App.tsx index 27a63ea04a..bfa26b7547 100644 --- a/packages/core-api/src/app/App.tsx +++ b/packages/core-api/src/app/App.tsx @@ -136,7 +136,7 @@ export class PrivateAppImpl implements BackstageApp { return this.icons[key]; } - getRoutes(): ComponentType<{}> { + getRoutes(): JSX.Element[] { const routes = new Array(); const registeredFeatureFlags = new Array(); @@ -191,14 +191,9 @@ export class PrivateAppImpl implements BackstageApp { FeatureFlags.registeredFeatureFlags = registeredFeatureFlags; } - const rendered = ( - - {routes} - } /> - - ); + routes.push(} />); - return () => rendered; + return routes; } getProvider(): ComponentType<{}> { diff --git a/packages/core-api/src/app/types.ts b/packages/core-api/src/app/types.ts index 6b022c2736..db7a0897b1 100644 --- a/packages/core-api/src/app/types.ts +++ b/packages/core-api/src/app/types.ts @@ -168,5 +168,5 @@ export type BackstageApp = { /** * Routes component that contains all routes for plugin pages in the app. */ - getRoutes(): ComponentType<{}>; + getRoutes(): JSX.Element[]; }; diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index 410f7592a5..cde39fdd05 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -85,7 +85,7 @@ class DevAppBuilder { const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); - const AppRoutes = app.getRoutes(); + const deprecatedAppRoutes = app.getRoutes(); const sidebar = this.setupSidebar(this.plugins); @@ -99,7 +99,7 @@ class DevAppBuilder { {sidebar} - + {deprecatedAppRoutes} diff --git a/packages/e2e-test/src/e2e-test.ts b/packages/e2e-test/src/e2e-test.ts index f74c3f7166..5dfac68d6a 100644 --- a/packages/e2e-test/src/e2e-test.ts +++ b/packages/e2e-test/src/e2e-test.ts @@ -269,7 +269,7 @@ async function createPlugin(pluginName: string, appDir: string) { /** * Start serving the newly created app and make sure that the create plugin is rendering correctly */ -async function testAppServe(_pluginName: string, appDir: string) { +async function testAppServe(pluginName: string, appDir: string) { const startApp = spawnPiped(['yarn', 'start'], { cwd: appDir, }); @@ -280,12 +280,11 @@ async function testAppServe(_pluginName: string, appDir: string) { const browser = new Browser(); await waitForPageWithText(browser, '/', 'Backstage Service Catalog'); - // TODO(shmidt-i): adjust the plugin creation flow with new routing patterns - // await waitForPageWithText( - // browser, - // `/${pluginName}`, - // `Welcome to ${pluginName}!`, - // ); + await waitForPageWithText( + browser, + `/${pluginName}`, + `Welcome to ${pluginName}!`, + ); print('Both App and Plugin loaded correctly'); successful = true; diff --git a/plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx b/plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx index 17f9f46830..6a1b80150e 100644 --- a/plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx +++ b/plugins/catalog/src/components/EntityPageLayout/EntityPageLayout.tsx @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useState } from 'react'; +import React, { useState, useContext } from 'react'; import { useParams, useNavigate } from 'react-router'; -import { useEntity } from '../../hooks/useEntity'; +import { EntityContext } from '../../hooks/useEntity'; import { pageTheme, PageTheme, @@ -74,7 +74,7 @@ function headerProps( export const EntityPageLayout = ({ children, }: { - children: React.ReactNode; + children?: React.ReactNode; }) => { const { optionalNamespaceAndName, kind } = useParams() as { optionalNamespaceAndName: string; @@ -82,7 +82,7 @@ export const EntityPageLayout = ({ }; const [name, namespace] = optionalNamespaceAndName.split(':').reverse(); - const { entity, loading, error } = useEntity(); + const { entity, loading, error } = useContext(EntityContext); const { headerTitle, headerType } = headerProps( kind, namespace, @@ -139,5 +139,4 @@ export const EntityPageLayout = ({ ); }; - EntityPageLayout.Content = Tabbed.Content; diff --git a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.test.tsx b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.test.tsx index 3eccafbf7a..da1e696fde 100644 --- a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.test.tsx +++ b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.test.tsx @@ -36,6 +36,21 @@ describe('Tabbed layout', () => { expect(rendered.getByText('tabbed-test-content')).toBeInTheDocument(); }); + it('throws if any other component is a child of Tabbed.Layout', async () => { + await expect( + renderInTestApp( + + tabbed-test-content} + /> +
This will cause app to throw
+
, + ), + ).rejects.toThrow(/EntityPageLayout component only accepts/); + }); + it('navigates when user clicks different tab', async () => { const rendered = await renderInTestApp( diff --git a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx index 693ee10c78..096b2fa708 100644 --- a/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx +++ b/plugins/catalog/src/components/EntityPageLayout/Tabbed/Tabbed.tsx @@ -25,7 +25,6 @@ import { RouteMatch, } from 'react-router'; import { Tab, HeaderTabs, Content } from '@backstage/core'; -import { Grid } from '@material-ui/core'; import { Helmet } from 'react-helmet'; const getSelectedIndexOrDefault = ( @@ -67,6 +66,11 @@ export const Tabbed = { // Skip conditionals resolved to falses/nulls/undefineds etc return; } + if (child.type !== Tabbed.Content) { + throw new Error( + 'This component only accepts Content elements as direct children. Check the code of the EntityPage.', + ); + } const pathAndId = (child as JSX.Element).props.path; // Child here must be then always a functional component without any wrappers @@ -92,7 +96,7 @@ export const Tabbed = { matchRoutes(routes as RouteObject[], `/${params['*']}`) ?? []; const selectedIndex = getSelectedIndexOrDefault(matchedRoute, tabs); const currentTab = tabs[selectedIndex]; - const title = currentTab.label; + const title = currentTab?.label; const onTabChange = (index: number) => // Remove trailing /* @@ -103,6 +107,7 @@ export const Tabbed = { const currentRouteElement = useRoutes(routes); + if (!currentTab) return null; return ( <> - - - {currentRouteElement} - + + {currentRouteElement} ); diff --git a/plugins/catalog/src/Router.tsx b/plugins/catalog/src/components/Router.tsx similarity index 71% rename from plugins/catalog/src/Router.tsx rename to plugins/catalog/src/components/Router.tsx index 161536c714..61855f56d9 100644 --- a/plugins/catalog/src/Router.tsx +++ b/plugins/catalog/src/components/Router.tsx @@ -14,13 +14,14 @@ * limitations under the License. */ import React, { ComponentType } from 'react'; -import { CatalogPage } from './components/CatalogPage'; -import { EntityPageLayout } from './components/EntityPageLayout'; +import { CatalogPage } from './CatalogPage'; +import { EntityPageLayout } from './EntityPageLayout'; import { Route, Routes } from 'react-router'; -import { entityRoute, rootRoute, entityRouteDefault } from './routes'; +import { entityRoute, rootRoute } from '../routes'; import { Content } from '@backstage/core'; import { Typography, Link } from '@material-ui/core'; -import { EntityProvider } from './components/EntityProvider'; +import { EntityProvider } from './EntityProvider'; +import { useEntity } from '../hooks/useEntity'; const DefaultEntityPage = () => ( @@ -43,7 +44,17 @@ const DefaultEntityPage = () => ( ); -export const CatalogRouter = ({ +const EntityPageSwitch = ({ EntityPage }: { EntityPage: ComponentType }) => { + const { entity } = useEntity(); + // Loading and error states + if (!entity) return ; + + // Otherwise EntityPage provided from the App + // Note that EntityPage will include EntityPageLayout already + return ; +}; + +export const Router = ({ EntityPage = DefaultEntityPage, }: { EntityPage?: ComponentType; @@ -54,15 +65,7 @@ export const CatalogRouter = ({ path={`/${entityRoute.path}`} element={ - - - } - /> - - + } /> diff --git a/plugins/catalog/src/hooks/useEntity.ts b/plugins/catalog/src/hooks/useEntity.ts index ede45efeef..53b1cff73a 100644 --- a/plugins/catalog/src/hooks/useEntity.ts +++ b/plugins/catalog/src/hooks/useEntity.ts @@ -24,13 +24,13 @@ const REDIRECT_DELAY = 2000; type EntityLoadingStatus = { entity?: Entity; - loading: boolean | null; + loading: boolean; error?: Error; }; export const EntityContext = createContext({ - entity: undefined as any, - loading: null, + entity: undefined, + loading: true, error: undefined, }); @@ -53,25 +53,20 @@ export const useEntityFromUrl = (): EntityLoadingStatus => { navigate('/'); }, REDIRECT_DELAY); } - }, [errorApi, navigate, error, loading, entity]); - if (!name) { - navigate('/catalog'); - return { - entity: undefined, - loading: null, - error: new Error('No name in url'), - } as never; - } + if (!name) { + errorApi.post(new Error('No name provided!')); + navigate('/'); + } + }, [errorApi, navigate, error, loading, entity, name]); return { entity, loading, error }; }; /** * Always going to return an entity, or throw an error if not a descendant of a EntityProvider. - * Otherwise the useEntityFromUrl will take care of the `undefined` entity */ -export const useEntity = () => - useContext & { entity: Entity }>( - EntityContext as any, - ); +export const useEntity = () => { + const { entity } = useContext<{ entity: Entity }>(EntityContext as any); + return { entity }; +}; diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index 26285b4814..a6d86e8102 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -19,7 +19,7 @@ export * from './api/CatalogClient'; export * from './api/types'; export * from './routes'; export { useEntityCompoundName } from './components/useEntityCompoundName'; -export * from './Router'; +export { Router } from './components/Router'; export { useEntity } from './hooks/useEntity'; export { AboutCard } from './components/AboutCard'; export { EntityPageLayout } from './components/EntityPageLayout'; diff --git a/plugins/catalog/src/routes.ts b/plugins/catalog/src/routes.ts index a9606760fb..14e23ff73b 100644 --- a/plugins/catalog/src/routes.ts +++ b/plugins/catalog/src/routes.ts @@ -28,8 +28,3 @@ export const entityRoute = createRouteRef({ path: ':kind/:optionalNamespaceAndName/*', title: 'Entity', }); -export const entityRouteDefault = createRouteRef({ - icon: NoIcon, - path: ':kind/:optionalNamespaceAndName/*', - title: 'Entity', -}); diff --git a/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx b/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx index f5a592b7b7..c6ed55c92c 100644 --- a/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx +++ b/plugins/scaffolder/src/components/JobStatusModal/JobStatusModal.tsx @@ -26,7 +26,7 @@ import { useJobPolling } from './useJobPolling'; import { Job } from '../../types'; import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { Button } from '@backstage/core'; -import { entityRouteDefault } from '@backstage/plugin-catalog'; +import { entityRoute } from '@backstage/plugin-catalog'; import { generatePath } from 'react-router-dom'; type Props = { @@ -72,7 +72,7 @@ export const JobStatusModal = ({ {entity && (
@@ -144,7 +144,7 @@ const DetailsPage = () => {
-
+ ); }; diff --git a/yarn.lock b/yarn.lock index 2840e9d7a5..9175c61e60 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18844,7 +18844,7 @@ react-transition-group@^4.0.0, react-transition-group@^4.3.0: loose-envify "^1.4.0" prop-types "^15.6.2" -react-universal-interface@^0.6.0, react-universal-interface@^0.6.2: +react-universal-interface@^0.6.2: version "0.6.2" resolved "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw== @@ -18866,26 +18866,6 @@ react-use@^12.2.0: ts-easing "^0.2.0" tslib "^1.10.0" -react-use@^14.2.0: - version "14.3.0" - resolved "https://registry.npmjs.org/react-use/-/react-use-14.3.0.tgz#aa794db42108e15363be5c04db35a57acf8ecb6b" - integrity sha512-Jx7Zl0k8dHA0UKpTVwYUThC5/V+Dt6JzCGiMHPNIhsxJGkiKuB1AQ7J7pNq4zj3l37ABd/RF+jRGThw0czrJXA== - dependencies: - "@types/js-cookie" "2.2.6" - "@xobotyi/scrollbar-width" "1.9.5" - copy-to-clipboard "^3.2.0" - fast-deep-equal "^3.1.1" - fast-shallow-equal "^1.0.0" - js-cookie "^2.2.1" - nano-css "^5.2.1" - react-universal-interface "^0.6.0" - resize-observer-polyfill "^1.5.1" - screenfull "^5.0.0" - set-harmonic-interval "^1.0.1" - throttle-debounce "^2.1.0" - ts-easing "^0.2.0" - tslib "^1.10.0" - react-use@^15.3.3: version "15.3.3" resolved "https://registry.npmjs.org/react-use/-/react-use-15.3.3.tgz#f16de7a16286c446388e8bd99680952fc3dc9a95" @@ -21882,7 +21862,7 @@ ts-loader@^7.0.4: micromatch "^4.0.0" semver "^6.0.0" -ts-node@^8.10.2, ts-node@^8.6.2: +ts-node@^8.6.2: version "8.10.2" resolved "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== From c9558f65701157a04e812510f6eaa6270d297347 Mon Sep 17 00:00:00 2001 From: ebarrios Date: Fri, 4 Sep 2020 10:23:51 +0200 Subject: [PATCH 297/359] Removed unused import for TableContainer --- .../src/components/ProjectDetailsPage/ProjectDetailsPage.tsx | 1 - .../src/components/ProjectListPage/ProjectListPage.tsx | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx b/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx index 57f1806973..41d0b11019 100644 --- a/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx +++ b/plugins/gcp-projects/src/components/ProjectDetailsPage/ProjectDetailsPage.tsx @@ -23,7 +23,6 @@ import { Table, TableBody, TableCell, - TableContainer, TableRow, Theme, Typography, diff --git a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx index 8d612f75f8..7e09a307c9 100644 --- a/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx +++ b/plugins/gcp-projects/src/components/ProjectListPage/ProjectListPage.tsx @@ -34,7 +34,6 @@ import { Table, TableBody, TableCell, - TableContainer, TableHead, TableRow, Tooltip, @@ -89,7 +88,7 @@ const PageContents = () => { } return ( - +
@@ -141,7 +140,7 @@ const PageContents = () => { ))}
-
+ ); }; From 08374355317d114e0ac182a3676d8ba695490404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Marschollek?= Date: Thu, 3 Sep 2020 09:20:04 +0200 Subject: [PATCH 298/359] Conditionally add Sentry widget to component page This adds the Sentry widget back to the component page (from which it was removed in #2246) if the component declares its own Sentry project ID in the annotation named backstage.io/sentry-project-id. The widget is hidden otherwise. Previous to #2246, the widget was just displaying an empty list because it was trying to load data for a hardcoded project name of sample-sentry-project-id. Fixes #2257 --- plugins/catalog/package.json | 1 + .../EntityPageOverview/EntityPageOverview.tsx | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index b088e1699b..e393be9d17 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -27,6 +27,7 @@ "@backstage/plugin-github-actions": "^0.1.1-alpha.21", "@backstage/plugin-jenkins": "^0.1.1-alpha.21", "@backstage/plugin-scaffolder": "^0.1.1-alpha.21", + "@backstage/plugin-sentry": "^0.1.1-alpha.21", "@backstage/plugin-techdocs": "^0.1.1-alpha.21", "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", diff --git a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx index 9b29b205cf..f261698f7d 100644 --- a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx +++ b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx @@ -17,6 +17,7 @@ // TODO(shmidt-i): move to the app import { Entity } from '@backstage/catalog-model'; import { Content } from '@backstage/core'; +import { SentryIssuesWidget } from '@backstage/plugin-sentry'; import { LatestWorkflowRunCard } from '@backstage/plugin-github-actions'; import { JenkinsBuildsWidget, @@ -52,6 +53,16 @@ export const EntityPageOverview: FC<{ entity: Entity }> = ({ entity }) => { )} + {entity.metadata?.annotations?.['backstage.io/sentry-project-id'] && ( + + + + )} ); From a4b4eb5793bc68d3702080f8882bb426c60c6c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 4 Sep 2020 12:10:58 +0200 Subject: [PATCH 299/359] docs(catalog): document the current set of well known annotations --- .../software-catalog/descriptor-format.md | 13 +- .../well-known-annotations.md | 116 ++++++++++++++++++ .../sentry-backend/src/service/sentry-api.ts | 2 +- 3 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 docs/features/software-catalog/well-known-annotations.md diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 78dfcf365c..e90792c628 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -235,6 +235,9 @@ The `backstage.io/` prefix is reserved for use by Backstage core components. Values can be of any length, but are limited to being strings. +There is a list of [well-known annotations](well-known-annotations.md), but +anybody is free to add more annotations as they see fit. + ### `tags` [optional] A list of single-valued strings, for example to classify catalog entities in @@ -290,7 +293,7 @@ Exactly equal to `backstage.io/v1alpha1` and `Component`, respectively. The type of component as a string, e.g. `website`. This field is required. -The software catalog accepts any type value, but an organisation should take +The software catalog accepts any type value, but an organization should take great care to establish a proper taxonomy for these. Tools including Backstage itself may read this field and behave differently depending on its value. For example, a website type component may present tooling in the Backstage interface @@ -304,9 +307,9 @@ The current set of well-known and common values for this field is: ### `spec.lifecycle` [required] -The lifecyle state of the component, e.g. `production`. This field is required. +The lifecycle state of the component, e.g. `production`. This field is required. -The software catalog accepts any lifecycle value, but an organisation should +The software catalog accepts any lifecycle value, but an organization should take great care to establish a proper taxonomy for these. The current set of well-known and common values for this field is: @@ -418,7 +421,7 @@ potentially search and group templates by these tags. The type of component as a string, e.g. `website`. This field is optional but recommended. -The software catalog accepts any type value, but an organisation should take +The software catalog accepts any type value, but an organization should take great care to establish a proper taxonomy for these. Tools including Backstage itself may read this field and behave differently depending on its value. For example, a website type component may present tooling in the Backstage interface @@ -508,7 +511,7 @@ Exactly equal to `backstage.io/v1alpha1` and `API`, respectively. The type of the API definition as a string, e.g. `openapi`. This field is required. -The software catalog accepts any type value, but an organisation should take +The software catalog accepts any type value, but an organization should take great care to establish a proper taxonomy for these. Tools including Backstage itself may read this field and behave differently depending on its value. For example, an OpenAPI type API may be displayed using an OpenAPI viewer tooling in diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md new file mode 100644 index 0000000000..159cfb4ca1 --- /dev/null +++ b/docs/features/software-catalog/well-known-annotations.md @@ -0,0 +1,116 @@ +--- +id: well-known-annotations +title: Well-known Annotations on Catalog Entities +sidebar_label: Well-known Annotations +--- + +This section lists a number of well known +[annotations](descriptor-format.md#annotations-optional), that have defined +semantics. They can be attached to catalog entities and consumed by plugins as +needed. + +## Annotations + +This is a (non-exhaustive) list of annotations that are known to be in active +use. + +### backstage.io/managed-by-location + +```yaml +# Example: +metadata: + annotations: + backstage.io/managed-by-location: github:http://github.com/spotify/backstage/catalog-info.yaml +``` + +The value of this annotation is a so called location reference string, that +points to the source from which the entity was originally fetched. This +annotation is added automatically by the catalog as it fetches the data from a +registered location, and is not meant to normally be written by humans. The +annotation may point to any type of generic location that the catalog supports, +so it cannot be relied on to always be specifically of type `github`, nor that +it even represents a single file. Note also that a single location can be the +source of many entities, so it represents a many-to-one relationship. + +The format of the value is `:`. Note that the target may also +contain colons, so it is not advisable to naively split the value on `:` and +expecting a two-item array out of it. The format of the target part is +type-dependent and could conceivably even be an empty string, but the separator +colon is always present. + +### backstage.io/techdocs-ref + +```yaml +# Example: +metadata: + annotations: + backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git +``` + +The value of this annotation is a location reference string (see above). If this +annotation is specified, it is expected to point to a repository that the +TechDocs system can read and generate docs from. + +### backstage.io/jenkins-github-folder + +```yaml +# Example: +metadata: + annotations: + backstage.io/jenkins-github-folder: folder-name/job-name +``` + +The value of this annotation is the path to a job on Jenkins, that builds this +entity. + +Specifying this annotation may enable Jenkins related features in Backstage for +that entity. + +### github.com/project-slug + +```yaml +# Example: +metadata: + annotations: + github.com/project-slug: spotify/backstage +``` + +The value of this annotation is the so-called slug that identifies a project on +[GitHub](https://github.com) that is related to this entity. It is on the format +`/`, and is the same as can be seen in the URL location +bar of the browser when viewing that project. + +Specifying this annotation will enable GitHub related features in Backstage for +that entity. + +### sentry.io/project-slug + +```yaml +# Example: +metadata: + annotations: + sentry.io/project-slug: pump-station +``` + +The value of this annotation is the so-called slug (or alternatively, the ID) of +a [Sentry](https://sentry.io) project within your organization. The organization +slug is currently not configurable on a per-entity basis, but is assumed to be +the same for all entities in the catalog. + +Specifying this annotation may enable Sentry related features in Backstage for +that entity. + +## Deprecated Annotations + +The following annotations are deprecated, and only listed here to aid in +migrating away from them. + +### backstage.io/github-actions-id + +This annotation was used for a while to enable the GitHub Actions feature. This +is now instead using the [github.com/project-slug](#github-com-project-slug) +annotation, with the same value format. + +## Links + +- [Descriptor Format: annotations](descriptor-format.md#annotations-optional) diff --git a/plugins/sentry-backend/src/service/sentry-api.ts b/plugins/sentry-backend/src/service/sentry-api.ts index 5c8a9f1aa5..8d35038840 100644 --- a/plugins/sentry-backend/src/service/sentry-api.ts +++ b/plugins/sentry-backend/src/service/sentry-api.ts @@ -26,7 +26,7 @@ export function getRequestHeaders(token: string) { } export function getSentryApiForwarder(token: string, logger: Logger) { - return function fowardRequest( + return function forwardRequest( request: express.Request, response: express.Response, ) { From eb4d54fcc4a2f756a12c56ee36fa4e71b132d6f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Marschollek?= Date: Fri, 4 Sep 2020 12:27:54 +0200 Subject: [PATCH 300/359] Adapt app-driven entity page Addresses the changes required after merging #2076. The Sentry plugin now defines a router and the widget will be displayed as a tab. --- .../app/src/components/catalog/EntityPage.tsx | 11 +++++ .../EntityPageOverview/EntityPageOverview.tsx | 11 ----- plugins/sentry/package.json | 2 + plugins/sentry/src/components/Router.tsx | 49 +++++++++++++++++++ plugins/sentry/src/index.ts | 1 + 5 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 plugins/sentry/src/components/Router.tsx diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 879e2fe6f7..cc1ae8bedc 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ import { Router as GitHubActionsRouter } from '@backstage/plugin-github-actions'; +import { Router as SentryRouter } from '@backstage/plugin-sentry'; import React from 'react'; import { EntityPageLayout, @@ -43,6 +44,11 @@ const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( title="CI/CD" element={} /> + } + /> ); @@ -58,6 +64,11 @@ const WebsiteEntityPage = ({ entity }: { entity: Entity }) => ( title="CI/CD" element={} /> + } + /> ); diff --git a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx index f261698f7d..9b29b205cf 100644 --- a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx +++ b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx @@ -17,7 +17,6 @@ // TODO(shmidt-i): move to the app import { Entity } from '@backstage/catalog-model'; import { Content } from '@backstage/core'; -import { SentryIssuesWidget } from '@backstage/plugin-sentry'; import { LatestWorkflowRunCard } from '@backstage/plugin-github-actions'; import { JenkinsBuildsWidget, @@ -53,16 +52,6 @@ export const EntityPageOverview: FC<{ entity: Entity }> = ({ entity }) => { )} - {entity.metadata?.annotations?.['backstage.io/sentry-project-id'] && ( - - - - )} ); diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index d5b29cc307..294d7159bf 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -21,6 +21,7 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/catalog-model": "^0.1.1-alpha.21", "@backstage/core": "^0.1.1-alpha.21", "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", @@ -29,6 +30,7 @@ "@types/react": "^16.9", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-router": "6.0.0-beta.0", "react-sparklines": "^1.7.0", "react-use": "^15.3.3", "timeago.js": "^4.0.2" diff --git a/plugins/sentry/src/components/Router.tsx b/plugins/sentry/src/components/Router.tsx new file mode 100644 index 0000000000..7c0468fda2 --- /dev/null +++ b/plugins/sentry/src/components/Router.tsx @@ -0,0 +1,49 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { Entity } from '@backstage/catalog-model'; +import { Routes, Route } from 'react-router'; +import { WarningPanel } from '@backstage/core'; +import { SentryPluginWidget } from './SentryPluginWidget/SentryPluginWidget'; + +const SENTRY_ANNOTATION = 'sentry.io/project-id'; + +const isPluginApplicableToEntity = (entity: Entity) => + Boolean(entity.metadata.annotations?.[SENTRY_ANNOTATION]) && + entity.metadata.annotations?.[SENTRY_ANNOTATION] !== ''; + +export const Router = ({ entity }: { entity: Entity }) => + !isPluginApplicableToEntity(entity) ? ( + + `entity.metadata.annotations[' + {SENTRY_ANNOTATION}']` key is missing on the entity.{' '} + + ) : ( + + + } + /> + ) + + ); diff --git a/plugins/sentry/src/index.ts b/plugins/sentry/src/index.ts index 50da801a40..a0d3cab1be 100644 --- a/plugins/sentry/src/index.ts +++ b/plugins/sentry/src/index.ts @@ -15,4 +15,5 @@ */ export { plugin } from './plugin'; +export { Router } from './components/Router'; export { SentryPluginWidget as SentryIssuesWidget } from './components/SentryPluginWidget/SentryPluginWidget'; From bb7430dababac68b9f4989aa5933b2157408f3d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Marschollek?= Date: Fri, 4 Sep 2020 13:56:42 +0200 Subject: [PATCH 301/359] Refactor the Sentry plugin router --- plugins/sentry/src/components/Router.tsx | 31 +++++++++++------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/plugins/sentry/src/components/Router.tsx b/plugins/sentry/src/components/Router.tsx index 7c0468fda2..47d532b4e3 100644 --- a/plugins/sentry/src/components/Router.tsx +++ b/plugins/sentry/src/components/Router.tsx @@ -19,31 +19,28 @@ import { Routes, Route } from 'react-router'; import { WarningPanel } from '@backstage/core'; import { SentryPluginWidget } from './SentryPluginWidget/SentryPluginWidget'; -const SENTRY_ANNOTATION = 'sentry.io/project-id'; +const SENTRY_ANNOTATION = 'sentry.io/project-slug'; -const isPluginApplicableToEntity = (entity: Entity) => - Boolean(entity.metadata.annotations?.[SENTRY_ANNOTATION]) && - entity.metadata.annotations?.[SENTRY_ANNOTATION] !== ''; +export const Router = ({ entity }: { entity: Entity }) => { + const projectId = entity.metadata.annotations?.[SENTRY_ANNOTATION]; -export const Router = ({ entity }: { entity: Entity }) => - !isPluginApplicableToEntity(entity) ? ( - - `entity.metadata.annotations[' - {SENTRY_ANNOTATION}']` key is missing on the entity.{' '} - - ) : ( + if (!projectId) { + return ( + +
{SENTRY_ANNOTATION}
annotation is missing on the entity. +
+ ); + } + + return ( + } /> ) ); +}; From d1e281c50d8d2a2aa2ee06032119814c25779924 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Fri, 4 Sep 2020 11:53:06 +0200 Subject: [PATCH 302/359] refactor(api-docs): update imports --- .../ApiCatalogPage/ApiCatalogPage.tsx | 2 +- .../src/components/ApiCatalogPage/index.ts | 17 ++++++++++++++ .../src/components/ApiCatalogTable/index.ts | 17 ++++++++++++++ .../ApiDefinitionCard/ApiDefinitionCard.tsx | 23 +++++++++++++------ .../src/components/ApiDefinitionCard/index.ts | 17 ++++++++++++++ .../ApiDefinitionWidget.tsx | 6 ++--- .../components/ApiDefinitionWidget/index.ts | 17 ++++++++++++++ .../ApiEntityPage/ApiEntityPage.tsx | 7 +++--- .../src/components/ApiEntityPage/index.ts | 17 ++++++++++++++ .../AsyncApiDefinitionWidget/index.ts | 17 ++++++++++++++ .../OpenApiDefinitionWidget/index.ts | 17 ++++++++++++++ .../PlainApiDefinitionWidget/index.ts | 17 ++++++++++++++ 12 files changed, 159 insertions(+), 15 deletions(-) create mode 100644 plugins/api-docs/src/components/ApiCatalogPage/index.ts create mode 100644 plugins/api-docs/src/components/ApiCatalogTable/index.ts create mode 100644 plugins/api-docs/src/components/ApiDefinitionCard/index.ts create mode 100644 plugins/api-docs/src/components/ApiDefinitionWidget/index.ts create mode 100644 plugins/api-docs/src/components/ApiEntityPage/index.ts create mode 100644 plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts create mode 100644 plugins/api-docs/src/components/OpenApiDefinitionWidget/index.ts create mode 100644 plugins/api-docs/src/components/PlainApiDefinitionWidget/index.ts diff --git a/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx b/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx index f624ec9f92..4f3b2b200d 100644 --- a/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx +++ b/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx @@ -19,7 +19,7 @@ import { Content, useApi } from '@backstage/core'; import { catalogApiRef } from '@backstage/plugin-catalog'; import React from 'react'; import { useAsync } from 'react-use'; -import { ApiCatalogTable } from '../ApiCatalogTable/ApiCatalogTable'; +import { ApiCatalogTable } from '../ApiCatalogTable'; import ApiCatalogLayout from './ApiCatalogLayout'; const CatalogPageContents = () => { diff --git a/plugins/api-docs/src/components/ApiCatalogPage/index.ts b/plugins/api-docs/src/components/ApiCatalogPage/index.ts new file mode 100644 index 0000000000..bec9de705a --- /dev/null +++ b/plugins/api-docs/src/components/ApiCatalogPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ApiCatalogPage } from './ApiCatalogPage'; diff --git a/plugins/api-docs/src/components/ApiCatalogTable/index.ts b/plugins/api-docs/src/components/ApiCatalogTable/index.ts new file mode 100644 index 0000000000..14129b2258 --- /dev/null +++ b/plugins/api-docs/src/components/ApiCatalogTable/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ApiCatalogTable } from './ApiCatalogTable'; diff --git a/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.tsx b/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.tsx index d5bd9d1851..c4a366c4d9 100644 --- a/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.tsx +++ b/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.tsx @@ -14,23 +14,32 @@ * limitations under the License. */ -import { ApiEntityV1alpha1 } from '@backstage/catalog-model'; +import { ApiEntity } from '@backstage/catalog-model'; import { InfoCard } from '@backstage/core'; import React from 'react'; -import { ApiDefinitionWidget } from '../ApiDefinitionWidget/ApiDefinitionWidget'; +import { ApiDefinitionWidget } from '../ApiDefinitionWidget'; +import { Alert } from '@material-ui/lab'; type Props = { title?: string; - apiEntity: ApiEntityV1alpha1; + apiEntity?: ApiEntity; }; export const ApiDefinitionCard = ({ title, apiEntity }: Props) => { - const type = apiEntity?.spec?.type || ''; - const definition = apiEntity?.spec?.definition || ''; + if (!apiEntity) { + return ( + + Could not fetch the API + + ); + } return ( - - + + ); }; diff --git a/plugins/api-docs/src/components/ApiDefinitionCard/index.ts b/plugins/api-docs/src/components/ApiDefinitionCard/index.ts new file mode 100644 index 0000000000..b2a2f3af62 --- /dev/null +++ b/plugins/api-docs/src/components/ApiDefinitionCard/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ApiDefinitionCard } from './ApiDefinitionCard'; diff --git a/plugins/api-docs/src/components/ApiDefinitionWidget/ApiDefinitionWidget.tsx b/plugins/api-docs/src/components/ApiDefinitionWidget/ApiDefinitionWidget.tsx index 0ea894b5a2..e1283bb525 100644 --- a/plugins/api-docs/src/components/ApiDefinitionWidget/ApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/ApiDefinitionWidget/ApiDefinitionWidget.tsx @@ -15,9 +15,9 @@ */ import React from 'react'; -import { AsyncApiDefinitionWidget } from '../AsyncApiDefinitionWidget/AsyncApiDefinitionWidget'; -import { OpenApiDefinitionWidget } from '../OpenApiDefinitionWidget/OpenApiDefinitionWidget'; -import { PlainApiDefinitionWidget } from '../PlainApiDefinitionWidget/PlainApiDefinitionWidget'; +import { AsyncApiDefinitionWidget } from '../AsyncApiDefinitionWidget'; +import { OpenApiDefinitionWidget } from '../OpenApiDefinitionWidget'; +import { PlainApiDefinitionWidget } from '../PlainApiDefinitionWidget'; type Props = { type: string; diff --git a/plugins/api-docs/src/components/ApiDefinitionWidget/index.ts b/plugins/api-docs/src/components/ApiDefinitionWidget/index.ts new file mode 100644 index 0000000000..64c5f87b21 --- /dev/null +++ b/plugins/api-docs/src/components/ApiDefinitionWidget/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ApiDefinitionWidget } from './ApiDefinitionWidget'; diff --git a/plugins/api-docs/src/components/ApiEntityPage/ApiEntityPage.tsx b/plugins/api-docs/src/components/ApiEntityPage/ApiEntityPage.tsx index 6717222a8e..aa0df3f20f 100644 --- a/plugins/api-docs/src/components/ApiEntityPage/ApiEntityPage.tsx +++ b/plugins/api-docs/src/components/ApiEntityPage/ApiEntityPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ApiEntityV1alpha1, Entity } from '@backstage/catalog-model'; +import { ApiEntity, Entity } from '@backstage/catalog-model'; import { Content, errorApiRef, @@ -25,14 +25,13 @@ import { Progress, useApi, } from '@backstage/core'; -// TODO: Circular ref import { catalogApiRef } from '@backstage/plugin-catalog'; import { Box } from '@material-ui/core'; import { Alert } from '@material-ui/lab'; import React, { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useAsync } from 'react-use'; -import { ApiDefinitionCard } from '../ApiDefinitionCard/ApiDefinitionCard'; +import { ApiDefinitionCard } from '../ApiDefinitionCard'; const REDIRECT_DELAY = 1000; function headerProps( @@ -125,7 +124,7 @@ export const ApiEntityPage = () => { {entity && ( <> - + )} diff --git a/plugins/api-docs/src/components/ApiEntityPage/index.ts b/plugins/api-docs/src/components/ApiEntityPage/index.ts new file mode 100644 index 0000000000..561350744b --- /dev/null +++ b/plugins/api-docs/src/components/ApiEntityPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ApiEntityPage } from './ApiEntityPage'; diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts new file mode 100644 index 0000000000..ecafd7d756 --- /dev/null +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { AsyncApiDefinitionWidget } from './AsyncApiDefinitionWidget'; diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/index.ts b/plugins/api-docs/src/components/OpenApiDefinitionWidget/index.ts new file mode 100644 index 0000000000..b2a0f0b86d --- /dev/null +++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { OpenApiDefinitionWidget } from './OpenApiDefinitionWidget'; diff --git a/plugins/api-docs/src/components/PlainApiDefinitionWidget/index.ts b/plugins/api-docs/src/components/PlainApiDefinitionWidget/index.ts new file mode 100644 index 0000000000..c9d18d1ae8 --- /dev/null +++ b/plugins/api-docs/src/components/PlainApiDefinitionWidget/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { PlainApiDefinitionWidget } from './PlainApiDefinitionWidget'; From 8df712b75c0e590bef573367f1a7567130a27ccc Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Fri, 4 Sep 2020 11:54:06 +0200 Subject: [PATCH 303/359] fix(api-docs): resolve style warnings --- .../AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx index 01e2ac33a7..2f64c4b2ab 100644 --- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx @@ -48,7 +48,7 @@ const useStyles = makeStyles(theme => ({ border: `1px solid ${fade(theme.palette.primary.main, 0.5)}`, '&:hover': { textDecoration: 'none', - '&$disabled': { + '&.Mui-disabled': { backgroundColor: 'transparent', }, border: `1px solid ${theme.palette.primary.main}`, @@ -61,7 +61,7 @@ const useStyles = makeStyles(theme => ({ backgroundColor: 'transparent', }, }, - '&$disabled': { + '&.Mui-disabled': { color: theme.palette.action.disabled, }, }, From a93450d55f3ac6dd5c4f89fd24f05f12c6f8366c Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Fri, 4 Sep 2020 12:00:49 +0200 Subject: [PATCH 304/359] feat(api-docs): move the api-tab from the catalog to the plugin and register it in the default app --- .../app/src/components/catalog/EntityPage.tsx | 8 ++- .../default-app/packages/app/package.json.hbs | 1 + .../app/src/components/catalog/EntityPage.tsx | 6 ++ plugins/api-docs/package.json | 1 + .../catalog/EntityPageApi/EntityPageApi.tsx | 47 ++++++++++++ .../src/catalog/EntityPageApi/index.ts | 17 +++++ plugins/api-docs/src/catalog/Router.tsx | 42 +++++++++++ plugins/api-docs/src/catalog/index.ts | 17 +++++ .../ApiCatalogPage/ApiCatalogPage.test.tsx | 1 - .../ApiCatalogPage/ApiCatalogPage.tsx | 1 - plugins/api-docs/src/components/index.ts | 19 +++++ .../src/components/useComponentApiEntities.ts | 69 ++++++++++++++++++ .../src/components/useComponentApiNames.ts | 21 ++++++ plugins/api-docs/src/index.ts | 2 +- plugins/api-docs/src/routes.ts | 5 ++ plugins/catalog/package.json | 1 - .../EntityPageApi/EntityPageApi.tsx | 72 ------------------- 17 files changed, 253 insertions(+), 77 deletions(-) create mode 100644 plugins/api-docs/src/catalog/EntityPageApi/EntityPageApi.tsx create mode 100644 plugins/api-docs/src/catalog/EntityPageApi/index.ts create mode 100644 plugins/api-docs/src/catalog/Router.tsx create mode 100644 plugins/api-docs/src/catalog/index.ts create mode 100644 plugins/api-docs/src/components/index.ts create mode 100644 plugins/api-docs/src/components/useComponentApiEntities.ts create mode 100644 plugins/api-docs/src/components/useComponentApiNames.ts delete mode 100644 plugins/catalog/src/components/EntityPageApi/EntityPageApi.tsx diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index cc1ae8bedc..34fc5c470e 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Router as ApiDocsRouter } from '@backstage/plugin-api-docs'; import { Router as GitHubActionsRouter } from '@backstage/plugin-github-actions'; import { Router as SentryRouter } from '@backstage/plugin-sentry'; import React from 'react'; import { + AboutCard, EntityPageLayout, useEntity, - AboutCard, } from '@backstage/plugin-catalog'; import { Entity } from '@backstage/catalog-model'; import { Grid } from '@material-ui/core'; @@ -49,6 +50,11 @@ const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( title="Sentry" element={} /> + } + /> ); diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs index c80d9c92d8..a52810b31c 100644 --- a/packages/create-app/templates/default-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs @@ -8,6 +8,7 @@ "@material-ui/icons": "^4.9.1", "@backstage/cli": "^{{version}}", "@backstage/core": "^{{version}}", + "@backstage/plugin-api-docs": "^{{version}}", "@backstage/plugin-catalog": "^{{version}}", "@backstage/plugin-register-component": "^{{version}}", "@backstage/plugin-scaffolder": "^{{version}}", diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx index 4087789deb..be22003f4f 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Router as ApiDocsRouter } from '@backstage/plugin-api-docs'; import { Router as GitHubActionsRouter } from '@backstage/plugin-github-actions'; import React from 'react'; import { @@ -38,6 +39,11 @@ const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( title="CI/CD" element={} /> + } + /> ); diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 644c6ed63a..b22cb6fd63 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -31,6 +31,7 @@ "@material-ui/lab": "4.0.0-alpha.45", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3", "swagger-ui-react": "^3.31.1" diff --git a/plugins/api-docs/src/catalog/EntityPageApi/EntityPageApi.tsx b/plugins/api-docs/src/catalog/EntityPageApi/EntityPageApi.tsx new file mode 100644 index 0000000000..da2cf71ac4 --- /dev/null +++ b/plugins/api-docs/src/catalog/EntityPageApi/EntityPageApi.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ComponentEntity, Entity } from '@backstage/catalog-model'; +import { Progress } from '@backstage/core'; +import React, { FC } from 'react'; +import { Grid } from '@material-ui/core'; +import { + ApiDefinitionCard, + useComponentApiEntities, + useComponentApiNames, +} from '../../components'; + +export const EntityPageApi: FC<{ entity: Entity }> = ({ entity }) => { + const apiNames = useComponentApiNames(entity as ComponentEntity); + + const { apiEntities, loading } = useComponentApiEntities({ + entity: entity as ComponentEntity, + }); + + if (loading) { + return ; + } + + return ( + + {apiNames.map(api => ( + + + + ))} + + ); +}; diff --git a/plugins/api-docs/src/catalog/EntityPageApi/index.ts b/plugins/api-docs/src/catalog/EntityPageApi/index.ts new file mode 100644 index 0000000000..1d382e01de --- /dev/null +++ b/plugins/api-docs/src/catalog/EntityPageApi/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { EntityPageApi } from './EntityPageApi'; diff --git a/plugins/api-docs/src/catalog/Router.tsx b/plugins/api-docs/src/catalog/Router.tsx new file mode 100644 index 0000000000..71640954b3 --- /dev/null +++ b/plugins/api-docs/src/catalog/Router.tsx @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { Entity } from '@backstage/catalog-model'; +import { Route, Routes } from 'react-router'; +import { WarningPanel } from '@backstage/core'; +import { catalogRoute } from '../routes'; +import { EntityPageApi } from './EntityPageApi'; + +const isPluginApplicableToEntity = (entity: Entity) => { + return ((entity.spec?.implementsApis as string[]) || []).length > 0; +}; + +export const Router = ({ entity }: { entity: Entity }) => + // TODO(shmidt-i): move warning to a separate standardized component + !isPluginApplicableToEntity(entity) ? ( + + The entity doesn't implement any APIs. + + ) : ( + + } + /> + ) + + ); diff --git a/plugins/api-docs/src/catalog/index.ts b/plugins/api-docs/src/catalog/index.ts new file mode 100644 index 0000000000..4c177df914 --- /dev/null +++ b/plugins/api-docs/src/catalog/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { Router } from './Router'; diff --git a/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.test.tsx b/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.test.tsx index 19cbaf6038..78afa51937 100644 --- a/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.test.tsx +++ b/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.test.tsx @@ -16,7 +16,6 @@ import { Entity } from '@backstage/catalog-model'; import { ApiProvider, ApiRegistry, storageApiRef } from '@backstage/core'; -// TODO: Circular ref! import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog'; import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; import { render } from '@testing-library/react'; diff --git a/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx b/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx index 4f3b2b200d..cfeb71f7b7 100644 --- a/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx +++ b/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx @@ -15,7 +15,6 @@ */ import { Content, useApi } from '@backstage/core'; -// TODO: Circular ref import { catalogApiRef } from '@backstage/plugin-catalog'; import React from 'react'; import { useAsync } from 'react-use'; diff --git a/plugins/api-docs/src/components/index.ts b/plugins/api-docs/src/components/index.ts new file mode 100644 index 0000000000..d49303c24a --- /dev/null +++ b/plugins/api-docs/src/components/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ApiDefinitionCard } from './ApiDefinitionCard'; +export { useComponentApiNames } from './useComponentApiNames'; +export { useComponentApiEntities } from './useComponentApiEntities'; diff --git a/plugins/api-docs/src/components/useComponentApiEntities.ts b/plugins/api-docs/src/components/useComponentApiEntities.ts new file mode 100644 index 0000000000..11b5de988f --- /dev/null +++ b/plugins/api-docs/src/components/useComponentApiEntities.ts @@ -0,0 +1,69 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { useAsyncRetry } from 'react-use'; +import { errorApiRef, useApi } from '@backstage/core'; +import { ApiEntity, ComponentEntity } from '@backstage/catalog-model'; +import { catalogApiRef } from '@backstage/plugin-catalog'; +import { useComponentApiNames } from './useComponentApiNames'; + +export function useComponentApiEntities({ + entity, +}: { + entity: ComponentEntity; +}): { + loading: boolean; + apiEntities?: Map; + error?: Error; + retry: () => void; +} { + const catalogApi = useApi(catalogApiRef); + const errorApi = useApi(errorApiRef); + + const apiNames = useComponentApiNames(entity); + + const { loading, value: apiEntities, retry, error } = useAsyncRetry< + Map + >(async () => { + const resultMap = new Map(); + + await Promise.all( + apiNames.map(async name => { + try { + const api = (await catalogApi.getEntityByName({ + kind: 'API', + name, + })) as ApiEntity | undefined; + + if (api) { + resultMap.set(api.metadata.name, api); + } + } catch (e) { + errorApi.post(e); + } + }), + ); + + return resultMap; + }, [catalogApi, entity]); + + return { + apiEntities, + loading, + error, + retry, + }; +} diff --git a/plugins/api-docs/src/components/useComponentApiNames.ts b/plugins/api-docs/src/components/useComponentApiNames.ts new file mode 100644 index 0000000000..0eabe2b6c7 --- /dev/null +++ b/plugins/api-docs/src/components/useComponentApiNames.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ComponentEntity } from '@backstage/catalog-model'; + +export const useComponentApiNames = (entity: ComponentEntity) => { + return (entity.spec?.implementsApis as string[]) || []; +}; diff --git a/plugins/api-docs/src/index.ts b/plugins/api-docs/src/index.ts index c82a2c263b..958355f063 100644 --- a/plugins/api-docs/src/index.ts +++ b/plugins/api-docs/src/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export { ApiDefinitionCard } from './components/ApiDefinitionCard/ApiDefinitionCard'; +export { Router } from './catalog'; export { plugin } from './plugin'; diff --git a/plugins/api-docs/src/routes.ts b/plugins/api-docs/src/routes.ts index 48c530d4b5..eea911dd62 100644 --- a/plugins/api-docs/src/routes.ts +++ b/plugins/api-docs/src/routes.ts @@ -28,3 +28,8 @@ export const entityRoute = createRouteRef({ path: '/api-docs/:optionalNamespaceAndName/', title: 'API', }); +export const catalogRoute = createRouteRef({ + icon: NoIcon, + path: '', + title: 'API', +}); diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index e393be9d17..5f17fc2a80 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -23,7 +23,6 @@ "dependencies": { "@backstage/catalog-model": "^0.1.1-alpha.21", "@backstage/core": "^0.1.1-alpha.21", - "@backstage/plugin-api-docs": "^0.1.1-alpha.21", "@backstage/plugin-github-actions": "^0.1.1-alpha.21", "@backstage/plugin-jenkins": "^0.1.1-alpha.21", "@backstage/plugin-scaffolder": "^0.1.1-alpha.21", diff --git a/plugins/catalog/src/components/EntityPageApi/EntityPageApi.tsx b/plugins/catalog/src/components/EntityPageApi/EntityPageApi.tsx deleted file mode 100644 index 5c0ff5485b..0000000000 --- a/plugins/catalog/src/components/EntityPageApi/EntityPageApi.tsx +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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 { ApiEntityV1alpha1, Entity } from '@backstage/catalog-model'; -import { Content, Progress, useApi } from '@backstage/core'; -import { ApiDefinitionCard } from '@backstage/plugin-api-docs'; -import { Grid } from '@material-ui/core'; -import { Alert } from '@material-ui/lab'; -import React, { FC } from 'react'; -import { useAsync } from 'react-use'; -import { catalogApiRef } from '../..'; - -export const EntityPageApi: FC<{ entity: Entity }> = ({ entity }) => { - const catalogApi = useApi(catalogApiRef); - - const { value: apiEntities, loading } = useAsync(async () => { - const a = await Promise.all( - ((entity?.spec?.implementsApis as string[]) || []).map(api => - catalogApi.getEntityByName({ - kind: 'API', - name: api, - }), - ), - ); - const b = new Map(); - - a.filter(api => !!api).forEach(api => { - b.set(api?.metadata?.name!, api as ApiEntityV1alpha1); - }); - return b; - }, [catalogApi, entity]); - - return ( - - {loading && } - {!loading && ( - - {((entity?.spec?.implementsApis as string[]) || []).map(api => { - const apiEntity = apiEntities && apiEntities.get(api); - - return ( - - {!apiEntity && ( - - Error on fetching the API: {api} - - )} - - {apiEntity && ( - - )} - - ); - })} - - )} - - ); -}; From 0f74167e824cfe83f9c3f3d11c352e158e00876a Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Fri, 4 Sep 2020 14:17:01 +0200 Subject: [PATCH 305/359] Example Plugin with Grid for Storybook (#2271) * Add an example plugin with a grid of information and data * Adjust types & imports * Abstract Header & ContentHeader for both plugin examples * Fix linting by removing unused import * Remove React.FC --- .../core/src/layout/Page/Page.stories.tsx | 149 ++++++++++++++++-- 1 file changed, 133 insertions(+), 16 deletions(-) diff --git a/packages/core/src/layout/Page/Page.stories.tsx b/packages/core/src/layout/Page/Page.stories.tsx index 786b4740fd..aec99967dc 100644 --- a/packages/core/src/layout/Page/Page.stories.tsx +++ b/packages/core/src/layout/Page/Page.stories.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React from 'react'; +import React, { useState } from 'react'; import { Header, Page, @@ -22,9 +22,18 @@ import { ContentHeader, Content, pageTheme, + InfoCard, + HeaderTabs, } from '../'; -import { SupportButton, Table, StatusOK, TableColumn } from '../../components'; -import { Box, Typography, Link, Chip, Button } from '@material-ui/core'; +import { + SupportButton, + Table, + StatusOK, + TableColumn, + ProgressCard, + TrendLine, +} from '../../components'; +import { Box, Typography, Link, Chip, Grid } from '@material-ui/core'; export default { title: 'Example Plugin', @@ -85,23 +94,131 @@ const columns: TableColumn[] = [ }, ]; +const tabs = [ + { label: 'Overview' }, + { label: 'CI/CD' }, + { label: 'Cost Efficency' }, + { label: 'Code Coverage' }, + { label: 'Test' }, + { label: 'Compliance Advisor' }, +]; + +const DataGrid = () => ( + + + + + + + + + + + + + + + + + + + Rightsize GKE deployment +

+ Services are considered underutilized in GKE when the average usage of + requested cores is less than 80%. +

+ What can I do? +

+ Review requested core and limit settings. Check HPA target scaling + settings in hpa.yaml. The recommended value for + targetCPUUtilizationPercentage is 80. +

+

+ For single pods, there is of course no HPA. But it can also be useful + to think about a single pod out of a larger deployment, then modify + based on HPA requirements. Within a pod, each container has its own + CPU and memory requests and limits. +

+ Definitions +

+ A request is a minimum reserved value; a container will never have + less than this amount allocated to it, even if it doesn't actually use + it. Requests are used for determining what nodes to schedule pods on + (bin-packing). The tension here is between not allocating resources we + don't need, and having easy-enough access to enough resources to be + able to function. +

+ + Contact #cost-awareness for information and support. + +
+
+
+); + +const ExampleHeader = () => ( +
+ + +
+); + +const ExampleContentHeader = ({ selectedTab }: { selectedTab?: number }) => ( + + + This Plugin is an example. This text could provide usefull information for + the user. + + +); + +export const PluginWithData = () => { + const [selectedTab, setSelectedTab] = useState(2); + return ( + + + setSelectedTab(index)} + tabs={tabs.map(({ label }, index) => ({ + id: index.toString(), + label, + }))} + /> + + + + + + ); +}; + export const PluginWithTable = () => { return ( -
- - -
+ - - - - This Plugin is an example. This text could provide usefull - information for the user. - - + Date: Fri, 4 Sep 2020 21:20:21 +0800 Subject: [PATCH 306/359] rename build to dist-types --- .gitignore | 2 +- packages/cli/src/lib/builder/config.ts | 4 ++-- tsconfig.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index deeca8aa11..b9e7f7d597 100644 --- a/.gitignore +++ b/.gitignore @@ -89,7 +89,7 @@ typings/ # Nuxt.js build / generate output .nuxt dist -build +dist-types # Microsite build output microsite/build diff --git a/packages/cli/src/lib/builder/config.ts b/packages/cli/src/lib/builder/config.ts index 78a50e29ed..9a3be63404 100644 --- a/packages/cli/src/lib/builder/config.ts +++ b/packages/cli/src/lib/builder/config.ts @@ -37,7 +37,7 @@ export const makeConfigs = async ( options: BuildOptions, ): Promise => { const typesInput = paths.resolveTargetRoot( - 'build', + 'dist-types', relativePath(paths.targetRoot, paths.targetDir), 'src/index.d.ts', ); @@ -120,7 +120,7 @@ export const makeConfigs = async ( configs.push({ input: typesInput, output: { - file: 'build/index.d.ts', + file: 'dist-types/index.d.ts', format: 'es', }, plugins: [dts()], diff --git a/tsconfig.json b/tsconfig.json index b4eab1a6e0..d6cb6f4c96 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "plugins/*/migrations" ], "compilerOptions": { - "outDir": "build", + "outDir": "dist-types", "rootDir": "." } } From b180748b96cfb6e859dd1e39659b10ac5a7bc5ba Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Fri, 4 Sep 2020 16:18:53 +0200 Subject: [PATCH 307/359] fix(github-actions): use only github.com/project-slug annotation --- catalog-info.yaml | 3 +-- .../catalog/src/components/EntityPageCi/EntityPageCi.tsx | 7 +++++-- .../components/EntityPageOverview/EntityPageOverview.tsx | 7 +++++-- plugins/github-actions/src/components/Cards/Cards.tsx | 3 ++- .../{{cookiecutter.component_id}}/component-info.yaml | 1 - .../{{cookiecutter.component_id}}/component-info.yaml | 1 - 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/catalog-info.yaml b/catalog-info.yaml index 4221ee6dc6..937a55bfca 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -3,10 +3,9 @@ kind: Component metadata: name: backstage description: | - Backstage is an open-source developer portal that puts the developer experience first. + Backstage is an open-source developer portal that puts the developer experience first. annotations: github.com/project-slug: spotify/backstage - backstage.io/github-actions-id: spotify/backstage backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git spec: type: library diff --git a/plugins/catalog/src/components/EntityPageCi/EntityPageCi.tsx b/plugins/catalog/src/components/EntityPageCi/EntityPageCi.tsx index 3261764d11..0974e6bb83 100644 --- a/plugins/catalog/src/components/EntityPageCi/EntityPageCi.tsx +++ b/plugins/catalog/src/components/EntityPageCi/EntityPageCi.tsx @@ -17,7 +17,10 @@ // TODO(shmidt-i): move to the app import { Entity } from '@backstage/catalog-model'; import { Content } from '@backstage/core'; -import { LatestWorkflowsForBranchCard } from '@backstage/plugin-github-actions'; +import { + LatestWorkflowsForBranchCard, + GITHUB_ACTIONS_ANNOTATION, +} from '@backstage/plugin-github-actions'; import { Grid } from '@material-ui/core'; import React, { FC } from 'react'; @@ -25,7 +28,7 @@ export const EntityPageCi: FC<{ entity: Entity }> = ({ entity }) => { return ( - {entity.metadata?.annotations?.['backstage.io/github-actions-id'] && ( + {entity.metadata?.annotations?.[GITHUB_ACTIONS_ANNOTATION] && ( diff --git a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx index 9b29b205cf..c43e83edaa 100644 --- a/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx +++ b/plugins/catalog/src/components/EntityPageOverview/EntityPageOverview.tsx @@ -17,7 +17,10 @@ // TODO(shmidt-i): move to the app import { Entity } from '@backstage/catalog-model'; import { Content } from '@backstage/core'; -import { LatestWorkflowRunCard } from '@backstage/plugin-github-actions'; +import { + LatestWorkflowRunCard, + GITHUB_ACTIONS_ANNOTATION, +} from '@backstage/plugin-github-actions'; import { JenkinsBuildsWidget, JenkinsLastBuildWidget, @@ -47,7 +50,7 @@ export const EntityPageOverview: FC<{ entity: Entity }> = ({ entity }) => { )} - {entity.metadata?.annotations?.['backstage.io/github-actions-id'] && ( + {entity.metadata?.annotations?.[GITHUB_ACTIONS_ANNOTATION] && ( diff --git a/plugins/github-actions/src/components/Cards/Cards.tsx b/plugins/github-actions/src/components/Cards/Cards.tsx index 478b3810cb..28783efdea 100644 --- a/plugins/github-actions/src/components/Cards/Cards.tsx +++ b/plugins/github-actions/src/components/Cards/Cards.tsx @@ -32,6 +32,7 @@ import { useApi, } from '@backstage/core'; import ExternalLinkIcon from '@material-ui/icons/Launch'; +import { GITHUB_ACTIONS_ANNOTATION } from '../useProjectName'; const useStyles = makeStyles({ externalLinkIcon: { @@ -83,7 +84,7 @@ export const LatestWorkflowRunCard = ({ }) => { const errorApi = useApi(errorApiRef); const [owner, repo] = ( - entity?.metadata.annotations?.['backstage.io/github-actions-id'] ?? '/' + entity?.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION] ?? '/' ).split('/'); const [{ runs, loading, error }] = useWorkflowRuns({ owner, diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/component-info.yaml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/component-info.yaml index 832b539ac6..f6e1d2c8d9 100644 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/component-info.yaml +++ b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/component-info.yaml @@ -5,7 +5,6 @@ metadata: description: {{cookiecutter.description}} annotations: github.com/project-slug: {{cookiecutter.storePath}} - backstage.io/github-actions-id: {{cookiecutter.storePath}} backstage.io/techdocs-ref: github:https://github.com/{{cookiecutter.storePath}} spec: type: website diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/component-info.yaml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/component-info.yaml index 15c8fdcfff..618209f44c 100644 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/component-info.yaml +++ b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/component-info.yaml @@ -5,7 +5,6 @@ metadata: description: {{cookiecutter.description}} annotations: github.com/project-slug: {{cookiecutter.storePath}} - backstage.io/github-actions-id: {{cookiecutter.storePath}} spec: type: service lifecycle: experimental From 4f172359267b6f685e6a073f56588500d2b6763b Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Sat, 5 Sep 2020 15:58:34 +0800 Subject: [PATCH 308/359] create-app tsconfig --- packages/cli/src/commands/clean/clean.ts | 1 + packages/create-app/templates/default-app/.gitignore | 1 + packages/create-app/templates/default-app/tsconfig.json | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/clean/clean.ts b/packages/cli/src/commands/clean/clean.ts index 2fa3e4a18f..bc2bcd23ac 100644 --- a/packages/cli/src/commands/clean/clean.ts +++ b/packages/cli/src/commands/clean/clean.ts @@ -19,5 +19,6 @@ import { paths } from '../../lib/paths'; export default async function clean() { await fs.remove(paths.resolveTarget('dist')); + await fs.remove(paths.resolveTarget('dist-types')); await fs.remove(paths.resolveTarget('coverage')); } diff --git a/packages/create-app/templates/default-app/.gitignore b/packages/create-app/templates/default-app/.gitignore index 3e786d566e..4f9065c60b 100644 --- a/packages/create-app/templates/default-app/.gitignore +++ b/packages/create-app/templates/default-app/.gitignore @@ -22,6 +22,7 @@ node_modules/ # Build output dist +dist-types # Temporary change files created by Vim *.swp diff --git a/packages/create-app/templates/default-app/tsconfig.json b/packages/create-app/templates/default-app/tsconfig.json index cccc375f63..b1ec99b986 100644 --- a/packages/create-app/templates/default-app/tsconfig.json +++ b/packages/create-app/templates/default-app/tsconfig.json @@ -8,7 +8,7 @@ ], "exclude": ["node_modules"], "compilerOptions": { - "outDir": "dist", + "outDir": "dist-types", "rootDir": ".", "skipLibCheck": true } From 0499c27fec34c68d0bcd04e52234b6a71f4e96db Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Sat, 5 Sep 2020 16:45:25 +0800 Subject: [PATCH 309/359] index types --- packages/cli/src/lib/builder/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/lib/builder/config.ts b/packages/cli/src/lib/builder/config.ts index 9a3be63404..e91fc32633 100644 --- a/packages/cli/src/lib/builder/config.ts +++ b/packages/cli/src/lib/builder/config.ts @@ -120,7 +120,7 @@ export const makeConfigs = async ( configs.push({ input: typesInput, output: { - file: 'dist-types/index.d.ts', + file: 'dist/index.d.ts', format: 'es', }, plugins: [dts()], From 66f53328f89323c3cf2ca00721c03f4c1e93c6eb Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 5 Sep 2020 11:16:14 +0200 Subject: [PATCH 310/359] microsite: remove from workspace and build independently --- .github/workflows/e2e-win.yml | 2 +- .github/workflows/e2e.yml | 2 +- .github/workflows/microsite-build-check.yml | 28 +- .../microsite-with-storybook-deploy.yml | 8 +- .gitignore | 4 - microsite/.gitignore | 4 + microsite/README.md | 26 +- microsite/package.json | 2 +- microsite/yarn.lock | 6650 +++++++++++++++++ package.json | 3 +- yarn.lock | 1329 +--- 11 files changed, 6752 insertions(+), 1306 deletions(-) create mode 100644 microsite/.gitignore create mode 100644 microsite/yarn.lock diff --git a/.github/workflows/e2e-win.yml b/.github/workflows/e2e-win.yml index 5ebf67bc9a..b4ea1e350f 100644 --- a/.github/workflows/e2e-win.yml +++ b/.github/workflows/e2e-win.yml @@ -49,6 +49,6 @@ jobs: - run: yarn tsc - name: yarn build - run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite + run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli - name: run E2E test run: yarn workspace e2e-test start diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 401fe7297a..d53ca1b607 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -64,7 +64,7 @@ jobs: - run: yarn tsc - name: yarn build - run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite + run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli - name: run E2E test run: | sudo sysctl fs.inotify.max_user_watches=524288 diff --git a/.github/workflows/microsite-build-check.yml b/.github/workflows/microsite-build-check.yml index dc86ca2630..ff290b71e4 100644 --- a/.github/workflows/microsite-build-check.yml +++ b/.github/workflows/microsite-build-check.yml @@ -22,33 +22,17 @@ jobs: steps: - uses: actions/checkout@v2 - # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - registry-url: https://registry.npmjs.org/ # Needed for auth - - name: cache all node_modules - id: cache-modules - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} - - name: find location of global yarn cache - id: yarn-cache - if: steps.cache-modules.outputs.cache-hit != 'true' - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - if: steps.cache-modules.outputs.cache-hit != 'true' - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- + + # Skip caching of microsite dependencies, it keeps the global cache size + # smaller, which make Windows builds a lot faster for the rest of the project. - name: yarn install run: yarn install --frozen-lockfile - # End of yarn setup + working-directory: microsite - name: build microsite - run: yarn workspace backstage-microsite build + run: yarn build + working-directory: microsite diff --git a/.github/workflows/microsite-with-storybook-deploy.yml b/.github/workflows/microsite-with-storybook-deploy.yml index 2c8b8af40c..a1e9723be0 100644 --- a/.github/workflows/microsite-with-storybook-deploy.yml +++ b/.github/workflows/microsite-with-storybook-deploy.yml @@ -54,8 +54,14 @@ jobs: run: yarn install --frozen-lockfile # End of yarn setup + # Previous install was to be able to build storybook, this is for the microsite, and we skip caching + - name: yarn install + run: yarn install --frozen-lockfile + working-directory: microsite + - name: build microsite - run: yarn workspace backstage-microsite build + run: yarn build + working-directory: microsite - name: build storybook run: yarn workspace storybook build-storybook diff --git a/.gitignore b/.gitignore index aee80f4c10..9884edf96e 100644 --- a/.gitignore +++ b/.gitignore @@ -90,10 +90,6 @@ typings/ .nuxt dist -# Microsite build output -microsite/build -microsite/i18n - # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js diff --git a/microsite/.gitignore b/microsite/.gitignore new file mode 100644 index 0000000000..f5bcdb3385 --- /dev/null +++ b/microsite/.gitignore @@ -0,0 +1,4 @@ + +# Build output +build +i18n diff --git a/microsite/README.md b/microsite/README.md index d7187888a3..d244caa22a 100644 --- a/microsite/README.md +++ b/microsite/README.md @@ -2,12 +2,36 @@ This website was created with [Docusaurus](https://docusaurus.io/). # What's In This Document -- [Get Started in 5 Minutes](#get-started-in-5-minutes) +- [Getting Started](#getting-started) - [Directory Structure](#directory-structure) - [Editing Content](#editing-content) - [Adding Content](#adding-content) - [Full Documentation](#full-documentation) +# Getting Started + +## Installation + +``` +$ yarn install +``` + +## Local Development + +``` +$ yarn start +``` + +This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. + +## Build + +``` +$ yarn build +``` + +This command generates static content into the `build` directory, which is what will be deployed to GitHub pages from the master branch. + ## Directory Structure Your project file structure should look something like this diff --git a/microsite/package.json b/microsite/package.json index ee3c7f8dc2..25598c18b4 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -1,5 +1,5 @@ { - "version": "0.1.1-alpha.21", + "version": "0.0.0", "name": "backstage-microsite", "license": "Apache-2.0", "private": true, diff --git a/microsite/yarn.lock b/microsite/yarn.lock new file mode 100644 index 0000000000..422959d3fd --- /dev/null +++ b/microsite/yarn.lock @@ -0,0 +1,6650 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@7.5.5": + version "7.5.5" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" + integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0": + version "7.11.0" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.11.0.tgz#e9f73efe09af1355b723a7f39b11bad637d7c99c" + integrity sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ== + dependencies: + browserslist "^4.12.0" + invariant "^2.2.4" + semver "^5.5.0" + +"@babel/core@^7.9.0": + version "7.11.6" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" + integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.6" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.5" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.5" + "@babel/types" "^7.11.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": + version "7.11.6" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" + integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== + dependencies: + "@babel/types" "^7.11.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" + integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" + integrity sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-builder-react-jsx-experimental@^7.10.4", "@babel/helper-builder-react-jsx-experimental@^7.11.5": + version "7.11.5" + resolved "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.11.5.tgz#4ea43dd63857b0a35cd1f1b161dc29b43414e79f" + integrity sha512-Vc4aPJnRZKWfzeCBsqTBnzulVNjABVdahSPhtdMD3Vs80ykx4a87jTHtF/VR+alSrDmNvat7l13yrRHauGcHVw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-module-imports" "^7.10.4" + "@babel/types" "^7.11.5" + +"@babel/helper-builder-react-jsx@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz#8095cddbff858e6fa9c326daee54a2f2732c1d5d" + integrity sha512-5nPcIZ7+KKDxT1427oBivl9V9YTal7qk0diccnh7RrcgrT/pGFOjgGw1dgryyx1GvHEpXVfoDF6Ak3rTiWh8Rg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-compilation-targets@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz#804ae8e3f04376607cc791b9d47d540276332bd2" + integrity sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ== + dependencies: + "@babel/compat-data" "^7.10.4" + browserslist "^4.12.0" + invariant "^2.2.4" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/helper-create-class-features-plugin@^7.10.4": + version "7.10.5" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" + integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.10.5" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + +"@babel/helper-create-regexp-features-plugin@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" + integrity sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + regexpu-core "^4.7.0" + +"@babel/helper-define-map@^7.10.4": + version "7.10.5" + resolved "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" + integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/types" "^7.10.5" + lodash "^4.17.19" + +"@babel/helper-explode-assignable-expression@^7.10.4": + version "7.11.4" + resolved "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz#2d8e3470252cc17aba917ede7803d4a7a276a41b" + integrity sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-hoist-variables@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" + integrity sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5": + version "7.11.0" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" + integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-module-imports@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" + integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0": + version "7.11.0" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" + integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/template" "^7.10.4" + "@babel/types" "^7.11.0" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-regex@^7.10.4": + version "7.10.5" + resolved "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0" + integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg== + dependencies: + lodash "^4.17.19" + +"@babel/helper-remap-async-to-generator@^7.10.4": + version "7.11.4" + resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz#4474ea9f7438f18575e30b0cac784045b402a12d" + integrity sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-wrap-function" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-replace-supers@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" + integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-simple-access@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" + integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== + dependencies: + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-skip-transparent-expression-wrappers@^7.11.0": + version "7.11.0" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729" + integrity sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/helper-wrap-function@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" + integrity sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helpers@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" + integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/highlight@^7.0.0", "@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.10.4", "@babel/parser@^7.11.5": + version "7.11.5" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" + integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== + +"@babel/plugin-proposal-async-generator-functions@^7.10.4": + version "7.10.5" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" + integrity sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" + "@babel/plugin-syntax-async-generators" "^7.8.0" + +"@babel/plugin-proposal-class-properties@^7.10.4", "@babel/plugin-proposal-class-properties@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807" + integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-dynamic-import@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e" + integrity sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + +"@babel/plugin-proposal-export-namespace-from@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz#570d883b91031637b3e2958eea3c438e62c05f54" + integrity sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db" + integrity sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.0" + +"@babel/plugin-proposal-logical-assignment-operators@^7.11.0": + version "7.11.0" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8" + integrity sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a" + integrity sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + +"@babel/plugin-proposal-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz#ce1590ff0a65ad12970a609d78855e9a4c1aef06" + integrity sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.11.0", "@babel/plugin-proposal-object-rest-spread@^7.9.0": + version "7.11.0" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af" + integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.10.4" + +"@babel/plugin-proposal-optional-catch-binding@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd" + integrity sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + +"@babel/plugin-proposal-optional-chaining@^7.11.0": + version "7.11.0" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076" + integrity sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + +"@babel/plugin-proposal-private-methods@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909" + integrity sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-unicode-property-regex@^7.10.4", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d" + integrity sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-async-generators@^7.8.0": + version "7.8.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" + integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-dynamic-import@^7.8.0": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-json-strings@^7.8.0": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz#39abaae3cbf710c4373d8429484e6ba21340166c" + integrity sha512-KCg9mio9jwiARCB7WAcQ7Y1q+qicILjoK8LP/VkPkEKaf5dkaZZK1EcTe91a3JJlZ3qy6L5s9X52boEYi8DM9g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.0": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.0": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.0": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz#4bbeb8917b54fcf768364e0a81f560e33a3ef57d" + integrity sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-arrow-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" + integrity sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-async-to-generator@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37" + integrity sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" + +"@babel/plugin-transform-block-scoped-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8" + integrity sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-block-scoping@^7.10.4": + version "7.11.1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215" + integrity sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-classes@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" + integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-define-map" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb" + integrity sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-destructuring@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5" + integrity sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-dotall-regex@^7.10.4", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee" + integrity sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-duplicate-keys@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47" + integrity sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-exponentiation-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e" + integrity sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-for-of@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9" + integrity sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7" + integrity sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c" + integrity sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7" + integrity sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-modules-amd@^7.10.4": + version "7.10.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1" + integrity sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw== + dependencies: + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-commonjs@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0" + integrity sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w== + dependencies: + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-systemjs@^7.10.4": + version "7.10.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85" + integrity sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw== + dependencies: + "@babel/helper-hoist-variables" "^7.10.4" + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-umd@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e" + integrity sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA== + dependencies: + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6" + integrity sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + +"@babel/plugin-transform-new-target@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888" + integrity sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-object-super@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894" + integrity sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + +"@babel/plugin-transform-parameters@^7.10.4": + version "7.10.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a" + integrity sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-property-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0" + integrity sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-react-display-name@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.4.tgz#b5795f4e3e3140419c3611b7a2a3832b9aef328d" + integrity sha512-Zd4X54Mu9SBfPGnEcaGcOrVAYOtjT2on8QZkLKEq1S/tHexG39d9XXGZv19VfRrDjPJzFmPfTAqOQS1pfFOujw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-react-jsx-development@^7.10.4": + version "7.11.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.11.5.tgz#e1439e6a57ee3d43e9f54ace363fb29cefe5d7b6" + integrity sha512-cImAmIlKJ84sDmpQzm4/0q/2xrXlDezQoixy3qoz1NJeZL/8PRon6xZtluvr4H4FzwlDGI5tCcFupMnXGtr+qw== + dependencies: + "@babel/helper-builder-react-jsx-experimental" "^7.11.5" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.10.4" + +"@babel/plugin-transform-react-jsx-self@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.4.tgz#cd301a5fed8988c182ed0b9d55e9bd6db0bd9369" + integrity sha512-yOvxY2pDiVJi0axdTWHSMi5T0DILN+H+SaeJeACHKjQLezEzhLx9nEF9xgpBLPtkZsks9cnb5P9iBEi21En3gg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.10.4" + +"@babel/plugin-transform-react-jsx-source@^7.10.4": + version "7.10.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.5.tgz#34f1779117520a779c054f2cdd9680435b9222b4" + integrity sha512-wTeqHVkN1lfPLubRiZH3o73f4rfon42HpgxUSs86Nc+8QIcm/B9s8NNVXu/gwGcOyd7yDib9ikxoDLxJP0UiDA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.10.4" + +"@babel/plugin-transform-react-jsx@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz#673c9f913948764a4421683b2bef2936968fddf2" + integrity sha512-L+MfRhWjX0eI7Js093MM6MacKU4M6dnCRa/QPDwYMxjljzSCzzlzKzj9Pk4P3OtrPcxr2N3znR419nr3Xw+65A== + dependencies: + "@babel/helper-builder-react-jsx" "^7.10.4" + "@babel/helper-builder-react-jsx-experimental" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.10.4" + +"@babel/plugin-transform-react-pure-annotations@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.4.tgz#3eefbb73db94afbc075f097523e445354a1c6501" + integrity sha512-+njZkqcOuS8RaPakrnR9KvxjoG1ASJWpoIv/doyWngId88JoFlPlISenGXjrVacZUIALGUr6eodRs1vmPnF23A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-regenerator@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63" + integrity sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw== + dependencies: + regenerator-transform "^0.14.2" + +"@babel/plugin-transform-reserved-words@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd" + integrity sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-shorthand-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6" + integrity sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-spread@^7.11.0": + version "7.11.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc" + integrity sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" + +"@babel/plugin-transform-sticky-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d" + integrity sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + +"@babel/plugin-transform-template-literals@^7.10.4": + version "7.10.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c" + integrity sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-typeof-symbol@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc" + integrity sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-unicode-escapes@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" + integrity sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-unicode-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8" + integrity sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/polyfill@^7.8.7": + version "7.11.5" + resolved "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.11.5.tgz#df550b2ec53abbc2ed599367ec59e64c7a707bb5" + integrity sha512-FunXnE0Sgpd61pKSj2OSOs1D44rKTD3pGOfGilZ6LGrrIH0QEtJlTjqOqdF8Bs98JmjfGhni2BBkTfv9KcKJ9g== + dependencies: + core-js "^2.6.5" + regenerator-runtime "^0.13.4" + +"@babel/preset-env@^7.9.0": + version "7.11.5" + resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.5.tgz#18cb4b9379e3e92ffea92c07471a99a2914e4272" + integrity sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA== + dependencies: + "@babel/compat-data" "^7.11.0" + "@babel/helper-compilation-targets" "^7.10.4" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-async-generator-functions" "^7.10.4" + "@babel/plugin-proposal-class-properties" "^7.10.4" + "@babel/plugin-proposal-dynamic-import" "^7.10.4" + "@babel/plugin-proposal-export-namespace-from" "^7.10.4" + "@babel/plugin-proposal-json-strings" "^7.10.4" + "@babel/plugin-proposal-logical-assignment-operators" "^7.11.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.4" + "@babel/plugin-proposal-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread" "^7.11.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.10.4" + "@babel/plugin-proposal-optional-chaining" "^7.11.0" + "@babel/plugin-proposal-private-methods" "^7.10.4" + "@babel/plugin-proposal-unicode-property-regex" "^7.10.4" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.10.4" + "@babel/plugin-transform-arrow-functions" "^7.10.4" + "@babel/plugin-transform-async-to-generator" "^7.10.4" + "@babel/plugin-transform-block-scoped-functions" "^7.10.4" + "@babel/plugin-transform-block-scoping" "^7.10.4" + "@babel/plugin-transform-classes" "^7.10.4" + "@babel/plugin-transform-computed-properties" "^7.10.4" + "@babel/plugin-transform-destructuring" "^7.10.4" + "@babel/plugin-transform-dotall-regex" "^7.10.4" + "@babel/plugin-transform-duplicate-keys" "^7.10.4" + "@babel/plugin-transform-exponentiation-operator" "^7.10.4" + "@babel/plugin-transform-for-of" "^7.10.4" + "@babel/plugin-transform-function-name" "^7.10.4" + "@babel/plugin-transform-literals" "^7.10.4" + "@babel/plugin-transform-member-expression-literals" "^7.10.4" + "@babel/plugin-transform-modules-amd" "^7.10.4" + "@babel/plugin-transform-modules-commonjs" "^7.10.4" + "@babel/plugin-transform-modules-systemjs" "^7.10.4" + "@babel/plugin-transform-modules-umd" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.10.4" + "@babel/plugin-transform-new-target" "^7.10.4" + "@babel/plugin-transform-object-super" "^7.10.4" + "@babel/plugin-transform-parameters" "^7.10.4" + "@babel/plugin-transform-property-literals" "^7.10.4" + "@babel/plugin-transform-regenerator" "^7.10.4" + "@babel/plugin-transform-reserved-words" "^7.10.4" + "@babel/plugin-transform-shorthand-properties" "^7.10.4" + "@babel/plugin-transform-spread" "^7.11.0" + "@babel/plugin-transform-sticky-regex" "^7.10.4" + "@babel/plugin-transform-template-literals" "^7.10.4" + "@babel/plugin-transform-typeof-symbol" "^7.10.4" + "@babel/plugin-transform-unicode-escapes" "^7.10.4" + "@babel/plugin-transform-unicode-regex" "^7.10.4" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.11.5" + browserslist "^4.12.0" + core-js-compat "^3.6.2" + invariant "^2.2.2" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/preset-modules@^0.1.3": + version "0.1.4" + resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" + integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.9.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.10.4.tgz#92e8a66d816f9911d11d4cc935be67adfc82dbcf" + integrity sha512-BrHp4TgOIy4M19JAfO1LhycVXOPWdDbTRep7eVyatf174Hff+6Uk53sDyajqZPu8W1qXRBiYOfIamek6jA7YVw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-react-display-name" "^7.10.4" + "@babel/plugin-transform-react-jsx" "^7.10.4" + "@babel/plugin-transform-react-jsx-development" "^7.10.4" + "@babel/plugin-transform-react-jsx-self" "^7.10.4" + "@babel/plugin-transform-react-jsx-source" "^7.10.4" + "@babel/plugin-transform-react-pure-annotations" "^7.10.4" + +"@babel/register@^7.9.0": + version "7.11.5" + resolved "https://registry.npmjs.org/@babel/register/-/register-7.11.5.tgz#79becf89e0ddd0fba8b92bc279bc0f5d2d7ce2ea" + integrity sha512-CAml0ioKX+kOAvBQDHa/+t1fgOt3qkTIz0TrRtRAT6XY0m5qYZXR85k6/sLCNPMGhYDlCFHCYuU0ybTJbvlC6w== + dependencies: + find-cache-dir "^2.0.0" + lodash "^4.17.19" + make-dir "^2.1.0" + pirates "^4.0.0" + source-map-support "^0.5.16" + +"@babel/runtime@^7.8.4": + version "7.11.2" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" + integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5", "@babel/traverse@^7.9.0": + version "7.11.5" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" + integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.5" + "@babel/types" "^7.11.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.4.4", "@babel/types@^7.9.0": + version "7.11.5" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@sindresorhus/is@^0.7.0": + version "0.7.0" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" + integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== + +"@types/cheerio@^0.22.8": + version "0.22.21" + resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3" + integrity sha512-aGI3DfswwqgKPiEOTaiHV2ZPC9KEhprpgEbJnv0fZl3SGX0cGgEva1126dGrMC6AJM6v/aihlUgJn9M5DbDZ/Q== + dependencies: + "@types/node" "*" + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/node@*": + version "14.6.4" + resolved "https://registry.npmjs.org/@types/node/-/node-14.6.4.tgz#a145cc0bb14ef9c4777361b7bbafa5cf8e3acb5a" + integrity sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ== + +"@types/q@^1.5.1": + version "1.5.4" + resolved "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" + integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +address@1.1.2, address@^1.0.1: + version "1.1.2" + resolved "https://registry.npmjs.org/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" + integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== + +ajv@^6.12.3: + version "6.12.4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +alphanum-sort@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= + +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-red@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c" + integrity sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw= + dependencies: + ansi-wrap "0.1.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +ansi-wrap@0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" + integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +arch@^2.1.0: + version "2.1.2" + resolved "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz#0c52bbe7344bb4fa260c443d2cbad9c00ff2f0bf" + integrity sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ== + +archive-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz#f92e72233056dfc6969472749c267bdb046b1d70" + integrity sha1-+S5yIzBW38aWlHJ0nCZ72wRrHXA= + dependencies: + file-type "^4.2.0" + +argparse@^1.0.10, argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async@^2.6.2: + version "2.6.3" + resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +autolinker@^3.11.0: + version "3.14.1" + resolved "https://registry.npmjs.org/autolinker/-/autolinker-3.14.1.tgz#6ae4b812b6eaf42d4d68138b9e67757cbf2bc1e4" + integrity sha512-yvsRHIaY51EYDml6MGlbqyJGfl4n7zezGYf+R7gvM8c5LNpRGc4SISkvgAswSS8SWxk/OrGCylKV9mJyVstz7w== + dependencies: + tslib "^1.9.3" + +autolinker@~0.28.0: + version "0.28.1" + resolved "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz#0652b491881879f0775dace0cdca3233942a4e47" + integrity sha1-BlK0kYgYefB3XazgzcoyM5QqTkc= + dependencies: + gulp-header "^1.7.1" + +autoprefixer@^9.7.5: + version "9.8.6" + resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" + integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== + dependencies: + browserslist "^4.12.0" + caniuse-lite "^1.0.30001109" + colorette "^1.2.1" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.32" + postcss-value-parser "^4.1.0" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.10.1" + resolved "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" + integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== + +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +bin-build@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/bin-build/-/bin-build-3.0.0.tgz#c5780a25a8a9f966d8244217e6c1f5082a143861" + integrity sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA== + dependencies: + decompress "^4.0.0" + download "^6.2.2" + execa "^0.7.0" + p-map-series "^1.0.0" + tempfile "^2.0.0" + +bin-check@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz#fc495970bdc88bb1d5a35fc17e65c4a149fc4a49" + integrity sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA== + dependencies: + execa "^0.7.0" + executable "^4.1.0" + +bin-version-check@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/bin-version-check/-/bin-version-check-4.0.0.tgz#7d819c62496991f80d893e6e02a3032361608f71" + integrity sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ== + dependencies: + bin-version "^3.0.0" + semver "^5.6.0" + semver-truncate "^1.1.2" + +bin-version@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/bin-version/-/bin-version-3.1.0.tgz#5b09eb280752b1bd28f0c9db3f96f2f43b6c0839" + integrity sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ== + dependencies: + execa "^1.0.0" + find-versions "^3.0.0" + +bin-wrapper@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/bin-wrapper/-/bin-wrapper-4.1.0.tgz#99348f2cf85031e3ef7efce7e5300aeaae960605" + integrity sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q== + dependencies: + bin-check "^4.1.0" + bin-version-check "^4.0.0" + download "^7.1.0" + import-lazy "^3.1.0" + os-filter-obj "^2.0.0" + pify "^4.0.1" + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bl@^1.0.0: + version "1.2.3" + resolved "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" + integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +body@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069" + integrity sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk= + dependencies: + continuable-cache "^0.3.1" + error "^7.0.0" + raw-body "~1.1.0" + safe-json-parse "~1.0.1" + +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +browserslist@4.7.0: + version "4.7.0" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz#9ee89225ffc07db03409f2fee524dc8227458a17" + integrity sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA== + dependencies: + caniuse-lite "^1.0.30000989" + electron-to-chromium "^1.3.247" + node-releases "^1.1.29" + +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.5: + version "4.14.1" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.14.1.tgz#cb2b490ba881d45dc3039078c7ed04411eaf3fa3" + integrity sha512-zyBTIHydW37pnb63c7fHFXUG6EcqWOqoMdDx6cdyaDFriZ20EoVxcE95S54N+heRqY8m8IUgB5zYta/gCwSaaA== + dependencies: + caniuse-lite "^1.0.30001124" + electron-to-chromium "^1.3.562" + escalade "^3.0.2" + node-releases "^1.1.60" + +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer@^5.2.1: + version "5.6.0" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +bytes@1: + version "1.0.0" + resolved "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" + integrity sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-request@^2.1.1: + version "2.1.4" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" + integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0= + dependencies: + clone-response "1.0.2" + get-stream "3.0.0" + http-cache-semantics "3.8.1" + keyv "3.0.0" + lowercase-keys "1.0.0" + normalize-url "2.0.1" + responselike "1.0.2" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001124: + version "1.0.30001124" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001124.tgz#5d9998190258e11630d674fc50ea8e579ae0ced2" + integrity sha512-zQW8V3CdND7GHRH6rxm6s59Ww4g/qGWTheoboW9nfeMg7sUoopIfKCcNZUjwYRCOrvereh3kwDpZj4VLQ7zGtA== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +caw@^2.0.0, caw@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz#6c3ca071fc194720883c2dc5da9b074bfc7e9e95" + integrity sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA== + dependencies: + get-proxy "^2.0.0" + isurl "^1.0.0-alpha5" + tunnel-agent "^0.6.0" + url-to-options "^1.0.1" + +chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^1.0.0, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +cheerio@0.22.0: + version "0.22.0" + resolved "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" + integrity sha1-qbqoYKP5tZWmuBsahocxIe06Jp4= + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash.assignin "^4.0.9" + lodash.bind "^4.1.4" + lodash.defaults "^4.0.1" + lodash.filter "^4.4.0" + lodash.flatten "^4.2.0" + lodash.foreach "^4.3.0" + lodash.map "^4.4.0" + lodash.merge "^4.4.0" + lodash.pick "^4.2.1" + lodash.reduce "^4.4.0" + lodash.reject "^4.4.0" + lodash.some "^4.4.0" + +chokidar@^2.0.4: + version "2.1.8" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +classnames@^2.2.6: + version "2.2.6" + resolved "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + +clipboard@^2.0.0: + version "2.0.6" + resolved "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376" + integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg== + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + +clone-response@1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + +coffee-script@^1.12.4: + version "1.12.7" + resolved "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz#c05dae0cb79591d05b3070a8433a98c9a89ccc53" + integrity sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0, color-convert@^1.9.1: + version "1.9.3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.0.0: + version "3.1.2" + resolved "https://registry.npmjs.org/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" + integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +colorette@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" + integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^2.8.1: + version "2.20.3" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^4.0.1: + version "4.1.1" + resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +commander@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +concat-with-sourcemaps@*: + version "1.1.0" + resolved "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" + integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== + dependencies: + source-map "^0.6.1" + +config-chain@^1.1.11: + version "1.1.12" + resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" + integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +console-stream@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/console-stream/-/console-stream-0.1.1.tgz#a095fe07b20465955f2fafd28b5d72bccd949d44" + integrity sha1-oJX+B7IEZZVfL6/Si11yvM2UnUQ= + +content-disposition@0.5.3, content-disposition@^0.5.2: + version "0.5.3" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +continuable-cache@^0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" + integrity sha1-vXJ6f67XfnH/OYWskzUakSczrQ8= + +convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-js-compat@^3.6.2: + version "3.6.5" + resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c" + integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng== + dependencies: + browserslist "^4.8.5" + semver "7.0.0" + +core-js@^2.6.5: + version "2.6.11" + resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" + integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cosmiconfig@^5.0.0: + version "5.2.1" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +cross-spawn@6.0.5, cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +crowdin-cli@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/crowdin-cli/-/crowdin-cli-0.3.0.tgz#eac9989a6fe7feaaf33090397afc187c67b46191" + integrity sha1-6smYmm/n/qrzMJA5evwYfGe0YZE= + dependencies: + request "^2.53.0" + yamljs "^0.2.1" + yargs "^2.3.0" + +css-color-names@0.0.4, css-color-names@^0.0.4: + version "0.0.4" + resolved "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= + +css-declaration-sorter@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" + integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== + dependencies: + postcss "^7.0.1" + timsort "^0.3.0" + +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== + +css-select@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" + integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== + dependencies: + boolbase "^1.0.0" + css-what "^3.2.1" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + +css-tree@1.0.0-alpha.39: + version "1.0.0-alpha.39" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb" + integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== + dependencies: + mdn-data "2.0.6" + source-map "^0.6.1" + +css-what@2.1: + version "2.1.3" + resolved "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + +css-what@^3.2.1: + version "3.3.0" + resolved "https://registry.npmjs.org/css-what/-/css-what-3.3.0.tgz#10fec696a9ece2e591ac772d759aacabac38cd39" + integrity sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-default@^4.0.7: + version "4.0.7" + resolved "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" + integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== + dependencies: + css-declaration-sorter "^4.0.1" + cssnano-util-raw-cache "^4.0.1" + postcss "^7.0.0" + postcss-calc "^7.0.1" + postcss-colormin "^4.0.3" + postcss-convert-values "^4.0.1" + postcss-discard-comments "^4.0.2" + postcss-discard-duplicates "^4.0.2" + postcss-discard-empty "^4.0.1" + postcss-discard-overridden "^4.0.1" + postcss-merge-longhand "^4.0.11" + postcss-merge-rules "^4.0.3" + postcss-minify-font-values "^4.0.2" + postcss-minify-gradients "^4.0.2" + postcss-minify-params "^4.0.2" + postcss-minify-selectors "^4.0.2" + postcss-normalize-charset "^4.0.1" + postcss-normalize-display-values "^4.0.2" + postcss-normalize-positions "^4.0.2" + postcss-normalize-repeat-style "^4.0.2" + postcss-normalize-string "^4.0.2" + postcss-normalize-timing-functions "^4.0.2" + postcss-normalize-unicode "^4.0.1" + postcss-normalize-url "^4.0.1" + postcss-normalize-whitespace "^4.0.2" + postcss-ordered-values "^4.1.2" + postcss-reduce-initial "^4.0.3" + postcss-reduce-transforms "^4.0.2" + postcss-svgo "^4.0.2" + postcss-unique-selectors "^4.0.1" + +cssnano-util-get-arguments@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" + integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= + +cssnano-util-get-match@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" + integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= + +cssnano-util-raw-cache@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" + integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== + dependencies: + postcss "^7.0.0" + +cssnano-util-same-parent@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" + integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== + +cssnano@^4.1.10: + version "4.1.10" + resolved "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" + integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== + dependencies: + cosmiconfig "^5.0.0" + cssnano-preset-default "^4.0.7" + is-resolvable "^1.0.0" + postcss "^7.0.0" + +csso@^4.0.2: + version "4.0.3" + resolved "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903" + integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ== + dependencies: + css-tree "1.0.0-alpha.39" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" + integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg== + dependencies: + ms "^2.1.1" + +debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: + version "3.2.6" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.0: + version "4.1.1" + resolved "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" + integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== + dependencies: + file-type "^5.2.0" + is-stream "^1.1.0" + tar-stream "^1.5.2" + +decompress-tarbz2@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" + integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== + dependencies: + decompress-tar "^4.1.0" + file-type "^6.1.0" + is-stream "^1.1.0" + seek-bzip "^1.0.5" + unbzip2-stream "^1.0.9" + +decompress-targz@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" + integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== + dependencies: + decompress-tar "^4.1.1" + file-type "^5.2.0" + is-stream "^1.1.0" + +decompress-unzip@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" + integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k= + dependencies: + file-type "^3.8.0" + get-stream "^2.2.0" + pify "^2.3.0" + yauzl "^2.4.2" + +decompress@^4.0.0, decompress@^4.2.0: + version "4.2.1" + resolved "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118" + integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== + dependencies: + decompress-tar "^4.0.0" + decompress-tarbz2 "^4.0.0" + decompress-targz "^4.0.0" + decompress-unzip "^4.0.1" + graceful-fs "^4.1.10" + make-dir "^1.0.0" + pify "^2.3.0" + strip-dirs "^2.0.0" + +deep-is@^0.1.3: + version "0.1.3" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-port-alt@1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" + integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +diacritics-map@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz#6dfc0ff9d01000a2edf2865371cac316e94977af" + integrity sha1-bfwP+dAQAKLt8oZTccrDFulJd68= + +dir-glob@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== + dependencies: + arrify "^1.0.1" + path-type "^3.0.0" + +docusaurus@^2.0.0-alpha.61: + version "2.0.0-alpha.63" + resolved "https://registry.npmjs.org/docusaurus/-/docusaurus-2.0.0-alpha.63.tgz#40402d47b18c42b62e93beb78ce06e000cb88312" + integrity sha512-R19pAqcTemJMt7Qykd7ogB2i6R86vbE4/vA/l5/Uuh7xg7ixSKOVZ5M7d9uSoX5jCALOwEyp5iJdleznXUVwAw== + dependencies: + "@babel/core" "^7.9.0" + "@babel/plugin-proposal-class-properties" "^7.8.3" + "@babel/plugin-proposal-object-rest-spread" "^7.9.0" + "@babel/polyfill" "^7.8.7" + "@babel/preset-env" "^7.9.0" + "@babel/preset-react" "^7.9.4" + "@babel/register" "^7.9.0" + "@babel/traverse" "^7.9.0" + "@babel/types" "^7.9.0" + autoprefixer "^9.7.5" + babylon "^6.18.0" + chalk "^3.0.0" + classnames "^2.2.6" + commander "^4.0.1" + crowdin-cli "^0.3.0" + cssnano "^4.1.10" + escape-string-regexp "^2.0.0" + express "^4.17.1" + feed "^4.0.0" + fs-extra "^8.1.0" + gaze "^1.1.3" + github-slugger "^1.2.1" + glob "^7.1.6" + highlight.js "^9.16.2" + imagemin "^6.0.0" + imagemin-gifsicle "^6.0.1" + imagemin-jpegtran "^6.0.0" + imagemin-optipng "^6.0.0" + imagemin-svgo "^7.0.0" + lodash "^4.17.15" + markdown-toc "^1.2.0" + mkdirp "^0.5.1" + portfinder "^1.0.25" + postcss "^7.0.23" + prismjs "^1.17.1" + react "^16.8.4" + react-dev-utils "^9.1.0" + react-dom "^16.8.4" + remarkable "^2.0.0" + request "^2.88.0" + shelljs "^0.8.4" + sitemap "^3.2.2" + tcp-port-used "^1.0.1" + tiny-lr "^1.1.1" + tree-node-cli "^1.2.5" + truncate-html "^1.0.3" + +dom-serializer@0: + version "0.2.2" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +dom-serializer@~0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== + dependencies: + domelementtype "^1.3.0" + entities "^1.1.1" + +domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domelementtype@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" + integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1, domutils@^1.7.0: + version "1.7.0" + resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-prop@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" + integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== + dependencies: + is-obj "^2.0.0" + +download@^6.2.2: + version "6.2.5" + resolved "https://registry.npmjs.org/download/-/download-6.2.5.tgz#acd6a542e4cd0bb42ca70cfc98c9e43b07039714" + integrity sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA== + dependencies: + caw "^2.0.0" + content-disposition "^0.5.2" + decompress "^4.0.0" + ext-name "^5.0.0" + file-type "5.2.0" + filenamify "^2.0.0" + get-stream "^3.0.0" + got "^7.0.0" + make-dir "^1.0.0" + p-event "^1.0.0" + pify "^3.0.0" + +download@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/download/-/download-7.1.0.tgz#9059aa9d70b503ee76a132897be6dec8e5587233" + integrity sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ== + dependencies: + archive-type "^4.0.0" + caw "^2.0.1" + content-disposition "^0.5.2" + decompress "^4.2.0" + ext-name "^5.0.0" + file-type "^8.1.0" + filenamify "^2.0.0" + get-stream "^3.0.0" + got "^8.3.1" + make-dir "^1.2.0" + p-event "^2.1.0" + pify "^3.0.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +duplexer@^0.1.1: + version "0.1.2" + resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.247, electron-to-chromium@^1.3.562: + version "1.3.562" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.562.tgz#79c20277ee1c8d0173a22af00e38433b752bc70f" + integrity sha512-WhRe6liQ2q/w1MZc8mD8INkenHivuHdrr4r5EQHNomy3NJux+incP6M6lDMd0paShP3MD0WGe5R1TWmEClf+Bg== + +"emoji-regex@>=6.0.0 <=6.1.1": + version "6.1.1" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz#c6cd0ec1b0642e2a3c67a1137efc5e796da4f88e" + integrity sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4= + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +entities@^1.1.1, entities@~1.1.1: + version "1.1.2" + resolved "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0: + version "2.0.3" + resolved "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f" + integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ== + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +error@^7.0.0: + version "7.2.1" + resolved "https://registry.npmjs.org/error/-/error-7.2.1.tgz#eab21a4689b5f684fc83da84a0e390de82d94894" + integrity sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA== + dependencies: + string-template "~0.2.1" + +es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: + version "1.17.6" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-regex "^1.1.0" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escalade@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" + integrity sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eventsource@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + dependencies: + original "^1.0.0" + +exec-buffer@^3.0.0: + version "3.2.0" + resolved "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz#b1686dbd904c7cf982e652c1f5a79b1e5573082b" + integrity sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA== + dependencies: + execa "^0.7.0" + p-finally "^1.0.0" + pify "^3.0.0" + rimraf "^2.5.4" + tempfile "^2.0.0" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +executable@^4.1.0: + version "4.1.1" + resolved "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= + dependencies: + fill-range "^2.1.0" + +express@^4.17.1: + version "4.17.1" + resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext-list@^2.0.0: + version "2.2.2" + resolved "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" + integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== + dependencies: + mime-db "^1.28.0" + +ext-name@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6" + integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== + dependencies: + ext-list "^2.0.0" + sort-keys-length "^1.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^2.0.2: + version "2.2.7" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +faye-websocket@~0.10.0: + version "0.10.0" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.1: + version "0.11.3" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" + integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== + dependencies: + websocket-driver ">=0.5.1" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + +feed@^4.0.0: + version "4.2.1" + resolved "https://registry.npmjs.org/feed/-/feed-4.2.1.tgz#b246ef891051c7dbf088ca203341d9fb0444baee" + integrity sha512-l28KKcK1J/u3iq5dRDmmoB2p7dtBfACC2NqJh4dI2kFptxH0asfjmOfcxqh5Sv8suAlVa73gZJ4REY5RrafVvg== + dependencies: + xml-js "^1.6.11" + +figures@^1.3.5: + version "1.7.0" + resolved "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-type@5.2.0, file-type@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" + integrity sha1-LdvqfHP/42No365J3DOMBYwritY= + +file-type@^10.4.0, file-type@^10.7.0: + version "10.11.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz#2961d09e4675b9fb9a3ee6b69e9cd23f43fd1890" + integrity sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw== + +file-type@^3.8.0: + version "3.9.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" + integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= + +file-type@^4.2.0: + version "4.4.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz#1b600e5fca1fbdc6e80c0a70c71c8dba5f7906c5" + integrity sha1-G2AOX8ofvcboDApwxxyNul95BsU= + +file-type@^6.1.0: + version "6.2.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" + integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== + +file-type@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz#244f3b7ef641bbe0cca196c7276e4b332399f68c" + integrity sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ== + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +filename-reserved-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" + integrity sha1-q/c9+rc10EVECr/qLZHzieu/oik= + +filenamify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9" + integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA== + dependencies: + filename-reserved-regex "^2.0.0" + strip-outer "^1.0.0" + trim-repeated "^1.0.0" + +filesize@3.6.1: + version "3.6.1" + resolved "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== + +fill-range@^2.1.0: + version "2.2.4" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^3.0.0" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-cache-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-versions@^3.0.0: + version "3.2.0" + resolved "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" + integrity sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww== + dependencies: + semver-regex "^2.0.0" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +fork-ts-checker-webpack-plugin@1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.5.0.tgz#ce1d77190b44d81a761b10b6284a373795e41f0c" + integrity sha512-zEhg7Hz+KhZlBhILYpXy+Beu96gwvkROWJiTXOCyOOMMrdBIRPvsBpBqgTI4jfJGrJXcqGwJR8zsBGDmzY0jsA== + dependencies: + babel-code-frame "^6.22.0" + chalk "^2.4.1" + chokidar "^2.0.4" + micromatch "^3.1.10" + minimatch "^3.0.4" + semver "^5.6.0" + tapable "^1.0.0" + worker-rpc "^0.1.0" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +from2@^2.1.1: + version "2.3.0" + resolved "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gaze@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" + integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== + dependencies: + globule "^1.0.0" + +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + +get-proxy@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz#349f2b4d91d44c4d4d4e9cba2ad90143fac5ef93" + integrity sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw== + dependencies: + npm-conf "^1.1.0" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + +get-stream@3.0.0, get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^2.2.0: + version "2.3.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" + integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4= + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +gifsicle@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/gifsicle/-/gifsicle-4.0.1.tgz#30e1e61e3ee4884ef702641b2e98a15c2127b2e2" + integrity sha512-A/kiCLfDdV+ERV/UB+2O41mifd+RxH8jlRG8DMxZO84Bma/Fw0htqZ+hY2iaalLRNyUu7tYZQslqUBJxBggxbg== + dependencies: + bin-build "^3.0.0" + bin-wrapper "^4.0.0" + execa "^1.0.0" + logalot "^2.0.0" + +github-slugger@^1.2.1: + version "1.3.0" + resolved "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz#9bd0a95c5efdfc46005e82a906ef8e2a059124c9" + integrity sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q== + dependencies: + emoji-regex ">=6.0.0 <=6.1.1" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + +glob@^7.0.0, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@~7.1.1: + version "7.1.6" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-modules@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globby@8.0.2, globby@^8.0.1: + version "8.0.2" + resolved "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" + integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== + dependencies: + array-union "^1.0.1" + dir-glob "2.0.0" + fast-glob "^2.0.2" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +globule@^1.0.0: + version "1.3.2" + resolved "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz#d8bdd9e9e4eef8f96e245999a5dee7eb5d8529c4" + integrity sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA== + dependencies: + glob "~7.1.1" + lodash "~4.17.10" + minimatch "~3.0.2" + +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= + dependencies: + delegate "^3.1.2" + +got@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +got@^8.3.1: + version "8.3.2" + resolved "https://registry.npmjs.org/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" + integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw== + dependencies: + "@sindresorhus/is" "^0.7.0" + cacheable-request "^2.1.1" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + into-stream "^3.1.0" + is-retry-allowed "^1.1.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + mimic-response "^1.0.0" + p-cancelable "^0.4.0" + p-timeout "^2.0.1" + pify "^3.0.0" + safe-buffer "^5.1.1" + timed-out "^4.0.1" + url-parse-lax "^3.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.4" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +gray-matter@^2.1.0: + version "2.1.1" + resolved "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz#3042d9adec2a1ded6a7707a9ed2380f8a17a430e" + integrity sha1-MELZrewqHe1qdwep7SOA+KF6Qw4= + dependencies: + ansi-red "^0.1.1" + coffee-script "^1.12.4" + extend-shallow "^2.0.1" + js-yaml "^3.8.1" + toml "^2.3.2" + +gulp-header@^1.7.1: + version "1.8.12" + resolved "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz#ad306be0066599127281c4f8786660e705080a84" + integrity sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ== + dependencies: + concat-with-sourcemaps "*" + lodash.template "^4.4.0" + through2 "^2.0.0" + +gzip-size@5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" + integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== + dependencies: + duplexer "^0.1.1" + pify "^4.0.1" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.0, has@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== + +highlight.js@^9.16.2: + version "9.18.3" + resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.3.tgz#a1a0a2028d5e3149e2380f8a865ee8516703d634" + integrity sha512-zBZAmhSupHIl5sITeMqIJnYCDfAEc3Gdkqj65wC1lpI468MMQeeQkhcIAvk+RylAkxrCcI9xy9piHiXeQ1BdzQ== + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= + +html-comment-regex@^1.1.0, html-comment-regex@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" + integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== + +htmlparser2@^3.9.1: + version "3.10.1" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +http-cache-semantics@3.8.1: + version "3.8.1" + resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-parser-js@>=0.5.1: + version "0.5.2" + resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz#da2e31d237b393aae72ace43882dd7e270a8ff77" + integrity sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ== + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + +imagemin-gifsicle@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/imagemin-gifsicle/-/imagemin-gifsicle-6.0.1.tgz#6abad4e95566d52e5a104aba1c24b4f3b48581b3" + integrity sha512-kuu47c6iKDQ6R9J10xCwL0lgs0+sMz3LRHqRcJ2CRBWdcNmo3T5hUaM8hSZfksptZXJLGKk8heSAvwtSdB1Fng== + dependencies: + exec-buffer "^3.0.0" + gifsicle "^4.0.0" + is-gif "^3.0.0" + +imagemin-jpegtran@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/imagemin-jpegtran/-/imagemin-jpegtran-6.0.0.tgz#c8d3bcfb6ec9c561c20a987142854be70d90b04f" + integrity sha512-Ih+NgThzqYfEWv9t58EItncaaXIHR0u9RuhKa8CtVBlMBvY0dCIxgQJQCfwImA4AV1PMfmUKlkyIHJjb7V4z1g== + dependencies: + exec-buffer "^3.0.0" + is-jpg "^2.0.0" + jpegtran-bin "^4.0.0" + +imagemin-optipng@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/imagemin-optipng/-/imagemin-optipng-6.0.0.tgz#a6bfc7b542fc08fc687e83dfb131249179a51a68" + integrity sha512-FoD2sMXvmoNm/zKPOWdhKpWdFdF9qiJmKC17MxZJPH42VMAp17/QENI/lIuP7LCUnLVAloO3AUoTSNzfhpyd8A== + dependencies: + exec-buffer "^3.0.0" + is-png "^1.0.0" + optipng-bin "^5.0.0" + +imagemin-svgo@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/imagemin-svgo/-/imagemin-svgo-7.1.0.tgz#528a42fd3d55eff5d4af8fd1113f25fb61ad6d9a" + integrity sha512-0JlIZNWP0Luasn1HT82uB9nU9aa+vUj6kpT+MjPW11LbprXC+iC4HDwn1r4Q2/91qj4iy9tRZNsFySMlEpLdpg== + dependencies: + is-svg "^4.2.1" + svgo "^1.3.2" + +imagemin@^6.0.0: + version "6.1.0" + resolved "https://registry.npmjs.org/imagemin/-/imagemin-6.1.0.tgz#62508b465728fea36c03cdc07d915fe2d8cf9e13" + integrity sha512-8ryJBL1CN5uSHpiBMX0rJw79C9F9aJqMnjGnrd/1CafegpNuA81RBAAru/jQQEOWlOJJlpRnlcVFF6wq+Ist0A== + dependencies: + file-type "^10.7.0" + globby "^8.0.1" + make-dir "^1.0.0" + p-pipe "^1.1.0" + pify "^4.0.1" + replace-ext "^1.0.0" + +immer@1.10.0: + version "1.10.0" + resolved "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" + integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-lazy@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz#891279202c8a2280fdbd6674dbd8da1a1dfc67cc" + integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ== + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@^1.3.4, ini@^1.3.5: + version "1.3.5" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +inquirer@6.5.0: + version "6.5.0" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42" + integrity sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +into-stream@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" + integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY= + dependencies: + from2 "^2.1.1" + p-is-promise "^1.1.0" + +invariant@^2.2.2, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4, is-callable@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" + integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== + +is-color-stop@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= + dependencies: + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-gif@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-gif/-/is-gif-3.0.0.tgz#c4be60b26a301d695bb833b20d9b5d66c6cf83b1" + integrity sha512-IqJ/jlbw5WJSNfwQ/lHEDXF8rxhRgF6ythk2oiEvhpG29F704eX9NO6TvPfMiq9DrbwgcEDnETYNcZDPewQoVw== + dependencies: + file-type "^10.4.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-jpg@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-jpg/-/is-jpg-2.0.0.tgz#2e1997fa6e9166eaac0242daae443403e4ef1d97" + integrity sha1-LhmX+m6RZuqsAkLarkQ0A+TvHZc= + +is-natural-number@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" + integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= + +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-png@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-png/-/is-png-1.1.0.tgz#d574b12bf275c0350455570b0e5b57ab062077ce" + integrity sha1-1XSxK/J1wDUEVVcLDltXqwYgd84= + +is-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== + +is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: + version "1.2.0" + resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-root@2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" + integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== + +is-stream@^1.0.0, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-svg@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" + integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== + dependencies: + html-comment-regex "^1.1.0" + +is-svg@^4.2.1: + version "4.2.1" + resolved "https://registry.npmjs.org/is-svg/-/is-svg-4.2.1.tgz#095b496e345fec9211c2a7d5d021003e040d6f81" + integrity sha512-PHx3ANecKsKNl5y5+Jvt53Y4J7MfMpbNZkv384QNiswMKAWIbvcqbPz+sYbFKJI8Xv3be01GSFniPmoaP+Ai5A== + dependencies: + html-comment-regex "^1.1.2" + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-url@^1.2.2: + version "1.2.4" + resolved "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +is2@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/is2/-/is2-2.0.1.tgz#8ac355644840921ce435d94f05d3a94634d3481a" + integrity sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA== + dependencies: + deep-is "^0.1.3" + ip-regex "^2.1.0" + is-url "^1.2.2" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +jpegtran-bin@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/jpegtran-bin/-/jpegtran-bin-4.0.0.tgz#d00aed809fba7aa6f30817e59eee4ddf198f8f10" + integrity sha512-2cRl1ism+wJUoYAYFt6O/rLBfpXNWG2dUWbgcEkTt5WGMnqI46eEro8T4C5zGROxKRqyKpCBSdHPvt5UYCtxaQ== + dependencies: + bin-build "^3.0.0" + bin-wrapper "^4.0.0" + logalot "^2.0.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@^3.13.1, js-yaml@^3.14.0, js-yaml@^3.8.1: + version "3.14.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json3@^3.3.2: + version "3.3.3" + resolved "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" + integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +keyv@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" + integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +lazy-cache@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264" + integrity sha1-uRkKT5EzVGlIQIWfio9whNiCImQ= + dependencies: + set-getter "^0.1.0" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levenary@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77" + integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== + dependencies: + leven "^3.1.0" + +list-item@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz#0c65d00e287cb663ccb3cb3849a77e89ec268a56" + integrity sha1-DGXQDih8tmPMs8s4Sad+iewmilY= + dependencies: + expand-range "^1.8.1" + extend-shallow "^2.0.1" + is-number "^2.1.0" + repeat-string "^1.5.2" + +livereload-js@^2.3.0: + version "2.4.0" + resolved "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" + integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +loader-utils@1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash.assignin@^4.0.9: + version "4.2.0" + resolved "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" + integrity sha1-uo31+4QesKPoBEIysOJjqNxqKKI= + +lodash.bind@^4.1.4: + version "4.2.1" + resolved "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" + integrity sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU= + +lodash.chunk@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz#66e5ce1f76ed27b4303d8c6512e8d1216e8106bc" + integrity sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw= + +lodash.defaults@^4.0.1: + version "4.2.0" + resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= + +lodash.filter@^4.4.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" + integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4= + +lodash.flatten@^4.2.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + +lodash.foreach@^4.3.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" + integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= + +lodash.map@^4.4.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM= + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.merge@^4.4.0: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.padstart@^4.6.1: + version "4.6.1" + resolved "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b" + integrity sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs= + +lodash.pick@^4.2.1: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= + +lodash.reduce@^4.4.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" + integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs= + +lodash.reject@^4.4.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" + integrity sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU= + +lodash.some@^4.4.0: + version "4.6.0" + resolved "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" + integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash.template@^4.4.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@~4.17.10: + version "4.17.20" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +logalot@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/logalot/-/logalot-2.1.0.tgz#5f8e8c90d304edf12530951a5554abb8c5e3f552" + integrity sha1-X46MkNME7fElMJUaVVSruMXj9VI= + dependencies: + figures "^1.3.5" + squeak "^1.0.0" + +longest@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lowercase-keys@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY= + +lowercase-keys@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lpad-align@^1.0.1: + version "1.1.2" + resolved "https://registry.npmjs.org/lpad-align/-/lpad-align-1.1.2.tgz#21f600ac1c3095c3c6e497ee67271ee08481fe9e" + integrity sha1-IfYArBwwlcPG5JfuZyce4ISB/p4= + dependencies: + get-stdin "^4.0.1" + indent-string "^2.1.0" + longest "^1.0.0" + meow "^3.3.0" + +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +make-dir@^1.0.0, make-dir@^1.2.0: + version "1.3.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +markdown-link@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz#32c5c65199a6457316322d1e4229d13407c8c7cf" + integrity sha1-MsXGUZmmRXMWMi0eQinRNAfIx88= + +markdown-toc@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz#44a15606844490314afc0444483f9e7b1122c339" + integrity sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg== + dependencies: + concat-stream "^1.5.2" + diacritics-map "^0.1.0" + gray-matter "^2.1.0" + lazy-cache "^2.0.2" + list-item "^1.1.1" + markdown-link "^0.1.1" + minimist "^1.2.0" + mixin-deep "^1.1.3" + object.pick "^1.2.0" + remarkable "^1.7.1" + repeat-string "^1.6.1" + strip-color "^0.1.0" + +math-random@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" + integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== + +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + +mdn-data@2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" + integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +meow@^3.3.0: + version "3.7.0" + resolved "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge2@^1.2.3: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +microevent.ts@~0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" + integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +mime-db@1.44.0, mime-db@^1.28.0: + version "1.44.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.27" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +minimatch@3.0.4, minimatch@^3.0.4, minimatch@~3.0.2: + version "3.0.4" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mixin-deep@^1.1.3, mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +nan@^2.12.1: + version "2.14.1" + resolved "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" + integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-releases@^1.1.29, node-releases@^1.1.60: + version "1.1.60" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" + integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.5.0" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +normalize-url@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" + integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== + dependencies: + prepend-http "^2.0.0" + query-string "^5.0.1" + sort-keys "^2.0.0" + +normalize-url@^3.0.0: + version "3.3.0" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" + integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== + +npm-conf@^1.1.0: + version "1.1.3" + resolved "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" + integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== + dependencies: + config-chain "^1.1.11" + pify "^3.0.0" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +nth-check@^1.0.2, nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.7.0: + version "1.8.0" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.getownpropertydescriptors@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +object.pick@^1.2.0, object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.values@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +open@^6.3.0: + version "6.4.0" + resolved "https://registry.npmjs.org/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" + integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== + dependencies: + is-wsl "^1.1.0" + +optipng-bin@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/optipng-bin/-/optipng-bin-5.1.0.tgz#a7c7ab600a3ab5a177dae2f94c2d800aa386b5a9" + integrity sha512-9baoqZTNNmXQjq/PQTWEXbVV3AMO2sI/GaaqZJZ8SExfAzjijeAP7FEeT+TtyumSw7gr0PZtSUYB/Ke7iHQVKA== + dependencies: + bin-build "^3.0.0" + bin-wrapper "^4.0.0" + logalot "^2.0.0" + +original@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + +os-filter-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz#1c0b62d5f3a2442749a2d139e6dddee6e81d8d16" + integrity sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg== + dependencies: + arch "^2.1.0" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + +p-cancelable@^0.4.0: + version "0.4.1" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" + integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== + +p-event@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/p-event/-/p-event-1.3.0.tgz#8e6b4f4f65c72bc5b6fe28b75eda874f96a4a085" + integrity sha1-jmtPT2XHK8W2/ii3XtqHT5akoIU= + dependencies: + p-timeout "^1.1.1" + +p-event@^2.1.0: + version "2.3.1" + resolved "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz#596279ef169ab2c3e0cae88c1cfbb08079993ef6" + integrity sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA== + dependencies: + p-timeout "^2.0.1" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-is-promise@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" + integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-map-series@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" + integrity sha1-v5j+V1cFZYqeE1G++4WuTB8Hvco= + dependencies: + p-reduce "^1.0.0" + +p-pipe@^1.1.0: + version "1.2.0" + resolved "https://registry.npmjs.org/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" + integrity sha1-SxoROZoRUgpneQ7loMHViB1r7+k= + +p-reduce@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" + integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= + dependencies: + p-finally "^1.0.0" + +p-timeout@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" + integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pirates@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-up@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= + dependencies: + find-up "^2.1.0" + +portfinder@^1.0.25: + version "1.0.28" + resolved "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" + integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== + dependencies: + async "^2.6.2" + debug "^3.1.1" + mkdirp "^0.5.5" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +postcss-calc@^7.0.1: + version "7.0.4" + resolved "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.4.tgz#5e177ddb417341e6d4a193c5d9fd8ada79094f8b" + integrity sha512-0I79VRAd1UTkaHzY9w83P39YGO/M3bG7/tNLrHGEunBolfoGM0hSjrGvjoeaj0JE/zIw5GsI2KZ0UwDJqv5hjw== + dependencies: + postcss "^7.0.27" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.2" + +postcss-colormin@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" + integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== + dependencies: + browserslist "^4.0.0" + color "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-convert-values@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" + integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-discard-comments@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" + integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== + dependencies: + postcss "^7.0.0" + +postcss-discard-duplicates@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" + integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== + dependencies: + postcss "^7.0.0" + +postcss-discard-empty@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" + integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== + dependencies: + postcss "^7.0.0" + +postcss-discard-overridden@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" + integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== + dependencies: + postcss "^7.0.0" + +postcss-merge-longhand@^4.0.11: + version "4.0.11" + resolved "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" + integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== + dependencies: + css-color-names "0.0.4" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + stylehacks "^4.0.0" + +postcss-merge-rules@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" + integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + cssnano-util-same-parent "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + vendors "^1.0.0" + +postcss-minify-font-values@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" + integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-gradients@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" + integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + is-color-stop "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-params@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" + integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== + dependencies: + alphanum-sort "^1.0.0" + browserslist "^4.0.0" + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + uniqs "^2.0.0" + +postcss-minify-selectors@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" + integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== + dependencies: + alphanum-sort "^1.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +postcss-normalize-charset@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" + integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== + dependencies: + postcss "^7.0.0" + +postcss-normalize-display-values@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" + integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-positions@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" + integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== + dependencies: + cssnano-util-get-arguments "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-repeat-style@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" + integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-string@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" + integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== + dependencies: + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-timing-functions@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" + integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-unicode@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" + integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-url@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" + integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-whitespace@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" + integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-ordered-values@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" + integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== + dependencies: + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-reduce-initial@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" + integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + +postcss-reduce-transforms@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" + integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== + dependencies: + cssnano-util-get-match "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-selector-parser@^3.0.0: + version "3.1.2" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" + integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== + dependencies: + dot-prop "^5.2.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" + integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" + integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== + dependencies: + is-svg "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + svgo "^1.0.0" + +postcss-unique-selectors@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" + integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== + dependencies: + alphanum-sort "^1.0.0" + postcss "^7.0.0" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.0: + version "3.3.1" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + +postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.23, postcss@^7.0.27, postcss@^7.0.32: + version "7.0.32" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" + integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +prismjs@^1.17.1: + version "1.21.0" + resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.21.0.tgz#36c086ec36b45319ec4218ee164c110f9fc015a3" + integrity sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw== + optionalDependencies: + clipboard "^2.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +prop-types@^15.6.2: + version "15.7.2" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= + +proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@^6.4.0: + version "6.9.4" + resolved "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" + integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +randomatic@^3.0.0: + version "3.1.1" + resolved "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" + integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== + dependencies: + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-body@~1.1.0: + version "1.1.7" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425" + integrity sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU= + dependencies: + bytes "1" + string_decoder "0.10" + +react-dev-utils@^9.1.0: + version "9.1.0" + resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.1.0.tgz#3ad2bb8848a32319d760d0a84c56c14bdaae5e81" + integrity sha512-X2KYF/lIGyGwP/F/oXgGDF24nxDA2KC4b7AFto+eqzc/t838gpSGiaU8trTqHXOohuLxxc5qi1eDzsl9ucPDpg== + dependencies: + "@babel/code-frame" "7.5.5" + address "1.1.2" + browserslist "4.7.0" + chalk "2.4.2" + cross-spawn "6.0.5" + detect-port-alt "1.1.6" + escape-string-regexp "1.0.5" + filesize "3.6.1" + find-up "3.0.0" + fork-ts-checker-webpack-plugin "1.5.0" + global-modules "2.0.0" + globby "8.0.2" + gzip-size "5.1.1" + immer "1.10.0" + inquirer "6.5.0" + is-root "2.1.0" + loader-utils "1.2.3" + open "^6.3.0" + pkg-up "2.0.0" + react-error-overlay "^6.0.3" + recursive-readdir "2.2.2" + shell-quote "1.7.2" + sockjs-client "1.4.0" + strip-ansi "5.2.0" + text-table "0.2.0" + +react-dom@^16.8.4: + version "16.13.1" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" + integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.19.1" + +react-error-overlay@^6.0.3: + version "6.0.7" + resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" + integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== + +react-is@^16.8.1: + version "16.13.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react@^16.8.4: + version "16.13.1" + resolved "https://registry.npmjs.org/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" + integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1: + version "3.6.0" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + dependencies: + resolve "^1.1.6" + +recursive-readdir@2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" + integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + dependencies: + minimatch "3.0.4" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +regenerate-unicode-properties@^8.2.0: + version "8.2.0" + resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" + integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.1" + resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz#cad92ad8e6b591773485fbe05a485caf4f457e6f" + integrity sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A== + +regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +regenerator-transform@^0.14.2: + version "0.14.5" + resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" + integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== + dependencies: + "@babel/runtime" "^7.8.4" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpu-core@^4.7.0: + version "4.7.0" + resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" + integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.2.0" + +regjsgen@^0.5.1: + version "0.5.2" + resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" + integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== + +regjsparser@^0.6.4: + version "0.6.4" + resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" + integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw== + dependencies: + jsesc "~0.5.0" + +remarkable@^1.7.1: + version "1.7.4" + resolved "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz#19073cb960398c87a7d6546eaa5e50d2022fcd00" + integrity sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg== + dependencies: + argparse "^1.0.10" + autolinker "~0.28.0" + +remarkable@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/remarkable/-/remarkable-2.0.1.tgz#280ae6627384dfb13d98ee3995627ca550a12f31" + integrity sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA== + dependencies: + argparse "^1.0.10" + autolinker "^3.11.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +replace-ext@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" + integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== + +request@^2.53.0, request@^2.88.0: + version "2.88.2" + resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.3.2: + version "1.17.0" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +responselike@1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= + +rimraf@^2.5.4: + version "2.7.1" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +run-async@^2.2.0: + version "2.4.1" + resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rxjs@^6.4.0: + version "6.6.2" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" + integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-json-parse@~1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" + integrity sha1-PnZyPjjf3aE8mx0poeB//uSzC1c= + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4, sax@~1.2.4: + version "1.2.4" + resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +seek-bzip@^1.0.5: + version "1.0.6" + resolved "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4" + integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ== + dependencies: + commander "^2.8.1" + +select@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= + +semver-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" + integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== + +semver-truncate@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/semver-truncate/-/semver-truncate-1.1.2.tgz#57f41de69707a62709a7e0104ba2117109ea47e8" + integrity sha1-V/Qd5pcHpicJp+AQS6IRcQnqR+g= + dependencies: + semver "^5.3.0" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.npmjs.org/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +set-getter@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376" + integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y= + dependencies: + to-object-path "^0.3.0" + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shell-quote@1.7.2: + version "1.7.2" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== + +shelljs@^0.8.4: + version "0.8.4" + resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" + integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +sitemap@^3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/sitemap/-/sitemap-3.2.2.tgz#3f77c358fa97b555c879e457098e39910095c62b" + integrity sha512-TModL/WU4m2q/mQcrDgNANn0P4LwprM9MMvG4hu5zP4c6IIKs2YLTu6nXXnNr8ODW/WFtxKggiJ1EGn2W0GNmg== + dependencies: + lodash.chunk "^4.2.0" + lodash.padstart "^4.6.1" + whatwg-url "^7.0.0" + xmlbuilder "^13.0.0" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sockjs-client@1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" + integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + +sort-keys-length@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" + integrity sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg= + dependencies: + sort-keys "^1.0.0" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= + dependencies: + is-plain-obj "^1.0.0" + +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= + dependencies: + is-plain-obj "^1.0.0" + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.16: + version "0.5.19" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.0, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.5" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +squeak@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/squeak/-/squeak-1.3.0.tgz#33045037b64388b567674b84322a6521073916c3" + integrity sha1-MwRQN7ZDiLVnZ0uEMiplIQc5FsM= + dependencies: + chalk "^1.0.0" + console-stream "^0.1.1" + lpad-align "^1.0.1" + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-template@~0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" + integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= + +string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string.prototype.trimend@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trimstart@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string_decoder@0.10: + version "0.10.31" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@5.2.0, strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-color@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz#106f65d3d3e6a2d9401cac0eb0ce8b8a702b4f7b" + integrity sha1-EG9l09PmotlAHKwOsM6LinArT3s= + +strip-dirs@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" + integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== + dependencies: + is-natural-number "^4.0.1" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + +strip-outer@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" + integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== + dependencies: + escape-string-regexp "^1.0.2" + +stylehacks@^4.0.0: + version "4.0.3" + resolved "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" + integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +svgo@^1.0.0, svgo@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" + integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.37" + csso "^4.0.2" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +tapable@^1.0.0: + version "1.1.3" + resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tar-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== + dependencies: + bl "^1.0.0" + buffer-alloc "^1.2.0" + end-of-stream "^1.0.0" + fs-constants "^1.0.0" + readable-stream "^2.3.0" + to-buffer "^1.1.1" + xtend "^4.0.0" + +tcp-port-used@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.1.tgz#46061078e2d38c73979a2c2c12b5a674e6689d70" + integrity sha512-rwi5xJeU6utXoEIiMvVBMc9eJ2/ofzB+7nLOdnZuFTmNCLqRiQh2sMG9MqCxHU/69VC/Fwp5dV9306Qd54ll1Q== + dependencies: + debug "4.1.0" + is2 "2.0.1" + +temp-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" + integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= + +tempfile@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz#6b0446856a9b1114d1856ffcbe509cccb0977265" + integrity sha1-awRGhWqbERTRhW/8vlCczLCXcmU= + dependencies: + temp-dir "^1.0.0" + uuid "^3.0.1" + +text-table@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through2@^2.0.0: + version "2.0.5" + resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@^2.3.6, through@^2.3.8: + version "2.3.8" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== + +tiny-lr@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz#9fa547412f238fedb068ee295af8b682c98b2aab" + integrity sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA== + dependencies: + body "^5.1.0" + debug "^3.1.0" + faye-websocket "~0.10.0" + livereload-js "^2.3.0" + object-assign "^4.1.0" + qs "^6.4.0" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-buffer@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +toml@^2.3.2: + version "2.3.6" + resolved "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz#25b0866483a9722474895559088b436fd11f861b" + integrity sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + +tree-node-cli@^1.2.5: + version "1.4.0" + resolved "https://registry.npmjs.org/tree-node-cli/-/tree-node-cli-1.4.0.tgz#8f4028554610d6ee1cdeb98554a60841a3cfa3ac" + integrity sha512-hBc/cp7rTSHFSFvaTzmHNYyJv87UJBsxsfCoq2DtDQuMES4vhnLuvXZit/asGtZG8edWTCydWeFWoBz9LYkJdQ== + dependencies: + commander "^5.0.0" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + +trim-repeated@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" + integrity sha1-42RqLqTokTEr9+rObPsFOAvAHCE= + dependencies: + escape-string-regexp "^1.0.2" + +truncate-html@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/truncate-html/-/truncate-html-1.0.3.tgz#0166dfc7890626130c2e4174c6b73d4d63993e5f" + integrity sha512-1o1prdRv+iehXcGwn29YgXU17DotHkr+OK3ijVEG7FGMwHNG9RyobXwimw6djDvbIc24rhmz3tjNNvNESjkNkQ== + dependencies: + "@types/cheerio" "^0.22.8" + cheerio "0.22.0" + +tslib@^1.9.0, tslib@^1.9.3: + version "1.13.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +unbzip2-stream@^1.0.9: + version "1.4.3" + resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" + integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" + integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.1.0" + resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" + integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +uri-js@^4.2.2: + version "4.4.0" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-parse@^1.4.3: + version "1.4.7" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util.promisify@~1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.0.1, uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +vendors@^1.0.0: + version "1.0.4" + resolved "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" + integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +websocket-driver@>=0.5.1: + version "0.7.4" + resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +which@^1.2.9, which@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= + +worker-rpc@^0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5" + integrity sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg== + dependencies: + microevent.ts "~0.1.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +xml-js@^1.6.11: + version "1.6.11" + resolved "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" + integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== + dependencies: + sax "^1.2.4" + +xmlbuilder@^13.0.0: + version "13.0.2" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz#02ae33614b6a047d1c32b5389c1fdacb2bce47a7" + integrity sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ== + +xtend@^4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yamljs@^0.2.1: + version "0.2.10" + resolved "https://registry.npmjs.org/yamljs/-/yamljs-0.2.10.tgz#481cc7c25ca73af59f591f0c96e3ce56c757a40f" + integrity sha1-SBzHwlynOvWfWR8MluPOVsdXpA8= + dependencies: + argparse "^1.0.7" + glob "^7.0.5" + +yargs@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/yargs/-/yargs-2.3.0.tgz#e900c87250ec5cd080db6009fe3dd63156f1d7fb" + integrity sha1-6QDIclDsXNCA22AJ/j3WMVbx1/s= + dependencies: + wordwrap "0.0.2" + +yauzl@^2.4.2: + version "2.10.0" + resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" diff --git a/package.json b/package.json index e6ee961701..5645d913eb 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,7 @@ "workspaces": { "packages": [ "packages/*", - "plugins/*", - "microsite" + "plugins/*" ] }, "version": "1.0.0", diff --git a/yarn.lock b/yarn.lock index 5b0bec8005..1853f23f7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -396,7 +396,7 @@ "@babel/helper-remap-async-to-generator" "^7.10.4" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-class-properties@^7.10.4", "@babel/plugin-proposal-class-properties@^7.7.0", "@babel/plugin-proposal-class-properties@^7.8.3": +"@babel/plugin-proposal-class-properties@^7.10.4", "@babel/plugin-proposal-class-properties@^7.7.0": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807" integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg== @@ -452,7 +452,7 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.11.0", "@babel/plugin-proposal-object-rest-spread@^7.6.2", "@babel/plugin-proposal-object-rest-spread@^7.9.0": +"@babel/plugin-proposal-object-rest-spread@^7.11.0", "@babel/plugin-proposal-object-rest-spread@^7.6.2": version "7.11.0" resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af" integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA== @@ -922,15 +922,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/polyfill@^7.8.7": - version "7.10.4" - resolved "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.10.4.tgz#915e5bfe61490ac0199008e35ca9d7d151a8e45a" - integrity sha512-8BYcnVqQ5kMD2HXoHInBH7H1b/uP3KdnwCYXOqFnXqguOyuu443WXusbIUbWEfY3Z0Txk0M1uG/8YuAMhNl6zg== - dependencies: - core-js "^2.6.5" - regenerator-runtime "^0.13.4" - -"@babel/preset-env@^7.4.5", "@babel/preset-env@^7.9.0", "@babel/preset-env@^7.9.5": +"@babel/preset-env@^7.4.5", "@babel/preset-env@^7.9.5": version "7.11.0" resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz#860ee38f2ce17ad60480c2021ba9689393efb796" integrity sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg== @@ -1036,17 +1028,6 @@ "@babel/plugin-transform-react-jsx-source" "^7.10.4" "@babel/plugin-transform-react-pure-annotations" "^7.10.4" -"@babel/register@^7.9.0": - version "7.10.5" - resolved "https://registry.npmjs.org/@babel/register/-/register-7.10.5.tgz#354f3574895f1307f79efe37a51525e52fd38d89" - integrity sha512-eYHdLv43nyvmPn9bfNfrcC4+iYNwdQ8Pxk1MFJuU/U5LpSYl/PH4dFMazCYZDFVi8ueG3shvO+AQfLrxpYulQw== - dependencies: - find-cache-dir "^2.0.0" - lodash "^4.17.19" - make-dir "^2.1.0" - pirates "^4.0.0" - source-map-support "^0.5.16" - "@babel/runtime-corejs2@^7.10.4", "@babel/runtime-corejs2@^7.8.7": version "7.11.2" resolved "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.11.2.tgz#700a03945ebad0d31ba6690fc8a6bcc9040faa47" @@ -1079,7 +1060,7 @@ "@babel/parser" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.11.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.9.0": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.11.0", "@babel/traverse@^7.7.4": version "7.11.0" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz#9b996ce1b98f53f7c3e4175115605d56ed07dd24" integrity sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg== @@ -1094,7 +1075,7 @@ globals "^11.1.0" lodash "^4.17.19" -"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.9.0", "@babel/types@^7.9.5": +"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.9.5": version "7.11.0" resolved "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d" integrity sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA== @@ -2936,11 +2917,6 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@sindresorhus/is@^0.7.0": - version "0.7.0" - resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" - integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== - "@sindresorhus/is@^2.0.0": version "2.1.1" resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz#ceff6a28a5b4867c2dd4a1ba513de278ccbe8bb1" @@ -4047,13 +4023,6 @@ "@types/node" "*" "@types/responselike" "*" -"@types/cheerio@^0.22.8": - version "0.22.21" - resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3" - integrity sha512-aGI3DfswwqgKPiEOTaiHV2ZPC9KEhprpgEbJnv0fZl3SGX0cGgEva1126dGrMC6AJM6v/aihlUgJn9M5DbDZ/Q== - dependencies: - "@types/node" "*" - "@types/classnames@^2.2.9": version "2.2.10" resolved "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.10.tgz#cc658ca319b6355399efc1f5b9e818f1a24bf999" @@ -5447,13 +5416,6 @@ ansi-html@0.0.7: resolved "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= -ansi-red@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c" - integrity sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw= - dependencies: - ansi-wrap "0.1.0" - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -5501,11 +5463,6 @@ ansi-to-html@^0.6.11: dependencies: entities "^1.1.2" -ansi-wrap@0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= - any-observable@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" @@ -5728,18 +5685,11 @@ aproba@^2.0.0: resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== -arch@^2.1.0, arch@^2.1.2: +arch@^2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz#0c52bbe7344bb4fa260c443d2cbad9c00ff2f0bf" integrity sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ== -archive-type@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz#f92e72233056dfc6969472749c267bdb046b1d70" - integrity sha1-+S5yIzBW38aWlHJ0nCZ72wRrHXA= - dependencies: - file-type "^4.2.0" - are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" @@ -6014,14 +5964,7 @@ autolinker@^3.11.0: dependencies: tslib "^1.9.3" -autolinker@~0.28.0: - version "0.28.1" - resolved "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz#0652b491881879f0775dace0cdca3233942a4e47" - integrity sha1-BlK0kYgYefB3XazgzcoyM5QqTkc= - dependencies: - gulp-header "^1.7.1" - -autoprefixer@^9.7.2, autoprefixer@^9.7.5: +autoprefixer@^9.7.2: version "9.8.6" resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== @@ -6395,11 +6338,6 @@ babel-runtime@6.26.0, babel-runtime@^6.26.0: core-js "^2.4.0" regenerator-runtime "^0.11.0" -babylon@^6.18.0: - version "6.18.0" - resolved "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" - integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== - backo2@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" @@ -6487,54 +6425,6 @@ big.js@^5.2.2: resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -bin-build@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/bin-build/-/bin-build-3.0.0.tgz#c5780a25a8a9f966d8244217e6c1f5082a143861" - integrity sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA== - dependencies: - decompress "^4.0.0" - download "^6.2.2" - execa "^0.7.0" - p-map-series "^1.0.0" - tempfile "^2.0.0" - -bin-check@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz#fc495970bdc88bb1d5a35fc17e65c4a149fc4a49" - integrity sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA== - dependencies: - execa "^0.7.0" - executable "^4.1.0" - -bin-version-check@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/bin-version-check/-/bin-version-check-4.0.0.tgz#7d819c62496991f80d893e6e02a3032361608f71" - integrity sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ== - dependencies: - bin-version "^3.0.0" - semver "^5.6.0" - semver-truncate "^1.1.2" - -bin-version@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/bin-version/-/bin-version-3.1.0.tgz#5b09eb280752b1bd28f0c9db3f96f2f43b6c0839" - integrity sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ== - dependencies: - execa "^1.0.0" - find-versions "^3.0.0" - -bin-wrapper@^4.0.0: - version "4.1.0" - resolved "https://registry.npmjs.org/bin-wrapper/-/bin-wrapper-4.1.0.tgz#99348f2cf85031e3ef7efce7e5300aeaae960605" - integrity sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q== - dependencies: - bin-check "^4.1.0" - bin-version-check "^4.0.0" - download "^7.1.0" - import-lazy "^3.1.0" - os-filter-obj "^2.0.0" - pify "^4.0.1" - binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -6600,16 +6490,6 @@ body-parser@1.19.0, body-parser@^1.18.3: raw-body "2.4.0" type-is "~1.6.17" -body@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069" - integrity sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk= - dependencies: - continuable-cache "^0.3.1" - error "^7.0.0" - raw-body "~1.1.0" - safe-json-parse "~1.0.1" - bonjour@^3.5.0: version "3.5.0" resolved "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" @@ -6856,7 +6736,7 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: +buffer@^5.5.0, buffer@^5.6.0: version "5.6.0" resolved "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== @@ -6896,11 +6776,6 @@ byte-size@^5.0.1: resolved "https://registry.npmjs.org/byte-size/-/byte-size-5.0.1.tgz#4b651039a5ecd96767e71a3d7ed380e48bed4191" integrity sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw== -bytes@1: - version "1.0.0" - resolved "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" - integrity sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g= - bytes@3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -6984,19 +6859,6 @@ cacheable-lookup@^5.0.3: resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz#049fdc59dffdd4fc285e8f4f82936591bd59fec3" integrity sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w== -cacheable-request@^2.1.1: - version "2.1.4" - resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" - integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0= - dependencies: - clone-response "1.0.2" - get-stream "3.0.0" - http-cache-semantics "3.8.1" - keyv "3.0.0" - lowercase-keys "1.0.0" - normalize-url "2.0.1" - responselike "1.0.2" - cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -7172,16 +7034,6 @@ caseless@~0.12.0: resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -caw@^2.0.0, caw@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz#6c3ca071fc194720883c2dc5da9b074bfc7e9e95" - integrity sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA== - dependencies: - get-proxy "^2.0.0" - isurl "^1.0.0-alpha5" - tunnel-agent "^0.6.0" - url-to-options "^1.0.1" - chalk@2.4.2, chalk@^2.0.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -7253,28 +7105,6 @@ check-types@^11.1.1: resolved "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f" integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ== -cheerio@0.22.0: - version "0.22.0" - resolved "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" - integrity sha1-qbqoYKP5tZWmuBsahocxIe06Jp4= - dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.0" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash.assignin "^4.0.9" - lodash.bind "^4.1.4" - lodash.defaults "^4.0.1" - lodash.filter "^4.4.0" - lodash.flatten "^4.2.0" - lodash.foreach "^4.3.0" - lodash.map "^4.4.0" - lodash.merge "^4.4.0" - lodash.pick "^4.2.1" - lodash.reduce "^4.4.0" - lodash.reject "^4.4.0" - lodash.some "^4.4.0" - chokidar@^2.0.4, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -7496,7 +7326,7 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clone-response@1.0.2, clone-response@^1.0.2: +clone-response@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= @@ -7550,11 +7380,6 @@ codemirror@^5.52.2: resolved "https://registry.npmjs.org/codemirror/-/codemirror-5.53.2.tgz#9799121cf8c50809cca487304e9de3a74d33f428" integrity sha512-wvSQKS4E+P8Fxn/AQ+tQtJnF1qH5UOlxtugFLpubEZ5jcdH2iXTVinb+Xc/4QjshuOxRm4fUsU2QPF1JJKiyXA== -coffee-script@^1.12.4: - version "1.12.7" - resolved "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz#c05dae0cb79591d05b3070a8433a98c9a89ccc53" - integrity sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw== - collapse-white-space@^1.0.2: version "1.0.6" resolved "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" @@ -7669,7 +7494,7 @@ command-exists-promise@^2.0.2: resolved "https://registry.npmjs.org/command-exists-promise/-/command-exists-promise-2.0.2.tgz#7beecc4b218299f3c61fa69a4047aa0b36a64a99" integrity sha512-T6PB6vdFrwnHXg/I0kivM3DqaCGZLjjYSOe0a5WgFKcz1sOnmOeIjnhQPXVXX3QjVbLyTJ85lJkX6lUpukTzaA== -commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, commander@^2.8.1, commander@~2.20.3: +commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, commander@~2.20.3: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -7761,7 +7586,7 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.5.0, concat-stream@^1.5.2, concat-stream@^1.6.2: +concat-stream@^1.5.0, concat-stream@^1.6.2: version "1.6.2" resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -7781,7 +7606,7 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" -concat-with-sourcemaps@*, concat-with-sourcemaps@^1.1.0: +concat-with-sourcemaps@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== @@ -7838,11 +7663,6 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -console-stream@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/console-stream/-/console-stream-0.1.1.tgz#a095fe07b20465955f2fafd28b5d72bccd949d44" - integrity sha1-oJX+B7IEZZVfL6/Si11yvM2UnUQ= - constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -7863,7 +7683,7 @@ content-disposition@0.5.2: resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= -content-disposition@0.5.3, content-disposition@^0.5.2: +content-disposition@0.5.3: version "0.5.3" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== @@ -7875,11 +7695,6 @@ content-type@~1.0.4: resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -continuable-cache@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" - integrity sha1-vXJ6f67XfnH/OYWskzUakSczrQ8= - conventional-changelog-angular@^5.0.3: version "5.0.6" resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.6.tgz#269540c624553aded809c29a3508fdc2b544c059" @@ -8160,15 +7975,6 @@ cross-spawn@7.0.1: shebang-command "^2.0.0" which "^2.0.1" -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -8178,15 +7984,6 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" -crowdin-cli@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/crowdin-cli/-/crowdin-cli-0.3.0.tgz#eac9989a6fe7feaaf33090397afc187c67b46191" - integrity sha1-6smYmm/n/qrzMJA5evwYfGe0YZE= - dependencies: - request "^2.53.0" - yamljs "^0.2.1" - yargs "^2.3.0" - crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -8280,7 +8077,7 @@ css-select-base-adapter@^0.1.1: resolved "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== -css-select@^1.1.0, css-select@~1.2.0: +css-select@^1.1.0: version "1.2.0" resolved "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= @@ -8642,13 +8439,6 @@ debug@3.1.0, debug@=3.1.0: dependencies: ms "2.0.0" -debug@4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" - integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg== - dependencies: - ms "^2.1.1" - debug@4.1.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -8691,7 +8481,7 @@ decode-uri-component@^0.2.0: resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= -decompress-response@^3.2.0, decompress-response@^3.3.0: +decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= @@ -8719,59 +8509,6 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" - integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== - dependencies: - file-type "^5.2.0" - is-stream "^1.1.0" - tar-stream "^1.5.2" - -decompress-tarbz2@^4.0.0: - version "4.1.1" - resolved "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" - integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== - dependencies: - decompress-tar "^4.1.0" - file-type "^6.1.0" - is-stream "^1.1.0" - seek-bzip "^1.0.5" - unbzip2-stream "^1.0.9" - -decompress-targz@^4.0.0: - version "4.1.1" - resolved "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" - integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== - dependencies: - decompress-tar "^4.1.1" - file-type "^5.2.0" - is-stream "^1.1.0" - -decompress-unzip@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" - integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k= - dependencies: - file-type "^3.8.0" - get-stream "^2.2.0" - pify "^2.3.0" - yauzl "^2.4.2" - -decompress@^4.0.0, decompress@^4.2.0: - version "4.2.1" - resolved "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118" - integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== - dependencies: - decompress-tar "^4.0.0" - decompress-tarbz2 "^4.0.0" - decompress-targz "^4.0.0" - decompress-unzip "^4.0.1" - graceful-fs "^4.1.10" - make-dir "^1.0.0" - pify "^2.3.0" - strip-dirs "^2.0.0" - dedent@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -8992,11 +8729,6 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diacritics-map@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz#6dfc0ff9d01000a2edf2865371cac316e94977af" - integrity sha1-bfwP+dAQAKLt8oZTccrDFulJd68= - diagnostics@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz#cab6ac33df70c9d9a727490ae43ac995a769b22a" @@ -9119,58 +8851,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -docusaurus@^2.0.0-alpha.61: - version "2.0.0-alpha.61" - resolved "https://registry.npmjs.org/docusaurus/-/docusaurus-2.0.0-alpha.61.tgz#f347b81c98c66f1de3ecfccf63fa421ddff52fbb" - integrity sha512-qDU3nOA4Xs95tIjjSETnEuRmTukTzgxyTZ5MgMyuG7y6h4oDHtpLcYf8F+xlXuuWHKv3VSxRJNzV8fZHPgnK3g== - dependencies: - "@babel/core" "^7.9.0" - "@babel/plugin-proposal-class-properties" "^7.8.3" - "@babel/plugin-proposal-object-rest-spread" "^7.9.0" - "@babel/polyfill" "^7.8.7" - "@babel/preset-env" "^7.9.0" - "@babel/preset-react" "^7.9.4" - "@babel/register" "^7.9.0" - "@babel/traverse" "^7.9.0" - "@babel/types" "^7.9.0" - autoprefixer "^9.7.5" - babylon "^6.18.0" - chalk "^3.0.0" - classnames "^2.2.6" - commander "^4.0.1" - crowdin-cli "^0.3.0" - cssnano "^4.1.10" - escape-string-regexp "^2.0.0" - express "^4.17.1" - feed "^4.0.0" - fs-extra "^8.1.0" - gaze "^1.1.3" - github-slugger "^1.2.1" - glob "^7.1.6" - highlight.js "^9.16.2" - imagemin "^6.0.0" - imagemin-gifsicle "^6.0.1" - imagemin-jpegtran "^6.0.0" - imagemin-optipng "^6.0.0" - imagemin-svgo "^7.0.0" - lodash "^4.17.15" - markdown-toc "^1.2.0" - mkdirp "^0.5.1" - portfinder "^1.0.25" - postcss "^7.0.23" - prismjs "^1.17.1" - react "^16.8.4" - react-dev-utils "^9.1.0" - react-dom "^16.8.4" - remarkable "^2.0.0" - request "^2.88.0" - shelljs "^0.8.4" - sitemap "^3.2.2" - tcp-port-used "^1.0.1" - tiny-lr "^1.1.1" - tree-node-cli "^1.2.5" - truncate-html "^1.0.3" - dom-accessibility-api@^0.5.1: version "0.5.2" resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.2.tgz#ef3cdb5d3f0d599d8f9c8b18df2fb63c9793739d" @@ -9199,14 +8879,6 @@ dom-serializer@0, dom-serializer@^0.2.1: domelementtype "^2.0.1" entities "^2.0.0" -dom-serializer@~0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" - integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== - dependencies: - domelementtype "^1.3.0" - entities "^1.1.1" - dom-walk@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" @@ -9217,7 +8889,7 @@ domain-browser@^1.1.1: resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: +domelementtype@1, domelementtype@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== @@ -9348,41 +9020,6 @@ dotenv@^8.0.0: resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== -download@^6.2.2: - version "6.2.5" - resolved "https://registry.npmjs.org/download/-/download-6.2.5.tgz#acd6a542e4cd0bb42ca70cfc98c9e43b07039714" - integrity sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA== - dependencies: - caw "^2.0.0" - content-disposition "^0.5.2" - decompress "^4.0.0" - ext-name "^5.0.0" - file-type "5.2.0" - filenamify "^2.0.0" - get-stream "^3.0.0" - got "^7.0.0" - make-dir "^1.0.0" - p-event "^1.0.0" - pify "^3.0.0" - -download@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/download/-/download-7.1.0.tgz#9059aa9d70b503ee76a132897be6dec8e5587233" - integrity sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ== - dependencies: - archive-type "^4.0.0" - caw "^2.0.1" - content-disposition "^0.5.2" - decompress "^4.2.0" - ext-name "^5.0.0" - file-type "^8.1.0" - filenamify "^2.0.0" - get-stream "^3.0.0" - got "^8.3.1" - make-dir "^1.2.0" - p-event "^2.1.0" - pify "^3.0.0" - duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -9588,13 +9225,6 @@ error-stack-parser@^2.0.6: dependencies: stackframe "^1.1.1" -error@^7.0.0: - version "7.2.1" - resolved "https://registry.npmjs.org/error/-/error-7.2.1.tgz#eab21a4689b5f684fc83da84a0e390de82d94894" - integrity sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA== - dependencies: - string-template "~0.2.1" - es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4: version "1.17.4" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" @@ -10072,17 +9702,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -exec-buffer@^3.0.0: - version "3.2.0" - resolved "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz#b1686dbd904c7cf982e652c1f5a79b1e5573082b" - integrity sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA== - dependencies: - execa "^0.7.0" - p-finally "^1.0.0" - pify "^3.0.0" - rimraf "^2.5.4" - tempfile "^2.0.0" - exec-sh@^0.3.2: version "0.3.4" resolved "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" @@ -10104,19 +9723,6 @@ execa@3.4.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -10145,7 +9751,7 @@ execa@^4.0.0, execa@^4.0.1: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -executable@^4.1.0, executable@^4.1.1: +executable@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== @@ -10175,13 +9781,6 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= - dependencies: - fill-range "^2.1.0" - expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" @@ -10254,21 +9853,6 @@ express@^4.0.0, express@^4.17.0, express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" -ext-list@^2.0.0: - version "2.2.2" - resolved "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" - integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== - dependencies: - mime-db "^1.28.0" - -ext-name@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6" - integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== - dependencies: - ext-list "^2.0.0" - sort-keys-length "^1.0.0" - ext@^1.1.2: version "1.4.0" resolved "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" @@ -10436,7 +10020,7 @@ fault@^1.0.0, fault@^1.0.2: dependencies: format "^0.2.0" -faye-websocket@^0.10.0, faye-websocket@~0.10.0: +faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= @@ -10469,13 +10053,6 @@ fecha@^2.3.3: resolved "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd" integrity sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg== -feed@^4.0.0: - version "4.2.1" - resolved "https://registry.npmjs.org/feed/-/feed-4.2.1.tgz#b246ef891051c7dbf088ca203341d9fb0444baee" - integrity sha512-l28KKcK1J/u3iq5dRDmmoB2p7dtBfACC2NqJh4dI2kFptxH0asfjmOfcxqh5Sv8suAlVa73gZJ4REY5RrafVvg== - dependencies: - xml-js "^1.6.11" - fetch-readablestream@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/fetch-readablestream/-/fetch-readablestream-0.2.0.tgz#eaa6d1a76b12de2d4731a343393c6ccdcfe2c795" @@ -10486,7 +10063,7 @@ figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: resolved "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== -figures@^1.3.5, figures@^1.7.0: +figures@^1.7.0: version "1.7.0" resolved "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= @@ -10536,36 +10113,6 @@ file-system-cache@^1.0.5: fs-extra "^0.30.0" ramda "^0.21.0" -file-type@5.2.0, file-type@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" - integrity sha1-LdvqfHP/42No365J3DOMBYwritY= - -file-type@^10.4.0, file-type@^10.7.0: - version "10.11.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz#2961d09e4675b9fb9a3ee6b69e9cd23f43fd1890" - integrity sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw== - -file-type@^3.8.0: - version "3.9.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" - integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= - -file-type@^4.2.0: - version "4.4.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz#1b600e5fca1fbdc6e80c0a70c71c8dba5f7906c5" - integrity sha1-G2AOX8ofvcboDApwxxyNul95BsU= - -file-type@^6.1.0: - version "6.2.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" - integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== - -file-type@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz#244f3b7ef641bbe0cca196c7276e4b332399f68c" - integrity sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ== - file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -10576,20 +10123,6 @@ filefy@0.1.10: resolved "https://registry.npmjs.org/filefy/-/filefy-0.1.10.tgz#174677c8e2fa5bc39a3af0ed6fb492f16b8fbf42" integrity sha512-VgoRVOOY1WkTpWH+KBy8zcU1G7uQTVsXqhWEgzryB9A5hg2aqCyZ6aQ/5PSzlqM5+6cnVrX6oYV0XqD3HZSnmQ== -filename-reserved-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" - integrity sha1-q/c9+rc10EVECr/qLZHzieu/oik= - -filenamify@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9" - integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA== - dependencies: - filename-reserved-regex "^2.0.0" - strip-outer "^1.0.0" - trim-repeated "^1.0.0" - filesize@3.6.1: version "3.6.1" resolved "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" @@ -10600,17 +10133,6 @@ filesize@6.0.1: resolved "https://registry.npmjs.org/filesize/-/filesize-6.0.1.tgz#f850b509909c7c86f7e450ea19006c31c2ed3d2f" integrity sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg== -fill-range@^2.1.0: - version "2.2.4" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" - integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^3.0.0" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - fill-range@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -10641,7 +10163,7 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: +find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== @@ -10694,7 +10216,7 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" -find-versions@^3.0.0, find-versions@^3.2.0: +find-versions@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" integrity sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww== @@ -10900,7 +10422,7 @@ fresh@0.5.2: resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -from2@^2.1.0, from2@^2.1.1: +from2@^2.1.0: version "2.3.0" resolved "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= @@ -11047,13 +10569,6 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gaze@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" - integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== - dependencies: - globule "^1.0.0" - generic-names@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz#f8a378ead2ccaa7a34f0317b05554832ae41b872" @@ -11120,13 +10635,6 @@ get-port@^5.1.1: resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== -get-proxy@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz#349f2b4d91d44c4d4d4e9cba2ad90143fac5ef93" - integrity sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw== - dependencies: - npm-conf "^1.1.0" - get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" @@ -11137,19 +10645,6 @@ get-stdin@^6.0.0: resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== -get-stream@3.0.0, get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - -get-stream@^2.2.0: - version "2.3.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" - integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4= - dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" - get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -11188,16 +10683,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -gifsicle@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/gifsicle/-/gifsicle-4.0.1.tgz#30e1e61e3ee4884ef702641b2e98a15c2127b2e2" - integrity sha512-A/kiCLfDdV+ERV/UB+2O41mifd+RxH8jlRG8DMxZO84Bma/Fw0htqZ+hY2iaalLRNyUu7tYZQslqUBJxBggxbg== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - execa "^1.0.0" - logalot "^2.0.0" - git-raw-commits@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz#d92addf74440c14bcc5c83ecce3fb7f8a79118b5" @@ -11247,7 +10732,7 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" -github-slugger@^1.2.1, github-slugger@^1.3.0: +github-slugger@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz#9bd0a95c5efdfc46005e82a906ef8e2a059124c9" integrity sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q== @@ -11289,7 +10774,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1: +glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -11383,7 +10868,7 @@ globby@11.0.1, globby@^11.0.0: merge2 "^1.3.0" slash "^3.0.0" -globby@8.0.2, globby@^8.0.1: +globby@8.0.2: version "8.0.2" resolved "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== @@ -11447,15 +10932,6 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" -globule@^1.0.0: - version "1.3.2" - resolved "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz#d8bdd9e9e4eef8f96e245999a5dee7eb5d8529c4" - integrity sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA== - dependencies: - glob "~7.1.1" - lodash "~4.17.10" - minimatch "~3.0.2" - good-listener@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" @@ -11501,49 +10977,6 @@ got@^11.5.2: p-cancelable "^2.0.0" responselike "^2.0.0" -got@^7.0.0: - version "7.1.0" - resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" - integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== - dependencies: - decompress-response "^3.2.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" - -got@^8.3.1: - version "8.3.2" - resolved "https://registry.npmjs.org/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" - integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw== - dependencies: - "@sindresorhus/is" "^0.7.0" - cacheable-request "^2.1.1" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - into-stream "^3.1.0" - is-retry-allowed "^1.1.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - mimic-response "^1.0.0" - p-cancelable "^0.4.0" - p-timeout "^2.0.1" - pify "^3.0.0" - safe-buffer "^5.1.1" - timed-out "^4.0.1" - url-parse-lax "^3.0.0" - url-to-options "^1.0.1" - got@^9.6.0: version "9.6.0" resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -11561,7 +10994,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -11692,17 +11125,6 @@ graphql@^14.5.3: dependencies: iterall "^1.2.2" -gray-matter@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz#3042d9adec2a1ded6a7707a9ed2380f8a17a430e" - integrity sha1-MELZrewqHe1qdwep7SOA+KF6Qw4= - dependencies: - ansi-red "^0.1.1" - coffee-script "^1.12.4" - extend-shallow "^2.0.1" - js-yaml "^3.8.1" - toml "^2.3.2" - growly@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -11713,15 +11135,6 @@ gud@^1.0.0: resolved "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== -gulp-header@^1.7.1: - version "1.8.12" - resolved "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz#ad306be0066599127281c4f8786660e705080a84" - integrity sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ== - dependencies: - concat-with-sourcemaps "*" - lodash.template "^4.4.0" - through2 "^2.0.0" - gzip-size@5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" @@ -11787,23 +11200,11 @@ has-flag@^4.0.0: resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" - integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== - has-symbols@^1.0.0, has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== - dependencies: - has-symbol-support-x "^1.4.1" - has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -11908,11 +11309,6 @@ highlight.js@^10.1.1, highlight.js@~10.1.0: resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.2.tgz#c20db951ba1c22c055010648dfffd7b2a968e00c" integrity sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA== -highlight.js@^9.16.2: - version "9.18.3" - resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.3.tgz#a1a0a2028d5e3149e2380f8a865ee8516703d634" - integrity sha512-zBZAmhSupHIl5sITeMqIJnYCDfAEc3Gdkqj65wC1lpI468MMQeeQkhcIAvk+RylAkxrCcI9xy9piHiXeQ1BdzQ== - highlight.js@~9.13.0: version "9.13.1" resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" @@ -11995,7 +11391,7 @@ hsla-regex@^1.0.0: resolved "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= -html-comment-regex@^1.1.0, html-comment-regex@^1.1.2: +html-comment-regex@^1.1.0: version "1.1.2" resolved "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== @@ -12069,7 +11465,7 @@ html2canvas@1.0.0-alpha.12: dependencies: css-line-break "1.0.1" -htmlparser2@^3.3.0, htmlparser2@^3.9.1: +htmlparser2@^3.3.0: version "3.10.1" resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== @@ -12091,7 +11487,7 @@ htmlparser2@^4.0, htmlparser2@^4.1.0: domutils "^2.0.0" entities "^2.0.0" -http-cache-semantics@3.8.1, http-cache-semantics@^3.8.1: +http-cache-semantics@^3.8.1: version "3.8.1" resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== @@ -12317,53 +11713,6 @@ ignore@^5.1.1, ignore@^5.1.4: resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== -imagemin-gifsicle@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/imagemin-gifsicle/-/imagemin-gifsicle-6.0.1.tgz#6abad4e95566d52e5a104aba1c24b4f3b48581b3" - integrity sha512-kuu47c6iKDQ6R9J10xCwL0lgs0+sMz3LRHqRcJ2CRBWdcNmo3T5hUaM8hSZfksptZXJLGKk8heSAvwtSdB1Fng== - dependencies: - exec-buffer "^3.0.0" - gifsicle "^4.0.0" - is-gif "^3.0.0" - -imagemin-jpegtran@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/imagemin-jpegtran/-/imagemin-jpegtran-6.0.0.tgz#c8d3bcfb6ec9c561c20a987142854be70d90b04f" - integrity sha512-Ih+NgThzqYfEWv9t58EItncaaXIHR0u9RuhKa8CtVBlMBvY0dCIxgQJQCfwImA4AV1PMfmUKlkyIHJjb7V4z1g== - dependencies: - exec-buffer "^3.0.0" - is-jpg "^2.0.0" - jpegtran-bin "^4.0.0" - -imagemin-optipng@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/imagemin-optipng/-/imagemin-optipng-6.0.0.tgz#a6bfc7b542fc08fc687e83dfb131249179a51a68" - integrity sha512-FoD2sMXvmoNm/zKPOWdhKpWdFdF9qiJmKC17MxZJPH42VMAp17/QENI/lIuP7LCUnLVAloO3AUoTSNzfhpyd8A== - dependencies: - exec-buffer "^3.0.0" - is-png "^1.0.0" - optipng-bin "^5.0.0" - -imagemin-svgo@^7.0.0: - version "7.1.0" - resolved "https://registry.npmjs.org/imagemin-svgo/-/imagemin-svgo-7.1.0.tgz#528a42fd3d55eff5d4af8fd1113f25fb61ad6d9a" - integrity sha512-0JlIZNWP0Luasn1HT82uB9nU9aa+vUj6kpT+MjPW11LbprXC+iC4HDwn1r4Q2/91qj4iy9tRZNsFySMlEpLdpg== - dependencies: - is-svg "^4.2.1" - svgo "^1.3.2" - -imagemin@^6.0.0: - version "6.1.0" - resolved "https://registry.npmjs.org/imagemin/-/imagemin-6.1.0.tgz#62508b465728fea36c03cdc07d915fe2d8cf9e13" - integrity sha512-8ryJBL1CN5uSHpiBMX0rJw79C9F9aJqMnjGnrd/1CafegpNuA81RBAAru/jQQEOWlOJJlpRnlcVFF6wq+Ist0A== - dependencies: - file-type "^10.7.0" - globby "^8.0.1" - make-dir "^1.0.0" - p-pipe "^1.1.0" - pify "^4.0.1" - replace-ext "^1.0.0" - immer@1.10.0: version "1.10.0" resolved "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" @@ -12423,11 +11772,6 @@ import-lazy@^2.1.0: resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= -import-lazy@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz#891279202c8a2280fdbd6674dbd8da1a1dfc67cc" - integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ== - import-local@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -12629,14 +11973,6 @@ interpret@^2.0.0, interpret@^2.2.0: resolved "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== -into-stream@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" - integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY= - dependencies: - from2 "^2.1.1" - p-is-promise "^1.1.0" - invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -12881,13 +12217,6 @@ is-generator-fn@^2.0.0: resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-gif@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-gif/-/is-gif-3.0.0.tgz#c4be60b26a301d695bb833b20d9b5d66c6cf83b1" - integrity sha512-IqJ/jlbw5WJSNfwQ/lHEDXF8rxhRgF6ythk2oiEvhpG29F704eX9NO6TvPfMiq9DrbwgcEDnETYNcZDPewQoVw== - dependencies: - file-type "^10.4.0" - is-glob@4.0.1, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" @@ -12932,11 +12261,6 @@ is-interactive@^1.0.0: resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-jpg@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-jpg/-/is-jpg-2.0.0.tgz#2e1997fa6e9166eaac0242daae443403e4ef1d97" - integrity sha1-LhmX+m6RZuqsAkLarkQ0A+TvHZc= - is-map@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" @@ -12947,23 +12271,11 @@ is-module@^1.0.0: resolved "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= -is-natural-number@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" - integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= - is-npm@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= - dependencies: - kind-of "^3.0.2" - is-number@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -12971,11 +12283,6 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== - is-number@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -13046,11 +12353,6 @@ is-plain-object@^3.0.0: dependencies: isobject "^4.0.0" -is-png@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-png/-/is-png-1.1.0.tgz#d574b12bf275c0350455570b0e5b57ab062077ce" - integrity sha1-1XSxK/J1wDUEVVcLDltXqwYgd84= - is-potential-custom-element-name@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" @@ -13092,11 +12394,6 @@ is-resolvable@^1.0.0: resolved "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== -is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - is-root@2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" @@ -13114,7 +12411,7 @@ is-ssh@^1.3.0: dependencies: protocols "^1.1.0" -is-stream@^1.0.0, is-stream@^1.1.0: +is-stream@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -13136,13 +12433,6 @@ is-svg@^3.0.0: dependencies: html-comment-regex "^1.1.0" -is-svg@^4.2.1: - version "4.2.1" - resolved "https://registry.npmjs.org/is-svg/-/is-svg-4.2.1.tgz#095b496e345fec9211c2a7d5d021003e040d6f81" - integrity sha512-PHx3ANecKsKNl5y5+Jvt53Y4J7MfMpbNZkv384QNiswMKAWIbvcqbPz+sYbFKJI8Xv3be01GSFniPmoaP+Ai5A== - dependencies: - html-comment-regex "^1.1.2" - is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -13169,11 +12459,6 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" -is-url@^1.2.2: - version "1.2.4" - resolved "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" - integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== - is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -13221,15 +12506,6 @@ is-yarn-global@^0.3.0: resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== -is2@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/is2/-/is2-2.0.1.tgz#8ac355644840921ce435d94f05d3a94634d3481a" - integrity sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA== - dependencies: - deep-is "^0.1.3" - ip-regex "^2.1.0" - is-url "^1.2.2" - isarray@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -13333,14 +12609,6 @@ istanbul-reports@^3.0.2: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" - iterall@^1.1.3, iterall@^1.2.1, iterall@^1.2.2: version "1.3.0" resolved "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" @@ -13799,15 +13067,6 @@ jose@^1.27.1: dependencies: "@panva/asn1.js" "^1.0.0" -jpegtran-bin@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/jpegtran-bin/-/jpegtran-bin-4.0.0.tgz#d00aed809fba7aa6f30817e59eee4ddf198f8f10" - integrity sha512-2cRl1ism+wJUoYAYFt6O/rLBfpXNWG2dUWbgcEkTt5WGMnqI46eEro8T4C5zGROxKRqyKpCBSdHPvt5UYCtxaQ== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - logalot "^2.0.0" - js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" @@ -13833,7 +13092,7 @@ js-tokens@^3.0.2: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@^3.13.1, js-yaml@^3.14.0, js-yaml@^3.8.1, js-yaml@^3.8.3: +js-yaml@^3.13.1, js-yaml@^3.14.0, js-yaml@^3.8.3: version "3.14.0" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== @@ -14194,13 +13453,6 @@ jwt-decode@2.2.0: resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79" integrity sha1-fYa9VmefWM5qhHBKZX3TkruoGnk= -keyv@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" - integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== - dependencies: - json-buffer "3.0.0" - keyv@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -14313,13 +13565,6 @@ lazy-cache@^1.0.3: resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= -lazy-cache@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264" - integrity sha1-uRkKT5EzVGlIQIWfio9whNiCImQ= - dependencies: - set-getter "^0.1.0" - lazy-universal-dotenv@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/lazy-universal-dotenv/-/lazy-universal-dotenv-3.0.1.tgz#a6c8938414bca426ab8c9463940da451a911db38" @@ -14442,16 +13687,6 @@ lint-staged@^10.1.0: string-argv "0.3.1" stringify-object "^3.3.0" -list-item@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz#0c65d00e287cb663ccb3cb3849a77e89ec268a56" - integrity sha1-DGXQDih8tmPMs8s4Sad+iewmilY= - dependencies: - expand-range "^1.8.1" - extend-shallow "^2.0.1" - is-number "^2.1.0" - repeat-string "^1.5.2" - listr-silent-renderer@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" @@ -14510,11 +13745,6 @@ listr@^0.14.3: p-map "^2.0.0" rxjs "^6.3.3" -livereload-js@^2.3.0: - version "2.4.0" - resolved "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" - integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== - load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -14627,26 +13857,11 @@ lodash.assign@^4.1.0, lodash.assign@^4.2.0: resolved "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= -lodash.assignin@^4.0.9: - version "4.2.0" - resolved "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" - integrity sha1-uo31+4QesKPoBEIysOJjqNxqKKI= - -lodash.bind@^4.1.4: - version "4.2.1" - resolved "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" - integrity sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU= - lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= -lodash.chunk@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz#66e5ce1f76ed27b4303d8c6512e8d1216e8106bc" - integrity sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw= - lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -14657,17 +13872,7 @@ lodash.debounce@^4, lodash.debounce@^4.0.8: resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= -lodash.defaults@^4.0.1: - version "4.2.0" - resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= - -lodash.filter@^4.4.0: - version "4.6.0" - resolved "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" - integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4= - -lodash.flatten@^4.2.0, lodash.flatten@^4.4.0: +lodash.flatten@^4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= @@ -14677,11 +13882,6 @@ lodash.flattendeep@^4.0.0: resolved "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= -lodash.foreach@^4.3.0: - version "4.5.0" - resolved "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" - integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= - lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -14692,62 +13892,27 @@ lodash.ismatch@^4.4.0: resolved "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= -lodash.map@^4.4.0: - version "4.6.0" - resolved "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" - integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM= - lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= -lodash.merge@^4.4.0: - version "4.6.2" - resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - lodash.once@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= -lodash.padstart@^4.6.1: - version "4.6.1" - resolved "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b" - integrity sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs= - -lodash.pick@^4.2.1: - version "4.4.0" - resolved "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" - integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= - -lodash.reduce@^4.4.0: - version "4.6.0" - resolved "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" - integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs= - -lodash.reject@^4.4.0: - version "4.6.0" - resolved "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" - integrity sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU= - lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= -lodash.some@^4.4.0: - version "4.6.0" - resolved "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" - integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= - lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash.template@^4.0.2, lodash.template@^4.4.0, lodash.template@^4.5.0: +lodash.template@^4.0.2, lodash.template@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== @@ -14777,7 +13942,7 @@ lodash.without@^4.4.0: resolved "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.1, lodash@~4.17.10: +lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.1: version "4.17.20" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -14822,14 +13987,6 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" -logalot@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/logalot/-/logalot-2.1.0.tgz#5f8e8c90d304edf12530951a5554abb8c5e3f552" - integrity sha1-X46MkNME7fElMJUaVVSruMXj9VI= - dependencies: - figures "^1.3.5" - squeak "^1.0.0" - logform@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz#957155ebeb67a13164069825ce67ddb5bb2dd360" @@ -14851,11 +14008,6 @@ long@^4.0.0: resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -longest@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -14878,11 +14030,6 @@ lower-case@^2.0.1: dependencies: tslib "^1.10.0" -lowercase-keys@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY= - lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -14917,24 +14064,6 @@ lowlight@~1.11.0: fault "^1.0.2" highlight.js "~9.13.0" -lpad-align@^1.0.1: - version "1.1.2" - resolved "https://registry.npmjs.org/lpad-align/-/lpad-align-1.1.2.tgz#21f600ac1c3095c3c6e497ee67271ee08481fe9e" - integrity sha1-IfYArBwwlcPG5JfuZyce4ISB/p4= - dependencies: - get-stdin "^4.0.1" - indent-string "^2.1.0" - longest "^1.0.0" - meow "^3.3.0" - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - lru-cache@^5.0.0, lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -14961,7 +14090,7 @@ magic-string@^0.25.2: dependencies: sourcemap-codec "^1.4.4" -make-dir@^1.0.0, make-dir@^1.2.0: +make-dir@^1.0.0: version "1.3.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== @@ -15083,11 +14212,6 @@ markdown-it@^9.1.0: mdurl "^1.0.1" uc.micro "^1.0.5" -markdown-link@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz#32c5c65199a6457316322d1e4229d13407c8c7cf" - integrity sha1-MsXGUZmmRXMWMi0eQinRNAfIx88= - markdown-to-jsx@^6.11.4: version "6.11.4" resolved "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.4.tgz#b4528b1ab668aef7fe61c1535c27e837819392c5" @@ -15096,24 +14220,6 @@ markdown-to-jsx@^6.11.4: prop-types "^15.6.2" unquote "^1.1.0" -markdown-toc@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz#44a15606844490314afc0444483f9e7b1122c339" - integrity sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg== - dependencies: - concat-stream "^1.5.2" - diacritics-map "^0.1.0" - gray-matter "^2.1.0" - lazy-cache "^2.0.2" - list-item "^1.1.1" - markdown-link "^0.1.1" - minimist "^1.2.0" - mixin-deep "^1.1.3" - object.pick "^1.2.0" - remarkable "^1.7.1" - repeat-string "^1.6.1" - strip-color "^0.1.0" - material-table@1.68.0: version "1.68.0" resolved "https://registry.npmjs.org/material-table/-/material-table-1.68.0.tgz#275c3d9a885c40ae4bc5a7461c00e877f92397b9" @@ -15132,11 +14238,6 @@ material-table@1.68.0: react-beautiful-dnd "^13.0.0" react-double-scrollbar "0.0.15" -math-random@^1.0.1: - version "1.0.4" - resolved "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" - integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== - md5.js@^1.3.4: version "1.3.5" resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -15340,7 +14441,7 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.44.0, "mime-db@>= 1.43.0 < 2", mime-db@^1.28.0: +mime-db@1.44.0, "mime-db@>= 1.43.0 < 2": version "1.44.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== @@ -15449,7 +14550,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: +minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -15541,7 +14642,7 @@ mitt@^1.1.2: resolved "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz#cb24e6569c806e31bd4e3995787fe38a04fdf90d" integrity sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw== -mixin-deep@^1.1.3, mixin-deep@^1.2.0: +mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== @@ -16094,15 +15195,6 @@ normalize-url@1.9.1: query-string "^4.1.0" sort-keys "^1.0.0" -normalize-url@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" - integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== - dependencies: - prepend-http "^2.0.0" - query-string "^5.0.1" - sort-keys "^2.0.0" - normalize-url@^3.0.0, normalize-url@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" @@ -16120,14 +15212,6 @@ npm-bundled@^1.0.1: dependencies: npm-normalize-package-bin "^1.0.1" -npm-conf@^1.1.0: - version "1.1.3" - resolved "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - npm-lifecycle@^3.1.2: version "3.1.4" resolved "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.4.tgz#de6975c7d8df65f5150db110b57cce498b0b604c" @@ -16471,15 +15555,6 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -optipng-bin@^5.0.0: - version "5.1.0" - resolved "https://registry.npmjs.org/optipng-bin/-/optipng-bin-5.1.0.tgz#a7c7ab600a3ab5a177dae2f94c2d800aa386b5a9" - integrity sha512-9baoqZTNNmXQjq/PQTWEXbVV3AMO2sI/GaaqZJZ8SExfAzjijeAP7FEeT+TtyumSw7gr0PZtSUYB/Ke7iHQVKA== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - logalot "^2.0.0" - ora@*, ora@^4.0.3: version "4.0.4" resolved "https://registry.npmjs.org/ora/-/ora-4.0.4.tgz#e8da697cc5b6a47266655bf68e0fb588d29a545d" @@ -16506,13 +15581,6 @@ os-browserify@^0.3.0: resolved "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-filter-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz#1c0b62d5f3a2442749a2d139e6dddee6e81d8d16" - integrity sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg== - dependencies: - arch "^2.1.0" - os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -16551,16 +15619,6 @@ ospath@^1.2.2: resolved "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" - integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== - -p-cancelable@^0.4.0: - version "0.4.1" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" - integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== - p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -16576,20 +15634,6 @@ p-each-series@^2.1.0: resolved "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" integrity sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ== -p-event@^1.0.0: - version "1.3.0" - resolved "https://registry.npmjs.org/p-event/-/p-event-1.3.0.tgz#8e6b4f4f65c72bc5b6fe28b75eda874f96a4a085" - integrity sha1-jmtPT2XHK8W2/ii3XtqHT5akoIU= - dependencies: - p-timeout "^1.1.1" - -p-event@^2.1.0: - version "2.3.1" - resolved "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz#596279ef169ab2c3e0cae88c1cfbb08079993ef6" - integrity sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA== - dependencies: - p-timeout "^2.0.1" - p-event@^4.0.0: version "4.2.0" resolved "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" @@ -16607,11 +15651,6 @@ p-finally@^2.0.0: resolved "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== -p-is-promise@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" - integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= - p-limit@3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" @@ -16680,7 +15719,7 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" -p-pipe@^1.1.0, p-pipe@^1.2.0: +p-pipe@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" integrity sha1-SxoROZoRUgpneQ7loMHViB1r7+k= @@ -16712,20 +15751,6 @@ p-retry@^3.0.1: dependencies: retry "^0.12.0" -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" - integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= - dependencies: - p-finally "^1.0.0" - -p-timeout@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" - integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== - dependencies: - p-finally "^1.0.0" - p-timeout@^3.1.0: version "3.2.0" resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" @@ -17326,7 +16351,7 @@ pinkie@^2.0.0: resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pirates@^4.0.0, pirates@^4.0.1: +pirates@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== @@ -17404,7 +16429,7 @@ popper.js@^1.14.1, popper.js@^1.14.4, popper.js@^1.14.7: resolved "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== -portfinder@^1.0.25, portfinder@^1.0.26: +portfinder@^1.0.26: version "1.0.28" resolved "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== @@ -17793,7 +16818,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^ resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== -"postcss@5 - 7", postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.23, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: +"postcss@5 - 7", postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: version "7.0.32" resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== @@ -17857,7 +16882,7 @@ prelude-ls@~1.1.2: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prepend-http@^1.0.0, prepend-http@^1.0.1: +prepend-http@^1.0.0: version "1.0.4" resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= @@ -17915,7 +16940,7 @@ pretty-hrtime@^1.0.3: resolved "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= -prismjs@^1.17.1, prismjs@^1.21.0, prismjs@^1.8.4, prismjs@~1.21.0: +prismjs@^1.21.0, prismjs@^1.8.4, prismjs@~1.21.0: version "1.21.0" resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.21.0.tgz#36c086ec36b45319ec4218ee164c110f9fc015a3" integrity sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw== @@ -18091,11 +17116,6 @@ ps-tree@1.2.0: dependencies: event-stream "=3.3.4" -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - psl@^1.1.28: version "1.8.0" resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -18183,7 +17203,7 @@ qs@6.7.0: resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== -qs@^6.4.0, qs@^6.5.1, qs@^6.6.0, qs@^6.9.4: +qs@^6.5.1, qs@^6.6.0, qs@^6.9.4: version "6.9.4" resolved "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== @@ -18201,15 +17221,6 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -query-string@^5.0.1: - version "5.1.1" - resolved "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" - integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - querystring-browser@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/querystring-browser/-/querystring-browser-1.0.4.tgz#f2e35881840a819bc7b1bf597faf0979e6622dc6" @@ -18282,15 +17293,6 @@ ramldt2jsonschema@^1.1.0: json-schema-migrate "^0.2.0" webapi-parser "^0.5.0" -randomatic@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" - integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== - dependencies: - is-number "^4.0.0" - kind-of "^6.0.0" - math-random "^1.0.1" - randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -18326,14 +17328,6 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@~1.1.0: - version "1.1.7" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425" - integrity sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU= - dependencies: - bytes "1" - string_decoder "0.10" - raw-loader@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/raw-loader/-/raw-loader-3.1.0.tgz#5e9d399a5a222cc0de18f42c3bc5e49677532b3f" @@ -18445,7 +17439,7 @@ react-dev-utils@^10.2.1: strip-ansi "6.0.0" text-table "0.2.0" -react-dev-utils@^9.0.0, react-dev-utils@^9.1.0: +react-dev-utils@^9.0.0: version "9.1.0" resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.1.0.tgz#3ad2bb8848a32319d760d0a84c56c14bdaae5e81" integrity sha512-X2KYF/lIGyGwP/F/oXgGDF24nxDA2KC4b7AFto+eqzc/t838gpSGiaU8trTqHXOohuLxxc5qi1eDzsl9ucPDpg== @@ -18490,7 +17484,7 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" -react-dom@^16.12.0, react-dom@^16.13.1, react-dom@^16.8.3, react-dom@^16.8.4: +react-dom@^16.12.0, react-dom@^16.13.1, react-dom@^16.8.3: version "16.13.1" resolved "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== @@ -18903,7 +17897,7 @@ react-wait@^0.3.0: resolved "https://registry.npmjs.org/react-wait/-/react-wait-0.3.0.tgz#0cdd4d919012451a5bc3ab0a16d00c6fd9a8c10b" integrity sha512-kB5x/kMKWcn0uVr9gBdNz21/oGbQwEQnF3P9p6E9yLfJ9DRcKS0fagbgYMFI0YFOoyKDj+2q6Rwax0kTYJF37g== -react@^16.0.0, react@^16.12.0, react@^16.13.1, react@^16.8.3, react@^16.8.4: +react@^16.0.0, react@^16.12.0, react@^16.13.1, react@^16.8.3: version "16.13.1" resolved "https://registry.npmjs.org/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== @@ -19270,15 +18264,7 @@ remark-parse@^5.0.0: vfile-location "^2.0.0" xtend "^4.0.1" -remarkable@^1.7.1: - version "1.7.4" - resolved "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz#19073cb960398c87a7d6546eaa5e50d2022fcd00" - integrity sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg== - dependencies: - argparse "^1.0.10" - autolinker "~0.28.0" - -remarkable@^2.0.0, remarkable@^2.0.1: +remarkable@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/remarkable/-/remarkable-2.0.1.tgz#280ae6627384dfb13d98ee3995627ca550a12f31" integrity sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA== @@ -19324,11 +18310,6 @@ replace-ext@1.0.0: resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= -replace-ext@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" - integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== - replace-in-file@^6.0.0: version "6.1.0" resolved "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.1.0.tgz#9f9ddd7bb70d6ad231d2ad692e1b646e73d06647" @@ -19361,7 +18342,7 @@ request-promise-native@^1.0.5, request-promise-native@^1.0.8: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.53.0, request@^2.55.0, request@^2.85.0, request@^2.87.0, request@^2.88.0, request@^2.88.2: +request@^2.55.0, request@^2.85.0, request@^2.87.0, request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -19483,7 +18464,7 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12. dependencies: path-parse "^1.0.6" -responselike@1.0.2, responselike@^1.0.2: +responselike@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= @@ -19746,11 +18727,6 @@ safe-identifier@^0.4.1: resolved "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.1.tgz#b6516bf72594f03142b5f914f4c01842ccb1b678" integrity sha512-73tOz5TXsq3apuCc3vC8c9QRhhdNZGiBhHmPPjqpH4TO5oCDqk8UIsDcSs/RG6dYcFAkOOva0pqHS3u7hh7XXA== -safe-json-parse@~1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" - integrity sha1-PnZyPjjf3aE8mx0poeB//uSzC1c= - safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -19833,13 +18809,6 @@ screenfull@^5.0.0: resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.0.2.tgz#b9acdcf1ec676a948674df5cd0ff66b902b0bed7" integrity sha512-cCF2b+L/mnEiORLN5xSAz6H3t18i2oHh9BA8+CQlAh5DRw2+NFAGQJOSYbcGw8B2k04g/lVvFcfZ83b3ysH5UQ== -seek-bzip@^1.0.5: - version "1.0.6" - resolved "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4" - integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ== - dependencies: - commander "^2.8.1" - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -19874,13 +18843,6 @@ semver-regex@^2.0.0: resolved "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== -semver-truncate@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/semver-truncate/-/semver-truncate-1.1.2.tgz#57f41de69707a62709a7e0104ba2117109ea47e8" - integrity sha1-V/Qd5pcHpicJp+AQS6IRcQnqR+g= - dependencies: - semver "^5.3.0" - "semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -19993,13 +18955,6 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-getter@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376" - integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y= - dependencies: - to-object-path "^0.3.0" - set-harmonic-interval@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" @@ -20099,7 +19054,7 @@ shell-quote@1.7.2: resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== -shelljs@^0.8.3, shelljs@^0.8.4: +shelljs@^0.8.3: version "0.8.4" resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== @@ -20179,16 +19134,6 @@ sisteransi@^1.0.4: resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -sitemap@^3.2.2: - version "3.2.2" - resolved "https://registry.npmjs.org/sitemap/-/sitemap-3.2.2.tgz#3f77c358fa97b555c879e457098e39910095c62b" - integrity sha512-TModL/WU4m2q/mQcrDgNANn0P4LwprM9MMvG4hu5zP4c6IIKs2YLTu6nXXnNr8ODW/WFtxKggiJ1EGn2W0GNmg== - dependencies: - lodash.chunk "^4.2.0" - lodash.padstart "^4.6.1" - whatwg-url "^7.0.0" - xmlbuilder "^13.0.0" - slash@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -20313,13 +19258,6 @@ socks@~2.3.2: ip "1.1.5" smart-buffer "^4.1.0" -sort-keys-length@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" - integrity sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg= - dependencies: - sort-keys "^1.0.0" - sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" @@ -20350,7 +19288,7 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12: +source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -20493,15 +19431,6 @@ sqlite3@^4.2.0: nan "^2.12.1" node-pre-gyp "^0.11.0" -squeak@^1.0.0: - version "1.3.0" - resolved "https://registry.npmjs.org/squeak/-/squeak-1.3.0.tgz#33045037b64388b567674b84322a6521073916c3" - integrity sha1-MwRQN7ZDiLVnZ0uEMiplIQc5FsM= - dependencies: - chalk "^1.0.0" - console-stream "^0.1.1" - lpad-align "^1.0.1" - srcset@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/srcset/-/srcset-2.0.1.tgz#8f842d357487eb797f413d9c309de7a5149df5ac" @@ -20752,11 +19681,6 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-template@~0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" - integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -20836,11 +19760,6 @@ string.prototype.trimright@^2.1.1: define-properties "^1.1.3" function-bind "^1.1.1" -string_decoder@0.10: - version "0.10.31" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -20909,18 +19828,6 @@ strip-bom@^4.0.0: resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== -strip-color@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz#106f65d3d3e6a2d9401cac0eb0ce8b8a702b4f7b" - integrity sha1-EG9l09PmotlAHKwOsM6LinArT3s= - -strip-dirs@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" - integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== - dependencies: - is-natural-number "^4.0.1" - strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -20960,13 +19867,6 @@ strip-json-comments@~2.0.1: resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -strip-outer@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" - integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== - dependencies: - escape-string-regexp "^1.0.2" - strong-log-transformer@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -21115,7 +20015,7 @@ svg-parser@^2.0.0, svg-parser@^2.0.2: resolved "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== -svgo@^1.0.0, svgo@^1.2.2, svgo@^1.3.2: +svgo@^1.0.0, svgo@^1.2.2: version "1.3.2" resolved "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== @@ -21261,7 +20161,7 @@ tar-fs@~2.0.1: pump "^3.0.0" tar-stream "^2.0.0" -tar-stream@^1.1.2, tar-stream@^1.5.2: +tar-stream@^1.1.2: version "1.6.2" resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== @@ -21315,14 +20215,6 @@ tarn@^3.0.0: resolved "https://registry.npmjs.org/tarn/-/tarn-3.0.0.tgz#a4082405216c0cce182b8b4cb2639c52c1e870d4" integrity sha512-PKUnlDFODZueoA8owLehl8vLcgtA8u4dRuVbZc92tspDYZixjJL6TqYOmryf/PfP/EBX+2rgNcrj96NO+RPkdQ== -tcp-port-used@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.1.tgz#46061078e2d38c73979a2c2c12b5a674e6689d70" - integrity sha512-rwi5xJeU6utXoEIiMvVBMc9eJ2/ofzB+7nLOdnZuFTmNCLqRiQh2sMG9MqCxHU/69VC/Fwp5dV9306Qd54ll1Q== - dependencies: - debug "4.1.0" - is2 "2.0.1" - tdigest@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz#2e3cb2c39ea449e55d1e6cd91117accca4588021" @@ -21375,14 +20267,6 @@ temp-write@^3.4.0: temp-dir "^1.0.0" uuid "^3.0.1" -tempfile@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz#6b0446856a9b1114d1856ffcbe509cccb0977265" - integrity sha1-awRGhWqbERTRhW/8vlCczLCXcmU= - dependencies: - temp-dir "^1.0.0" - uuid "^3.0.1" - term-size@^2.1.0: version "2.2.0" resolved "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753" @@ -21540,11 +20424,6 @@ timeago.js@^4.0.2: resolved "https://registry.npmjs.org/timeago.js/-/timeago.js-4.0.2.tgz#724e8c8833e3490676c7bb0a75f5daf20e558028" integrity sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w== -timed-out@^4.0.0, timed-out@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= - timers-browserify@^2.0.4: version "2.0.11" resolved "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" @@ -21575,18 +20454,6 @@ tiny-invariant@^1.0.2, tiny-invariant@^1.0.6: resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== -tiny-lr@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz#9fa547412f238fedb068ee295af8b682c98b2aab" - integrity sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA== - dependencies: - body "^5.1.0" - debug "^3.1.0" - faye-websocket "~0.10.0" - livereload-js "^2.3.0" - object-assign "^4.1.0" - qs "^6.4.0" - tiny-merge-patch@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/tiny-merge-patch/-/tiny-merge-patch-0.1.2.tgz#2e8ded19c56ea15dbd3ad4ed5db1c8e5ad544c3c" @@ -21683,11 +20550,6 @@ toidentifier@1.0.0: resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -toml@^2.3.2: - version "2.3.6" - resolved "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz#25b0866483a9722474895559088b436fd11f861b" - integrity sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ== - toposort@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" @@ -21751,13 +20613,6 @@ tree-kill@^1.2.2: resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== -tree-node-cli@^1.2.5: - version "1.4.0" - resolved "https://registry.npmjs.org/tree-node-cli/-/tree-node-cli-1.4.0.tgz#8f4028554610d6ee1cdeb98554a60841a3cfa3ac" - integrity sha512-hBc/cp7rTSHFSFvaTzmHNYyJv87UJBsxsfCoq2DtDQuMES4vhnLuvXZit/asGtZG8edWTCydWeFWoBz9LYkJdQ== - dependencies: - commander "^5.0.0" - trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -21773,13 +20628,6 @@ trim-off-newlines@^1.0.0: resolved "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -trim-repeated@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" - integrity sha1-42RqLqTokTEr9+rObPsFOAvAHCE= - dependencies: - escape-string-regexp "^1.0.2" - trim-trailing-lines@^1.0.0: version "1.1.3" resolved "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz#7f0739881ff76657b7776e10874128004b625a94" @@ -21800,14 +20648,6 @@ trough@^1.0.0: resolved "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== -truncate-html@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/truncate-html/-/truncate-html-1.0.3.tgz#0166dfc7890626130c2e4174c6b73d4d63993e5f" - integrity sha512-1o1prdRv+iehXcGwn29YgXU17DotHkr+OK3ijVEG7FGMwHNG9RyobXwimw6djDvbIc24rhmz3tjNNvNESjkNkQ== - dependencies: - "@types/cheerio" "^0.22.8" - cheerio "0.22.0" - tryer@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" @@ -22039,14 +20879,6 @@ umask@^1.1.0: resolved "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= -unbzip2-stream@^1.0.9: - version "1.4.3" - resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" - integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== - dependencies: - buffer "^5.2.1" - through "^2.3.8" - unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" @@ -22292,13 +21124,6 @@ url-loader@^4.1.0: mime-types "^2.1.26" schema-utils "^2.6.5" -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= - dependencies: - prepend-http "^1.0.1" - url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" @@ -22314,11 +21139,6 @@ url-parse@^1.4.3, url-parse@^1.4.7: querystringify "^2.1.1" requires-port "^1.0.0" -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= - url-value-parser@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/url-value-parser/-/url-value-parser-2.0.1.tgz#c8179a095ab9ec1f5aa17ca36af5af396b4e95ed" @@ -22929,11 +21749,6 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= - wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -23104,13 +21919,6 @@ xml-encryption@^1.0.0: xmldom "~0.1.15" xpath "0.0.27" -xml-js@^1.6.11: - version "1.6.11" - resolved "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" - integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== - dependencies: - sax "^1.2.4" - "xml-name-validator@>= 2.0.1 < 3.0.0": version "2.0.1" resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" @@ -23134,11 +21942,6 @@ xmlbuilder@^11.0.0, xmlbuilder@~11.0.0: resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== -xmlbuilder@^13.0.0: - version "13.0.2" - resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz#02ae33614b6a047d1c32b5389c1fdacb2bce47a7" - integrity sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ== - xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" @@ -23194,11 +21997,6 @@ yaeti@^0.0.6: resolved "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: version "3.1.1" resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -23219,14 +22017,6 @@ yaml@*, yaml@^1.10.0, yaml@^1.7.2, yaml@^1.9.2: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== -yamljs@^0.2.1: - version "0.2.10" - resolved "https://registry.npmjs.org/yamljs/-/yamljs-0.2.10.tgz#481cc7c25ca73af59f591f0c96e3ce56c757a40f" - integrity sha1-SBzHwlynOvWfWR8MluPOVsdXpA8= - dependencies: - argparse "^1.0.7" - glob "^7.0.5" - yargs-parser@18.x, yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" @@ -23316,13 +22106,6 @@ yargs@^15.3.1, yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-2.3.0.tgz#e900c87250ec5cd080db6009fe3dd63156f1d7fb" - integrity sha1-6QDIclDsXNCA22AJ/j3WMVbx1/s= - dependencies: - wordwrap "0.0.2" - yargs@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/yargs/-/yargs-5.0.0.tgz#3355144977d05757dbb86d6e38ec056123b3a66e" @@ -23343,7 +22126,7 @@ yargs@^5.0.0: y18n "^3.2.1" yargs-parser "^3.2.0" -yauzl@^2.10.0, yauzl@^2.4.2: +yauzl@^2.10.0: version "2.10.0" resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= From d26d5e7fc892c3ae79370a7e8aa58023e2b114d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Sat, 5 Sep 2020 12:20:14 +0200 Subject: [PATCH 311/359] Add issue template for RFCs (#2290) * Add issue template for RFCs * Update rfc_template.md * Update rfc_template.md --- .github/ISSUE_TEMPLATE/rfc_template.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/rfc_template.md diff --git a/.github/ISSUE_TEMPLATE/rfc_template.md b/.github/ISSUE_TEMPLATE/rfc_template.md new file mode 100644 index 0000000000..c4990ee5d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/rfc_template.md @@ -0,0 +1,26 @@ +--- +name: 'RFC' +about: 'Request For Comments (RFC) from the community' +labels: rfc +title: '[RFC] ' +--- + +**Status:** Open for comments + + + +## Need + + + +## Proposal + + + +## Alternatives + + + +## Risks + + From df9fc649fd7b3eab8f901cf7c8d1e6a5e50c13f2 Mon Sep 17 00:00:00 2001 From: Fabian Chong Date: Sat, 5 Sep 2020 19:20:24 +0800 Subject: [PATCH 312/359] ignore dist-types dir --- .eslintignore | 1 + packages/cli/config/eslint.backend.js | 2 +- packages/cli/config/eslint.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.eslintignore b/.eslintignore index 2f59b98bca..c73074efb3 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,6 @@ **/node_modules/** **/dist/** +**/dist-types/** **/storybook-static/** **/coverage/** **/build/** diff --git a/packages/cli/config/eslint.backend.js b/packages/cli/config/eslint.backend.js index 9b9efc7649..113ba55818 100644 --- a/packages/cli/config/eslint.backend.js +++ b/packages/cli/config/eslint.backend.js @@ -35,7 +35,7 @@ module.exports = { ecmaVersion: 2018, sourceType: 'module', }, - ignorePatterns: ['.eslintrc.js', '**/dist/**'], + ignorePatterns: ['.eslintrc.js', '**/dist/**', '**/dist-types/**'], rules: { 'no-console': 0, // Permitted in console programs 'new-cap': ['error', { capIsNew: false }], // Because Express constructs things e.g. like 'const r = express.Router()' diff --git a/packages/cli/config/eslint.js b/packages/cli/config/eslint.js index 792cd467d5..3e171d1f82 100644 --- a/packages/cli/config/eslint.js +++ b/packages/cli/config/eslint.js @@ -39,7 +39,7 @@ module.exports = { version: 'detect', }, }, - ignorePatterns: ['.eslintrc.js', '**/dist/**'], + ignorePatterns: ['.eslintrc.js', '**/dist/**', '**/dist-types/**'], rules: { 'import/newline-after-import': 'error', 'import/no-duplicates': 'warn', From 3fa419d7658dbf921d19a59af9cccc15f1253e4a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 5 Sep 2020 15:27:27 +0200 Subject: [PATCH 313/359] workflows: skip caching in microsite deployment --- .../microsite-with-storybook-deploy.yml | 30 ++++--------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/.github/workflows/microsite-with-storybook-deploy.yml b/.github/workflows/microsite-with-storybook-deploy.yml index a1e9723be0..22fd48789a 100644 --- a/.github/workflows/microsite-with-storybook-deploy.yml +++ b/.github/workflows/microsite-with-storybook-deploy.yml @@ -26,36 +26,18 @@ jobs: steps: - uses: actions/checkout@v2 - # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - name: cache all node_modules - id: cache-modules - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} - - name: find location of global yarn cache - id: yarn-cache - if: steps.cache-modules.outputs.cache-hit != 'true' - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v2 - if: steps.cache-modules.outputs.cache-hit != 'true' - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: yarn install - run: yarn install --frozen-lockfile - # End of yarn setup - # Previous install was to be able to build storybook, this is for the microsite, and we skip caching - - name: yarn install + # We avoid caching in this workflow, as we're running an install of both the top-level + # dependencies and the microsite. We leave it to the main master workflow to produce the + # cache, as that results in a smaller bundle. + - name: top-level yarn install + run: yarn install --frozen-lockfile + - name: microsite yarn install run: yarn install --frozen-lockfile working-directory: microsite From 9e07bdb43d7372c92201eef2e0a60cea7b1f520f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 5 Sep 2020 13:42:58 +0200 Subject: [PATCH 314/359] auth-backend: slim down oauth start interface and allow for state modifications --- .../auth-backend/src/lib/oauth/OAuthAdapter.ts | 17 +++++++---------- plugins/auth-backend/src/lib/oauth/index.ts | 2 ++ plugins/auth-backend/src/lib/oauth/types.ts | 10 ++++++---- .../src/providers/auth0/provider.ts | 15 +++++++-------- .../src/providers/github/provider.ts | 12 +++++++----- .../src/providers/gitlab/provider.ts | 12 +++++++----- .../src/providers/google/provider.ts | 15 +++++++-------- .../src/providers/microsoft/provider.ts | 12 +++++++----- .../src/providers/oauth2/provider.ts | 15 +++++++-------- .../auth-backend/src/providers/okta/provider.ts | 15 +++++++-------- 10 files changed, 64 insertions(+), 61 deletions(-) diff --git a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts index 0f092ae780..a0583cdd9e 100644 --- a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts +++ b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts @@ -24,9 +24,9 @@ import { } from '../../providers/types'; import { InputError } from '@backstage/backend-common'; import { TokenIssuer } from '../../identity'; -import { verifyNonce, encodeState } from './helpers'; +import { verifyNonce } from './helpers'; import { postMessageResponse, ensuresXRequestedWith } from '../flow'; -import { OAuthHandlers } from './types'; +import { OAuthHandlers, OAuthStartRequest } from './types'; export const THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000; export const TEN_MINUTES_MS = 600 * 1000; @@ -86,15 +86,12 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { // set a nonce cookie before redirecting to oauth provider this.setNonceCookie(res, nonce); - const stateObject = { nonce: nonce, env: env }; - const stateParameter = encodeState(stateObject); + const state = { nonce: nonce, env: env }; + const forwardReq = Object.assign(req, { scope, state }); - const queryParameters = { - scope, - state: stateParameter, - }; - - const { url, status } = await this.handlers.start(req, queryParameters); + const { url, status } = await this.handlers.start( + forwardReq as OAuthStartRequest, + ); res.statusCode = status || 302; res.setHeader('Location', url); diff --git a/plugins/auth-backend/src/lib/oauth/index.ts b/plugins/auth-backend/src/lib/oauth/index.ts index 05c8bd9d3d..20a1ca6f99 100644 --- a/plugins/auth-backend/src/lib/oauth/index.ts +++ b/plugins/auth-backend/src/lib/oauth/index.ts @@ -16,10 +16,12 @@ export { OAuthEnvironmentHandler } from './OAuthEnvironmentHandler'; export { OAuthAdapter } from './OAuthAdapter'; +export { encodeState } from './helpers'; export type { OAuthHandlers, OAuthProviderInfo, OAuthProviderOptions, OAuthResponse, OAuthState, + OAuthStartRequest, } from './types'; diff --git a/plugins/auth-backend/src/lib/oauth/types.ts b/plugins/auth-backend/src/lib/oauth/types.ts index a854326bed..f3a50e8596 100644 --- a/plugins/auth-backend/src/lib/oauth/types.ts +++ b/plugins/auth-backend/src/lib/oauth/types.ts @@ -67,6 +67,11 @@ export type OAuthState = { env: string; }; +export type OAuthStartRequest = express.Request<{}> & { + scope: string; + state: OAuthState; +}; + /** * Any OAuth provider needs to implement this interface which has provider specific * handlers for different methods to perform authentication, get access tokens, @@ -78,10 +83,7 @@ export interface OAuthHandlers { * @param {express.Request} req * @param options */ - start( - req: express.Request, - options: Record, - ): Promise; + start(req: OAuthStartRequest): Promise; /** * Handles the redirect from the auth provider when the user has signed in. diff --git a/plugins/auth-backend/src/providers/auth0/provider.ts b/plugins/auth-backend/src/providers/auth0/provider.ts index 3b7817ffe9..d71ee1bbed 100644 --- a/plugins/auth-backend/src/providers/auth0/provider.ts +++ b/plugins/auth-backend/src/providers/auth0/provider.ts @@ -23,6 +23,8 @@ import { OAuthHandlers, OAuthResponse, OAuthEnvironmentHandler, + OAuthStartRequest, + encodeState, } from '../../lib/oauth'; import { executeFetchUserProfileStrategy, @@ -81,16 +83,13 @@ export class Auth0AuthProvider implements OAuthHandlers { ); } - async start( - req: express.Request, - options: Record, - ): Promise { - const providerOptions = { - ...options, + async start(req: OAuthStartRequest): Promise { + return await executeRedirectStrategy(req, this._strategy, { accessType: 'offline', prompt: 'consent', - }; - return await executeRedirectStrategy(req, this._strategy, providerOptions); + scope: req.scope, + state: encodeState(req.state), + }); } async handler( diff --git a/plugins/auth-backend/src/providers/github/provider.ts b/plugins/auth-backend/src/providers/github/provider.ts index 2205fb6794..bab7b3fc28 100644 --- a/plugins/auth-backend/src/providers/github/provider.ts +++ b/plugins/auth-backend/src/providers/github/provider.ts @@ -29,6 +29,8 @@ import { OAuthHandlers, OAuthResponse, OAuthEnvironmentHandler, + OAuthStartRequest, + encodeState, } from '../../lib/oauth'; import passport from 'passport'; @@ -117,11 +119,11 @@ export class GithubAuthProvider implements OAuthHandlers { ); } - async start( - req: express.Request, - options: Record, - ): Promise { - return await executeRedirectStrategy(req, this._strategy, options); + async start(req: OAuthStartRequest): Promise { + return await executeRedirectStrategy(req, this._strategy, { + scope: req.scope, + state: encodeState(req.state), + }); } async handler(req: express.Request) { diff --git a/plugins/auth-backend/src/providers/gitlab/provider.ts b/plugins/auth-backend/src/providers/gitlab/provider.ts index 97f0b2bd22..4d4ecc3b56 100644 --- a/plugins/auth-backend/src/providers/gitlab/provider.ts +++ b/plugins/auth-backend/src/providers/gitlab/provider.ts @@ -29,6 +29,8 @@ import { OAuthHandlers, OAuthResponse, OAuthEnvironmentHandler, + OAuthStartRequest, + encodeState, } from '../../lib/oauth'; import passport from 'passport'; @@ -122,11 +124,11 @@ export class GitlabAuthProvider implements OAuthHandlers { ); } - async start( - req: express.Request, - options: Record, - ): Promise { - return await executeRedirectStrategy(req, this._strategy, options); + async start(req: OAuthStartRequest): Promise { + return await executeRedirectStrategy(req, this._strategy, { + scope: req.scope, + state: encodeState(req.state), + }); } async handler(req: express.Request): Promise<{ response: OAuthResponse }> { diff --git a/plugins/auth-backend/src/providers/google/provider.ts b/plugins/auth-backend/src/providers/google/provider.ts index 9ee2e1d680..2eb7b8573b 100644 --- a/plugins/auth-backend/src/providers/google/provider.ts +++ b/plugins/auth-backend/src/providers/google/provider.ts @@ -31,6 +31,8 @@ import { OAuthProviderOptions, OAuthResponse, OAuthEnvironmentHandler, + OAuthStartRequest, + encodeState, } from '../../lib/oauth'; import passport from 'passport'; @@ -79,16 +81,13 @@ export class GoogleAuthProvider implements OAuthHandlers { ); } - async start( - req: express.Request, - options: Record, - ): Promise { - const providerOptions = { - ...options, + async start(req: OAuthStartRequest): Promise { + return await executeRedirectStrategy(req, this._strategy, { accessType: 'offline', prompt: 'consent', - }; - return await executeRedirectStrategy(req, this._strategy, providerOptions); + scope: req.scope, + state: encodeState(req.state), + }); } async handler( diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index edc5509d84..2eba3b5191 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -35,6 +35,8 @@ import { OAuthHandlers, OAuthResponse, OAuthEnvironmentHandler, + OAuthStartRequest, + encodeState, } from '../../lib/oauth'; import got from 'got'; @@ -111,11 +113,11 @@ export class MicrosoftAuthProvider implements OAuthHandlers { ); } - async start( - req: express.Request, - options: Record, - ): Promise { - return await executeRedirectStrategy(req, this._strategy, options); + async start(req: OAuthStartRequest): Promise { + return await executeRedirectStrategy(req, this._strategy, { + scope: req.scope, + state: encodeState(req.state), + }); } async handler( diff --git a/plugins/auth-backend/src/providers/oauth2/provider.ts b/plugins/auth-backend/src/providers/oauth2/provider.ts index 5a4882fa6f..4a510f8c25 100644 --- a/plugins/auth-backend/src/providers/oauth2/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2/provider.ts @@ -23,6 +23,8 @@ import { OAuthHandlers, OAuthResponse, OAuthEnvironmentHandler, + OAuthStartRequest, + encodeState, } from '../../lib/oauth'; import { executeFetchUserProfileStrategy, @@ -84,16 +86,13 @@ export class OAuth2AuthProvider implements OAuthHandlers { ); } - async start( - req: express.Request, - options: Record, - ): Promise { - const providerOptions = { - ...options, + async start(req: OAuthStartRequest): Promise { + return await executeRedirectStrategy(req, this._strategy, { accessType: 'offline', prompt: 'consent', - }; - return await executeRedirectStrategy(req, this._strategy, providerOptions); + scope: req.scope, + state: encodeState(req.state), + }); } async handler( diff --git a/plugins/auth-backend/src/providers/okta/provider.ts b/plugins/auth-backend/src/providers/okta/provider.ts index 0368bd8415..0ac75beaf8 100644 --- a/plugins/auth-backend/src/providers/okta/provider.ts +++ b/plugins/auth-backend/src/providers/okta/provider.ts @@ -20,6 +20,8 @@ import { OAuthHandlers, OAuthResponse, OAuthEnvironmentHandler, + OAuthStartRequest, + encodeState, } from '../../lib/oauth'; import { Strategy as OktaStrategy } from 'passport-okta-oauth'; import passport from 'passport'; @@ -101,16 +103,13 @@ export class OktaAuthProvider implements OAuthHandlers { ); } - async start( - req: express.Request, - options: Record, - ): Promise { - const providerOptions = { - ...options, + async start(req: OAuthStartRequest): Promise { + return await executeRedirectStrategy(req, this._strategy, { accessType: 'offline', prompt: 'consent', - }; - return await executeRedirectStrategy(req, this._strategy, providerOptions); + scope: req.scope, + state: encodeState(req.state), + }); } async handler( From 40b71f8299bf0d63ea80d7a92b671be241d849d0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 5 Sep 2020 15:08:10 +0200 Subject: [PATCH 315/359] auth-backend: refactory oauth refresh handler to receive a single request object --- plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts | 8 ++++++-- plugins/auth-backend/src/lib/oauth/index.ts | 1 + plugins/auth-backend/src/lib/oauth/types.ts | 10 ++++++---- plugins/auth-backend/src/providers/auth0/provider.ts | 7 ++++--- plugins/auth-backend/src/providers/google/provider.ts | 7 ++++--- .../auth-backend/src/providers/microsoft/provider.ts | 7 ++++--- plugins/auth-backend/src/providers/oauth2/provider.ts | 7 ++++--- plugins/auth-backend/src/providers/okta/provider.ts | 7 ++++--- 8 files changed, 33 insertions(+), 21 deletions(-) diff --git a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts index a0583cdd9e..62cb8e444d 100644 --- a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts +++ b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts @@ -26,7 +26,7 @@ import { InputError } from '@backstage/backend-common'; import { TokenIssuer } from '../../identity'; import { verifyNonce } from './helpers'; import { postMessageResponse, ensuresXRequestedWith } from '../flow'; -import { OAuthHandlers, OAuthStartRequest } from './types'; +import { OAuthHandlers, OAuthStartRequest, OAuthRefreshRequest } from './types'; export const THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000; export const TEN_MINUTES_MS = 600 * 1000; @@ -182,8 +182,12 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { const scope = req.query.scope?.toString() ?? ''; + const forwardReq = Object.assign(req, { scope, refreshToken }); + // get new access_token - const response = await this.handlers.refresh(refreshToken, scope); + const response = await this.handlers.refresh( + forwardReq as OAuthRefreshRequest, + ); await this.populateIdentity(response.backstageIdentity); diff --git a/plugins/auth-backend/src/lib/oauth/index.ts b/plugins/auth-backend/src/lib/oauth/index.ts index 20a1ca6f99..564a0e1e7c 100644 --- a/plugins/auth-backend/src/lib/oauth/index.ts +++ b/plugins/auth-backend/src/lib/oauth/index.ts @@ -24,4 +24,5 @@ export type { OAuthResponse, OAuthState, OAuthStartRequest, + OAuthRefreshRequest, } from './types'; diff --git a/plugins/auth-backend/src/lib/oauth/types.ts b/plugins/auth-backend/src/lib/oauth/types.ts index f3a50e8596..b2b7915a4e 100644 --- a/plugins/auth-backend/src/lib/oauth/types.ts +++ b/plugins/auth-backend/src/lib/oauth/types.ts @@ -72,6 +72,11 @@ export type OAuthStartRequest = express.Request<{}> & { state: OAuthState; }; +export type OAuthRefreshRequest = express.Request<{}> & { + scope: string; + refreshToken: string; +}; + /** * Any OAuth provider needs to implement this interface which has provider specific * handlers for different methods to perform authentication, get access tokens, @@ -101,10 +106,7 @@ export interface OAuthHandlers { * @param {string} refreshToken * @param {string} scope */ - refresh?( - refreshToken: string, - scope: string, - ): Promise>; + refresh?(req: OAuthRefreshRequest): Promise>; /** * (Optional) Sign out of the auth provider. diff --git a/plugins/auth-backend/src/providers/auth0/provider.ts b/plugins/auth-backend/src/providers/auth0/provider.ts index d71ee1bbed..668ab17ee1 100644 --- a/plugins/auth-backend/src/providers/auth0/provider.ts +++ b/plugins/auth-backend/src/providers/auth0/provider.ts @@ -25,6 +25,7 @@ import { OAuthEnvironmentHandler, OAuthStartRequest, encodeState, + OAuthRefreshRequest, } from '../../lib/oauth'; import { executeFetchUserProfileStrategy, @@ -106,11 +107,11 @@ export class Auth0AuthProvider implements OAuthHandlers { }; } - async refresh(refreshToken: string, scope: string): Promise { + async refresh(req: OAuthRefreshRequest): Promise { const { accessToken, params } = await executeRefreshTokenStrategy( this._strategy, - refreshToken, - scope, + req.refreshToken, + req.scope, ); const profile = await executeFetchUserProfileStrategy( diff --git a/plugins/auth-backend/src/providers/google/provider.ts b/plugins/auth-backend/src/providers/google/provider.ts index 2eb7b8573b..3cc585605c 100644 --- a/plugins/auth-backend/src/providers/google/provider.ts +++ b/plugins/auth-backend/src/providers/google/provider.ts @@ -33,6 +33,7 @@ import { OAuthEnvironmentHandler, OAuthStartRequest, encodeState, + OAuthRefreshRequest, } from '../../lib/oauth'; import passport from 'passport'; @@ -104,11 +105,11 @@ export class GoogleAuthProvider implements OAuthHandlers { }; } - async refresh(refreshToken: string, scope: string): Promise { + async refresh(req: OAuthRefreshRequest): Promise { const { accessToken, params } = await executeRefreshTokenStrategy( this._strategy, - refreshToken, - scope, + req.refreshToken, + req.scope, ); const profile = await executeFetchUserProfileStrategy( diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index 2eba3b5191..baa66f0662 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -37,6 +37,7 @@ import { OAuthEnvironmentHandler, OAuthStartRequest, encodeState, + OAuthRefreshRequest, } from '../../lib/oauth'; import got from 'got'; @@ -134,11 +135,11 @@ export class MicrosoftAuthProvider implements OAuthHandlers { }; } - async refresh(refreshToken: string, scope: string): Promise { + async refresh(req: OAuthRefreshRequest): Promise { const { accessToken, params } = await executeRefreshTokenStrategy( this._strategy, - refreshToken, - scope, + req.refreshToken, + req.scope, ); const profile = await executeFetchUserProfileStrategy( diff --git a/plugins/auth-backend/src/providers/oauth2/provider.ts b/plugins/auth-backend/src/providers/oauth2/provider.ts index 4a510f8c25..a657ea2195 100644 --- a/plugins/auth-backend/src/providers/oauth2/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2/provider.ts @@ -25,6 +25,7 @@ import { OAuthEnvironmentHandler, OAuthStartRequest, encodeState, + OAuthRefreshRequest, } from '../../lib/oauth'; import { executeFetchUserProfileStrategy, @@ -109,11 +110,11 @@ export class OAuth2AuthProvider implements OAuthHandlers { }; } - async refresh(refreshToken: string, scope: string): Promise { + async refresh(req: OAuthRefreshRequest): Promise { const refreshTokenResponse = await executeRefreshTokenStrategy( this._strategy, - refreshToken, - scope, + req.refreshToken, + req.scope, ); const { accessToken, diff --git a/plugins/auth-backend/src/providers/okta/provider.ts b/plugins/auth-backend/src/providers/okta/provider.ts index 0ac75beaf8..09597696ba 100644 --- a/plugins/auth-backend/src/providers/okta/provider.ts +++ b/plugins/auth-backend/src/providers/okta/provider.ts @@ -22,6 +22,7 @@ import { OAuthEnvironmentHandler, OAuthStartRequest, encodeState, + OAuthRefreshRequest, } from '../../lib/oauth'; import { Strategy as OktaStrategy } from 'passport-okta-oauth'; import passport from 'passport'; @@ -126,11 +127,11 @@ export class OktaAuthProvider implements OAuthHandlers { }; } - async refresh(refreshToken: string, scope: string): Promise { + async refresh(req: OAuthRefreshRequest): Promise { const { accessToken, params } = await executeRefreshTokenStrategy( this._strategy, - refreshToken, - scope, + req.refreshToken, + req.scope, ); const profile = await executeFetchUserProfileStrategy( From 7a7396be5594896a77633326c06a368c2dee5cb7 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 5 Sep 2020 15:53:57 +0200 Subject: [PATCH 316/359] add prettier scripts and .prettierignore + workflow check --- .github/workflows/ci.yml | 3 +++ .prettierignore | 7 +++++++ package.json | 1 + 3 files changed, 11 insertions(+) create mode 100644 .prettierignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 492b008431..9ea1308336 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,9 @@ jobs: - name: verify doc links run: node docs/verify-links.js + - name: prettier + run: yarn prettier:check + - name: lint run: yarn lerna -- run lint --since origin/master diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..ef75947604 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +.yarn +dist +microsite/build +coverage +*.hbs +templates +plugins/scaffolder-backend/sample-templates diff --git a/package.json b/package.json index 5645d913eb..bf2dfc3246 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "create-plugin": "backstage-cli create-plugin", "remove-plugin": "backstage-cli remove-plugin", "release": "if [ \"$(git symbolic-ref --short HEAD)\" = master ]; then echo \"don't try to release master\"; exit 1; else lerna version --no-push --force-publish; fi", + "prettier:check": "prettier --check .", "lerna": "lerna", "storybook": "yarn workspace storybook start", "build-storybook": "yarn workspace storybook build-storybook" From b045b166b62bc75764f128b07f986f235cccc80c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 5 Sep 2020 16:07:04 +0200 Subject: [PATCH 317/359] run prettier --- .github/PULL_REQUEST_TEMPLATE.md | 2 + .github/workflows/master-win.yml | 6 +-- .github/workflows/master.yml | 6 +-- CODE_OF_CONDUCT.md | 31 +++++++------ app-config.yaml | 4 +- docs/api/utility-apis.md | 5 +-- docs/dls/design.md | 4 +- .../extending/create-your-own-publisher.md | 12 ++--- docs/features/software-templates/index.md | 2 +- .../techdocs/creating-and-publishing.md | 6 +-- docs/getting-started/index.md | 3 +- install/kubernetes/app.yaml | 14 +++--- install/kubernetes/backend.yaml | 14 +++--- install/kubernetes/backstage/Chart.yaml | 2 +- install/kubernetes/backstage/values.yaml | 44 +++++++++++-------- install/kubernetes/ingress.yaml | 22 +++++----- install/kubernetes/service.yaml | 16 +++---- .../data/plugins/github-pull-requests.yaml | 1 - microsite/data/plugins/rollbar.yaml | 1 - microsite/data/plugins/travis-ci.yaml | 1 - microsite/sidebars.json | 6 ++- .../remove-plugin/removePlugin.test.ts | 2 +- .../commands/remove-plugin/removePlugin.ts | 4 +- .../auth/okta/OktaAuth.test.ts | 9 ++-- .../apis/implementations/auth/okta/index.ts | 2 +- .../SimpleStepper/SimpleStepper.stories.tsx | 2 +- .../src/layout/Sidebar/Sidebar.stories.tsx | 20 ++++----- .../techdocs-container/mock-docs/mkdocs.yml | 2 +- .../mock-docs/sub-docs/mkdocs.yml | 2 +- plugins/auth-backend/README.md | 4 +- .../auth-backend/src/providers/okta/index.ts | 2 +- .../src/providers/okta/types.d.ts | 4 +- plugins/explore/README.md | 1 + .../explore/src/components/ExploreCard.tsx | 2 +- plugins/gcp-projects/dev/index.tsx | 4 +- .../GraphiQLBrowser/GraphiQLBrowser.tsx | 2 +- .../components/AuditList/AuditListTable.tsx | 12 ++--- .../lighthouse/src/components/Intro/index.tsx | 2 +- plugins/scaffolder-backend/README.md | 1 + plugins/scaffolder-backend/src/index.ts | 1 - plugins/techdocs/src/api.ts | 8 +++- plugins/welcome/README.md | 1 + 42 files changed, 150 insertions(+), 139 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 57746da2e8..0437698301 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,7 +4,9 @@ That makes it easier to understand the change so we can :shipit: faster. --> #### :heavy_check_mark: Checklist + + - [ ] All tests are passing `yarn test` - [ ] Screenshots attached (for UI changes) - [ ] Relevant documentation updated diff --git a/.github/workflows/master-win.yml b/.github/workflows/master-win.yml index 296a7511bc..9fdff4af32 100644 --- a/.github/workflows/master-win.yml +++ b/.github/workflows/master-win.yml @@ -38,8 +38,6 @@ jobs: - name: yarn install run: yarn install --frozen-lockfile # End of yarn setup - - # Tests are broken on Windows, disabled for now - # - name: test - # run: yarn lerna -- run test +# - name: test +# run: yarn lerna -- run test diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 354838037e..b5c4fdf370 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -71,9 +71,9 @@ jobs: # Tags the commit with the version in the core package if the tag doesn't exist - uses: Klemensas/action-autotag@1.2.3 with: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - package_root: "packages/core" - tag_prefix: "v" + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + package_root: 'packages/core' + tag_prefix: 'v' - name: Discord notification if: ${{ failure() }} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 69f6457dd9..55269dd2a5 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -4,12 +4,12 @@ This code of conduct outlines our expectations for participants within the **Spo Our open source community strives to: -* **Be friendly and patient.** -* **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. -* **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language. -* **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one. -* **Be careful in the words that we choose**: we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable. -* **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes. +- **Be friendly and patient.** +- **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. +- **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language. +- **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one. +- **Be careful in the words that we choose**: we are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable. +- **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes. ## Definitions @@ -18,7 +18,7 @@ Harassment includes, but is not limited to: - Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, neuro(a)typicality, physical appearance, body size, race, age, regional discrimination, political or religious affiliation - Unwelcome comments regarding a person’s lifestyle choices and practices, including those related to food, health, parenting, drugs, and employment - Deliberate misgendering. This includes deadnaming or persistently using a pronoun that does not correctly reflect a person's gender identity. You must address people by the name they give you when not addressing them by their username or handle -- Physical contact and simulated physical contact (eg, textual descriptions like “*hug*” or “*backrub*”) without consent or after a request to stop +- Physical contact and simulated physical contact (eg, textual descriptions like “_hug_” or “_backrub_”) without consent or after a request to stop - Threats of violence, both physical and psychological - Incitement of violence towards any individual, including encouraging a person to commit suicide or to engage in self-harm - Deliberate intimidation @@ -39,7 +39,6 @@ Our open source community prioritizes marginalized people’s safety over privil - Communicating in a ‘tone’ you don’t find congenial - Criticizing racist, sexist, cissexist, or otherwise oppressive behavior or assumptions - ### Diversity Statement We encourage everyone to participate and are committed to building a community for all. Although we will fail at times, we seek to treat everyone both as fairly and equally as possible. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong. @@ -53,18 +52,18 @@ If you experience or witness unacceptable behavior—or have any other concerns - Your contact information. - Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please -include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link. + include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public IRC logger), please include a link. - Any additional information that may be helpful. After filing a report, a representative will contact you personally, review the incident, follow up with any additional questions, and make a decision as to how to respond. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. If the complaint originates from a member of the response team, it will be handled by a different member of the response team. We will respect confidentiality requests for the purpose of protecting victims of abuse. ### Attribution & Acknowledgements -We all stand on the shoulders of giants across many open source communities. We'd like to thank the communities and projects that established code of conducts and diversity statements as our inspiration: +We all stand on the shoulders of giants across many open source communities. We'd like to thank the communities and projects that established code of conducts and diversity statements as our inspiration: -* [Django](https://www.djangoproject.com/conduct/reporting/) -* [Python](https://www.python.org/community/diversity/) -* [Ubuntu](http://www.ubuntu.com/about/about-ubuntu/conduct) -* [Contributor Covenant](http://contributor-covenant.org/) -* [Geek Feminism](http://geekfeminism.org/about/code-of-conduct/) -* [Citizen Code of Conduct](http://citizencodeofconduct.org/) +- [Django](https://www.djangoproject.com/conduct/reporting/) +- [Python](https://www.python.org/community/diversity/) +- [Ubuntu](http://www.ubuntu.com/about/about-ubuntu/conduct) +- [Contributor Covenant](http://contributor-covenant.org/) +- [Geek Feminism](http://geekfeminism.org/about/code-of-conduct/) +- [Citizen Code of Conduct](http://citizencodeofconduct.org/) diff --git a/app-config.yaml b/app-config.yaml index 957d17990a..2620f6b9b7 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -121,8 +121,8 @@ auth: $secret: env: GITLAB_BASE_URL saml: - entryPoint: "http://localhost:7001/" - issuer: "passport-saml" + entryPoint: 'http://localhost:7001/' + issuer: 'passport-saml' okta: development: clientId: diff --git a/docs/api/utility-apis.md b/docs/api/utility-apis.md index b4c2fd161e..0e09da3f83 100644 --- a/docs/api/utility-apis.md +++ b/docs/api/utility-apis.md @@ -71,10 +71,9 @@ import { AlertApiForwarder, ErrorApiForwarder, ErrorAlerter, - ConfigApi + ConfigApi, } from '@backstage/core'; - const apis = (config: ConfigApi) => { const builder = ApiRegistry.builder(); @@ -84,7 +83,7 @@ const apis = (config: ConfigApi) => { // The error API uses the alert API to send error notifications to the user. builder.add(errorApiRef, new ErrorAlerter(alertApi, new ErrorApiForwarder())); return builder.build(); -} +}; const app = createApp({ apis, diff --git a/docs/dls/design.md b/docs/dls/design.md index 23ba84024a..b434fc8791 100644 --- a/docs/dls/design.md +++ b/docs/dls/design.md @@ -116,8 +116,8 @@ components. If you’d like to help build up our design system, you can also add components we’ve designed to the Storybook as well. **[Figma](https://www.figma.com/@backstage)** - we're stoked to be using Figma -Community to share our design assets. You can duplicate our UI Kit -and design your own plugin for Backstage. +Community to share our design assets. You can duplicate our UI Kit and design +your own plugin for Backstage. **[Discord](https://discord.gg/EBHEGzX)** - all design questions should be directed to the _#design_ channel. diff --git a/docs/features/software-templates/extending/create-your-own-publisher.md b/docs/features/software-templates/extending/create-your-own-publisher.md index 391f6f0658..111125b49f 100644 --- a/docs/features/software-templates/extending/create-your-own-publisher.md +++ b/docs/features/software-templates/extending/create-your-own-publisher.md @@ -7,18 +7,18 @@ Publishers are responsible for pushing and storing the templated skeleton after the values have been templated by the `Templater`. See [Create your own templater](./create-your-own-templater.md) for more info. -They receive a directory or location where the templater has sucessfully run -and is now ready to store somewhere. They also are given some other options -which are sent from the frontend, such as the `storePath` which is a string of -where the frontend thinks we should save this templated folder. +They receive a directory or location where the templater has sucessfully run and +is now ready to store somewhere. They also are given some other options which +are sent from the frontend, such as the `storePath` which is a string of where +the frontend thinks we should save this templated folder. Currently we provide the following `publishers`: - `github` This publisher is passed through to the `createRouter` function of the -`@spotify/plugin-scaffolder-backend`. Currently, only one publisher is supported, -but PR's are always welcome. +`@spotify/plugin-scaffolder-backend`. Currently, only one publisher is +supported, but PR's are always welcome. An full example backend can be found [here](https://github.com/spotify/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts), diff --git a/docs/features/software-templates/index.md b/docs/features/software-templates/index.md index 1c67676b3e..b594cfce2a 100644 --- a/docs/features/software-templates/index.md +++ b/docs/features/software-templates/index.md @@ -4,7 +4,7 @@ title: Software Templates --- The Software Templates part of Backstage is a tool that can help you create -Components inside Backstage. By default, it has the ability to load skeletons of +Components inside Backstage. By default, it has the ability to load skeletons of code, template in some variables, and then publish the template to some location like GitHub. diff --git a/docs/features/techdocs/creating-and-publishing.md b/docs/features/techdocs/creating-and-publishing.md index 037c4be172..4a37b6bca5 100644 --- a/docs/features/techdocs/creating-and-publishing.md +++ b/docs/features/techdocs/creating-and-publishing.md @@ -30,9 +30,9 @@ the documentation template. Create an entity from the documentation template and you will get the needed setup for free. -!!! warning Currently the Backstage Software Templates are limited to create repositories -inside GitHub organizations. You also need to generate an personal access token -and use as an environment variable. Read more about this +!!! warning Currently the Backstage Software Templates are limited to create +repositories inside GitHub organizations. You also need to generate an personal +access token and use as an environment variable. Read more about this [here](../software-templates/installation.md#runtime-dependencies). ### Manually add documentation setup to already existing repository diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index abe7776371..c1c6ea9231 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -4,7 +4,8 @@ title: Running Backstage Locally --- First make sure you are using NodeJS with an Active LTS Release, currently v12. -This is made easy with a version manager such as nvm which allows for version switching. +This is made easy with a version manager such as nvm which allows for version +switching. ```bash # Checking your version diff --git a/install/kubernetes/app.yaml b/install/kubernetes/app.yaml index ce284afd31..c54e25d519 100644 --- a/install/kubernetes/app.yaml +++ b/install/kubernetes/app.yaml @@ -18,10 +18,10 @@ spec: component: frontend spec: containers: - - name: app - image: spotify/backstage:latest - imagePullPolicy: IfNotPresent - ports: - - containerPort: 80 - name: app - protocol: TCP + - name: app + image: spotify/backstage:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + name: app + protocol: TCP diff --git a/install/kubernetes/backend.yaml b/install/kubernetes/backend.yaml index bc82731958..f0695753fd 100644 --- a/install/kubernetes/backend.yaml +++ b/install/kubernetes/backend.yaml @@ -18,10 +18,10 @@ spec: component: backend spec: containers: - - name: backend - image: spotify/backstage-backend:latest - imagePullPolicy: IfNotPresent - ports: - - containerPort: 7000 - name: backend - protocol: TCP + - name: backend + image: spotify/backstage-backend:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 7000 + name: backend + protocol: TCP diff --git a/install/kubernetes/backstage/Chart.yaml b/install/kubernetes/backstage/Chart.yaml index 23ea41ec41..efc3635a7f 100644 --- a/install/kubernetes/backstage/Chart.yaml +++ b/install/kubernetes/backstage/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 -appVersion: "1.0" +appVersion: '1.0' description: A Helm chart for Spotify Backstage name: backstage version: 0.1.1-alpha.12 diff --git a/install/kubernetes/backstage/values.yaml b/install/kubernetes/backstage/values.yaml index fd4dd66b53..d91808ed28 100644 --- a/install/kubernetes/backstage/values.yaml +++ b/install/kubernetes/backstage/values.yaml @@ -1,12 +1,12 @@ app: enabled: true - nameOverride: "" - fullnameOverride: "" + nameOverride: '' + fullnameOverride: '' replicaCount: 1 serviceAccount: create: false - Name: "" - image: + Name: '' + image: repository: spotify/backstage tag: latest pullPolicy: Always @@ -15,20 +15,23 @@ app: port: 80 ingress: enabled: false - annotations: {} + annotations: + {} # kubernetes.io/ingress.class: "nginx" hosts: - - host: backstage.local - paths: - - / + - host: backstage.local + paths: + - / tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local imagePullSecrets: [] - podSecurityContext: {} + podSecurityContext: + {} # fsGroup: 2000 - securityContext: {} + securityContext: + {} # capabilities: # drop: # - ALL @@ -48,12 +51,12 @@ app: backend: enabled: false - nameOverride: "" - fullnameOverride: "" + nameOverride: '' + fullnameOverride: '' replicaCount: 1 serviceAccount: create: false - Name: "" + Name: '' image: repository: spotify/backstage-backend tag: latest @@ -63,20 +66,23 @@ backend: port: 7000 ingress: enabled: false - annotations: {} + annotations: + {} # kubernetes.io/ingress.class: "nginx" hosts: - - host: backstage.local - paths: - - /backend + - host: backstage.local + paths: + - /backend tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local imagePullSecrets: [] - podSecurityContext: {} + podSecurityContext: + {} # fsGroup: 2000 - securityContext: {} + securityContext: + {} # capabilities: # drop: # - ALL diff --git a/install/kubernetes/ingress.yaml b/install/kubernetes/ingress.yaml index d2ecae74c6..f93a814a0c 100644 --- a/install/kubernetes/ingress.yaml +++ b/install/kubernetes/ingress.yaml @@ -7,14 +7,14 @@ metadata: component: ingress spec: rules: - - host: - http: - paths: - - backend: - serviceName: backstage - servicePort: frontend - path: / - - backend: - serviceName: backstage-backend - servicePort: backend - path: /backend + - host: + http: + paths: + - backend: + serviceName: backstage + servicePort: frontend + path: / + - backend: + serviceName: backstage-backend + servicePort: backend + path: /backend diff --git a/install/kubernetes/service.yaml b/install/kubernetes/service.yaml index bbbc3d6e17..4d947b7afc 100644 --- a/install/kubernetes/service.yaml +++ b/install/kubernetes/service.yaml @@ -11,10 +11,10 @@ spec: app: backstage component: frontend ports: - - name: frontend - port: 80 - protocol: TCP - targetPort: app + - name: frontend + port: 80 + protocol: TCP + targetPort: app --- apiVersion: v1 kind: Service @@ -29,7 +29,7 @@ spec: app: backstage component: backend ports: - - name: backend - port: 7000 - protocol: TCP - targetPort: backend + - name: backend + port: 7000 + protocol: TCP + targetPort: backend diff --git a/microsite/data/plugins/github-pull-requests.yaml b/microsite/data/plugins/github-pull-requests.yaml index 917198a221..6479452c8e 100644 --- a/microsite/data/plugins/github-pull-requests.yaml +++ b/microsite/data/plugins/github-pull-requests.yaml @@ -7,4 +7,3 @@ description: View GitHub pull requests for your service in Backstage. documentation: https://roadie.io/backstage/plugins/github-pull-requests iconUrl: https://roadie.io/static/7f13bb8d861d8dedc5112fb939d215f9/351f2/GitHub-Mark-Light-120px-plus.png npmPackageName: '@roadiehq/backstage-plugin-github-pull-requests' - diff --git a/microsite/data/plugins/rollbar.yaml b/microsite/data/plugins/rollbar.yaml index 0118fab391..8ef3d8d66a 100644 --- a/microsite/data/plugins/rollbar.yaml +++ b/microsite/data/plugins/rollbar.yaml @@ -7,4 +7,3 @@ description: View Rollbar errors for your services in Backstage. documentation: https://github.com/spotify/backstage/tree/master/plugins/rollbar iconUrl: https://rollbar.com/assets/media/rollbar-mark-color.png npmPackageName: '@backstage/plugin-rollbar' - diff --git a/microsite/data/plugins/travis-ci.yaml b/microsite/data/plugins/travis-ci.yaml index 48c0a4cb86..520b884c20 100644 --- a/microsite/data/plugins/travis-ci.yaml +++ b/microsite/data/plugins/travis-ci.yaml @@ -7,4 +7,3 @@ description: View Travis CI builds for your service in Backstage. documentation: https://roadie.io/backstage/plugins/travis-ci iconUrl: https://roadie.io/static/af2941eaf0af675facb281d566f42e14/45f2b/travis-ci-mascot-200x200.png npmPackageName: '@roadiehq/backstage-plugin-travis-ci' - diff --git a/microsite/sidebars.json b/microsite/sidebars.json index f5741bd355..d31c14fd1d 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -92,7 +92,11 @@ { "type": "subcategory", "label": "Publishing", - "ids": ["plugins/publishing", "plugins/publish-private", "plugins/add-to-marketplace"] + "ids": [ + "plugins/publishing", + "plugins/publish-private", + "plugins/add-to-marketplace" + ] } ], "Configuration": [ diff --git a/packages/cli/src/commands/remove-plugin/removePlugin.test.ts b/packages/cli/src/commands/remove-plugin/removePlugin.test.ts index 465260008d..72fbc6f084 100644 --- a/packages/cli/src/commands/remove-plugin/removePlugin.test.ts +++ b/packages/cli/src/commands/remove-plugin/removePlugin.test.ts @@ -66,7 +66,7 @@ const createTestPluginFile = async ( fse.copyFileSync(pluginsFilePath, testFilePath); const pluginNameCapitalized = testPluginName .split('-') - .map((name) => capitalize(name)) + .map(name => capitalize(name)) .join(''); const exportStatement = `export { default as ${pluginNameCapitalized}} from @backstage/plugin-${testPluginName}`; addExportStatement(testFilePath, exportStatement); diff --git a/packages/cli/src/commands/remove-plugin/removePlugin.ts b/packages/cli/src/commands/remove-plugin/removePlugin.ts index b9bf1c4af3..1395384022 100644 --- a/packages/cli/src/commands/remove-plugin/removePlugin.ts +++ b/packages/cli/src/commands/remove-plugin/removePlugin.ts @@ -84,7 +84,7 @@ const removeAllStatementsContainingID = async (file: string, ID: string) => { const originalContent = await fse.readFile(file, 'utf8'); const contentAfterRemoval = originalContent .split('\n') - .filter((statement) => !statement.includes(`${ID}`)) // get rid of lines with pluginName + .filter(statement => !statement.includes(`${ID}`)) // get rid of lines with pluginName .join('\n'); if (originalContent !== contentAfterRemoval) { await fse.writeFile(file, contentAfterRemoval, 'utf8'); @@ -100,7 +100,7 @@ export const removeReferencesFromPluginsFile = async ( ) => { const pluginNameCapitalized = pluginName .split('-') - .map((name) => capitalize(name)) + .map(name => capitalize(name)) .join(''); await Task.forItem('removing', 'export references', async () => { diff --git a/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.test.ts b/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.test.ts index ab6c46c9b4..40e6f78909 100644 --- a/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.test.ts +++ b/packages/core-api/src/apis/implementations/auth/okta/OktaAuth.test.ts @@ -23,7 +23,7 @@ const PREFIX = 'okta.'; describe('OktaAuth', () => { it('should get refreshed access token', async () => { const getSession = jest.fn().mockResolvedValue({ - providerInfo: { accessToken: 'access-token', expiresAt: theFuture }, + providerInfo: { accessToken: 'access-token', expiresAt: theFuture }, }); const oktaAuth = new OktaAuth({ getSession } as any); @@ -116,7 +116,10 @@ describe('OktaAuth', () => { ['profile email', ['profile', 'email']], [`${PREFIX}groups.manage`, [`${PREFIX}groups.manage`]], ['groups.read', [`${PREFIX}groups.read`]], - [`${PREFIX}groups.manage groups.read, openid`, [`${PREFIX}groups.manage`, `${PREFIX}groups.read`, 'openid']], + [ + `${PREFIX}groups.manage groups.read, openid`, + [`${PREFIX}groups.manage`, `${PREFIX}groups.read`, 'openid'], + ], [`email\t ${PREFIX}groups.read`, ['email', `${PREFIX}groups.read`]], // Some incorrect scopes that we don't try to fix @@ -126,4 +129,4 @@ describe('OktaAuth', () => { ])(`should normalize scopes correctly - %p`, (scope, scopes) => { expect(OktaAuth.normalizeScopes(scope)).toEqual(new Set(scopes)); }); -}); +}); diff --git a/packages/core-api/src/apis/implementations/auth/okta/index.ts b/packages/core-api/src/apis/implementations/auth/okta/index.ts index 2bef0ce0db..5d6d382c45 100644 --- a/packages/core-api/src/apis/implementations/auth/okta/index.ts +++ b/packages/core-api/src/apis/implementations/auth/okta/index.ts @@ -15,4 +15,4 @@ */ export * from './types'; -export { default as OktaAuth } from './OktaAuth'; +export { default as OktaAuth } from './OktaAuth'; diff --git a/packages/core/src/components/SimpleStepper/SimpleStepper.stories.tsx b/packages/core/src/components/SimpleStepper/SimpleStepper.stories.tsx index 81f558f49d..52710ffeaf 100644 --- a/packages/core/src/components/SimpleStepper/SimpleStepper.stories.tsx +++ b/packages/core/src/components/SimpleStepper/SimpleStepper.stories.tsx @@ -51,7 +51,7 @@ export const ConditionalButtons = () => { setRequired(!!e.target.value)} + onChange={e => setRequired(!!e.target.value)} /> diff --git a/packages/core/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core/src/layout/Sidebar/Sidebar.stories.tsx index b286071426..d7451965fe 100644 --- a/packages/core/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core/src/layout/Sidebar/Sidebar.stories.tsx @@ -29,9 +29,7 @@ import HomeOutlinedIcon from '@material-ui/icons/HomeOutlined'; import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; import Star from '@material-ui/icons/Star'; import { MemoryRouter } from 'react-router-dom'; -import { - githubAuthApiRef, -} from '@backstage/core-api'; +import { githubAuthApiRef } from '@backstage/core-api'; export default { title: 'Sidebar', @@ -60,12 +58,14 @@ export const SampleSidebar = () => ( - - } /> + + } + /> ); diff --git a/packages/techdocs-container/mock-docs/mkdocs.yml b/packages/techdocs-container/mock-docs/mkdocs.yml index 8639c6abef..d769fddfcf 100644 --- a/packages/techdocs-container/mock-docs/mkdocs.yml +++ b/packages/techdocs-container/mock-docs/mkdocs.yml @@ -1,7 +1,7 @@ site_name: 'mock-docs' site_description: 'mock-docs site description' -nav: +nav: - Home: index.md - SubDocs: '!include ./sub-docs/mkdocs.yml' diff --git a/packages/techdocs-container/mock-docs/sub-docs/mkdocs.yml b/packages/techdocs-container/mock-docs/sub-docs/mkdocs.yml index 09504c1b31..4490ddbefa 100644 --- a/packages/techdocs-container/mock-docs/sub-docs/mkdocs.yml +++ b/packages/techdocs-container/mock-docs/sub-docs/mkdocs.yml @@ -1,4 +1,4 @@ site_name: subdocs nav: - - Home 2: "index.md" + - Home 2: 'index.md' diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index e1d144c1e0..9af90df3f2 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -85,8 +85,8 @@ Click [here](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMe - Give the app a name. e.g. `backstage-dev` - Select `Accounts in this organizational directory only` under supported account types. - Enter the callback URL for your backstage backend instance: - - For local development, this is likely `http://localhost:7000/auth/microsoft/handler/frame` - - For non-local deployments, this will be `https://{APP_FQDN}:{APP_BACKEND_PORT}/auth/microsoft/handler/frame` + - For local development, this is likely `http://localhost:7000/auth/microsoft/handler/frame` + - For non-local deployments, this will be `https://{APP_FQDN}:{APP_BACKEND_PORT}/auth/microsoft/handler/frame` - Click `Register`. We also need to generate a client secret so Backstage can authenticate as this app. diff --git a/plugins/auth-backend/src/providers/okta/index.ts b/plugins/auth-backend/src/providers/okta/index.ts index bc32601ac2..05cc398f43 100644 --- a/plugins/auth-backend/src/providers/okta/index.ts +++ b/plugins/auth-backend/src/providers/okta/index.ts @@ -13,4 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { createOktaProvider } from './provider'; +export { createOktaProvider } from './provider'; diff --git a/plugins/auth-backend/src/providers/okta/types.d.ts b/plugins/auth-backend/src/providers/okta/types.d.ts index bed6d24043..6b49d99817 100644 --- a/plugins/auth-backend/src/providers/okta/types.d.ts +++ b/plugins/auth-backend/src/providers/okta/types.d.ts @@ -14,9 +14,7 @@ * limitations under the License. */ declare module 'passport-okta-oauth' { - export class Strategy { - constructor(options: any, verify: any) + constructor(options: any, verify: any); } } - \ No newline at end of file diff --git a/plugins/explore/README.md b/plugins/explore/README.md index 12a4e58380..d5453e594c 100644 --- a/plugins/explore/README.md +++ b/plugins/explore/README.md @@ -1,4 +1,5 @@ # Title + Welcome to the explore plugin! ## Sub-section 1 diff --git a/plugins/explore/src/components/ExploreCard.tsx b/plugins/explore/src/components/ExploreCard.tsx index af6eca61b7..9c61e9bea8 100644 --- a/plugins/explore/src/components/ExploreCard.tsx +++ b/plugins/explore/src/components/ExploreCard.tsx @@ -28,7 +28,7 @@ import { } from '@material-ui/core'; import { BackstageTheme } from '@backstage/theme'; -const useStyles = makeStyles((theme) => ({ +const useStyles = makeStyles(theme => ({ card: { display: 'flex', flexDirection: 'column', diff --git a/plugins/gcp-projects/dev/index.tsx b/plugins/gcp-projects/dev/index.tsx index d97643057b..812a5585d4 100644 --- a/plugins/gcp-projects/dev/index.tsx +++ b/plugins/gcp-projects/dev/index.tsx @@ -17,6 +17,4 @@ import { createDevApp } from '@backstage/dev-utils'; import { plugin } from '../src/plugin'; -createDevApp() - .registerPlugin(plugin) - .render(); +createDevApp().registerPlugin(plugin).render(); diff --git a/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx index b41fc04b9e..ce56017984 100644 --- a/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx +++ b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx @@ -24,7 +24,7 @@ import { BackstageTheme } from '@backstage/theme'; const GraphiQL = React.lazy(() => import('graphiql')); -const useStyles = makeStyles((theme) => ({ +const useStyles = makeStyles(theme => ({ root: { height: '100%', display: 'flex', diff --git a/plugins/lighthouse/src/components/AuditList/AuditListTable.tsx b/plugins/lighthouse/src/components/AuditList/AuditListTable.tsx index 20dd1f39f8..e52805000a 100644 --- a/plugins/lighthouse/src/components/AuditList/AuditListTable.tsx +++ b/plugins/lighthouse/src/components/AuditList/AuditListTable.tsx @@ -31,7 +31,7 @@ const columns: TableColumn[] = [ title: 'Website URL', field: 'websiteUrl', }, - ...CATEGORIES.map((category) => ({ + ...CATEGORIES.map(category => ({ title: CATEGORY_LABELS[category], field: category, })), @@ -56,7 +56,7 @@ export const AuditListTable: FC<{ items: Website[] }> = ({ items }) => { const lighthouseApi = useApi(lighthouseApiRef); const runRefresh = (websites: Website[]) => { - websites.forEach(async (website) => { + websites.forEach(async website => { const response = await lighthouseApi.getWebsiteForAuditId( website.lastAudit.id, ); @@ -64,7 +64,7 @@ export const AuditListTable: FC<{ items: Website[] }> = ({ items }) => { if (auditStatus === 'COMPLETED' || auditStatus === 'FAILED') { const newWebsiteData = websiteState.slice(0); newWebsiteData[ - newWebsiteData.findIndex((w) => w.url === response.url) + newWebsiteData.findIndex(w => w.url === response.url) ] = response; setWebsiteState(newWebsiteData); } @@ -72,7 +72,7 @@ export const AuditListTable: FC<{ items: Website[] }> = ({ items }) => { }; const runningWebsiteAudits = websiteState - ? websiteState.filter((website) => website.lastAudit.status === 'RUNNING') + ? websiteState.filter(website => website.lastAudit.status === 'RUNNING') : []; useInterval( @@ -80,10 +80,10 @@ export const AuditListTable: FC<{ items: Website[] }> = ({ items }) => { runningWebsiteAudits.length > 0 ? 5000 : null, ); - const data = websiteState.map((website) => { + const data = websiteState.map(website => { const trendlineData = buildSparklinesDataForItem(website); const trendlines: any = {}; - CATEGORIES.forEach((category) => { + CATEGORIES.forEach(category => { trendlines[category] = ( ({ +const useStyles = makeStyles(theme => ({ tabs: { marginBottom: -18 }, tab: { minWidth: 72, paddingLeft: 1, paddingRight: 1 }, content: { marginBottom: theme.spacing(2) }, diff --git a/plugins/scaffolder-backend/README.md b/plugins/scaffolder-backend/README.md index 7ffbeae1d1..185938b006 100644 --- a/plugins/scaffolder-backend/README.md +++ b/plugins/scaffolder-backend/README.md @@ -1,4 +1,5 @@ # Title + Welcome to the scaffolder plugin! ## Sub-section 1 diff --git a/plugins/scaffolder-backend/src/index.ts b/plugins/scaffolder-backend/src/index.ts index 0a0a4cb95f..c461bfede6 100644 --- a/plugins/scaffolder-backend/src/index.ts +++ b/plugins/scaffolder-backend/src/index.ts @@ -16,4 +16,3 @@ export * from './scaffolder'; export * from './service/router'; - diff --git a/plugins/techdocs/src/api.ts b/plugins/techdocs/src/api.ts index 951ad5e16c..6fc913e5df 100644 --- a/plugins/techdocs/src/api.ts +++ b/plugins/techdocs/src/api.ts @@ -42,7 +42,9 @@ export class TechDocsStorageApi implements TechDocsStorage { async getEntityDocs(entityId: ParsedEntityId, path: string) { const { kind, namespace, name } = entityId; - const url = `${this.apiOrigin}/${kind}/${namespace ? namespace : 'default'}/${name}/${path}`; + const url = `${this.apiOrigin}/${kind}/${ + namespace ? namespace : 'default' + }/${name}/${path}`; const request = await fetch( `${url.endsWith('/') ? url : `${url}/`}index.html`, @@ -64,7 +66,9 @@ export class TechDocsStorageApi implements TechDocsStorage { return new URL( oldBaseUrl, - `${this.apiOrigin}/${kind}/${namespace ? namespace : 'default'}/${name}/${path}`, + `${this.apiOrigin}/${kind}/${ + namespace ? namespace : 'default' + }/${name}/${path}`, ).toString(); } } diff --git a/plugins/welcome/README.md b/plugins/welcome/README.md index 95d8947824..f0211c248a 100644 --- a/plugins/welcome/README.md +++ b/plugins/welcome/README.md @@ -1,4 +1,5 @@ # Title + Welcome to the welcome plugin! ## Sub-section 1 From d1fc52d740bc7d8b6be8f9e853e2aba62fd93dd6 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Sat, 5 Sep 2020 20:10:23 +0200 Subject: [PATCH 318/359] TechDocs: add docs by default to springboot grpc template (#2288) * add docs by default to springboot grpc template * delete line from rebase --- .../component-info.yaml | 1 + .../docs/index.md | 28 +++++++++++++++++++ .../{{cookiecutter.component_id}}/mkdocs.yml | 8 ++++++ 3 files changed, 37 insertions(+) create mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/docs/index.md create mode 100644 plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/mkdocs.yml diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/component-info.yaml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/component-info.yaml index 618209f44c..05b87c9c0c 100644 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/component-info.yaml +++ b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/component-info.yaml @@ -5,6 +5,7 @@ metadata: description: {{cookiecutter.description}} annotations: github.com/project-slug: {{cookiecutter.storePath}} + backstage.io/techdocs-ref: github:https://github.com/{{cookiecutter.storePath}} spec: type: service lifecycle: experimental diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/docs/index.md b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/docs/index.md new file mode 100644 index 0000000000..5352ef7801 --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/docs/index.md @@ -0,0 +1,28 @@ +## {{ cookiecutter.component_id }} + +{{ cookiecutter.description }} + +## Getting started + +Start write your documentation by adding more markdown (.md) files to this folder (/docs) or replace the content in this file. + +## Table of Contents + +The Table of Contents on the right is generated automatically based on the hierarchy +of headings. Only use one H1 (`#` in Markdown) per file. + +## Site navigation + +For new pages to appear in the left hand navigation you need edit the `mkdocs.yml` +file in root of your repo. The navigation can also link out to other sites. + +Alternatively, if there is no `nav` section in `mkdocs.yml`, a navigation section +will be created for you. However, you will not be able to use alternate titles for +pages, or include links to other sites. + +Note that MkDocs uses `mkdocs.yml`, not `mkdocs.yaml`, although both appear to work. +See also . + +## Support + +That's it. If you need support, reach out in [#docs-like-code](https://discord.com/channels/687207715902193673/714754240933003266) on Discord. diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/mkdocs.yml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/mkdocs.yml new file mode 100644 index 0000000000..0d10d11063 --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/mkdocs.yml @@ -0,0 +1,8 @@ +site_name: {{cookiecutter.component_id}} +site_description: {{cookiecutter.description}} + +nav: + - Introduction: index.md + +plugins: + - techdocs-core From cff489d03dc49ff5489136d4c8f610daba120d52 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Sat, 5 Sep 2020 20:10:44 +0200 Subject: [PATCH 319/359] link on entity page to link to correct docs (#2289) --- plugins/catalog/src/components/AboutCard/AboutCard.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx index d83beaedb5..0ab918dd6f 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -104,7 +104,9 @@ export function AboutCard({ entity }: AboutCardProps) { } - href={`/docs/${''}`} + href={`/docs/${entity.kind}:${entity.metadata.namespace ?? ''}:${ + entity.metadata.name + }`} /> } From 76269629d0ac51207cf86d228f5a4d90b11f59d6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 6 Sep 2020 10:15:48 +0200 Subject: [PATCH 320/359] cli: simplify command loading --- packages/cli/src/index.ts | 62 ++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 9fb909592b..1d78771006 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -26,18 +26,18 @@ const main = (argv: string[]) => { .command('app:build') .description('Build an app for a production release') .option('--stats', 'Write bundle stats to output directory') - .action(lazyAction(() => import('./commands/app/build'), 'default')); + .action(run(() => import('./commands/app/build').then(m => m.default))); program .command('app:serve') .description('Serve an app for local development') .option('--check', 'Enable type checking and linting') - .action(lazyAction(() => import('./commands/app/serve'), 'default')); + .action(run(() => import('./commands/app/serve').then(m => m.default))); program .command('backend:build') .description('Build a backend plugin') - .action(lazyAction(() => import('./commands/backend/build'), 'default')); + .action(run(() => import('./commands/backend/build').then(m => m.default))); program .command('backend:build-image ') @@ -45,7 +45,7 @@ const main = (argv: string[]) => { 'Builds a docker image from the package, with all local deps included', ) .action( - lazyAction(() => import('./commands/backend/buildImage'), 'default'), + run(() => import('./commands/backend/buildImage').then(m => m.default)), ); program @@ -53,22 +53,21 @@ const main = (argv: string[]) => { .description('Start local development server with HMR for the backend') .option('--check', 'Enable type checking and linting') .option('--inspect', 'Enable debugger') - .action(lazyAction(() => import('./commands/backend/dev'), 'default')); + .action(run(() => import('./commands/backend/dev').then(m => m.default))); program .command('app:diff') .option('--check', 'Fail if changes are required') .option('--yes', 'Apply all changes') .description('Diff an existing app with the creation template') - .action(lazyAction(() => import('./commands/app/diff'), 'default')); + .action(run(() => import('./commands/app/diff').then(m => m.default))); program .command('create-plugin') .description('Creates a new plugin in the current repository') .action( - lazyAction( - () => import('./commands/create-plugin/createPlugin'), - 'default', + run(() => + import('./commands/create-plugin/createPlugin').then(m => m.default), ), ); @@ -76,41 +75,40 @@ const main = (argv: string[]) => { .command('remove-plugin') .description('Removes plugin in the current repository') .action( - lazyAction( - () => import('./commands/remove-plugin/removePlugin'), - 'default', + run(() => + import('./commands/remove-plugin/removePlugin').then(m => m.default), ), ); program .command('plugin:build') .description('Build a plugin') - .action(lazyAction(() => import('./commands/plugin/build'), 'default')); + .action(run(() => import('./commands/plugin/build').then(m => m.default))); program .command('plugin:serve') .description('Serves the dev/ folder of a plugin') .option('--check', 'Enable type checking and linting') - .action(lazyAction(() => import('./commands/plugin/serve'), 'default')); + .action(run(() => import('./commands/plugin/serve').then(m => m.default))); program .command('plugin:export') .description('Exports the dev/ folder of a plugin') .option('--stats', 'Write bundle stats to output directory') - .action(lazyAction(() => import('./commands/plugin/export'), 'default')); + .action(run(() => import('./commands/plugin/export').then(m => m.default))); program .command('plugin:diff') .option('--check', 'Fail if changes are required') .option('--yes', 'Apply all changes') .description('Diff an existing plugin with the creation template') - .action(lazyAction(() => import('./commands/plugin/diff'), 'default')); + .action(run(() => import('./commands/plugin/diff').then(m => m.default))); program .command('build') .description('Build a package for publishing') .option('--outputs ', 'List of formats to output [types,cjs,esm]') - .action(lazyAction(() => import('./commands/build'), 'default')); + .action(run(() => import('./commands/build').then(m => m.default))); program .command('lint') @@ -121,14 +119,14 @@ const main = (argv: string[]) => { ) .option('--fix', 'Attempt to automatically fix violations') .description('Lint a package') - .action(lazyAction(() => import('./commands/lint'), 'default')); + .action(run(() => import('./commands/lint').then(m => m.default))); program .command('test') .allowUnknownOption(true) // Allows the command to run, but we still need to parse raw args .helpOption(', --backstage-cli-help') // Let Jest handle help .description('Run tests, forwarding args to Jest, defaulting to watch mode') - .action(lazyAction(() => import('./commands/testCommand'), 'default')); + .action(run(() => import('./commands/testCommand').then(m => m.default))); program .command('config:print') @@ -142,27 +140,29 @@ const main = (argv: string[]) => { 'Format to print the configuration in, either json or yaml [yaml]', ) .description('Print the app configuration for the current package') - .action(lazyAction(() => import('./commands/config/print'), 'default')); + .action(run(() => import('./commands/config/print').then(m => m.default))); program .command('prepack') .description('Prepares a package for packaging before publishing') - .action(lazyAction(() => import('./commands/pack'), 'pre')); + .action(run(() => import('./commands/pack').then(m => m.pre))); program .command('postpack') .description('Restores the changes made by the prepack command') - .action(lazyAction(() => import('./commands/pack'), 'post')); + .action(run(() => import('./commands/pack').then(m => m.post))); program .command('clean') .description('Delete cache directories') - .action(lazyAction(() => import('./commands/clean/clean'), 'default')); + .action(run(() => import('./commands/clean/clean').then(m => m.default))); program .command('build-workspace ...') .description('Builds a temporary dist workspace from the provided packages') - .action(lazyAction(() => import('./commands/buildWorkspace'), 'default')); + .action( + run(() => import('./commands/buildWorkspace').then(m => m.default)), + ); program.on('command:*', () => { console.log(); @@ -182,16 +182,12 @@ const main = (argv: string[]) => { }; // Wraps an action function so that it always exits and handles errors -function lazyAction( - actionRequireFunc: () => Promise< - { [name in Export]: (...args: T) => Promise } - >, - exportName: Export, -): (...args: T) => Promise { - return async (...args: T) => { +function run( + getActionFunc: () => Promise<(...args: any[]) => Promise>, +): (...args: any[]) => Promise { + return async (...args: any[]) => { try { - const module = await actionRequireFunc(); - const actionFunc = module[exportName]; + const actionFunc = await getActionFunc(); await actionFunc(...args); process.exit(0); } catch (error) { From ab66dd5ea25d3a02d80972ea2018f1f69d553b8f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 6 Sep 2020 10:26:32 +0200 Subject: [PATCH 321/359] cli: moved command index to index in commands --- packages/cli/src/commands/index.ts | 169 +++++++++++++++++++++++++++++ packages/cli/src/index.ts | 158 +-------------------------- 2 files changed, 171 insertions(+), 156 deletions(-) create mode 100644 packages/cli/src/commands/index.ts diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts new file mode 100644 index 0000000000..e8406231a5 --- /dev/null +++ b/packages/cli/src/commands/index.ts @@ -0,0 +1,169 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { CommanderStatic } from 'commander'; +import { exitWithError } from '../lib/errors'; + +export function registerCommands(program: CommanderStatic) { + program + .command('app:build') + .description('Build an app for a production release') + .option('--stats', 'Write bundle stats to output directory') + .action(lazy(() => import('./app/build').then(m => m.default))); + + program + .command('app:serve') + .description('Serve an app for local development') + .option('--check', 'Enable type checking and linting') + .action(lazy(() => import('./app/serve').then(m => m.default))); + + program + .command('backend:build') + .description('Build a backend plugin') + .action(lazy(() => import('./backend/build').then(m => m.default))); + + program + .command('backend:build-image ') + .description( + 'Builds a docker image from the package, with all local deps included', + ) + .action(lazy(() => import('./backend/buildImage').then(m => m.default))); + + program + .command('backend:dev') + .description('Start local development server with HMR for the backend') + .option('--check', 'Enable type checking and linting') + .option('--inspect', 'Enable debugger') + .action(lazy(() => import('./backend/dev').then(m => m.default))); + + program + .command('app:diff') + .option('--check', 'Fail if changes are required') + .option('--yes', 'Apply all changes') + .description('Diff an existing app with the creation template') + .action(lazy(() => import('./app/diff').then(m => m.default))); + + program + .command('create-plugin') + .description('Creates a new plugin in the current repository') + .action( + lazy(() => import('./create-plugin/createPlugin').then(m => m.default)), + ); + + program + .command('remove-plugin') + .description('Removes plugin in the current repository') + .action( + lazy(() => import('./remove-plugin/removePlugin').then(m => m.default)), + ); + + program + .command('plugin:build') + .description('Build a plugin') + .action(lazy(() => import('./plugin/build').then(m => m.default))); + + program + .command('plugin:serve') + .description('Serves the dev/ folder of a plugin') + .option('--check', 'Enable type checking and linting') + .action(lazy(() => import('./plugin/serve').then(m => m.default))); + + program + .command('plugin:export') + .description('Exports the dev/ folder of a plugin') + .option('--stats', 'Write bundle stats to output directory') + .action(lazy(() => import('./plugin/export').then(m => m.default))); + + program + .command('plugin:diff') + .option('--check', 'Fail if changes are required') + .option('--yes', 'Apply all changes') + .description('Diff an existing plugin with the creation template') + .action(lazy(() => import('./plugin/diff').then(m => m.default))); + + program + .command('build') + .description('Build a package for publishing') + .option('--outputs ', 'List of formats to output [types,cjs,esm]') + .action(lazy(() => import('./build').then(m => m.default))); + + program + .command('lint') + .option( + '--format ', + 'Lint report output format', + 'eslint-formatter-friendly', + ) + .option('--fix', 'Attempt to automatically fix violations') + .description('Lint a package') + .action(lazy(() => import('./lint').then(m => m.default))); + + program + .command('test') + .allowUnknownOption(true) // Allows the command to run, but we still need to parse raw args + .helpOption(', --backstage-cli-help') // Let Jest handle help + .description('Run tests, forwarding args to Jest, defaulting to watch mode') + .action(lazy(() => import('./testCommand').then(m => m.default))); + + program + .command('config:print') + .option('--with-secrets', 'Include secrets in the printed configuration') + .option( + '--env ', + 'The environment to print configuration for [NODE_ENV or development]', + ) + .option( + '--format ', + 'Format to print the configuration in, either json or yaml [yaml]', + ) + .description('Print the app configuration for the current package') + .action(lazy(() => import('./config/print').then(m => m.default))); + + program + .command('prepack') + .description('Prepares a package for packaging before publishing') + .action(lazy(() => import('./pack').then(m => m.pre))); + + program + .command('postpack') + .description('Restores the changes made by the prepack command') + .action(lazy(() => import('./pack').then(m => m.post))); + + program + .command('clean') + .description('Delete cache directories') + .action(lazy(() => import('./clean/clean').then(m => m.default))); + + program + .command('build-workspace ...') + .description('Builds a temporary dist workspace from the provided packages') + .action(lazy(() => import('./buildWorkspace').then(m => m.default))); +} + +// Wraps an action function so that it always exits and handles errors +function lazy( + getActionFunc: () => Promise<(...args: any[]) => Promise>, +): (...args: any[]) => Promise { + return async (...args: any[]) => { + try { + const actionFunc = await getActionFunc(); + await actionFunc(...args); + process.exit(0); + } catch (error) { + exitWithError(error); + } + }; +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 1d78771006..e278164f4c 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -18,151 +18,12 @@ import program from 'commander'; import chalk from 'chalk'; import { exitWithError } from './lib/errors'; import { version } from './lib/version'; +import { registerCommands } from './commands'; const main = (argv: string[]) => { program.name('backstage-cli').version(version); - program - .command('app:build') - .description('Build an app for a production release') - .option('--stats', 'Write bundle stats to output directory') - .action(run(() => import('./commands/app/build').then(m => m.default))); - - program - .command('app:serve') - .description('Serve an app for local development') - .option('--check', 'Enable type checking and linting') - .action(run(() => import('./commands/app/serve').then(m => m.default))); - - program - .command('backend:build') - .description('Build a backend plugin') - .action(run(() => import('./commands/backend/build').then(m => m.default))); - - program - .command('backend:build-image ') - .description( - 'Builds a docker image from the package, with all local deps included', - ) - .action( - run(() => import('./commands/backend/buildImage').then(m => m.default)), - ); - - program - .command('backend:dev') - .description('Start local development server with HMR for the backend') - .option('--check', 'Enable type checking and linting') - .option('--inspect', 'Enable debugger') - .action(run(() => import('./commands/backend/dev').then(m => m.default))); - - program - .command('app:diff') - .option('--check', 'Fail if changes are required') - .option('--yes', 'Apply all changes') - .description('Diff an existing app with the creation template') - .action(run(() => import('./commands/app/diff').then(m => m.default))); - - program - .command('create-plugin') - .description('Creates a new plugin in the current repository') - .action( - run(() => - import('./commands/create-plugin/createPlugin').then(m => m.default), - ), - ); - - program - .command('remove-plugin') - .description('Removes plugin in the current repository') - .action( - run(() => - import('./commands/remove-plugin/removePlugin').then(m => m.default), - ), - ); - - program - .command('plugin:build') - .description('Build a plugin') - .action(run(() => import('./commands/plugin/build').then(m => m.default))); - - program - .command('plugin:serve') - .description('Serves the dev/ folder of a plugin') - .option('--check', 'Enable type checking and linting') - .action(run(() => import('./commands/plugin/serve').then(m => m.default))); - - program - .command('plugin:export') - .description('Exports the dev/ folder of a plugin') - .option('--stats', 'Write bundle stats to output directory') - .action(run(() => import('./commands/plugin/export').then(m => m.default))); - - program - .command('plugin:diff') - .option('--check', 'Fail if changes are required') - .option('--yes', 'Apply all changes') - .description('Diff an existing plugin with the creation template') - .action(run(() => import('./commands/plugin/diff').then(m => m.default))); - - program - .command('build') - .description('Build a package for publishing') - .option('--outputs ', 'List of formats to output [types,cjs,esm]') - .action(run(() => import('./commands/build').then(m => m.default))); - - program - .command('lint') - .option( - '--format ', - 'Lint report output format', - 'eslint-formatter-friendly', - ) - .option('--fix', 'Attempt to automatically fix violations') - .description('Lint a package') - .action(run(() => import('./commands/lint').then(m => m.default))); - - program - .command('test') - .allowUnknownOption(true) // Allows the command to run, but we still need to parse raw args - .helpOption(', --backstage-cli-help') // Let Jest handle help - .description('Run tests, forwarding args to Jest, defaulting to watch mode') - .action(run(() => import('./commands/testCommand').then(m => m.default))); - - program - .command('config:print') - .option('--with-secrets', 'Include secrets in the printed configuration') - .option( - '--env ', - 'The environment to print configuration for [NODE_ENV or development]', - ) - .option( - '--format ', - 'Format to print the configuration in, either json or yaml [yaml]', - ) - .description('Print the app configuration for the current package') - .action(run(() => import('./commands/config/print').then(m => m.default))); - - program - .command('prepack') - .description('Prepares a package for packaging before publishing') - .action(run(() => import('./commands/pack').then(m => m.pre))); - - program - .command('postpack') - .description('Restores the changes made by the prepack command') - .action(run(() => import('./commands/pack').then(m => m.post))); - - program - .command('clean') - .description('Delete cache directories') - .action(run(() => import('./commands/clean/clean').then(m => m.default))); - - program - .command('build-workspace ...') - .description('Builds a temporary dist workspace from the provided packages') - .action( - run(() => import('./commands/buildWorkspace').then(m => m.default)), - ); + registerCommands(program); program.on('command:*', () => { console.log(); @@ -181,21 +42,6 @@ const main = (argv: string[]) => { program.parse(argv); }; -// Wraps an action function so that it always exits and handles errors -function run( - getActionFunc: () => Promise<(...args: any[]) => Promise>, -): (...args: any[]) => Promise { - return async (...args: any[]) => { - try { - const actionFunc = await getActionFunc(); - await actionFunc(...args); - process.exit(0); - } catch (error) { - exitWithError(error); - } - }; -} - process.on('unhandledRejection', rejection => { if (rejection instanceof Error) { exitWithError(rejection); From a18c054e9badc045f26b94ad4747b3359a609816 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 6 Sep 2020 10:49:20 +0200 Subject: [PATCH 322/359] workflows: ensure that working directory is clean at the end of build --- .github/workflows/ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ea1308336..2246a1dc09 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,3 +99,16 @@ jobs: - name: verify plugin template run: yarn lerna -- run diff -- --check + + - name: ensure clean working directory + run: | + if files=$(git ls-files --exclude-standard --others --modified) && [[ -z "$files" ]]; then + exit 0 + else + echo "" + echo "Working directory has been modified:" + echo "" + git status --short + echo "" + exit 1 + fi From 8da37131dacac4a8d2455c4f7087bb8f26dc89ca Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 00:56:34 +0200 Subject: [PATCH 323/359] cli-common: fix drive letter for target dir being lowercased on Windows --- packages/cli-common/src/paths.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/cli-common/src/paths.ts b/packages/cli-common/src/paths.ts index 70761ad2d7..0db712e6cb 100644 --- a/packages/cli-common/src/paths.ts +++ b/packages/cli-common/src/paths.ts @@ -106,7 +106,10 @@ export function findOwnRootDir(ownDir: string) { */ export function findPaths(searchDir: string): Paths { const ownDir = findOwnDir(searchDir); - const targetDir = fs.realpathSync(process.cwd()); + // Drive letter can end up being lowercased here on Windows, bring back to uppercase for consistency + const targetDir = fs + .realpathSync(process.cwd()) + .replace(/^[a-z]:/, str => str.toUpperCase()); // Lazy load this as it will throw an error if we're not inside the Backstage repo. let ownRoot = ''; From 20158f0c3649cc1445d3d271e1e3f79b59de8571 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 6 Sep 2020 11:49:57 +0200 Subject: [PATCH 324/359] cli: forward all extra backend:build-image args to docker build --- packages/backend/package.json | 2 +- packages/cli/src/commands/backend/buildImage.ts | 12 ++++++++++-- packages/cli/src/commands/index.ts | 7 +++++-- .../default-app/packages/backend/package.json.hbs | 2 +- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 03d768ca0b..f9bf855371 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -10,7 +10,7 @@ }, "scripts": { "build": "backstage-cli backend:build", - "build-image": "backstage-cli backend:build-image example-backend", + "build-image": "backstage-cli backend:build-image --tag example-backend", "start": "backstage-cli backend:dev", "lint": "backstage-cli lint", "test": "backstage-cli test", diff --git a/packages/cli/src/commands/backend/buildImage.ts b/packages/cli/src/commands/backend/buildImage.ts index 258ef102d7..14b62d2894 100644 --- a/packages/cli/src/commands/backend/buildImage.ts +++ b/packages/cli/src/commands/backend/buildImage.ts @@ -18,10 +18,17 @@ import fs from 'fs-extra'; import { createDistWorkspace } from '../../lib/packager'; import { paths } from '../../lib/paths'; import { run } from '../../lib/run'; +import { Command } from 'commander'; const PKG_PATH = 'package.json'; -export default async (imageTag: string) => { +export default async (cmd: Command) => { + // Skip the preparation steps if we're being asked for help + if (cmd.args.includes('--help')) { + await run('docker', ['image', 'build', '--help']); + return; + } + const pkgPath = paths.resolveTarget(PKG_PATH); const pkg = await fs.readJson(pkgPath); const tempDistWorkspace = await createDistWorkspace([pkg.name], { @@ -34,7 +41,8 @@ export default async (imageTag: string) => { }); console.log(`Dist workspace ready at ${tempDistWorkspace}`); - await run('docker', ['build', '.', '-t', imageTag], { + // all args are forwarded to docker build + await run('docker', ['image', 'build', '.', ...cmd.args], { cwd: tempDistWorkspace, }); diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index e8406231a5..e8b7fccfee 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -36,9 +36,12 @@ export function registerCommands(program: CommanderStatic) { .action(lazy(() => import('./backend/build').then(m => m.default))); program - .command('backend:build-image ') + .command('backend:build-image') + .allowUnknownOption(true) + .helpOption(', --backstage-cli-help') // Let docker handle --help + .option('--build', 'Build packages before packing them into the image') .description( - 'Builds a docker image from the package, with all local deps included', + 'Build a docker , all extra options are forwarded to docker build', ) .action(lazy(() => import('./backend/buildImage').then(m => m.default))); diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs index 53bfd6d990..8fc1e3bbe1 100644 --- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs @@ -9,7 +9,7 @@ }, "scripts": { "build": "backstage-cli backend:build", - "build-image": "backstage-cli backend:build-image example-backend", + "build-image": "backstage-cli backend:build-image --tag example-backend", "start": "backstage-cli backend:dev", "lint": "backstage-cli lint", "test": "backstage-cli test", From 905a924c18c8eb5e4ba59443cfd6b573f205d1d9 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 23:04:02 +0200 Subject: [PATCH 325/359] cli-loader: use mock-fs for tests --- packages/config-loader/package.json | 4 +- .../config-loader/src/lib/resolver.test.ts | 73 ++++++++++--------- packages/config-loader/src/loader.test.ts | 59 ++++++--------- yarn.lock | 12 +++ 4 files changed, 75 insertions(+), 73 deletions(-) diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 92d20ef1e7..592b8eefb3 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -37,8 +37,10 @@ }, "devDependencies": { "@types/jest": "^26.0.7", + "@types/mock-fs": "^4.10.0", "@types/node": "^12.0.0", - "@types/yup": "^0.28.2" + "@types/yup": "^0.28.2", + "mock-fs": "^4.13.0" }, "files": [ "dist" diff --git a/packages/config-loader/src/lib/resolver.test.ts b/packages/config-loader/src/lib/resolver.test.ts index c804a6cef3..9fd71f2486 100644 --- a/packages/config-loader/src/lib/resolver.test.ts +++ b/packages/config-loader/src/lib/resolver.test.ts @@ -14,46 +14,48 @@ * limitations under the License. */ -const pathExists = jest.fn(); - -jest.mock('fs-extra', () => ({ pathExists })); - +import mockFs from 'mock-fs'; import { resolveStaticConfig } from './resolver'; +function normalizePaths(paths: string[]) { + return paths.map(p => + p + .replace(/^[a-z]:/i, '') + .split('\\') + .join('/'), + ); +} + describe('resolveStaticConfig', () => { afterEach(() => { - jest.resetAllMocks(); + mockFs.restore(); }); it('should resolve no files for empty roots', async () => { + mockFs({}); const resolved = await resolveStaticConfig({ env: 'development', rootPaths: [], }); - expect(resolved).toEqual([]); - expect(pathExists).not.toHaveBeenCalled(); + expect(normalizePaths(resolved)).toEqual([]); }); it('should resolve a single app-config', async () => { - pathExists.mockImplementation(async (path: string) => - ['/repo/app-config.yaml'].includes(path), - ); + mockFs({ '/repo/app-config.yaml': '' }); const resolved = await resolveStaticConfig({ env: 'development', rootPaths: ['/repo'], }); - expect(resolved).toEqual(['/repo/app-config.yaml']); - expect(pathExists).toHaveBeenCalledTimes(4); + expect(normalizePaths(resolved)).toEqual(['/repo/app-config.yaml']); }); it('should resolve a app-configs in different directories', async () => { - pathExists.mockImplementation(async (path: string) => - ['/repo/app-config.yaml', '/repo/packages/a/app-config.yaml'].includes( - path, - ), - ); + mockFs({ + '/repo/app-config.yaml': '', + '/repo/packages/a/app-config.yaml': '', + }); const resolved = await resolveStaticConfig({ env: 'development', rootPaths: [ @@ -64,53 +66,54 @@ describe('resolveStaticConfig', () => { ], }); - expect(resolved).toEqual([ + expect(normalizePaths(resolved)).toEqual([ '/repo/app-config.yaml', '/repo/packages/a/app-config.yaml', ]); - expect(pathExists).toHaveBeenCalledTimes(16); }); it('should resolve env and local configs', async () => { - pathExists.mockImplementation(async (path: string) => - [ - '/repo/app-config.yaml', - '/repo/app-config.local.yaml', - '/repo/app-config.production.yaml', - '/repo/app-config.production.local.yaml', - '/repo/app-config.development.local.yaml', - '/repo/packages/a/app-config.development.yaml', - '/repo/packages/a/app-config.local.yaml', - ].includes(path), - ); + mockFs({ + '/repo/app-config.yaml': '', + '/repo/app-config.local.yaml': '', + '/repo/app-config.production.yaml': '', + '/repo/app-config.production.local.yaml': '', + '/repo/app-config.development.local.yaml': '', + '/repo/packages/a/app-config.development.yaml': '', + '/repo/packages/a/app-config.local.yaml': '', + }); const resolved = await resolveStaticConfig({ env: 'development', rootPaths: ['/repo', '/repo/packages/a'], }); - expect(resolved).toEqual([ + expect(normalizePaths(resolved)).toEqual([ '/repo/app-config.yaml', '/repo/app-config.local.yaml', '/repo/app-config.development.local.yaml', '/repo/packages/a/app-config.local.yaml', '/repo/packages/a/app-config.development.yaml', ]); - expect(pathExists).toHaveBeenCalledTimes(8); }); it('resolves suffixed configs in the correct order', async () => { - pathExists.mockImplementation(async () => true); + mockFs({ + '/repo/app-config.yaml': '', + '/repo/app-config.local.yaml': '', + '/repo/app-config.production.yaml': '', + '/repo/app-config.production.local.yaml': '', + }); + const resolved = await resolveStaticConfig({ env: 'production', rootPaths: ['/repo'], }); - expect(resolved).toEqual([ + expect(normalizePaths(resolved)).toEqual([ '/repo/app-config.yaml', '/repo/app-config.local.yaml', '/repo/app-config.production.yaml', '/repo/app-config.production.local.yaml', ]); - expect(pathExists).toHaveBeenCalledTimes(4); }); }); diff --git a/packages/config-loader/src/loader.test.ts b/packages/config-loader/src/loader.test.ts index 4a7c9f238c..a7547ee8fb 100644 --- a/packages/config-loader/src/loader.test.ts +++ b/packages/config-loader/src/loader.test.ts @@ -15,45 +15,30 @@ */ import { loadConfig } from './loader'; - -jest.mock('fs-extra', () => { - const mockFiles: { [path in string]: string } = { - '/root/app-config.yaml': ` - app: - title: Example App - sessionKey: - $secret: - file: secrets/session-key.txt - `, - '/root/app-config.development.yaml': ` - app: - sessionKey: development-key - `, - '/root/secrets/session-key.txt': 'abc123', - '/secret-port/app-config.yaml': ` - backend: - listen: - port: - $secret: - file: secrets/port.txt - `, - '/secret-port/secrets/port.txt': '12345', - }; - - return { - async readFile(path: string) { - if (path in mockFiles) { - return mockFiles[path]; - } - throw new Error(`File not found, ${path}`); - }, - async pathExists(path: string) { - return path in mockFiles; - }, - }; -}); +import mockFs from 'mock-fs'; describe('loadConfig', () => { + beforeAll(() => { + mockFs({ + '/root/app-config.yaml': ` + app: + title: Example App + sessionKey: + $secret: + file: secrets/session-key.txt + `, + '/root/app-config.development.yaml': ` + app: + sessionKey: development-key + `, + '/root/secrets/session-key.txt': 'abc123', + }); + }); + + afterAll(() => { + mockFs.restore(); + }); + it('loads config without secrets', async () => { await expect( loadConfig({ diff --git a/yarn.lock b/yarn.lock index 1853f23f7f..d241aff898 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4460,6 +4460,13 @@ dependencies: "@types/node" "*" +"@types/mock-fs@^4.10.0": + version "4.10.0" + resolved "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.10.0.tgz#460061b186993d76856f669d5317cda8a007c24b" + integrity sha512-FQ5alSzmHMmliqcL36JqIA4Yyn9jyJKvRSGV3mvPh108VFatX7naJDzSG4fnFQNZFq9dIx0Dzoe6ddflMB2Xkg== + dependencies: + "@types/node" "*" + "@types/morgan@^1.9.0": version "1.9.1" resolved "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.1.tgz#6457872df95647c1dbc6b3741e8146b71ece74bf" @@ -14682,6 +14689,11 @@ mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdir dependencies: minimist "^1.2.5" +mock-fs@^4.13.0: + version "4.13.0" + resolved "https://registry.npmjs.org/mock-fs/-/mock-fs-4.13.0.tgz#31c02263673ec3789f90eb7b6963676aa407a598" + integrity sha512-DD0vOdofJdoaRNtnWcrXe6RQbpHkPPmtqGq14uRX0F8ZKJ5nv89CVTYl/BZdppDxBDaV0hl75htg3abpEWlPZA== + modify-values@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" From ae10876e0efeda0fae80edd0a63316ff7aae5eb8 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 23:15:40 +0200 Subject: [PATCH 326/359] cli: make tests work on windows --- .../cli/src/commands/create-plugin/createPlugin.test.ts | 2 +- .../cli/src/commands/remove-plugin/removePlugin.test.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.test.ts b/packages/cli/src/commands/create-plugin/createPlugin.test.ts index 57fa33d173..72b95b072c 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.test.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.test.ts @@ -59,7 +59,7 @@ describe('createPlugin', () => { await createTemporaryPluginFolder(tempDir); await movePlugin(tempDir, pluginDir, id); await expect(fs.pathExists(pluginDir)).resolves.toBe(true); - expect(pluginDir).toMatch(`/plugins\/${id}`); + expect(pluginDir).toMatch(path.join('', 'plugins', id)); } finally { await del(tempDir, { force: true }); await del(rootDir, { force: true }); diff --git a/packages/cli/src/commands/remove-plugin/removePlugin.test.ts b/packages/cli/src/commands/remove-plugin/removePlugin.test.ts index 72fbc6f084..3ffa8d63cf 100644 --- a/packages/cli/src/commands/remove-plugin/removePlugin.test.ts +++ b/packages/cli/src/commands/remove-plugin/removePlugin.test.ts @@ -39,7 +39,7 @@ const testPluginPackage = `${BACKSTAGE}/plugin-${testPluginName}`; const tempDir = path.join(os.tmpdir(), 'remove-plugin-test'); const removeEmptyLines = (file: string): string => - file.split('\n').filter(Boolean).join('\n'); + file.split(/\r?\n/).filter(Boolean).join('\n'); const createTestPackageFile = async ( testFilePath: string, @@ -100,7 +100,7 @@ describe('removePlugin', () => { const packageFileContent = removeEmptyLines( fse.readFileSync(packageFilePath, 'utf8'), ); - expect(testFileContent === packageFileContent).toBe(true); + expect(testFileContent).toBe(packageFileContent); } finally { fse.removeSync(testFilePath); } @@ -117,7 +117,7 @@ describe('removePlugin', () => { const pluginsFileContent = removeEmptyLines( fse.readFileSync(pluginsFilePaths, 'utf8'), ); - expect(testFileContent === pluginsFileContent).toBe(true); + expect(testFileContent).toBe(pluginsFileContent); } finally { fse.removeSync(testFilePath); } @@ -138,7 +138,7 @@ describe('removePlugin', () => { 'test@gmail.com', ]); await removePluginFromCodeOwners(testFilePath, testPluginName); - expect(testFileContent === codeOwnersFileContent).toBeTruthy(); + expect(testFileContent).toBe(codeOwnersFileContent); } finally { if (fse.existsSync(testFilePath)) fse.removeSync(testFilePath); } From 30747fd72464e413c68152a512d64d8a963ce21a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 23:23:01 +0200 Subject: [PATCH 327/359] app-backend: make tests work on windows --- plugins/app-backend/src/service/router.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/app-backend/src/service/router.test.ts b/plugins/app-backend/src/service/router.test.ts index 4e0c27c47a..417fdc43b6 100644 --- a/plugins/app-backend/src/service/router.test.ts +++ b/plugins/app-backend/src/service/router.test.ts @@ -47,21 +47,21 @@ describe('createRouter', () => { const response = await request(app).get('/index.html'); expect(response.status).toBe(200); - expect(response.text).toBe('this is index.html\n'); + expect(response.text.trim()).toBe('this is index.html'); }); it('returns other.html', async () => { const response = await request(app).get('/other.html'); expect(response.status).toBe(200); - expect(response.text).toBe('this is other.html\n'); + expect(response.text.trim()).toBe('this is other.html'); }); it('returns index.html if missing', async () => { const response = await request(app).get('/missing.html'); expect(response.status).toBe(200); - expect(response.text).toBe('this is index.html\n'); + expect(response.text.trim()).toBe('this is index.html'); }); }); @@ -83,11 +83,11 @@ describe('createRouter with static fallback handler', () => { const response1 = await request(app).get('/static/main.txt'); expect(response1.status).toBe(200); - expect(response1.text).toBe('this is main.txt\n'); + expect(response1.text.trim()).toBe('this is main.txt'); const response2 = await request(app).get('/static/test.txt'); expect(response2.status).toBe(200); - expect(response2.text).toBe('this is test.txt'); + expect(response2.text.trim()).toBe('this is test.txt'); const response3 = await request(app).get('/static/missing.txt'); expect(response3.status).toBe(404); From 2723fb78d2be81be1c5196a2d0ff662f44643120 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 1 Sep 2020 23:39:22 +0200 Subject: [PATCH 328/359] scaffolder-backend: fix or skip tests on windows --- .../src/scaffolder/stages/prepare/github.test.ts | 4 +++- .../src/scaffolder/stages/templater/helpers.test.ts | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.test.ts index a61e5de867..4409d815b5 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.test.ts @@ -96,6 +96,8 @@ describe('GitHubPreparer', () => { mockEntity.spec.path = './template/test/1/2/3'; const response = await preparer.prepare(mockEntity); - expect(response).toMatch(new RegExp(/\/template\/test\/1\/2\/3$/)); + expect(response.split('\\').join('/')).toMatch( + /\/template\/test\/1\/2\/3$/, + ); }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts index b7e969cd47..a4441ca23f 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/helpers.test.ts @@ -20,6 +20,13 @@ import Docker from 'dockerode'; import { runDockerContainer } from './helpers'; describe('helpers', () => { + if (process.platform === 'win32') { + // eslint-disable-next-line jest/no-focused-tests + it.only('should skip tests on windows', () => { + expect('test').not.toBe('run'); + }); + } + const mockDocker = new Docker() as jest.Mocked; beforeEach(() => { From 566f1f890e230da8473ed1a624eaf29fb9f774fd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 2 Sep 2020 00:02:47 +0200 Subject: [PATCH 329/359] techdocs-backend: make tests work on windows --- .../src/techdocs/stages/prepare/dir.test.ts | 13 ++++++++++--- .../src/techdocs/stages/prepare/github.test.ts | 9 ++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts index 1635995b9e..a77c7978da 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/dir.test.ts @@ -17,6 +17,13 @@ import { DirectoryPreparer } from './dir'; import { getVoidLogger } from '@backstage/backend-common'; import { checkoutGitRepository } from './helpers'; +function normalizePath(path: string) { + return path + .replace(/^[a-z]:/i, '') + .split('\\') + .join('/'); +} + jest.mock('./helpers', () => ({ ...jest.requireActual<{}>('./helpers'), checkoutGitRepository: jest.fn(() => '/tmp/backstage-repo/org/name/branch/'), @@ -47,7 +54,7 @@ describe('directory preparer', () => { 'backstage.io/techdocs-ref': 'dir:./our-documentation', }); - expect(await directoryPreparer.prepare(mockEntity)).toEqual( + expect(normalizePath(await directoryPreparer.prepare(mockEntity))).toEqual( '/directory/our-documentation', ); }); @@ -61,7 +68,7 @@ describe('directory preparer', () => { 'backstage.io/techdocs-ref': 'dir:/our-documentation/techdocs', }); - expect(await directoryPreparer.prepare(mockEntity)).toEqual( + expect(normalizePath(await directoryPreparer.prepare(mockEntity))).toEqual( '/our-documentation/techdocs', ); }); @@ -75,7 +82,7 @@ describe('directory preparer', () => { 'backstage.io/techdocs-ref': 'dir:./docs', }); - expect(await directoryPreparer.prepare(mockEntity)).toEqual( + expect(normalizePath(await directoryPreparer.prepare(mockEntity))).toEqual( '/tmp/backstage-repo/org/name/branch/docs', ); expect(checkoutGitRepository).toHaveBeenCalledTimes(1); diff --git a/plugins/techdocs-backend/src/techdocs/stages/prepare/github.test.ts b/plugins/techdocs-backend/src/techdocs/stages/prepare/github.test.ts index 99a7d3bb5c..b78203d766 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/prepare/github.test.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/prepare/github.test.ts @@ -18,6 +18,13 @@ import { getVoidLogger } from '@backstage/backend-common'; import { GithubPreparer } from './github'; import { checkoutGithubRepository } from './helpers'; +function normalizePath(path: string) { + return path + .replace(/^[a-z]:/i, '') + .split('\\') + .join('/'); +} + jest.mock('./helpers', () => ({ ...jest.requireActual<{}>('./helpers'), checkoutGithubRepository: jest.fn( @@ -51,7 +58,7 @@ describe('github preparer', () => { const tempDocsPath = await preparer.prepare(mockEntity); expect(checkoutGithubRepository).toHaveBeenCalledTimes(1); - expect(tempDocsPath).toEqual( + expect(normalizePath(tempDocsPath)).toEqual( '/tmp/backstage-repo/org/name/branch/plugins/techdocs-backend/examples/documented-component', ); }); From 5957123277643870403b13b09f76c0e1bc6624b3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 2 Sep 2020 00:15:21 +0200 Subject: [PATCH 330/359] core: fix app config tests on windows --- .../core/src/api-wrappers/createApp.test.tsx | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/packages/core/src/api-wrappers/createApp.test.tsx b/packages/core/src/api-wrappers/createApp.test.tsx index 33623b4869..a1655b88dd 100644 --- a/packages/core/src/api-wrappers/createApp.test.tsx +++ b/packages/core/src/api-wrappers/createApp.test.tsx @@ -15,21 +15,21 @@ */ import { defaultConfigLoader } from './createApp'; -import { AppConfig } from '@backstage/config'; + +(process as any).env = { NODE_ENV: 'test' }; +const anyEnv = process.env as any; describe('defaultConfigLoader', () => { afterEach(() => { - delete process.env.APP_CONFIG; + delete anyEnv.APP_CONFIG; }); it('loads static config', async () => { - Object.defineProperty(process.env, 'APP_CONFIG', { - configurable: true, - value: [ - { data: { my: 'config' }, context: 'a' }, - { data: { my: 'override-config' }, context: 'b' }, - ] as AppConfig[], - }); + anyEnv.APP_CONFIG = [ + { data: { my: 'config' }, context: 'a' }, + { data: { my: 'override-config' }, context: 'b' }, + ]; + const configs = await defaultConfigLoader(); expect(configs).toEqual([ { data: { my: 'config' }, context: 'a' }, @@ -38,13 +38,11 @@ describe('defaultConfigLoader', () => { }); it('loads runtime config', async () => { - Object.defineProperty(process.env, 'APP_CONFIG', { - configurable: true, - value: [ - { data: { my: 'override-config' }, context: 'a' }, - { data: { my: 'config' }, context: 'b' }, - ] as AppConfig[], - }); + anyEnv.APP_CONFIG = [ + { data: { my: 'override-config' }, context: 'a' }, + { data: { my: 'config' }, context: 'b' }, + ]; + const configs = await (defaultConfigLoader as any)( '{"my":"runtime-config"}', ); @@ -62,20 +60,14 @@ describe('defaultConfigLoader', () => { }); it('fails to load invalid static config', async () => { - Object.defineProperty(process.env, 'APP_CONFIG', { - configurable: true, - value: { my: 'invalid-config' } as any, - }); + anyEnv.APP_CONFIG = { my: 'invalid-config' }; await expect(defaultConfigLoader()).rejects.toThrow( 'Static configuration has invalid format', ); }); it('fails to load bad runtime config', async () => { - Object.defineProperty(process.env, 'APP_CONFIG', { - configurable: true, - value: [{ data: { my: 'config' }, context: 'a' }] as AppConfig[], - }); + anyEnv.APP_CONFIG = [{ data: { my: 'config' }, context: 'a' }]; await expect((defaultConfigLoader as any)('}')).rejects.toThrow( 'Failed to load runtime configuration, SyntaxError: Unexpected token } in JSON at position 0', From 4ddc1e6f774fa300fe6d8aba1f4ae774411c23ec Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 2 Sep 2020 00:22:54 +0200 Subject: [PATCH 331/359] workflows: lint and test in windows master build --- .github/workflows/master-win.yml | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/.github/workflows/master-win.yml b/.github/workflows/master-win.yml index 9fdff4af32..5951721f68 100644 --- a/.github/workflows/master-win.yml +++ b/.github/workflows/master-win.yml @@ -38,6 +38,23 @@ jobs: - name: yarn install run: yarn install --frozen-lockfile # End of yarn setup -# Tests are broken on Windows, disabled for now -# - name: test -# run: yarn lerna -- run test + + - name: lint + run: yarn lerna -- run lint + + - name: type checking and declarations + run: yarn tsc --incremental false + + - name: verify type dependencies + run: yarn lint:type-deps + + - name: test + run: yarn lerna -- run test + + - name: Discord notification + if: ${{ failure() }} + uses: Ilshidur/action-discord@0.2.0 + env: + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} + with: + args: 'Windows master build failed https://github.com/{{GITHUB_REPOSITORY}}/actions/runs/{{GITHUB_RUN_ID}}' From 83ade4252c497bff798e5cdcc7577c24711f7bbf Mon Sep 17 00:00:00 2001 From: o-farooq Date: Mon, 7 Sep 2020 06:00:47 +1200 Subject: [PATCH 332/359] storybook: Upgrade Storybook to version 6 (#2296) * upgrade Storybook to version 6 * update storybook renderer layout Co-authored-by: Omer Farooq <17722640+o-farooq@users.noreply.github.com> --- .../.storybook/{config.js => preview.js} | 1 + packages/storybook/package.json | 12 +- yarn.lock | 1794 +++++++++-------- 3 files changed, 983 insertions(+), 824 deletions(-) rename packages/storybook/.storybook/{config.js => preview.js} (96%) diff --git a/packages/storybook/.storybook/config.js b/packages/storybook/.storybook/preview.js similarity index 96% rename from packages/storybook/.storybook/config.js rename to packages/storybook/.storybook/preview.js index 69145ea057..bb8e040c7b 100644 --- a/packages/storybook/.storybook/config.js +++ b/packages/storybook/.storybook/preview.js @@ -22,4 +22,5 @@ addParameters({ // Set the initial theme current: 'light', }, + layout: 'fullscreen', }); diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 35b609eebf..8f12a2891c 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -17,11 +17,11 @@ "@backstage/theme": "^0.1.1-alpha.21" }, "devDependencies": { - "@storybook/addon-actions": "^5.3.17", - "@storybook/addon-links": "^5.3.17", - "@storybook/addon-storysource": "^5.3.18", - "@storybook/addons": "^6.0.4", - "@storybook/react": "^5.3.17", - "storybook-dark-mode": "^0.6.1" + "@storybook/addon-actions": "^6.0.21", + "@storybook/addon-links": "^6.0.21", + "@storybook/addon-storysource": "^6.0.21", + "@storybook/addons": "^6.0.21", + "@storybook/react": "^6.0.21", + "storybook-dark-mode": "^1.0.2" } } diff --git a/yarn.lock b/yarn.lock index 1853f23f7f..83ecfc14af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -95,13 +95,6 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/code-frame@7.5.5": - version "7.5.5" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" - integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== - dependencies: - "@babel/highlight" "^7.0.0" - "@babel/code-frame@7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" @@ -125,7 +118,7 @@ invariant "^2.2.4" semver "^5.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.4.4", "@babel/core@^7.4.5", "@babel/core@^7.7.5", "@babel/core@^7.9.0": +"@babel/core@^7.1.0", "@babel/core@^7.4.4", "@babel/core@^7.7.5", "@babel/core@^7.9.0": version "7.11.1" resolved "https://registry.npmjs.org/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" integrity sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ== @@ -199,7 +192,7 @@ levenary "^1.1.1" semver "^5.5.0" -"@babel/helper-create-class-features-plugin@^7.10.4": +"@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5": version "7.10.5" resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A== @@ -396,7 +389,7 @@ "@babel/helper-remap-async-to-generator" "^7.10.4" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-class-properties@^7.10.4", "@babel/plugin-proposal-class-properties@^7.7.0": +"@babel/plugin-proposal-class-properties@^7.10.4", "@babel/plugin-proposal-class-properties@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807" integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg== @@ -404,6 +397,15 @@ "@babel/helper-create-class-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-proposal-decorators@^7.8.3": + version "7.10.5" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.5.tgz#42898bba478bc4b1ae242a703a953a7ad350ffb4" + integrity sha512-Sc5TAQSZuLzgY0664mMDn24Vw2P8g/VhyLyGPaWiHahhgLqeZvcGeyBZOrJW0oSKIK2mvQ22a1ENXBIQLhrEiQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-decorators" "^7.10.4" + "@babel/plugin-proposal-dynamic-import@^7.10.4": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e" @@ -412,6 +414,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-dynamic-import" "^7.8.0" +"@babel/plugin-proposal-export-default-from@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.10.4.tgz#08f66eef0067cbf6a7bc036977dcdccecaf0c6c5" + integrity sha512-G1l00VvDZ7Yk2yRlC5D8Ybvu3gmeHS3rCHoUYdjrqGYUtdeOBoRypnvDZ5KQqxyaiiGHWnVDeSEzA5F9ozItig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-export-default-from" "^7.10.4" + "@babel/plugin-proposal-export-namespace-from@^7.10.4": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz#570d883b91031637b3e2958eea3c438e62c05f54" @@ -436,7 +446,7 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": +"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a" integrity sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw== @@ -452,7 +462,7 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.11.0", "@babel/plugin-proposal-object-rest-spread@^7.6.2": +"@babel/plugin-proposal-object-rest-spread@^7.11.0", "@babel/plugin-proposal-object-rest-spread@^7.9.6": version "7.11.0" resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af" integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA== @@ -469,7 +479,7 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-optional-chaining@^7.11.0": +"@babel/plugin-proposal-optional-chaining@^7.10.1", "@babel/plugin-proposal-optional-chaining@^7.11.0": version "7.11.0" resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076" integrity sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA== @@ -478,7 +488,7 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" -"@babel/plugin-proposal-private-methods@^7.10.4": +"@babel/plugin-proposal-private-methods@^7.10.4", "@babel/plugin-proposal-private-methods@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909" integrity sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw== @@ -515,13 +525,27 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-dynamic-import@^7.2.0", "@babel/plugin-syntax-dynamic-import@^7.8.0": +"@babel/plugin-syntax-decorators@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.4.tgz#6853085b2c429f9d322d02f5a635018cdeb2360c" + integrity sha512-2NaoC6fAk2VMdhY1eerkfHV+lVYC1u8b+jmRJISqANCJlTxYy19HGdIkkQtix2UtkcPuPu+IlDgrVseZnU03bw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-export-default-from@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.10.4.tgz#e5494f95006355c10292a0ff1ce42a5746002ec8" + integrity sha512-79V6r6Pgudz0RnuMGp5xidu6Z+bPFugh8/Q9eDHonmLp4wKFAZDwygJwYgCzuDu8lFA/sYyT+mc5y2wkd7bTXA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-export-namespace-from@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" @@ -606,7 +630,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-arrow-functions@^7.10.4": +"@babel/plugin-syntax-typescript@^7.10.4": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.4.tgz#2f55e770d3501e83af217d782cb7517d7bb34d25" + integrity sha512-oSAEz1YkBCAKr5Yiq8/BNtvSAPwkp/IyUnwZogd8p+F0RuYQQrLeRUzIQhueQTTBy/F+a40uS7OFKxnkRvmvFQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-arrow-functions@^7.10.4", "@babel/plugin-transform-arrow-functions@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" integrity sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA== @@ -636,7 +667,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-classes@^7.10.4": +"@babel/plugin-transform-block-scoping@^7.8.3": + version "7.11.1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215" + integrity sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-classes@^7.10.4", "@babel/plugin-transform-classes@^7.9.5": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA== @@ -657,7 +695,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-destructuring@^7.10.4": +"@babel/plugin-transform-destructuring@^7.10.4", "@babel/plugin-transform-destructuring@^7.9.5": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5" integrity sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA== @@ -695,7 +733,7 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-flow" "^7.8.3" -"@babel/plugin-transform-for-of@^7.10.4": +"@babel/plugin-transform-for-of@^7.10.4", "@babel/plugin-transform-for-of@^7.9.0": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9" integrity sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ== @@ -783,7 +821,7 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-replace-supers" "^7.10.4" -"@babel/plugin-transform-parameters@^7.10.4": +"@babel/plugin-transform-parameters@^7.10.4", "@babel/plugin-transform-parameters@^7.9.5": version "7.10.5" resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a" integrity sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw== @@ -798,7 +836,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-react-constant-elements@^7.0.0", "@babel/plugin-transform-react-constant-elements@^7.2.0", "@babel/plugin-transform-react-constant-elements@^7.6.3", "@babel/plugin-transform-react-constant-elements@^7.7.4", "@babel/plugin-transform-react-constant-elements@^7.9.0": +"@babel/plugin-transform-react-constant-elements@^7.7.4", "@babel/plugin-transform-react-constant-elements@^7.9.0": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.10.4.tgz#0f485260bf1c29012bb973e7e404749eaac12c9e" integrity sha512-cYmQBW1pXrqBte1raMkAulXmi7rjg3VI6ZLg9QIic8Hq7BtYXaWuZSxsr2siOMI6SWwpxjWfnwhTUrd7JlAV7g== @@ -869,14 +907,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-shorthand-properties@^7.10.4": +"@babel/plugin-transform-shorthand-properties@^7.10.4", "@babel/plugin-transform-shorthand-properties@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6" integrity sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q== dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-spread@^7.11.0": +"@babel/plugin-transform-spread@^7.11.0", "@babel/plugin-transform-spread@^7.8.3": version "7.11.0" resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc" integrity sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw== @@ -892,7 +930,7 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-regex" "^7.10.4" -"@babel/plugin-transform-template-literals@^7.10.4": +"@babel/plugin-transform-template-literals@^7.10.4", "@babel/plugin-transform-template-literals@^7.8.3": version "7.10.5" resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c" integrity sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw== @@ -907,6 +945,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-typescript@^7.10.4": + version "7.11.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.11.0.tgz#2b4879676af37342ebb278216dd090ac67f13abb" + integrity sha512-edJsNzTtvb3MaXQwj8403B7mZoGu9ElDJQZOKjGUnvilquxBA3IQoEIOvkX/1O8xfAsnHS/oQhe2w/IXrr+w0w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-typescript" "^7.10.4" + "@babel/plugin-transform-unicode-escapes@^7.10.4": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" @@ -922,7 +969,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/preset-env@^7.4.5", "@babel/preset-env@^7.9.5": +"@babel/preset-env@^7.9.5": version "7.11.0" resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz#860ee38f2ce17ad60480c2021ba9689393efb796" integrity sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg== @@ -996,6 +1043,80 @@ levenary "^1.1.1" semver "^5.5.0" +"@babel/preset-env@^7.9.6": + version "7.11.5" + resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.5.tgz#18cb4b9379e3e92ffea92c07471a99a2914e4272" + integrity sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA== + dependencies: + "@babel/compat-data" "^7.11.0" + "@babel/helper-compilation-targets" "^7.10.4" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-async-generator-functions" "^7.10.4" + "@babel/plugin-proposal-class-properties" "^7.10.4" + "@babel/plugin-proposal-dynamic-import" "^7.10.4" + "@babel/plugin-proposal-export-namespace-from" "^7.10.4" + "@babel/plugin-proposal-json-strings" "^7.10.4" + "@babel/plugin-proposal-logical-assignment-operators" "^7.11.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.4" + "@babel/plugin-proposal-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread" "^7.11.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.10.4" + "@babel/plugin-proposal-optional-chaining" "^7.11.0" + "@babel/plugin-proposal-private-methods" "^7.10.4" + "@babel/plugin-proposal-unicode-property-regex" "^7.10.4" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.10.4" + "@babel/plugin-transform-arrow-functions" "^7.10.4" + "@babel/plugin-transform-async-to-generator" "^7.10.4" + "@babel/plugin-transform-block-scoped-functions" "^7.10.4" + "@babel/plugin-transform-block-scoping" "^7.10.4" + "@babel/plugin-transform-classes" "^7.10.4" + "@babel/plugin-transform-computed-properties" "^7.10.4" + "@babel/plugin-transform-destructuring" "^7.10.4" + "@babel/plugin-transform-dotall-regex" "^7.10.4" + "@babel/plugin-transform-duplicate-keys" "^7.10.4" + "@babel/plugin-transform-exponentiation-operator" "^7.10.4" + "@babel/plugin-transform-for-of" "^7.10.4" + "@babel/plugin-transform-function-name" "^7.10.4" + "@babel/plugin-transform-literals" "^7.10.4" + "@babel/plugin-transform-member-expression-literals" "^7.10.4" + "@babel/plugin-transform-modules-amd" "^7.10.4" + "@babel/plugin-transform-modules-commonjs" "^7.10.4" + "@babel/plugin-transform-modules-systemjs" "^7.10.4" + "@babel/plugin-transform-modules-umd" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.10.4" + "@babel/plugin-transform-new-target" "^7.10.4" + "@babel/plugin-transform-object-super" "^7.10.4" + "@babel/plugin-transform-parameters" "^7.10.4" + "@babel/plugin-transform-property-literals" "^7.10.4" + "@babel/plugin-transform-regenerator" "^7.10.4" + "@babel/plugin-transform-reserved-words" "^7.10.4" + "@babel/plugin-transform-shorthand-properties" "^7.10.4" + "@babel/plugin-transform-spread" "^7.11.0" + "@babel/plugin-transform-sticky-regex" "^7.10.4" + "@babel/plugin-transform-template-literals" "^7.10.4" + "@babel/plugin-transform-typeof-symbol" "^7.10.4" + "@babel/plugin-transform-unicode-escapes" "^7.10.4" + "@babel/plugin-transform-unicode-regex" "^7.10.4" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.11.5" + browserslist "^4.12.0" + core-js-compat "^3.6.2" + invariant "^2.2.2" + levenary "^1.1.1" + semver "^5.5.0" + "@babel/preset-flow@^7.0.0": version "7.9.0" resolved "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.9.0.tgz#fee847c3e090b0b2d9227c1949e4da1d1379280d" @@ -1015,7 +1136,7 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.0.0", "@babel/preset-react@^7.9.4": +"@babel/preset-react@^7.0.0", "@babel/preset-react@^7.8.3", "@babel/preset-react@^7.9.4": version "7.10.4" resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.10.4.tgz#92e8a66d816f9911d11d4cc935be67adfc82dbcf" integrity sha512-BrHp4TgOIy4M19JAfO1LhycVXOPWdDbTRep7eVyatf174Hff+6Uk53sDyajqZPu8W1qXRBiYOfIamek6jA7YVw== @@ -1028,6 +1149,25 @@ "@babel/plugin-transform-react-jsx-source" "^7.10.4" "@babel/plugin-transform-react-pure-annotations" "^7.10.4" +"@babel/preset-typescript@^7.9.0": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.10.4.tgz#7d5d052e52a682480d6e2cc5aa31be61c8c25e36" + integrity sha512-SdYnvGPv+bLlwkF2VkJnaX/ni1sMNetcGI1+nThF1gyv6Ph8Qucc4ZZAjM5yZcE/AKRXIOTZz7eSRDWOEjPyRQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-typescript" "^7.10.4" + +"@babel/register@^7.10.5": + version "7.11.5" + resolved "https://registry.npmjs.org/@babel/register/-/register-7.11.5.tgz#79becf89e0ddd0fba8b92bc279bc0f5d2d7ce2ea" + integrity sha512-CAml0ioKX+kOAvBQDHa/+t1fgOt3qkTIz0TrRtRAT6XY0m5qYZXR85k6/sLCNPMGhYDlCFHCYuU0ybTJbvlC6w== + dependencies: + find-cache-dir "^2.0.0" + lodash "^4.17.19" + make-dir "^2.1.0" + pirates "^4.0.0" + source-map-support "^0.5.16" + "@babel/runtime-corejs2@^7.10.4", "@babel/runtime-corejs2@^7.8.7": version "7.11.2" resolved "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.11.2.tgz#700a03945ebad0d31ba6690fc8a6bcc9040faa47" @@ -1044,7 +1184,7 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.11.2" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== @@ -1084,6 +1224,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.11.5": + version "7.11.5" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1494,6 +1643,11 @@ prop-types "^15.6.2" scheduler "^0.19.0" +"@icons/material@^0.2.4": + version "0.2.4" + resolved "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8" + integrity sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.0.0" resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b" @@ -2553,6 +2707,13 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@npmcli/move-file@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464" + integrity sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw== + dependencies: + mkdirp "^1.0.4" + "@octokit/auth-token@^2.4.0": version "2.4.0" resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz#b64178975218b99e4dfe948253f0673cbbb59d9f" @@ -2779,7 +2940,7 @@ resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@reach/router@^1.2.1", "@reach/router@^1.3.3": +"@reach/router@^1.3.3": version "1.3.4" resolved "https://registry.npmjs.org/@reach/router/-/router-1.3.4.tgz#d2574b19370a70c80480ed91f3da840136d10f8c" integrity sha512-+mtn9wjlB9NN2CNnnC/BRYtwdKBfSyyasPYraNAyvaV1occr/5NnB4CVzjEZipNHwYebQwcndGUmpFzxAUoqSA== @@ -2998,128 +3159,97 @@ glob "^7.1.4" read-pkg-up "^7.0.1" -"@storybook/addon-actions@^5.3.17": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-5.3.19.tgz#50548fa6e84bc79ad95233ce23ade4878fc7cfac" - integrity sha512-gXF29FFUgYlUoFf1DcVCmH1chg2ElaHWMmCi5h7aZe+g6fXBQw0UtEdJnYLMOqZCIiWoZyuf1ETD0RbNHPhRIw== +"@storybook/addon-actions@^6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-6.0.21.tgz#0de1d109d4b1eb99f644bbe84e74c25cfd2b1b6b" + integrity sha512-9y3ve+3GK1TsxQ5pxDjhB7E/XJXY+WqcSNlOX8Mb+XbS6AAgpFbkZCw1q8CGzyEUclHsQ6UK2+lo+IRGs4TLpA== dependencies: - "@storybook/addons" "5.3.19" - "@storybook/api" "5.3.19" - "@storybook/client-api" "5.3.19" - "@storybook/components" "5.3.19" - "@storybook/core-events" "5.3.19" - "@storybook/theming" "5.3.19" + "@storybook/addons" "6.0.21" + "@storybook/api" "6.0.21" + "@storybook/client-api" "6.0.21" + "@storybook/components" "6.0.21" + "@storybook/core-events" "6.0.21" + "@storybook/theming" "6.0.21" core-js "^3.0.1" - fast-deep-equal "^2.0.1" + fast-deep-equal "^3.1.1" global "^4.3.2" - polished "^3.3.1" + lodash "^4.17.15" + polished "^3.4.4" prop-types "^15.7.2" react "^16.8.3" - react-inspector "^4.0.0" - uuid "^3.3.2" + react-inspector "^5.0.1" + regenerator-runtime "^0.13.3" + ts-dedent "^1.1.1" + util-deprecate "^1.0.2" + uuid "^8.0.0" -"@storybook/addon-links@^5.3.17": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-5.3.19.tgz#3c23e886d44b56978ae254fed3bf8be54c877178" - integrity sha512-gn9u8lebREfRsyzxoDPG0O+kOf5aJ0BhzcCJGZZdqha0F6OWHhh8vJYZZvjJ/Qwze+Qt2zjrgWm+Q6+JLD8ugQ== +"@storybook/addon-links@^6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-6.0.21.tgz#6d4497933d560615617eaffeacec00ad8a788b01" + integrity sha512-5cRFxXS9BviDbS+DCKElr1vSafDcRhX74iIAWl/yOBUldUZvR+gX3WOZ7bO+OBSlQ1NJkt1NUAMag3aiJa4UUw== dependencies: - "@storybook/addons" "5.3.19" - "@storybook/client-logger" "5.3.19" - "@storybook/core-events" "5.3.19" + "@storybook/addons" "6.0.21" + "@storybook/client-logger" "6.0.21" + "@storybook/core-events" "6.0.21" "@storybook/csf" "0.0.1" - "@storybook/router" "5.3.19" + "@storybook/router" "6.0.21" + "@types/qs" "^6.9.0" core-js "^3.0.1" global "^4.3.2" prop-types "^15.7.2" qs "^6.6.0" - ts-dedent "^1.1.0" + regenerator-runtime "^0.13.3" + ts-dedent "^1.1.1" -"@storybook/addon-storysource@^5.3.18": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/addon-storysource/-/addon-storysource-5.3.19.tgz#ae693e88db5d220cb256a9ef4a2366c300e8d88c" - integrity sha512-W7mIAHuxYT+b1huaHCHLkBAh2MbeWmF8CxeBCFiOgZaYYQUTDEh018HJF8u2AqiWSouRhcfzhTnGxOo0hNRBgw== +"@storybook/addon-storysource@^6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/addon-storysource/-/addon-storysource-6.0.21.tgz#fce9a6de8b276239dbb49b809f3b5efd9fbcecb4" + integrity sha512-h8bu2twPfBRbWlxg8LRtCM5/r2FxWahJa0RC70qDX6eNdzDw6Xv0B8bZsVxKPWqBNQbwYPz5ui44ym53dFDM/Q== dependencies: - "@storybook/addons" "5.3.19" - "@storybook/components" "5.3.19" - "@storybook/router" "5.3.19" - "@storybook/source-loader" "5.3.19" - "@storybook/theming" "5.3.19" + "@storybook/addons" "6.0.21" + "@storybook/api" "6.0.21" + "@storybook/client-logger" "6.0.21" + "@storybook/components" "6.0.21" + "@storybook/router" "6.0.21" + "@storybook/source-loader" "6.0.21" + "@storybook/theming" "6.0.21" core-js "^3.0.1" estraverse "^4.2.0" - loader-utils "^1.2.3" - prettier "^1.16.4" + loader-utils "^2.0.0" + prettier "~2.0.5" prop-types "^15.7.2" - react-syntax-highlighter "^11.0.2" + react "^16.9.17" + react-syntax-highlighter "^12.2.1" regenerator-runtime "^0.13.3" - util-deprecate "^1.0.2" -"@storybook/addons@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/addons/-/addons-5.3.19.tgz#3a7010697afd6df9a41b8c8a7351d9a06ff490a4" - integrity sha512-Ky/k22p6i6FVNvs1VhuFyGvYJdcp+FgXqFgnPyY/OXJW/vPDapdElpTpHJZLFI9I2FQBDcygBPU5RXkumQ+KUQ== +"@storybook/addons@6.0.21", "@storybook/addons@^6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.0.21.tgz#bd5229652102c3aed59b78ef6920ff6b482b4d78" + integrity sha512-yDttNLc3vXqBxwK795ykgzTC6MpvuXDQuF4LHSlHZQe6wsMu1m3fljnbYdafJWdx6cNZwUblU3KYcR11PqhkPg== dependencies: - "@storybook/api" "5.3.19" - "@storybook/channels" "5.3.19" - "@storybook/client-logger" "5.3.19" - "@storybook/core-events" "5.3.19" - core-js "^3.0.1" - global "^4.3.2" - util-deprecate "^1.0.2" - -"@storybook/addons@^6.0.4": - version "6.0.12" - resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.0.12.tgz#f57f89e0aa55febfb5437ddc2628a0ccc9f44f44" - integrity sha512-gVCyWK4jys5cUY0d3/Bxi02oeCsgdi6xVvA+T4v+SgeduAfm/k01tdO2qDXL37Sl+2TT9HBQGazDrsIUW4d7Ug== - dependencies: - "@storybook/api" "6.0.12" - "@storybook/channels" "6.0.12" - "@storybook/client-logger" "6.0.12" - "@storybook/core-events" "6.0.12" - "@storybook/router" "6.0.12" - "@storybook/theming" "6.0.12" + "@storybook/api" "6.0.21" + "@storybook/channels" "6.0.21" + "@storybook/client-logger" "6.0.21" + "@storybook/core-events" "6.0.21" + "@storybook/router" "6.0.21" + "@storybook/theming" "6.0.21" core-js "^3.0.1" global "^4.3.2" regenerator-runtime "^0.13.3" -"@storybook/api@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/api/-/api-5.3.19.tgz#77f15e9e2eee59fe1ddeaba1ef39bc34713a6297" - integrity sha512-U/VzDvhNCPmw2igvJYNNM+uwJCL+3teiL6JmuoL4/cmcqhI6IqqG9dZmMP1egoCd19wXEP7rnAfB/VcYVg41dQ== - dependencies: - "@reach/router" "^1.2.1" - "@storybook/channels" "5.3.19" - "@storybook/client-logger" "5.3.19" - "@storybook/core-events" "5.3.19" - "@storybook/csf" "0.0.1" - "@storybook/router" "5.3.19" - "@storybook/theming" "5.3.19" - "@types/reach__router" "^1.2.3" - core-js "^3.0.1" - fast-deep-equal "^2.0.1" - global "^4.3.2" - lodash "^4.17.15" - memoizerific "^1.11.3" - prop-types "^15.6.2" - react "^16.8.3" - semver "^6.0.0" - shallow-equal "^1.1.0" - store2 "^2.7.1" - telejson "^3.2.0" - util-deprecate "^1.0.2" - -"@storybook/api@6.0.12": - version "6.0.12" - resolved "https://registry.npmjs.org/@storybook/api/-/api-6.0.12.tgz#d6ded5c9ac8f989c4915e11a80a4db69341fc95f" - integrity sha512-8+jPtfhUVM1hT22OT4rjHRxkW924gbWrAxCFYUXOw80a0x7BcT4sL2ah1D4FWf0IpCT/onLf9jLvSVXr8V0xOw== +"@storybook/api@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/api/-/api-6.0.21.tgz#a25a1eb4d07dc43500e03c856db43baba46726f1" + integrity sha512-cRRGf/KGFwYiDouTouEcDdp45N1AbYnAfvLqYZ3KuUTGZ+CiU/PN/vavkp07DQeM4FIQO8TLhzHdsLFpLT7Lkw== dependencies: "@reach/router" "^1.3.3" - "@storybook/channels" "6.0.12" - "@storybook/client-logger" "6.0.12" - "@storybook/core-events" "6.0.12" + "@storybook/channels" "6.0.21" + "@storybook/client-logger" "6.0.21" + "@storybook/core-events" "6.0.21" "@storybook/csf" "0.0.1" - "@storybook/router" "6.0.12" + "@storybook/router" "6.0.21" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.0.12" + "@storybook/theming" "6.0.21" "@types/reach__router" "^1.3.5" core-js "^3.0.1" fast-deep-equal "^3.1.1" @@ -3133,159 +3263,166 @@ ts-dedent "^1.1.1" util-deprecate "^1.0.2" -"@storybook/channel-postmessage@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-5.3.19.tgz#ef9fe974c2a529d89ce342ff7acf5cc22805bae9" - integrity sha512-Iq0f4NPHR0UVVFCWt0cI7Myadk4/SATXYJPT6sv95KhnLjKEeYw571WBlThfp8a9FM80887xG+eIRe93c8dleA== +"@storybook/channel-postmessage@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.0.21.tgz#97e8f43c1b66f84c7b8271e447d45d4f66d355d1" + integrity sha512-ArRnoaS+b7qpAku/SO27z/yjRDCXb37mCPYGX0ntPbiQajootUbGO7otfnjFkaP44hCEC9uDYlOfMU1hYU1N6A== dependencies: - "@storybook/channels" "5.3.19" - "@storybook/client-logger" "5.3.19" + "@storybook/channels" "6.0.21" + "@storybook/client-logger" "6.0.21" + "@storybook/core-events" "6.0.21" core-js "^3.0.1" global "^4.3.2" - telejson "^3.2.0" + qs "^6.6.0" + telejson "^5.0.2" -"@storybook/channels@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/channels/-/channels-5.3.19.tgz#65ad7cd19d70aa5eabbb2e5e39ceef5e510bcb7f" - integrity sha512-38seaeyshRGotTEZJppyYMg/Vx2zRKgFv1L6uGqkJT0LYoNSYtJhsiNFCJ2/KUJu2chAJ/j8h80bpVBVLQ/+WA== - dependencies: - core-js "^3.0.1" - -"@storybook/channels@6.0.12": - version "6.0.12" - resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.0.12.tgz#377f8049272f1862f9235a5051d95124d53fa08e" - integrity sha512-0EMtjde4tRrBnJj5jOXSgtMYfMxGZgoe/0hvVSJuOABf0FY5x6xrqNNDfory7+TtgieuoQE4idl2/tdHE6QJJA== +"@storybook/channels@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.0.21.tgz#bc0951efacbaa5f8827693fba4fe7c2290b5772c" + integrity sha512-G6gjcEotSwDmOlxSmOMgsO3VhQ42RLJK7kFp6D5eg0Q6S8vsypltdT8orxdu+6+AbcBrL+5Sla8lThzaCvXsVQ== dependencies: core-js "^3.0.1" ts-dedent "^1.1.1" util-deprecate "^1.0.2" -"@storybook/client-api@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-5.3.19.tgz#7a5630bb8fffb92742b1773881e9004ee7fdf8e0" - integrity sha512-Dh8ZLrLH91j9Fa28Gmp0KFUvvgK348aNMrDNAUdj4m4witz/BWQ2pxz6qq9/xFVErk/GanVC05kazGElqgYCRQ== +"@storybook/client-api@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.0.21.tgz#6a652dea67d219a31d18af0e05b9f17ba6c7c316" + integrity sha512-emBXd/ml6pc3G8gP3MsR9zQsAq1zZbqof9MxB51tG/jpTXdqWQ8ce1pt1tJS8Xj0QDM072jR6wsY+mmro0GZnA== dependencies: - "@storybook/addons" "5.3.19" - "@storybook/channel-postmessage" "5.3.19" - "@storybook/channels" "5.3.19" - "@storybook/client-logger" "5.3.19" - "@storybook/core-events" "5.3.19" + "@storybook/addons" "6.0.21" + "@storybook/channel-postmessage" "6.0.21" + "@storybook/channels" "6.0.21" + "@storybook/client-logger" "6.0.21" + "@storybook/core-events" "6.0.21" "@storybook/csf" "0.0.1" - "@types/webpack-env" "^1.15.0" + "@types/qs" "^6.9.0" + "@types/webpack-env" "^1.15.2" core-js "^3.0.1" - eventemitter3 "^4.0.0" global "^4.3.2" - is-plain-object "^3.0.0" lodash "^4.17.15" memoizerific "^1.11.3" qs "^6.6.0" stable "^0.1.8" - ts-dedent "^1.1.0" + store2 "^2.7.1" + ts-dedent "^1.1.1" util-deprecate "^1.0.2" -"@storybook/client-logger@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-5.3.19.tgz#fbbd186e82102eaca1d6a5cca640271cae862921" - integrity sha512-nHftT9Ow71YgAd2/tsu79kwKk30mPuE0sGRRUHZVyCRciGFQweKNOS/6xi2Aq+WwBNNjPKNlbgxwRt1yKe1Vkg== - dependencies: - core-js "^3.0.1" - -"@storybook/client-logger@6.0.12": - version "6.0.12" - resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.0.12.tgz#33b4c3cd1f1d98dab32d8c8c906301f1ab18f969" - integrity sha512-MEFDlBbbqcivF/Xmxitx/ky8kxN7TVBZ7K754/pPEI5q6UW32DecJIRg79UWp/1nBPMX/A0U3ORwv+0MjgDZBQ== +"@storybook/client-logger@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.0.21.tgz#20369addf9eb79fc0c85a2e0dcb48f5a1a544532" + integrity sha512-8aUEbhjXV+UMYQWukVYnp+kZafF+LD4Dm7eMo37IUZvt3VIjV1VvhxIDVJtqjk2vv0KZTepESFBkZQLmBzI9Zg== dependencies: core-js "^3.0.1" global "^4.3.2" -"@storybook/components@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/components/-/components-5.3.19.tgz#aac1f9eea1247cc85bd93b10fca803876fb84a6b" - integrity sha512-3g23/+ktlocaHLJKISu9Neu3XKa6aYP2ctDYkRtGchSB0Q55hQsUVGO+BEVuT7Pk2D59mVCxboBjxcRoPUY4pw== +"@storybook/components@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/components/-/components-6.0.21.tgz#2f355370f993e0b7b9062094a03dffc2cdda91db" + integrity sha512-r6btqFW/rcXIU5v231EifZfdh9O0fy7bJDXwwDf8zVUgLx8JRc0VnSs3nvK3Is9HF1wZ9vjx/7Lh4rTIDZAjgg== dependencies: - "@storybook/client-logger" "5.3.19" - "@storybook/theming" "5.3.19" + "@storybook/client-logger" "6.0.21" + "@storybook/csf" "0.0.1" + "@storybook/theming" "6.0.21" + "@types/overlayscrollbars" "^1.9.0" + "@types/react-color" "^3.0.1" "@types/react-syntax-highlighter" "11.0.4" - "@types/react-textarea-autosize" "^4.3.3" core-js "^3.0.1" + fast-deep-equal "^3.1.1" global "^4.3.2" lodash "^4.17.15" markdown-to-jsx "^6.11.4" memoizerific "^1.11.3" - polished "^3.3.1" + overlayscrollbars "^1.10.2" + polished "^3.4.4" popper.js "^1.14.7" - prop-types "^15.7.2" react "^16.8.3" + react-color "^2.17.0" react-dom "^16.8.3" - react-focus-lock "^2.1.0" - react-helmet-async "^1.0.2" - react-popper-tooltip "^2.8.3" - react-syntax-highlighter "^11.0.2" - react-textarea-autosize "^7.1.0" - simplebar-react "^1.0.0-alpha.6" - ts-dedent "^1.1.0" + react-popper-tooltip "^2.11.0" + react-syntax-highlighter "^12.2.1" + react-textarea-autosize "^8.1.1" + ts-dedent "^1.1.1" -"@storybook/core-events@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-5.3.19.tgz#18020cd52e0d8ef0973a8e9622a10d5f99796f79" - integrity sha512-lh78ySqMS7pDdMJAQAe35d1I/I4yPTqp09Cq0YIYOxx9BQZhah4DZTV1QIZt22H5p2lPb5MWLkWSxBaexZnz8A== +"@storybook/core-events@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.0.21.tgz#2ce51e6d7524e7543dbb29571beac1dbeb4e5f40" + integrity sha512-p84fbPcsAhnqDhp+HJ4P8+vI2BqJus4IRoVAemLAwuPjyPElrV9UvOa/RHy1BN8Z6jXwFA+FFzfGl2kPJ3WYcA== dependencies: core-js "^3.0.1" -"@storybook/core-events@6.0.12": - version "6.0.12" - resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.0.12.tgz#499ae06092103b149fcd9417d8e0baa356adc2c0" - integrity sha512-52yNnp+dBkHiG9S+rQO7Nv3PdSDi0XnBt7FoQ+v8H31vGpgdBLEhy8w5ZA4eTrL951VaU/4/XoOaG2+yPALaoA== +"@storybook/core@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/core/-/core-6.0.21.tgz#105c2b90ab27e7b478cb1b7d10e9fe5aba5e0708" + integrity sha512-/Et5NLabB12dnuPdhHDA/Q1pj0Mm2DGdL3KiLO4IC2VZeICCLGmU3/EGJBgjLK+anQ59pkclOiQ8i9eMXFiJ6A== dependencies: - core-js "^3.0.1" - -"@storybook/core@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/core/-/core-5.3.19.tgz#1e61f35c5148343a0c580f5d5efb77f3b4243a30" - integrity sha512-4EYzglqb1iD6x9gxtAYpRGwGP6qJGiU2UW4GiYrErEmeu6y6tkyaqW5AwGlIo9+6jAfwD0HjaK8afvjKTtmmMQ== - dependencies: - "@babel/plugin-proposal-class-properties" "^7.7.0" - "@babel/plugin-proposal-object-rest-spread" "^7.6.2" - "@babel/plugin-syntax-dynamic-import" "^7.2.0" - "@babel/plugin-transform-react-constant-elements" "^7.2.0" - "@babel/preset-env" "^7.4.5" - "@storybook/addons" "5.3.19" - "@storybook/channel-postmessage" "5.3.19" - "@storybook/client-api" "5.3.19" - "@storybook/client-logger" "5.3.19" - "@storybook/core-events" "5.3.19" + "@babel/plugin-proposal-class-properties" "^7.8.3" + "@babel/plugin-proposal-decorators" "^7.8.3" + "@babel/plugin-proposal-export-default-from" "^7.8.3" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.1" + "@babel/plugin-proposal-object-rest-spread" "^7.9.6" + "@babel/plugin-proposal-optional-chaining" "^7.10.1" + "@babel/plugin-proposal-private-methods" "^7.8.3" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-arrow-functions" "^7.8.3" + "@babel/plugin-transform-block-scoping" "^7.8.3" + "@babel/plugin-transform-classes" "^7.9.5" + "@babel/plugin-transform-destructuring" "^7.9.5" + "@babel/plugin-transform-for-of" "^7.9.0" + "@babel/plugin-transform-parameters" "^7.9.5" + "@babel/plugin-transform-shorthand-properties" "^7.8.3" + "@babel/plugin-transform-spread" "^7.8.3" + "@babel/plugin-transform-template-literals" "^7.8.3" + "@babel/preset-env" "^7.9.6" + "@babel/preset-react" "^7.8.3" + "@babel/preset-typescript" "^7.9.0" + "@babel/register" "^7.10.5" + "@storybook/addons" "6.0.21" + "@storybook/api" "6.0.21" + "@storybook/channel-postmessage" "6.0.21" + "@storybook/channels" "6.0.21" + "@storybook/client-api" "6.0.21" + "@storybook/client-logger" "6.0.21" + "@storybook/components" "6.0.21" + "@storybook/core-events" "6.0.21" "@storybook/csf" "0.0.1" - "@storybook/node-logger" "5.3.19" - "@storybook/router" "5.3.19" - "@storybook/theming" "5.3.19" - "@storybook/ui" "5.3.19" + "@storybook/node-logger" "6.0.21" + "@storybook/router" "6.0.21" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.0.21" + "@storybook/ui" "6.0.21" + "@types/glob-base" "^0.3.0" + "@types/micromatch" "^4.0.1" + "@types/node-fetch" "^2.5.4" airbnb-js-shims "^2.2.1" ansi-to-html "^0.6.11" autoprefixer "^9.7.2" - babel-plugin-add-react-displayname "^0.0.5" + babel-loader "^8.0.6" babel-plugin-emotion "^10.0.20" - babel-plugin-macros "^2.7.0" + babel-plugin-macros "^2.8.0" babel-preset-minify "^0.5.0 || 0.6.0-alpha.5" + better-opn "^2.0.0" boxen "^4.1.0" case-sensitive-paths-webpack-plugin "^2.2.0" - chalk "^3.0.0" - cli-table3 "0.5.1" - commander "^4.0.1" + chalk "^4.0.0" + cli-table3 "0.6.0" + commander "^5.0.0" core-js "^3.0.1" - corejs-upgrade-webpack-plugin "^2.2.0" - css-loader "^3.0.0" + css-loader "^3.5.3" detect-port "^1.3.0" dotenv-webpack "^1.7.0" - ejs "^2.7.4" + ejs "^3.1.2" express "^4.17.0" - file-loader "^4.2.0" + file-loader "^6.0.0" file-system-cache "^1.0.5" - find-cache-dir "^3.0.0" find-up "^4.1.0" - fs-extra "^8.0.1" + fork-ts-checker-webpack-plugin "^4.1.4" + fs-extra "^9.0.0" + glob "^7.1.6" glob-base "^0.3.0" + glob-promise "^3.4.0" global "^4.3.2" - html-webpack-plugin "^4.0.0-beta.2" + html-webpack-plugin "^4.2.1" inquirer "^7.0.0" interpret "^2.0.0" ip "^1.1.5" @@ -3293,30 +3430,29 @@ lazy-universal-dotenv "^3.0.1" micromatch "^4.0.2" node-fetch "^2.6.0" - open "^7.0.0" - pnp-webpack-plugin "1.5.0" + pkg-dir "^4.2.0" + pnp-webpack-plugin "1.6.4" postcss-flexbugs-fixes "^4.1.0" postcss-loader "^3.0.0" pretty-hrtime "^1.0.3" qs "^6.6.0" - raw-loader "^3.1.0" - react-dev-utils "^9.0.0" + raw-loader "^4.0.1" + react-dev-utils "^10.0.0" regenerator-runtime "^0.13.3" - resolve "^1.11.0" resolve-from "^5.0.0" - semver "^6.0.0" serve-favicon "^2.5.0" shelljs "^0.8.3" - style-loader "^1.0.0" - terser-webpack-plugin "^2.1.2" - ts-dedent "^1.1.0" + stable "^0.1.8" + style-loader "^1.2.1" + terser-webpack-plugin "^3.0.0" + ts-dedent "^1.1.1" unfetch "^4.1.0" - url-loader "^2.0.1" + url-loader "^4.0.0" util-deprecate "^1.0.2" - webpack "^4.33.0" + webpack "^4.43.0" webpack-dev-middleware "^3.7.0" webpack-hot-middleware "^2.25.0" - webpack-virtual-modules "^0.2.0" + webpack-virtual-modules "^0.2.2" "@storybook/csf@0.0.1": version "0.0.1" @@ -3325,64 +3461,47 @@ dependencies: lodash "^4.17.15" -"@storybook/node-logger@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-5.3.19.tgz#c414e4d3781aeb06298715220012f552a36dff29" - integrity sha512-hKshig/u5Nj9fWy0OsyU04yqCxr0A9pydOHIassr4fpLAaePIN2YvqCqE2V+TxQHjZUnowSSIhbXrGt0DI5q2A== +"@storybook/node-logger@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.0.21.tgz#5b8ba589d5cca6a67c69ee8f5258755b7e1dbc08" + integrity sha512-KRBf+Fz7fgtwHdnYt70JTZbcYMZ1pQPtDyqbrFYCjwkbx5GPX5vMOozlxCIj9elseqPIsF8CKgHOW7cFHVyWYw== dependencies: "@types/npmlog" "^4.1.2" - chalk "^3.0.0" + chalk "^4.0.0" core-js "^3.0.1" npmlog "^4.1.2" pretty-hrtime "^1.0.3" - regenerator-runtime "^0.13.3" -"@storybook/react@^5.3.17": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/react/-/react-5.3.19.tgz#ad7e7a5538399e2794cdb5a1b844a2b77c10bd09" - integrity sha512-OBRUqol3YLQi/qE55x2pWkv4YpaAmmfj6/Km+7agx+og+oNQl0nnlXy7r27X/4j3ERczzURa5pJHtSjwiNaJNw== +"@storybook/react@^6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/react/-/react-6.0.21.tgz#68f8a318e9940305b06eb894896624a35a9868b0" + integrity sha512-L3PcoBJq5aK1aTaJNfwsSJ8Kxgcyk0WknN4TDqhP7a+oXmuMY1YEi96hEvQVIm0TBCkQxs61K70/T7vlilEtHg== dependencies: - "@babel/plugin-transform-react-constant-elements" "^7.6.3" "@babel/preset-flow" "^7.0.0" "@babel/preset-react" "^7.0.0" - "@storybook/addons" "5.3.19" - "@storybook/core" "5.3.19" - "@storybook/node-logger" "5.3.19" - "@svgr/webpack" "^4.0.3" - "@types/webpack-env" "^1.15.0" + "@storybook/addons" "6.0.21" + "@storybook/core" "6.0.21" + "@storybook/node-logger" "6.0.21" + "@storybook/semver" "^7.3.2" + "@svgr/webpack" "^5.4.0" + "@types/webpack-env" "^1.15.2" babel-plugin-add-react-displayname "^0.0.5" babel-plugin-named-asset-import "^0.3.1" - babel-plugin-react-docgen "^4.0.0" + babel-plugin-react-docgen "^4.1.0" core-js "^3.0.1" global "^4.3.2" lodash "^4.17.15" - mini-css-extract-plugin "^0.7.0" prop-types "^15.7.2" - react-dev-utils "^9.0.0" + react-dev-utils "^10.0.0" + react-docgen-typescript-plugin "^0.5.2" regenerator-runtime "^0.13.3" - semver "^6.0.0" - ts-dedent "^1.1.0" - webpack "^4.33.0" + ts-dedent "^1.1.1" + webpack "^4.43.0" -"@storybook/router@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/router/-/router-5.3.19.tgz#0f783b85658f99e4007f74347ad7ef17dbf7fc3a" - integrity sha512-yNClpuP7BXQlBTRf6Ggle3/R349/k6kvI5Aim4jf6X/2cFVg2pzBXDAF41imNm9PcvdxwabQLm6I48p7OvKr/w== - dependencies: - "@reach/router" "^1.2.1" - "@storybook/csf" "0.0.1" - "@types/reach__router" "^1.2.3" - core-js "^3.0.1" - global "^4.3.2" - lodash "^4.17.15" - memoizerific "^1.11.3" - qs "^6.6.0" - util-deprecate "^1.0.2" - -"@storybook/router@6.0.12": - version "6.0.12" - resolved "https://registry.npmjs.org/@storybook/router/-/router-6.0.12.tgz#f66d979ec01e15c11a378eb5dde3e696747ac184" - integrity sha512-vv1jHOOGelSzmDJnp9SdC/KR5RpE2am568ImOAQ9/XCmXNDhVshVlIS7ajy6yCKN/mS/63zKflbRNef+3SLU9Q== +"@storybook/router@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/router/-/router-6.0.21.tgz#0f22261d4782c72a5a13e80cfcd8d50aed1f98c6" + integrity sha512-46SsKJfcd12lRrISnfrWhicJx8EylkgGDGohfH0n5p7inkkGOkKV8QFZoYPRKZueMXmUKpzJ0Z3HmVsLTCrCDw== dependencies: "@reach/router" "^1.3.3" "@types/reach__router" "^1.3.5" @@ -3399,49 +3518,31 @@ core-js "^3.6.5" find-up "^4.1.0" -"@storybook/source-loader@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-5.3.19.tgz#ff0a00731c24c61721d8b9d84152f8542913a3b7" - integrity sha512-srSZRPgEOUse8nRVnlazweB2QGp63mPqM0uofg8zYARyaYSOzkC155ymdeiHsmiBTS3X3I0FQE4+KnwiH7iLtw== +"@storybook/source-loader@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-6.0.21.tgz#f4ae0fa3f3d119f9ace1d3364df21f8f7cf65bd7" + integrity sha512-Duzpz8udadR7wzH8/4F3GnMRe23oBOxTm4jBZw/T8NA+HqBtd9Y16swWw4BfwsRwfdZS4EVw3PtWgsAfoqF7ow== dependencies: - "@storybook/addons" "5.3.19" - "@storybook/client-logger" "5.3.19" + "@storybook/addons" "6.0.21" + "@storybook/client-logger" "6.0.21" "@storybook/csf" "0.0.1" core-js "^3.0.1" estraverse "^4.2.0" global "^4.3.2" - loader-utils "^1.2.3" - prettier "^1.16.4" - prop-types "^15.7.2" + loader-utils "^2.0.0" + lodash "^4.17.15" + prettier "~2.0.5" regenerator-runtime "^0.13.3" -"@storybook/theming@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/theming/-/theming-5.3.19.tgz#177d9819bd64f7a1a6ea2f1920ffa5baf9a5f467" - integrity sha512-ecG+Rq3hc1GOzKHamYnD4wZ0PEP9nNg0mXbC3RhbxfHj+pMMCWWmx9B2Uu75SL1PTT8WcfkFO0hU/0IO84Pzlg== - dependencies: - "@emotion/core" "^10.0.20" - "@emotion/styled" "^10.0.17" - "@storybook/client-logger" "5.3.19" - core-js "^3.0.1" - deep-object-diff "^1.1.0" - emotion-theming "^10.0.19" - global "^4.3.2" - memoizerific "^1.11.3" - polished "^3.3.1" - prop-types "^15.7.2" - resolve-from "^5.0.0" - ts-dedent "^1.1.0" - -"@storybook/theming@6.0.12": - version "6.0.12" - resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.0.12.tgz#83b2099e7a7a5bd3acadb8e4106234ede62197c7" - integrity sha512-hmF6EIbm2A7G84+JR36UQWteElSwSNfGLzccAlUMiZIhdMG0SuCtyHe6FmckAWC226Mv+MW14fr+a4+OuRpM4g== +"@storybook/theming@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.0.21.tgz#d56051c0b8679c2b701ce08385660ab4146cf15f" + integrity sha512-n97DfB9kG6WrV1xBGDyeQibTrh8pBBCp3dSL3UTGH+KX3C2+4sm6QHlTgyekbi5FrbFEbnuZOKAS3YbLVONsRQ== dependencies: "@emotion/core" "^10.0.20" "@emotion/is-prop-valid" "^0.8.6" "@emotion/styled" "^10.0.17" - "@storybook/client-logger" "6.0.12" + "@storybook/client-logger" "6.0.21" core-js "^3.0.1" deep-object-diff "^1.1.0" emotion-theming "^10.0.19" @@ -3451,32 +3552,32 @@ resolve-from "^5.0.0" ts-dedent "^1.1.1" -"@storybook/ui@5.3.19": - version "5.3.19" - resolved "https://registry.npmjs.org/@storybook/ui/-/ui-5.3.19.tgz#ac03b67320044a3892ee784111d4436b61874332" - integrity sha512-r0VxdWab49nm5tzwvveVDnsHIZHMR76veYOu/NHKDUZ5hnQl1LMG1YyMCFFa7KiwD/OrZxRWr6/Ma7ep9kR4Gw== +"@storybook/ui@6.0.21": + version "6.0.21" + resolved "https://registry.npmjs.org/@storybook/ui/-/ui-6.0.21.tgz#5dac2b68a30f5dba5457e0315f58977e07138968" + integrity sha512-50QYF8tHUgpVq7B7PWp7kmyf79NySWJO0piQFjHv027vV8GfbXMWVswAXwo3IfCihPlnLKe01WbsigM/9T1HCQ== dependencies: "@emotion/core" "^10.0.20" - "@storybook/addons" "5.3.19" - "@storybook/api" "5.3.19" - "@storybook/channels" "5.3.19" - "@storybook/client-logger" "5.3.19" - "@storybook/components" "5.3.19" - "@storybook/core-events" "5.3.19" - "@storybook/router" "5.3.19" - "@storybook/theming" "5.3.19" + "@storybook/addons" "6.0.21" + "@storybook/api" "6.0.21" + "@storybook/channels" "6.0.21" + "@storybook/client-logger" "6.0.21" + "@storybook/components" "6.0.21" + "@storybook/core-events" "6.0.21" + "@storybook/router" "6.0.21" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.0.21" + "@types/markdown-to-jsx" "^6.11.0" copy-to-clipboard "^3.0.8" core-js "^3.0.1" core-js-pure "^3.0.1" emotion-theming "^10.0.19" - fast-deep-equal "^2.0.1" - fuse.js "^3.4.6" + fuse.js "^3.6.1" global "^4.3.2" lodash "^4.17.15" markdown-to-jsx "^6.11.4" memoizerific "^1.11.3" - polished "^3.3.1" - prop-types "^15.7.2" + polished "^3.4.4" qs "^6.6.0" react "^16.8.3" react-dom "^16.8.3" @@ -3484,12 +3585,9 @@ react-helmet-async "^1.0.2" react-hotkeys "2.0.0" react-sizeme "^2.6.7" - regenerator-runtime "^0.13.2" + regenerator-runtime "^0.13.3" resolve-from "^5.0.0" - semver "^6.0.0" store2 "^2.7.1" - telejson "^3.2.0" - util-deprecate "^1.0.2" "@styled-system/background@^5.1.2": version "5.1.2" @@ -3597,100 +3695,46 @@ dependencies: loader-utils "^1.1.0" -"@svgr/babel-plugin-add-jsx-attribute@^4.2.0": - version "4.2.0" - resolved "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1" - integrity sha512-j7KnilGyZzYr/jhcrSYS3FGWMZVaqyCG0vzMCwzvei0coIkczuYMcniK07nI0aHJINciujjH11T72ICW5eL5Ig== - "@svgr/babel-plugin-add-jsx-attribute@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" integrity sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg== -"@svgr/babel-plugin-remove-jsx-attribute@^4.2.0": - version "4.2.0" - resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-4.2.0.tgz#297550b9a8c0c7337bea12bdfc8a80bb66f85abc" - integrity sha512-3XHLtJ+HbRCH4n28S7y/yZoEQnRpl0tvTZQsHqvaeNXPra+6vE5tbRliH3ox1yZYPCxrlqaJT/Mg+75GpDKlvQ== - "@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz#6b2c770c95c874654fd5e1d5ef475b78a0a962ef" integrity sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg== -"@svgr/babel-plugin-remove-jsx-empty-expression@^4.2.0": - version "4.2.0" - resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-4.2.0.tgz#c196302f3e68eab6a05e98af9ca8570bc13131c7" - integrity sha512-yTr2iLdf6oEuUE9MsRdvt0NmdpMBAkgK8Bjhl6epb+eQWk6abBaX3d65UZ3E3FWaOwePyUgNyNCMVG61gGCQ7w== - "@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": version "5.0.1" resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz#25621a8915ed7ad70da6cea3d0a6dbc2ea933efd" integrity sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA== -"@svgr/babel-plugin-replace-jsx-attribute-value@^4.2.0": - version "4.2.0" - resolved "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-4.2.0.tgz#310ec0775de808a6a2e4fd4268c245fd734c1165" - integrity sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w== - "@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": version "5.0.1" resolved "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz#0b221fc57f9fcd10e91fe219e2cd0dd03145a897" integrity sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ== -"@svgr/babel-plugin-svg-dynamic-title@^4.3.3": - version "4.3.3" - resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.3.tgz#2cdedd747e5b1b29ed4c241e46256aac8110dd93" - integrity sha512-w3Be6xUNdwgParsvxkkeZb545VhXEwjGMwExMVBIdPQJeyMQHqm9Msnb2a1teHBqUYL66qtwfhNkbj1iarCG7w== - "@svgr/babel-plugin-svg-dynamic-title@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz#139b546dd0c3186b6e5db4fefc26cb0baea729d7" integrity sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg== -"@svgr/babel-plugin-svg-em-dimensions@^4.2.0": - version "4.2.0" - resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-4.2.0.tgz#9a94791c9a288108d20a9d2cc64cac820f141391" - integrity sha512-C0Uy+BHolCHGOZ8Dnr1zXy/KgpBOkEUYY9kI/HseHVPeMbluaX3CijJr7D4C5uR8zrc1T64nnq/k63ydQuGt4w== - "@svgr/babel-plugin-svg-em-dimensions@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz#6543f69526632a133ce5cabab965deeaea2234a0" integrity sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw== -"@svgr/babel-plugin-transform-react-native-svg@^4.2.0": - version "4.2.0" - resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-4.2.0.tgz#151487322843359a1ca86b21a3815fd21a88b717" - integrity sha512-7YvynOpZDpCOUoIVlaaOUU87J4Z6RdD6spYN4eUb5tfPoKGSF9OG2NuhgYnq4jSkAxcpMaXWPf1cePkzmqTPNw== - "@svgr/babel-plugin-transform-react-native-svg@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz#00bf9a7a73f1cad3948cdab1f8dfb774750f8c80" integrity sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q== -"@svgr/babel-plugin-transform-svg-component@^4.2.0": - version "4.2.0" - resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-4.2.0.tgz#5f1e2f886b2c85c67e76da42f0f6be1b1767b697" - integrity sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw== - "@svgr/babel-plugin-transform-svg-component@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.4.0.tgz#a2212b4d018e6075a058bb7e220a66959ef7a03c" integrity sha512-zLl4Fl3NvKxxjWNkqEcpdSOpQ3LGVH2BNFQ6vjaK6sFo2IrSznrhURIPI0HAphKiiIwNYjAfE0TNoQDSZv0U9A== -"@svgr/babel-preset@^4.3.3": - version "4.3.3" - resolved "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-4.3.3.tgz#a75d8c2f202ac0e5774e6bfc165d028b39a1316c" - integrity sha512-6PG80tdz4eAlYUN3g5GZiUjg2FMcp+Wn6rtnz5WJG9ITGEF1pmFdzq02597Hn0OmnQuCVaBYQE1OVFAnwOl+0A== - dependencies: - "@svgr/babel-plugin-add-jsx-attribute" "^4.2.0" - "@svgr/babel-plugin-remove-jsx-attribute" "^4.2.0" - "@svgr/babel-plugin-remove-jsx-empty-expression" "^4.2.0" - "@svgr/babel-plugin-replace-jsx-attribute-value" "^4.2.0" - "@svgr/babel-plugin-svg-dynamic-title" "^4.3.3" - "@svgr/babel-plugin-svg-em-dimensions" "^4.2.0" - "@svgr/babel-plugin-transform-react-native-svg" "^4.2.0" - "@svgr/babel-plugin-transform-svg-component" "^4.2.0" - "@svgr/babel-preset@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.4.0.tgz#da21854643e1c4ad2279239baa7d5a8b128c1f15" @@ -3705,15 +3749,6 @@ "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" "@svgr/babel-plugin-transform-svg-component" "^5.4.0" -"@svgr/core@^4.3.3": - version "4.3.3" - resolved "https://registry.npmjs.org/@svgr/core/-/core-4.3.3.tgz#b37b89d5b757dc66e8c74156d00c368338d24293" - integrity sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w== - dependencies: - "@svgr/plugin-jsx" "^4.3.3" - camelcase "^5.3.1" - cosmiconfig "^5.2.1" - "@svgr/core@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/core/-/core-5.4.0.tgz#655378ee43679eb94fee3d4e1976e38252dff8e7" @@ -3723,13 +3758,6 @@ camelcase "^6.0.0" cosmiconfig "^6.0.0" -"@svgr/hast-util-to-babel-ast@^4.3.2": - version "4.3.2" - resolved "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.3.2.tgz#1d5a082f7b929ef8f1f578950238f630e14532b8" - integrity sha512-JioXclZGhFIDL3ddn4Kiq8qEqYM2PyDKV0aYno8+IXTLuYt6TOgHUbUAAFvqtb0Xn37NwP0BTHglejFoYr8RZg== - dependencies: - "@babel/types" "^7.4.4" - "@svgr/hast-util-to-babel-ast@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.4.0.tgz#bb5d002e428f510aa5b53ec0a02377a95b367715" @@ -3747,17 +3775,7 @@ "@svgr/hast-util-to-babel-ast" "^5.4.0" svg-parser "^2.0.2" -"@svgr/plugin-jsx@^4.3.3": - version "4.3.3" - resolved "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-4.3.3.tgz#e2ba913dbdfbe85252a34db101abc7ebd50992fa" - integrity sha512-cLOCSpNWQnDB1/v+SUENHH7a0XY09bfuMKdq9+gYvtuwzC2rU4I0wKGFEp1i24holdQdwodCtDQdFtJiTCWc+w== - dependencies: - "@babel/core" "^7.4.5" - "@svgr/babel-preset" "^4.3.3" - "@svgr/hast-util-to-babel-ast" "^4.3.2" - svg-parser "^2.0.0" - -"@svgr/plugin-svgo@4.3.x", "@svgr/plugin-svgo@^4.3.1": +"@svgr/plugin-svgo@4.3.x": version "4.3.1" resolved "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-4.3.1.tgz#daac0a3d872e3f55935c6588dd370336865e9e32" integrity sha512-PrMtEDUWjX3Ea65JsVCwTIXuSqa3CG9px+DluF1/eo9mlDrgrtFE7NE/DjdhjJgSM9wenlVBzkzneSIUgfUI/w== @@ -3789,7 +3807,7 @@ "@svgr/plugin-svgo" "^5.4.0" rollup-pluginutils "^2.8.2" -"@svgr/webpack@5.4.x": +"@svgr/webpack@5.4.x", "@svgr/webpack@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.4.0.tgz#b68bc86e29cf007292b96ced65f80971175632e0" integrity sha512-LjepnS/BSAvelnOnnzr6Gg0GcpLmnZ9ThGFK5WJtm1xOqdBE/1IACZU7MMdVzjyUkfFqGz87eRE4hFaSLiUwYg== @@ -3803,20 +3821,6 @@ "@svgr/plugin-svgo" "^5.4.0" loader-utils "^2.0.0" -"@svgr/webpack@^4.0.3": - version "4.3.3" - resolved "https://registry.npmjs.org/@svgr/webpack/-/webpack-4.3.3.tgz#13cc2423bf3dff2d494f16b17eb7eacb86895017" - integrity sha512-bjnWolZ6KVsHhgyCoYRFmbd26p8XVbulCzSG53BDQqAr+JOAderYK7CuYrB3bDjHJuF6LJ7Wrr42+goLRV9qIg== - dependencies: - "@babel/core" "^7.4.5" - "@babel/plugin-transform-react-constant-elements" "^7.0.0" - "@babel/preset-env" "^7.4.5" - "@babel/preset-react" "^7.0.0" - "@svgr/core" "^4.3.3" - "@svgr/plugin-jsx" "^4.3.3" - "@svgr/plugin-svgo" "^4.3.1" - loader-utils "^1.2.3" - "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -4013,6 +4017,11 @@ "@types/connect" "*" "@types/node" "*" +"@types/braces@*": + version "3.0.0" + resolved "https://registry.npmjs.org/@types/braces/-/braces-3.0.0.tgz#7da1c0d44ff1c7eb660a36ec078ea61ba7eb42cb" + integrity sha512-TbH79tcyi9FHwbyboOKeRachRq63mSuWYXOflsNO9ZyE5ClQ/JaozNKl+aWUq87qPNsXasXxi2AbgfwIJ+8GQw== + "@types/cacheable-request@^6.0.1": version "6.0.1" resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" @@ -4222,6 +4231,19 @@ resolved "https://registry.npmjs.org/@types/github-slugger/-/github-slugger-1.3.0.tgz#16ab393b30d8ae2a111ac748a015ac05a1fc5524" integrity sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g== +"@types/glob-base@^0.3.0": + version "0.3.0" + resolved "https://registry.npmjs.org/@types/glob-base/-/glob-base-0.3.0.tgz#a581d688347e10e50dd7c17d6f2880a10354319d" + integrity sha1-pYHWiDR+EOUN18F9byiAoQNUMZ0= + +"@types/glob@*": + version "7.1.3" + resolved "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" + integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + "@types/glob@^7.1.1": version "7.1.1" resolved "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" @@ -4436,6 +4458,20 @@ resolved "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== +"@types/markdown-to-jsx@^6.11.0": + version "6.11.2" + resolved "https://registry.npmjs.org/@types/markdown-to-jsx/-/markdown-to-jsx-6.11.2.tgz#05d1aaffbf15be7be12c70535fa4fed65cc7c64f" + integrity sha512-ESuCu8Bk7jpTZ3YPdMW1+6wUj13F5N15vXfc7BuUAN0eCp0lrvVL9nzOTzoqvbRzXMciuqXr1KrHt3xQAhfwPA== + dependencies: + "@types/react" "*" + +"@types/micromatch@^4.0.1": + version "4.0.1" + resolved "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.1.tgz#9381449dd659fc3823fd2a4190ceacc985083bc7" + integrity sha512-my6fLBvpY70KattTNzYOK6KU1oR1+UCz9ug/JbcF5UrEmeCt9P7DV2t7L8+t18mMPINqGQCE4O8PLOPbI84gxw== + dependencies: + "@types/braces" "*" + "@types/mime@*": version "2.0.1" resolved "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" @@ -4467,7 +4503,7 @@ dependencies: "@types/node" "*" -"@types/node-fetch@2.5.7", "@types/node-fetch@^2.5.7": +"@types/node-fetch@2.5.7", "@types/node-fetch@^2.5.4", "@types/node-fetch@^2.5.7": version "2.5.7" resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" integrity sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw== @@ -4526,6 +4562,11 @@ dependencies: ora "*" +"@types/overlayscrollbars@^1.9.0": + version "1.12.0" + resolved "https://registry.npmjs.org/@types/overlayscrollbars/-/overlayscrollbars-1.12.0.tgz#98456caceca8ad73bd5bb572632a585074e70764" + integrity sha512-h/pScHNKi4mb+TrJGDon8Yb06ujFG0mSg12wIO0sWMUF3dQIe2ExRRdNRviaNt9IjxIiOfnRr7FsQAdHwK4sMg== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -4595,7 +4636,7 @@ resolved "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== -"@types/qs@*": +"@types/qs@*", "@types/qs@^6.9.0": version "6.9.4" resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz#a59e851c1ba16c0513ea123830dd639a0a15cb6a" integrity sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ== @@ -4605,7 +4646,7 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== -"@types/reach__router@^1.2.3", "@types/reach__router@^1.3.5": +"@types/reach__router@^1.3.5": version "1.3.5" resolved "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.5.tgz#14e1e981cccd3a5e50dc9e969a72de0b9d472f6d" integrity sha512-h0NbqXN/tJuBY/xggZSej1SKQEstbHO7J/omt1tYoFGmj3YXOodZKbbqD4mNDh7zvEGYd7YFrac1LTtAr3xsYQ== @@ -4613,6 +4654,14 @@ "@types/history" "*" "@types/react" "*" +"@types/react-color@^3.0.1": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.4.tgz#c63daf012ad067ac0127bdd86725f079d02082bd" + integrity sha512-EswbYJDF1kkrx93/YU+BbBtb46CCtDMvTiGmcOa/c5PETnwTiSWoseJ1oSWeRl/4rUXkhME9bVURvvPg0W5YQw== + dependencies: + "@types/react" "*" + "@types/reactcss" "*" + "@types/react-dev-utils@^9.0.4": version "9.0.4" resolved "https://registry.npmjs.org/@types/react-dev-utils/-/react-dev-utils-9.0.4.tgz#3e4bee79b7536777cef219427ab1d38adc24f3f2" @@ -4667,13 +4716,6 @@ dependencies: "@types/react" "*" -"@types/react-textarea-autosize@^4.3.3": - version "4.3.5" - resolved "https://registry.npmjs.org/@types/react-textarea-autosize/-/react-textarea-autosize-4.3.5.tgz#6c4d2753fa1864c98c0b2b517f67bb1f6e4c46de" - integrity sha512-PiDL83kPMTolyZAWW3lyzO6ktooTb9tFTntVy7CA83/qFLWKLJ5bLeRboy6J6j3b1e8h2Eec6gBTEOOJRjV14A== - dependencies: - "@types/react" "*" - "@types/react-transition-group@^4.2.0": version "4.2.4" resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.4.tgz#c7416225987ccdb719262766c1483da8f826838d" @@ -4703,6 +4745,13 @@ dependencies: csstype "^2.2.0" +"@types/reactcss@*": + version "1.2.3" + resolved "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.3.tgz#af28ae11bbb277978b99d04d1eedfd068ca71834" + integrity sha512-d2gQQ0IL6hXLnoRfVYZukQNWHuVsE75DzFTLPUuyyEhJS8G2VvlE+qfQQ91SJjaMqlURRCNIsX7Jcsw6cEuJlA== + dependencies: + "@types/react" "*" + "@types/recursive-readdir@^2.2.0": version "2.2.0" resolved "https://registry.npmjs.org/@types/recursive-readdir/-/recursive-readdir-2.2.0.tgz#b39cd5474fd58ea727fe434d5c68b7a20ba9121c" @@ -4896,7 +4945,7 @@ "@types/serve-static" "*" "@types/webpack" "*" -"@types/webpack-env@^1.15.0", "@types/webpack-env@^1.15.2": +"@types/webpack-env@^1.15.2": version "1.15.2" resolved "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.15.2.tgz#927997342bb9f4a5185a86e6579a0a18afc33b0a" integrity sha512-67ZgZpAlhIICIdfQrB5fnDvaKFcDxpKibxznfYRVAT4mQE41Dido/3Ty+E3xGBmTogc5+0Qb8tWhna+5B8z1iQ== @@ -5165,6 +5214,18 @@ "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" +"@webpack-contrib/schema-utils@^1.0.0-beta.0": + version "1.0.0-beta.0" + resolved "https://registry.npmjs.org/@webpack-contrib/schema-utils/-/schema-utils-1.0.0-beta.0.tgz#bf9638c9464d177b48209e84209e23bee2eb4f65" + integrity sha512-LonryJP+FxQQHsjGBi6W786TQB1Oym+agTpY0c+Kj8alnIw+DLUJb6SI8Y1GHGhLCH1yPRrucjObUmxNICQ1pg== + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chalk "^2.3.2" + strip-ansi "^4.0.0" + text-table "^0.2.0" + webpack-log "^1.1.2" + "@wry/equality@^0.1.2": version "0.1.11" resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.1.11.tgz#35cb156e4a96695aa81a9ecc4d03787bc17f1790" @@ -5352,6 +5413,11 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + ajv@6.5.2: version "6.5.2" resolved "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz#678495f9b82f7cca6be248dd92f59bff5e1f4360" @@ -5382,6 +5448,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.1, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.5.5, ajv@ json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.12.4: + version "6.12.4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -5925,6 +6001,11 @@ async-retry@^1.2.1: dependencies: retry "0.12.0" +async@0.9.x: + version "0.9.2" + resolved "https://registry.npmjs.org/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" + integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= + async@^2.6.1, async@^2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" @@ -6064,6 +6145,17 @@ babel-jest@^26.3.0: graceful-fs "^4.2.4" slash "^3.0.0" +babel-loader@^8.0.6: + version "8.1.0" + resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" + integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== + dependencies: + find-cache-dir "^2.1.0" + loader-utils "^1.4.0" + mkdirp "^0.5.3" + pify "^4.0.1" + schema-utils "^2.6.5" + babel-plugin-add-react-displayname@^0.0.5: version "0.0.5" resolved "https://registry.npmjs.org/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5" @@ -6113,7 +6205,7 @@ babel-plugin-jest-hoist@^26.2.0: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.7.0: +babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.8.0: version "2.8.0" resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== @@ -6203,7 +6295,7 @@ babel-plugin-named-asset-import@^0.3.1: resolved "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.6.tgz#c9750a1b38d85112c9e166bf3ef7c5dbc605f4be" integrity sha512-1aGDUfL1qOOIoqk9QKGIo2lANk+C7ko/fqH0uIyC71x3PEGz0uVP8ISgfEsFuG+FKmjHTvFK/nNM8dowpmUxLA== -babel-plugin-react-docgen@^4.0.0: +babel-plugin-react-docgen@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.1.0.tgz#1dfa447dac9ca32d625a123df5733a9e47287c26" integrity sha512-vzpnBlfGv8XOhJM2zbPyyqw2OLEbelgZZsaaRRTpVwNKuYuc+pUg4+dy7i9gCRms0uOQn4osX571HRcCJMJCmA== @@ -6410,6 +6502,13 @@ before-after-hook@^2.0.0, before-after-hook@^2.1.0: resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== +better-opn@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/better-opn/-/better-opn-2.0.0.tgz#c70d198e51164bdc220306a28a885d9ac7a14c44" + integrity sha512-PPbGRgO/K0LowMHbH/JNvaV3qY3Vt+A2nH28fzJxy16h/DfR5OsVti6ldGl6S9SMsyUqT13sltikiAVtI6tKLA== + dependencies: + open "^7.0.3" + bfj@^7.0.2: version "7.0.2" resolved "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz#1988ce76f3add9ac2913fd8ba47aad9e651bfbb2" @@ -6636,15 +6735,6 @@ browserslist@4.10.0: node-releases "^1.1.52" pkg-up "^3.1.0" -browserslist@4.7.0: - version "4.7.0" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz#9ee89225ffc07db03409f2fee524dc8227458a17" - integrity sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA== - dependencies: - caniuse-lite "^1.0.30000989" - electron-to-chromium "^1.3.247" - node-releases "^1.1.29" - browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.3: version "4.13.0" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz#42556cba011e1b0a2775b611cba6a8eca18e940d" @@ -6807,28 +6897,27 @@ cacache@^12.0.0, cacache@^12.0.2, cacache@^12.0.3: unique-filename "^1.1.1" y18n "^4.0.0" -cacache@^13.0.1: - version "13.0.1" - resolved "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz#a8000c21697089082f85287a1aec6e382024a71c" - integrity sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w== +cacache@^15.0.5: + version "15.0.5" + resolved "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" + integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== dependencies: - chownr "^1.1.2" - figgy-pudding "^3.5.1" + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" fs-minipass "^2.0.0" glob "^7.1.4" - graceful-fs "^4.2.2" infer-owner "^1.0.4" - lru-cache "^5.1.1" - minipass "^3.0.0" + lru-cache "^6.0.0" + minipass "^3.1.1" minipass-collect "^1.0.2" minipass-flush "^1.0.5" minipass-pipeline "^1.2.2" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - p-map "^3.0.0" + mkdirp "^1.0.3" + p-map "^4.0.0" promise-inflight "^1.0.1" - rimraf "^2.7.1" - ssri "^7.0.0" + rimraf "^3.0.2" + ssri "^8.0.0" + tar "^6.0.2" unique-filename "^1.1.1" cache-base@^1.0.1: @@ -6978,11 +7067,6 @@ camelcase@^6.0.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== -can-use-dom@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz#22cc4a34a0abc43950f42c6411024a3f6366b45a" - integrity sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo= - caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -6993,7 +7077,7 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001109: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001109: version "1.0.30001113" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001113.tgz#22016ab55b5a8b04fa00ca342d9ee1b98df48065" integrity sha512-qMvjHiKH21zzM/VDZr6oosO6Ri3U0V2tC015jRXjOecwQCJtsU5zklTNTk31jQbIOP8gha0h1ccM/g0ECP+4BA== @@ -7034,7 +7118,7 @@ caseless@~0.12.0: resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@2.4.2, chalk@^2.0.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -7105,7 +7189,7 @@ check-types@^11.1.1: resolved "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f" integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ== -chokidar@^2.0.4, chokidar@^2.1.8: +chokidar@^2.1.8: version "2.1.8" resolved "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -7139,6 +7223,21 @@ chokidar@^3.2.2, chokidar@^3.3.0, chokidar@^3.3.1: optionalDependencies: fsevents "~2.1.2" +chokidar@^3.4.1: + version "3.4.2" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + chownr@^1.0.1, chownr@^1.1.1, chownr@^1.1.2: version "1.1.4" resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -7234,7 +7333,17 @@ cli-spinners@^2.2.0: resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== -cli-table3@0.5.1, cli-table3@~0.5.1: +cli-table3@0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee" + integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ== + dependencies: + object-assign "^4.1.0" + string-width "^4.2.0" + optionalDependencies: + colors "^1.1.2" + +cli-table3@~0.5.1: version "0.5.1" resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== @@ -7499,7 +7608,7 @@ commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, comm resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^4.0.0, commander@^4.0.1, commander@^4.1.1: +commander@^4.0.0, commander@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== @@ -7865,14 +7974,6 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -corejs-upgrade-webpack-plugin@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/corejs-upgrade-webpack-plugin/-/corejs-upgrade-webpack-plugin-2.2.0.tgz#503293bf1fdcb104918eb40d0294e4776ad6923a" - integrity sha512-J0QMp9GNoiw91Kj/dkIQFZeiCXgXoja/Wlht1SPybxerBWh4NCmb0pOgCv61lrlQZETwvVVfAFAA3IqoEO9aqQ== - dependencies: - resolve-from "^5.0.0" - webpack "^4.38.0" - cors@^2.8.4, cors@^2.8.5: version "2.8.5" resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" @@ -7955,7 +8056,16 @@ cross-fetch@3.0.5, cross-fetch@^3.0.4, cross-fetch@^3.0.5: dependencies: node-fetch "2.6.0" -cross-spawn@6.0.5, cross-spawn@^6.0.0: +cross-spawn@7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" + integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -7966,15 +8076,6 @@ cross-spawn@6.0.5, cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" - integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -8041,7 +8142,7 @@ css-line-break@1.0.1: dependencies: base64-arraybuffer "^0.1.5" -css-loader@^3.0.0, css-loader@^3.5.3: +css-loader@^3.5.3: version "3.6.0" resolved "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" integrity sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ== @@ -9053,12 +9154,14 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -ejs@^2.7.4: - version "2.7.4" - resolved "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" - integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== +ejs@^3.1.2: + version "3.1.5" + resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.5.tgz#aed723844dc20acb4b170cd9ab1017e476a0d93b" + integrity sha512-dldq3ZfFtgVTJMLjOe+/3sROTzALlL9E34V4/sDtUd/KlBSS0s6U1/+WPE1B4sj9CXHJpL1M6rhNJnc9Wbal9w== + dependencies: + jake "^10.6.1" -electron-to-chromium@^1.3.247, electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.488: +electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.488: version "1.3.509" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.509.tgz#830fcb89cd66dc2984d18d794973b99e3f00584c" integrity sha512-cN4lkjNRuTG8rtAqTOVgwpecEC2kbKA04PG6YijcKGHK/kD0xLjiqExcAOmLUwtXZRF8cBeam2I0VZcih919Ug== @@ -9158,6 +9261,15 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" +endent@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/endent/-/endent-2.0.1.tgz#fb18383a3f37ae3213a5d9f6c4a880d1061eb4c5" + integrity sha512-mADztvcC+vCk4XEZaCz6xIPO2NHQuprv5CAEjuVAu6aZwqAj7nVNlMyl1goPFYqCCpS2OJV9jwpumJLkotZrNw== + dependencies: + dedent "^0.7.0" + fast-json-parse "^1.0.3" + objectorarray "^1.0.4" + enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: version "4.1.1" resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" @@ -9167,6 +9279,15 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: memory-fs "^0.5.0" tapable "^1.0.0" +enhanced-resolve@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" + integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + enquirer@^2.3.5: version "2.3.5" resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.5.tgz#3ab2b838df0a9d8ab9e7dff235b0e8712ef92381" @@ -9347,16 +9468,16 @@ escape-html@^1.0.3, escape-html@~1.0.3: resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + escodegen@^1.14.1, escodegen@^1.6.1, escodegen@^1.9.1: version "1.14.3" resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" @@ -9962,6 +10083,11 @@ fast-glob@^3.0.3, fast-glob@^3.1.1: micromatch "^4.0.2" picomatch "^2.2.1" +fast-json-parse@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" + integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw== + fast-json-patch@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.2.1.tgz#18150d36c9ab65c7209e7d4eb113f4f8eaabe6d9" @@ -10092,13 +10218,13 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" -file-loader@^4.2.0: - version "4.3.0" - resolved "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz#780f040f729b3d18019f20605f723e844b8a58af" - integrity sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA== +file-loader@^6.0.0: + version "6.1.0" + resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.1.0.tgz#65b9fcfb0ea7f65a234a1f10cdd7f1ab9a33f253" + integrity sha512-26qPdHyTsArQ6gU4P1HJbAbnFTyT2r0pG7czh1GFAd9TZbj0n94wWbupgixZH/ET/meqi2/5+F7DhW4OAXD+Lg== dependencies: - loader-utils "^1.2.3" - schema-utils "^2.5.0" + loader-utils "^2.0.0" + schema-utils "^2.7.1" file-saver@eligrey/FileSaver.js#1.3.8: version "1.3.8" @@ -10123,10 +10249,12 @@ filefy@0.1.10: resolved "https://registry.npmjs.org/filefy/-/filefy-0.1.10.tgz#174677c8e2fa5bc39a3af0ed6fb492f16b8fbf42" integrity sha512-VgoRVOOY1WkTpWH+KBy8zcU1G7uQTVsXqhWEgzryB9A5hg2aqCyZ6aQ/5PSzlqM5+6cnVrX6oYV0XqD3HZSnmQ== -filesize@3.6.1: - version "3.6.1" - resolved "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" - integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== +filelist@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz#f10d1a3ae86c1694808e8f20906f43d4c9132dbb" + integrity sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ== + dependencies: + minimatch "^3.0.4" filesize@6.0.1: version "6.0.1" @@ -10163,7 +10291,7 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@^2.1.0: +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== @@ -10172,7 +10300,7 @@ find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-cache-dir@^3.0.0, find-cache-dir@^3.2.0: +find-cache-dir@^3.2.0, find-cache-dir@^3.3.1: version "3.3.1" resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== @@ -10186,13 +10314,6 @@ find-root@^1.1.0: resolved "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== -find-up@3.0.0, find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -10216,6 +10337,13 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + find-versions@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" @@ -10276,11 +10404,6 @@ fn-name@~3.0.0: resolved "https://registry.npmjs.org/fn-name/-/fn-name-3.0.0.tgz#0596707f635929634d791f452309ab41558e3c5c" integrity sha512-eNMNr5exLoavuAMhIUVsOKF79SWd/zG104ef6sxBTSw+cZc6BXdQXDvYcGvp0VbxVVSp1XDUNoz7mg1xMtSznA== -focus-lock@^0.6.6: - version "0.6.6" - resolved "https://registry.npmjs.org/focus-lock/-/focus-lock-0.6.6.tgz#98119a755a38cfdbeda0280eaa77e307eee850c7" - integrity sha512-Dx69IXGCq1qsUExWuG+5wkiMqVM/zGx/reXSJSLogECwp3x6KeNQZ+NAetgxEFpnC41rD8U3+jRCW68+LNzdtw== - follow-redirects@1.5.10: version "1.5.10" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" @@ -10327,20 +10450,6 @@ forever-agent@~0.6.1: resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -fork-ts-checker-webpack-plugin@1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.5.0.tgz#ce1d77190b44d81a761b10b6284a373795e41f0c" - integrity sha512-zEhg7Hz+KhZlBhILYpXy+Beu96gwvkROWJiTXOCyOOMMrdBIRPvsBpBqgTI4jfJGrJXcqGwJR8zsBGDmzY0jsA== - dependencies: - babel-code-frame "^6.22.0" - chalk "^2.4.1" - chokidar "^2.0.4" - micromatch "^3.1.10" - minimatch "^3.0.4" - semver "^5.6.0" - tapable "^1.0.0" - worker-rpc "^0.1.0" - fork-ts-checker-webpack-plugin@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz#a1642c0d3e65f50c2cc1742e9c0a80f441f86b19" @@ -10355,7 +10464,7 @@ fork-ts-checker-webpack-plugin@3.1.1: tapable "^1.0.0" worker-rpc "^0.1.0" -fork-ts-checker-webpack-plugin@^4.0.5: +fork-ts-checker-webpack-plugin@^4.0.5, fork-ts-checker-webpack-plugin@^4.1.4: version "4.1.6" resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz#5055c703febcf37fa06405d400c122b905167fc5" integrity sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw== @@ -10445,7 +10554,7 @@ fs-constants@^1.0.0: resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@8.1.0, fs-extra@^8.0.1, fs-extra@^8.1.0: +fs-extra@8.1.0, fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== @@ -10550,7 +10659,7 @@ functions-have-names@^1.2.0: resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.1.tgz#a981ac397fa0c9964551402cdc5533d7a4d52f91" integrity sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA== -fuse.js@^3.4.6: +fuse.js@^3.6.1: version "3.6.1" resolved "https://registry.npmjs.org/fuse.js/-/fuse.js-3.6.1.tgz#7de85fdd6e1b3377c23ce010892656385fd9b10c" integrity sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw== @@ -10769,6 +10878,13 @@ glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" +glob-promise@^3.4.0: + version "3.4.0" + resolved "https://registry.npmjs.org/glob-promise/-/glob-promise-3.4.0.tgz#b6b8f084504216f702dc2ce8c9bc9ac8866fdb20" + integrity sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw== + dependencies: + "@types/glob" "*" + glob-to-regexp@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" @@ -11309,11 +11425,6 @@ highlight.js@^10.1.1, highlight.js@~10.1.0: resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.2.tgz#c20db951ba1c22c055010648dfffd7b2a968e00c" integrity sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA== -highlight.js@~9.13.0: - version "9.13.1" - resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" - integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== - highlight.js@~9.15.0, highlight.js@~9.15.1: version "9.15.10" resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2" @@ -11443,7 +11554,22 @@ html-to-react@^1.3.4: lodash.camelcase "^4.3.0" ramda "^0.26" -html-webpack-plugin@^4.0.0-beta.2, html-webpack-plugin@^4.3.0: +html-webpack-plugin@^4.2.1: + version "4.4.1" + resolved "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.4.1.tgz#61ab85aa1a84ba181443345ebaead51abbb84149" + integrity sha512-nEtdEIsIGXdXGG7MjTTZlmhqhpHU9pJFc1OYxcP36c5/ZKP6b0BJMww2QTvJGQYA9aMxUnjDujpZdYcVOXiBCQ== + dependencies: + "@types/html-minifier-terser" "^5.0.0" + "@types/tapable" "^1.0.5" + "@types/webpack" "^4.41.8" + html-minifier-terser "^5.0.1" + loader-utils "^1.2.3" + lodash "^4.17.15" + pretty-error "^2.1.1" + tapable "^1.1.3" + util.promisify "1.0.0" + +html-webpack-plugin@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.3.0.tgz#53bf8f6d696c4637d5b656d3d9863d89ce8174fd" integrity sha512-C0fzKN8yQoVLTelcJxZfJCE+aAvQiY2VUf3UuKrR4a9k5UMWYOtpDLsaXwATbcVCnI05hUS7L9ULQHWLZhyi3w== @@ -11870,25 +11996,6 @@ inline-style-prefixer@^4.0.0: bowser "^1.7.3" css-in-js-utils "^2.0.0" -inquirer@6.5.0: - version "6.5.0" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42" - integrity sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA== - dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" - inquirer@7.0.4: version "7.0.4" resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703" @@ -12155,7 +12262,7 @@ is-docker@^2.0.0: resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== -is-dom@^1.0.9: +is-dom@^1.0.9, is-dom@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-dom/-/is-dom-1.1.0.tgz#af1fced292742443bb59ca3f76ab5e80907b4e8a" integrity sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ== @@ -12207,7 +12314,7 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-function@^1.0.1, is-function@^1.0.2: +is-function@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== @@ -12627,6 +12734,16 @@ iterate-value@^1.0.0: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" +jake@^10.6.1: + version "10.8.2" + resolved "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b" + integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A== + dependencies: + async "0.9.x" + chalk "^2.4.2" + filelist "^1.0.1" + minimatch "^3.0.4" + jenkins@^0.28.0: version "0.28.0" resolved "https://registry.npmjs.org/jenkins/-/jenkins-0.28.0.tgz#72d6fcc452145403b34f6d4ecbd877ee1ab77fca" @@ -13034,15 +13151,7 @@ jest-watcher@^26.3.0: jest-util "^26.3.0" string-length "^4.0.1" -jest-worker@^25.1.0: - version "25.1.0" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz#75d038bad6fdf58eba0d2ec1835856c497e3907a" - integrity sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg== - dependencies: - merge-stream "^2.0.0" - supports-color "^7.0.0" - -jest-worker@^26.3.0: +jest-worker@^26.2.1, jest-worker@^26.3.0: version "26.3.0" resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz#7c8a97e4f4364b4f05ed8bca8ca0c24de091871f" integrity sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw== @@ -13801,7 +13910,7 @@ loader-utils@1.2.3: emojis-list "^2.0.0" json5 "^1.0.1" -loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: +loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== @@ -13867,7 +13976,7 @@ lodash.clonedeep@^4.5.0: resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= -lodash.debounce@^4, lodash.debounce@^4.0.8: +lodash.debounce@^4: version "4.0.8" resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= @@ -13927,11 +14036,6 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" -lodash.throttle@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" - integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= - lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -13942,7 +14046,7 @@ lodash.without@^4.4.0: resolved "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.1: +lodash@^4.0.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.1: version "4.17.20" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -13954,6 +14058,13 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" +log-symbols@^2.1.0: + version "2.2.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + log-symbols@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" @@ -14003,6 +14114,14 @@ loglevel@^1.6.7, loglevel@^1.6.8: resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA== +loglevelnext@^1.0.1: + version "1.0.5" + resolved "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz#36fc4f5996d6640f539ff203ba819641680d75a2" + integrity sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A== + dependencies: + es6-symbol "^3.1.1" + object.assign "^4.1.0" + long@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -14056,14 +14175,6 @@ lowlight@^1.14.0: fault "^1.0.0" highlight.js "~10.1.0" -lowlight@~1.11.0: - version "1.11.0" - resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.11.0.tgz#1304d83005126d4e8b1dc0f07981e9b689ec2efc" - integrity sha512-xrGGN6XLL7MbTMdPD6NfWPwY43SNkjf/d0mecSx/CW36fUZTjRHEq0/Cdug3TWKtRXLWi7iMl1eP0olYxj/a4A== - dependencies: - fault "^1.0.2" - highlight.js "~9.13.0" - lru-cache@^5.0.0, lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -14071,6 +14182,13 @@ lru-cache@^5.0.0, lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + lru-queue@0.1: version "0.1.0" resolved "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" @@ -14220,6 +14338,11 @@ markdown-to-jsx@^6.11.4: prop-types "^15.6.2" unquote "^1.1.0" +material-colors@^1.2.1: + version "1.2.6" + resolved "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46" + integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg== + material-table@1.68.0: version "1.68.0" resolved "https://registry.npmjs.org/material-table/-/material-table-1.68.0.tgz#275c3d9a885c40ae4bc5a7461c00e877f92397b9" @@ -14520,16 +14643,6 @@ mini-create-react-context@^0.4.0: "@babel/runtime" "^7.5.5" tiny-warning "^1.0.3" -mini-css-extract-plugin@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz#5ba8290fbb4179a43dd27cca444ba150bee743a0" - integrity sha512-RQIw6+7utTYn8DBGsf/LpRgZCJMpZt+kuawJ/fju0KiOL6nAaTBNmCJwS7HtwSCXfS47gCkmtBFS7HdsquhdxQ== - dependencies: - loader-utils "^1.1.0" - normalize-url "1.9.1" - schema-utils "^1.0.0" - webpack-sources "^1.1.0" - mini-css-extract-plugin@^0.9.0: version "0.9.0" resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e" @@ -14621,6 +14734,14 @@ minizlib@^2.1.0: minipass "^3.0.0" yallist "^4.0.0" +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mississippi@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" @@ -15060,7 +15181,7 @@ node-pre-gyp@^0.13.0: semver "^5.3.0" tar "^4" -node-releases@^1.1.29, node-releases@^1.1.52, node-releases@^1.1.58: +node-releases@^1.1.52, node-releases@^1.1.58: version "1.1.60" resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== @@ -15439,6 +15560,11 @@ object.values@^1.1.0, object.values@^1.1.1: function-bind "^1.1.1" has "^1.0.3" +objectorarray@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.4.tgz#d69b2f0ff7dc2701903d308bb85882f4ddb49483" + integrity sha512-91k8bjcldstRz1bG6zJo8lWD7c6QXcB4nTDUqiEvIL1xAsLoZlOOZZG+nd6YPz+V7zY1580J4Xxh1vZtyv4i/w== + obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" @@ -15497,14 +15623,7 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -open@^6.3.0: - version "6.4.0" - resolved "https://registry.npmjs.org/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" - integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== - dependencies: - is-wsl "^1.1.0" - -open@^7.0.0, open@^7.0.2: +open@^7.0.2: version "7.0.3" resolved "https://registry.npmjs.org/open/-/open-7.0.3.tgz#db551a1af9c7ab4c7af664139930826138531c48" integrity sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA== @@ -15512,6 +15631,14 @@ open@^7.0.0, open@^7.0.2: is-docker "^2.0.0" is-wsl "^2.1.1" +open@^7.0.3: + version "7.2.1" + resolved "https://registry.npmjs.org/open/-/open-7.2.1.tgz#07b0ade11a43f2a8ce718480bdf3d7563a095195" + integrity sha512-xbYCJib4spUdmcs0g/2mK1nKo/jO2T7INClWd/beL7PFkXRWgr8B23ssDHX/USPn2M2IjDR5UdpYs6I67SnTSA== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + openapi-sampler@^1.0.0-beta.15: version "1.0.0-beta.16" resolved "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.0.0-beta.16.tgz#7813524d5b88d222efb772ceb5a809075d6d9174" @@ -15619,6 +15746,11 @@ ospath@^1.2.2: resolved "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= +overlayscrollbars@^1.10.2: + version "1.13.0" + resolved "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.0.tgz#1edb436328133b94877b558f77966d5497ca36a7" + integrity sha512-p8oHrMeRAKxXDMPI/EBNITj/zTVHKNnAnM59Im+xnoZUlV07FyTg46wom2286jJlXGGfcPFG/ba5NUiCwWNd4w== + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -15651,7 +15783,7 @@ p-finally@^2.0.0: resolved "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== -p-limit@3.0.2: +p-limit@3.0.2, p-limit@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== @@ -15665,7 +15797,7 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.2: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.2.2" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== @@ -16351,7 +16483,7 @@ pinkie@^2.0.0: resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pirates@^4.0.1: +pirates@^4.0.0, pirates@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== @@ -16379,13 +16511,6 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -pkg-up@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" - integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= - dependencies: - find-up "^2.1.0" - pkg-up@3.1.0, pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" @@ -16410,14 +16535,14 @@ pn@^1.1.0: resolved "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== -pnp-webpack-plugin@1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.5.0.tgz#62a1cd3068f46d564bb33c56eb250e4d586676eb" - integrity sha512-jd9olUr9D7do+RN8Wspzhpxhgp1n6Vd0NtQ4SFkmIACZoEL1nkyAdW9Ygrinjec0vgDcWjscFQQ1gDW8rsfKTg== +pnp-webpack-plugin@1.6.4: + version "1.6.4" + resolved "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" + integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== dependencies: - ts-pnp "^1.1.2" + ts-pnp "^1.1.6" -polished@^3.3.1, polished@^3.4.4: +polished@^3.4.4: version "3.6.5" resolved "https://registry.npmjs.org/polished/-/polished-3.6.5.tgz#dbefdde64c675935ec55119fe2a2ab627ca82e9c" integrity sha512-VwhC9MlhW7O5dg/z7k32dabcAFW1VI2+7fSe8cE/kXcfL7mVdoa5UxciYGW2sJU78ldDLT6+ROEKIZKFNTnUXQ== @@ -16892,12 +17017,7 @@ prepend-http@^2.0.0: resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -prettier@^1.16.4: - version "1.19.1" - resolved "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" - integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== - -prettier@^2.0.5: +prettier@^2.0.5, prettier@~2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== @@ -17328,14 +17448,6 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -raw-loader@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/raw-loader/-/raw-loader-3.1.0.tgz#5e9d399a5a222cc0de18f42c3bc5e49677532b3f" - integrity sha512-lzUVMuJ06HF4rYveaz9Tv0WRlUMxJ0Y1hgSkkgg+50iEdaI0TthyEDe08KIHb0XsF6rn8WYTqPCaGTZg3sX+qA== - dependencies: - loader-utils "^1.1.0" - schema-utils "^2.0.1" - raw-loader@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.1.tgz#14e1f726a359b68437e183d5a5b7d33a3eba6933" @@ -17386,12 +17498,17 @@ react-beautiful-dnd@^13.0.0: redux "^4.0.4" use-memo-one "^1.1.1" -react-clientside-effect@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.2.tgz#6212fb0e07b204e714581dd51992603d1accc837" - integrity sha512-nRmoyxeok5PBO6ytPvSjKp9xwXg9xagoTK1mMjwnQxqM9Hd7MNPl+LS1bOSOe+CV2+4fnEquc7H/S8QD3q697A== +react-color@^2.17.0: + version "2.18.1" + resolved "https://registry.npmjs.org/react-color/-/react-color-2.18.1.tgz#2cda8cc8e06a9e2c52ad391a30ddad31972472f4" + integrity sha512-X5XpyJS6ncplZs74ak0JJoqPi+33Nzpv5RYWWxn17bslih+X7OlgmfpmGC1fNvdkK7/SGWYf1JJdn7D2n5gSuQ== dependencies: - "@babel/runtime" "^7.0.0" + "@icons/material" "^0.2.4" + lodash "^4.17.11" + material-colors "^1.2.1" + prop-types "^15.5.10" + reactcss "^1.2.0" + tinycolor2 "^1.4.1" react-copy-to-clipboard@5.0.1: version "5.0.1" @@ -17409,7 +17526,7 @@ react-debounce-input@^3.2.0: lodash.debounce "^4" prop-types "^15.7.2" -react-dev-utils@^10.2.1: +react-dev-utils@^10.0.0, react-dev-utils@^10.2.1: version "10.2.1" resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.1.tgz#f6de325ae25fa4d546d09df4bb1befdc6dd19c19" integrity sha512-XxTbgJnYZmxuPtY3y/UV0D8/65NKkmaia4rXzViknVnZeVlklSh8u6TnaEYPfAi/Gh1TP4mEOXHI6jQOPbeakQ== @@ -17439,36 +17556,31 @@ react-dev-utils@^10.2.1: strip-ansi "6.0.0" text-table "0.2.0" -react-dev-utils@^9.0.0: - version "9.1.0" - resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.1.0.tgz#3ad2bb8848a32319d760d0a84c56c14bdaae5e81" - integrity sha512-X2KYF/lIGyGwP/F/oXgGDF24nxDA2KC4b7AFto+eqzc/t838gpSGiaU8trTqHXOohuLxxc5qi1eDzsl9ucPDpg== +react-docgen-typescript-loader@^3.7.2: + version "3.7.2" + resolved "https://registry.npmjs.org/react-docgen-typescript-loader/-/react-docgen-typescript-loader-3.7.2.tgz#45cb2305652c0602767242a8700ad1ebd66bbbbd" + integrity sha512-fNzUayyUGzSyoOl7E89VaPKJk9dpvdSgyXg81cUkwy0u+NBvkzQG3FC5WBIlXda0k/iaxS+PWi+OC+tUiGxzPA== dependencies: - "@babel/code-frame" "7.5.5" - address "1.1.2" - browserslist "4.7.0" - chalk "2.4.2" - cross-spawn "6.0.5" - detect-port-alt "1.1.6" - escape-string-regexp "1.0.5" - filesize "3.6.1" - find-up "3.0.0" - fork-ts-checker-webpack-plugin "1.5.0" - global-modules "2.0.0" - globby "8.0.2" - gzip-size "5.1.1" - immer "1.10.0" - inquirer "6.5.0" - is-root "2.1.0" - loader-utils "1.2.3" - open "^6.3.0" - pkg-up "2.0.0" - react-error-overlay "^6.0.3" - recursive-readdir "2.2.2" - shell-quote "1.7.2" - sockjs-client "1.4.0" - strip-ansi "5.2.0" - text-table "0.2.0" + "@webpack-contrib/schema-utils" "^1.0.0-beta.0" + loader-utils "^1.2.3" + react-docgen-typescript "^1.15.0" + +react-docgen-typescript-plugin@^0.5.2: + version "0.5.2" + resolved "https://registry.npmjs.org/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-0.5.2.tgz#2b294d75ef3145c36303da82be5d447cb67dc0dc" + integrity sha512-NQfWyWLmzUnedkiN2nPDb6Nkm68ik6fqbC3UvgjqYSeZsbKijXUA4bmV6aU7qICOXdop9PevPdjEgJuAN0nNVQ== + dependencies: + debug "^4.1.1" + endent "^2.0.1" + micromatch "^4.0.2" + react-docgen-typescript "^1.20.1" + react-docgen-typescript-loader "^3.7.2" + tslib "^2.0.0" + +react-docgen-typescript@^1.15.0, react-docgen-typescript@^1.20.1: + version "1.20.4" + resolved "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-1.20.4.tgz#9a5655986077ccfc58c1a447f92c3d19f7a875bf" + integrity sha512-gE2SeseJd6+o981qr9VQJRbvFJ5LjLSKQiwhHsuLN4flt+lheKtG1jp2BPzrv2MKR5gmbLwpmNtK4wbLCPSZAw== react-docgen@^5.0.0: version "5.3.0" @@ -17507,7 +17619,7 @@ react-draggable@^4.0.3: classnames "^2.2.5" prop-types "^15.6.0" -react-error-overlay@^6.0.3, react-error-overlay@^6.0.7: +react-error-overlay@^6.0.7: version "6.0.7" resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== @@ -17522,18 +17634,6 @@ react-fast-compare@^3.1.1: resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== -react-focus-lock@^2.1.0: - version "2.2.1" - resolved "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.2.1.tgz#1d12887416925dc53481914b7cedd39494a3b24a" - integrity sha512-47g0xYcCTZccdzKRGufepY8oZ3W1Qg+2hn6u9SHZ0zUB6uz/4K4xJe7yYFNZ1qT6m+2JDm82F6QgKeBTbjW4PQ== - dependencies: - "@babel/runtime" "^7.0.0" - focus-lock "^0.6.6" - prop-types "^15.6.2" - react-clientside-effect "^1.2.2" - use-callback-ref "^1.2.1" - use-sidecar "^1.0.1" - react-helmet-async@^1.0.2: version "1.0.4" resolved "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.0.4.tgz#079ef10b7fefcaee6240fefd150711e62463cc97" @@ -17602,13 +17702,13 @@ react-inspector@^2.3.0: is-dom "^1.0.9" prop-types "^15.6.1" -react-inspector@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/react-inspector/-/react-inspector-4.0.1.tgz#0f888f78ff7daccbc7be5d452b20c96dc6d5fbb8" - integrity sha512-xSiM6CE79JBqSj8Fzd9dWBHv57tLTH7OM57GP3VrE5crzVF3D5Khce9w1Xcw75OAbvrA0Mi2vBneR1OajKmXFg== +react-inspector@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/react-inspector/-/react-inspector-5.0.1.tgz#8a30f3d488c4f40203624bbe24800f508ae05d3a" + integrity sha512-qRIENuAIcRaytrmg/TL5nN5igYZMzyQqIKlWA8zoYRDltULsZC1bWy2Ua5wYJuwEYnC3gK4FCjcIQnb+5OyLsQ== dependencies: - "@babel/runtime" "^7.6.3" - is-dom "^1.0.9" + "@babel/runtime" "^7.8.7" + is-dom "^1.1.0" prop-types "^15.6.1" react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.6, react-is@^16.9.0: @@ -17659,15 +17759,15 @@ react-motion@^0.5.2: prop-types "^15.5.8" raf "^3.1.0" -react-popper-tooltip@^2.8.3: - version "2.10.1" - resolved "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-2.10.1.tgz#e10875f31916297c694d64a677d6f8fa0a48b4d1" - integrity sha512-cib8bKiyYcrIlHo9zXx81G0XvARfL8Jt+xum709MFCgQa3HTqTi4au3iJ9tm7vi7WU7ngnqbpWkMinBOtwo+IQ== +react-popper-tooltip@^2.11.0: + version "2.11.1" + resolved "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-2.11.1.tgz#3c4bdfd8bc10d1c2b9a162e859bab8958f5b2644" + integrity sha512-04A2f24GhyyMicKvg/koIOQ5BzlrRbKiAgP6L+Pdj1MVX3yJ1NeZ8+EidndQsbejFT55oW1b++wg2Z8KlAyhfQ== dependencies: - "@babel/runtime" "^7.7.4" - react-popper "^1.3.6" + "@babel/runtime" "^7.9.2" + react-popper "^1.3.7" -react-popper@^1.3.6: +react-popper@^1.3.7: version "1.3.7" resolved "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz#f6a3471362ef1f0d10a4963673789de1baca2324" integrity sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww== @@ -17777,7 +17877,7 @@ react-string-replace@^0.4.1: dependencies: lodash "^4.17.4" -react-syntax-highlighter@=12.2.1: +react-syntax-highlighter@=12.2.1, react-syntax-highlighter@^12.2.1: version "12.2.1" resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-12.2.1.tgz#14d78352da1c1c3f93c6698b70ec7c706b83493e" integrity sha512-CTsp0ZWijwKRYFg9xhkWD4DSpQqE4vb2NKVMdPAkomnILSmsNBHE0n5GuI5zB+PU3ySVvXvdt9jo+ViD9XibCA== @@ -17788,17 +17888,6 @@ react-syntax-highlighter@=12.2.1: prismjs "^1.8.4" refractor "^2.4.1" -react-syntax-highlighter@^11.0.2: - version "11.0.2" - resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-11.0.2.tgz#4e3f376e752b20d2f54e4c55652fd663149e4029" - integrity sha512-kqmpM2OH5OodInbEADKARwccwSQWBfZi0970l5Jhp4h39q9Q65C4frNcnd6uHE5pR00W8pOWj9HDRntj2G4Rww== - dependencies: - "@babel/runtime" "^7.3.1" - highlight.js "~9.13.0" - lowlight "~1.11.0" - prismjs "^1.8.4" - refractor "^2.4.1" - react-syntax-highlighter@^13.5.1: version "13.5.1" resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.1.tgz#f21737cf6d582474a0f18b06b52613f4349c0e64" @@ -17820,13 +17909,14 @@ react-test-renderer@^16.13.1: react-is "^16.8.6" scheduler "^0.19.1" -react-textarea-autosize@^7.1.0: - version "7.1.2" - resolved "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-7.1.2.tgz#70fdb333ef86bcca72717e25e623e90c336e2cda" - integrity sha512-uH3ORCsCa3C6LHxExExhF4jHoXYCQwE5oECmrRsunlspaDAbS4mGKNlWZqjLfInWtFQcf0o1n1jC/NGXFdUBCg== +react-textarea-autosize@^8.1.1: + version "8.2.0" + resolved "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.2.0.tgz#fae38653f5ec172a855fd5fffb39e466d56aebdb" + integrity sha512-grajUlVbkx6VdtSxCgzloUIphIZF5bKr21OYMceWPKkniy7H0mRAT/AXPrRtObAe+zUePnNlBwUc4ivVjUGIjw== dependencies: - "@babel/runtime" "^7.1.2" - prop-types "^15.6.0" + "@babel/runtime" "^7.10.2" + use-composed-ref "^1.0.0" + use-latest "^1.0.0" react-transition-group@^4.0.0, react-transition-group@^4.3.0: version "4.3.0" @@ -17897,7 +17987,7 @@ react-wait@^0.3.0: resolved "https://registry.npmjs.org/react-wait/-/react-wait-0.3.0.tgz#0cdd4d919012451a5bc3ab0a16d00c6fd9a8c10b" integrity sha512-kB5x/kMKWcn0uVr9gBdNz21/oGbQwEQnF3P9p6E9yLfJ9DRcKS0fagbgYMFI0YFOoyKDj+2q6Rwax0kTYJF37g== -react@^16.0.0, react@^16.12.0, react@^16.13.1, react@^16.8.3: +react@^16.0.0, react@^16.12.0, react@^16.13.1, react@^16.8.3, react@^16.9.17: version "16.13.1" resolved "https://registry.npmjs.org/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== @@ -17906,6 +17996,13 @@ react@^16.0.0, react@^16.12.0, react@^16.13.1, react@^16.8.3: object-assign "^4.1.1" prop-types "^15.6.2" +reactcss@^1.2.0: + version "1.2.3" + resolved "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd" + integrity sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A== + dependencies: + lodash "^4.0.1" + read-cmd-shim@^1.0.1: version "1.0.5" resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16" @@ -18166,7 +18263,7 @@ regenerator-runtime@^0.11.0: resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.5: +regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.5: version "0.13.5" resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== @@ -18544,7 +18641,7 @@ rifm@^0.7.0: dependencies: "@babel/runtime" "^7.3.1" -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -18558,7 +18655,7 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.0: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -18795,7 +18892,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.4, schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0: +schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0: version "2.7.0" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== @@ -18804,6 +18901,15 @@ schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.4, schema-utils@^2.6 ajv "^6.12.2" ajv-keywords "^3.4.1" +schema-utils@^2.7.1: + version "2.7.1" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + screenfull@^5.0.0: version "5.0.2" resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.0.2.tgz#b9acdcf1ec676a948674df5cd0ff66b902b0bed7" @@ -18902,6 +19008,13 @@ serialize-javascript@^2.1.2: resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + serve-favicon@^2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz#935d240cdfe0f5805307fdfe967d88942a2cbcf0" @@ -19015,11 +19128,6 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -shallow-equal@^1.1.0: - version "1.2.1" - resolved "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da" - integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA== - shallowequal@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" @@ -19109,26 +19217,6 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -simplebar-react@^1.0.0-alpha.6: - version "1.2.3" - resolved "https://registry.npmjs.org/simplebar-react/-/simplebar-react-1.2.3.tgz#bd81fa9827628470e9470d06caef6ece15e1c882" - integrity sha512-1EOWJzFC7eqHUp1igD1/tb8GBv5aPQA5ZMvpeDnVkpNJ3jAuvmrL2kir3HuijlxhG7njvw9ssxjjBa89E5DrJg== - dependencies: - prop-types "^15.6.1" - simplebar "^4.2.3" - -simplebar@^4.2.3: - version "4.2.3" - resolved "https://registry.npmjs.org/simplebar/-/simplebar-4.2.3.tgz#dac40aced299c17928329eab3d5e6e795fafc10c" - integrity sha512-9no0pK7/1y+8/oTF3sy/+kx0PjQ3uk4cYwld5F1CJGk2gx+prRyUq8GRfvcVLq5niYWSozZdX73a2wIr1o9l/g== - dependencies: - can-use-dom "^0.1.0" - core-js "^3.0.1" - lodash.debounce "^4.0.8" - lodash.memoize "^4.1.2" - lodash.throttle "^4.1.1" - resize-observer-polyfill "^1.5.1" - sisteransi@^1.0.4: version "1.0.5" resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -19288,7 +19376,7 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12: +source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -19474,12 +19562,11 @@ ssri@^6.0.0, ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" -ssri@^7.0.0: - version "7.1.0" - resolved "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz#92c241bf6de82365b5c7fb4bd76e975522e1294d" - integrity sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g== +ssri@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808" + integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA== dependencies: - figgy-pudding "^3.5.1" minipass "^3.1.1" stable@^0.1.8: @@ -19594,10 +19681,10 @@ store2@^2.7.1: resolved "https://registry.npmjs.org/store2/-/store2-2.10.0.tgz#46b82bb91878daf1b0d56dec2f1d41e54d5103cf" integrity sha512-tWEpK0snS2RPUq1i3R6OahfJNjWCQYNxq0+by1amCSuw0mXtymJpzmZIeYpA1UAa+7B0grCpNYIbDcd7AgTbFg== -storybook-dark-mode@^0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/storybook-dark-mode/-/storybook-dark-mode-0.6.1.tgz#0527567ac5853c49f6f7a6f68f792fe9322c5a5a" - integrity sha512-E8LIHnVfFhOsPqBc2fLshVBnspziYMXHdwQc/qAjpf4h5ewzrDzeqy4QfJioE+jDoyyZXEtIMugzb0wIaK10Uw== +storybook-dark-mode@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/storybook-dark-mode/-/storybook-dark-mode-1.0.2.tgz#963007e72b628e0efe29bdc5ee8449513bc056b8" + integrity sha512-HBeXTUzYaRgdGJ6YbmcJ3RXw47xp2b5kegZwtcqnSvRblNUZlAKSKsD6UQTWA30hPJ+u3O/5OE5bXk8+H4Zdqg== dependencies: fast-deep-equal "^3.0.0" memoizerific "^1.11.3" @@ -19881,7 +19968,7 @@ style-inject@^0.3.0: resolved "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz#d21c477affec91811cc82355832a700d22bf8dd3" integrity sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw== -style-loader@^1.0.0, style-loader@^1.2.1: +style-loader@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" integrity sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg== @@ -20010,7 +20097,7 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" -svg-parser@^2.0.0, svg-parser@^2.0.2: +svg-parser@^2.0.2: version "2.0.4" resolved "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== @@ -20210,6 +20297,18 @@ tar@^6.0.1: mkdirp "^1.0.3" yallist "^4.0.0" +tar@^6.0.2: + version "6.0.5" + resolved "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" + integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + tarn@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/tarn/-/tarn-3.0.0.tgz#a4082405216c0cce182b8b4cb2639c52c1e870d4" @@ -20222,20 +20321,6 @@ tdigest@^0.1.1: dependencies: bintrees "1.0.1" -telejson@^3.2.0: - version "3.3.0" - resolved "https://registry.npmjs.org/telejson/-/telejson-3.3.0.tgz#6d814f3c0d254d5c4770085aad063e266b56ad03" - integrity sha512-er08AylQ+LEbDLp1GRezORZu5wKOHaBczF6oYJtgC3Idv10qZ8A3p6ffT+J5BzDKkV9MqBvu8HAKiIIOp6KJ2w== - dependencies: - "@types/is-function" "^1.0.0" - global "^4.4.0" - is-function "^1.0.1" - is-regex "^1.0.4" - is-symbol "^1.0.3" - isobject "^4.0.0" - lodash "^4.17.15" - memoizerific "^1.11.3" - telejson@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/telejson/-/telejson-5.0.2.tgz#ed1e64be250cc1c757a53c19e1740b49832b3d51" @@ -20295,22 +20380,22 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser-webpack-plugin@^2.1.2: - version "2.3.5" - resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz#5ad971acce5c517440ba873ea4f09687de2f4a81" - integrity sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w== +terser-webpack-plugin@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-3.1.0.tgz#91e6d39571460ed240c0cf69d295bcf30ebf98cb" + integrity sha512-cjdZte66fYkZ65rQ2oJfrdCAkkhJA7YLYk5eGOcGCSGlq0ieZupRdjedSQXYknMPo2IveQL+tPdrxUkERENCFA== dependencies: - cacache "^13.0.1" - find-cache-dir "^3.2.0" - jest-worker "^25.1.0" - p-limit "^2.2.2" - schema-utils "^2.6.4" - serialize-javascript "^2.1.2" + cacache "^15.0.5" + find-cache-dir "^3.3.1" + jest-worker "^26.2.1" + p-limit "^3.0.2" + schema-utils "^2.6.6" + serialize-javascript "^4.0.0" source-map "^0.6.1" - terser "^4.4.3" + terser "^4.8.0" webpack-sources "^1.4.3" -terser@^4.1.2, terser@^4.4.3, terser@^4.6.3: +terser@^4.1.2, terser@^4.6.3: version "4.6.7" resolved "https://registry.npmjs.org/terser/-/terser-4.6.7.tgz#478d7f9394ec1907f0e488c5f6a6a9a2bad55e72" integrity sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g== @@ -20319,6 +20404,15 @@ terser@^4.1.2, terser@^4.4.3, terser@^4.6.3: source-map "~0.6.1" source-map-support "~0.5.12" +terser@^4.8.0: + version "4.8.0" + resolved "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" + integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -20464,6 +20558,11 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3: resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tinycolor2@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8" + integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g= + tmp@^0.0.33: version "0.0.33" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -20653,7 +20752,7 @@ tryer@^1.0.1: resolved "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== -ts-dedent@^1.1.0, ts-dedent@^1.1.1: +ts-dedent@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/ts-dedent/-/ts-dedent-1.1.1.tgz#68fad040d7dbd53a90f545b450702340e17d18f3" integrity sha512-UGTRZu1evMw4uTPyYF66/KFd22XiU+jMaIuHrkIHQ2GivAXVlLV0v/vHrpOuTRf9BmpNHi/SO7Vd0rLu0y57jg== @@ -20663,6 +20762,11 @@ ts-easing@^0.2.0: resolved "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== +ts-essentials@^2.0.3: + version "2.0.12" + resolved "https://registry.npmjs.org/ts-essentials/-/ts-essentials-2.0.12.tgz#c9303f3d74f75fa7528c3d49b80e089ab09d8745" + integrity sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w== + ts-interface-checker@^0.1.9: version "0.1.10" resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.10.tgz#b68a49e37e90a05797e590f08494dd528bf383cf" @@ -20713,10 +20817,10 @@ ts-node@^8.6.2: source-map-support "^0.5.17" yn "3.1.1" -ts-pnp@^1.1.2: - version "1.1.6" - resolved "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz#389a24396d425a0d3162e96d2b4638900fdc289a" - integrity sha512-CrG5GqAAzMT7144Cl+UIFP7mz/iIhiy+xQ6GGcnjTezhALT02uPMRw7tgDSESgB5MsfKt55+GPWw4ir1kVtMIQ== +ts-pnp@^1.1.6: + version "1.2.0" + resolved "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" + integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== tsconfig-paths@^3.9.0: version "3.9.0" @@ -21106,16 +21210,7 @@ urix@^0.1.0: resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-loader@^2.0.1: - version "2.3.0" - resolved "https://registry.npmjs.org/url-loader/-/url-loader-2.3.0.tgz#e0e2ef658f003efb8ca41b0f3ffbf76bab88658b" - integrity sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog== - dependencies: - loader-utils "^1.2.3" - mime "^2.4.4" - schema-utils "^2.5.0" - -url-loader@^4.1.0: +url-loader@^4.0.0, url-loader@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/url-loader/-/url-loader-4.1.0.tgz#c7d6b0d6b0fccd51ab3ffc58a78d32b8d89a7be2" integrity sha512-IzgAAIC8wRrg6NYkFIJY09vtktQcsvU8V6HhtQj9PTefbYImzLB1hufqo4m+RyM5N3mLx5BqJKccgxJS+W3kqw== @@ -21152,24 +21247,30 @@ url@^0.11.0, url@~0.11.0: punycode "1.3.2" querystring "0.2.0" -use-callback-ref@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.1.tgz#898759ccb9e14be6c7a860abafa3ffbd826c89bb" - integrity sha512-C3nvxh0ZpaOxs9RCnWwAJ+7bJPwQI8LHF71LzbQ3BvzH5XkdtlkMadqElGevg5bYBDFip4sAnD4m06zAKebg1w== +use-composed-ref@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.0.0.tgz#bb13e8f4a0b873632cde4940abeb88b92d03023a" + integrity sha512-RVqY3NFNjZa0xrmK3bIMWNmQ01QjKPDc7DeWR3xa/N8aliVppuutOE5bZzPkQfvL+5NRWMMp0DJ99Trd974FIw== + dependencies: + ts-essentials "^2.0.3" + +use-isomorphic-layout-effect@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.0.0.tgz#f56b4ed633e1c21cd9fc76fe249002a1c28989fb" + integrity sha512-JMwJ7Vd86NwAt1jH7q+OIozZSIxA4ND0fx6AsOe2q1H8ooBUp5aN6DvVCqZiIaYU6JaMRJGyR0FO7EBCIsb/Rg== + +use-latest@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/use-latest/-/use-latest-1.1.0.tgz#7bf9684555869c3f5f37e10d0884c8accf4d3aa6" + integrity sha512-gF04d0ZMV3AMB8Q7HtfkAWe+oq1tFXP6dZKwBHQF5nVXtGsh2oAYeeqma5ZzxtlpOcW8Ro/tLcfmEodjDeqtuw== + dependencies: + use-isomorphic-layout-effect "^1.0.0" use-memo-one@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.1.tgz#39e6f08fe27e422a7d7b234b5f9056af313bd22c" integrity sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ== -use-sidecar@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.2.tgz#e72f582a75842f7de4ef8becd6235a4720ad8af6" - integrity sha512-287RZny6m5KNMTb/Kq9gmjafi7lQL0YHO1lYolU6+tY1h9+Z3uCtkJJ3OSOq3INwYf2hBryCcDh4520AhJibMA== - dependencies: - detect-node "^2.0.4" - tslib "^1.9.3" - use@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -21410,6 +21511,13 @@ warning@^4.0.2, warning@^4.0.3: dependencies: loose-envify "^1.0.0" +watchpack-chokidar2@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0" + integrity sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA== + dependencies: + chokidar "^2.1.8" + watchpack@^1.6.1: version "1.6.1" resolved "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz#280da0a8718592174010c078c7585a74cd8cd0e2" @@ -21419,6 +21527,17 @@ watchpack@^1.6.1: graceful-fs "^4.1.2" neo-async "^2.5.0" +watchpack@^1.7.4: + version "1.7.4" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b" + integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg== + dependencies: + graceful-fs "^4.1.2" + neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.0" + wbuf@^1.1.0, wbuf@^1.7.3: version "1.7.3" resolved "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" @@ -21520,6 +21639,16 @@ webpack-hot-middleware@^2.25.0: querystring "^0.2.0" strip-ansi "^3.0.0" +webpack-log@^1.1.2: + version "1.2.0" + resolved "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz#a4b34cda6b22b518dbb0ab32e567962d5c72a43d" + integrity sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA== + dependencies: + chalk "^2.1.0" + log-symbols "^2.1.0" + loglevelnext "^1.0.1" + uuid "^3.1.0" + webpack-log@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" @@ -21541,14 +21670,14 @@ webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack- source-list-map "^2.0.0" source-map "~0.6.1" -webpack-virtual-modules@^0.2.0: - version "0.2.1" - resolved "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.2.1.tgz#8ab73d4df0fd37ed27bb8d823bc60ea7266c8bf7" - integrity sha512-0PWBlxyt4uGDofooIEanWhhyBOHdd+lr7QpYNDLC7/yc5lqJT8zlc04MTIBnKj+c2BlQNNuwE5er/Tg4wowHzA== +webpack-virtual-modules@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.2.2.tgz#20863dc3cb6bb2104729fff951fbe14b18bd0299" + integrity sha512-kDUmfm3BZrei0y+1NTHJInejzxfhtU8eDj2M7OKb2IWrPFAeO1SOH2KuQ68MSZu9IGEHcxbkKKR1v18FrUSOmA== dependencies: debug "^3.0.0" -webpack@^4.33.0, webpack@^4.38.0, webpack@^4.41.6: +webpack@^4.41.6: version "4.43.0" resolved "https://registry.npmjs.org/webpack/-/webpack-4.43.0.tgz#c48547b11d563224c561dad1172c8aa0b8a678e6" integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g== @@ -21577,6 +21706,35 @@ webpack@^4.33.0, webpack@^4.38.0, webpack@^4.41.6: watchpack "^1.6.1" webpack-sources "^1.4.1" +webpack@^4.43.0: + version "4.44.1" + resolved "https://registry.npmjs.org/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21" + integrity sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.3.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.3" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.7.4" + webpack-sources "^1.4.1" + websocket-driver@0.6.5: version "0.6.5" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" From 6fa721eb977fd4d15aff6d8729724266a40dd8dd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 6 Sep 2020 12:06:50 +0200 Subject: [PATCH 333/359] cli: add --build option to backend:image-build --- packages/backend/package.json | 2 +- packages/cli/src/commands/backend/buildImage.ts | 1 + packages/cli/src/commands/index.ts | 2 +- packages/cli/src/lib/packager/index.ts | 12 ++++++++++++ .../default-app/packages/backend/package.json.hbs | 2 +- 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index f9bf855371..d944ac6535 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -10,7 +10,7 @@ }, "scripts": { "build": "backstage-cli backend:build", - "build-image": "backstage-cli backend:build-image --tag example-backend", + "build-image": "backstage-cli backend:build-image --build --tag example-backend", "start": "backstage-cli backend:dev", "lint": "backstage-cli lint", "test": "backstage-cli test", diff --git a/packages/cli/src/commands/backend/buildImage.ts b/packages/cli/src/commands/backend/buildImage.ts index 14b62d2894..2be04dfa76 100644 --- a/packages/cli/src/commands/backend/buildImage.ts +++ b/packages/cli/src/commands/backend/buildImage.ts @@ -32,6 +32,7 @@ export default async (cmd: Command) => { const pkgPath = paths.resolveTarget(PKG_PATH); const pkg = await fs.readJson(pkgPath); const tempDistWorkspace = await createDistWorkspace([pkg.name], { + buildDependencies: Boolean(cmd.build), files: [ 'package.json', 'yarn.lock', diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index e8b7fccfee..4e031cdbc1 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -41,7 +41,7 @@ export function registerCommands(program: CommanderStatic) { .helpOption(', --backstage-cli-help') // Let docker handle --help .option('--build', 'Build packages before packing them into the image') .description( - 'Build a docker , all extra options are forwarded to docker build', + 'Bundles the package into a docker image. All extra args are forwarded to docker image build', ) .action(lazy(() => import('./backend/buildImage').then(m => m.default))); diff --git a/packages/cli/src/lib/packager/index.ts b/packages/cli/src/lib/packager/index.ts index fda85dd68a..5584e1fc21 100644 --- a/packages/cli/src/lib/packager/index.ts +++ b/packages/cli/src/lib/packager/index.ts @@ -48,6 +48,11 @@ type Options = { * Defaults to ['yarn.lock', 'package.json']. */ files?: FileEntry[]; + + /** + * If set to true, the target packages are built before they are packaged into the workspace. + */ + buildDependencies?: boolean; }; /** @@ -68,6 +73,13 @@ export async function createDistWorkspace( const targets = await findTargetPackages(packageNames); + if (options.buildDependencies) { + const scopeArgs = targets.flatMap(target => ['--scope', target.name]); + await run('yarn', ['lerna', 'run', ...scopeArgs, 'build'], { + cwd: paths.targetRoot, + }); + } + await moveToDistWorkspace(targetDir, targets); const files: FileEntry[] = options.files ?? ['yarn.lock', 'package.json']; diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs index 8fc1e3bbe1..044ee1560a 100644 --- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs @@ -9,7 +9,7 @@ }, "scripts": { "build": "backstage-cli backend:build", - "build-image": "backstage-cli backend:build-image --tag example-backend", + "build-image": "backstage-cli backend:build-image --build --tag example-backend", "start": "backstage-cli backend:dev", "lint": "backstage-cli lint", "test": "backstage-cli test", From f8a871979e367855f58246914b70d1778e85dc15 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 6 Sep 2020 12:32:44 +0200 Subject: [PATCH 334/359] cli: copy all app-config flavors into backend image --- .../cli/src/commands/backend/buildImage.ts | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/backend/buildImage.ts b/packages/cli/src/commands/backend/buildImage.ts index 2be04dfa76..36d4ef7129 100644 --- a/packages/cli/src/commands/backend/buildImage.ts +++ b/packages/cli/src/commands/backend/buildImage.ts @@ -15,6 +15,7 @@ */ import fs from 'fs-extra'; +import { join as joinPath, relative as relativePath } from 'path'; import { createDistWorkspace } from '../../lib/packager'; import { paths } from '../../lib/paths'; import { run } from '../../lib/run'; @@ -31,12 +32,13 @@ export default async (cmd: Command) => { const pkgPath = paths.resolveTarget(PKG_PATH); const pkg = await fs.readJson(pkgPath); + const appConfigs = await findAppConfigs(); const tempDistWorkspace = await createDistWorkspace([pkg.name], { buildDependencies: Boolean(cmd.build), files: [ 'package.json', 'yarn.lock', - 'app-config.yaml', + ...appConfigs, { src: paths.resolveTarget('Dockerfile'), dest: 'Dockerfile' }, ], }); @@ -49,3 +51,28 @@ export default async (cmd: Command) => { await fs.remove(tempDistWorkspace); }; + +/** + * Find all config files to copy into the image + */ +async function findAppConfigs(): Promise { + const files = []; + + for (const name of await fs.readdir(paths.targetRoot)) { + if (name.startsWith('app-config.') && name.endsWith('.yaml')) { + files.push(name); + } + } + + if (paths.targetRoot !== paths.targetDir) { + const dirPath = relativePath(paths.targetRoot, paths.targetDir); + + for (const name of await fs.readdir(paths.targetDir)) { + if (name.startsWith('app-config.') && name.endsWith('.yaml')) { + files.push(joinPath(dirPath, name)); + } + } + } + + return files; +} From b104ebce53e63d08209d72e814d44289b9c1dedc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 6 Sep 2020 12:39:09 +0200 Subject: [PATCH 335/359] app-config: split cors and urls out to separate development config --- app-config.development.yaml | 11 +++++++++++ app-config.yaml | 6 +----- .../templates/default-app/app-config.development.yaml | 11 +++++++++++ .../templates/default-app/app-config.yaml.hbs | 6 +----- 4 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 app-config.development.yaml create mode 100644 packages/create-app/templates/default-app/app-config.development.yaml diff --git a/app-config.development.yaml b/app-config.development.yaml new file mode 100644 index 0000000000..da274ba1a8 --- /dev/null +++ b/app-config.development.yaml @@ -0,0 +1,11 @@ +app: + baseUrl: http://localhost:3000 + +backend: + baseUrl: http://localhost:7000 + listen: + port: 7000 + cors: + origin: http://localhost:3000 + methods: [GET, POST, PUT, DELETE] + credentials: true diff --git a/app-config.yaml b/app-config.yaml index 2620f6b9b7..da2f5f2d11 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -1,15 +1,11 @@ app: title: Backstage Example App - baseUrl: http://localhost:3000 + baseUrl: http://localhost:7000 backend: baseUrl: http://localhost:7000 listen: port: 7000 - cors: - origin: http://localhost:3000 - methods: [GET, POST, PUT, DELETE] - credentials: true database: client: sqlite3 connection: ':memory:' diff --git a/packages/create-app/templates/default-app/app-config.development.yaml b/packages/create-app/templates/default-app/app-config.development.yaml new file mode 100644 index 0000000000..da274ba1a8 --- /dev/null +++ b/packages/create-app/templates/default-app/app-config.development.yaml @@ -0,0 +1,11 @@ +app: + baseUrl: http://localhost:3000 + +backend: + baseUrl: http://localhost:7000 + listen: + port: 7000 + cors: + origin: http://localhost:3000 + methods: [GET, POST, PUT, DELETE] + credentials: true diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index dea37814c1..f774ba8174 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -1,6 +1,6 @@ app: title: Scaffolded Backstage App - baseUrl: http://localhost:3000 + baseUrl: http://localhost:7000 organization: name: Acme Corporation @@ -9,10 +9,6 @@ backend: baseUrl: http://localhost:7000 listen: port: 7000 - cors: - origin: http://localhost:3000 - methods: [GET, POST, PUT, DELETE] - credentials: true {{#if dbTypeSqlite}} database: client: sqlite3 From 10216391ba5b96ddf4d5e5e669bdaff3b4006b6c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 6 Sep 2020 14:01:24 +0200 Subject: [PATCH 336/359] switch default docker-build command to build backend --- Dockerfile | 7 ++++++- docker-compose.yaml | 8 ++------ docs/getting-started/deployment-other.md | 13 ++++++------- docs/overview/architecture-overview.md | 12 +++++------- package.json | 4 ++-- 5 files changed, 21 insertions(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index fa89debcee..174548a90c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,14 @@ FROM nginx:mainline +# The purpose of this image is to serve the frontend app content separately. +# By default the Backstage backend uses the app-backend plugin to serve the +# app from the backend itself, but it may be desirable to move the frontend +# content serving to a separate deployment, in which case this image can be used. + # This dockerfile requires the app to be built on the host first, as it # simply copies in the build output into the image. -# The safest way to build this image is to use `yarn docker-build` +# The safest way to build this image is to use `yarn docker-build:app` RUN apt-get update && apt-get -y install jq && rm -rf /var/lib/apt/lists/* diff --git a/docker-compose.yaml b/docker-compose.yaml index d32928b4e3..a9922cfb6c 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,14 +1,10 @@ # Make sure that before you # run the docker-compose that you have run -# $ yarn docker-build:all +# $ yarn docker-build version: '3' services: - frontend: - image: 'spotify/backstage:latest' - ports: - - '3000:80' - backend: + backstage: image: 'example-backend:latest' ports: - '7000:7000' diff --git a/docs/getting-started/deployment-other.md b/docs/getting-started/deployment-other.md index 3b7796f8fe..d3fb9d932b 100644 --- a/docs/getting-started/deployment-other.md +++ b/docs/getting-started/deployment-other.md @@ -10,21 +10,20 @@ title: Other Run the following commands if you have Docker environment ```bash +$ yarn install $ yarn docker-build -$ docker run --rm -it -p 80:80 spotify/backstage +$ docker run --rm -it -p 7000:7000 -e NODE_ENV=development example-backend:latest ``` Then open http://localhost/ on your browser. ### Running with `docker-compose` -Run the following commands if you have docker and docker-compose for a full -example, with the example backend also deployed. +There is also a `docker-compose.yaml` that you can use to replace the previous +`docker run` command: ```bash -$ yarn docker-build:all +$ yarn install +$ yarn docker-build $ docker-compose up ``` - -Then open http://localhost:3000 on your browser to see the example app with an -example backend. diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md index 9f1d6d3291..55a1aeec04 100644 --- a/docs/overview/architecture-overview.md +++ b/docs/overview/architecture-overview.md @@ -171,21 +171,19 @@ The frontend container can be built with a provided command. ```bash yarn install yarn tsc -yarn build -yarn run docker-build +yarn run docker-build:app ``` Running this will simply generate a Docker container containing the contents of -the UIs `dist` directory. The resulting container will be about 50MB in size. +the UIs `dist` directory. -The backend container can be built by running the following command in the -`packages/backend` directory. +The backend container can be built by running the following command: ```bash -yarn run build-image +yarn run docker-build ``` -This will create a ~500MB container called `example-backend`. +This will create a container called `example-backend`. The lighthouse-audit-service container is already publicly available in Docker Hub and can be downloaded and ran with diff --git a/package.json b/package.json index bf2dfc3246..93f8f694a6 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ "lint:all": "lerna run lint --", "lint:type-deps": "node scripts/check-type-dependencies.js", "docgen": "lerna run docgen", - "docker-build": "yarn workspace example-app build && docker build . -t spotify/backstage", - "docker-build:all": "yarn tsc && yarn build && yarn docker-build && yarn workspace example-backend build-image", + "docker-build:app": "yarn workspace example-app build && docker build . -t spotify/backstage", + "docker-build": "yarn tsc && yarn workspace example-backend build-image", "create-plugin": "backstage-cli create-plugin", "remove-plugin": "backstage-cli remove-plugin", "release": "if [ \"$(git symbolic-ref --short HEAD)\" = master ]; then echo \"don't try to release master\"; exit 1; else lerna version --no-push --force-publish; fi", From 1193a0f57b18075a8d0982463ffef4fe98974d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Sun, 6 Sep 2020 23:11:40 +0200 Subject: [PATCH 337/359] Sort Storybook stories (#2300) --- packages/storybook/.storybook/preview.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/storybook/.storybook/preview.js b/packages/storybook/.storybook/preview.js index bb8e040c7b..f92e07b045 100644 --- a/packages/storybook/.storybook/preview.js +++ b/packages/storybook/.storybook/preview.js @@ -24,3 +24,22 @@ addParameters({ }, layout: 'fullscreen', }); + +export const parameters = { + options: { + storySort: { + order: [ + 'Example Plugin', + 'Header', + 'Sidebar', + 'Tabs', + 'Information Card', + 'Tabbed Card', + 'Table', + 'Status', + 'Trendline', + 'Progress Card', + ], + }, + }, +}; From 8b43c483b297c9bff4b9b1ff45100112bce79cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Doreau?= <32459935+ayshiff@users.noreply.github.com> Date: Mon, 7 Sep 2020 09:45:47 +0200 Subject: [PATCH 338/359] feat(core): add chip component (#2225) --- .../src/components/stories/Chip.stories.tsx | 56 +++++++++++++++++++ packages/theme/src/baseTheme.ts | 21 +++++++ 2 files changed, 77 insertions(+) create mode 100644 packages/core/src/components/stories/Chip.stories.tsx diff --git a/packages/core/src/components/stories/Chip.stories.tsx b/packages/core/src/components/stories/Chip.stories.tsx new file mode 100644 index 0000000000..29170214a2 --- /dev/null +++ b/packages/core/src/components/stories/Chip.stories.tsx @@ -0,0 +1,56 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { Chip } from '@material-ui/core'; + +export default { + title: 'Chip', + component: Chip, +}; + +export const Default = () => ( + +); + +export const LargeDeletable = () => ( + ({})} + /> +); + +export const LargeNotDeletable = () => ( + +); + +export const SmallDeletable = () => ( + ({})} + /> +); + +export const SmallNotDeletable = () => ( + +); diff --git a/packages/theme/src/baseTheme.ts b/packages/theme/src/baseTheme.ts index dc68db11d6..00a485bb41 100644 --- a/packages/theme/src/baseTheme.ts +++ b/packages/theme/src/baseTheme.ts @@ -192,10 +192,31 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides { }, MuiChip: { root: { + backgroundColor: '#D9D9D9', // By default there's no margin, but it's usually wanted, so we add some trailing margin marginRight: theme.spacing(1), marginBottom: theme.spacing(1), }, + label: { + color: theme.palette.grey[900], + lineHeight: `${theme.spacing(2.5)}px`, + fontWeight: theme.typography.fontWeightMedium, + fontSize: `${theme.spacing(1.75)}px`, + }, + labelSmall: { + fontSize: `${theme.spacing(1.5)}px`, + }, + deleteIcon: { + color: theme.palette.grey[500], + width: `${theme.spacing(3)}px`, + height: `${theme.spacing(3)}px`, + margin: `0 ${theme.spacing(0.75)}px 0 -${theme.spacing(0.75)}px`, + }, + deleteIconSmall: { + width: `${theme.spacing(2)}px`, + height: `${theme.spacing(2)}px`, + margin: `0 ${theme.spacing(0.5)}px 0 -${theme.spacing(0.5)}px`, + }, }, MuiCardHeader: { root: { From ea10e74557ca22dbbafe757d4e64ebac83b3c373 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2020 09:46:48 +0200 Subject: [PATCH 339/359] chore(deps): bump @rjsf/core from 2.2.2 to 2.3.0 (#2305) Bumps [@rjsf/core](https://github.com/rjsf-team/react-jsonschema-form) from 2.2.2 to 2.3.0. - [Release notes](https://github.com/rjsf-team/react-jsonschema-form/releases) - [Commits](https://github.com/rjsf-team/react-jsonschema-form/compare/v2.2.2...v2.3.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index 83ecfc14af..57eb20b22d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2951,9 +2951,9 @@ react-lifecycles-compat "^3.0.4" "@rjsf/core@^2.1.0": - version "2.2.2" - resolved "https://registry.npmjs.org/@rjsf/core/-/core-2.2.2.tgz#1ebb6fe47448998f3b54e2dea8d58de8a46014dc" - integrity sha512-4d6DHIiTJEkUq5vyl4LIxLGIYYKKnHcprf94oVchUtGQvRFjNUDFxeFQoyr90oaxcBMs2WDDcCgjcFaKVyfErg== + version "2.3.0" + resolved "https://registry.npmjs.org/@rjsf/core/-/core-2.3.0.tgz#334c73d2262ef1a8cda477e238067af7336c5599" + integrity sha512-OZKYHt9tjKhzOH4CvsPiCwepuIacqI++cNmnL2fsxh1IF+uEWGlo3NLDWhhSaBbOv9jps6a5YQcLbLtjNuSwug== dependencies: "@babel/runtime-corejs2" "^7.8.7" "@types/json-schema" "^7.0.4" @@ -5438,17 +5438,7 @@ ajv@^5.0.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.1, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.5.5, ajv@^6.7.0: - version "6.12.3" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" - integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ajv@^6.12.4: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.1, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.5.5, ajv@^6.7.0: version "6.12.4" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== From 7245158e71e2f6a520023febf2db1ed3af4c732b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 7 Sep 2020 11:14:56 +0200 Subject: [PATCH 340/359] chore: fix prettier errors --- .prettierignore | 1 + .../src/components/stories/Chip.stories.tsx | 27 +++++-------------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/.prettierignore b/.prettierignore index ef75947604..9e75b74eee 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,3 +5,4 @@ coverage *.hbs templates plugins/scaffolder-backend/sample-templates +.vscode diff --git a/packages/core/src/components/stories/Chip.stories.tsx b/packages/core/src/components/stories/Chip.stories.tsx index 29170214a2..c0e644fcc5 100644 --- a/packages/core/src/components/stories/Chip.stories.tsx +++ b/packages/core/src/components/stories/Chip.stories.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; import { Chip } from '@material-ui/core'; @@ -21,36 +22,20 @@ export default { component: Chip, }; -export const Default = () => ( - -); +export const Default = () => ; export const LargeDeletable = () => ( - ({})} - /> + ({})} /> ); export const LargeNotDeletable = () => ( - + ); export const SmallDeletable = () => ( - ({})} - /> + ({})} /> ); export const SmallNotDeletable = () => ( - + ); From 215c87ec2bb366dde6783d4facaae604ee37eee5 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Mon, 7 Sep 2020 09:16:45 +0200 Subject: [PATCH 341/359] Remove newlines added by morgan --- packages/backend-common/src/middleware/requestLoggingHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-common/src/middleware/requestLoggingHandler.ts b/packages/backend-common/src/middleware/requestLoggingHandler.ts index 6604ec245c..6bb42a35b7 100644 --- a/packages/backend-common/src/middleware/requestLoggingHandler.ts +++ b/packages/backend-common/src/middleware/requestLoggingHandler.ts @@ -33,7 +33,7 @@ export function requestLoggingHandler(logger?: Logger): RequestHandler { return morgan('combined', { stream: { write(message: String) { - actualLogger.info(message); + actualLogger.info(message.trim()); }, }, }); From 0e0a0f4917411e7301dec57ffed063e843e3811b Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Mon, 7 Sep 2020 11:13:05 +0200 Subject: [PATCH 342/359] Use trimRight instead --- packages/backend-common/src/middleware/requestLoggingHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-common/src/middleware/requestLoggingHandler.ts b/packages/backend-common/src/middleware/requestLoggingHandler.ts index 6bb42a35b7..061dfbbd25 100644 --- a/packages/backend-common/src/middleware/requestLoggingHandler.ts +++ b/packages/backend-common/src/middleware/requestLoggingHandler.ts @@ -33,7 +33,7 @@ export function requestLoggingHandler(logger?: Logger): RequestHandler { return morgan('combined', { stream: { write(message: String) { - actualLogger.info(message.trim()); + actualLogger.info(message.trimRight()); }, }, }); From a1b3ff5f6ee285011efae981cbb8d4b773e21782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Mon, 7 Sep 2020 12:24:39 +0200 Subject: [PATCH 343/359] Minor cleanup of Stories (#2302) * Minor cleanup of Stories * Remove ugly styling * Update InfoCard.stories.tsx --- .../src/layout/InfoCard/InfoCard.stories.tsx | 28 ++++-- .../core/src/layout/Page/Page.stories.tsx | 94 ++++++++++--------- 2 files changed, 68 insertions(+), 54 deletions(-) diff --git a/packages/core/src/layout/InfoCard/InfoCard.stories.tsx b/packages/core/src/layout/InfoCard/InfoCard.stories.tsx index 7d888f5486..3e476c5480 100644 --- a/packages/core/src/layout/InfoCard/InfoCard.stories.tsx +++ b/packages/core/src/layout/InfoCard/InfoCard.stories.tsx @@ -15,9 +15,8 @@ */ import React, { FC } from 'react'; import { InfoCard } from '.'; -import { Grid } from '@material-ui/core'; +import { Grid, Typography } from '@material-ui/core'; -const cardContentStyle = { height: 200, width: 500 }; const linkInfo = { title: 'Go to XYZ Location', link: '#' }; export default { @@ -25,24 +24,35 @@ export default { component: InfoCard, }; +const text = ( + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat + non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +); + const Wrapper: FC<{}> = ({ children }) => ( - {children} + + {children} + ); export const Default = () => ( - -
- + {text} ); export const Subhead = () => ( - -
+ + {text} ); @@ -50,7 +60,7 @@ export const Subhead = () => ( export const LinkInFooter = () => ( -
+ {text} ); diff --git a/packages/core/src/layout/Page/Page.stories.tsx b/packages/core/src/layout/Page/Page.stories.tsx index aec99967dc..0b91875546 100644 --- a/packages/core/src/layout/Page/Page.stories.tsx +++ b/packages/core/src/layout/Page/Page.stories.tsx @@ -97,7 +97,7 @@ const columns: TableColumn[] = [ const tabs = [ { label: 'Overview' }, { label: 'CI/CD' }, - { label: 'Cost Efficency' }, + { label: 'Cost Efficiency' }, { label: 'Code Coverage' }, { label: 'Test' }, { label: 'Compliance Advisor' }, @@ -137,38 +137,38 @@ const DataGrid = () => ( - Rightsize GKE deployment -

+ Rightsize GKE deployment + Services are considered underutilized in GKE when the average usage of requested cores is less than 80%. -

- What can I do? -

+ + What can I do? + Review requested core and limit settings. Check HPA target scaling - settings in hpa.yaml. The recommended value for - targetCPUUtilizationPercentage is 80. -

-

+ settings in hpa.yaml. The recommended value for  + targetCPUUtilizationPercentage is 80. + + For single pods, there is of course no HPA. But it can also be useful to think about a single pod out of a larger deployment, then modify based on HPA requirements. Within a pod, each container has its own CPU and memory requests and limits. -

- Definitions -

+ + Definitions + A request is a minimum reserved value; a container will never have less than this amount allocated to it, even if it doesn't actually use it. Requests are used for determining what nodes to schedule pods on (bin-packing). The tension here is between not allocating resources we don't need, and having easy-enough access to enough resources to be able to function. -

- + + Contact #cost-awareness for information and support. - +
@@ -195,37 +195,41 @@ const ExampleContentHeader = ({ selectedTab }: { selectedTab?: number }) => ( export const PluginWithData = () => { const [selectedTab, setSelectedTab] = useState(2); return ( - - - setSelectedTab(index)} - tabs={tabs.map(({ label }, index) => ({ - id: index.toString(), - label, - }))} - /> - - - - - +
+ + + setSelectedTab(index)} + tabs={tabs.map(({ label }, index) => ({ + id: index.toString(), + label, + }))} + /> + + + + + +
); }; export const PluginWithTable = () => { return ( - - - - -
- - +
+ + + + +
+ + + ); }; From d372b1c675c7b9585c95168b21e8aaf32a934c49 Mon Sep 17 00:00:00 2001 From: davidamitchell Date: Mon, 7 Sep 2020 23:51:13 +1200 Subject: [PATCH 344/359] docs: minor typo correction (#2307) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: minor typo correction * chore: fix prettier errors Co-authored-by: Fredrik Adelöw --- docs/features/software-catalog/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/software-catalog/index.md b/docs/features/software-catalog/index.md index bfc3d53d4d..07b22808cd 100644 --- a/docs/features/software-catalog/index.md +++ b/docs/features/software-catalog/index.md @@ -48,7 +48,7 @@ There are 3 ways to add components to the catalog: 1. Manually register components 2. Creating new components through Backstage -3. Integrating with and [external source](external-integrations.md) +3. Integrating with an [external source](external-integrations.md) ### Manually register components From 2b114fde44793a93e09311b11233e6be2cc1b2ac Mon Sep 17 00:00:00 2001 From: Esteban Barrios Date: Mon, 7 Sep 2020 13:53:30 +0200 Subject: [PATCH 345/359] Show Display name in catalog instead of userId (#2310) * Use display name instead of userid if this exists in the user profile for the catalog welcome banner * Rename variable for clarity * Updated test for CatalogPage to include getProfile function --- plugins/catalog/src/components/CatalogPage/CatalogLayout.tsx | 3 ++- .../catalog/src/components/CatalogPage/CatalogPage.test.tsx | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/catalog/src/components/CatalogPage/CatalogLayout.tsx b/plugins/catalog/src/components/CatalogPage/CatalogLayout.tsx index 39b3c66013..92f51ace06 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogLayout.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogLayout.tsx @@ -31,12 +31,13 @@ type Props = { const CatalogLayout = ({ children }: Props) => { const greeting = getTimeBasedGreeting(); + const profile = useApi(identityApiRef).getProfile(); const userId = useApi(identityApiRef).getUserId(); return (
{ getLocationByEntity: () => Promise.resolve({ id: 'id', type: 'github', target: 'url' }), }; + const testProfile: Partial = { + displayName: 'Display Name', + }; const indentityApi: Partial = { getUserId: () => 'tools@example.com', + getProfile: () => testProfile, }; const renderWrapped = (children: React.ReactNode) => From 66ddd0cc560cee97a90506262155dbbd3a9cf9ec Mon Sep 17 00:00:00 2001 From: Andrew Thauer <6507159+andrewthauer@users.noreply.github.com> Date: Mon, 7 Sep 2020 08:38:02 -0400 Subject: [PATCH 346/359] refactor(rollbar): update to use routing & entities --- .../well-known-annotations.md | 18 +++++ plugins/rollbar/README.md | 60 ++++++++++++---- plugins/rollbar/package.json | 2 + plugins/rollbar/src/api/RollbarApi.ts | 1 + plugins/rollbar/src/api/RollbarClient.ts | 16 ++--- plugins/rollbar/src/api/RollbarMockClient.ts | 70 ------------------- plugins/rollbar/src/api/index.ts | 18 +++++ .../EntityPageRollbar/EntityPageRollbar.tsx | 27 +++++++ .../RollbarHome.test.tsx} | 15 +++- .../RollbarHome.tsx} | 40 ++++------- .../RollbarProject/RollbarProject.tsx | 40 +++++++++++ .../RollbarProjectPage.test.tsx | 14 +++- .../RollbarProjectPage/RollbarProjectPage.tsx | 43 ++++-------- .../RollbarProjectTable.tsx | 68 +++++++----------- .../RollbarTopItemsTable.tsx | 19 ++--- plugins/rollbar/src/components/Router.tsx | 47 +++++++++++++ .../TrendGraph.test.tsx} | 8 +-- .../TrendGraph.tsx} | 2 +- plugins/rollbar/src/constants.ts | 17 +++++ plugins/rollbar/src/hooks/useCatalogEntity.ts | 34 +++++++++ plugins/rollbar/src/hooks/useProject.ts | 37 ++++++++++ .../useRollbarEntities.ts} | 36 +++++----- .../rollbar/src/hooks/useTopActiveItems.ts | 46 ++++++++++++ plugins/rollbar/src/index.ts | 9 ++- plugins/rollbar/src/plugin.ts | 8 +-- plugins/rollbar/src/routes.ts | 15 +++- plugins/rollbar/src/utils/index.ts | 21 ++++++ 27 files changed, 489 insertions(+), 242 deletions(-) delete mode 100644 plugins/rollbar/src/api/RollbarMockClient.ts create mode 100644 plugins/rollbar/src/api/index.ts create mode 100644 plugins/rollbar/src/components/EntityPageRollbar/EntityPageRollbar.tsx rename plugins/rollbar/src/components/{RollbarPage/RollbarPage.test.tsx => RollbarHome/RollbarHome.test.tsx} (80%) rename plugins/rollbar/src/components/{RollbarLayout/RollbarLayout.tsx => RollbarHome/RollbarHome.tsx} (51%) create mode 100644 plugins/rollbar/src/components/RollbarProject/RollbarProject.tsx rename plugins/rollbar/src/components/{RollbarPage => RollbarProjectTable}/RollbarProjectTable.tsx (57%) create mode 100644 plugins/rollbar/src/components/Router.tsx rename plugins/rollbar/src/components/{RollbarTrendGraph/RollbarTrendGraph.test.tsx => TrendGraph/TrendGraph.test.tsx} (81%) rename plugins/rollbar/src/components/{RollbarTrendGraph/RollbarTrendGraph.tsx => TrendGraph/TrendGraph.tsx} (93%) create mode 100644 plugins/rollbar/src/constants.ts create mode 100644 plugins/rollbar/src/hooks/useCatalogEntity.ts create mode 100644 plugins/rollbar/src/hooks/useProject.ts rename plugins/rollbar/src/{components/RollbarPage/RollbarPage.tsx => hooks/useRollbarEntities.ts} (55%) create mode 100644 plugins/rollbar/src/hooks/useTopActiveItems.ts create mode 100644 plugins/rollbar/src/utils/index.ts diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index 159cfb4ca1..18de63d704 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -100,6 +100,24 @@ the same for all entities in the catalog. Specifying this annotation may enable Sentry related features in Backstage for that entity. +### rollbar.com/project-slug + +```yaml +# Example: +metadata: + annotations: + rollbar.com/project-slug: spotify/pump-station +``` + +The value of this annotation is the so-called slug (or alternatively, the ID) of +a [Rollbar](https://rollbar.com) project within your organization. The value can +be the format of `[organization]/[project-slug]` or just `[project-slug]`. When +the organization slug is omitted the `app-config.yaml` will be used as a +fallback (`rollbar.organization` followed by `organization.name`). + +Specifying this annotation may enable Rollbar related features in Backstage for +that entity. + ## Deprecated Annotations The following annotations are deprecated, and only listed here to aid in diff --git a/plugins/rollbar/README.md b/plugins/rollbar/README.md index 94ef38b43a..b2d2100dd9 100644 --- a/plugins/rollbar/README.md +++ b/plugins/rollbar/README.md @@ -26,25 +26,57 @@ export { plugin as Rollbar } from '@backstage/plugin-rollbar'; import { RollbarClient, rollbarApiRef } from '@backstage/plugin-rollbar'; // ... - -builder.add( - rollbarApiRef, - new RollbarClient({ - apiOrigin: backendUrl, - basePath: '/rollbar', - }), -); - -// Alternatively you can use the mock client -// builder.add(rollbarApiRef, new RollbarMockClient()); +builder.add(rollbarApiRef, new RollbarClient({ discoveryApi })); ``` -5. Run app with `yarn start` and navigate to `/rollbar` +5. Add to the app `EntityPage` component: + +```ts +// packages/app/src/components/catalog/EntityPage.tsx +import { Router as RollbarRouter } from '@backstage/plugin-rollbar'; + +// ... +const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( + + // ... + } + /> + +); +``` + +6. Setup the `app.config.yaml` and account token environment variable + +```yaml +# app.config.yaml +rollbar: + organization: spotify + accountToken: + $secret: + env: ROLLBAR_ACCOUNT_TOKEN +``` + +7. Annotate entities with the rollbar project slug + +```yaml +# pump-station-catalog-component.yaml +# ... +metadata: + annotations: + rollbar.com/project-slug: organization-name/project-name + # -- or just --- + rollbar.com/project-slug: project-name +``` + +8. Run app with `yarn start` and navigate to `/rollbar` or a catalog entity ## Features -- List rollbar projects -- View top active items for each project +- List rollbar entities that are annotated with `rollbar.com/project-slug` +- View top active items for each rollbar annotated entity ## Limitations diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 9c5343247c..1708dc31ce 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -21,7 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/catalog-model": "^0.1.1-alpha.21", "@backstage/core": "^0.1.1-alpha.21", + "@backstage/plugin-catalog": "^0.1.1-alpha.21", "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", diff --git a/plugins/rollbar/src/api/RollbarApi.ts b/plugins/rollbar/src/api/RollbarApi.ts index c08bc08d48..bde6c5e9f2 100644 --- a/plugins/rollbar/src/api/RollbarApi.ts +++ b/plugins/rollbar/src/api/RollbarApi.ts @@ -29,6 +29,7 @@ export const rollbarApiRef = createApiRef({ export interface RollbarApi { getAllProjects(): Promise; + getProject(projectName: string): Promise; getTopActiveItems( project: string, hours?: number, diff --git a/plugins/rollbar/src/api/RollbarClient.ts b/plugins/rollbar/src/api/RollbarClient.ts index 1862ad8270..e612890655 100644 --- a/plugins/rollbar/src/api/RollbarClient.ts +++ b/plugins/rollbar/src/api/RollbarClient.ts @@ -30,9 +30,11 @@ export class RollbarClient implements RollbarApi { } async getAllProjects(): Promise { - const path = `/projects`; + return await this.get(`/projects`); + } - return await this.get(path); + async getProject(projectName: string): Promise { + return await this.get(`/projects/${projectName}`); } async getTopActiveItems( @@ -40,15 +42,13 @@ export class RollbarClient implements RollbarApi { hours = 24, environment = 'production', ): Promise { - const path = `/projects/${project}/top_active_items?environment=${environment}&hours=${hours}`; - - return await this.get(path); + return await this.get( + `/projects/${project}/top_active_items?environment=${environment}&hours=${hours}`, + ); } async getProjectItems(project: string): Promise { - const path = `/projects/${project}/items`; - - return await this.get(path); + return await this.get(`/projects/${project}/items`); } private async get(path: string): Promise { diff --git a/plugins/rollbar/src/api/RollbarMockClient.ts b/plugins/rollbar/src/api/RollbarMockClient.ts deleted file mode 100644 index cab9a0d23a..0000000000 --- a/plugins/rollbar/src/api/RollbarMockClient.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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. - */ - -/* eslint-disable @typescript-eslint/no-unused-vars */ - -import { RollbarApi } from './RollbarApi'; -import { - RollbarItemsResponse, - RollbarProject, - RollbarTopActiveItem, -} from './types'; - -export class RollbarMockClient implements RollbarApi { - async getAllProjects(): Promise { - return Promise.resolve([ - { id: 123, name: 'project-a', accountId: 1, status: 'enabled' }, - { id: 356, name: 'project-b', accountId: 1, status: 'enabled' }, - { id: 789, name: 'project-c', accountId: 1, status: 'enabled' }, - ]); - } - - async getTopActiveItems( - _project: string, - _hours = 24, - _environment = 'production', - ): Promise { - const createItem = (id: number): RollbarTopActiveItem => ({ - item: { - id, - counter: id, - environment: 'production', - framework: 2, - lastOccurrenceTimestamp: new Date().getTime() / 1000, - level: 50, - occurrences: 100, - projectId: 12345, - title: `Some error occurred in service - ${id}`, - uniqueOccurrences: 10, - }, - counts: Array.from({ length: 168 }, () => - Math.floor(Math.random() * 100), - ), - }); - - const items = Array.from({ length: 10 }, (_, i) => createItem(i)); - - return Promise.resolve(items); - } - - async getProjectItems(_project: string): Promise { - return Promise.resolve({ - items: [], - page: 0, - totalCount: 0, - }); - } -} diff --git a/plugins/rollbar/src/api/index.ts b/plugins/rollbar/src/api/index.ts new file mode 100644 index 0000000000..f45ff5bf8a --- /dev/null +++ b/plugins/rollbar/src/api/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './RollbarApi'; +export * from './RollbarClient'; diff --git a/plugins/rollbar/src/components/EntityPageRollbar/EntityPageRollbar.tsx b/plugins/rollbar/src/components/EntityPageRollbar/EntityPageRollbar.tsx new file mode 100644 index 0000000000..888b9aabe0 --- /dev/null +++ b/plugins/rollbar/src/components/EntityPageRollbar/EntityPageRollbar.tsx @@ -0,0 +1,27 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { Entity } from '@backstage/catalog-model'; +import { RollbarProject } from '../RollbarProject/RollbarProject'; + +type Props = { + entity: Entity; +}; + +export const EntityPageRollbar = ({ entity }: Props) => { + return ; +}; diff --git a/plugins/rollbar/src/components/RollbarPage/RollbarPage.test.tsx b/plugins/rollbar/src/components/RollbarHome/RollbarHome.test.tsx similarity index 80% rename from plugins/rollbar/src/components/RollbarPage/RollbarPage.test.tsx rename to plugins/rollbar/src/components/RollbarHome/RollbarHome.test.tsx index 11263ed820..c6ee052c0e 100644 --- a/plugins/rollbar/src/components/RollbarPage/RollbarPage.test.tsx +++ b/plugins/rollbar/src/components/RollbarHome/RollbarHome.test.tsx @@ -21,13 +21,14 @@ import { ConfigApi, configApiRef, } from '@backstage/core'; +import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog'; import { wrapInTestApp } from '@backstage/test-utils'; import { render } from '@testing-library/react'; import { RollbarApi, rollbarApiRef } from '../../api/RollbarApi'; import { RollbarProject } from '../../api/types'; -import { RollbarPage } from './RollbarPage'; +import { RollbarHome } from './RollbarHome'; -describe('RollbarPage component', () => { +describe('RollbarHome component', () => { const projects: RollbarProject[] = [ { id: 123, name: 'abc', accountId: 1, status: 'enabled' }, { id: 456, name: 'xyz', accountId: 1, status: 'enabled' }, @@ -47,6 +48,14 @@ describe('RollbarPage component', () => { apis={ApiRegistry.from([ [rollbarApiRef, rollbarApi], [configApiRef, config], + [ + catalogApiRef, + ({ + async getEntities() { + return []; + }, + } as Partial) as CatalogApi, + ], ])} > {children} @@ -55,7 +64,7 @@ describe('RollbarPage component', () => { ); it('should render rollbar landing page', async () => { - const rendered = renderWrapped(); + const rendered = renderWrapped(); expect(rendered.getByText(/Rollbar/)).toBeInTheDocument(); }); }); diff --git a/plugins/rollbar/src/components/RollbarLayout/RollbarLayout.tsx b/plugins/rollbar/src/components/RollbarHome/RollbarHome.tsx similarity index 51% rename from plugins/rollbar/src/components/RollbarLayout/RollbarLayout.tsx rename to plugins/rollbar/src/components/RollbarHome/RollbarHome.tsx index c0524fabc5..d09c2054b1 100644 --- a/plugins/rollbar/src/components/RollbarLayout/RollbarLayout.tsx +++ b/plugins/rollbar/src/components/RollbarHome/RollbarHome.tsx @@ -14,42 +14,26 @@ * limitations under the License. */ -import React, { ReactNode } from 'react'; -import { - Header, - HeaderLabel, - Page, - pageTheme, - Content, - ContentHeader, - SupportButton, -} from '@backstage/core'; -import { Grid } from '@material-ui/core'; +import React from 'react'; +import { Content, Header, Page, pageTheme } from '@backstage/core'; +import { RollbarProjectTable } from '../RollbarProjectTable/RollbarProjectTable'; +import { useRollbarEntities } from '../../hooks/useRollbarEntities'; -type Props = { - title?: string; - children: ReactNode; -}; +export const RollbarHome = () => { + const { entities, loading, error } = useRollbarEntities(); -export const RollbarLayout = ({ title = 'Dashboard', children }: Props) => { return (
- - -
+ /> - - - Rollbar plugin allows you to preview issues and navigate to rollbar. - - - - {children} - +
); diff --git a/plugins/rollbar/src/components/RollbarProject/RollbarProject.tsx b/plugins/rollbar/src/components/RollbarProject/RollbarProject.tsx new file mode 100644 index 0000000000..34881ba560 --- /dev/null +++ b/plugins/rollbar/src/components/RollbarProject/RollbarProject.tsx @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { Entity } from '@backstage/catalog-model'; +import { useTopActiveItems } from '../../hooks/useTopActiveItems'; +import { RollbarTopItemsTable } from '../RollbarTopItemsTable/RollbarTopItemsTable'; + +type Props = { + entity: Entity; +}; + +export const RollbarProject = ({ entity }: Props) => { + const { items, organization, project, loading, error } = useTopActiveItems( + entity, + ); + + return ( + + ); +}; diff --git a/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.test.tsx b/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.test.tsx index e4919a68bd..ad3d4a554d 100644 --- a/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.test.tsx +++ b/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.test.tsx @@ -26,6 +26,7 @@ import { render } from '@testing-library/react'; import { RollbarApi, rollbarApiRef } from '../../api/RollbarApi'; import { RollbarTopActiveItem } from '../../api/types'; import { RollbarProjectPage } from './RollbarProjectPage'; +import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog'; describe('RollbarProjectPage component', () => { const items: RollbarTopActiveItem[] = [ @@ -60,6 +61,17 @@ describe('RollbarProjectPage component', () => { apis={ApiRegistry.from([ [rollbarApiRef, rollbarApi], [configApiRef, config], + [ + catalogApiRef, + ({ + async getEntityByName() { + return { + metadata: { name: 'foo' }, + spec: { owner: 'bar', lifecycle: 'experimental' }, + } as any; + }, + } as Partial) as CatalogApi, + ], ])} > {children} @@ -69,6 +81,6 @@ describe('RollbarProjectPage component', () => { it('should render rollbar project page', async () => { const rendered = renderWrapped(); - expect(rendered.getByText(/Top Active Items/)).toBeInTheDocument(); + expect(rendered.getByText(/Rollbar/)).toBeInTheDocument(); }); }); diff --git a/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.tsx b/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.tsx index 6635a72086..b18357e310 100644 --- a/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.tsx +++ b/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.tsx @@ -15,39 +15,22 @@ */ import React from 'react'; -import { useParams } from 'react-router-dom'; -import { useAsync } from 'react-use'; -import { configApiRef, useApi } from '@backstage/core'; -import { rollbarApiRef } from '../../api/RollbarApi'; -import { RollbarLayout } from '../RollbarLayout/RollbarLayout'; -import { RollbarTopItemsTable } from '../RollbarTopItemsTable/RollbarTopItemsTable'; +import { Content, Header, HeaderLabel, Page, pageTheme } from '@backstage/core'; +import { useCatalogEntity } from '../../hooks/useCatalogEntity'; +import { RollbarProject } from '../RollbarProject/RollbarProject'; export const RollbarProjectPage = () => { - const configApi = useApi(configApiRef); - const rollbarApi = useApi(rollbarApiRef); - const org = - configApi.getOptionalString('rollbar.organization') ?? - configApi.getString('organization.name'); - const { componentId } = useParams() as { - componentId: string; - }; - const { value, loading, error } = useAsync(() => - rollbarApi - .getTopActiveItems(componentId, 168) - .then(data => - data.sort((a, b) => b.item.occurrences - a.item.occurrences), - ), - ); + const { entity } = useCatalogEntity(); return ( - - - + +
+ + +
+ + {entity ? : 'Loading'} + +
); }; diff --git a/plugins/rollbar/src/components/RollbarPage/RollbarProjectTable.tsx b/plugins/rollbar/src/components/RollbarProjectTable/RollbarProjectTable.tsx similarity index 57% rename from plugins/rollbar/src/components/RollbarPage/RollbarProjectTable.tsx rename to plugins/rollbar/src/components/RollbarProjectTable/RollbarProjectTable.tsx index fdf90c468e..5b3631ade9 100644 --- a/plugins/rollbar/src/components/RollbarPage/RollbarProjectTable.tsx +++ b/plugins/rollbar/src/components/RollbarProjectTable/RollbarProjectTable.tsx @@ -15,68 +15,49 @@ */ import React from 'react'; -import { Link as RouterLink } from 'react-router-dom'; +import { Link as RouterLink, generatePath } from 'react-router-dom'; +import { Table, TableColumn } from '@backstage/core'; +import { Entity } from '@backstage/catalog-model'; import { Link } from '@material-ui/core'; import { Alert } from '@material-ui/lab'; -import OpenInNewIcon from '@material-ui/icons/OpenInNew'; -import { Table, TableColumn } from '@backstage/core'; -import { RollbarProject } from '../../api/types'; - -const projectUrl = (org: string, id: number) => - `https://rollbar.com/${org}/all/items/?projects=${id}`; +import { entityRouteRef } from '../../routes'; const columns: TableColumn[] = [ - { - title: 'ID', - field: 'id', - type: 'numeric', - align: 'left', - width: '100px', - }, { title: 'Name', - field: 'name', + field: 'metadata.name', type: 'string', highlight: true, - render: (row: Partial) => ( - - {row.name} - - ), - }, - { - title: 'Status', - field: 'status', - type: 'string', - }, - { - title: 'Open', - width: '10%', - render: (row: any) => ( + render: (entity: any) => ( - + {entity.metadata.name} ), }, + { + title: 'Description', + field: 'metadata.description', + }, ]; type Props = { - projects: RollbarProject[]; + entities: Entity[]; loading: boolean; - organization: string; error?: any; }; -export const RollbarProjectTable = ({ - projects, - organization, - loading, - error, -}: Props) => { +export const RollbarProjectTable = ({ entities, loading, error }: Props) => { if (error) { return (
@@ -92,14 +73,13 @@ export const RollbarProjectTable = ({ isLoading={loading} columns={columns} options={{ - padding: 'dense', search: true, paging: true, pageSize: 10, showEmptyDataSourceMessage: !loading, }} title="Projects" - data={projects.map(p => ({ organization, ...p }))} + data={entities} /> ); }; diff --git a/plugins/rollbar/src/components/RollbarTopItemsTable/RollbarTopItemsTable.tsx b/plugins/rollbar/src/components/RollbarTopItemsTable/RollbarTopItemsTable.tsx index 913ecca28a..a04155a0c3 100644 --- a/plugins/rollbar/src/components/RollbarTopItemsTable/RollbarTopItemsTable.tsx +++ b/plugins/rollbar/src/components/RollbarTopItemsTable/RollbarTopItemsTable.tsx @@ -16,17 +16,15 @@ import React from 'react'; import { Table, TableColumn } from '@backstage/core'; -import { Link } from '@material-ui/core'; +import { Box, Link, Typography } from '@material-ui/core'; import { Alert } from '@material-ui/lab'; import { RollbarFrameworkId, RollbarLevel, RollbarTopActiveItem, } from '../../api/types'; -import { RollbarTrendGraph } from '../RollbarTrendGraph/RollbarTrendGraph'; - -const itemUrl = (org: string, project: string, id: number) => - `https://rollbar.com/${org}/${project}/items/${id}`; +import { buildItemUrl } from '../../utils'; +import { TrendGraph } from '../TrendGraph/TrendGraph'; const columns: TableColumn[] = [ { @@ -37,7 +35,7 @@ const columns: TableColumn[] = [ width: '70px', render: (data: any) => ( @@ -54,7 +52,7 @@ const columns: TableColumn[] = [ { title: 'Trend', sorting: false, - render: (data: any) => , + render: (data: any) => , }, { title: 'Occurrences', @@ -127,7 +125,12 @@ export const RollbarTopItemsTable = ({ pageSize: 5, showEmptyDataSourceMessage: !loading, }} - title="Top Active Items" + title={ + + + Top Active Items / {project} + + } data={items.map(i => ({ org: organization, project, ...i }))} /> ); diff --git a/plugins/rollbar/src/components/Router.tsx b/plugins/rollbar/src/components/Router.tsx new file mode 100644 index 0000000000..c765c14f14 --- /dev/null +++ b/plugins/rollbar/src/components/Router.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { Routes, Route } from 'react-router'; +import { Entity } from '@backstage/catalog-model'; +import { WarningPanel } from '@backstage/core'; +import { catalogRouteRef } from '../routes'; +import { ROLLBAR_ANNOTATION } from '../constants'; +import { EntityPageRollbar } from './EntityPageRollbar/EntityPageRollbar'; + +export const isPluginApplicableToEntity = (entity: Entity) => + entity.metadata.annotations?.[ROLLBAR_ANNOTATION] !== ''; + +type Props = { + entity: Entity; +}; + +export const Router = ({ entity }: Props) => + !isPluginApplicableToEntity(entity) ? ( + +
+        entity.metadata.annotations['{ROLLBAR_ANNOTATION}']` key is missing on
+        the entity.
+      
+
+ ) : ( + + } + /> + + ); diff --git a/plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.test.tsx b/plugins/rollbar/src/components/TrendGraph/TrendGraph.test.tsx similarity index 81% rename from plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.test.tsx rename to plugins/rollbar/src/components/TrendGraph/TrendGraph.test.tsx index 1f9d317e5b..ac1a328521 100644 --- a/plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.test.tsx +++ b/plugins/rollbar/src/components/TrendGraph/TrendGraph.test.tsx @@ -17,15 +17,13 @@ import React from 'react'; import { render } from '@testing-library/react'; import { wrapInTestApp } from '@backstage/test-utils'; -import { RollbarTrendGraph } from './RollbarTrendGraph'; +import { TrendGraph } from './TrendGraph'; -describe('RollbarTrendGraph component', () => { +describe('TrendGraph component', () => { it('should render a trend graph sparkline', async () => { const mockCounts = [1, 2, 3, 4]; const rendered = render( - wrapInTestApp( - , - ), + wrapInTestApp(), ); expect(rendered).toBeTruthy(); }); diff --git a/plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.tsx b/plugins/rollbar/src/components/TrendGraph/TrendGraph.tsx similarity index 93% rename from plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.tsx rename to plugins/rollbar/src/components/TrendGraph/TrendGraph.tsx index 4f0b53eb4a..5d518817bc 100644 --- a/plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.tsx +++ b/plugins/rollbar/src/components/TrendGraph/TrendGraph.tsx @@ -21,7 +21,7 @@ type Props = { counts: number[]; }; -export const RollbarTrendGraph = ({ counts }: Props) => { +export const TrendGraph = ({ counts }: Props) => { return ( diff --git a/plugins/rollbar/src/constants.ts b/plugins/rollbar/src/constants.ts new file mode 100644 index 0000000000..e3a89d655e --- /dev/null +++ b/plugins/rollbar/src/constants.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 const ROLLBAR_ANNOTATION = 'rollbar.com/project-slug'; diff --git a/plugins/rollbar/src/hooks/useCatalogEntity.ts b/plugins/rollbar/src/hooks/useCatalogEntity.ts new file mode 100644 index 0000000000..172d057738 --- /dev/null +++ b/plugins/rollbar/src/hooks/useCatalogEntity.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { useAsync } from 'react-use'; +import { useApi } from '@backstage/core'; +import { + catalogApiRef, + useEntityCompoundName, +} from '@backstage/plugin-catalog'; + +export function useCatalogEntity() { + const catalogApi = useApi(catalogApiRef); + const { namespace, name } = useEntityCompoundName(); + + const { value: entity, error, loading } = useAsync( + () => catalogApi.getEntityByName({ kind: 'Component', namespace, name }), + [catalogApi, namespace, name], + ); + + return { entity, error, loading }; +} diff --git a/plugins/rollbar/src/hooks/useProject.ts b/plugins/rollbar/src/hooks/useProject.ts new file mode 100644 index 0000000000..5d61b63ffb --- /dev/null +++ b/plugins/rollbar/src/hooks/useProject.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { useApi, configApiRef } from '@backstage/core'; +import { Entity } from '@backstage/catalog-model'; +import { ROLLBAR_ANNOTATION } from '../constants'; + +export function useProjectSlugFromEntity(entity: Entity) { + const configApi = useApi(configApiRef); + + const [project, organization] = ( + entity?.metadata?.annotations?.[ROLLBAR_ANNOTATION] ?? '' + ) + .split('/') + .reverse(); + + return { + project, + organization: + organization ?? + configApi.getOptionalString('rollbar.organization') ?? + configApi.getString('organization.name'), + }; +} diff --git a/plugins/rollbar/src/components/RollbarPage/RollbarPage.tsx b/plugins/rollbar/src/hooks/useRollbarEntities.ts similarity index 55% rename from plugins/rollbar/src/components/RollbarPage/RollbarPage.tsx rename to plugins/rollbar/src/hooks/useRollbarEntities.ts index 0460b4a308..3edf34d1f3 100644 --- a/plugins/rollbar/src/components/RollbarPage/RollbarPage.tsx +++ b/plugins/rollbar/src/hooks/useRollbarEntities.ts @@ -14,29 +14,25 @@ * limitations under the License. */ -import React from 'react'; import { useAsync } from 'react-use'; -import { configApiRef, useApi } from '@backstage/core'; -import { rollbarApiRef } from '../../api/RollbarApi'; -import { RollbarLayout } from '../RollbarLayout/RollbarLayout'; -import { RollbarProjectTable } from './RollbarProjectTable'; +import { useApi, configApiRef } from '@backstage/core'; +import { catalogApiRef } from '@backstage/plugin-catalog'; +import { ROLLBAR_ANNOTATION } from '../constants'; -export const RollbarPage = () => { +export function useRollbarEntities() { const configApi = useApi(configApiRef); - const rollbarApi = useApi(rollbarApiRef); - const org = + const catalogApi = useApi(catalogApiRef); + + const organization = configApi.getOptionalString('rollbar.organization') ?? configApi.getString('organization.name'); - const { value, loading, error } = useAsync(() => rollbarApi.getAllProjects()); - return ( - - - - ); -}; + const { value, loading, error } = useAsync(async () => { + const entities = await catalogApi.getEntities(); + return entities.filter(entity => { + return !!entity.metadata.annotations?.[ROLLBAR_ANNOTATION]; + }); + }, [catalogApi]); + + return { entities: value, organization, loading, error }; +} diff --git a/plugins/rollbar/src/hooks/useTopActiveItems.ts b/plugins/rollbar/src/hooks/useTopActiveItems.ts new file mode 100644 index 0000000000..cb1689355f --- /dev/null +++ b/plugins/rollbar/src/hooks/useTopActiveItems.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { useAsync } from 'react-use'; +import { useApi } from '@backstage/core'; +import { Entity } from '@backstage/catalog-model'; +import { rollbarApiRef } from '../api'; +import { RollbarTopActiveItem } from '../api/types'; +import { useProjectSlugFromEntity } from './useProject'; + +export function useTopActiveItems(entity: Entity) { + const api = useApi(rollbarApiRef); + const { organization, project } = useProjectSlugFromEntity(entity); + const { value, loading, error } = useAsync(() => { + if (!project) { + return Promise.resolve([]); + } + + return api + .getTopActiveItems(project, 168) + .then(data => + data.sort((a, b) => b.item.occurrences - a.item.occurrences), + ); + }, [api, organization, project, entity]); + + return { + items: value as RollbarTopActiveItem[], + organization, + project, + loading, + error, + }; +} diff --git a/plugins/rollbar/src/index.ts b/plugins/rollbar/src/index.ts index 3a8288a545..2a31c91a13 100644 --- a/plugins/rollbar/src/index.ts +++ b/plugins/rollbar/src/index.ts @@ -15,6 +15,9 @@ */ export { plugin } from './plugin'; -export * from './api/RollbarApi'; -export { RollbarClient } from './api/RollbarClient'; -export { RollbarMockClient } from './api/RollbarMockClient'; +export * from './api'; +export * from './routes'; +export { Router } from './components/Router'; +export { RollbarProjectPage } from './components/RollbarProjectPage/RollbarProjectPage'; +export { EntityPageRollbar } from './components/EntityPageRollbar/EntityPageRollbar'; +export { ROLLBAR_ANNOTATION } from './constants'; diff --git a/plugins/rollbar/src/plugin.ts b/plugins/rollbar/src/plugin.ts index d682750a83..2cf464a8f6 100644 --- a/plugins/rollbar/src/plugin.ts +++ b/plugins/rollbar/src/plugin.ts @@ -15,14 +15,14 @@ */ import { createPlugin } from '@backstage/core'; -import { RollbarPage } from './components/RollbarPage/RollbarPage'; +import { rootRouteRef, entityRouteRef } from './routes'; +import { RollbarHome } from './components/RollbarHome/RollbarHome'; import { RollbarProjectPage } from './components/RollbarProjectPage/RollbarProjectPage'; -import { rootRoute, rootProjectRoute } from './routes'; export const plugin = createPlugin({ id: 'rollbar', register({ router }) { - router.addRoute(rootRoute, RollbarPage); - router.addRoute(rootProjectRoute, RollbarProjectPage); + router.addRoute(rootRouteRef, RollbarHome); + router.addRoute(entityRouteRef, RollbarProjectPage); }, }); diff --git a/plugins/rollbar/src/routes.ts b/plugins/rollbar/src/routes.ts index ecf20f3167..b514dff3c8 100644 --- a/plugins/rollbar/src/routes.ts +++ b/plugins/rollbar/src/routes.ts @@ -16,12 +16,21 @@ import { createRouteRef } from '@backstage/core'; -export const rootRoute = createRouteRef({ +const NoIcon = () => null; + +export const rootRouteRef = createRouteRef({ + icon: NoIcon, path: '/rollbar', title: 'Rollbar Home', }); -export const rootProjectRoute = createRouteRef({ - path: '/rollbar/:componentId/*', +export const entityRouteRef = createRouteRef({ + path: '/rollbar/:optionalNamespaceAndName', + title: 'Rollbar', +}); + +export const catalogRouteRef = createRouteRef({ + icon: NoIcon, + path: '', title: 'Rollbar', }); diff --git a/plugins/rollbar/src/utils/index.ts b/plugins/rollbar/src/utils/index.ts new file mode 100644 index 0000000000..1c8f8ff39e --- /dev/null +++ b/plugins/rollbar/src/utils/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 const buildProjectUrl = (org: string, id: number) => + `https://rollbar.com/${org}/all/items/?projects=${id}`; + +export const buildItemUrl = (org: string, project: string, id: number) => + `https://rollbar.com/${org}/${project}/items/${id}`; From 9bfef247ccb6ddbfc1abeab969289b87259ccb4f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 7 Sep 2020 14:58:56 +0200 Subject: [PATCH 347/359] cli: add skeleton tarball for docker build caching --- packages/backend/Dockerfile | 9 ++++-- .../cli/src/commands/backend/buildImage.ts | 1 + packages/cli/src/lib/packager/index.ts | 32 +++++++++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/packages/backend/Dockerfile b/packages/backend/Dockerfile index 8d17821101..a7bd814b19 100644 --- a/packages/backend/Dockerfile +++ b/packages/backend/Dockerfile @@ -2,10 +2,15 @@ FROM node:12 WORKDIR /usr/src/app +# Copy repo skeleton first, to avoid unnecessary docker cache invalidation. +# The skeleton contains the package.json of each package in the monorepo, +# and along with yarn.lock and the root package.json, that's enough to run yarn install. +ADD yarn.lock package.json skeleton.tar ./ + +RUN yarn install --frozen-lockfile --production + # This will copy the contents of the dist-workspace when running the build-image command. # Do not use this Dockerfile outside of that command, as it will copy in the source code instead. COPY . . -RUN yarn install --frozen-lockfile --production - CMD ["node", "packages/backend"] diff --git a/packages/cli/src/commands/backend/buildImage.ts b/packages/cli/src/commands/backend/buildImage.ts index 36d4ef7129..b6e9da7b39 100644 --- a/packages/cli/src/commands/backend/buildImage.ts +++ b/packages/cli/src/commands/backend/buildImage.ts @@ -41,6 +41,7 @@ export default async (cmd: Command) => { ...appConfigs, { src: paths.resolveTarget('Dockerfile'), dest: 'Dockerfile' }, ], + skeleton: 'skeleton.tar', }); console.log(`Dist workspace ready at ${tempDistWorkspace}`); diff --git a/packages/cli/src/lib/packager/index.ts b/packages/cli/src/lib/packager/index.ts index 5584e1fc21..564317e46a 100644 --- a/packages/cli/src/lib/packager/index.ts +++ b/packages/cli/src/lib/packager/index.ts @@ -15,10 +15,14 @@ */ import fs from 'fs-extra'; -import { resolve as resolvePath, relative as relativePath } from 'path'; +import { + join as joinPath, + resolve as resolvePath, + relative as relativePath, +} from 'path'; import { paths } from '../paths'; import { run } from '../run'; -import tar from 'tar'; +import tar, { CreateOptions } from 'tar'; import { tmpdir } from 'os'; type LernaPackage = { @@ -53,6 +57,12 @@ type Options = { * If set to true, the target packages are built before they are packaged into the workspace. */ buildDependencies?: boolean; + + /** + * If set, creates a skeleton tarball that contains all package.json files + * with the same structure as the workspace dir. + */ + skeleton?: 'skeleton.tar'; }; /** @@ -89,6 +99,24 @@ export async function createDistWorkspace( const dest = typeof file === 'string' ? file : file.dest; await fs.copy(paths.resolveTargetRoot(src), resolvePath(targetDir, dest)); } + + if (options.skeleton) { + const skeletonFiles = targets.map(target => { + const dir = relativePath(paths.targetRoot, target.location); + return joinPath(dir, 'package.json'); + }); + + await tar.create( + { + file: resolvePath(targetDir, options.skeleton), + cwd: targetDir, + portable: true, + noMtime: true, + } as CreateOptions & { noMtime: boolean }, + skeletonFiles, + ); + } + return targetDir; } From b6fcf9985356371e4e1e34900827f288a919e14f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Mon, 7 Sep 2020 15:35:39 +0200 Subject: [PATCH 348/359] Fix typo in gRPC template (#2315) --- .../sample-templates/springboot-grpc-template/template.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml index c6189fe7c8..32b9383855 100644 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml @@ -2,11 +2,12 @@ apiVersion: backstage.io/v1alpha1 kind: Template metadata: name: springboot-template - title: Spring Boot GRPC Service + title: Spring Boot gRPC Service description: Create a simple microservice using gRPC and Spring Boot Java tags: - recommended - java + - grpc spec: owner: service@example.com templater: cookiecutter @@ -29,4 +30,4 @@ spec: title: Port type: integer default: 8080 - description: The port to run the GRPC service on + description: The port to run the gRPC service on From 969cf48858cd34e0dc400dc8039d1492a0fc29ec Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Mon, 7 Sep 2020 16:56:03 +0200 Subject: [PATCH 349/359] [TechDocs] migrate techdocs to use new routing/catalog api (#2312) * migrate techdocs to use new routing * use new techdocs routing in default app * new EntityPageDocs and Router components * remove unused code * remove unused import --- packages/app/src/App.tsx | 2 + .../app/src/components/catalog/EntityPage.tsx | 17 ++++++- .../default-app/packages/app/src/App.tsx | 2 + .../app/src/components/catalog/EntityPage.tsx | 17 +++++++ .../src/EntityPageDocs.tsx} | 19 ++++---- plugins/techdocs/src/Router.tsx | 48 +++++++++++++++++++ plugins/techdocs/src/index.ts | 1 + plugins/techdocs/src/plugin.ts | 15 +++--- .../src/reader/components/TechDocsHome.tsx | 11 +++-- 9 files changed, 108 insertions(+), 24 deletions(-) rename plugins/{catalog/src/components/EntityPageDocs/EntityDocsPage.tsx => techdocs/src/EntityPageDocs.tsx} (72%) create mode 100644 plugins/techdocs/src/Router.tsx diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index cb38726716..8ddf53783d 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -27,6 +27,7 @@ import { apis } from './apis'; import { hot } from 'react-hot-loader/root'; import { providers } from './identityProviders'; import { Router as CatalogRouter } from '@backstage/plugin-catalog'; +import { Router as DocsRouter } from '@backstage/plugin-techdocs'; import { Route, Routes, Navigate } from 'react-router'; import { EntityPage } from './components/catalog/EntityPage'; @@ -58,6 +59,7 @@ const AppRoutes = () => ( path="/catalog/*" element={} /> + } /> {...deprecatedAppRoutes} diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 34fc5c470e..8783df9452 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -16,6 +16,7 @@ import { Router as ApiDocsRouter } from '@backstage/plugin-api-docs'; import { Router as GitHubActionsRouter } from '@backstage/plugin-github-actions'; import { Router as SentryRouter } from '@backstage/plugin-sentry'; +import { EmbeddedDocsRouter as DocsRouter } from '@backstage/plugin-techdocs'; import React from 'react'; import { AboutCard, @@ -55,6 +56,11 @@ const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( title="API" element={} /> + } + /> ); @@ -75,9 +81,13 @@ const WebsiteEntityPage = ({ entity }: { entity: Entity }) => ( title="Sentry" element={} /> + } + /> ); - const DefaultEntityPage = ({ entity }: { entity: Entity }) => ( ( title="Overview" element={} /> + } + /> ); diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index f002bbc7c7..3e6a0ef00e 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -10,6 +10,7 @@ import * as plugins from './plugins'; import { AppSidebar } from './sidebar'; import { Route, Routes, Navigate } from 'react-router'; import { Router as CatalogRouter } from '@backstage/plugin-catalog'; +import { Router as DocsRouter } from '@backstage/plugin-techdocs'; import { EntityPage } from './components/catalog/EntityPage'; const app = createApp({ @@ -33,6 +34,7 @@ const App: FC<{}> = () => ( path="/catalog/*" element={} /> + } /> {deprecatedAppRoutes} diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx index be22003f4f..559a838135 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -15,6 +15,8 @@ */ import { Router as ApiDocsRouter } from '@backstage/plugin-api-docs'; import { Router as GitHubActionsRouter } from '@backstage/plugin-github-actions'; +import { EmbeddedDocsRouter as DocsRouter } from '@backstage/plugin-techdocs'; + import React from 'react'; import { EntityPageLayout, @@ -44,6 +46,11 @@ const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( title="API" element={} /> + } + /> ); @@ -59,6 +66,11 @@ const WebsiteEntityPage = ({ entity }: { entity: Entity }) => ( title="CI/CD" element={} /> + } + /> ); @@ -69,6 +81,11 @@ const DefaultEntityPage = ({ entity }: { entity: Entity }) => ( title="Overview" element={} /> + } + /> ); diff --git a/plugins/catalog/src/components/EntityPageDocs/EntityDocsPage.tsx b/plugins/techdocs/src/EntityPageDocs.tsx similarity index 72% rename from plugins/catalog/src/components/EntityPageDocs/EntityDocsPage.tsx rename to plugins/techdocs/src/EntityPageDocs.tsx index ceba7e3200..d10c7a5932 100644 --- a/plugins/catalog/src/components/EntityPageDocs/EntityDocsPage.tsx +++ b/plugins/techdocs/src/EntityPageDocs.tsx @@ -16,19 +16,16 @@ import React from 'react'; import { Entity } from '@backstage/catalog-model'; -import { Reader } from '@backstage/plugin-techdocs'; -import { Content } from '@backstage/core'; +import { Reader } from './reader'; export const EntityPageDocs = ({ entity }: { entity: Entity }) => { return ( - - - + ); }; diff --git a/plugins/techdocs/src/Router.tsx b/plugins/techdocs/src/Router.tsx new file mode 100644 index 0000000000..45daa3f3d9 --- /dev/null +++ b/plugins/techdocs/src/Router.tsx @@ -0,0 +1,48 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { Route, Routes } from 'react-router-dom'; +import { Entity } from '@backstage/catalog-model'; + +import { + rootRouteRef, + rootDocsRouteRef, + rootCatalogDocsRouteRef, +} from './plugin'; +import { TechDocsHome } from './reader/components/TechDocsHome'; +import { TechDocsPage } from './reader/components/TechDocsPage'; +import { EntityPageDocs } from './EntityPageDocs'; + +export const Router = () => { + return ( + + } /> + } /> + + ); +}; + +export const EmbeddedDocsRouter = ({ entity }: { entity: Entity }) => { + return ( + + } + /> + + ); +}; diff --git a/plugins/techdocs/src/index.ts b/plugins/techdocs/src/index.ts index 2a412d1b20..1726e0daf3 100644 --- a/plugins/techdocs/src/index.ts +++ b/plugins/techdocs/src/index.ts @@ -15,5 +15,6 @@ */ export { plugin } from './plugin'; +export { Router, EmbeddedDocsRouter } from './Router'; export * from './reader'; export * from './api'; diff --git a/plugins/techdocs/src/plugin.ts b/plugins/techdocs/src/plugin.ts index 9b09544427..e75d488595 100644 --- a/plugins/techdocs/src/plugin.ts +++ b/plugins/techdocs/src/plugin.ts @@ -30,23 +30,22 @@ */ import { createPlugin, createRouteRef } from '@backstage/core'; -import { TechDocsHome } from './reader/components/TechDocsHome'; -import { TechDocsPage } from './reader/components/TechDocsPage'; export const rootRouteRef = createRouteRef({ - path: '/docs', + path: '', title: 'TechDocs Landing Page', }); export const rootDocsRouteRef = createRouteRef({ - path: '/docs/:entityId/*', + path: ':entityId/*', + title: 'Docs', +}); + +export const rootCatalogDocsRouteRef = createRouteRef({ + path: '*', title: 'Docs', }); export const plugin = createPlugin({ id: 'techdocs', - register({ router }) { - router.addRoute(rootRouteRef, TechDocsHome); - router.addRoute(rootDocsRouteRef, TechDocsPage); - }, }); diff --git a/plugins/techdocs/src/reader/components/TechDocsHome.tsx b/plugins/techdocs/src/reader/components/TechDocsHome.tsx index 17b9fd36f7..e51de8f57f 100644 --- a/plugins/techdocs/src/reader/components/TechDocsHome.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsHome.tsx @@ -16,11 +16,12 @@ import React from 'react'; import { useAsync } from 'react-use'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, generatePath } from 'react-router-dom'; import { Grid } from '@material-ui/core'; import { ItemCard, Progress, useApi } from '@backstage/core'; import { TechDocsPageWrapper } from './TechDocsPageWrapper'; import { catalogApiRef } from '@backstage/plugin-catalog'; +import { rootDocsRouteRef } from '../../plugin'; export const TechDocsHome = () => { const catalogApi = useApi(catalogApiRef); @@ -67,9 +68,11 @@ export const TechDocsHome = () => { navigate( - `/docs/${entity.kind}:${ - entity.metadata.namespace ?? '' - }:${entity.metadata.name}`, + generatePath(rootDocsRouteRef.path, { + entityId: `${entity.kind}:${ + entity.metadata.namespace ?? '' + }:${entity.metadata.name}`, + }), ) } title={entity.metadata.name} From 3ec99fbc55119810861c9861789d6713709c3603 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 7 Sep 2020 17:30:57 +0200 Subject: [PATCH 350/359] cli: make skipLibCheck default in tsconfig, but provide tsc:full --- .github/workflows/ci.yml | 2 +- .github/workflows/master-win.yml | 2 +- .github/workflows/master.yml | 2 +- docs/getting-started/development-environment.md | 1 + package.json | 1 + packages/cli/config/tsconfig.json | 1 + packages/create-app/templates/default-app/package.json.hbs | 1 + packages/create-app/templates/default-app/tsconfig.json | 3 +-- 8 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2246a1dc09..0a094a10b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,7 +76,7 @@ jobs: run: yarn lerna -- run lint --since origin/master - name: type checking and declarations - run: yarn tsc --incremental false + run: yarn tsc:full - name: build changed packages if: ${{ steps.yarn-lock.outcome == 'success' }} diff --git a/.github/workflows/master-win.yml b/.github/workflows/master-win.yml index 5951721f68..3f65cf9aec 100644 --- a/.github/workflows/master-win.yml +++ b/.github/workflows/master-win.yml @@ -43,7 +43,7 @@ jobs: run: yarn lerna -- run lint - name: type checking and declarations - run: yarn tsc --incremental false + run: yarn tsc:full - name: verify type dependencies run: yarn lint:type-deps diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index b5c4fdf370..469ef20cda 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -51,7 +51,7 @@ jobs: run: yarn lerna -- run lint - name: type checking and declarations - run: yarn tsc --incremental false + run: yarn tsc:full - name: build run: yarn build diff --git a/docs/getting-started/development-environment.md b/docs/getting-started/development-environment.md index 421ce6d2c5..9c75c8eabf 100644 --- a/docs/getting-started/development-environment.md +++ b/docs/getting-started/development-environment.md @@ -65,6 +65,7 @@ yarn storybook # Start local storybook, useful for working on components in @bac yarn workspace @backstage/plugin-welcome start # Serve welcome plugin only, also supports --check yarn tsc # Run typecheck, use --watch for watch mode +yarn tsc:full # Run full type checking, for example without skipLibCheck, use in CI yarn build # Build published versions of packages, depends on tsc diff --git a/package.json b/package.json index 93f8f694a6..c64b7f6951 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "start-backend": "yarn workspace example-backend start", "build": "lerna run build", "tsc": "tsc", + "tsc:full": "tsc --skipLibCheck false --incremental false", "clean": "backstage-cli clean && lerna run clean", "diff": "lerna run diff --", "test": "lerna run test --since origin/master -- --coverage", diff --git a/packages/cli/config/tsconfig.json b/packages/cli/config/tsconfig.json index ce031bc5e7..ef1a8df353 100644 --- a/packages/cli/config/tsconfig.json +++ b/packages/cli/config/tsconfig.json @@ -25,6 +25,7 @@ "removeComments": false, "resolveJsonModule": true, "sourceMap": false, + "skipLibCheck": true, "strict": true, "strictBindCallApply": true, "strictFunctionTypes": true, diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index af4384318a..b95b5bbc5b 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -9,6 +9,7 @@ "start": "yarn workspace app start", "build": "lerna run build", "tsc": "tsc", + "tsc:full": "tsc --skipLibCheck false --incremental false", "clean": "backstage-cli clean && lerna run clean", "diff": "lerna run diff --", "test": "lerna run test --since origin/master -- --coverage", diff --git a/packages/create-app/templates/default-app/tsconfig.json b/packages/create-app/templates/default-app/tsconfig.json index b1ec99b986..ba3f90177d 100644 --- a/packages/create-app/templates/default-app/tsconfig.json +++ b/packages/create-app/templates/default-app/tsconfig.json @@ -9,7 +9,6 @@ "exclude": ["node_modules"], "compilerOptions": { "outDir": "dist-types", - "rootDir": ".", - "skipLibCheck": true + "rootDir": "." } } From 0a1416d476aecc8de93ffa74abf08ca8f6025cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Mon, 7 Sep 2020 19:44:17 +0200 Subject: [PATCH 351/359] [Docs] Clarify what is core features (#2314) --- docs/features/software-catalog/index.md | 2 +- docs/features/software-templates/index.md | 3 ++- microsite/sidebars.json | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/features/software-catalog/index.md b/docs/features/software-catalog/index.md index 07b22808cd..9b5de16472 100644 --- a/docs/features/software-catalog/index.md +++ b/docs/features/software-catalog/index.md @@ -1,7 +1,7 @@ --- id: software-catalog-overview title: Backstage Service Catalog (alpha) -sidebar_label: Backstage Service Catalog +sidebar_label: Overview --- ## What is a Service Catalog? diff --git a/docs/features/software-templates/index.md b/docs/features/software-templates/index.md index b594cfce2a..8989fa35eb 100644 --- a/docs/features/software-templates/index.md +++ b/docs/features/software-templates/index.md @@ -1,6 +1,7 @@ --- id: software-templates-index -title: Software Templates +title: Backstage Software Templates +sidebar_label: Overview --- The Software Templates part of Backstage is a tool that can help you create diff --git a/microsite/sidebars.json b/microsite/sidebars.json index d31c14fd1d..d1d1a0ecc4 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -31,7 +31,7 @@ ] } ], - "Features": [ + "Core Features": [ { "type": "subcategory", "label": "Software Catalog", @@ -46,7 +46,7 @@ }, { "type": "subcategory", - "label": "Software creation templates", + "label": "Software Templates", "ids": [ "features/software-templates/software-templates-index", "features/software-templates/adding-templates", From 66a2a870bccab753ff80f9d093f3519850bb36d2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 7 Sep 2020 20:04:23 +0200 Subject: [PATCH 352/359] plugins/tech-radar: migrate to new plugin pattern and avoid using api for config --- packages/app/src/App.tsx | 5 + packages/app/src/apis.ts | 10 -- .../default-app/packages/app/src/App.tsx | 5 + .../default-app/packages/app/src/apis.ts | 10 -- plugins/tech-radar/README.md | 108 ++++------------- plugins/tech-radar/dev/index.tsx | 12 +- plugins/tech-radar/package.json | 2 +- plugins/tech-radar/src/api.ts | 35 ------ .../src/components/RadarComponent.test.tsx | 2 +- .../src/components/RadarPage.test.tsx | 110 ++++-------------- .../tech-radar/src/components/RadarPage.tsx | 66 ++++++----- plugins/tech-radar/src/index.ts | 2 + plugins/tech-radar/src/plugin.ts | 4 - 13 files changed, 102 insertions(+), 269 deletions(-) diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 8ddf53783d..8482bc9a8a 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -28,6 +28,7 @@ import { hot } from 'react-hot-loader/root'; import { providers } from './identityProviders'; import { Router as CatalogRouter } from '@backstage/plugin-catalog'; import { Router as DocsRouter } from '@backstage/plugin-techdocs'; +import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar'; import { Route, Routes, Navigate } from 'react-router'; import { EntityPage } from './components/catalog/EntityPage'; @@ -60,6 +61,10 @@ const AppRoutes = () => ( element={} /> } /> + } + /> {...deprecatedAppRoutes} diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index b391e95d62..b79a6850bd 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -51,8 +51,6 @@ import { LighthouseRestApi, } from '@backstage/plugin-lighthouse'; -import { techRadarApiRef, TechRadar } from '@backstage/plugin-tech-radar'; - import { CircleCIApi, circleCIApiRef } from '@backstage/plugin-circleci'; import { catalogApiRef, CatalogClient } from '@backstage/plugin-catalog'; @@ -182,14 +180,6 @@ export const apis = (config: ConfigApi) => { }), ); - builder.add( - techRadarApiRef, - new TechRadar({ - width: 1500, - height: 800, - }), - ); - builder.add(catalogApiRef, new CatalogClient({ discoveryApi })); builder.add(scaffolderApiRef, new ScaffolderApi({ discoveryApi })); diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index 3e6a0ef00e..06ef257931 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -11,6 +11,7 @@ import { AppSidebar } from './sidebar'; import { Route, Routes, Navigate } from 'react-router'; import { Router as CatalogRouter } from '@backstage/plugin-catalog'; import { Router as DocsRouter } from '@backstage/plugin-techdocs'; +import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar'; import { EntityPage } from './components/catalog/EntityPage'; const app = createApp({ @@ -35,6 +36,10 @@ const App: FC<{}> = () => ( element={} /> } /> + } + /> {deprecatedAppRoutes} diff --git a/packages/create-app/templates/default-app/packages/app/src/apis.ts b/packages/create-app/templates/default-app/packages/app/src/apis.ts index 14351eaba7..ba4343801f 100644 --- a/packages/create-app/templates/default-app/packages/app/src/apis.ts +++ b/packages/create-app/templates/default-app/packages/app/src/apis.ts @@ -29,8 +29,6 @@ import { TechDocsStorageApi, } from '@backstage/plugin-techdocs'; -import { techRadarApiRef, TechRadar } from '@backstage/plugin-tech-radar'; - import { catalogApiRef, CatalogClient } from '@backstage/plugin-catalog'; import { CircleCIApi, circleCIApiRef } from '@backstage/plugin-circleci'; @@ -72,14 +70,6 @@ export const apis = (config: ConfigApi) => { builder.add(scaffolderApiRef, new ScaffolderApi({ discoveryApi })); - builder.add( - techRadarApiRef, - new TechRadar({ - width: 1500, - height: 800, - }), - ); - builder.add( techdocsStorageApiRef, new TechDocsStorageApi({ apiOrigin: techdocsStorageUrl }), diff --git a/plugins/tech-radar/README.md b/plugins/tech-radar/README.md index fb9d220b8c..cbe4dd22eb 100644 --- a/plugins/tech-radar/README.md +++ b/plugins/tech-radar/README.md @@ -29,71 +29,34 @@ For either simple or advanced installations, you'll need to add the dependency u yarn add @backstage/plugin-tech-radar ``` -### Simple Configuration +### Configuration -In your `apis.ts` set up the simple "out of the box" implementation for Tech Radar: - -```ts -import { ApiHolder, ApiRegistry } from '@backstage/core'; -import { - techRadarApiRef, - TechRadar, -} from '@backstage/plugin-tech-radar'; - -const builder = ApiRegistry.builder(); - -builder.add(techRadarApiRef, new TechRadar({ - width: 1400, - height: 800 -)); - -export default builder.build() as ApiHolder; -``` - -Congrats, you're done! We'll just load it with [example data](src/sampleData.ts) to get you started. Just go to to see it live in action. - -And if you'd like to configure it more, such as providing it with your own data, see the `TechRadarApi` TypeScript interface below for the options: - -```ts -export interface TechRadarComponentProps { - width: number; - height: number; - getData?: () => Promise; - svgProps?: object; -} - -export interface TechRadarApi extends TechRadarComponentProps { - title?: string; - subtitle?: string; -} -``` - -You can see the API directly over at [src/api.ts](./src/api.ts). - -### Advanced Configuration - -This way won't expose an `/tech-radar` path. Instead, you'll need to create your own Backstage plugin and use the Tech Radar as any other React UI component. - -In your Backstage app, run the following command: - -```sh -yarn create-plugin -``` - -In your plugin, in any React component you'd like to import the Tech Radar, do the following: +Modify your app routes to include the Router component exported from the tech radar, for example: ```tsx -import { TechRadarComponent } from '@backstage/plugin-tech-radar'; +import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar'; -function MyCustomRadar() { - return ; -} +// Inside App component + + {/* other routes ... */} + } + /> + {/* other routes ... */} +; ``` -If you'd like to configure it more, see the `TechRadarComponentProps` TypeScript interface for options: +If you'd like to configure it more, see the `TechRadarPageProps` and `TechRadarComponentProps` types for options: ```ts -export interface TechRadarComponentProps { +export type TechRadarPageProps = TechRadarComponentProps & { + title?: string; + subtitle?: string; + pageTitle?: string; +}; + +export interface TechRadarPageProps { width: number; height: number; getData?: () => Promise; @@ -101,8 +64,6 @@ export interface TechRadarComponentProps { } ``` -You can see the API directly over at [src/api.ts](./src/api.ts). - ## Frequently Asked Questions ### Who created the Tech Radar? @@ -111,7 +72,7 @@ You can see the API directly over at [src/api.ts](./src/api.ts). ### How do I load in my own data? -It's simple. In both the Simple (Backstage plugin) and Advanced (React component) configurations, you can pass through a `getData` prop which expects a `Promise` signature. See more in [src/api.ts](./src/api.ts). +It's simple, you can pass through a `getData` prop which expects a `Promise` signature. Here's an example: @@ -133,42 +94,21 @@ const getHardCodedData = () => ], }); -// Simple -builder.add(techRadarApiRef, new TechRadar({ - width: 1400, - height: 800, - getData: getHardCodedData -)); - -// Advanced - +; ``` ### How do I write tests? You can use the `svgProps` option to pass custom React props to the `` element we create for the Tech Radar. This complements well with the `data-testid` attribute and the `@testing-library/react` library we use in Backstage. -```ts -// Simple -builder.add( - techRadarApiRef, - new TechRadar({ - width: 1400, - height: 800, - svgProps: { - 'data-testid': 'tech-radar-svg', - }, - }), -); - -// Advanced +```tsx ; +/> // Then, in your tests... // const { getByTestId } = render(...); diff --git a/plugins/tech-radar/dev/index.tsx b/plugins/tech-radar/dev/index.tsx index ac19b63a90..92eb6da567 100644 --- a/plugins/tech-radar/dev/index.tsx +++ b/plugins/tech-radar/dev/index.tsx @@ -15,14 +15,6 @@ */ import { createDevApp } from '@backstage/dev-utils'; -import { plugin } from '../src/plugin'; -import { techRadarApiRef, TechRadar } from '../src'; +import { plugin } from '../src'; -createDevApp() - .registerPlugin(plugin) - .registerApiFactory({ - implements: techRadarApiRef, - deps: {}, - factory: () => new TechRadar({ width: 1500, height: 800 }), - }) - .render(); +createDevApp().registerPlugin(plugin).render(); diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 4039f40878..f5639c7581 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@backstage/core": "^0.1.1-alpha.21", - "@backstage/test-utils-core": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", diff --git a/plugins/tech-radar/src/api.ts b/plugins/tech-radar/src/api.ts index 856cf4ff3c..7a6e790136 100644 --- a/plugins/tech-radar/src/api.ts +++ b/plugins/tech-radar/src/api.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { createApiRef } from '@backstage/core'; import { MovedState } from './utils/types'; /** @@ -72,37 +71,3 @@ export interface TechRadarApi extends TechRadarComponentProps { subtitle?: string; pageTitle?: string; } - -export const techRadarApiRef = createApiRef({ - id: 'plugin.techradar', - description: 'Used by the Tech Radar to render the visualization', -}); - -export class TechRadar implements TechRadarApi { - // Default columns - public width: TechRadarApi['width']; - public height: TechRadarApi['height']; - public getData: TechRadarApi['getData']; - public svgProps: TechRadarApi['svgProps']; - public title: TechRadarApi['title']; - public subtitle: TechRadarApi['subtitle']; - public pageTitle: TechRadarApi['pageTitle']; - - constructor(overrideOptions: TechRadarApi) { - const defaultOptions: Partial = { - title: 'Tech Radar', - subtitle: 'Pick the recommended technologies for your projects', - pageTitle: 'Company Radar', - }; - - const options = { ...defaultOptions, ...overrideOptions }; - - this.width = options.width; - this.height = options.height; - this.getData = options.getData; - this.svgProps = options.svgProps; - this.title = options.title; - this.subtitle = options.subtitle; - this.pageTitle = options.pageTitle; - } -} diff --git a/plugins/tech-radar/src/components/RadarComponent.test.tsx b/plugins/tech-radar/src/components/RadarComponent.test.tsx index 2f3fcff198..5591d74b82 100644 --- a/plugins/tech-radar/src/components/RadarComponent.test.tsx +++ b/plugins/tech-radar/src/components/RadarComponent.test.tsx @@ -19,7 +19,7 @@ import { render, waitForElement } from '@testing-library/react'; import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; import { ApiRegistry, ApiProvider, errorApiRef } from '@backstage/core'; -import { withLogCollector } from '@backstage/test-utils-core'; +import { withLogCollector } from '@backstage/test-utils'; import GetBBoxPolyfill from '../utils/polyfills/getBBox'; import RadarComponent from './RadarComponent'; diff --git a/plugins/tech-radar/src/components/RadarPage.test.tsx b/plugins/tech-radar/src/components/RadarPage.test.tsx index c318985acd..97fae16380 100644 --- a/plugins/tech-radar/src/components/RadarPage.test.tsx +++ b/plugins/tech-radar/src/components/RadarPage.test.tsx @@ -19,11 +19,10 @@ import { render, waitForElement } from '@testing-library/react'; import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; import { ApiRegistry, ApiProvider, errorApiRef } from '@backstage/core'; -import { withLogCollector } from '@backstage/test-utils-core'; import GetBBoxPolyfill from '../utils/polyfills/getBBox'; -import { techRadarApiRef, TechRadar } from '../index'; -import RadarPage from './RadarPage'; +import { RadarPage } from './RadarPage'; +import { MockErrorApi, wrapInTestApp } from '@backstage/test-utils'; describe('RadarPage', () => { beforeAll(() => { @@ -35,24 +34,18 @@ describe('RadarPage', () => { }); it('should render a progress bar', async () => { - const errorApi = { post: () => {} }; - const techRadarApi = new TechRadar({ + const techRadarProps = { width: 1200, height: 800, svgProps: { 'data-testid': 'tech-radar-svg' }, - }); + }; const { getByTestId, queryByTestId } = render( - - - - - , + wrapInTestApp( + + + , + ), ); expect(getByTestId('progress')).toBeInTheDocument(); @@ -61,24 +54,18 @@ describe('RadarPage', () => { }); it('should render a header with a svg', async () => { - const errorApi = { post: () => {} }; - const techRadarApi = new TechRadar({ + const techRadarProps = { width: 1200, height: 800, svgProps: { 'data-testid': 'tech-radar-svg' }, - }); + }; const { getByText, getByTestId } = render( - - - - - , + wrapInTestApp( + + + , + ), ); await waitForElement(() => getByTestId('tech-radar-svg')); @@ -90,78 +77,29 @@ describe('RadarPage', () => { }); it('should call the errorApi if load fails', async () => { - const errorApi = { post: jest.fn() }; + const errorApi = new MockErrorApi({ collect: true }); const techRadarLoadFail = () => Promise.reject(new Error('404 Page Not Found')); - const techRadarApi = new TechRadar({ + const techRadarProps = { width: 1200, height: 800, getData: techRadarLoadFail, svgProps: { 'data-testid': 'tech-radar-svg' }, - }); + }; const { queryByTestId } = render( - - + + , ); await waitForElement(() => !queryByTestId('progress')); - expect(errorApi.post).toHaveBeenCalledTimes(1); - expect(errorApi.post).toHaveBeenCalledWith(new Error('404 Page Not Found')); + expect(errorApi.getErrors()).toEqual([ + { error: new Error('404 Page Not Found'), context: undefined }, + ]); expect(queryByTestId('tech-radar-svg')).not.toBeInTheDocument(); }); - - it('should not render without errorApiRef', () => { - const techRadarApi = new TechRadar({ - width: 1200, - height: 800, - }); - - expect( - withLogCollector(['error'], () => { - expect(() => { - render( - - - - - , - ); - }).toThrow(); - }).error[0], - ).toMatch( - /^Error: Uncaught \[Error: No implementation available for apiRef{core.error}\]/, - ); - }); - - it('should not render without techRadarApiRef', () => { - const errorApi = { post: () => {} }; - - expect( - withLogCollector(['error'], () => { - expect(() => { - render( - - - - - , - ); - }).toThrow(); - }).error[0], - ).toMatch( - /^Error: Uncaught \[Error: No implementation available for apiRef{plugin.techradar}\]/, - ); - }); }); diff --git a/plugins/tech-radar/src/components/RadarPage.tsx b/plugins/tech-radar/src/components/RadarPage.tsx index 1aefb72eca..519c9afeee 100644 --- a/plugins/tech-radar/src/components/RadarPage.tsx +++ b/plugins/tech-radar/src/components/RadarPage.tsx @@ -24,36 +24,46 @@ import { HeaderLabel, SupportButton, pageTheme, - useApi, } from '@backstage/core'; import RadarComponent from '../components/RadarComponent'; -import { techRadarApiRef, TechRadarApi } from '../api'; +import { TechRadarComponentProps } from '../api'; -const RadarPage = (): JSX.Element => { - const techRadarApi = useApi(techRadarApiRef); - - return ( - -
- - -
- - - - This is used for visualizing the official guidelines of different - areas of software development such as languages, frameworks, - infrastructure and processes. - - - - - - - - -
- ); +export type TechRadarPageProps = TechRadarComponentProps & { + title?: string; + subtitle?: string; + pageTitle?: string; }; -export default RadarPage; +export const RadarPage = ({ + title, + subtitle, + pageTitle, + ...props +}: TechRadarPageProps): JSX.Element => ( + +
+ + +
+ + + + This is used for visualizing the official guidelines of different + areas of software development such as languages, frameworks, + infrastructure and processes. + + + + + + + + +
+); + +RadarPage.defaultProps = { + title: 'Tech Radar', + subtitle: 'Pick the recommended technologies for your projects', + pageTitle: 'Company Radar', +}; diff --git a/plugins/tech-radar/src/index.ts b/plugins/tech-radar/src/index.ts index df22e57558..d7b4921e9a 100644 --- a/plugins/tech-radar/src/index.ts +++ b/plugins/tech-radar/src/index.ts @@ -16,6 +16,8 @@ export { plugin } from './plugin'; +export { RadarPage as Router } from './components/RadarPage'; + /** * The TypeScript API for configuring Tech Radar. */ diff --git a/plugins/tech-radar/src/plugin.ts b/plugins/tech-radar/src/plugin.ts index 3497a66f5d..d8dc41af2e 100644 --- a/plugins/tech-radar/src/plugin.ts +++ b/plugins/tech-radar/src/plugin.ts @@ -15,11 +15,7 @@ */ import { createPlugin } from '@backstage/core'; -import RadarPage from './components/RadarPage'; export const plugin = createPlugin({ id: 'tech-radar', - register({ router }) { - router.registerRoute('/tech-radar', RadarPage); - }, }); From 0ffed8b711aa0b62c5b59ff8e60cec95f6fad5c6 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 7 Sep 2020 22:54:23 +0200 Subject: [PATCH 353/359] fix: 404 page --- packages/core-api/src/app/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-api/src/app/App.tsx b/packages/core-api/src/app/App.tsx index bfa26b7547..ccdfc4fc7f 100644 --- a/packages/core-api/src/app/App.tsx +++ b/packages/core-api/src/app/App.tsx @@ -191,7 +191,7 @@ export class PrivateAppImpl implements BackstageApp { FeatureFlags.registeredFeatureFlags = registeredFeatureFlags; } - routes.push(} />); + routes.push(} />); return routes; } From 298851b31240fabcc1ba20bb126fb9f4e806c291 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 7 Sep 2020 23:03:33 +0200 Subject: [PATCH 354/359] feat(plugin-explore): migrate to new routing api --- packages/app/src/App.tsx | 4 +++- .../templates/default-app/packages/app/src/App.tsx | 5 ++++- plugins/explore/package.json | 3 ++- plugins/explore/src/components/ExplorePluginPage.tsx | 6 ++---- plugins/explore/src/components/Router.tsx | 10 ++++++++++ plugins/explore/src/index.ts | 1 + plugins/explore/src/plugin.ts | 7 ++----- 7 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 plugins/explore/src/components/Router.tsx diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 8ddf53783d..759901c11e 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -28,6 +28,7 @@ import { hot } from 'react-hot-loader/root'; import { providers } from './identityProviders'; import { Router as CatalogRouter } from '@backstage/plugin-catalog'; import { Router as DocsRouter } from '@backstage/plugin-techdocs'; +import { Router as ExploreRouter } from '@backstage/plugin-explore'; import { Route, Routes, Navigate } from 'react-router'; import { EntityPage } from './components/catalog/EntityPage'; @@ -55,12 +56,13 @@ const deprecatedAppRoutes = app.getRoutes(); const AppRoutes = () => ( + } /> } /> - + } /> {...deprecatedAppRoutes} ); diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index 3e6a0ef00e..cd03e20f06 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -11,6 +11,8 @@ import { AppSidebar } from './sidebar'; import { Route, Routes, Navigate } from 'react-router'; import { Router as CatalogRouter } from '@backstage/plugin-catalog'; import { Router as DocsRouter } from '@backstage/plugin-techdocs'; +import { Router as ExploreRouter } from '@backstage/plugin-explore'; + import { EntityPage } from './components/catalog/EntityPage'; const app = createApp({ @@ -30,12 +32,13 @@ const App: FC<{}> = () => ( + } /> } /> - + } /> {deprecatedAppRoutes} diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 42176c45cf..1a4a44b9cd 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -29,7 +29,8 @@ "classnames": "^2.2.6", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-use": "^15.3.3" + "react-use": "^15.3.3", + "react-router": "6.0.0-beta.0" }, "devDependencies": { "@backstage/cli": "^0.1.1-alpha.21", diff --git a/plugins/explore/src/components/ExplorePluginPage.tsx b/plugins/explore/src/components/ExplorePluginPage.tsx index 3e073ad322..4365157f62 100644 --- a/plugins/explore/src/components/ExplorePluginPage.tsx +++ b/plugins/explore/src/components/ExplorePluginPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { FC } from 'react'; +import React from 'react'; import { makeStyles, Typography } from '@material-ui/core'; import { Content, @@ -107,7 +107,7 @@ const toolsCards = [ }, ]; -const ExplorePluginPage: FC<{}> = () => { +export const ExplorePluginPage = () => { const classes = useStyles(); return ( @@ -130,5 +130,3 @@ const ExplorePluginPage: FC<{}> = () => { ); }; - -export default ExplorePluginPage; diff --git a/plugins/explore/src/components/Router.tsx b/plugins/explore/src/components/Router.tsx new file mode 100644 index 0000000000..f2556b75ed --- /dev/null +++ b/plugins/explore/src/components/Router.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Route, Routes } from 'react-router'; +import { ExplorePluginPage } from './ExplorePluginPage'; +import { rootRouteRef } from '../plugin'; + +export const Router = () => ( + + } /> + +); diff --git a/plugins/explore/src/index.ts b/plugins/explore/src/index.ts index 3a0a0fe2d3..ff7857cacd 100644 --- a/plugins/explore/src/index.ts +++ b/plugins/explore/src/index.ts @@ -15,3 +15,4 @@ */ export { plugin } from './plugin'; +export { Router } from './components/Router'; diff --git a/plugins/explore/src/plugin.ts b/plugins/explore/src/plugin.ts index 66e48a9b15..75ea892242 100644 --- a/plugins/explore/src/plugin.ts +++ b/plugins/explore/src/plugin.ts @@ -14,12 +14,9 @@ * limitations under the License. */ -import { createPlugin } from '@backstage/core'; -import ExplorePluginPage from './components/ExplorePluginPage'; +import { createPlugin, createRouteRef } from '@backstage/core'; +export const rootRouteRef = createRouteRef({ path: '', title: 'Explore' }); export const plugin = createPlugin({ id: 'explore', - register({ router }) { - router.registerRoute('/explore', ExplorePluginPage); - }, }); From 1486990b9100a03a7210052c6ee31d6339c7d80b Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 7 Sep 2020 23:28:00 +0200 Subject: [PATCH 355/359] fix: missing header --- plugins/explore/src/components/Router.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/plugins/explore/src/components/Router.tsx b/plugins/explore/src/components/Router.tsx index f2556b75ed..becb2522d0 100644 --- a/plugins/explore/src/components/Router.tsx +++ b/plugins/explore/src/components/Router.tsx @@ -1,3 +1,19 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; import { Route, Routes } from 'react-router'; import { ExplorePluginPage } from './ExplorePluginPage'; From 2371529edda0e84d6fe50a21245166e7a04120f5 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Tue, 8 Sep 2020 09:53:34 +0200 Subject: [PATCH 356/359] feat: remove explore plugin from app --- packages/app/src/App.tsx | 2 -- .../create-app/templates/default-app/packages/app/src/App.tsx | 2 -- 2 files changed, 4 deletions(-) diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 759901c11e..187ee27e3e 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -28,7 +28,6 @@ import { hot } from 'react-hot-loader/root'; import { providers } from './identityProviders'; import { Router as CatalogRouter } from '@backstage/plugin-catalog'; import { Router as DocsRouter } from '@backstage/plugin-techdocs'; -import { Router as ExploreRouter } from '@backstage/plugin-explore'; import { Route, Routes, Navigate } from 'react-router'; import { EntityPage } from './components/catalog/EntityPage'; @@ -62,7 +61,6 @@ const AppRoutes = () => ( element={} /> } /> - } /> {...deprecatedAppRoutes} ); diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index cd03e20f06..ba43c351b2 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -11,7 +11,6 @@ import { AppSidebar } from './sidebar'; import { Route, Routes, Navigate } from 'react-router'; import { Router as CatalogRouter } from '@backstage/plugin-catalog'; import { Router as DocsRouter } from '@backstage/plugin-techdocs'; -import { Router as ExploreRouter } from '@backstage/plugin-explore'; import { EntityPage } from './components/catalog/EntityPage'; @@ -38,7 +37,6 @@ const App: FC<{}> = () => ( element={} /> } /> - } /> {deprecatedAppRoutes} From 493e8052b8c8d6802fb44408931ed50d6ed43204 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 8 Sep 2020 10:28:17 +0200 Subject: [PATCH 357/359] graphiql: move to new plugin pattern --- packages/app/src/App.tsx | 2 ++ .../graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx | 4 ++-- plugins/graphiql/src/index.ts | 1 + plugins/graphiql/src/plugin.ts | 5 ----- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index aa6727c859..3d4f297e8e 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -28,6 +28,7 @@ import { hot } from 'react-hot-loader/root'; import { providers } from './identityProviders'; import { Router as CatalogRouter } from '@backstage/plugin-catalog'; import { Router as DocsRouter } from '@backstage/plugin-techdocs'; +import { Router as GraphiQLRouter } from '@backstage/plugin-graphiql'; import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar'; import { Route, Routes, Navigate } from 'react-router'; @@ -66,6 +67,7 @@ const AppRoutes = () => ( path="/tech-radar" element={} /> + } /> {...deprecatedAppRoutes} ); diff --git a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx index a16fdf3ee6..7eb91b59d8 100644 --- a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx +++ b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { FC } from 'react'; +import React from 'react'; import { Content, Header, @@ -30,7 +30,7 @@ import { graphQlBrowseApiRef } from '../../lib/api'; import { GraphiQLBrowser } from '../GraphiQLBrowser'; import { Typography } from '@material-ui/core'; -export const GraphiQLPage: FC<{}> = () => { +export const GraphiQLPage = () => { const graphQlBrowseApi = useApi(graphQlBrowseApiRef); const endpoints = useAsync(() => graphQlBrowseApi.getEndpoints()); diff --git a/plugins/graphiql/src/index.ts b/plugins/graphiql/src/index.ts index e7614aba81..50698cdb4c 100644 --- a/plugins/graphiql/src/index.ts +++ b/plugins/graphiql/src/index.ts @@ -15,5 +15,6 @@ */ export { plugin } from './plugin'; +export { GraphiQLPage as Router } from './components'; export * from './lib/api'; export * from './route-refs'; diff --git a/plugins/graphiql/src/plugin.ts b/plugins/graphiql/src/plugin.ts index f118faf871..7763284d81 100644 --- a/plugins/graphiql/src/plugin.ts +++ b/plugins/graphiql/src/plugin.ts @@ -15,12 +15,7 @@ */ import { createPlugin } from '@backstage/core'; -import { GraphiQLPage } from './components'; -import { graphiQLRouteRef } from './route-refs'; export const plugin = createPlugin({ id: 'graphiql', - register({ router }) { - router.addRoute(graphiQLRouteRef, GraphiQLPage); - }, }); From 0fa4c72a92d1e419fbad5df08d18b1cc66733d2d Mon Sep 17 00:00:00 2001 From: Omer Farooq <17722640+o-farooq@users.noreply.github.com> Date: Wed, 9 Sep 2020 00:25:46 +1200 Subject: [PATCH 358/359] revert yarn.lock upgrade Storybook to version 6 update storybook renderer layout docs: update create app command and link revert yarn.lock changes revert yarn.lock revert yarn --- docs/features/techdocs/getting-started.md | 2 +- microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/features/techdocs/getting-started.md b/docs/features/techdocs/getting-started.md index 414b3baae8..6cc6f43e72 100644 --- a/docs/features/techdocs/getting-started.md +++ b/docs/features/techdocs/getting-started.md @@ -28,7 +28,7 @@ installed: To create a new Backstage application for TechDocs, run the following command: ```bash -npx @backstage/cli create-app +npx @backstage/create-app ``` You will then be prompted to enter a name for your application. Once that's diff --git a/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md b/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md index 65ee02f6a0..dff567eaf3 100644 --- a/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md +++ b/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md @@ -34,7 +34,7 @@ You get to take full advantage of a platform that we at Spotify have been using Just run the backstage-cli: ```bash -npx @backstage/cli create-app +npx @backstage/create-app ``` Name your app, and we will create everything you need: @@ -50,7 +50,7 @@ yarn start And you are good to go! 👍 -Read the full documentation on how to [create an app](/docs/getting-started/create-an-app.md) on GitHub. +Read the full documentation on how to [create an app](/docs/getting-started/create-an-app) on GitHub. ## What do I get? (Let's get technical...) From 1b9673799f21eb9595857386832da663bc3490be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Tue, 8 Sep 2020 14:39:49 +0200 Subject: [PATCH 359/359] Remove Explore from sidebar (#2327) * Remove Explore from sidebar * Also in app --- packages/app/src/components/Root/Root.tsx | 2 -- .../templates/default-app/packages/app/src/sidebar.tsx | 2 -- 2 files changed, 4 deletions(-) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index ab95f623af..ae0d24fa5e 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -18,7 +18,6 @@ import React, { FC, useContext } from 'react'; import PropTypes from 'prop-types'; import { Link, makeStyles } from '@material-ui/core'; import HomeIcon from '@material-ui/icons/Home'; -import ExploreIcon from '@material-ui/icons/Explore'; import ExtensionIcon from '@material-ui/icons/Extension'; import BuildIcon from '@material-ui/icons/BuildRounded'; import RuleIcon from '@material-ui/icons/AssignmentTurnedIn'; @@ -91,7 +90,6 @@ const Root: FC<{}> = ({ children }) => ( {/* Global nav, not org-specific */} - diff --git a/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx b/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx index fa9a397751..9e343b86ac 100644 --- a/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/sidebar.tsx @@ -2,7 +2,6 @@ import React from 'react'; import HomeIcon from '@material-ui/icons/Home'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; -import ExploreIcon from '@material-ui/icons/Explore'; import BuildIcon from '@material-ui/icons/BuildRounded'; import RuleIcon from '@material-ui/icons/AssignmentTurnedIn'; import MapIcon from '@material-ui/icons/MyLocation'; @@ -23,7 +22,6 @@ export const AppSidebar = () => ( {/* Global nav, not org-specific */} -