diff --git a/.changeset/breezy-mugs-build.md b/.changeset/breezy-mugs-build.md new file mode 100644 index 0000000000..026932e971 --- /dev/null +++ b/.changeset/breezy-mugs-build.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Export template logos `TemplateBackstageLogo` and `TemplateBackstageLogoIcon` from package. diff --git a/.changeset/clever-buckets-doubt.md b/.changeset/clever-buckets-doubt.md new file mode 100644 index 0000000000..751db72bf8 --- /dev/null +++ b/.changeset/clever-buckets-doubt.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +imports from `@backstage/plugin-search-react` instead of `@backstage/plugin-search` diff --git a/.changeset/clever-pumpkins-tease.md b/.changeset/clever-pumpkins-tease.md new file mode 100644 index 0000000000..d0784361ff --- /dev/null +++ b/.changeset/clever-pumpkins-tease.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Bump the `rushstack` api generator libraries to their latest versions diff --git a/.changeset/dependabot-699dc23.md b/.changeset/dependabot-699dc23.md new file mode 100644 index 0000000000..b73b0eab46 --- /dev/null +++ b/.changeset/dependabot-699dc23.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kafka-backend': patch +--- + +build(deps-dev): bump `@types/jest-when` from 2.7.2 to 3.5.0 diff --git a/.changeset/dependabot-cccf2f0.md b/.changeset/dependabot-cccf2f0.md new file mode 100644 index 0000000000..6b420f9a48 --- /dev/null +++ b/.changeset/dependabot-cccf2f0.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +build(deps): bump `npm-packlist` from 3.0.0 to 5.0.0 diff --git a/.changeset/few-hotels-approve.md b/.changeset/few-hotels-approve.md new file mode 100644 index 0000000000..49ca040ef7 --- /dev/null +++ b/.changeset/few-hotels-approve.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Changed input label for owner field in GitlabRepoPicker diff --git a/.changeset/gorgeous-donuts-float.md b/.changeset/gorgeous-donuts-float.md new file mode 100644 index 0000000000..72a28ba6f9 --- /dev/null +++ b/.changeset/gorgeous-donuts-float.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-todo-backend': patch +--- + +Fix method to get source-location. diff --git a/.changeset/hot-items-smoke.md b/.changeset/hot-items-smoke.md new file mode 100644 index 0000000000..c9b9c8a135 --- /dev/null +++ b/.changeset/hot-items-smoke.md @@ -0,0 +1,61 @@ +--- +'@backstage/plugin-kubernetes-backend': minor +--- + +**BREAKING** Custom cluster suppliers need to cache their getClusters result + +To allow custom `KubernetesClustersSupplier` instances to refresh the list of clusters +the `getClusters` method is now called whenever the list of clusters is needed. + +Existing `KubernetesClustersSupplier` implementations will need to ensure that `getClusters` +can be called frequently and should return a cached result from `getClusters` instead. + +For example, here's a simple example of a custom supplier in `packages/backend/src/plugins/kubernetes.ts`: + +```diff +-import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend'; ++import { ++ ClusterDetails, ++ KubernetesBuilder, ++ KubernetesClustersSupplier, ++} from '@backstage/plugin-kubernetes-backend'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; ++import { Duration } from 'luxon'; ++ ++export class CustomClustersSupplier implements KubernetesClustersSupplier { ++ constructor(private clusterDetails: ClusterDetails[] = []) {} ++ ++ static create(refreshInterval: Duration) { ++ const clusterSupplier = new CustomClustersSupplier(); ++ // setup refresh, e.g. using a copy of https://github.com/backstage/backstage/blob/master/plugins/search-backend-node/src/runPeriodically.ts ++ runPeriodically( ++ () => clusterSupplier.refreshClusters(), ++ refreshInterval.toMillis(), ++ ); ++ return clusterSupplier; ++ } ++ ++ async refreshClusters(): Promise { ++ this.clusterDetails = []; // fetch from somewhere ++ } ++ ++ async getClusters(): Promise { ++ return this.clusterDetails; ++ } ++} + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { +- const { router } = await KubernetesBuilder.createBuilder({ ++ const builder = await KubernetesBuilder.createBuilder({ + logger: env.logger, + config: env.config, +- }).build(); ++ }); ++ builder.setClusterSupplier( ++ CustomClustersSupplier.create(Duration.fromObject({ minutes: 60 })), ++ ); ++ const { router } = await builder.build(); +``` diff --git a/.changeset/light-dragons-crash.md b/.changeset/light-dragons-crash.md new file mode 100644 index 0000000000..35dea73ae6 --- /dev/null +++ b/.changeset/light-dragons-crash.md @@ -0,0 +1,13 @@ +--- +'@backstage/create-app': patch +--- + +Removed `@octokit/rest` and `@gitbeaker/node` from backend dependencies as these are unused in the default app. + +To apply these changes to your existing app, remove the following lines from the `dependencies` section of `packages/backend/package.json` + +```diff + "@backstage/plugin-techdocs-backend": "^1.0.0", +- "@gitbeaker/node": "^34.6.0", +- "@octokit/rest": "^18.5.3", +``` diff --git a/.changeset/many-cameras-search.md b/.changeset/many-cameras-search.md new file mode 100644 index 0000000000..6998dfd713 --- /dev/null +++ b/.changeset/many-cameras-search.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Added `spec.profile.displayName` to search index for Group kinds diff --git a/.changeset/ninety-islands-report.md b/.changeset/ninety-islands-report.md new file mode 100644 index 0000000000..4a515b75cd --- /dev/null +++ b/.changeset/ninety-islands-report.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Defensively encode URL parameters when fetching ELB keys diff --git a/.changeset/rude-bees-rest.md b/.changeset/rude-bees-rest.md new file mode 100644 index 0000000000..7ca4e7492c --- /dev/null +++ b/.changeset/rude-bees-rest.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-react': minor +--- + +New search package to hold things the search plugin itself and other frontend plugins (e.g. techdocs, home) depend on. diff --git a/.changeset/thirty-sloths-knock.md b/.changeset/thirty-sloths-knock.md new file mode 100644 index 0000000000..1ad559e368 --- /dev/null +++ b/.changeset/thirty-sloths-knock.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-search': patch +--- + +The following exports has been moved to `@backstage/plugin-search-react` and will be removed in the next release. import from `@backstage/plugin-search-react` instead. + +- `SearchApi` interface. +- `searchApiRef` +- `SearchContext` +- `SearchContextProvider` +- `useSearch` diff --git a/.changeset/wild-emus-film.md b/.changeset/wild-emus-film.md new file mode 100644 index 0000000000..abb77c5ddc --- /dev/null +++ b/.changeset/wild-emus-film.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-tech-insights': minor +--- + +Add checksId option to EntityTechInsightsScorecardContent component diff --git a/ADOPTERS.md b/ADOPTERS.md index ce5b2b70ef..1a1c3686d4 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -26,7 +26,7 @@ _If you're using Backstage in your organization, please try to add your company | [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | | [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | | [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. | -| [Peloton](https://www.onepeloton.com/) | [Jim Haughwout](https://github.com/JimHaughwout) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. | +| [Peloton](https://www.onepeloton.com/) | [Matt Waldron](https://github.com/daftgopher) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. | | [TELUS](https://telus.com) | [Seb Barre](https://github.com/sbarre) | The Go-to place to find answers about development and delivery at TELUS. | | [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a team’s engineering dependencies. | | [Oriflame](https://www.oriflame.com/) | [Oriflame](https://github.com/oriflame) | Internal developer portal for services, single page apps and packages overview, API documentation, technical guides, tech-radar and more. | @@ -34,7 +34,7 @@ _If you're using Backstage in your organization, please try to add your company | [Netflix](https://www.netflix.com/) | [bleathem](https://github.com/bleathem) | Our Backstage implementation will be the front door to a unified experience connecting our internal platform products across important workflows with integrated knowledge and support. | | [b.well](https://www.icanbwell.com/) | [Jacob Rosales](https://github.com/jrosales) | Foundation for our engineering portal and cloud insights. | | [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. | -| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 | +| [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process 🌕🚀🧑‍🚀 | | [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes | | [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | | [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. | @@ -110,3 +110,6 @@ _If you're using Backstage in your organization, please try to add your company | [Beez Innovation Labs Pvt. Ltd](https://www.beezlabs.com/) | [Karthikeyan Venkatesan](https://github.com/karthikeyan23) | Developer portal with software catalog, scaffolding, tech docs, templates, and infra. | | [Agorapulse](https://www.agorapulse.com/) | [@jvdrean](https://github.com/jvdrean) | Developer portal with software catalog, documentation, monitoring, runbooks, tech radar and more. | | [Wistia](https://wistia.com/) | [@qrush](https://github.com/qrush), [@okize](https://github.com/okize) | Internal Developer Portal, service catalog, tech docs and more | +| [SIX](https://www.six-group.com/) | [@jbadeau](https://github.com/jbadeau), [@tomassatka](https://github.com/tomassatka) | Internal DevOps portal hosting our software and dataset catalog, as well as custom plugins for observability, service virtualization, deployments, incident managment and quality metrics. | +| [Raiffeisen Bank International](https://www.rbinternational.com/) | [Daniel Baumgartner](https://github.com/dabarbi) | From developers for developers: software catalog, techdocs and heavy use of scaffolder to drive reuse on engineering level forward. Part of inner source initiative. Multi national setup coming. | +| [Spread Group](https://www.spreadgroup.com/) | [Luna Stadler](https://github.com/heyLu), [Iván González](https://github.com/ivangonzalezacuna) | Internal Developer Portal, an overview of all running software, architecture documentation and more; replacing and unifying a variety of internal tools. | diff --git a/docs/assets/architecture-overview/package-decision.drawio.svg b/docs/assets/architecture-overview/package-decision.drawio.svg new file mode 100644 index 0000000000..e5199adf3f --- /dev/null +++ b/docs/assets/architecture-overview/package-decision.drawio.svg @@ -0,0 +1,269 @@ + + + + + + + + + +
+
+
+ No +
+
+
+
+ + No + +
+
+ + + + + +
+
+
+ Yes +
+
+
+
+ + Yes + +
+
+ + + + +
+
+
+ Is the new addition public API? +
+ i.e. exported from the package +
+
+
+
+ + Is the new addition public API?... + +
+
+ + + + + + +
+
+
+ In what plugin package should I put my code? +
+
+
+
+ + In what plugin package sho... + +
+
+ + + + +
+
+
+ Put it in the package +
+ that uses it +
+
+
+
+ + Put it in the package... + +
+
+ + + + + +
+
+
+ Only app/backend +
+
+
+
+ + Only app/backend + +
+
+ + + + + + +
+
+
+ Is the export supposed +
+ to be used by other plugins or just app/backend packages? +
+
+
+
+ + Is the export supposed... + +
+
+ + + + +
+
+
+ Put it in the frontend or backend plugin package +
+
+
+
+ + Put it in the frontend or... + +
+
+ + + + + +
+
+
+ No +
+
+
+
+ + No + +
+
+ + + + + +
+
+
+ Yes +
+
+
+
+ + Yes + +
+
+ + + + +
+
+
+ Should the export be +
+ usable by both Node.js and browser packages? +
+
+
+
+ + Should the export be... + +
+
+ + + +
+
+
+ Yes, used by other plugins +
+
+
+
+ + Yes, used by other plugins + +
+
+ + + + +
+
+
+ Put frontend exports in <plugin>-react, and backend exports in <plugin>-node +
+
+
+
+ + Put frontend exports in <p... + +
+
+ + + + +
+
+
+ Add it to <plugin>-common, but be sure to support both Node.js and web environments +
+
+
+
+ + Add it to <plugin>-common, but be... + +
+
+ +
+ + + + + Viewer does not support full SVG 1.1 + + + +
diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index 612f4903d9..dd7e666be4 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -57,6 +57,10 @@ This is an array used to determine where to retrieve cluster configuration from. Valid cluster locator methods are: +- [`config`](#config) +- [`gke`](#gke) +- [custom `KubernetesClustersSupplier`](#custom-kubernetesclusterssupplier) + #### `config` This cluster locator method will read cluster information from your app-config @@ -261,6 +265,12 @@ Kubernetes plugin. Defaults to `false`. +#### Custom `KubernetesClustersSupplier` + +If the configuration-based cluster locators do not work for your use-case, +it is also possible to implement a +[custom `KubernetesClustersSupplier`](installation.md#custom-cluster-discovery). + ### `customResources` (optional) Configures which [custom resources][3] to look for when returning an entity's diff --git a/docs/features/kubernetes/installation.md b/docs/features/kubernetes/installation.md index 2698580491..e920bd44ea 100644 --- a/docs/features/kubernetes/installation.md +++ b/docs/features/kubernetes/installation.md @@ -90,6 +90,63 @@ async function main() { That's it! The Kubernetes frontend and backend have now been added to your Backstage app. +### Custom cluster discovery + +If either existing +[cluster locators](https://backstage.io/docs/features/kubernetes/configuration#clusterlocatormethods) +don't work for your use-case, it is possible to implement a custom +[KubernetesClustersSupplier](https://backstage.io/docs/reference/plugin-kubernetes-backend.kubernetesclusterssupplier). + +Change the following in `packages/backend/src/plugin/kubernetes.ts`: + +```diff +-import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend'; ++import { ++ ClusterDetails, ++ KubernetesBuilder, ++ KubernetesClustersSupplier, ++} from '@backstage/plugin-kubernetes-backend'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; ++import { Duration } from 'luxon'; ++ ++export class CustomClustersSupplier implements KubernetesClustersSupplier { ++ constructor(private clusterDetails: ClusterDetails[] = []) {} ++ ++ static create(refreshInterval: Duration) { ++ const clusterSupplier = new CustomClustersSupplier(); ++ // setup refresh, e.g. using a copy of https://github.com/backstage/backstage/blob/master/plugins/search-backend-node/src/runPeriodically.ts ++ runPeriodically( ++ () => clusterSupplier.refreshClusters(), ++ refreshInterval.toMillis(), ++ ); ++ return clusterSupplier; ++ } ++ ++ async refreshClusters(): Promise { ++ this.clusterDetails = []; // fetch from somewhere ++ } ++ ++ async getClusters(): Promise { ++ return this.clusterDetails; ++ } ++} + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { +- const { router } = await KubernetesBuilder.createBuilder({ ++ const builder = await KubernetesBuilder.createBuilder({ + logger: env.logger, + config: env.config, +- }).build(); ++ }); ++ builder.setClusterSupplier( ++ CustomClustersSupplier.create(Duration.fromObject({ minutes: 60 })), ++ ); ++ const { router } = await builder.build(); +``` + ## Running Backstage locally Start the frontend and the backend app by diff --git a/docs/integrations/github/discovery.md b/docs/integrations/github/discovery.md index 46e054364f..1cbf8905a2 100644 --- a/docs/integrations/github/discovery.md +++ b/docs/integrations/github/discovery.md @@ -16,11 +16,12 @@ catalog. You will have to add the processors in the catalog initialization code of your backend. They are not installed by default, therefore you have to add a -dependency to `@backstage/plugin-catalog-backend-module-github` to your backend -package. +dependency on `@backstage/plugin-catalog-backend-module-github` to your backend +package, plus `@backstage/integration` for the basic credentials management: ```bash # From your Backstage root directory +yarn add --cwd packages/backend @backstage/integration yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-github ``` diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md index 5b6b81cf10..382399e603 100644 --- a/docs/overview/architecture-overview.md +++ b/docs/overview/architecture-overview.md @@ -256,6 +256,18 @@ The Backstage CLI is in a category of its own and is depended on by virtually all other packages. It's not a library in itself though, and must always be a development dependency only. +### Deciding where you place your code + +It can sometimes be difficult to decide where to place your plugin code. For example +should it go directly in the `-backend` plugin package or in the `-node` package? +As a rule of thumb you should try to keep the exposure of your code as low +as possible. If it doesn't need to be public API, it's best to avoid. If you don't +need it to be used by other plugins, then keep it directly in the plugin packages. + +Below is a chart to help you decide where to place your code. + +![Package decision](../assets/architecture-overview/package-decision.drawio.svg) + ## Databases As we have seen, both the `lighthouse-audit-service` and `catalog-backend` diff --git a/package.json b/package.json index dc72b8974b..6fea4e081a 100644 --- a/package.json +++ b/package.json @@ -52,10 +52,10 @@ "version": "1.1.0-next.2", "dependencies": { "@manypkg/get-packages": "^1.1.3", - "@microsoft/api-documenter": "^7.17.0", - "@microsoft/api-extractor": "^7.19.4", - "@microsoft/api-extractor-model": "^7.16.0", - "@microsoft/tsdoc": "^0.13.2" + "@microsoft/api-documenter": "^7.17.5", + "@microsoft/api-extractor": "^7.21.2", + "@microsoft/api-extractor-model": "^7.16.1", + "@microsoft/tsdoc": "^0.14.1" }, "devDependencies": { "@changesets/cli": "^2.14.0", diff --git a/packages/app/package.json b/packages/app/package.json index 4522cabaf1..f8dc6127c8 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -10,6 +10,7 @@ "@backstage/app-defaults": "^1.0.1-next.1", "@backstage/catalog-model": "^1.0.1-next.0", "@backstage/cli": "^0.17.0-next.1", + "@backstage/config": "^1.0.0", "@backstage/core-app-api": "^1.0.1-next.0", "@backstage/core-components": "^0.9.3-next.0", "@backstage/core-plugin-api": "^1.0.0", @@ -47,9 +48,11 @@ "@backstage/plugin-rollbar": "^0.4.4-next.0", "@backstage/plugin-scaffolder": "^1.0.1-next.1", "@backstage/plugin-search": "^0.7.5-next.0", + "@backstage/plugin-search-react": "^0.0.0", "@backstage/plugin-search-common": "^0.3.3-next.1", "@backstage/plugin-sentry": "^0.3.42-next.0", "@backstage/plugin-shortcuts": "^0.2.5-next.0", + "@backstage/plugin-stack-overflow": "^0.1.0-next.0", "@backstage/plugin-tech-radar": "^0.5.11-next.1", "@backstage/plugin-techdocs": "^1.0.1-next.1", "@backstage/plugin-todo": "^0.2.6-next.0", diff --git a/plugins/home/src/templates/DefaultTemplate.stories.tsx b/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx similarity index 92% rename from plugins/home/src/templates/DefaultTemplate.stories.tsx rename to packages/app/src/components/home/templates/DefaultTemplate.stories.tsx index e2e570dcf6..8b9c5100b1 100644 --- a/plugins/home/src/templates/DefaultTemplate.stories.tsx +++ b/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx @@ -14,14 +14,14 @@ * limitations under the License. */ -import { TemplateBackstageLogo } from './TemplateBackstageLogo'; -import { TemplateBackstageLogoIcon } from './TemplateBackstageLogoIcon'; import { HomePageToolkit, HomePageCompanyLogo, HomePageStarredEntities, -} from '../plugin'; -import { wrapInTestApp, TestApiProvider} from '@backstage/test-utils'; + TemplateBackstageLogo, + TemplateBackstageLogoIcon +} from '@backstage/plugin-home'; +import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils'; import { Content, Page, InfoCard } from '@backstage/core-components'; import { starredEntitiesApiRef, @@ -32,10 +32,9 @@ import { configApiRef } from '@backstage/core-plugin-api'; import { ConfigReader } from '@backstage/config'; import { HomePageSearchBar, - SearchContextProvider, - searchApiRef, searchPlugin, } from '@backstage/plugin-search'; +import { searchApiRef, SearchContextProvider } from '@backstage/plugin-search-react'; import { HomePageStackOverflowQuestions } from '@backstage/plugin-stack-overflow'; import { Grid, makeStyles } from '@material-ui/core'; import React, { ComponentType } from 'react'; @@ -54,10 +53,7 @@ export default { <> Promise.resolve({ results: [] }) }], [ configApiRef, diff --git a/packages/cli/package.json b/packages/cli/package.json index e362caf40e..bd8964ef68 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -93,7 +93,7 @@ "mini-css-extract-plugin": "^2.4.2", "minimatch": "5.0.1", "node-libs-browser": "^2.2.1", - "npm-packlist": "^3.0.0", + "npm-packlist": "^5.0.0", "ora": "^5.3.0", "postcss": "^8.1.0", "process": "^0.11.10", @@ -153,7 +153,7 @@ "ts-node": "^10.0.0" }, "peerDependencies": { - "@microsoft/api-extractor": "^7.19.2" + "@microsoft/api-extractor": "^7.21.2" }, "peerDependenciesMeta": { "@microsoft/api-extractor": { 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 b91366dee8..939b13bf86 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 @@ -35,8 +35,6 @@ {{/if}} "@backstage/plugin-search-backend-node": "^{{version '@backstage/plugin-search-backend-node'}}", "@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}", - "@gitbeaker/node": "^34.6.0", - "@octokit/rest": "^18.5.3", "dockerode": "^3.3.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", diff --git a/plugins/auth-backend/src/providers/aws-alb/provider.ts b/plugins/auth-backend/src/providers/aws-alb/provider.ts index 12f7c7f4b4..5206dc125c 100644 --- a/plugins/auth-backend/src/providers/aws-alb/provider.ts +++ b/plugins/auth-backend/src/providers/aws-alb/provider.ts @@ -211,8 +211,10 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers { if (optionalCacheKey) { return crypto.createPublicKey(optionalCacheKey); } - const keyText: string = await fetch( - `https://public-keys.auth.elb.${this.region}.amazonaws.com/${keyId}`, + const keyText = await fetch( + `https://public-keys.auth.elb.${encodeURIComponent( + this.region, + )}.amazonaws.com/${encodeURIComponent(keyId)}`, ).then(response => response.text()); const keyValue = crypto.createPublicKey(keyText); this.keyCache.set(keyId, keyValue.export({ format: 'pem', type: 'spki' })); diff --git a/plugins/catalog-backend/src/search/DefaultCatalogCollatorFactory.ts b/plugins/catalog-backend/src/search/DefaultCatalogCollatorFactory.ts index 98bcfcd8d4..cf082c271c 100644 --- a/plugins/catalog-backend/src/search/DefaultCatalogCollatorFactory.ts +++ b/plugins/catalog-backend/src/search/DefaultCatalogCollatorFactory.ts @@ -23,11 +23,7 @@ import { CatalogClient, GetEntitiesRequest, } from '@backstage/catalog-client'; -import { - Entity, - stringifyEntityRef, - UserEntity, -} from '@backstage/catalog-model'; +import { stringifyEntityRef } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; import { @@ -36,6 +32,7 @@ import { } from '@backstage/plugin-catalog-common'; import { Permission } from '@backstage/plugin-permission-common'; import { Readable } from 'stream'; +import { getDocumentText } from './util'; /** @public */ export type DefaultCatalogCollatorFactoryOptions = { @@ -100,24 +97,6 @@ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { return formatted.toLowerCase(); } - private isUserEntity(entity: Entity): entity is UserEntity { - return entity.kind.toLocaleUpperCase('en-US') === 'USER'; - } - - private getDocumentText(entity: Entity): string { - let documentText = entity.metadata.description || ''; - if (this.isUserEntity(entity)) { - if (entity.spec?.profile?.displayName && documentText) { - // combine displayName and description - const displayName = entity.spec?.profile?.displayName; - documentText = displayName.concat(' : ', documentText); - } else { - documentText = entity.spec?.profile?.displayName || documentText; - } - } - return documentText; - } - private async *execute(): AsyncGenerator { const { token } = await this.tokenManager.getToken(); let entitiesRetrieved = 0; @@ -150,7 +129,7 @@ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { kind: entity.kind, name: entity.metadata.name, }), - text: this.getDocumentText(entity), + text: getDocumentText(entity), componentType: entity.spec?.type?.toString() || 'other', type: entity.spec?.type?.toString() || 'other', namespace: entity.metadata.namespace || 'default', diff --git a/plugins/catalog-backend/src/search/util.test.ts b/plugins/catalog-backend/src/search/util.test.ts new file mode 100644 index 0000000000..d2d1776338 --- /dev/null +++ b/plugins/catalog-backend/src/search/util.test.ts @@ -0,0 +1,146 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ComponentEntity, + GroupEntity, + UserEntity, +} from '@backstage/catalog-model'; +import { getDocumentText } from './util'; + +describe('getDocumentText', () => { + describe('kind is not User or Group', () => { + test('contains description if set', () => { + const entity = createComponent(); + entity.metadata.description = 'The expected description'; + const actual = getDocumentText(entity); + expect(actual).toContain(entity.metadata.description); + }); + + test('is empty if description is not set', () => { + const entity = createComponent(); + const actual = getDocumentText(entity); + expect(actual).toEqual(''); + }); + }); + + describe('kind is User', () => { + test('contains display name if set', () => { + const entity = createUser(); + const actual = getDocumentText(entity); + expect(actual).toContain(entity.spec.profile?.displayName); + }); + + test('contains description if set', () => { + const entity = createUser(); + const actual = getDocumentText(entity); + expect(actual).toContain(entity.metadata.description); + }); + + test('contains both description and display name if both are set', () => { + const entity = createUser(); + const actual = getDocumentText(entity); + expect(actual).toContain(entity.spec.profile?.displayName); + expect(actual).toContain(entity.metadata.description); + }); + + test('is empty if description and display name are not set', () => { + const entity = createUser(); + delete entity.metadata.description; + delete entity.spec.profile?.displayName; + const actual = getDocumentText(entity); + expect(actual).toEqual(''); + }); + }); + + describe('kind is Group', () => { + test('contains display name if set', () => { + const entity = createGroup(); + const actual = getDocumentText(entity); + expect(actual).toContain(entity.spec.profile?.displayName); + }); + + test('contains description if set', () => { + const entity = createGroup(); + const actual = getDocumentText(entity); + expect(actual).toContain(entity.metadata.description); + }); + + test('contains both description and display name if both are set', () => { + const entity = createGroup(); + const actual = getDocumentText(entity); + expect(actual).toContain(entity.spec.profile?.displayName); + expect(actual).toContain(entity.metadata.description); + }); + + test('is empty if description and display name are not set', () => { + const entity = createGroup(); + delete entity.metadata.description; + delete entity.spec.profile?.displayName; + const actual = getDocumentText(entity); + expect(actual).toEqual(''); + }); + }); +}); + +function createGroup(): GroupEntity { + return { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { + name: 'group-1', + description: 'The expected description', + }, + spec: { + type: 'team', + profile: { + displayName: 'Group 1', + }, + children: [], + }, + }; +} + +function createUser(): UserEntity { + return { + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { + name: 'user-1', + description: 'The expected description', + }, + spec: { + profile: { + displayName: 'User 1', + }, + }, + }; +} + +function createComponent(): ComponentEntity { + return { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'component-1', + }, + spec: { + lifecycle: 'experimental', + owner: 'someone', + type: 'service', + }, + }; +} diff --git a/plugins/catalog-backend/src/search/util.ts b/plugins/catalog-backend/src/search/util.ts new file mode 100644 index 0000000000..7e7ce7064e --- /dev/null +++ b/plugins/catalog-backend/src/search/util.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity, UserEntity } from '@backstage/catalog-model'; + +function isUserEntity(entity: Entity): entity is UserEntity { + return entity.kind.toLocaleUpperCase('en-US') === 'USER'; +} + +function isGroupEntity(entity: Entity): entity is UserEntity { + return entity.kind.toLocaleUpperCase('en-US') === 'GROUP'; +} + +export function getDocumentText(entity: Entity): string { + const documentTexts: string[] = []; + documentTexts.push(entity.metadata.description || ''); + + if (isUserEntity(entity) || isGroupEntity(entity)) { + if (entity.spec?.profile?.displayName) { + documentTexts.push(entity.spec.profile.displayName); + } + } + return documentTexts.join(' : '); +} diff --git a/plugins/home/README.md b/plugins/home/README.md index a85994b3cb..0cd307b706 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -93,4 +93,4 @@ Additionally, the API is at a very early state, so contributing with additional ### Homepage Templates -We are hoping that we together can build up a collection of Homepage templates. We therefore put together a place where we can collect all the templates for the Home Plugin in the [storybook](https://backstage.io/storybook/?path=/story/plugins-home-templates). If you would like to contribute with a template, start by taking a look at the [DefaultTemplate storybook example to create your own](/plugins/home/src/templates/DefaultTemplate.stories.tsx), and then open a PR with your suggestion. +We are hoping that we together can build up a collection of Homepage templates. We therefore put together a place where we can collect all the templates for the Home Plugin in the [storybook](https://backstage.io/storybook/?path=/story/plugins-home-templates). If you would like to contribute with a template, start by taking a look at the [DefaultTemplate storybook example to create your own](/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx), and then open a PR with your suggestion. diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 9bd577da7c..a65f8cda5c 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -119,6 +119,19 @@ export const SettingsModal: (props: { children: JSX.Element; }) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "TemplateBackstageLogoProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "TemplateBackstageLogo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const TemplateBackstageLogo: ( + props: TemplateBackstageLogoProps, +) => JSX.Element; + +// Warning: (ae-missing-release-tag) "TemplateBackstageLogoIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const TemplateBackstageLogoIcon: () => JSX.Element; + // @public export const WelcomeTitle: () => JSX.Element; diff --git a/plugins/home/package.json b/plugins/home/package.json index 43b474704a..aac39c397a 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -38,7 +38,6 @@ "@backstage/core-components": "^0.9.3-next.1", "@backstage/core-plugin-api": "^1.0.0", "@backstage/plugin-catalog-react": "^1.0.1-next.2", - "@backstage/plugin-search": "^0.7.5-next.0", "@backstage/plugin-stack-overflow": "^0.1.0-next.0", "@backstage/theme": "^0.2.15", "@backstage/config": "^1.0.0", diff --git a/plugins/home/src/templates/TemplateBackstageLogo.tsx b/plugins/home/src/assets/TemplateBackstageLogo.tsx similarity index 98% rename from plugins/home/src/templates/TemplateBackstageLogo.tsx rename to plugins/home/src/assets/TemplateBackstageLogo.tsx index edce9bb02e..9088cfa58c 100644 --- a/plugins/home/src/templates/TemplateBackstageLogo.tsx +++ b/plugins/home/src/assets/TemplateBackstageLogo.tsx @@ -19,9 +19,13 @@ import React from 'react'; type Classes = { svg: string; path: string; -} +}; -export const TemplateBackstageLogo = (props: { classes: Classes }) => { +type TemplateBackstageLogoProps = { + classes: Classes; +}; + +export const TemplateBackstageLogo = (props: TemplateBackstageLogoProps) => { return ( { ); }; - diff --git a/plugins/home/src/assets/index.ts b/plugins/home/src/assets/index.ts new file mode 100644 index 0000000000..79de43ca99 --- /dev/null +++ b/plugins/home/src/assets/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './TemplateBackstageLogo'; +export * from './TemplateBackstageLogoIcon'; diff --git a/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.stories.tsx b/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.stories.tsx index ab71eea54e..741ad2e977 100644 --- a/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.stories.tsx +++ b/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.stories.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TemplateBackstageLogo } from '../../templates'; +import { TemplateBackstageLogo } from '../../assets'; import { HomePageCompanyLogo } from '../../plugin'; import { rootRouteRef } from '../../routes'; import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils'; diff --git a/plugins/home/src/homePageComponents/Toolkit/Toolkit.stories.tsx b/plugins/home/src/homePageComponents/Toolkit/Toolkit.stories.tsx index cad188c5d6..9a82301e49 100644 --- a/plugins/home/src/homePageComponents/Toolkit/Toolkit.stories.tsx +++ b/plugins/home/src/homePageComponents/Toolkit/Toolkit.stories.tsx @@ -20,7 +20,7 @@ import { Grid } from '@material-ui/core'; import React, { ComponentType } from 'react'; import { ComponentAccordion } from '../../componentRenderers'; import { HomePageToolkit } from '../../plugin'; -import { TemplateBackstageLogoIcon } from '../../templates'; +import { TemplateBackstageLogoIcon } from '../../assets'; export default { title: 'Plugins/Home/Components/Toolkit', diff --git a/plugins/home/src/index.ts b/plugins/home/src/index.ts index d30b76a7fc..8f137d589b 100644 --- a/plugins/home/src/index.ts +++ b/plugins/home/src/index.ts @@ -33,6 +33,7 @@ export { WelcomeTitle, } from './plugin'; export { SettingsModal, HeaderWorldClock } from './components'; +export * from './assets'; export type { ClockConfig } from './components'; export { createCardExtension } from './extensions'; export type { ComponentRenderer } from './extensions'; diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index 4deeefc788..732fc30927 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -48,7 +48,7 @@ }, "devDependencies": { "@backstage/cli": "^0.17.0-next.1", - "@types/jest-when": "^2.7.2", + "@types/jest-when": "^3.5.0", "@types/lodash": "^4.14.151", "jest-when": "^3.1.0", "supertest": "^6.1.3" diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index 8a3b64bd93..691b9443e5 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -4,6 +4,7 @@ ```ts import { Config } from '@backstage/config'; +import { Duration } from 'luxon'; import express from 'express'; import type { FetchResponse } from '@backstage/plugin-kubernetes-common'; import type { JsonObject } from '@backstage/types'; @@ -85,18 +86,20 @@ export class KubernetesBuilder { // (undocumented) build(): KubernetesBuilderReturn; // (undocumented) - protected buildClusterSupplier(): KubernetesClustersSupplier; + protected buildClusterSupplier( + refreshInterval: Duration, + ): KubernetesClustersSupplier; // (undocumented) protected buildCustomResources(): CustomResource[]; // (undocumented) protected buildFetcher(): KubernetesFetcher; // (undocumented) protected buildHttpServiceLocator( - _clusterDetails: ClusterDetails[], + _clusterSupplier: KubernetesClustersSupplier, ): KubernetesServiceLocator; // (undocumented) protected buildMultiTenantServiceLocator( - clusterDetails: ClusterDetails[], + clusterSupplier: KubernetesClustersSupplier, ): KubernetesServiceLocator; // (undocumented) protected buildObjectsProvider( @@ -105,12 +108,12 @@ export class KubernetesBuilder { // (undocumented) protected buildRouter( objectsProvider: KubernetesObjectsProvider, - clusterDetails: ClusterDetails[], + clusterSupplier: KubernetesClustersSupplier, ): express.Router; // (undocumented) protected buildServiceLocator( method: ServiceLocatorMethod, - clusterDetails: ClusterDetails[], + clusterSupplier: KubernetesClustersSupplier, ): KubernetesServiceLocator; // (undocumented) static createBuilder(env: KubernetesEnvironment): KubernetesBuilder; @@ -127,6 +130,8 @@ export class KubernetesBuilder { // (undocumented) setClusterSupplier(clusterSupplier?: KubernetesClustersSupplier): this; // (undocumented) + setDefaultClusterRefreshInterval(refreshInterval: Duration): this; + // (undocumented) setFetcher(fetcher?: KubernetesFetcher): this; // (undocumented) setObjectsProvider(objectsProvider?: KubernetesObjectsProvider): this; @@ -137,7 +142,6 @@ export class KubernetesBuilder { // @public export type KubernetesBuilderReturn = Promise<{ router: express.Router; - clusterDetails: ClusterDetails[]; clusterSupplier: KubernetesClustersSupplier; customResources: CustomResource[]; fetcher: KubernetesFetcher; @@ -149,7 +153,6 @@ export type KubernetesBuilderReturn = Promise<{ // // @public (undocumented) export interface KubernetesClustersSupplier { - // (undocumented) getClusters(): Promise; } diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index a55ec70780..f5eb974431 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -43,6 +43,7 @@ "@google-cloud/container": "^3.0.0", "@kubernetes/client-node": "^0.16.0", "@types/express": "^4.17.6", + "@types/luxon": "^2.0.4", "aws-sdk": "^2.840.0", "aws4": "^1.11.0", "compression": "^1.7.4", @@ -52,6 +53,7 @@ "fs-extra": "10.0.1", "helmet": "^5.0.2", "lodash": "^4.17.21", + "luxon": "^2.0.2", "morgan": "^1.10.0", "stream-buffers": "^3.0.2", "winston": "^3.2.1", @@ -60,8 +62,8 @@ "devDependencies": { "@backstage/cli": "^0.17.0-next.1", "@types/aws4": "^1.5.1", - "supertest": "^6.1.3", - "aws-sdk-mock": "^5.2.1" + "aws-sdk-mock": "^5.2.1", + "supertest": "^6.1.3" }, "files": [ "dist", diff --git a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts index 8b70db40eb..0eb25c2acd 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts @@ -17,7 +17,13 @@ import { Config } from '@backstage/config'; import { ForwardedError } from '@backstage/errors'; import * as container from '@google-cloud/container'; -import { GKEClusterDetails, KubernetesClustersSupplier } from '../types/types'; +import { Duration } from 'luxon'; +import { runPeriodically } from '../service/runPeriodically'; +import { + ClusterDetails, + GKEClusterDetails, + KubernetesClustersSupplier, +} from '../types/types'; type GkeClusterLocatorOptions = { projectId: string; @@ -31,11 +37,14 @@ export class GkeClusterLocator implements KubernetesClustersSupplier { constructor( private readonly options: GkeClusterLocatorOptions, private readonly client: container.v1.ClusterManagerClient, + private clusterDetails: GKEClusterDetails[] | undefined = undefined, + private hasClusterDetails: boolean = false, ) {} static fromConfigWithClient( config: Config, client: container.v1.ClusterManagerClient, + refreshInterval: Duration | undefined = undefined, ): GkeClusterLocator { const options = { projectId: config.getString('projectId'), @@ -45,18 +54,37 @@ export class GkeClusterLocator implements KubernetesClustersSupplier { config.getOptionalBoolean('skipMetricsLookup') ?? false, exposeDashboard: config.getOptionalBoolean('exposeDashboard') ?? false, }; - return new GkeClusterLocator(options, client); + const gkeClusterLocator = new GkeClusterLocator(options, client); + if (refreshInterval) { + runPeriodically( + () => gkeClusterLocator.refreshClusters(), + refreshInterval.toMillis(), + ); + } + return gkeClusterLocator; } - static fromConfig(config: Config): GkeClusterLocator { + static fromConfig( + config: Config, + refreshInterval: Duration | undefined = undefined, + ): GkeClusterLocator { return GkeClusterLocator.fromConfigWithClient( config, new container.v1.ClusterManagerClient(), + refreshInterval, ); } + async getClusters(): Promise { + if (!this.hasClusterDetails) { + // refresh at least once when first called, when retries are disabled and in tests + await this.refreshClusters(); + } + return this.clusterDetails ?? []; + } + // TODO pass caData into the object - async getClusters(): Promise { + async refreshClusters(): Promise { const { projectId, region, @@ -70,7 +98,7 @@ export class GkeClusterLocator implements KubernetesClustersSupplier { try { const [response] = await this.client.listClusters(request); - return (response.clusters ?? []).map(r => ({ + this.clusterDetails = (response.clusters ?? []).map(r => ({ // TODO filter out clusters which don't have name or endpoint name: r.name ?? 'unknown', url: `https://${r.endpoint ?? ''}`, @@ -88,6 +116,7 @@ export class GkeClusterLocator implements KubernetesClustersSupplier { } : {}), })); + this.hasClusterDetails = true; } catch (e) { throw new ForwardedError( `There was an error retrieving clusters from GKE for projectId=${projectId} region=${region}`, diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts index 2bc2c719a0..cc7e7d07d6 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts @@ -15,9 +15,9 @@ */ import { Config, ConfigReader } from '@backstage/config'; -import { getCombinedClusterDetails } from './index'; +import { getCombinedClusterSupplier } from './index'; -describe('getCombinedClusterDetails', () => { +describe('getCombinedClusterSupplier', () => { it('should retrieve cluster details from config', async () => { const config: Config = new ConfigReader( { @@ -45,7 +45,8 @@ describe('getCombinedClusterDetails', () => { 'ctx', ); - const result = await getCombinedClusterDetails(config); + const clusterSupplier = getCombinedClusterSupplier(config); + const result = await clusterSupplier.getClusters(); expect(result).toStrictEqual([ { @@ -99,7 +100,7 @@ describe('getCombinedClusterDetails', () => { 'ctx', ); - await expect(getCombinedClusterDetails(config)).rejects.toStrictEqual( + expect(() => getCombinedClusterSupplier(config)).toThrowError( new Error('Unsupported kubernetes.clusterLocatorMethods: "magic"'), ); }); diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.ts b/plugins/kubernetes-backend/src/cluster-locator/index.ts index a4bcf77395..53aeb44f8b 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.ts @@ -15,38 +15,49 @@ */ import { Config } from '@backstage/config'; -import { ClusterDetails } from '../types/types'; +import { Duration } from 'luxon'; +import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; import { ConfigClusterLocator } from './ConfigClusterLocator'; import { GkeClusterLocator } from './GkeClusterLocator'; -export const getCombinedClusterDetails = async ( +class CombinedClustersSupplier implements KubernetesClustersSupplier { + constructor(readonly clusterSuppliers: KubernetesClustersSupplier[]) {} + + async getClusters(): Promise { + return await Promise.all( + this.clusterSuppliers.map(supplier => supplier.getClusters()), + ) + .then(res => { + return res.flat(); + }) + .catch(e => { + throw e; + }); + } +} + +export const getCombinedClusterSupplier = ( rootConfig: Config, -): Promise => { - return Promise.all( - rootConfig - .getConfigArray('kubernetes.clusterLocatorMethods') - .map(clusterLocatorMethod => { - const type = clusterLocatorMethod.getString('type'); - switch (type) { - case 'config': - return ConfigClusterLocator.fromConfig( - clusterLocatorMethod, - ).getClusters(); - case 'gke': - return GkeClusterLocator.fromConfig( - clusterLocatorMethod, - ).getClusters(); - default: - throw new Error( - `Unsupported kubernetes.clusterLocatorMethods: "${type}"`, - ); - } - }), - ) - .then(res => { - return res.flat(); - }) - .catch(e => { - throw e; + refreshInterval: Duration | undefined = undefined, +): KubernetesClustersSupplier => { + const clusterSuppliers = rootConfig + .getConfigArray('kubernetes.clusterLocatorMethods') + .map(clusterLocatorMethod => { + const type = clusterLocatorMethod.getString('type'); + switch (type) { + case 'config': + return ConfigClusterLocator.fromConfig(clusterLocatorMethod); + case 'gke': + return GkeClusterLocator.fromConfig( + clusterLocatorMethod, + refreshInterval, + ); + default: + throw new Error( + `Unsupported kubernetes.clusterLocatorMethods: "${type}"`, + ); + } }); + + return new CombinedClustersSupplier(clusterSuppliers); }; diff --git a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts index 8586c5e445..0dce2305bf 100644 --- a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts +++ b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts @@ -19,7 +19,9 @@ import { MultiTenantServiceLocator } from './MultiTenantServiceLocator'; describe('MultiTenantConfigClusterLocator', () => { it('empty clusters returns empty cluster details', async () => { - const sut = new MultiTenantServiceLocator([]); + const sut = new MultiTenantServiceLocator({ + getClusters: async () => [], + }); const result = await sut.getClustersByServiceId('ignored'); @@ -27,14 +29,18 @@ describe('MultiTenantConfigClusterLocator', () => { }); it('one clusters returns one cluster details', async () => { - const sut = new MultiTenantServiceLocator([ - { - name: 'cluster1', - url: 'http://localhost:8080', - authProvider: 'serviceAccount', - serviceAccountToken: '12345', + const sut = new MultiTenantServiceLocator({ + getClusters: async () => { + return [ + { + name: 'cluster1', + url: 'http://localhost:8080', + authProvider: 'serviceAccount', + serviceAccountToken: '12345', + }, + ]; }, - ]); + }); const result = await sut.getClustersByServiceId('ignored'); @@ -49,19 +55,23 @@ describe('MultiTenantConfigClusterLocator', () => { }); it('two clusters returns two cluster details', async () => { - const sut = new MultiTenantServiceLocator([ - { - name: 'cluster1', - serviceAccountToken: 'token', - url: 'http://localhost:8080', - authProvider: 'serviceAccount', + const sut = new MultiTenantServiceLocator({ + getClusters: async () => { + return [ + { + name: 'cluster1', + serviceAccountToken: 'token', + url: 'http://localhost:8080', + authProvider: 'serviceAccount', + }, + { + name: 'cluster2', + url: 'http://localhost:8081', + authProvider: 'google', + }, + ]; }, - { - name: 'cluster2', - url: 'http://localhost:8081', - authProvider: 'google', - }, - ]); + }); const result = await sut.getClustersByServiceId('ignored'); diff --git a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts index 9f874c7fc8..30c66a38d1 100644 --- a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts +++ b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts @@ -14,20 +14,24 @@ * limitations under the License. */ -import { ClusterDetails, KubernetesServiceLocator } from '../types/types'; +import { + ClusterDetails, + KubernetesClustersSupplier, + KubernetesServiceLocator, +} from '../types/types'; // This locator assumes that every service is located on every cluster // Therefore it will always return all clusters provided export class MultiTenantServiceLocator implements KubernetesServiceLocator { - private readonly clusterDetails: ClusterDetails[]; + private readonly clusterSupplier: KubernetesClustersSupplier; - constructor(clusterDetails: ClusterDetails[]) { - this.clusterDetails = clusterDetails; + constructor(clusterSupplier: KubernetesClustersSupplier) { + this.clusterSupplier = clusterSupplier; } // As this implementation always returns all clusters serviceId is ignored here // eslint-disable-next-line @typescript-eslint/no-unused-vars async getClustersByServiceId(_serviceId: string): Promise { - return this.clusterDetails; + return this.clusterSupplier.getClusters(); } } diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts index a1eb60a239..a3dfc8b005 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts @@ -17,10 +17,10 @@ import { Config } from '@backstage/config'; import express from 'express'; import Router from 'express-promise-router'; import { Logger } from 'winston'; -import { getCombinedClusterDetails } from '../cluster-locator'; +import { Duration } from 'luxon'; +import { getCombinedClusterSupplier } from '../cluster-locator'; import { MultiTenantServiceLocator } from '../service-locator/MultiTenantServiceLocator'; import { - ClusterDetails, KubernetesObjectTypes, ServiceLocatorMethod, CustomResource, @@ -50,7 +50,6 @@ export interface KubernetesEnvironment { */ export type KubernetesBuilderReturn = Promise<{ router: express.Router; - clusterDetails: ClusterDetails[]; clusterSupplier: KubernetesClustersSupplier; customResources: CustomResource[]; fetcher: KubernetesFetcher; @@ -60,6 +59,9 @@ export type KubernetesBuilderReturn = Promise<{ export class KubernetesBuilder { private clusterSupplier?: KubernetesClustersSupplier; + private defaultClusterRefreshInterval: Duration = Duration.fromObject({ + minutes: 60, + }); private objectsProvider?: KubernetesObjectsProvider; private fetcher?: KubernetesFetcher; private serviceLocator?: KubernetesServiceLocator; @@ -91,13 +93,13 @@ export class KubernetesBuilder { const fetcher = this.fetcher ?? this.buildFetcher(); - const clusterSupplier = this.clusterSupplier ?? this.buildClusterSupplier(); - - const clusterDetails = await this.fetchClusterDetails(clusterSupplier); + const clusterSupplier = + this.clusterSupplier ?? + this.buildClusterSupplier(this.defaultClusterRefreshInterval); const serviceLocator = this.serviceLocator ?? - this.buildServiceLocator(this.getServiceLocatorMethod(), clusterDetails); + this.buildServiceLocator(this.getServiceLocatorMethod(), clusterSupplier); const objectsProvider = this.objectsProvider ?? @@ -109,10 +111,9 @@ export class KubernetesBuilder { objectTypesToFetch: this.getObjectTypesToFetch(), }); - const router = this.buildRouter(objectsProvider, clusterDetails); + const router = this.buildRouter(objectsProvider, clusterSupplier); return { - clusterDetails, clusterSupplier, customResources, fetcher, @@ -127,6 +128,11 @@ export class KubernetesBuilder { return this; } + public setDefaultClusterRefreshInterval(refreshInterval: Duration) { + this.defaultClusterRefreshInterval = refreshInterval; + return this; + } + public setObjectsProvider(objectsProvider?: KubernetesObjectsProvider) { this.objectsProvider = objectsProvider; return this; @@ -161,13 +167,11 @@ export class KubernetesBuilder { return customResources; } - protected buildClusterSupplier(): KubernetesClustersSupplier { + protected buildClusterSupplier( + refreshInterval: Duration, + ): KubernetesClustersSupplier { const config = this.env.config; - return { - getClusters() { - return getCombinedClusterDetails(config); - }, - }; + return getCombinedClusterSupplier(config, refreshInterval); } protected buildObjectsProvider( @@ -185,13 +189,13 @@ export class KubernetesBuilder { protected buildServiceLocator( method: ServiceLocatorMethod, - clusterDetails: ClusterDetails[], + clusterSupplier: KubernetesClustersSupplier, ): KubernetesServiceLocator { switch (method) { case 'multiTenant': - return this.buildMultiTenantServiceLocator(clusterDetails); + return this.buildMultiTenantServiceLocator(clusterSupplier); case 'http': - return this.buildHttpServiceLocator(clusterDetails); + return this.buildHttpServiceLocator(clusterSupplier); default: throw new Error( `Unsupported kubernetes.clusterLocatorMethod "${method}"`, @@ -200,20 +204,20 @@ export class KubernetesBuilder { } protected buildMultiTenantServiceLocator( - clusterDetails: ClusterDetails[], + clusterSupplier: KubernetesClustersSupplier, ): KubernetesServiceLocator { - return new MultiTenantServiceLocator(clusterDetails); + return new MultiTenantServiceLocator(clusterSupplier); } protected buildHttpServiceLocator( - _clusterDetails: ClusterDetails[], + _clusterSupplier: KubernetesClustersSupplier, ): KubernetesServiceLocator { throw new Error('not implemented'); } protected buildRouter( objectsProvider: KubernetesObjectsProvider, - clusterDetails: ClusterDetails[], + clusterSupplier: KubernetesClustersSupplier, ): express.Router { const logger = this.env.logger; const router = Router(); @@ -236,6 +240,7 @@ export class KubernetesBuilder { }); router.get('/clusters', async (_, res) => { + const clusterDetails = await this.fetchClusterDetails(clusterSupplier); res.json({ items: clusterDetails.map(cd => ({ name: cd.name, diff --git a/plugins/kubernetes-backend/src/service/runPeriodically.ts b/plugins/kubernetes-backend/src/service/runPeriodically.ts new file mode 100644 index 0000000000..2f3104e221 --- /dev/null +++ b/plugins/kubernetes-backend/src/service/runPeriodically.ts @@ -0,0 +1,54 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Runs a function repeatedly, with a fixed wait between invocations. + * + * Supports async functions, and silently ignores exceptions and rejections. + * + * @param fn - The function to run. May return a Promise. + * @param delayMs - The delay between a completed function invocation and the + * next. + * @returns A function that, when called, stops the invocation loop. + */ +export function runPeriodically(fn: () => any, delayMs: number): () => void { + let cancel: () => void; + let cancelled = false; + const cancellationPromise = new Promise(resolve => { + cancel = () => { + resolve(); + cancelled = true; + }; + }); + + const startRefresh = async () => { + while (!cancelled) { + try { + await fn(); + } catch { + // ignore intentionally + } + + await Promise.race([ + new Promise(resolve => setTimeout(resolve, delayMs)), + cancellationPromise, + ]); + } + }; + startRefresh(); + + return cancel!; +} diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index 37c26956fe..2d7e2d3da7 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -80,6 +80,12 @@ export type KubernetesObjectTypes = // Used to load cluster details from different sources export interface KubernetesClustersSupplier { + /** + * Returns the cached list of clusters. + * + * Implementations _should_ cache the clusters and refresh them periodically, + * as getClusters is called whenever the list of clusters is needed. + */ getClusters(): Promise; } diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx index 1c63852866..e2052b792f 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx @@ -65,7 +65,8 @@ export const GitlabRepoPicker = (props: { )} - The organization, user or project that this repo will belong to + The organization, groups, subgroups, user, project (also known as + namespaces in gitlab), that this repo will belong to Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { ApiRef } from '@backstage/core-plugin-api'; +import { AsyncState } from 'react-use/lib/useAsync'; +import { ComponentProps } from 'react'; +import { JsonObject } from '@backstage/types'; +import { PropsWithChildren } from 'react'; +import { default as React_2 } from 'react'; +import { SearchQuery } from '@backstage/plugin-search-common'; +import { SearchResultSet } from '@backstage/plugin-search-common'; + +// @public (undocumented) +export interface SearchApi { + // (undocumented) + query(query: SearchQuery): Promise; +} + +// Warning: (ae-forgotten-export) The symbol "QueryResultProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SearchApiProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function SearchApiProviderForStorybook( + props: PropsWithChildren, +): JSX.Element; + +// @public (undocumented) +export const searchApiRef: ApiRef; + +// @public (undocumented) +export const SearchContextProvider: ({ + initialState, + children, +}: React_2.PropsWithChildren<{ + initialState?: SearchContextState | undefined; +}>) => JSX.Element; + +// Warning: (ae-missing-release-tag) "SearchContextProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export const SearchContextProviderForStorybook: ( + props: ComponentProps & QueryResultProps, +) => JSX.Element; + +// @public (undocumented) +export type SearchContextState = { + term: string; + types: string[]; + filters: JsonObject; + pageCursor?: string; +}; + +// Warning: (ae-forgotten-export) The symbol "SearchContextValue" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export const useSearch: () => SearchContextValue; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json new file mode 100644 index 0000000000..151660943e --- /dev/null +++ b/plugins/search-react/package.json @@ -0,0 +1,53 @@ +{ + "name": "@backstage/plugin-search-react", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "web-library" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/search-react" + }, + "keywords": [ + "backstage" + ], + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean", + "start": "backstage-cli package start" + }, + "dependencies": { + "@backstage/plugin-search-common": "^0.3.3-next.1", + "@backstage/core-plugin-api": "^1.0.0", + "@backstage/core-app-api": "^1.0.1-next.0", + "react-use": "^17.3.2", + "@backstage/types": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.13.1 || ^17.0.0", + "react": "^16.13.1 || ^17.0.0" + }, + "devDependencies": { + "@backstage/test-utils": "^1.0.1-next.1", + "@testing-library/react": "^12.1.3", + "@testing-library/react-hooks": "^7.0.2", + "@testing-library/jest-dom": "^5.10.1" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/search-react/src/api.ts b/plugins/search-react/src/api.ts new file mode 100644 index 0000000000..eb8c9c23db --- /dev/null +++ b/plugins/search-react/src/api.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SearchQuery, SearchResultSet } from '@backstage/plugin-search-common'; +import { createApiRef } from '@backstage/core-plugin-api'; + +/** + * @public + */ +export const searchApiRef = createApiRef({ + id: 'plugin.search.queryservice', +}); + +/** + * @public + */ +export interface SearchApi { + query(query: SearchQuery): Promise; +} diff --git a/plugins/search/src/components/SearchContext/SearchContext.test.tsx b/plugins/search-react/src/context/SearchContext.test.tsx similarity index 99% rename from plugins/search/src/components/SearchContext/SearchContext.test.tsx rename to plugins/search-react/src/context/SearchContext.test.tsx index 6513dc21bb..b19953f6a5 100644 --- a/plugins/search/src/components/SearchContext/SearchContext.test.tsx +++ b/plugins/search-react/src/context/SearchContext.test.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugins/search-react/src/context/SearchContext.tsx b/plugins/search-react/src/context/SearchContext.tsx new file mode 100644 index 0000000000..d6245d5ccf --- /dev/null +++ b/plugins/search-react/src/context/SearchContext.tsx @@ -0,0 +1,147 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { JsonObject } from '@backstage/types'; +import { useApi, AnalyticsContext } from '@backstage/core-plugin-api'; +import { SearchResultSet } from '@backstage/plugin-search-common'; +import React, { + createContext, + PropsWithChildren, + useCallback, + useContext, + useEffect, + useState, +} from 'react'; +import useAsync, { AsyncState } from 'react-use/lib/useAsync'; +import usePrevious from 'react-use/lib/usePrevious'; +import { searchApiRef } from '../api'; + +type SearchContextValue = { + result: AsyncState; + setTerm: React.Dispatch>; + setTypes: React.Dispatch>; + setFilters: React.Dispatch>; + setPageCursor: React.Dispatch>; + fetchNextPage?: React.DispatchWithoutAction; + fetchPreviousPage?: React.DispatchWithoutAction; +} & SearchContextState; + +/** + * + * @public + */ +export type SearchContextState = { + term: string; + types: string[]; + filters: JsonObject; + pageCursor?: string; +}; + +/** + * @public + */ +export const SearchContext = createContext( + undefined, +); + +/** + * The initial state of `SearchContextProvider`. + * + */ +const searchInitialState: SearchContextState = { + term: '', + pageCursor: undefined, + filters: {}, + types: [], +}; + +/** + * @public + */ +export const SearchContextProvider = ({ + initialState = searchInitialState, + children, +}: PropsWithChildren<{ initialState?: SearchContextState }>) => { + const searchApi = useApi(searchApiRef); + const [pageCursor, setPageCursor] = useState( + initialState.pageCursor, + ); + const [filters, setFilters] = useState(initialState.filters); + const [term, setTerm] = useState(initialState.term); + const [types, setTypes] = useState(initialState.types); + + const prevTerm = usePrevious(term); + + const result = useAsync( + () => + searchApi.query({ + term, + filters, + pageCursor, + types, + }), + [term, filters, types, pageCursor], + ); + + const hasNextPage = + !result.loading && !result.error && result.value?.nextPageCursor; + const hasPreviousPage = + !result.loading && !result.error && result.value?.previousPageCursor; + const fetchNextPage = useCallback(() => { + setPageCursor(result.value?.nextPageCursor); + }, [result.value?.nextPageCursor]); + const fetchPreviousPage = useCallback(() => { + setPageCursor(result.value?.previousPageCursor); + }, [result.value?.previousPageCursor]); + + useEffect(() => { + // Any time a term is reset, we want to start from page 0. + if (term && prevTerm && term !== prevTerm) { + setPageCursor(undefined); + } + }, [term, prevTerm, initialState.pageCursor]); + + const value: SearchContextValue = { + result, + filters, + setFilters, + term, + setTerm, + types, + setTypes, + pageCursor, + setPageCursor, + fetchNextPage: hasNextPage ? fetchNextPage : undefined, + fetchPreviousPage: hasPreviousPage ? fetchPreviousPage : undefined, + }; + + return ( + + + + ); +}; + +/** + * @public + */ +export const useSearch = () => { + const context = useContext(SearchContext); + if (context === undefined) { + throw new Error('useSearch must be used within a SearchContextProvider'); + } + return context; +}; diff --git a/plugins/search/src/components/SearchContext/SearchContextForStorybook.stories.tsx b/plugins/search-react/src/context/SearchContextForStorybook.stories.tsx similarity index 91% rename from plugins/search/src/components/SearchContext/SearchContextForStorybook.stories.tsx rename to plugins/search-react/src/context/SearchContextForStorybook.stories.tsx index 7d6c35b00c..51297c965f 100644 --- a/plugins/search/src/components/SearchContext/SearchContextForStorybook.stories.tsx +++ b/plugins/search-react/src/context/SearchContextForStorybook.stories.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ import { ApiProvider } from '@backstage/core-app-api'; import { SearchResultSet } from '@backstage/plugin-search-common'; import { TestApiRegistry } from '@backstage/test-utils'; import React, { ComponentProps, PropsWithChildren } from 'react'; -import { searchApiRef } from '../../apis'; +import { searchApiRef } from '../api'; import { SearchContextProvider as RealSearchContextProvider } from './SearchContext'; type QueryResultProps = { @@ -26,7 +26,7 @@ type QueryResultProps = { /** * Utility context provider only for use in Storybook stories. You should use - * the real `` exported by `@backstage/plugin-search` in + * the real `` exported by `@backstage/plugin-search-react` in * your app instead of this! In some cases (like the search page) it may * already be provided on your behalf. */ @@ -40,6 +40,10 @@ export const SearchContextProvider = ( ); }; +/** + * Utility api provider only for use in Storybook stories. + * + */ export function SearchApiProvider(props: PropsWithChildren) { const { mockedResults, children } = props; const query: any = () => Promise.resolve(mockedResults || {}); diff --git a/plugins/search-react/src/context/index.tsx b/plugins/search-react/src/context/index.tsx new file mode 100644 index 0000000000..f2ea486e9d --- /dev/null +++ b/plugins/search-react/src/context/index.tsx @@ -0,0 +1,26 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { + SearchContextProvider, + SearchContext, + useSearch, +} from './SearchContext'; +export type { SearchContextState } from './SearchContext'; +export { + SearchContextProvider as SearchContextProviderForStorybook, + SearchApiProvider as SearchApiProviderForStorybook, +} from './SearchContextForStorybook.stories'; diff --git a/plugins/search-react/src/index.ts b/plugins/search-react/src/index.ts new file mode 100644 index 0000000000..934cf2e9f1 --- /dev/null +++ b/plugins/search-react/src/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { searchApiRef } from './api'; +export type { SearchApi } from './api'; +export { + SearchContextProvider, + useSearch, + SearchContextProviderForStorybook, + SearchApiProviderForStorybook, +} from './context'; +export type { SearchContextState } from './context'; diff --git a/plugins/home/src/templates/index.ts b/plugins/search-react/src/setupTests.ts similarity index 81% rename from plugins/home/src/templates/index.ts rename to plugins/search-react/src/setupTests.ts index bfebe73cd8..992b60d3a4 100644 --- a/plugins/home/src/templates/index.ts +++ b/plugins/search-react/src/setupTests.ts @@ -14,5 +14,4 @@ * limitations under the License. */ -export { TemplateBackstageLogoIcon } from './TemplateBackstageLogoIcon'; -export { TemplateBackstageLogo } from './TemplateBackstageLogo' +import '@testing-library/jest-dom'; diff --git a/plugins/search/api-report.md b/plugins/search/api-report.md index 1bdd918802..d85158c925 100644 --- a/plugins/search/api-report.md +++ b/plugins/search/api-report.md @@ -83,7 +83,7 @@ export const Router: () => JSX.Element; // Warning: (ae-missing-release-tag) "SearchApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public @deprecated (undocumented) export interface SearchApi { // (undocumented) query(query: SearchQuery): Promise; @@ -91,7 +91,7 @@ export interface SearchApi { // Warning: (ae-missing-release-tag) "searchApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public @deprecated (undocumented) export const searchApiRef: ApiRef; // @public (undocumented) @@ -140,7 +140,7 @@ export type SearchBarProps = Partial; // Warning: (ae-missing-release-tag) "SearchContextProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public @deprecated (undocumented) export const SearchContextProvider: ({ initialState, children, @@ -322,10 +322,11 @@ export type SidebarSearchProps = { icon?: IconComponent; }; +// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it // Warning: (ae-forgotten-export) The symbol "SearchContextValue" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "useSearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public @deprecated (undocumented) export const useSearch: () => SearchContextValue; // @public diff --git a/plugins/search/package.json b/plugins/search/package.json index c32e830950..4ac891775e 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -39,6 +39,7 @@ "@backstage/core-plugin-api": "^1.0.0", "@backstage/errors": "^1.0.0", "@backstage/plugin-catalog-react": "^1.0.1-next.1", + "@backstage/plugin-search-react": "^0.0.0", "@backstage/plugin-search-common": "^0.3.3-next.1", "@backstage/theme": "^0.2.15", "@backstage/types": "^1.0.0", diff --git a/plugins/search/src/apis.ts b/plugins/search/src/apis.ts index 908942d87d..096b11bb5e 100644 --- a/plugins/search/src/apis.ts +++ b/plugins/search/src/apis.ts @@ -21,12 +21,19 @@ import { } from '@backstage/core-plugin-api'; import { ResponseError } from '@backstage/errors'; import { SearchQuery, SearchResultSet } from '@backstage/plugin-search-common'; + import qs from 'qs'; +/** + * @deprecated import from `@backstage/plugin-search-react` instead + */ export const searchApiRef = createApiRef({ id: 'plugin.search.queryservice', }); +/** + * @deprecated import from `@backstage/plugin-search-react` instead + */ export interface SearchApi { query(query: SearchQuery): Promise; } diff --git a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx b/plugins/search/src/components/SearchBar/SearchBar.stories.tsx index c0d4b07965..72c7deb06b 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.stories.tsx @@ -16,7 +16,7 @@ import { Grid, makeStyles, Paper } from '@material-ui/core'; import React, { ComponentType } from 'react'; -import { SearchContextProvider } from '../SearchContext/SearchContextForStorybook.stories'; +import { SearchContextProviderForStorybook } from '@backstage/plugin-search-react'; import { SearchBar } from './SearchBar'; export default { @@ -24,13 +24,13 @@ export default { component: SearchBar, decorators: [ (Story: ComponentType<{}>) => ( - + - + ), ], }; diff --git a/plugins/search/src/components/SearchContext/SearchContext.tsx b/plugins/search/src/components/SearchContext/SearchContext.tsx index 100d8a3ac3..c87295ab99 100644 --- a/plugins/search/src/components/SearchContext/SearchContext.tsx +++ b/plugins/search/src/components/SearchContext/SearchContext.tsx @@ -51,6 +51,9 @@ export type SearchContextState = { pageCursor?: string; }; +/** + * @deprecated import from `@backstage/plugin-search-react` instead + */ export const SearchContext = createContext( undefined, ); @@ -62,6 +65,9 @@ const searchInitialState: SearchContextState = { types: [], }; +/** + * @deprecated import from `@backstage/plugin-search-react` instead + */ export const SearchContextProvider = ({ initialState = searchInitialState, children, @@ -126,6 +132,9 @@ export const SearchContextProvider = ({ ); }; +/** + * @deprecated import from "@backstage/plugin-search-react" instead + */ export const useSearch = () => { const context = useContext(SearchContext); if (context === undefined) { diff --git a/plugins/search/src/components/SearchFilter/SearchFilter.stories.tsx b/plugins/search/src/components/SearchFilter/SearchFilter.stories.tsx index c43753e8d2..3c49b76699 100644 --- a/plugins/search/src/components/SearchFilter/SearchFilter.stories.tsx +++ b/plugins/search/src/components/SearchFilter/SearchFilter.stories.tsx @@ -16,7 +16,7 @@ import { Grid, Paper } from '@material-ui/core'; import React, { ComponentType } from 'react'; -import { SearchContextProvider } from '../SearchContext/SearchContextForStorybook.stories'; +import { SearchContextProviderForStorybook } from '@backstage/plugin-search-react'; import { SearchFilter } from './SearchFilter'; export default { @@ -24,13 +24,13 @@ export default { component: SearchFilter, decorators: [ (Story: ComponentType<{}>) => ( - + - + ), ], }; diff --git a/plugins/search/src/components/SearchModal/SearchModal.stories.tsx b/plugins/search/src/components/SearchModal/SearchModal.stories.tsx index aa0ad0479a..a1d9517674 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.stories.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.stories.tsx @@ -18,7 +18,7 @@ import { wrapInTestApp } from '@backstage/test-utils'; import { Button } from '@material-ui/core'; import React, { ComponentType } from 'react'; import { rootRouteRef } from '../../plugin'; -import { SearchApiProvider } from '../SearchContext/SearchContextForStorybook.stories'; +import { SearchApiProviderForStorybook } from '@backstage/plugin-search-react'; import { SearchModal } from './SearchModal'; import { useSearchModal } from './useSearchModal'; @@ -57,9 +57,9 @@ export default { decorators: [ (Story: ComponentType<{}>) => wrapInTestApp( - + - , + , { mountedRoutes: { '/search': rootRouteRef } }, ), ], diff --git a/plugins/search/src/components/SearchResult/SearchResult.stories.tsx b/plugins/search/src/components/SearchResult/SearchResult.stories.tsx index c1685df2c1..4a3d950164 100644 --- a/plugins/search/src/components/SearchResult/SearchResult.stories.tsx +++ b/plugins/search/src/components/SearchResult/SearchResult.stories.tsx @@ -19,7 +19,8 @@ import { List, ListItem } from '@material-ui/core'; import React, { ComponentType } from 'react'; import { MemoryRouter } from 'react-router'; import { DefaultResultListItem } from '../DefaultResultListItem'; -import { SearchContextProvider } from '../SearchContext/SearchContextForStorybook.stories'; + +import { SearchContextProviderForStorybook } from '@backstage/plugin-search-react'; import { SearchResult } from './SearchResult'; const mockResults = { @@ -57,9 +58,9 @@ export default { decorators: [ (Story: ComponentType<{}>) => ( - + - + ), ], diff --git a/plugins/search/src/components/SearchType/SearchType.stories.tsx b/plugins/search/src/components/SearchType/SearchType.stories.tsx index 596c4a027a..b56f4c089c 100644 --- a/plugins/search/src/components/SearchType/SearchType.stories.tsx +++ b/plugins/search/src/components/SearchType/SearchType.stories.tsx @@ -19,7 +19,7 @@ import CatalogIcon from '@material-ui/icons/MenuBook'; import DocsIcon from '@material-ui/icons/Description'; import UsersGroupsIcon from '@material-ui/icons/Person'; import React, { ComponentType } from 'react'; -import { SearchContextProvider } from '../SearchContext/SearchContextForStorybook.stories'; +import { SearchContextProviderForStorybook } from '@backstage/plugin-search-react'; import { SearchType } from './SearchType'; export default { @@ -27,13 +27,13 @@ export default { component: SearchType, decorators: [ (Story: ComponentType<{}>) => ( - + - + ), ], }; diff --git a/plugins/tech-insights/README.md b/plugins/tech-insights/README.md index 5f7d7ad4e5..a305ef5dc7 100644 --- a/plugins/tech-insights/README.md +++ b/plugins/tech-insights/README.md @@ -38,6 +38,10 @@ const serviceEntityPage = ( title="Customized title for the scorecard" description="Small description about scorecards" /> + ... @@ -46,6 +50,8 @@ const serviceEntityPage = ( It is not obligatory to pass title and description props to `EntityTechInsightsScorecardContent`. If those are left out, default values from `defaultCheckResultRenderers` in `CheckResultRenderer` will be taken, hence `Boolean scorecard` and `This card represents an overview of default boolean Backstage checks`. +You can pass an array `checksId` as a prop with the [Fact Retrievers ids](../tech-insights-backend#creating-fact-retrievers) to limit which checks you want to show in this card, If you don't pass, the default value is show all checks. + ## Boolean Scorecard Example If you follow the [Backend Example](https://github.com/backstage/backstage/tree/master/plugins/tech-insights-backend#backend-example), once the needed facts have been generated the boolean scorecard will look like this: diff --git a/plugins/tech-insights/api-report.md b/plugins/tech-insights/api-report.md index deadd06010..9574efca28 100644 --- a/plugins/tech-insights/api-report.md +++ b/plugins/tech-insights/api-report.md @@ -34,9 +34,11 @@ export type CheckResultRenderer = { export const EntityTechInsightsScorecardContent: ({ title, description, + checksId, }: { title?: string | undefined; description?: string | undefined; + checksId?: string[] | undefined; }) => JSX.Element; // @public @@ -58,7 +60,7 @@ export interface TechInsightsApi { // (undocumented) runChecks( entityParams: CompoundEntityRef, - checks?: Check[], + checks?: string[], ): Promise; } diff --git a/plugins/tech-insights/src/api/TechInsightsApi.ts b/plugins/tech-insights/src/api/TechInsightsApi.ts index 0a6bb937a6..a24bf74a01 100644 --- a/plugins/tech-insights/src/api/TechInsightsApi.ts +++ b/plugins/tech-insights/src/api/TechInsightsApi.ts @@ -47,7 +47,7 @@ export interface TechInsightsApi { getAllChecks(): Promise; runChecks( entityParams: CompoundEntityRef, - checks?: Check[], + checks?: string[], ): Promise; runBulkChecks( entities: CompoundEntityRef[], diff --git a/plugins/tech-insights/src/api/TechInsightsClient.ts b/plugins/tech-insights/src/api/TechInsightsClient.ts index 5b9af9557f..04de46dba9 100644 --- a/plugins/tech-insights/src/api/TechInsightsClient.ts +++ b/plugins/tech-insights/src/api/TechInsightsClient.ts @@ -75,13 +75,12 @@ export class TechInsightsClient implements TechInsightsApi { async runChecks( entityParams: CompoundEntityRef, - checks?: Check[], + checks?: string[], ): Promise { const url = await this.discoveryApi.getBaseUrl('tech-insights'); const { token } = await this.identityApi.getCredentials(); const { namespace, kind, name } = entityParams; - const checkIds = checks ? checks.map(check => check.id) : []; - const requestBody = { checks: checkIds.length > 0 ? checkIds : undefined }; + const requestBody = { checks }; const response = await fetch( `${url}/checks/run/${encodeURIComponent(namespace)}/${encodeURIComponent( kind, diff --git a/plugins/tech-insights/src/components/ScorecardsOverview/ScorecardsOverview.tsx b/plugins/tech-insights/src/components/ScorecardsOverview/ScorecardsOverview.tsx index 73143a7ea8..53cf826e78 100644 --- a/plugins/tech-insights/src/components/ScorecardsOverview/ScorecardsOverview.tsx +++ b/plugins/tech-insights/src/components/ScorecardsOverview/ScorecardsOverview.tsx @@ -26,14 +26,16 @@ import { techInsightsApiRef } from '../../api/TechInsightsApi'; export const ScorecardsOverview = ({ title, description, + checksId, }: { title?: string; description?: string; + checksId?: string[]; }) => { const api = useApi(techInsightsApiRef); const { namespace, kind, name } = useParams(); const { value, loading, error } = useAsync( - async () => await api.runChecks({ namespace, kind, name }), + async () => await api.runChecks({ namespace, kind, name }, checksId), ); if (loading) { diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 79ef57ef99..7d2aafd79b 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -44,8 +44,7 @@ "@backstage/integration": "^1.1.0-next.1", "@backstage/integration-react": "^1.0.1-next.1", "@backstage/plugin-catalog-react": "^1.0.1-next.2", - "@backstage/plugin-catalog": "^1.1.0-next.2", - "@backstage/plugin-search": "^0.7.5-next.0", + "@backstage/plugin-search-react": "^0.0.0", "@backstage/plugin-techdocs-react": "^0.0.0", "@backstage/theme": "^0.2.15", "@backstage/version-bridge": "^1.0.0", diff --git a/plugins/techdocs/src/search/components/TechDocsSearch.test.tsx b/plugins/techdocs/src/search/components/TechDocsSearch.test.tsx index 06d5ac6aac..5957aecc4d 100644 --- a/plugins/techdocs/src/search/components/TechDocsSearch.test.tsx +++ b/plugins/techdocs/src/search/components/TechDocsSearch.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { ApiProvider } from '@backstage/core-app-api'; -import { searchApiRef } from '@backstage/plugin-search'; +import { searchApiRef } from '@backstage/plugin-search-react'; import { TestApiRegistry, wrapInTestApp } from '@backstage/test-utils'; import { act, diff --git a/plugins/techdocs/src/search/components/TechDocsSearch.tsx b/plugins/techdocs/src/search/components/TechDocsSearch.tsx index 30cc692b7c..f11fa38948 100644 --- a/plugins/techdocs/src/search/components/TechDocsSearch.tsx +++ b/plugins/techdocs/src/search/components/TechDocsSearch.tsx @@ -15,7 +15,10 @@ */ import { CompoundEntityRef } from '@backstage/catalog-model'; -import { SearchContextProvider, useSearch } from '@backstage/plugin-search'; +import { + SearchContextProvider, + useSearch, +} from '@backstage/plugin-search-react'; import { makeStyles, CircularProgress, diff --git a/plugins/todo-backend/README.md b/plugins/todo-backend/README.md index 4015087b02..4686c2d39f 100644 --- a/plugins/todo-backend/README.md +++ b/plugins/todo-backend/README.md @@ -52,8 +52,9 @@ async function main() { ## Scanned Files -The included `TodoReaderService` and `TodoScmReader` works by reading source code of to the entity that is being viewed. The location source code is determined by the value of the [`backstage.io/source-location` -](https://backstage.io/docs/features/software-catalog/well-known-annotations#backstageiosource-location) annotation of the entity, and if that is missing it falls back to the [`backstage.io/managed-by-location `](https://backstage.io/docs/features/software-catalog/well-known-annotations#backstageiomanaged-by-location) annotation. Only `url` locations are currently supported, meaning locally configured `file` locations won't work. Also note that dot-files and folders are ignored. +The included `TodoReaderService` and `TodoScmReader` works by getting the entity source location from the catalog. + +The location source code is determined automatically. In case of the source code of the component is not in the same place of the entity YAML file, you can explicitly set the value of the [`backstage.io/source-location`](https://backstage.io/docs/features/software-catalog/well-known-annotations#backstageiosource-location) annotation of the entity, and if that is missing it falls back to the [`backstage.io/managed-by-location `](https://backstage.io/docs/features/software-catalog/well-known-annotations#backstageiomanaged-by-location) annotation. Only `url` locations are currently supported, meaning locally configured `file` locations won't work. Also note that dot-files and folders are ignored. ## Parser Configuration diff --git a/plugins/todo-backend/src/service/TodoReaderService.test.ts b/plugins/todo-backend/src/service/TodoReaderService.test.ts index 943462bd63..6dd14afcb4 100644 --- a/plugins/todo-backend/src/service/TodoReaderService.test.ts +++ b/plugins/todo-backend/src/service/TodoReaderService.test.ts @@ -310,7 +310,7 @@ describe('TodoReaderService', () => { }); }); - it('should throw if entity does not have a location', async () => { + it('should not throw if entity does not have a location', async () => { const todoReader = mockTodoReader([]); const catalogClient = mockCatalogClient({ ...mockEntity, @@ -320,9 +320,8 @@ describe('TodoReaderService', () => { await expect(service.listTodos({ entity: entityName })).rejects.toEqual( expect.objectContaining({ - name: 'InputError', - message: - 'No entity location annotation found for component:default/my-component', + name: 'Error', + message: "Entity 'component:default/my-component' is missing location", }), ); }); @@ -334,7 +333,8 @@ describe('TodoReaderService', () => { metadata: { ...mockEntity.metadata, annotations: { - ['backstage.io/managed-by-location']: 'file:../info.yaml', + ['backstage.io/managed-by-location']: + 'file:../managed-by-location.yaml', }, }, }); @@ -343,7 +343,7 @@ describe('TodoReaderService', () => { await expect(service.listTodos({ entity: entityName })).rejects.toEqual( expect.objectContaining({ name: 'InputError', - message: `Invalid entity location type for component:default/my-component, got 'file'`, + message: `Invalid entity location type for component:default/my-component, got 'file' for location ../managed-by-location.yaml`, }), ); }); @@ -355,7 +355,7 @@ describe('TodoReaderService', () => { metadata: { ...mockEntity.metadata, annotations: { - ['backstage.io/source-location']: 'file:../info.yaml', + ['backstage.io/source-location']: 'file:../source-location.yaml', }, }, }); @@ -364,7 +364,7 @@ describe('TodoReaderService', () => { await expect(service.listTodos({ entity: entityName })).rejects.toEqual( expect.objectContaining({ name: 'InputError', - message: `Invalid entity source location type for component:default/my-component, got 'file'`, + message: `Invalid entity location type for component:default/my-component, got 'file' for location ../source-location.yaml`, }), ); }); diff --git a/plugins/todo-backend/src/service/TodoReaderService.ts b/plugins/todo-backend/src/service/TodoReaderService.ts index 4397f34045..35fd76b8be 100644 --- a/plugins/todo-backend/src/service/TodoReaderService.ts +++ b/plugins/todo-backend/src/service/TodoReaderService.ts @@ -17,10 +17,7 @@ import { InputError, NotFoundError } from '@backstage/errors'; import { CatalogApi } from '@backstage/catalog-client'; import { - ANNOTATION_LOCATION, - ANNOTATION_SOURCE_LOCATION, - Entity, - parseLocationRef, + getEntitySourceLocation, stringifyEntityRef, } from '@backstage/catalog-model'; import { TodoReader } from '../lib'; @@ -74,8 +71,15 @@ export class TodoReaderService implements TodoService { `Entity not found, ${stringifyEntityRef(req.entity)}`, ); } - - const url = this.getEntitySourceUrl(entity); + const entitySourceLocation = getEntitySourceLocation(entity); + if (entitySourceLocation.type !== 'url') { + throw new InputError( + `Invalid entity location type for ${stringifyEntityRef(entity)}, got '${ + entitySourceLocation.type + }' for location ${entitySourceLocation.target}`, + ); + } + const url = entitySourceLocation.target; const todos = await this.todoReader.readTodos({ url }); let limit = req.limit ?? this.defaultPageSize; @@ -125,36 +129,4 @@ export class TodoReaderService implements TodoService { limit, }; } - - private getEntitySourceUrl(entity: Entity) { - const sourceLocation = - entity.metadata.annotations?.[ANNOTATION_SOURCE_LOCATION]; - if (sourceLocation) { - const parsed = parseLocationRef(sourceLocation); - if (parsed.type !== 'url') { - throw new InputError( - `Invalid entity source location type for ${stringifyEntityRef( - entity, - )}, got '${parsed.type}'`, - ); - } - return parsed.target; - } - - const location = entity.metadata.annotations?.[ANNOTATION_LOCATION]; - if (location) { - const parsed = parseLocationRef(location); - if (parsed.type !== 'url') { - throw new InputError( - `Invalid entity location type for ${stringifyEntityRef( - entity, - )}, got '${parsed.type}'`, - ); - } - return parsed.target; - } - throw new InputError( - `No entity location annotation found for ${stringifyEntityRef(entity)}`, - ); - } } diff --git a/storybook/.storybook/main.js b/storybook/.storybook/main.js index 2356e4e41b..8ed4949cf3 100644 --- a/storybook/.storybook/main.js +++ b/storybook/.storybook/main.js @@ -6,6 +6,7 @@ const WebpackPluginFailBuildOnWarning = require('./webpack-plugin-fail-build-on- */ const BACKSTAGE_CORE_STORIES = [ 'packages/core-components', + 'packages/app', 'plugins/org', 'plugins/search', 'plugins/home', diff --git a/storybook/package.json b/storybook/package.json index d5b54d193d..87c627ac4f 100644 --- a/storybook/package.json +++ b/storybook/package.json @@ -17,7 +17,7 @@ "devDependencies": { "@storybook/addon-a11y": "^6.4.20", "@storybook/addon-actions": "^6.4.20", - "@storybook/addon-links": "^6.4.20", + "@storybook/addon-links": "^6.4.21", "@storybook/addon-storysource": "^6.4.20", "@storybook/addons": "^6.4.20", "@storybook/react": "^6.4.20", diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 82520ab5f5..fb7aa071fe 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -1356,16 +1356,16 @@ util-deprecate "^1.0.2" uuid-browser "^3.1.0" -"@storybook/addon-links@^6.4.20": - version "6.4.20" - resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-6.4.20.tgz#7e845a20deece65e7e684433d4c66a6ad61da52c" - integrity sha512-TyRuEd/3yRn2N9xasCKuE2bsY0dTRjAquGeg5WEtvHvr8V6QBLYAC4caXwPxIHSTcRQyO5IYYiVzEJ/+219neA== +"@storybook/addon-links@^6.4.21": + version "6.4.21" + resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-6.4.21.tgz#7251406c3060b63684f4de56799385f3675867c6" + integrity sha512-KajbsVAmCLVSKsrPnUEsfWuD5V0lbNBAtdil0EiOqWZU0r3ch92aSMh6H13zfT+lEPlh0PVLKamHur1js1iXGQ== dependencies: - "@storybook/addons" "6.4.20" - "@storybook/client-logger" "6.4.20" - "@storybook/core-events" "6.4.20" + "@storybook/addons" "6.4.21" + "@storybook/client-logger" "6.4.21" + "@storybook/core-events" "6.4.21" "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/router" "6.4.20" + "@storybook/router" "6.4.21" "@types/qs" "^6.9.5" core-js "^3.8.2" global "^4.4.0" @@ -1394,7 +1394,7 @@ react-syntax-highlighter "^13.5.3" regenerator-runtime "^0.13.7" -"@storybook/addons@6.4.20", "@storybook/addons@^6.4.20": +"@storybook/addons@6.4.20": version "6.4.20" resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.4.20.tgz#bbf568b7c4c5a25ef296f285aef0299998ec5933" integrity sha512-NbsLjDSkE9v2fOr0M7r2hpdYnlYs789ALkXemdTz2y0NUYSPdRfzVVQNXWrgmXivWQRL0aJ3bOjCOc668PPYjg== @@ -1411,6 +1411,23 @@ global "^4.4.0" regenerator-runtime "^0.13.7" +"@storybook/addons@6.4.21", "@storybook/addons@^6.4.20": + version "6.4.21" + resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.4.21.tgz#a0081d167eda8a30b2206ccabfe75abae0bb6b58" + integrity sha512-TFLv4FyqP5SBOHEqE6tiW+2++HngkyQ2KRbHICC7khQgRqDkrwvrdKZwzF29igseglhSmftpZrBLXyWbA7q1vg== + dependencies: + "@storybook/api" "6.4.21" + "@storybook/channels" "6.4.21" + "@storybook/client-logger" "6.4.21" + "@storybook/core-events" "6.4.21" + "@storybook/csf" "0.0.2--canary.87bc651.0" + "@storybook/router" "6.4.21" + "@storybook/theming" "6.4.21" + "@types/webpack-env" "^1.16.0" + core-js "^3.8.2" + global "^4.4.0" + regenerator-runtime "^0.13.7" + "@storybook/api@6.4.20": version "6.4.20" resolved "https://registry.npmjs.org/@storybook/api/-/api-6.4.20.tgz#65da720985b4b46998a405bddc42c9cef9bad7e4" @@ -1434,6 +1451,29 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/api@6.4.21": + version "6.4.21" + resolved "https://registry.npmjs.org/@storybook/api/-/api-6.4.21.tgz#efee41ae7bde37f6fe43ee960fef1a261b1b1dd6" + integrity sha512-AULsLd7ew11IRCpzffyLFGl5cwt9BLMok33DcIlCyvXsiqLm4/OsbgM4sj6QqWVuxcFlWMQJHoRJyeFlULFvZA== + dependencies: + "@storybook/channels" "6.4.21" + "@storybook/client-logger" "6.4.21" + "@storybook/core-events" "6.4.21" + "@storybook/csf" "0.0.2--canary.87bc651.0" + "@storybook/router" "6.4.21" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.4.21" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + store2 "^2.12.0" + telejson "^5.3.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/builder-webpack4@6.4.20": version "6.4.20" resolved "https://registry.npmjs.org/@storybook/builder-webpack4/-/builder-webpack4-6.4.20.tgz#e3b5d6b665fbf5a1ec75b7ef32c4c811897ef20d" @@ -1542,6 +1582,15 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/channels@6.4.21": + version "6.4.21" + resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.4.21.tgz#0f1924963f77ec0c3d82aa643a246824ca9f5fca" + integrity sha512-qgy8z3Hp04Q4p+E/8V9MamYYJLW8z1uv1Z+rvosNkg+eAApPg+Qe08BSj59OAUwPLrr2vpBW7WZ/BYSieW1tUg== + dependencies: + core-js "^3.8.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/client-api@6.4.20": version "6.4.20" resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.4.20.tgz#17a24af4bc047f7a6de647b9c1844ab4e40baf83" @@ -1576,6 +1625,14 @@ core-js "^3.8.2" global "^4.4.0" +"@storybook/client-logger@6.4.21": + version "6.4.21" + resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.4.21.tgz#7df21cec4d5426669e828af59232ec44ea19c81a" + integrity sha512-XkVCQ5swyYDVh5U+87DGRBdC5utJBpVW7kU5P14TQKMnSc/yHbMcXWaA89K8WKDa/WGkGbc0bKi4WrUwHFg2FA== + dependencies: + core-js "^3.8.2" + global "^4.4.0" + "@storybook/components@6.4.20": version "6.4.20" resolved "https://registry.npmjs.org/@storybook/components/-/components-6.4.20.tgz#d063b6a7e70e1be7c8aa79220bb2cd92be8057a1" @@ -1694,6 +1751,13 @@ dependencies: core-js "^3.8.2" +"@storybook/core-events@6.4.21": + version "6.4.21" + resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.4.21.tgz#28fff8b10c0d564259edf4439ff8677615ce59c0" + integrity sha512-K6b9M1zYvW/Kfb1cnH6JDfmFvTYDMx/ot9zdl9O5SPH9glUwzOXSk8qKu6GmZTiW2YnC2nKbjaN20mfMsCBPGw== + dependencies: + core-js "^3.8.2" + "@storybook/core-server@6.4.20": version "6.4.20" resolved "https://registry.npmjs.org/@storybook/core-server/-/core-server-6.4.20.tgz#6bdf6dd5d83713034df950a98f7638e23c64171c" @@ -1915,6 +1979,23 @@ react-router-dom "^6.0.0" ts-dedent "^2.0.0" +"@storybook/router@6.4.21": + version "6.4.21" + resolved "https://registry.npmjs.org/@storybook/router/-/router-6.4.21.tgz#a18172601907918c1442a8a125c9c625d798d09b" + integrity sha512-otn3xYc017SNebeA95xLQ7P6elfyu9541QteXbLR5gFvrT+MB/8zMRZrVuD7n1xwpBgazlonzAdODC736Be9jQ== + dependencies: + "@storybook/client-logger" "6.4.21" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + history "5.0.0" + lodash "^4.17.21" + memoizerific "^1.11.3" + qs "^6.10.0" + react-router "^6.0.0" + react-router-dom "^6.0.0" + ts-dedent "^2.0.0" + "@storybook/semver@^7.3.2": version "7.3.2" resolved "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz#f3b9c44a1c9a0b933c04e66d0048fcf2fa10dac0" @@ -1978,6 +2059,24 @@ resolve-from "^5.0.0" ts-dedent "^2.0.0" +"@storybook/theming@6.4.21": + version "6.4.21" + resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.4.21.tgz#ea1a33be70c654cb31e5b38fae93f72171e88ef8" + integrity sha512-7pLNwmqbyqCeHXzjsacI69IdJcAZr6zoZA84iGqx+Na32OI8wtIpFczbwuYpVPN2jzgRYp23CgIv1Gz27yk/zw== + dependencies: + "@emotion/core" "^10.1.1" + "@emotion/is-prop-valid" "^0.8.6" + "@emotion/styled" "^10.0.27" + "@storybook/client-logger" "6.4.21" + core-js "^3.8.2" + deep-object-diff "^1.1.0" + emotion-theming "^10.0.27" + global "^4.4.0" + memoizerific "^1.11.3" + polished "^4.0.5" + resolve-from "^5.0.0" + ts-dedent "^2.0.0" + "@storybook/ui@6.4.20": version "6.4.20" resolved "https://registry.npmjs.org/@storybook/ui/-/ui-6.4.20.tgz#30e8fba0877b66000841046133d3dc098a807d13" diff --git a/yarn.lock b/yarn.lock index 0dab1d54f6..ff8a4c26e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1617,16 +1617,16 @@ resolved "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.0.tgz#fe364f025ba74f6de6c837a84ef44bdb1d61e68f" integrity sha512-mgmE7XBYY/21erpzhexk4Cj1cyTQ9LzvnTxtzM17BJ7ERMNE6W72mQRo0I1Ud8eFJ+RVVIcBNhLFZ3GX4XFz5w== -"@changesets/apply-release-plan@^5.0.5": - version "5.0.5" - resolved "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-5.0.5.tgz#d67b1e022c876d18d887f3c475a3abcad9944b68" - integrity sha512-CxL9dkhzjHiVmXCyHgsLCQj7i/coFTMv/Yy0v6BC5cIWZkQml+lf7zvQqAcFXwY7b54HxRWZPku02XFB53Q0Uw== +"@changesets/apply-release-plan@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-6.0.0.tgz#6c663ff99d919bba3902343d76c35cbbbb046520" + integrity sha512-gp6nIdVdfYdwKww2+f8whckKmvfE4JEm4jJgBhTmooi0uzHWhnxvk6JIzQi89qEAMINN0SeVNnXiAtbFY0Mj3w== dependencies: "@babel/runtime" "^7.10.4" - "@changesets/config" "^1.7.0" + "@changesets/config" "^2.0.0" "@changesets/get-version-range-type" "^0.3.2" - "@changesets/git" "^1.3.1" - "@changesets/types" "^4.1.0" + "@changesets/git" "^1.3.2" + "@changesets/types" "^5.0.0" "@manypkg/get-packages" "^1.1.3" detect-indent "^6.0.0" fs-extra "^7.0.1" @@ -1636,44 +1636,44 @@ resolve-from "^5.0.0" semver "^5.4.1" -"@changesets/assemble-release-plan@^5.1.0": - version "5.1.0" - resolved "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-5.1.0.tgz#0fcb18253998e3bc037a554874de43bcc58c4840" - integrity sha512-iYlqffCMhcwZ+6Cv8cimf10OBGYXQKufBI7J6htpRgCV2nT99RKXEjbYOtrXWKQqzu0XxOsk15apSEwjZN0JRw== +"@changesets/assemble-release-plan@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-5.1.2.tgz#63ed3a00f62b5af08a82e83801a252ac9726c625" + integrity sha512-nOFyDw4APSkY/vh5WNwGEtThPgEjVShp03PKVdId6wZTJALVcAALCSLmDRfeqjE2z9EsGJb7hZdDlziKlnqZgw== dependencies: "@babel/runtime" "^7.10.4" "@changesets/errors" "^0.1.4" - "@changesets/get-dependents-graph" "^1.3.1" - "@changesets/types" "^4.1.0" + "@changesets/get-dependents-graph" "^1.3.2" + "@changesets/types" "^5.0.0" "@manypkg/get-packages" "^1.1.3" semver "^5.4.1" -"@changesets/changelog-git@^0.1.10": - version "0.1.10" - resolved "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.1.10.tgz#df616e92671082a7976381280b4af98ff3a7067d" - integrity sha512-4t7zqPOv3aDZp4Y+AyDhiOG2ypaUXDpOz+MT1wOk3uSZNv78AaDByam0hdk5kfYuH1RlMecWU4/U5lO1ZL5eaA== +"@changesets/changelog-git@^0.1.11": + version "0.1.11" + resolved "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.1.11.tgz#80eb45d3562aba2164f25ccc31ac97b9dcd1ded3" + integrity sha512-sWJvAm+raRPeES9usNpZRkooeEB93lOpUN0Lmjz5vhVAb7XGIZrHEJ93155bpE1S0c4oJ5Di9ZWgzIwqhWP/Wg== dependencies: - "@changesets/types" "^4.1.0" + "@changesets/types" "^5.0.0" "@changesets/cli@^2.14.0": - version "2.21.0" - resolved "https://registry.npmjs.org/@changesets/cli/-/cli-2.21.0.tgz#b689f91ed908150efc06e0985e6b4cfbd9aea3a3" - integrity sha512-cJXRg28MmF9VbQrlwSjpY4AJA2xZUbXFCpQ3kFmX0IeppO7wknZ2QfocAhIqwM828t8d3R4Zpi5xnvJ/crIcQw== + version "2.22.0" + resolved "https://registry.npmjs.org/@changesets/cli/-/cli-2.22.0.tgz#3bdbfb4a7a81ef37f63114e77da84e23f906b763" + integrity sha512-4bA3YoBkd5cm5WUxmrR2N9WYE7EeQcM+R3bVYMUj2NvffkQVpU3ckAI+z8UICoojq+HRl2OEwtz+S5UBmYY4zw== dependencies: "@babel/runtime" "^7.10.4" - "@changesets/apply-release-plan" "^5.0.5" - "@changesets/assemble-release-plan" "^5.1.0" - "@changesets/changelog-git" "^0.1.10" - "@changesets/config" "^1.7.0" + "@changesets/apply-release-plan" "^6.0.0" + "@changesets/assemble-release-plan" "^5.1.2" + "@changesets/changelog-git" "^0.1.11" + "@changesets/config" "^2.0.0" "@changesets/errors" "^0.1.4" - "@changesets/get-dependents-graph" "^1.3.1" - "@changesets/get-release-plan" "^3.0.6" - "@changesets/git" "^1.3.1" + "@changesets/get-dependents-graph" "^1.3.2" + "@changesets/get-release-plan" "^3.0.8" + "@changesets/git" "^1.3.2" "@changesets/logger" "^0.0.5" - "@changesets/pre" "^1.0.10" - "@changesets/read" "^0.5.4" - "@changesets/types" "^4.1.0" - "@changesets/write" "^0.1.7" + "@changesets/pre" "^1.0.11" + "@changesets/read" "^0.5.5" + "@changesets/types" "^5.0.0" + "@changesets/write" "^0.1.8" "@manypkg/get-packages" "^1.1.3" "@types/is-ci" "^3.0.0" "@types/semver" "^6.0.0" @@ -1687,20 +1687,21 @@ outdent "^0.5.0" p-limit "^2.2.0" preferred-pm "^3.0.0" + resolve-from "^5.0.0" semver "^5.4.1" spawndamnit "^2.0.0" term-size "^2.1.0" tty-table "^2.8.10" -"@changesets/config@^1.7.0": - version "1.7.0" - resolved "https://registry.npmjs.org/@changesets/config/-/config-1.7.0.tgz#18353f88ea8153d7f1fb7c321a3fe8667035eddb" - integrity sha512-Ctk6ZO5Ay6oZ95bbKXyA2a1QG0jQUePaGCY6BKkZtUG4PgysesfmiQOPgOY5OsRMt8exJeo6l+DJ75YiKmh0rQ== +"@changesets/config@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@changesets/config/-/config-2.0.0.tgz#1770fdfeba2155cf07154c37e96b55cbd27969f0" + integrity sha512-r5bIFY6CN3K6SQ+HZbjyE3HXrBIopONR47mmX7zUbORlybQXtympq9rVAOzc0Oflbap8QeIexc+hikfZoREXDg== dependencies: "@changesets/errors" "^0.1.4" - "@changesets/get-dependents-graph" "^1.3.1" + "@changesets/get-dependents-graph" "^1.3.2" "@changesets/logger" "^0.0.5" - "@changesets/types" "^4.1.0" + "@changesets/types" "^5.0.0" "@manypkg/get-packages" "^1.1.3" fs-extra "^7.0.1" micromatch "^4.0.2" @@ -1712,28 +1713,28 @@ dependencies: extendable-error "^0.1.5" -"@changesets/get-dependents-graph@^1.3.1": - version "1.3.1" - resolved "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-1.3.1.tgz#f1ebadbd4e17bb2b987c4542a588e0ee9f2e829a" - integrity sha512-HwUs8U0XK/ZqCQon1/80jJEyswS8JVmTiHTZslrTpuavyhhhxrSpO1eVCdKgaVHBRalOw3gRzdS3uzkmqYsQSQ== +"@changesets/get-dependents-graph@^1.3.2": + version "1.3.2" + resolved "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-1.3.2.tgz#f3ec7ce75f4afb6e3e4b6a87fde065f552c85998" + integrity sha512-tsqA6qZRB86SQuApSoDvI8yEWdyIlo/WLI4NUEdhhxLMJ0dapdeT6rUZRgSZzK1X2nv5YwR0MxQBbDAiDibKrg== dependencies: - "@changesets/types" "^4.1.0" + "@changesets/types" "^5.0.0" "@manypkg/get-packages" "^1.1.3" chalk "^2.1.0" fs-extra "^7.0.1" semver "^5.4.1" -"@changesets/get-release-plan@^3.0.6": - version "3.0.6" - resolved "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-3.0.6.tgz#15aac108b9d0f139841562c9372d8cfd738503dc" - integrity sha512-HpPyr8y6xkihy3rONLZ6OtfgYq88NotidPAuS3nwMeZjLHiIVLyejR2+/5q717f6HKcrATxAjTwMAcjl7X/uzA== +"@changesets/get-release-plan@^3.0.8": + version "3.0.8" + resolved "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-3.0.8.tgz#2ac7c4f7903aedede3d27af66311ad1db7937e5d" + integrity sha512-TJYiWNuP0Lzu2dL/KHuk75w7TkiE5HqoYirrXF7SJIxkhlgH9toQf2C7IapiFTObtuF1qDN8HJAX1CuIOwXldg== dependencies: "@babel/runtime" "^7.10.4" - "@changesets/assemble-release-plan" "^5.1.0" - "@changesets/config" "^1.7.0" - "@changesets/pre" "^1.0.10" - "@changesets/read" "^0.5.4" - "@changesets/types" "^4.1.0" + "@changesets/assemble-release-plan" "^5.1.2" + "@changesets/config" "^2.0.0" + "@changesets/pre" "^1.0.11" + "@changesets/read" "^0.5.5" + "@changesets/types" "^5.0.0" "@manypkg/get-packages" "^1.1.3" "@changesets/get-version-range-type@^0.3.2": @@ -1741,14 +1742,14 @@ resolved "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.3.2.tgz#8131a99035edd11aa7a44c341cbb05e668618c67" integrity sha512-SVqwYs5pULYjYT4op21F2pVbcrca4qA/bAA3FmFXKMN7Y+HcO8sbZUTx3TAy2VXulP2FACd1aC7f2nTuqSPbqg== -"@changesets/git@^1.3.1": - version "1.3.1" - resolved "https://registry.npmjs.org/@changesets/git/-/git-1.3.1.tgz#e86b4d2b28acdf9bc8949031027a9ac12420b99e" - integrity sha512-yg60QUi38VA0XGXdBy9SRYJhs8xJHE97Z1CaB/hFyByBlh5k1i+avFNBvvw66MsoT/aiml6y9scIG6sC8R5mfg== +"@changesets/git@^1.3.2": + version "1.3.2" + resolved "https://registry.npmjs.org/@changesets/git/-/git-1.3.2.tgz#336051d9a6d965806b1bc473559a9a2cc70773a6" + integrity sha512-p5UL+urAg0Nnpt70DLiBe2iSsMcDubTo9fTOD/61krmcJ466MGh71OHwdAwu1xG5+NKzeysdy1joRTg8CXcEXA== dependencies: "@babel/runtime" "^7.10.4" "@changesets/errors" "^0.1.4" - "@changesets/types" "^4.1.0" + "@changesets/types" "^5.0.0" "@manypkg/get-packages" "^1.1.3" is-subdir "^1.1.1" spawndamnit "^2.0.0" @@ -1760,51 +1761,56 @@ dependencies: chalk "^2.1.0" -"@changesets/parse@^0.3.12": - version "0.3.12" - resolved "https://registry.npmjs.org/@changesets/parse/-/parse-0.3.12.tgz#60569bb39ad4ffe47fc01d431613ce5c42e6590f" - integrity sha512-FOBz2L1dT9PcvyQU1Qp2sQ0B4Jw7EgRDAKFVzAQwhzXqCq03TcE7vgKU6VSksCJAioMYDowdVVHNnv/Uak6yZQ== +"@changesets/parse@^0.3.13": + version "0.3.13" + resolved "https://registry.npmjs.org/@changesets/parse/-/parse-0.3.13.tgz#82788c1fc18da4750b07357a7a06142d0d975aa1" + integrity sha512-wh9Ifa0dungY6d2nMz6XxF6FZ/1I7j+mEgPAqrIyKS64nifTh1Ua82qKKMMK05CL7i4wiB2NYc3SfnnCX3RVeA== dependencies: - "@changesets/types" "^4.1.0" + "@changesets/types" "^5.0.0" js-yaml "^3.13.1" -"@changesets/pre@^1.0.10": - version "1.0.10" - resolved "https://registry.npmjs.org/@changesets/pre/-/pre-1.0.10.tgz#e677031f271cdab8443b21e0b3cda036a3919c30" - integrity sha512-cZC1C1wTSC17/TcTWivAQ4LAXz5jEYDuy3UeZiBz1wnTTzMHyTHLLwJi60juhl4hawXunDLw0mwZkcpS8Ivitg== +"@changesets/pre@^1.0.11": + version "1.0.11" + resolved "https://registry.npmjs.org/@changesets/pre/-/pre-1.0.11.tgz#46a56790fdceabd03407559bbf91340c8e83fb6a" + integrity sha512-CXZnt4SV9waaC9cPLm7818+SxvLKIDHUxaiTXnJYDp1c56xIexx1BNfC1yMuOdzO2a3rAIcZua5Odxr3dwSKfg== dependencies: "@babel/runtime" "^7.10.4" "@changesets/errors" "^0.1.4" - "@changesets/types" "^4.1.0" + "@changesets/types" "^5.0.0" "@manypkg/get-packages" "^1.1.3" fs-extra "^7.0.1" -"@changesets/read@^0.5.4": - version "0.5.4" - resolved "https://registry.npmjs.org/@changesets/read/-/read-0.5.4.tgz#c6dc6ab00c4f70f2ce6766433d92eebde1b00e7a" - integrity sha512-12dTx+p5ztFs9QgJDGHRHR6HzTIbHct9S4lK2I/i6Qkz1cNfAPVIbdoMCdbPIWeLank9muMUjiiFmCWJD7tQIg== +"@changesets/read@^0.5.5": + version "0.5.5" + resolved "https://registry.npmjs.org/@changesets/read/-/read-0.5.5.tgz#9ed90ef3e9f1ba3436ba5580201854a3f4163058" + integrity sha512-bzonrPWc29Tsjvgh+8CqJ0apQOwWim0zheeD4ZK44ApSa/GudnZJTODtA3yNOOuQzeZmL0NUebVoHIurtIkA7w== dependencies: "@babel/runtime" "^7.10.4" - "@changesets/git" "^1.3.1" + "@changesets/git" "^1.3.2" "@changesets/logger" "^0.0.5" - "@changesets/parse" "^0.3.12" - "@changesets/types" "^4.1.0" + "@changesets/parse" "^0.3.13" + "@changesets/types" "^5.0.0" chalk "^2.1.0" fs-extra "^7.0.1" p-filter "^2.1.0" -"@changesets/types@^4.0.1", "@changesets/types@^4.1.0": +"@changesets/types@^4.0.1": version "4.1.0" resolved "https://registry.npmjs.org/@changesets/types/-/types-4.1.0.tgz#fb8f7ca2324fd54954824e864f9a61a82cb78fe0" integrity sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw== -"@changesets/write@^0.1.7": - version "0.1.7" - resolved "https://registry.npmjs.org/@changesets/write/-/write-0.1.7.tgz#671def0d871cf5970c5b2f766f0ac4b19ecf1ddb" - integrity sha512-6r+tc6u2l5BBIwEAh7ivRYWFir+XKiw0q/6Hx6NJA4dSN5fNu9uyWRQ+IMHCllD9dBcsh+e79sOepc+xT8l28g== +"@changesets/types@^5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@changesets/types/-/types-5.0.0.tgz#d5eb52d074bc0358ce47d54bca54370b907812a0" + integrity sha512-IT1kBLSbAgTS4WtpU6P5ko054hq12vk4tgeIFRVE7Vnm4a/wgbNvBalgiKP0MjEXbCkZbItiGQHkCGxYWR55sA== + +"@changesets/write@^0.1.8": + version "0.1.8" + resolved "https://registry.npmjs.org/@changesets/write/-/write-0.1.8.tgz#feed408f644c496bc52afc4dd1353670b4152ecb" + integrity sha512-oIHeFVMuP6jf0TPnKPpaFpvvAf3JBc+s2pmVChbeEgQTBTALoF51Z9kqxQfG4XONZPHZnqkmy564c7qohhhhTQ== dependencies: "@babel/runtime" "^7.10.4" - "@changesets/types" "^4.1.0" + "@changesets/types" "^5.0.0" fs-extra "^7.0.1" human-id "^1.0.2" prettier "^1.19.1" @@ -2218,21 +2224,6 @@ qs "^6.10.1" xcase "^2.0.1" -"@google-cloud/common@^3.8.1": - version "3.10.0" - resolved "https://registry.npmjs.org/@google-cloud/common/-/common-3.10.0.tgz#454d1155bb512109cd83c6183aabbd39f9aabda7" - integrity sha512-XMbJYMh/ZSaZnbnrrOFfR/oQrb0SxG4qh6hDisWCoEbFcBHV0qHQo4uXfeMCzolx2Mfkh6VDaOGg+hyJsmxrlw== - dependencies: - "@google-cloud/projectify" "^2.0.0" - "@google-cloud/promisify" "^2.0.0" - arrify "^2.0.1" - duplexify "^4.1.1" - ent "^2.2.0" - extend "^3.0.2" - google-auth-library "^7.14.0" - retry-request "^4.2.2" - teeny-request "^7.0.0" - "@google-cloud/container@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@google-cloud/container/-/container-3.0.0.tgz#b28d086152ac19f22f2574b7e0a39774379fb885" @@ -2269,12 +2260,12 @@ integrity sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw== "@google-cloud/storage@^5.6.0", "@google-cloud/storage@^5.8.0": - version "5.18.3" - resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.18.3.tgz#becacb909b2abf4bb54500a0efd65ceb51ef8eab" - integrity sha512-573qJ0ECoy3nkY5YaMWcVf4/46n/zdvfNgAyjaLQywl/eL38uxDhs7YVJd3pcgslaMUwKKsd/eD3St+Pq2iPew== + version "5.19.1" + resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.19.1.tgz#7252c4eb65e6014bf525a76f3af0774caa3a946d" + integrity sha512-bRTf/AD00+lPTamJdpihXC3AFtAnJFWNh/zQAor972VpuATF7u4V1anwWp0V6rKuKE3BwNM+xWxuuW/nAwEgTA== dependencies: - "@google-cloud/common" "^3.8.1" "@google-cloud/paginator" "^3.0.7" + "@google-cloud/projectify" "^2.0.0" "@google-cloud/promisify" "^2.0.0" abort-controller "^3.0.0" arrify "^2.0.0" @@ -2283,23 +2274,26 @@ configstore "^5.0.0" date-and-time "^2.0.0" duplexify "^4.0.0" + ent "^2.2.0" extend "^3.0.2" gaxios "^4.0.0" get-stream "^6.0.0" - google-auth-library "^7.0.0" + google-auth-library "^7.14.1" hash-stream-validation "^0.2.2" mime "^3.0.0" mime-types "^2.0.8" p-limit "^3.0.1" pumpify "^2.0.0" + retry-request "^4.2.2" snakeize "^0.1.0" stream-events "^1.0.4" + teeny-request "^7.1.3" xdg-basedir "^4.0.0" -"@graphiql/toolkit@^0.4.2": - version "0.4.2" - resolved "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.4.2.tgz#34de819add64672f3f7d4830dffb2094fb8d5366" - integrity sha512-14uG67QrONbRrhXwvBJFsMfcQfexmGhj7dgkputesx9xuPUkcCDNmVULnVA8sGYt8P/rSvjkfQYx3rtfW+GhAQ== +"@graphiql/toolkit@^0.4.3": + version "0.4.3" + resolved "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.4.3.tgz#2653d045693d902f7faaf87b9047e8786397ec48" + integrity sha512-L0l6BezvTXmWZhtdmZxirhYwKzcZToAciQY0A13KRAWSpJ9bb/ZdkBpcz3fOXrsjuJHf0wBr6Vk9kztJ6lV7uA== dependencies: "@n1ru4l/push-pull-async-iterable-iterator" "^3.1.0" meros "^1.1.4" @@ -3482,6 +3476,11 @@ underscore "^1.9.1" ws "^7.3.1" +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.3" + resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz#0300943770e04231041a51bd39f0439b5c7ab4f0" + integrity sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg== + "@lerna/add@4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" @@ -4336,48 +4335,39 @@ dependencies: "@types/gapi.client" "*" -"@microsoft/api-documenter@^7.17.0": - version "7.17.0" - resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.17.0.tgz#7d2b7775b37775fc7491b94cb5fa13ee8271fcc3" - integrity sha512-rIZcfDkBsWsflvN1n0tVgIUdivV7slBIsN0WyK8gUG1MjK7dv+rbAB5QPLhg3dPvbXLgPLanJCFwoO2yFdxs+A== +"@microsoft/api-documenter@^7.17.5": + version "7.17.5" + resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.17.5.tgz#cdaec54ad04d66b6f4c6f797f4120ff8c844a82d" + integrity sha512-zyJS3qORQP33S8EU0YPZb/JLIbMoslzj1bg8VmgfYCnxhX+3/FmlrTqDCwA9h2TR4bu1r7aQ26AfjUbBUz9+EA== dependencies: - "@microsoft/api-extractor-model" "7.16.0" + "@microsoft/api-extractor-model" "7.16.1" "@microsoft/tsdoc" "0.13.2" - "@rushstack/node-core-library" "3.45.1" - "@rushstack/ts-command-line" "4.10.7" + "@rushstack/node-core-library" "3.45.2" + "@rushstack/ts-command-line" "4.10.8" colors "~1.2.1" js-yaml "~3.13.1" resolve "~1.17.0" -"@microsoft/api-extractor-model@7.15.3": - version "7.15.3" - resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.15.3.tgz#cf76deeeb2733d974da678f530c2dbaceb18a065" - integrity sha512-NkSjolmSI7NGvbdz0Y7kjQfdpD+j9E5CwXTxEyjDqxd10MI7GXV8DnAsQ57GFJcgHKgTjf2aUnYfMJ9w3aMicw== +"@microsoft/api-extractor-model@7.16.1", "@microsoft/api-extractor-model@^7.16.1": + version "7.16.1" + resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.16.1.tgz#f6e53614e90c8a6be47bad5b91d49cc6d8c5c2bf" + integrity sha512-+1mlvy/ji+mUuH7WdVZ6fTo/aCKfS6m37aAFVOFWLfkMvmR+I9SjPfiv9qOg83If7GOrk2HPiHHibv6kA80VTg== dependencies: "@microsoft/tsdoc" "0.13.2" "@microsoft/tsdoc-config" "~0.15.2" - "@rushstack/node-core-library" "3.45.0" + "@rushstack/node-core-library" "3.45.2" -"@microsoft/api-extractor-model@7.16.0", "@microsoft/api-extractor-model@^7.16.0": - version "7.16.0" - resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.16.0.tgz#3db7360897115f26a857f1f684fb5af82b0ef9f6" - integrity sha512-0FOrbNIny8mzBrzQnSIkEjAXk0JMSnPmWYxt3ZDTPVg9S8xIPzB6lfgTg9+Mimu0RKCpGKBpd+v2WcR5vGzyUQ== +"@microsoft/api-extractor@^7.21.2": + version "7.21.2" + resolved "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.21.2.tgz#0f524e7e1cf7b000924772fb8f6b5f60cf42fba8" + integrity sha512-m0+YPaXVou01O/V9swugZG7Gn4mw6HSWY+uisf0j2JPRZcoEDyoYe4hg0ERKXOEf0hByOnMLT28nQ82v8ig9Yw== dependencies: + "@microsoft/api-extractor-model" "7.16.1" "@microsoft/tsdoc" "0.13.2" "@microsoft/tsdoc-config" "~0.15.2" - "@rushstack/node-core-library" "3.45.1" - -"@microsoft/api-extractor@^7.19.4": - version "7.19.4" - resolved "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.19.4.tgz#95d43d410a1dfb28a02062c4693bcb9c52afe9eb" - integrity sha512-iehC6YA3DGJvxTUaK7HUtQmP6hAQU07+Q/OR8TG4dVR6KpqCi9UPEVk8AgCvQkiK+6FbVEFQTx0qLuYk4EeuHg== - dependencies: - "@microsoft/api-extractor-model" "7.15.3" - "@microsoft/tsdoc" "0.13.2" - "@microsoft/tsdoc-config" "~0.15.2" - "@rushstack/node-core-library" "3.45.0" - "@rushstack/rig-package" "0.3.7" - "@rushstack/ts-command-line" "4.10.6" + "@rushstack/node-core-library" "3.45.2" + "@rushstack/rig-package" "0.3.9" + "@rushstack/ts-command-line" "4.10.8" colors "~1.2.1" lodash "~4.17.15" resolve "~1.17.0" @@ -4391,9 +4381,9 @@ integrity sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA== "@microsoft/microsoft-graph-types@^2.6.0": - version "2.16.0" - resolved "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-2.16.0.tgz#5329890d230c4fdd9f57f39e26dfade0882c94f3" - integrity sha512-Qvxv9mpXb/F4xlESEkSLjREHj3dAixTkH3LVO6Ct6sllc5RWrQxPxaSGqW9IpcLU6jI49f2XNSGLotVef3Irdg== + version "2.18.0" + resolved "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-2.18.0.tgz#b2ebdd55c149dfc11cca41cbf1f2082a56242450" + integrity sha512-cWiK0oaz+RrcL6EKfBeoai28L8jWJ1n+nS5cGUgRR4+POk5W5/8DV3Vs3gUNaFwmAqfYrhskyb/SNyytgAC78Q== "@microsoft/tsdoc-config@~0.15.2": version "0.15.2" @@ -4405,11 +4395,16 @@ jju "~1.4.0" resolve "~1.19.0" -"@microsoft/tsdoc@0.13.2", "@microsoft/tsdoc@^0.13.2": +"@microsoft/tsdoc@0.13.2": version "0.13.2" resolved "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz#3b0efb6d3903bd49edb073696f60e90df08efb26" integrity sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg== +"@microsoft/tsdoc@^0.14.1": + version "0.14.1" + resolved "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz#155ef21065427901994e765da8a0ba0eaae8b8bd" + integrity sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw== + "@mswjs/cookies@^0.1.6", "@mswjs/cookies@^0.1.7": version "0.1.7" resolved "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.1.7.tgz#d334081b2c51057a61c1dd7b76ca3cac02251651" @@ -5229,10 +5224,10 @@ estree-walker "^2.0.1" picomatch "^2.2.2" -"@rushstack/node-core-library@3.45.0": - version "3.45.0" - resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.0.tgz#8c86b39271b6d84260b1e70db87e1e265b54f620" - integrity sha512-YMuIJl19vQT1+g/OU9mLY6T5ZBT9uDlmeXExDQACpGuxTJW+LHNbk/lRX+eCApQI2eLBlaL4U68r3kZlqwbdmw== +"@rushstack/node-core-library@3.45.2": + version "3.45.2" + resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.2.tgz#68fc89c5bea4007359fa4ff203bf3eca27037f40" + integrity sha512-MJKdB6mxOoIkks3htGVCo7aiTzllm2I6Xua+KbTSb0cp7rBp8gTCOF/4d8R4HFMwpRdEdwzKgqMM6k9rAK73iw== dependencies: "@types/node" "12.20.24" colors "~1.2.1" @@ -5244,43 +5239,18 @@ timsort "~0.3.0" z-schema "~5.0.2" -"@rushstack/node-core-library@3.45.1": - version "3.45.1" - resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.1.tgz#787361b61a48d616eb4b059641721a3dc138f001" - integrity sha512-BwdssTNe007DNjDBxJgInHg8ePytIPyT0La7ZZSQZF9+rSkT42AygXPGvbGsyFfEntjr4X37zZSJI7yGzL16cQ== - dependencies: - "@types/node" "12.20.24" - colors "~1.2.1" - fs-extra "~7.0.1" - import-lazy "~4.0.0" - jju "~1.4.0" - resolve "~1.17.0" - semver "~7.3.0" - timsort "~0.3.0" - z-schema "~5.0.2" - -"@rushstack/rig-package@0.3.7": - version "0.3.7" - resolved "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.7.tgz#3fa564b1d129d28689dd4309502792b15e84bf81" - integrity sha512-pzMsTSeTC8IiZ6EJLr53gGMvhT4oLWH+hxD7907cHyWuIUlEXFtu/2pK25vUQT13nKp5DJCWxXyYoGRk/h6rtA== +"@rushstack/rig-package@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.9.tgz#5e10ada5a8348f886b6ebe3eed436492d6ccf70c" + integrity sha512-z3Oxpfb4n9mGXwseX+ifpkmUf9B8Fy8oieVwg8eFgpCbzllkgOwEiwLKEnRWVQ8owFcd46NCKz+7ICH35CRsAw== dependencies: resolve "~1.17.0" strip-json-comments "~3.1.1" -"@rushstack/ts-command-line@4.10.6": - version "4.10.6" - resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.6.tgz#5669e481e4339ceb4e1428183eb0937d3bc3841b" - integrity sha512-Y3GkUag39sTIlukDg9mUp8MCHrrlJ27POrBNRQGc/uF+VVgX8M7zMzHch5zP6O1QVquWgD7Engdpn2piPYaS/g== - dependencies: - "@types/argparse" "1.0.38" - argparse "~1.0.9" - colors "~1.2.1" - string-argv "~0.3.1" - -"@rushstack/ts-command-line@4.10.7": - version "4.10.7" - resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.7.tgz#21e3757a756cbd4f7eeab8f89ec028a64d980efc" - integrity sha512-CjS+DfNXUSO5Ab2wD1GBGtUTnB02OglRWGqfaTcac9Jn45V5MeUOsq/wA8wEeS5Y/3TZ2P1k+IWdVDiuOFP9Og== +"@rushstack/ts-command-line@4.10.8": + version "4.10.8" + resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.8.tgz#f4bec690e7f4838e6f91028d8a557e67c3a75f68" + integrity sha512-G7CQYY/m3aZU5fVxbebv35yDeua7sSumrDAB2pJp0d60ZEsxGkUQW8771CeMcGWwSKqT9PxPzKpmIakiWv54sA== dependencies: "@types/argparse" "1.0.38" argparse "~1.0.9" @@ -5546,9 +5516,9 @@ pretty-format "^27.0.2" "@testing-library/jest-dom@^5.10.1": - version "5.16.3" - resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.3.tgz#b76851a909586113c20486f1679ffb4d8ec27bfa" - integrity sha512-u5DfKj4wfSt6akfndfu1eG06jsdyA/IUrlX2n3pyq5UXgXMhXY+NJb8eNK/7pqPWAhCKsCGWDdDO0zKMKAYkEA== + version "5.16.4" + resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.4.tgz#938302d7b8b483963a3ae821f1c0808f872245cd" + integrity sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA== dependencies: "@babel/runtime" "^7.9.2" "@types/testing-library__jest-dom" "^5.9.1" @@ -6229,10 +6199,10 @@ dependencies: "@types/node" "*" -"@types/jest-when@^2.7.2": - version "2.7.2" - resolved "https://registry.npmjs.org/@types/jest-when/-/jest-when-2.7.2.tgz#619fbc5f623bcd0b29efde0e4993c7f0d50d026d" - integrity sha512-vOtj0cev6vO1VX7Jbfg/qvy+sfLI64STsHbKVkggK+1kd11rcMGzFpZKBxUvQfsm4JRULCBISu+qrfs7fYZFGg== +"@types/jest-when@^3.5.0": + version "3.5.0" + resolved "https://registry.npmjs.org/@types/jest-when/-/jest-when-3.5.0.tgz#6a573cd521da131e6801f0991b4f1d4dee2ebab5" + integrity sha512-rNUuZ3Mn/HDzpImPXDeOtW18zqyerPoOS2aKU0zUFbirWgJ7sN7LnRv73RmbBQ/uzw28sxf/nUofxbhJ5DWB0w== dependencies: "@types/jest" "*" @@ -6330,9 +6300,9 @@ "@types/node" "*" "@types/lodash@^4.14.151", "@types/lodash@^4.14.173", "@types/lodash@^4.14.175": - version "4.14.178" - resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8" - integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw== + version "4.14.181" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.181.tgz#d1d3740c379fda17ab175165ba04e2d03389385d" + integrity sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag== "@types/long@^4.0.0", "@types/long@^4.0.1": version "4.0.1" @@ -6835,9 +6805,9 @@ "@types/node" "*" "@types/supertest@^2.0.8": - version "2.0.11" - resolved "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.11.tgz#2e70f69f220bc77b4f660d72c2e1a4231f44a77d" - integrity sha512-uci4Esokrw9qGb9bvhhSVEjd6rkny/dk5PK/Qz4yxKiyppEI+dOPlNrZBahE3i+PoKFYyDxChVXZ/ysS/nrm1Q== + version "2.0.12" + resolved "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz#ddb4a0568597c9aadff8dbec5b2e8fddbe8692fc" + integrity sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ== dependencies: "@types/superagent" "*" @@ -6981,10 +6951,10 @@ dependencies: "@types/node" "*" -"@types/ws@^8.0.0", "@types/ws@^8.2.2": - version "8.2.2" - resolved "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21" - integrity sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg== +"@types/ws@^8.0.0", "@types/ws@^8.5.1": + version "8.5.3" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" + integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== dependencies: "@types/node" "*" @@ -7545,9 +7515,9 @@ ansi-regex@^2.0.0: 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= + version "3.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== ansi-regex@^4.1.0: version "4.1.0" @@ -7852,7 +7822,7 @@ array-flatten@1.1.1: resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= -array-flatten@^2.1.0: +array-flatten@^2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== @@ -8509,17 +8479,15 @@ body-parser@1.19.2, body-parser@^1.19.0: raw-body "2.4.3" type-is "~1.6.18" -bonjour@^3.5.0: - version "3.5.0" - resolved "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" - integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= +bonjour-service@^1.0.11: + version "1.0.11" + resolved "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.11.tgz#5418e5c1ac91c89a406f853a942e7892829c0d89" + integrity sha512-drMprzr2rDTCtgEE3VgdA9uUFaUHF+jXduwYSThHJnKMYM+FhI9Z3ph+TX3xy0LtgYHae6CHYPJ/2UnK8nQHcA== dependencies: - array-flatten "^2.1.0" - deep-equal "^1.0.1" + array-flatten "^2.1.2" dns-equal "^1.0.0" - dns-txt "^2.0.2" - multicast-dns "^6.0.1" - multicast-dns-service-types "^1.1.0" + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.4" boolbase@^1.0.0: version "1.0.0" @@ -8726,11 +8694,6 @@ buffer-indexof-polyfill@~1.0.0: resolved "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== -buffer-indexof@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" - integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== - buffer-writer@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" @@ -10863,18 +10826,6 @@ dedent@^0.7.0: resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -deep-equal@^1.0.1: - version "1.1.1" - resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" - integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== - dependencies: - is-arguments "^1.0.4" - is-date-object "^1.0.1" - is-regex "^1.0.4" - object-is "^1.0.1" - object-keys "^1.1.1" - regexp.prototype.flags "^1.2.0" - deep-extend@0.6.0, deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -11149,20 +11100,12 @@ dns-equal@^1.0.0: resolved "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= -dns-packet@^1.3.1: - version "1.3.4" - resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" - integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== +dns-packet@^5.2.2: + version "5.3.1" + resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-5.3.1.tgz#eb94413789daec0f0ebe2fcc230bdc9d7c91b43d" + integrity sha512-spBwIj0TK0Ey3666GwIdWVfUpLyubpU53BTCu8iPn4r4oXd9O14Hjg3EHw3ts2oed77/SeckunUYCyRlSngqHw== dependencies: - ip "^1.1.0" - safe-buffer "^5.0.1" - -dns-txt@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" - integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= - dependencies: - buffer-indexof "^1.0.0" + "@leichtgewicht/ip-codec" "^2.0.1" docker-compose@^0.23.17: version "0.23.17" @@ -12178,6 +12121,7 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: "@backstage/app-defaults" "^1.0.1-next.1" "@backstage/catalog-model" "^1.0.1-next.0" "@backstage/cli" "^0.17.0-next.1" + "@backstage/config" "^1.0.0" "@backstage/core-app-api" "^1.0.1-next.0" "@backstage/core-components" "^0.9.3-next.0" "@backstage/core-plugin-api" "^1.0.0" @@ -12216,8 +12160,10 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: "@backstage/plugin-scaffolder" "^1.0.1-next.1" "@backstage/plugin-search" "^0.7.5-next.0" "@backstage/plugin-search-common" "^0.3.3-next.1" + "@backstage/plugin-search-react" "^0.0.0" "@backstage/plugin-sentry" "^0.3.42-next.0" "@backstage/plugin-shortcuts" "^0.2.5-next.0" + "@backstage/plugin-stack-overflow" "^0.1.0-next.0" "@backstage/plugin-tech-insights" "^0.1.14-next.0" "@backstage/plugin-tech-radar" "^0.5.11-next.1" "@backstage/plugin-techdocs" "^1.0.1-next.1" @@ -12388,7 +12334,7 @@ express-xml-bodyparser@^0.3.0: dependencies: xml2js "^0.4.11" -express@^4.17.1: +express@^4.17.1, express@^4.17.3: version "4.17.3" resolved "https://registry.npmjs.org/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== @@ -13459,7 +13405,7 @@ globby@^7.1.1: pify "^3.0.0" slash "^1.0.0" -google-auth-library@^7.0.0, google-auth-library@^7.14.0, google-auth-library@^7.6.1: +google-auth-library@^7.14.1, google-auth-library@^7.6.1: version "7.14.1" resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz#e3483034162f24cc71b95c8a55a210008826213c" integrity sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA== @@ -13545,11 +13491,11 @@ grapheme-splitter@^1.0.4: integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== graphiql@^1.5.12: - version "1.8.3" - resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.8.3.tgz#4696755cbba851c93aea9deacbcf4229044a705d" - integrity sha512-3qb3jdlzg8nqQCRMnch6lG11royrE6etP2eoDKLxdrSpdaxI0PgNNttbMAJJYfsqEJ0XHcXtUDt1jH6jkKOK4Q== + version "1.8.4" + resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.8.4.tgz#3a447eae85d839eb207a07fc3aabc6f3a10df46d" + integrity sha512-mEYtfympvlZ4E1VMEMZ0BG3NCLA9L69EEbnpHpAA12oWz3aul4HXWl59JS0aHZftyiPx1/gHjSsjctkhbazT9g== dependencies: - "@graphiql/toolkit" "^0.4.2" + "@graphiql/toolkit" "^0.4.3" codemirror "^5.58.2" codemirror-graphql "^1.2.15" copy-to-clipboard "^3.2.0" @@ -14069,7 +14015,7 @@ http-parser-js@>=0.5.1: resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== -http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: +http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== @@ -14087,7 +14033,7 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" -http-proxy-middleware@^2.0.0: +http-proxy-middleware@^2.0.0, http-proxy-middleware@^2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.4.tgz#03af0f4676d172ae775cb5c33f592f40e1a4e07a" integrity sha512-m/4FxX17SUvz4lJ5WPXOHDUuCwIqXLfLHs1s0uZ3oYjhoXlx9csYxaOa0ElDEJ+h8Q4iJ1s+lTMbiCa4EXIJqg== @@ -14243,6 +14189,13 @@ ignore-walk@^4.0.1: dependencies: minimatch "^3.0.4" +ignore-walk@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776" + integrity sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw== + dependencies: + minimatch "^5.0.1" + ignore@^3.3.5: version "3.3.10" resolved "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" @@ -14519,7 +14472,7 @@ ioredis@^4.28.5: redis-parser "^3.0.0" standard-as-callback "^2.1.0" -ip@^1.1.0, ip@^1.1.5: +ip@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= @@ -14960,7 +14913,7 @@ is-reference@^1.2.1: dependencies: "@types/estree" "*" -is-regex@^1.0.4, is-regex@^1.1.4: +is-regex@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== @@ -17976,7 +17929,7 @@ minimatch@3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@5.0.1, minimatch@^5.0.0: +minimatch@5.0.1, minimatch@^5.0.0, minimatch@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== @@ -18162,9 +18115,9 @@ moment-timezone@^0.5.x: moment ">= 2.9.0" "moment@>= 2.9.0", moment@>=2.14.0, moment@^2.27.0, moment@^2.29.1: - version "2.29.1" - resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" - integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== + version "2.29.2" + resolved "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4" + integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg== morgan@^1.10.0: version "1.10.0" @@ -18254,17 +18207,12 @@ msw@^0.36.3: type-fest "^1.2.2" yargs "^17.3.0" -multicast-dns-service-types@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" - integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= - -multicast-dns@^6.0.1: - version "6.2.3" - resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" - integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== +multicast-dns@^7.2.4: + version "7.2.4" + resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.4.tgz#cf0b115c31e922aeb20b64e6556cbeb34cf0dd19" + integrity sha512-XkCYOU+rr2Ft3LI6w4ye51M3VK31qJXFIxu0XLw169PtKG0Zx47OrXeVW/GCYOfpC9s1yyyf1S+L8/4LY0J9Zw== dependencies: - dns-packet "^1.3.1" + dns-packet "^5.2.2" thunky "^1.0.2" multimatch@^5.0.0: @@ -18453,10 +18401,10 @@ node-fetch@2.6.7, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node- dependencies: whatwg-url "^5.0.0" -node-forge@^1.0.0, node-forge@^1.2.0: - version "1.3.0" - resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz#37a874ea723855f37db091e6c186e5b67a01d4b2" - integrity sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA== +node-forge@^1, node-forge@^1.0.0: + version "1.3.1" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== node-gyp@^5.0.2: version "5.1.0" @@ -18641,10 +18589,10 @@ normalize-url@^6.0.1: resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== -npm-bundled@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" - integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== +npm-bundled@^1.1.1, npm-bundled@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" + integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== dependencies: npm-normalize-package-bin "^1.0.1" @@ -18703,6 +18651,16 @@ npm-packlist@^3.0.0: npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" +npm-packlist@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.0.0.tgz#74795ebbbf91bd5a2db6ecff4d6fe1f1c1a07e11" + integrity sha512-uU20UwM4Hogfab1Q7htJbhcyafM9lGHxOrDjkKvR2S3z7Ds0uRaESk0cXctczk+ABT4DZWNwjB10xlurFdEwZg== + dependencies: + glob "^7.2.0" + ignore-walk "^5.0.1" + npm-bundled "^1.1.2" + npm-normalize-package-bin "^1.0.1" + npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.0, npm-pick-manifest@^6.1.1: version "6.1.1" resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" @@ -18853,11 +18811,6 @@ object-inspect@^1.11.0, object-inspect@^1.12.0, object-inspect@^1.9.0: resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== -object-is@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" - integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== - 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" @@ -20997,9 +20950,9 @@ react-helmet@6.1.0: react-side-effect "^2.1.0" react-hook-form@^7.12.2, react-hook-form@^7.13.0: - version "7.28.1" - resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.28.1.tgz#95fc37be6c6b9d57212eb7eca055fb36d079b3b7" - integrity sha512-mgwxvXuvt3FMY/mdnWbPc++Zf1U5xYzkhOaL05mtFMLvXc9MvUhMUlKtUVuO12sOrgT3nPXBgVFawtiJ4ONrgg== + version "7.29.0" + resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.29.0.tgz#5e7e41a483b70731720966ed8be52163ea1fecf1" + integrity sha512-NcJqWRF6el5HMW30fqZRt27s+lorvlCCDbTpAyHoodQeYWXgQCvZJJQLC1kRMKdrJknVH0NIg3At6TUzlZJFOQ== react-hot-loader@^4.13.0: version "4.13.0" @@ -21618,7 +21571,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.1: +regexp.prototype.flags@^1.3.1: version "1.4.1" resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== @@ -22287,12 +22240,12 @@ select-hose@^2.0.0: resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= -selfsigned@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz#e927cd5377cbb0a1075302cff8df1042cc2bce5b" - integrity sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ== +selfsigned@^2.0.0, selfsigned@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz#8b2df7fa56bf014d19b6007655fff209c0ef0a56" + integrity sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ== dependencies: - node-forge "^1.2.0" + node-forge "^1" semver-diff@^3.1.1: version "3.1.1" @@ -23329,7 +23282,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.0, strip-ansi@^7.0.1: +strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== @@ -23773,12 +23726,12 @@ tdigest@^0.1.1: react-router-dom "6.0.0-beta.0" react-use "^17.2.4" -teeny-request@^7.0.0: - version "7.0.1" - resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz#bdd41fdffea5f8fbc0d29392cb47bec4f66b2b4c" - integrity sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw== +teeny-request@^7.1.3: + version "7.2.0" + resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-7.2.0.tgz#41347ece068f08d741e7b86df38a4498208b2633" + integrity sha512-SyY0pek1zWsi0LRVAALem+avzMLc33MKW/JLLakdP4s9+D7+jHcy5x6P+h94g2QNZsAqQNfX5lsbd3WSeJXrrw== dependencies: - http-proxy-agent "^4.0.0" + http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" node-fetch "^2.6.1" stream-events "^1.0.5" @@ -25178,38 +25131,37 @@ webpack-dev-middleware@^5.3.1: schema-utils "^4.0.0" webpack-dev-server@^4.7.3: - version "4.7.4" - resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz#d0ef7da78224578384e795ac228d8efb63d5f945" - integrity sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A== + version "4.8.1" + resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.8.1.tgz#58f9d797710d6e25fa17d6afab8708f958c11a29" + integrity sha512-dwld70gkgNJa33czmcj/PlKY/nOy/BimbrgZRaR9vDATBQAYgLzggR0nxDtPLJiLrMgZwbE6RRfJ5vnBBasTyg== dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" "@types/express" "^4.17.13" "@types/serve-index" "^1.9.1" "@types/sockjs" "^0.3.33" - "@types/ws" "^8.2.2" + "@types/ws" "^8.5.1" ansi-html-community "^0.0.8" - bonjour "^3.5.0" + bonjour-service "^1.0.11" chokidar "^3.5.3" colorette "^2.0.10" compression "^1.7.4" connect-history-api-fallback "^1.6.0" default-gateway "^6.0.3" - del "^6.0.0" - express "^4.17.1" + express "^4.17.3" graceful-fs "^4.2.6" html-entities "^2.3.2" - http-proxy-middleware "^2.0.0" + http-proxy-middleware "^2.0.3" ipaddr.js "^2.0.1" open "^8.0.9" p-retry "^4.5.0" portfinder "^1.0.28" + rimraf "^3.0.2" schema-utils "^4.0.0" - selfsigned "^2.0.0" + selfsigned "^2.0.1" serve-index "^1.9.1" sockjs "^0.3.21" spdy "^4.0.2" - strip-ansi "^7.0.0" webpack-dev-middleware "^5.3.1" ws "^8.4.2"