diff --git a/.changeset/brave-spoons-grow.md b/.changeset/brave-spoons-grow.md new file mode 100644 index 0000000000..d1bd166510 --- /dev/null +++ b/.changeset/brave-spoons-grow.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Switched to relying on the built-in support for async loading in `react-syntax-highlighter`. This should provide further improvements to async rendering and lazy loading, and avoid test flakiness that was happening because of the significant number or resources being loaded in lazily all at once. diff --git a/.changeset/giant-phones-exercise.md b/.changeset/giant-phones-exercise.md new file mode 100644 index 0000000000..b190c2e7f0 --- /dev/null +++ b/.changeset/giant-phones-exercise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-fossa': patch +--- + +Add `entitiesFilter` option to `FossaPage` to filter entities displayed in the table. diff --git a/.changeset/quiet-parrots-occur.md b/.changeset/quiet-parrots-occur.md new file mode 100644 index 0000000000..c9a6ce9a1d --- /dev/null +++ b/.changeset/quiet-parrots-occur.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-graph': patch +--- + +Capture analytics events for clicks in the graph. diff --git a/.changeset/rare-cooks-play.md b/.changeset/rare-cooks-play.md new file mode 100644 index 0000000000..b76d34ee62 --- /dev/null +++ b/.changeset/rare-cooks-play.md @@ -0,0 +1,14 @@ +--- +'@backstage/create-app': minor +--- + +Removed `@backstage/plugin-welcome`, no new updates to the packages will be +published in the future. + +The welcome plugin was used by early alpha versions of Backstage, but today only +contained a simple page with welcome instructions. It was superseded by +`@backstage/plugin-home` which can be used to build a homepage customized to the +needs of your organization. + +If it's still used in your app, remove the dependency from your `package.json` +as well as left over code. diff --git a/.changeset/rude-comics-float.md b/.changeset/rude-comics-float.md new file mode 100644 index 0000000000..cb3a883fa1 --- /dev/null +++ b/.changeset/rude-comics-float.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-jenkins': patch +--- + +Sort Jenkins build by timestamp in `CITable` diff --git a/.changeset/selfish-papayas-watch.md b/.changeset/selfish-papayas-watch.md new file mode 100644 index 0000000000..1d9b8a1ab6 --- /dev/null +++ b/.changeset/selfish-papayas-watch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-git-release-manager': patch +--- + +Minor internal type improvement diff --git a/.changeset/techdocs-dull-glasses-decide.md b/.changeset/techdocs-dull-glasses-decide.md new file mode 100644 index 0000000000..f043c2f607 --- /dev/null +++ b/.changeset/techdocs-dull-glasses-decide.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Only replace the shadow dom if the content is changed to avoid a flickering UI. diff --git a/.github/workflows/master-win.yml b/.github/workflows/master-win.yml index c6a9222483..873f12086a 100644 --- a/.github/workflows/master-win.yml +++ b/.github/workflows/master-win.yml @@ -58,7 +58,7 @@ jobs: run: lerna run --scope @backstage/core-* build - name: test - run: yarn lerna -- run test --runInBand + run: yarn lerna -- run test env: BACKSTAGE_TEST_DISABLE_DOCKER: 1 diff --git a/ADOPTERS.md b/ADOPTERS.md index 1a4913be17..f96cc08e18 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,60 +1,60 @@ -| Organization | Contact | Description of Use | -| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | -| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | -| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | -| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | -| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | -| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | -| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | -| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | -| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | -| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | -| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | -| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | -| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | -| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | -| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | -| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | -| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | -| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | -| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | -| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | -| [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. | -| [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. | -| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | -| [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 ๐ŸŒ•๐Ÿš€๐Ÿง‘โ€๐Ÿš€ | -| [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. | -| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | -| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | -| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | -| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | -| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | -| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | -| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | -| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | -| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | -| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | -| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | -| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | -| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. | -| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | -| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | -| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | -| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | -| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | -| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems๐Ÿš€. | -| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | -| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. | -| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. | +| Organization | Contact | Description of Use | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | +| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. | +| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | +| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | +| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | +| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | +| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | +| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | +| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | +| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | +| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | +| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | +| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | +| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | +| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | +| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | +| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | +| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | +| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | +| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | +| [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. | +| [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. | +| [Booz Allen Hamilton](https://www.boozallen.com/) | [Jason Miller](https://github.com/JasonMiller-BAH) | Developer portal for a full-stack software development ecosystem that accelerates consistent and repeatable Modern Software Development practices for internal innovation and investments. | +| [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 ๐ŸŒ•๐Ÿš€๐Ÿง‘โ€๐Ÿš€ | +| [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. | +| [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | +| [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | +| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | +| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | +| [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | +| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | +| [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | +| [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | +| [2U](https://2u.com) | [Andrew Thal](https://github.com/athal7) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | +| [Taxfix](https://taxfix.de/) | [Sami Ur Rehman](https://github.com/samiurrehman92) | Developer's portal with software catalog at it's core. Hosts API Specs, Tech Docs, Tech Radar and some custom plugins. | +| [Busuu](https://busuu.com/) | [Adam Tester](https://github.com/adamtester) | Developer portal with service catalog, API docs, Event docs, service templating, and cost insights. | +| [Loadsmart](https://loadsmart.com/) | [Loadsmart](https://github.com/loadsmart) | Improve services visibility and operations for service owners and developers. | +| [Monzo](https://monzo.com/) | [@WillSewell](https://github.com/WillSewell), [@joechrisellis](https://github.com/joechrisellis) | Developer portal showing metadata and docs for over 2000 microservices. We have built a number of plugins such as a UI for our system to measure [software excellence](https://monzo.com/blog/2021/09/15/how-we-measure-software-excellence), and a UI to show deployment and config change events. | +| [Vaimo](https://www.vaimo.com) | [@vaimo-magnus](https://github.com/vaimo-magnus) | Developer Portal for our developers at Vaimo, currently docs and self-service towards our internal PaaS based on k8s. Plans to extend the catalog into Projects, Environments etc | +| [Wayfair](https://www.wayfair.com) | [@fransan](https://github.com/fransan), [@errskipower](https://github.com/errskipower), [@hrrs](https://github.com/hrrs) | Developer portal for service catalog, technical documentation, and APIs. | +| [CircleHD](https://www.circlehd.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [CastDesk](https://castdesk.com) | [@circlehddev](https://github.com/circlehddev) | Developer Portal for internal dev team across the globe | +| [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | +| [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | +| [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems๐Ÿš€. | +| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | +| [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. | +| [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. | diff --git a/contrib/chart/backstage/files/app-config.development.yaml.tpl b/contrib/chart/backstage/files/app-config.development.yaml.tpl index 1e0a41ad63..6d3ded16c1 100644 --- a/contrib/chart/backstage/files/app-config.development.yaml.tpl +++ b/contrib/chart/backstage/files/app-config.development.yaml.tpl @@ -47,5 +47,5 @@ sentry: organization: {{ .Values.appConfig.sentry.organization | quote }} techdocs: - generators: - techdocs: 'local' + generator: + runIn: 'local' \ No newline at end of file diff --git a/contrib/chart/backstage/templates/frontend-deployment.yaml b/contrib/chart/backstage/templates/frontend-deployment.yaml index 04983513a6..dba4e2c96a 100644 --- a/contrib/chart/backstage/templates/frontend-deployment.yaml +++ b/contrib/chart/backstage/templates/frontend-deployment.yaml @@ -32,7 +32,7 @@ spec: ports: - containerPort: {{ .Values.frontend.containerPort }} resources: - {{- toYaml .Values.backend.resources | nindent 12 }} + {{- toYaml .Values.frontend.resources | nindent 12 }} envFrom: - configMapRef: name: {{ include "backstage.fullname" . }}-app-env diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md index 43dfc41815..621d46b51e 100644 --- a/docs/features/search/how-to-guides.md +++ b/docs/features/search/how-to-guides.md @@ -47,18 +47,18 @@ const app = createApp({ ## How to index TechDocs documents -The techdocs-plugin has supported integrations to Search, meaning that it +The TechDocs plugin has supported integrations to Search, meaning that it provides a default collator ready to be used. -The purpose of this how-to guide is to walk you through how to register the +The purpose of this guide is to walk you through how to register the [DefaultTechDocsCollator](https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts) -in your App in two steps, so that you can get TechDocs documents indexed. +in your App, so that you can get TechDocs documents indexed. If you have been through the -[getting started guide](https://backstage.io/docs/features/search/getting-started) -already, you should have the `packages/backend/src/plugins/search.ts` file -available. If you have that, you can jump to the first step of this guide, if -not - start by go through the getting started guide. +[Getting Started with Search guide](https://backstage.io/docs/features/search/getting-started), +you should have the `packages/backend/src/plugins/search.ts` file available. If +so, you can go ahead and follow this guide - if not, start by going through the +getting started guide. 1. Import the DefaultTechDocsCollator from `@backstage/plugin-techdocs-backend`. @@ -82,7 +82,7 @@ You should now have your TechDocs documents indexed to your search engine of choice! If you want your users to be able to filter down to the techdocs type when -searching. You can update your `SearchPage.tsx` file in +searching, you can update your `SearchPage.tsx` file in `packages/app/src/components/search` by adding `techdocs` to the list of values of the `SearchType` component. diff --git a/docs/features/techdocs/creating-and-publishing.md b/docs/features/techdocs/creating-and-publishing.md index 93ce3ad4d9..c0989bf02e 100644 --- a/docs/features/techdocs/creating-and-publishing.md +++ b/docs/features/techdocs/creating-and-publishing.md @@ -5,11 +5,12 @@ sidebar_label: Creating and Publishing Documentation description: Guidance on how to create and publish documentation --- -This section will guide you through: +This section will guide you through how to: - [Create a basic documentation setup](#create-a-basic-documentation-setup) + - [Use any software template](#use-any-software-template) - [Use the documentation template](#use-the-documentation-template) - - [Manually add documentation setup to already existing repository](#manually-add-documentation-setup-to-already-existing-repository) + - [Enable documentation for an already existing entity](#enable-documentation-for-an-already-existing-entity) - [Writing and previewing your documentation](#writing-and-previewing-your-documentation) ## Prerequisites @@ -21,29 +22,47 @@ This section will guide you through: If you have an existing repository that you'd like to add documentation to, skip to the -[Manually add documentation setup](#manually-add-documentation-setup-to-already-existing-repository) -section below. Otherwise, continue reading to start a documentation repo from -scratch. +[Enable documentation for an already existing entity setup](#enable-documentation-for-an-already-existing-entity) +section below. Otherwise, continue reading to create a new software entity +including documentation from scratch. + +### Use any software template + +TechDocs is built on top of the +[docs like code approach](https://www.docslikecode.com/about/). This, in short, +means that you should keep documentation close to the code. + +Your Backstage app has a set of software templates added by default. All of +these software templates include everything you need to get your TechDocs site +up and running and to start writing your documentation. + +If you have created software templates that do not include documentation by +default, we highly recommend you to set that up. Follow our how-to guide +[How to add documentation setup to your software templates](./how-to-guides.md#how-to-add-documentation-setup-to-your-software-templates) +to get started. ### Use the documentation template -Your working Backstage instance should by default have a documentation template -added. If not, copy the catalog locations from the +There could be _some_ situations where you don't want to keep your docs close to +your code, but still want to publish documentation - for example, an onboarding +tutorial. For this use case, we have put together a documentation template. Your +Backstage instance should by default have a documentation template added. If +not, copy the catalog locations from the [create-app template](https://github.com/backstage/backstage/blob/master/packages/create-app/templates/default-app/app-config.yaml.hbs) -to add the documentation template. The template creates a component with only -TechDocs configuration and default markdown files as below mentioned in manual -documentation setup, and is otherwise empty. +to add the documentation template. The template creates a component with +**only** TechDocs configuration and default markdown files, and is otherwise +empty. ![Documentation Template](../../assets/techdocs/documentation-template.png) Create an entity from the documentation template and you will get the needed setup for free. -### Manually add documentation setup to already existing repository +### Enable documentation for an already existing entity Prerequisites: -- An existing component +- An existing entity [registered in backstage](../software-catalog/index.md#adding-components-to-the-catalog) (e.g. via a `catalog-info.yaml` file). @@ -72,11 +91,11 @@ metadata: The [`backstage.io/techdocs-ref` annotation](../software-catalog/well-known-annotations.md#backstageiotechdocs-ref) is used by TechDocs to download the documentation source files for generating an -Entity's TechDocs site. +entity's TechDocs site. -Create a `/docs` folder in the root of the project with at least an `index.md` -file. _(If you add more markdown files, make sure to update the nav in the -mkdocs.yml file to get a proper navigation for your documentation.)_ +Create a `/docs` folder in the root of your repository with at least an +`index.md` file in it. _(If you add more markdown files, make sure to update the +nav in the mkdocs.yml file to get a proper navigation for your documentation.)_ > Note - Although `docs` is a popular directory name for storing documentation, > it can be renamed to something else and can be configured by `mkdocs.yml`. See @@ -95,9 +114,10 @@ updated documentation next time you run Backstage! ## Writing and previewing your documentation -Using the `techdocs-cli` you can preview your docs inside a local Backstage -instance and get live reload on changes. This is useful when you want to preview -your documentation while writing. +Using the [techdocs-cli](https://github.com/backstage/techdocs-cli) you can +preview your docs inside a local Backstage instance and get live reload on +changes. This is useful when you want to preview your documentation while +writing. To do this you can run: diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index 9c41c6dde2..edca4e84ad 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -348,3 +348,75 @@ const app = createApp({ ], }); ``` + +## How to add the documentation setup to your software templates + +[Software Templates](https://backstage.io/docs/features/software-templates/software-templates-index) +in Backstage is a tool that can help your users to create new components out of +already configured templates. It comes with a set of default templates to use, +but you can also +[add your own templates](https://backstage.io/docs/features/software-templates/adding-templates). + +If you have your own templates set up, we highly recommend that you include the +required setup for TechDocs in those templates. When creating a new component, +your users will then get a TechDocs site up and running automatically, ready for +them to start writing technical documentation. + +The purpose of this how-to guide is to walk you through how to add the required +configuration and some default markdown files to your new template. You can use +the +[react-ssr-template](https://github.com/backstage/software-templates/tree/main/scaffolder-templates/react-ssr-template) +as a reference when walking through the steps. + +Prerequisites: + +- An existing software template including a `template.yaml` together with a + skeleton folder including at least a `catalog-info.yaml`. + +1. Update your component's entity description by adding the following lines to + the `catalog-info.yaml` in your skeleton folder. + +```yaml +annotations: + backstage.io/techdocs-ref: dir:. +``` + +The +[`backstage.io/techdocs-ref` annotation](../software-catalog/well-known-annotations.md#backstageiotechdocs-ref) +is used by TechDocs to download the documentation source files for generating an +entity's TechDocs site. + +2. Create an `mkdocs.yml` file in the root of your skeleton folder with the + following content: + +```yaml +site_name: ${{values.component_id}} +site_description: ${{values.description}} + +nav: + - Introduction: index.md + +plugins: + - techdocs-core +``` + +3. Create a `/docs` folder in the skeleton folder with at least an `index.md` + file in it. + +The `docs/index.md` can for example have the following content: + +```md +# ${{ values.component_id }} + +${{ values.description }} + +## Getting started + +Start writing your documentation by adding more markdown (.md) files to this +folder (/docs) or replace the content in this file. +``` + +> Note: The values of `site_name`, `component_id` and `site_description` depends +> on how you have configured your `template.yaml` + +Done! You now have support for TechDocs in your own software template! diff --git a/docs/getting-started/contributors.md b/docs/getting-started/contributors.md index ed56d63cab..28b062b5ca 100644 --- a/docs/getting-started/contributors.md +++ b/docs/getting-started/contributors.md @@ -96,7 +96,7 @@ yarn start # Start serving the example app, use --check to include type checks a yarn storybook # Start local storybook, useful for working on components in @backstage/core-components -yarn workspace @backstage/plugin-welcome start # Serve welcome plugin only, also supports --check +yarn workspace @backstage/plugin-api-docs start # Serve api-docs plugin only, also supports --check yarn tsc # Run typecheck, use --watch for watch mode yarn tsc:full # Run full type checking, for example without skipLibCheck, use in CI diff --git a/docs/plugins/analytics.md b/docs/plugins/analytics.md index 0b3d5dfd9c..7adf2650df 100644 --- a/docs/plugins/analytics.md +++ b/docs/plugins/analytics.md @@ -287,7 +287,7 @@ describe('SomeComponent', () => { await waitFor(() => { expect(apiSpy.getEvents()[0]).toMatchObject({ action: 'expected action', - subject: 'expected subject'', + subject: 'expected subject', attributes: { foo: 'bar', }, diff --git a/microsite/data/plugins/gke-usage.yaml b/microsite/data/plugins/gke-usage.yaml index 2ca9f5784a..e092f84973 100644 --- a/microsite/data/plugins/gke-usage.yaml +++ b/microsite/data/plugins/gke-usage.yaml @@ -1,7 +1,7 @@ --- title: GKE Usage author: BESTSELLER -authorUrl: bestsellerit.com +authorUrl: https://bestsellerit.com category: Discovery description: This plugin will show you the cost and resource usage of your application within Google Kubernetes Engine (GKE). documentation: https://github.com/BESTSELLER/backstage-plugin-gkeusage/blob/master/README.md diff --git a/microsite/data/plugins/harbor.yaml b/microsite/data/plugins/harbor.yaml index 4e31113c83..9023e0c434 100644 --- a/microsite/data/plugins/harbor.yaml +++ b/microsite/data/plugins/harbor.yaml @@ -1,7 +1,7 @@ --- title: Harbor author: BESTSELLER -authorUrl: bestsellerit.com +authorUrl: https://bestsellerit.com category: Discovery description: This plugin will show you information about Docker images within the Harbor cloud native registry. documentation: https://github.com/BESTSELLER/backstage-plugin-harbor/blob/master/README.md diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 6b04eb5e8c..e637bc1b11 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -56,14 +56,14 @@ "react-dom": "^16.12.0", "react-helmet": "6.1.0", "react-hook-form": "^7.12.2", - "react-markdown": "^5.0.2", + "react-markdown": "^7.0.1", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-sparklines": "^1.7.0", "react-syntax-highlighter": "^15.4.3", "react-text-truncate": "^0.16.0", "react-use": "^17.2.4", - "remark-gfm": "^1.0.0", + "remark-gfm": "^2.0.0", "zen-observable": "^0.8.15" }, "devDependencies": { diff --git a/packages/core-components/src/components/CodeSnippet/CodeSnippet.test.tsx b/packages/core-components/src/components/CodeSnippet/CodeSnippet.test.tsx index a4ffd3462b..fec5b7ddf7 100644 --- a/packages/core-components/src/components/CodeSnippet/CodeSnippet.test.tsx +++ b/packages/core-components/src/components/CodeSnippet/CodeSnippet.test.tsx @@ -31,6 +31,12 @@ const minProps = { }; describe('', () => { + // react-syntax-highlighter is large and can cause significant slowdowns + // This test makes sure we're loading things in asynchronously and not too broadly. + it('renders quickly', async () => { + await renderInTestApp(); + }, 1000); + it('renders text without exploding', async () => { const { getByText } = await renderInTestApp(); expect(getByText(/"Hello"/)).toBeInTheDocument(); diff --git a/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx b/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx index 3aa2f42984..e1c407276b 100644 --- a/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx +++ b/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx @@ -14,56 +14,13 @@ * limitations under the License. */ -import React, { lazy, Suspense } from 'react'; +import React from 'react'; import { useTheme } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; import { CopyTextButton } from '../CopyTextButton'; -import { Progress } from '../Progress'; - -const LazySyntaxHighlighter = lazy(async () => { - const [{ default: SyntaxHighlighter }, { docco, dark }] = await Promise.all([ - import('react-syntax-highlighter'), - import('react-syntax-highlighter/dist/cjs/styles/hljs'), - ]); - - function LazyHighlighter(props: CodeSnippetProps) { - const { - text, - language, - showLineNumbers = false, - highlightedNumbers, - customStyle, - } = props; - const theme = useTheme(); - const mode = theme.palette.type === 'dark' ? dark : docco; - const highlightColor = - theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed'; - - return ( - - highlightedNumbers?.includes(lineNumber) - ? { - style: { - backgroundColor: highlightColor, - }, - } - : {} - } - > - {text} - - ); - } - - return { default: LazyHighlighter }; -}); +import { LightAsync } from 'react-syntax-highlighter'; +import dark from 'react-syntax-highlighter/dist/esm/styles/hljs/dark'; +import docco from 'react-syntax-highlighter/dist/esm/styles/hljs/docco'; /** * Properties for {@link CodeSnippet} @@ -112,12 +69,39 @@ export interface CodeSnippetProps { * providing consistent theming and copy code button */ export function CodeSnippet(props: CodeSnippetProps) { - const { text, showCopyCodeButton = false } = props; + const { + text, + language, + showLineNumbers = false, + highlightedNumbers, + customStyle, + showCopyCodeButton = false, + } = props; + const theme = useTheme(); + const mode = theme.palette.type === 'dark' ? dark : docco; + const highlightColor = theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed'; + return (
- }> - - + + highlightedNumbers?.includes(lineNumber) + ? { + style: { + backgroundColor: highlightColor, + }, + } + : {} + } + > + {text} + {showCopyCodeButton && (
diff --git a/packages/core-components/src/components/MarkdownContent/MarkdownContent.test.tsx b/packages/core-components/src/components/MarkdownContent/MarkdownContent.test.tsx index d39e92c75e..2e1550078f 100644 --- a/packages/core-components/src/components/MarkdownContent/MarkdownContent.test.tsx +++ b/packages/core-components/src/components/MarkdownContent/MarkdownContent.test.tsx @@ -52,14 +52,14 @@ describe('', () => { it('render MarkdownContent component with CodeSnippet for code blocks', async () => { const rendered = await renderWithEffects( - wrapInTestApp(), + wrapInTestApp( + , + ), ); - const fp1 = rendered.getByText('jest', { selector: 'span' }); + const fp1 = await rendered.findByText('jest(test:', { selector: 'span' }); expect(fp1).toBeInTheDocument(); - expect(fp1.className).toEqual('hljs-function'); - const fp2 = rendered.getByText('(test: string)', { selector: 'span' }); + const fp2 = rendered.getByText('string', { selector: 'span' }); expect(fp2).toBeInTheDocument(); - expect(fp2.className).toEqual('hljs-function'); - expect(rendered.getByText(';', { selector: 'span' })).toBeInTheDocument(); + expect(rendered.getByText(');', { selector: 'span' })).toBeInTheDocument(); }); }); diff --git a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx index eacd9adc8a..6728c48580 100644 --- a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx +++ b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx @@ -15,7 +15,7 @@ */ import { makeStyles } from '@material-ui/core/styles'; -import ReactMarkdown from 'react-markdown'; +import ReactMarkdown, { Options } from 'react-markdown'; import gfm from 'remark-gfm'; import React from 'react'; import { BackstageTheme } from '@backstage/theme'; @@ -69,9 +69,17 @@ type Props = { dialect?: 'gfm' | 'common-mark'; }; -const renderers = { - code: ({ language, value }: { language: string; value?: string }) => { - return ; +const components: Options['components'] = { + code: ({ inline, className, children, ...props }) => { + const text = String(children).replace(/\n+$/, ''); + const match = /language-(\w+)/.exec(className || ''); + return !inline && match ? ( + + ) : ( + + {children} + + ); }, }; @@ -86,10 +94,10 @@ export function MarkdownContent(props: Props) { const classes = useStyles(); return ( ); } diff --git a/packages/techdocs-common/api-report.md b/packages/techdocs-common/api-report.md index fca67d8948..f5a00c25ea 100644 --- a/packages/techdocs-common/api-report.md +++ b/packages/techdocs-common/api-report.md @@ -256,7 +256,7 @@ export class TechdocsGenerator implements GeneratorBase { config: Config; scmIntegrations: ScmIntegrationRegistry; }); - static readonly defaultDockerImage = 'spotify/techdocs:v0.3.3'; + static readonly defaultDockerImage = 'spotify/techdocs:v0.3.4'; // (undocumented) static fromConfig( config: Config, diff --git a/packages/techdocs-common/src/stages/generate/techdocs.ts b/packages/techdocs-common/src/stages/generate/techdocs.ts index d5d4584c34..44a9d5a14a 100644 --- a/packages/techdocs-common/src/stages/generate/techdocs.ts +++ b/packages/techdocs-common/src/stages/generate/techdocs.ts @@ -42,7 +42,7 @@ export class TechdocsGenerator implements GeneratorBase { * The default docker image (and version) used to generate content. Public * and static so that techdocs-common consumers can use the same version. */ - public static readonly defaultDockerImage = 'spotify/techdocs:v0.3.3'; + public static readonly defaultDockerImage = 'spotify/techdocs:v0.3.4'; private readonly logger: Logger; private readonly containerRunner: ContainerRunner; private readonly options: GeneratorConfig; diff --git a/plugins/bazaar-backend/.eslintrc.js b/plugins/bazaar-backend/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/plugins/bazaar-backend/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/plugins/bazaar-backend/README.md b/plugins/bazaar-backend/README.md new file mode 100644 index 0000000000..82c1376d8b --- /dev/null +++ b/plugins/bazaar-backend/README.md @@ -0,0 +1,49 @@ +# Bazaar Backend + +Welcome to the Bazaar backend plugin! + +# Installation + +## Install the package + +```bash +# From your Backstage root directory +cd packages/backend +yarn add @backstage/plugin-bazaar-backend +``` + +## Adding the plugin to your `packages/backend` + +You'll need to add the plugin to the router in your `backend` package. You can do this by creating a file called `packages/backend/src/plugins/bazaar.ts` + +```tsx +import { PluginEnvironment } from '../types'; +import { createRouter } from '@backstage/plugin-bazaar-backend'; + +export default async function createPlugin({ + logger, + database, + config, +}: PluginEnvironment) { + return await createRouter({ logger, config, database }); +} +``` + +With the `bazaar.ts` router setup in place, add the router to `packages/backend/src/index.ts`: + +```diff ++ import bazaar from './plugins/bazaar'; + +async function main() { + ... + const createEnv = makeCreateEnv(config); + + const catalogEnv = useHotMemoize(module, () => createEnv('catalog')); ++ const bazaarEnv = useHotMemoize(module, () => createEnv('bazaar')); + + const apiRouter = Router(); ++ apiRouter.use('/bazaar', await bazaar(bazaarEnv)); + ... + apiRouter.use(notFoundHandler()); + +``` diff --git a/plugins/bazaar-backend/api-report.md b/plugins/bazaar-backend/api-report.md new file mode 100644 index 0000000000..89f6da5305 --- /dev/null +++ b/plugins/bazaar-backend/api-report.md @@ -0,0 +1,29 @@ +## API Report File for "@backstage/plugin-bazaar-backend" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { Config } from '@backstage/config'; +import express from 'express'; +import { Logger as Logger_2 } from 'winston'; +import { PluginDatabaseManager } from '@backstage/backend-common'; + +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function createRouter(options: RouterOptions): Promise; + +// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface RouterOptions { + // (undocumented) + config: Config; + // (undocumented) + database: PluginDatabaseManager; + // (undocumented) + logger: Logger_2; +} + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/bazaar-backend/migrations/20211014144054_init.js b/plugins/bazaar-backend/migrations/20211014144054_init.js new file mode 100644 index 0000000000..5bf66698ac --- /dev/null +++ b/plugins/bazaar-backend/migrations/20211014144054_init.js @@ -0,0 +1,65 @@ +/* + * 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. + */ + +exports.up = async function up(knex) { + await knex.schema.createTable('metadata', table => { + table.comment('The table of Bazaar metadata'); + table + .text('entity_ref') + .notNullable() + .unique() + .comment('The ref of the entity'); + table.text('name').notNullable().comment('The name of the entity'); + table + .text('community') + .comment('Link to where the community can discuss ideas'); + table + .text('announcement') + .notNullable() + .comment('The announcement of the Bazaar project'); + table + .text('status') + .defaultTo('proposed') + .notNullable() + .comment('The status of the Bazaar project'); + table + .text('updated_at') + .notNullable() + .comment('Timestamp on ISO 8601 format when entity was last updated'); + }); + + await knex.schema.createTable('members', table => { + table.comment('The table of Bazaar members'); + table + .text('entity_ref') + .notNullable() + .references('metadata.entity_ref') + .onDelete('CASCADE') + .comment('The ref of the entity'); + table.text('user_id').notNullable().comment('The user id of the member'); + table + .dateTime('join_date') + .defaultTo(knex.fn.now()) + .notNullable() + .comment('The timestamp when this member joined'); + table.text('picture').comment('Link to profile picture'); + }); +}; + +exports.down = async function down(knex) { + await knex.schema.dropTable('metadata'); + await knex.schema.dropTable('members'); +}; diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json new file mode 100644 index 0000000000..82c3317555 --- /dev/null +++ b/plugins/bazaar-backend/package.json @@ -0,0 +1,40 @@ +{ + "name": "@backstage/plugin-bazaar-backend", + "version": "0.1.1", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "start": "backstage-cli backend:dev", + "build": "backstage-cli backend:build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/backend-common": "^0.9.2", + "@backstage/backend-test-utils": "^0.1.7", + "@backstage/config": "^0.1.5", + "@types/express": "^4.17.6", + "express": "^4.17.1", + "express-promise-router": "^4.1.0", + "knex": "^0.95.1", + "winston": "^3.2.1", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.7.6" + }, + "files": [ + "dist", + "migrations/**/*.{js,d.ts}" + ] +} diff --git a/plugins/welcome/src/components/WelcomePage/index.ts b/plugins/bazaar-backend/src/index.ts similarity index 93% rename from plugins/welcome/src/components/WelcomePage/index.ts rename to plugins/bazaar-backend/src/index.ts index 0347756aa9..ca73cb27ba 100644 --- a/plugins/welcome/src/components/WelcomePage/index.ts +++ b/plugins/bazaar-backend/src/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { default } from './WelcomePage'; +export * from './service/router'; diff --git a/plugins/welcome/src/plugin.ts b/plugins/bazaar-backend/src/run.ts similarity index 53% rename from plugins/welcome/src/plugin.ts rename to plugins/bazaar-backend/src/run.ts index 187a7b5b96..54d2716290 100644 --- a/plugins/welcome/src/plugin.ts +++ b/plugins/bazaar-backend/src/run.ts @@ -14,27 +14,20 @@ * limitations under the License. */ -import { - createPlugin, - createRoutableExtension, - createRouteRef, -} from '@backstage/core-plugin-api'; +import { getRootLogger } from '@backstage/backend-common'; +import yn from 'yn'; +import { startStandaloneServer } from './service/standaloneServer'; -export const rootRouteRef = createRouteRef({ - title: 'Welcome', +const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7000; +const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); +const logger = getRootLogger(); + +startStandaloneServer({ port, enableCors, logger }).catch(err => { + logger.error(err); + process.exit(1); }); -export const welcomePlugin = createPlugin({ - id: 'welcome', - register({ featureFlags }) { - featureFlags.register('enable-welcome-box'); - }, +process.on('SIGINT', () => { + logger.info('CTRL+C pressed; exiting.'); + process.exit(0); }); - -export const WelcomePage = welcomePlugin.provide( - createRoutableExtension({ - name: 'WelcomePage', - component: () => import('./components/WelcomePage').then(m => m.default), - mountPoint: rootRouteRef, - }), -); diff --git a/plugins/bazaar-backend/src/service/DatabaseHandler.test.ts b/plugins/bazaar-backend/src/service/DatabaseHandler.test.ts new file mode 100644 index 0000000000..4413228006 --- /dev/null +++ b/plugins/bazaar-backend/src/service/DatabaseHandler.test.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DatabaseHandler } from './DatabaseHandler'; +import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; + +const bazaarProject: any = { + name: 'name', + entityRef: 'ref', + community: '', + status: 'proposed', + announcement: 'a', + membersCount: 0, +}; + +describe('DatabaseHandler', () => { + const databases = TestDatabases.create({ + ids: ['POSTGRES_13'], + }); + + async function createDatabaseHandler(databaseId: TestDatabaseId) { + const knex = await databases.init(databaseId); + return await DatabaseHandler.create({ database: knex }); + } + + it.each(databases.eachSupportedId())( + 'should do a full sync with the locations on connect, %p', + async databaseId => { + const db = await createDatabaseHandler(databaseId); + await db.insertMetadata(bazaarProject); + + const entities = await db.getEntities(); + expect(entities.length).toEqual(1); + expect(entities[0].entity_ref).toEqual(bazaarProject.entityRef); + }, + 60_000, + ); +}); diff --git a/plugins/bazaar-backend/src/service/DatabaseHandler.ts b/plugins/bazaar-backend/src/service/DatabaseHandler.ts new file mode 100644 index 0000000000..d4da6e65e7 --- /dev/null +++ b/plugins/bazaar-backend/src/service/DatabaseHandler.ts @@ -0,0 +1,145 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { resolvePackagePath } from '@backstage/backend-common'; +import { Knex } from 'knex'; + +const migrationsDir = resolvePackagePath( + '@backstage/plugin-bazaar-backend', + 'migrations', +); + +type Options = { + database: Knex; +}; + +export class DatabaseHandler { + static async create(options: Options): Promise { + const { database } = options; + + await database.migrate.latest({ + directory: migrationsDir, + }); + + return new DatabaseHandler(options); + } + + private readonly database: Knex; + + private constructor(options: Options) { + this.database = options.database; + } + + async getMembers(entityRef: string) { + return await this.database + .select('*') + .from('members') + .where({ entity_ref: entityRef }); + } + + async addMember(userId: string, entityRef: string, picture?: string) { + await this.database + .insert({ + entity_ref: entityRef, + user_id: userId, + picture: picture, + }) + .into('members'); + } + + async deleteMember(userId: string, entityRef: string) { + return await this.database('members') + .where({ entity_ref: decodeURIComponent(entityRef) }) + .andWhere('user_id', userId) + .del(); + } + + async getMetadata(entityRef: string) { + const coalesce = this.database.raw( + 'coalesce(count(members.entity_ref), 0) as members_count', + ); + + const columns = [ + 'members.entity_ref', + 'metadata.entity_ref', + 'metadata.name', + 'metadata.announcement', + 'metadata.status', + 'metadata.updated_at', + 'metadata.community', + ]; + + return await this.database('metadata') + .select([...columns, coalesce]) + .where({ 'metadata.entity_ref': entityRef }) + .groupBy(columns) + .leftJoin('members', 'metadata.entity_ref', '=', 'members.entity_ref'); + } + + async insertMetadata(bazaarProject: any) { + const { name, entityRef, community, announcement, status } = bazaarProject; + + await this.database + .insert({ + name: name, + entity_ref: entityRef, + community: community, + announcement: announcement, + status: status, + updated_at: new Date().toISOString(), + }) + .into('metadata'); + } + + async updateMetadata(bazaarProject: any) { + const { entityRef, community, announcement, status } = bazaarProject; + + return await this.database('metadata') + .where({ entity_ref: entityRef }) + .update({ + announcement: announcement, + community: community, + status: status, + updated_at: new Date().toISOString(), + }); + } + + async deleteMetadata(entityRef: string) { + return await this.database('metadata') + .where({ entity_ref: entityRef }) + .del(); + } + + async getEntities() { + const coalesce = this.database.raw( + 'coalesce(count(members.entity_ref), 0) as members_count', + ); + + const columns = [ + 'members.entity_ref', + 'metadata.entity_ref', + 'metadata.name', + 'metadata.announcement', + 'metadata.status', + 'metadata.updated_at', + 'metadata.community', + ]; + return await this.database('metadata') + .select([...columns, coalesce]) + .groupBy(columns) + .leftJoin('members', 'metadata.entity_ref', '=', 'members.entity_ref'); + } +} diff --git a/plugins/bazaar-backend/src/service/router.ts b/plugins/bazaar-backend/src/service/router.ts new file mode 100644 index 0000000000..f0bc02c65b --- /dev/null +++ b/plugins/bazaar-backend/src/service/router.ts @@ -0,0 +1,113 @@ +/* + * 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. + */ + +import { errorHandler, PluginDatabaseManager } from '@backstage/backend-common'; +import express from 'express'; +import Router from 'express-promise-router'; +import { Logger } from 'winston'; +import { Config } from '@backstage/config'; +import { DatabaseHandler } from './DatabaseHandler'; + +export interface RouterOptions { + logger: Logger; + database: PluginDatabaseManager; + config: Config; +} + +export async function createRouter( + options: RouterOptions, +): Promise { + const { logger, database } = options; + const db = await database.getClient(); + + const dbHandler = await DatabaseHandler.create({ database: db }); + + logger.info('Initializing Bazaar backend'); + + const router = Router(); + router.use(express.json()); + + router.get('/members/:ref', async (request, response) => { + const entity_ref = decodeURIComponent(request.params.ref); + const data = await dbHandler.getMembers(entity_ref); + + if (data?.length) { + response.json({ status: 'ok', data: data }); + } else { + response.json({ status: 'ok', data: [] }); + } + }); + + router.put('/member', async (request, response) => { + const { user_id, entity_ref, picture } = request.body; + await dbHandler.addMember(user_id, entity_ref, picture); + + response.json({ status: 'ok' }); + }); + + router.delete('/member/:ref/:id', async (request, response) => { + const { ref, id } = request.params; + + const count = await dbHandler.deleteMember(id, ref); + + if (count) { + response.json({ status: 'ok' }); + } else { + response.status(404).json({ message: 'Record not found' }); + } + }); + + router.get('/metadata/:ref', async (request, response) => { + const ref = decodeURIComponent(request.params.ref); + + const data = await dbHandler.getMetadata(ref); + response.json({ status: 'ok', data: data }); + }); + + router.get('/entities', async (_, response) => { + const data = await dbHandler.getEntities(); + + response.json({ status: 'ok', data: data }); + }); + + router.put('/metadata', async (request, response) => { + const bazaarProject = request.body; + + const count = await dbHandler.updateMetadata(bazaarProject); + + if (count) { + response.json({ status: 'ok' }); + } else { + await dbHandler.insertMetadata(bazaarProject); + response.json({ status: 'ok' }); + } + }); + + router.delete('/metadata/:ref', async (request, response) => { + const ref = decodeURIComponent(request.params.ref); + + const count = await dbHandler.deleteMetadata(ref); + + if (count) { + response.json({ status: 'ok' }); + } else { + response.status(404).json({ message: 'Record not found' }); + } + }); + + router.use(errorHandler()); + return router; +} diff --git a/plugins/bazaar-backend/src/service/standaloneServer.ts b/plugins/bazaar-backend/src/service/standaloneServer.ts new file mode 100644 index 0000000000..b229f5bcf8 --- /dev/null +++ b/plugins/bazaar-backend/src/service/standaloneServer.ts @@ -0,0 +1,72 @@ +/* + * 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. + */ + +import { + createServiceBuilder, + loadBackendConfig, + useHotMemoize, +} from '@backstage/backend-common'; +import { Server } from 'http'; +import { Logger } from 'winston'; +import { createRouter } from './router'; +import knexFactory from 'knex'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const logger = options.logger.child({ service: 'bazaar-backend' }); + const config = await loadBackendConfig({ logger, argv: process.argv }); + + const db = useHotMemoize(module, () => { + const knex = knexFactory({ + client: 'sqlite3', + connection: ':memory:', + useNullAsDefault: true, + }); + + knex.client.pool.on('createSuccess', (_eventId: any, resource: any) => { + resource.run('PRAGMA foreign_keys = ON', () => {}); + }); + + return knex; + }); + + const router = await createRouter({ + logger, + database: { getClient: async () => db }, + config: config, + }); + + let service = createServiceBuilder(module) + .setPort(options.port) + .addRouter('/bazaar', router); + if (options.enableCors) { + service = service.enableCors({ origin: 'http://localhost:3000' }); + } + + return await service.start().catch(err => { + logger.error(err); + process.exit(1); + }); +} + +module.hot?.accept(); diff --git a/plugins/welcome/src/setupTests.ts b/plugins/bazaar-backend/src/setupTests.ts similarity index 94% rename from plugins/welcome/src/setupTests.ts rename to plugins/bazaar-backend/src/setupTests.ts index 963c0f188b..d3232290a7 100644 --- a/plugins/welcome/src/setupTests.ts +++ b/plugins/bazaar-backend/src/setupTests.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -import '@testing-library/jest-dom'; +export {}; diff --git a/plugins/welcome/.eslintrc.js b/plugins/bazaar/.eslintrc.js similarity index 100% rename from plugins/welcome/.eslintrc.js rename to plugins/bazaar/.eslintrc.js diff --git a/plugins/bazaar/README.md b/plugins/bazaar/README.md new file mode 100644 index 0000000000..cd26f01997 --- /dev/null +++ b/plugins/bazaar/README.md @@ -0,0 +1,127 @@ +# @backstage/plugin-bazaar + +### What is the Bazaar? + +The Bazaar is a place where teams can propose projects for cross-functional team development. Essentially a marketplace for internal projects suitable for [Inner Sourcing](https://en.wikipedia.org/wiki/Inner_source). With "Inner Sourcing", we mean projects that are developed internally within a company, but with Open Source best practices. + +### Why? + +Many companies today are of high need to increase the ease of cross-team cooperation. In large organizations, engineers often have limited ways of discovering or announcing the projects which could benefit from a wider development effort in terms of different expertise, experiences, and teams spread across the organization. With no good way to find these existing internal projects to join, the possibility of working with Inner Sourcing practices suffers. + +### How? + +The Bazaar allows engineers and teams to open up and announce their new and exciting projects for transparent cooperation in other parts of larger organizations. The Bazaar ensures that new Inner Sourcing friendly projects gain visibility through Backstage and a way for interested engineers to show their interest and in the future contribute with their specific skill set. The Bazaar also provides an easy way to manage, catalog, and browse these Inner Sourcing friendly projects and components. + +## Getting Started + +First install the plugin into your app: + +```sh +# From your Backstage root directory +cd packages/app +yarn add @backstage/plugin-bazaar +``` + +Modify your app routes in `packages/app/src/App.tsx` to include the `Bazaar` component exported from the plugin, for example: + +```diff ++ import { BazaarPage } from '@backstage/plugin-bazaar'; + +const routes = ( + + + ... ++ } /> + {/* other routes... */} + +``` + +Add a **Bazaar icon** to the Sidebar to easily access the Bazaar. In `packages/app/src/components/Root.tsx` add: + +```diff ++ import StorefrontIcon from '@material-ui/icons/Storefront'; + + + ++ + {/* ...other sidebar-items */} +``` + +Add a **Bazaar card** to the overview tab on the `packages/app/src/components/catalog/EntityPage.tsx` add: + +```diff ++ import { EntityBazaarInfoCard } from '@backstage/plugin-bazaar'; + +const overviewContent = ( + + + + + ++ ++ ++ + + {/* ...other entity-cards */} +``` + +## How does the Bazaar work? + +### Layout + +The latest modified Bazaar projects are displayed in the Bazaar landing page, located at the Bazaar icon in the sidebar. Each project is represented as a card containing its most relevant data to give an overview of the project. The list of project is paginated. + +![home](media/bazaar_pr_fullscreen.png) + +### Workflow + +To add a project to the Bazaar, you need to create a project with one of the templates in Backstage. Click the add project-button, choose the project and fill in the form. You will be asked to add an announcement for new team members. The purpose of the announcement is for you to present your ideas and what skills you are looking for. Further you need to provide the status of the project. + +When the project is added, you will see the Bazaar information in the Bazaar card on the entity page. There you can join a project, edit or delete it. + +![workflow](media/bazaar_demo.gif) + +### Database + +The metadata related to the Bazaar is stored in a database. Right now there are two tables, one for storing the metadata and one for storing the members of a Bazaar project. + +**metadata**: + +- name - name of the entity +- entity_ref - namespace/kind/name of the entity +- announcement - announcement of the project and its current need of skills/team member +- status - status of the project, 'proposed' or 'ongoing' +- updated_at - date when the Bazaar information was last modified (ISO 8601 format) + +**members**: + +- entity_ref - namespace/kind/name of the entity +- user_name +- join_date - date when the user joined the project (ISO 8601 format) + +## Future work and ideas + +- Workflow + + - Make it possible to add a Bazaar project without linking it to a Backstage entity, this would make it easier to just add an idea to the Bazaar. + +- Bazaar landing page + + - Add a tab 'My page', where your personal data is displayed. For example: your projects and its latest activities, projects or tags you are following etc. + - Make it possible to sort the project based on the number of members + +- Bazaar card + + - Make it possible to follow tags/projects + +- Bazaar tab on the EntityPage + + - Fill Bazaar-tab with more content, for example images and achievements + - Show all the members that have joined the project + +- Dialogues + + - Extend the dialogue for adding a project with more fields, e.g. the possibility to add images + +- Testing + - Add tests to all the components diff --git a/plugins/bazaar/api-report.md b/plugins/bazaar/api-report.md new file mode 100644 index 0000000000..de94d7a48e --- /dev/null +++ b/plugins/bazaar/api-report.md @@ -0,0 +1,32 @@ +## API Report File for "@backstage/plugin-bazaar" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { RouteRef } from '@backstage/core-plugin-api'; + +// Warning: (ae-missing-release-tag) "BazaarPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const BazaarPage: () => JSX.Element; + +// Warning: (ae-missing-release-tag) "bazaarPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const bazaarPlugin: BackstagePlugin< + { + root: RouteRef; + }, + {} +>; + +// Warning: (ae-missing-release-tag) "EntityBazaarInfoCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const EntityBazaarInfoCard: () => JSX.Element | null; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/welcome/dev/index.tsx b/plugins/bazaar/dev/index.tsx similarity index 78% rename from plugins/welcome/dev/index.tsx rename to plugins/bazaar/dev/index.tsx index 5d4a5c1587..a4509bc1fc 100644 --- a/plugins/welcome/dev/index.tsx +++ b/plugins/bazaar/dev/index.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * 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. @@ -16,12 +16,12 @@ import React from 'react'; import { createDevApp } from '@backstage/dev-utils'; -import { welcomePlugin, WelcomePage } from '../src/plugin'; +import { bazaarPlugin, BazaarPage } from '../src/plugin'; createDevApp() - .registerPlugin(welcomePlugin) + .registerPlugin(bazaarPlugin) .addPage({ - title: 'Welcome', - element: , + element: , + title: 'Root Page', }) .render(); diff --git a/plugins/bazaar/media/bazaar_demo.gif b/plugins/bazaar/media/bazaar_demo.gif new file mode 100644 index 0000000000..c5383de477 Binary files /dev/null and b/plugins/bazaar/media/bazaar_demo.gif differ diff --git a/plugins/bazaar/media/bazaar_pr_fullscreen.png b/plugins/bazaar/media/bazaar_pr_fullscreen.png new file mode 100644 index 0000000000..d0187977db Binary files /dev/null and b/plugins/bazaar/media/bazaar_pr_fullscreen.png differ diff --git a/plugins/welcome/package.json b/plugins/bazaar/package.json similarity index 57% rename from plugins/welcome/package.json rename to plugins/bazaar/package.json index a286857ccc..81472eff11 100644 --- a/plugins/welcome/package.json +++ b/plugins/bazaar/package.json @@ -1,59 +1,48 @@ { - "name": "@backstage/plugin-welcome", - "description": "An old Backstage plugin that provides a welcome page", - "version": "0.3.8", + "name": "@backstage/plugin-bazaar", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", - "private": false, "license": "Apache-2.0", + "private": false, "publishConfig": { "access": "public", "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, - "homepage": "https://backstage.io", - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage", - "directory": "plugins/welcome" - }, - "keywords": [ - "backstage" - ], "scripts": { "build": "backstage-cli plugin:build", + "start": "backstage-cli plugin:serve", "lint": "backstage-cli lint", "test": "backstage-cli test", "diff": "backstage-cli plugin:diff", "prepack": "backstage-cli prepack", "postpack": "backstage-cli postpack", - "clean": "backstage-cli clean", - "start": "backstage-cli plugin:serve" + "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/catalog-model": "^0.9.0", + "@backstage/cli": "^0.7.16", "@backstage/core-components": "^0.7.0", "@backstage/core-plugin-api": "^0.1.10", - "@backstage/theme": "^0.2.11", + "@backstage/plugin-catalog": "^0.7.0", + "@backstage/plugin-catalog-react": "^0.5.0", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", + "@testing-library/jest-dom": "^5.10.1", + "luxon": "^2.0.2", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-hook-form": "^7.13.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "^0.7.16", - "@backstage/core-app-api": "^0.1.17", "@backstage/dev-utils": "^0.2.12", - "@backstage/test-utils": "^0.1.19", "@testing-library/jest-dom": "^5.10.1", - "@testing-library/react": "^11.2.5", - "@testing-library/user-event": "^13.1.8", - "@types/jest": "^26.0.7", - "@types/node": "^14.14.32", - "cross-fetch": "^3.0.6", - "msw": "^0.29.0" + "cross-fetch": "^3.0.6" }, "files": [ "dist" diff --git a/plugins/bazaar/src/api.ts b/plugins/bazaar/src/api.ts new file mode 100644 index 0000000000..db20e5e0cb --- /dev/null +++ b/plugins/bazaar/src/api.ts @@ -0,0 +1,141 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; +import { + createApiRef, + DiscoveryApi, + IdentityApi, +} from '@backstage/core-plugin-api'; +import { BazaarProject } from './types'; + +export const bazaarApiRef = createApiRef({ + id: 'bazaar', + description: 'Used to make requests towards the bazaar backend', +}); + +export interface BazaarApi { + updateMetadata(bazaarProject: BazaarProject): Promise; + + getMetadata(entity: Entity): Promise; + + getMembers(entity: Entity): Promise; + + deleteMember(entity: Entity): Promise; + + addMember(entity: Entity): Promise; + + getEntities(): Promise; + + deleteEntity(bazaarProject: BazaarProject): Promise; +} + +export class BazaarClient implements BazaarApi { + private readonly identityApi: IdentityApi; + private readonly discoveryApi: DiscoveryApi; + + constructor(options: { + identityApi: IdentityApi; + discoveryApi: DiscoveryApi; + }) { + this.identityApi = options.identityApi; + this.discoveryApi = options.discoveryApi; + } + + async updateMetadata(bazaarProject: BazaarProject): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('bazaar'); + + return await fetch(`${baseUrl}/metadata`, { + method: 'PUT', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(bazaarProject), + }).then(resp => resp.json()); + } + + async getMetadata(entity: Entity): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('bazaar'); + + const response = await fetch( + `${baseUrl}/metadata/${encodeURIComponent(stringifyEntityRef(entity))}`, + { + method: 'GET', + }, + ); + + return response.ok ? response : null; + } + + async getMembers(entity: Entity): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('bazaar'); + + return await fetch( + `${baseUrl}/members/${encodeURIComponent(stringifyEntityRef(entity))}`, + { + method: 'GET', + }, + ).then(resp => resp.json()); + } + + async addMember(entity: Entity): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('bazaar'); + + await fetch(`${baseUrl}/member`, { + method: 'PUT', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + entity_ref: stringifyEntityRef(entity), + user_id: this.identityApi.getUserId(), + picture: this.identityApi.getProfile()?.picture, + }), + }); + } + + async deleteMember(entity: Entity): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('bazaar'); + + await fetch( + `${baseUrl}/member/${encodeURIComponent( + stringifyEntityRef(entity), + )}/${this.identityApi.getUserId()}`, + { + method: 'DELETE', + }, + ); + } + + async getEntities(): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('bazaar'); + + return await fetch(`${baseUrl}/entities`, { + method: 'GET', + }).then(resp => resp.json()); + } + + async deleteEntity(bazaarProject: BazaarProject): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('bazaar'); + const entityRef = bazaarProject.entityRef as string; + + await fetch(`${baseUrl}/metadata/${encodeURIComponent(entityRef)}`, { + method: 'DELETE', + }); + } +} diff --git a/plugins/bazaar/src/components/About/About.tsx b/plugins/bazaar/src/components/About/About.tsx new file mode 100644 index 0000000000..d9c529a209 --- /dev/null +++ b/plugins/bazaar/src/components/About/About.tsx @@ -0,0 +1,78 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { Grid, Typography, Link, makeStyles } from '@material-ui/core'; +import { InfoCard } from '@backstage/core-components'; + +const useStyles = makeStyles({ + subheader: { + fontWeight: 'bold', + }, +}); + +export const About = () => { + const classes = useStyles(); + return ( + + + + + What is the Bazaar? + + + The Bazaar is a place where teams can propose projects for + cross-functional team development. Essentially a marketplace for + internal projects suitable for{' '} + + Inner Sourcing + + . With "Inner Sourcing", we mean projects that are developed + internally within a company, but with Open Source best practices. + + + Why? + + + Many companies today are of high need to increase the ease of + cross-team cooperation. In large organizations, engineers often have + limited ways of discovering or announcing the projects which could + benefit from a wider development effort in terms of different + expertise, experiences, and teams spread across the organization. + With no good way to find these existing internal projects to join, + the possibility of working with Inner Sourcing practices suffers. + + + How? + + + The Bazaar allows engineers and teams to open up and announce their + new and exciting projects for transparent cooperation in other parts + of larger organizations. The Bazaar ensures that new Inner Sourcing + friendly projects gain visibility through Backstage and a way for + interested engineers to show their interest and in the future + contribute with their specific skill set. The Bazaar also provides + an easy way to manage, catalog, and browse these Inner Sourcing + friendly projects and components. + + + + + ); +}; diff --git a/plugins/bazaar/src/components/About/index.ts b/plugins/bazaar/src/components/About/index.ts new file mode 100644 index 0000000000..da078d2ce7 --- /dev/null +++ b/plugins/bazaar/src/components/About/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { About } from './About'; diff --git a/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx b/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx new file mode 100644 index 0000000000..82d682b4df --- /dev/null +++ b/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx @@ -0,0 +1,108 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useState, useEffect } from 'react'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; +import { SubmitHandler } from 'react-hook-form'; +import { useApi } from '@backstage/core-plugin-api'; +import { ProjectDialog } from '../ProjectDialog'; +import { ProjectSelector } from '../ProjectSelector'; +import { BazaarProject, FormValues, Status } from '../../types'; +import { bazaarApiRef } from '../../api'; + +type Props = { + catalogEntities: Entity[]; + open: boolean; + handleClose: () => void; + fetchBazaarProjects: () => Promise; + fetchCatalogEntities: () => Promise; +}; + +export const AddProjectDialog = ({ + catalogEntities, + open, + handleClose, + fetchBazaarProjects, + fetchCatalogEntities, +}: Props) => { + const bazaarApi = useApi(bazaarApiRef); + const [selectedEntity, setSelectedEntity] = useState( + catalogEntities ? catalogEntities[0] : null, + ); + + useEffect(() => { + setSelectedEntity(catalogEntities ? catalogEntities[0] : null); + }, [catalogEntities]); + + const defaultValues = { + title: 'Add project', + community: '', + announcement: '', + status: 'proposed' as Status, + }; + + const handleListItemClick = (entity: Entity) => { + setSelectedEntity(entity); + }; + + const handleCloseDialog = () => { + setSelectedEntity(catalogEntities ? catalogEntities[0] : null); + handleClose(); + }; + + const handleSave: SubmitHandler = async ( + getValues: any, + reset: any, + ) => { + const formValues = getValues(); + + if (selectedEntity) { + await bazaarApi.updateMetadata({ + name: selectedEntity.metadata.name, + entityRef: stringifyEntityRef(selectedEntity), + announcement: formValues.announcement, + status: formValues.status, + community: formValues.community, + membersCount: 0, + } as BazaarProject); + + fetchBazaarProjects(); + fetchCatalogEntities(); + + handleClose(); + reset(defaultValues); + } + }; + + return ( + + } + handleClose={handleCloseDialog} + /> + ); +}; diff --git a/plugins/bazaar/src/components/AddProjectDialog/index.ts b/plugins/bazaar/src/components/AddProjectDialog/index.ts new file mode 100644 index 0000000000..7974186744 --- /dev/null +++ b/plugins/bazaar/src/components/AddProjectDialog/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { AddProjectDialog } from './AddProjectDialog'; diff --git a/plugins/bazaar/src/components/AlertBanner/AlertBanner.tsx b/plugins/bazaar/src/components/AlertBanner/AlertBanner.tsx new file mode 100644 index 0000000000..1b6c1331c3 --- /dev/null +++ b/plugins/bazaar/src/components/AlertBanner/AlertBanner.tsx @@ -0,0 +1,51 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { Snackbar, IconButton } from '@material-ui/core'; +import CloseIcon from '@material-ui/icons/Close'; +import { Alert } from '@material-ui/lab'; + +type Props = { + open: boolean; + message: JSX.Element; + handleClose: () => void; +}; + +export const AlertBanner = ({ open, message, handleClose }: Props) => { + return ( + + + + + } + > + {message} + + + ); +}; diff --git a/plugins/bazaar/src/components/AlertBanner/index.ts b/plugins/bazaar/src/components/AlertBanner/index.ts new file mode 100644 index 0000000000..1728cab9fc --- /dev/null +++ b/plugins/bazaar/src/components/AlertBanner/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { AlertBanner } from './AlertBanner'; diff --git a/plugins/bazaar/src/components/DeleteProjectDialog/DeleteProjectDialog.tsx b/plugins/bazaar/src/components/DeleteProjectDialog/DeleteProjectDialog.tsx new file mode 100644 index 0000000000..737fa437da --- /dev/null +++ b/plugins/bazaar/src/components/DeleteProjectDialog/DeleteProjectDialog.tsx @@ -0,0 +1,139 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { Dispatch, SetStateAction } from 'react'; +import { + createStyles, + Theme, + withStyles, + WithStyles, +} from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import MuiDialogTitle from '@material-ui/core/DialogTitle'; +import MuiDialogContent from '@material-ui/core/DialogContent'; +import MuiDialogActions from '@material-ui/core/DialogActions'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; +import Typography from '@material-ui/core/Typography'; +import { useApi } from '@backstage/core-plugin-api'; +import { bazaarApiRef } from '../../api'; +import { BazaarProject } from '../../types'; + +const styles = (theme: Theme) => + createStyles({ + root: { + margin: 0, + padding: theme.spacing(2), + }, + closeButton: { + position: 'absolute', + right: theme.spacing(1), + top: theme.spacing(1), + color: theme.palette.grey[500], + }, + }); + +/* + DialogTitleProps, DialogTitle, DialogContent and DialogActions + are copied from the git-release plugin +*/ +export interface DialogTitleProps extends WithStyles { + id: string; + children: React.ReactNode; + onClose: () => void; +} + +const DialogTitle = withStyles(styles)((props: DialogTitleProps) => { + const { children, classes, onClose, ...other } = props; + return ( + + {children} + {onClose ? ( + + + + ) : null} + + ); +}); + +const DialogContent = withStyles((theme: Theme) => ({ + root: { + padding: theme.spacing(2), + }, +}))(MuiDialogContent); + +const DialogActions = withStyles((theme: Theme) => ({ + root: { + margin: 0, + padding: theme.spacing(1), + }, +}))(MuiDialogActions); + +type Props = { + bazaarProject: BazaarProject; + openDelete: boolean; + handleClose: () => void; + setIsBazaar: Dispatch>; +}; + +export const DeleteProjectDialog = ({ + bazaarProject, + openDelete, + handleClose, + setIsBazaar, +}: Props) => { + const handleCloseAndClear = () => { + handleClose(); + }; + + const bazaarApi = useApi(bazaarApiRef); + + const handleSubmit = async () => { + await bazaarApi.deleteEntity(bazaarProject); + setIsBazaar(false); + handleCloseAndClear(); + }; + + return ( + + + Delete project + + + + Are you sure you want to delete this project from the Bazaar? + + + + + + + ); +}; diff --git a/plugins/bazaar/src/components/DeleteProjectDialog/index.ts b/plugins/bazaar/src/components/DeleteProjectDialog/index.ts new file mode 100644 index 0000000000..fdd488aeb9 --- /dev/null +++ b/plugins/bazaar/src/components/DeleteProjectDialog/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { DeleteProjectDialog } from './DeleteProjectDialog'; diff --git a/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx b/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx new file mode 100644 index 0000000000..172bdf548d --- /dev/null +++ b/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx @@ -0,0 +1,82 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useState, useEffect } from 'react'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; +import { useApi } from '@backstage/core-plugin-api'; +import { ProjectDialog } from '../ProjectDialog'; +import { BazaarProject, FormValues } from '../../types'; +import { bazaarApiRef } from '../../api'; + +type Props = { + entity: Entity; + bazaarProject: BazaarProject; + fetchBazaarProject: () => Promise; + open: boolean; + handleClose: () => void; + isAddForm: boolean; +}; + +export const EditProjectDialog = ({ + entity, + bazaarProject, + fetchBazaarProject, + open, + handleClose, +}: Props) => { + const [defaultValues, setDefaultValues] = useState({ + announcement: bazaarProject.announcement, + community: bazaarProject.community, + status: bazaarProject.status, + }); + + const bazaarApi = useApi(bazaarApiRef); + + useEffect(() => { + setDefaultValues({ + announcement: bazaarProject.announcement, + community: bazaarProject.community, + status: bazaarProject.status, + }); + }, [bazaarProject]); + + const handleSave: any = async (getValues: any, _: any) => { + const formValues = getValues(); + + const updateResponse = await bazaarApi.updateMetadata({ + name: entity.metadata.name, + entityRef: stringifyEntityRef(entity), + announcement: formValues.announcement, + status: formValues.status, + community: formValues.community, + membersCount: bazaarProject.membersCount, + }); + + if (updateResponse.status === 'ok') fetchBazaarProject(); + handleClose(); + }; + + return ( + + ); +}; diff --git a/plugins/bazaar/src/components/EditProjectDialog/index.ts b/plugins/bazaar/src/components/EditProjectDialog/index.ts new file mode 100644 index 0000000000..4c48c375ce --- /dev/null +++ b/plugins/bazaar/src/components/EditProjectDialog/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { EditProjectDialog } from './EditProjectDialog'; diff --git a/plugins/bazaar/src/components/EntityBazaarInfoCard/EntityBazaarInfoCard.tsx b/plugins/bazaar/src/components/EntityBazaarInfoCard/EntityBazaarInfoCard.tsx new file mode 100644 index 0000000000..6bf844d592 --- /dev/null +++ b/plugins/bazaar/src/components/EntityBazaarInfoCard/EntityBazaarInfoCard.tsx @@ -0,0 +1,324 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useState, useEffect } from 'react'; +import { + Grid, + makeStyles, + Card, + CardContent, + CardHeader, + Typography, + Divider, + IconButton, + Popover, + MenuList, + MenuItem, + ListItemText, + Link, +} from '@material-ui/core'; +import { + Progress, + HeaderIconLinkRow, + IconLinkVerticalProps, + Avatar, +} from '@backstage/core-components'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { AboutField } from '@backstage/plugin-catalog'; +import { StatusTag } from '../StatusTag'; +import EditIcon from '@material-ui/icons/Edit'; +import ChatIcon from '@material-ui/icons/Chat'; +import PersonAddIcon from '@material-ui/icons/PersonAdd'; +import MoreVertIcon from '@material-ui/icons/MoreVert'; +import DeleteIcon from '@material-ui/icons/Delete'; +import { EditProjectDialog } from '../EditProjectDialog'; +import { DeleteProjectDialog } from '../DeleteProjectDialog'; +import ExitToAppIcon from '@material-ui/icons/ExitToApp'; +import { useApi, identityApiRef } from '@backstage/core-plugin-api'; +import { Member, BazaarProject } from '../../types'; +import { bazaarApiRef } from '../../api'; +import { Alert } from '@material-ui/lab'; +import { useAsyncFn } from 'react-use'; + +const useStyles = makeStyles({ + description: { + wordBreak: 'break-word', + }, + icon: { + marginRight: '1.75rem', + }, + link: { + color: '#9cc9ff', + '&:hover': { + textDecoration: 'underline', + }, + }, + memberLink: { + display: 'block', + marginBottom: '0.3rem', + }, +}); + +const sortMembers = (m1: Member, m2: Member) => { + return new Date(m2.joinDate!).getTime() - new Date(m1.joinDate!).getTime(); +}; + +export const EntityBazaarInfoCard = () => { + const { entity } = useEntity(); + const classes = useStyles(); + const bazaarApi = useApi(bazaarApiRef); + const identity = useApi(identityApiRef); + const [anchorEl, setAnchorEl] = useState(); + const [open, setOpen] = useState(false); + const [popoverOpen, setPopoverOpen] = useState(false); + const [openDelete, setOpenDelete] = useState(false); + const [isMember, setIsMember] = useState(false); + const [isBazaar, setIsBazaar] = useState(false); + const [members, fetchMembers] = useAsyncFn(async () => { + const response = await bazaarApi.getMembers(entity); + + const dbMembers = response.data.map((obj: any) => { + const member: Member = { + userId: obj.user_id, + entityRef: obj.entity_ref, + joinDate: obj.join_date, + picture: obj.picture, + }; + + return member; + }); + + dbMembers.sort(sortMembers); + + return dbMembers; + }); + + const [bazaarProject, fetchBazaarProject] = useAsyncFn(async () => { + const response = await bazaarApi.getMetadata(entity); + + if (response) { + const metadata = await response.json().then((resp: any) => resp.data[0]); + + if (metadata) { + return { + entityRef: metadata.entity_ref, + name: metadata.name, + community: metadata.community, + announcement: metadata.announcement, + status: metadata.status, + updatedAt: metadata.updated_at, + membersCount: metadata.members_count, + } as BazaarProject; + } + } + return null; + }); + + useEffect(() => { + fetchMembers(); + fetchBazaarProject(); + }, [fetchMembers, fetchBazaarProject]); + + useEffect(() => { + const isBazaarMember = + members?.value + ?.map((member: Member) => member.userId) + .indexOf(identity.getUserId()) >= 0; + const isBazaarProject = bazaarProject.value !== null; + + setIsMember(isBazaarMember); + setIsBazaar(isBazaarProject); + }, [bazaarProject, members, identity]); + + const onOpen = (event: React.SyntheticEvent) => { + setAnchorEl(event.currentTarget); + setPopoverOpen(true); + }; + + const closeEdit = () => { + setOpen(false); + }; + + const closeDelete = () => { + setOpenDelete(false); + }; + + const popoverCloseHandler = () => { + setPopoverOpen(false); + }; + + const handleMembersClick = async () => { + if (!isMember) { + await bazaarApi.addMember(entity); + } else { + await bazaarApi.deleteMember(entity); + } + + fetchMembers(); + fetchBazaarProject(); + }; + + const links: IconLinkVerticalProps[] = [ + { + label: isMember ? 'Leave' : 'Join', + icon: isMember ? : , + href: '', + onClick: async () => { + handleMembersClick(); + }, + }, + { + label: 'Community', + icon: , + href: bazaarProject?.value?.community, + disabled: !bazaarProject?.value?.community || !isMember, + }, + ]; + + if (!isBazaar) { + return null; + } else if (bazaarProject.loading || members.loading) { + return ; + } else if (bazaarProject.error) { + return {bazaarProject?.error?.message}; + } else if (members.error) { + return {members?.error?.message}; + } + return ( + + {bazaarProject?.value && ( + + )} + + {bazaarProject?.value && ( + + )} + + + + } + subheader={} + /> + + + + + { + setOpen(true); + setPopoverOpen(false); + }} + > + + + + + { + setOpenDelete(true); + setPopoverOpen(false); + }} + > + + + + + + + + + {bazaarProject?.value?.announcement + ? bazaarProject?.value?.announcement + .split('\n') + .map((str: string, i: number) => ( + + {str} + + )) + : 'No announcement'} + + + + + + + + + + + {' '} + + {members?.value?.length ? ( + members.value.slice(0, 3).map((member: Member) => { + return ( +
+ + + {member?.userId} + +
+ ); + }) + ) : ( +
+ )} + + + + + + ); +}; diff --git a/plugins/bazaar/src/components/EntityBazaarInfoCard/index.ts b/plugins/bazaar/src/components/EntityBazaarInfoCard/index.ts new file mode 100644 index 0000000000..89eee92f11 --- /dev/null +++ b/plugins/bazaar/src/components/EntityBazaarInfoCard/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { EntityBazaarInfoCard } from './EntityBazaarInfoCard'; diff --git a/plugins/bazaar/src/components/HomePage/HomePage.tsx b/plugins/bazaar/src/components/HomePage/HomePage.tsx new file mode 100644 index 0000000000..32a16291fc --- /dev/null +++ b/plugins/bazaar/src/components/HomePage/HomePage.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { Header, RoutedTabs } from '@backstage/core-components'; +import { Route } from 'react-router-dom'; +import { SortView } from '../SortView'; +import { About } from '../About'; + +export const HomePage = () => { + const tabContent = [ + { + path: '/', + title: 'Home', + children: } />, + }, + { + path: '/about', + title: 'About', + children: } />, + }, + ]; + + return ( +
+
+ +
+ ); +}; diff --git a/plugins/bazaar/src/components/HomePage/index.ts b/plugins/bazaar/src/components/HomePage/index.ts new file mode 100644 index 0000000000..41d7bc5d03 --- /dev/null +++ b/plugins/bazaar/src/components/HomePage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { HomePage } from './HomePage'; diff --git a/plugins/bazaar/src/components/InputField/InputField.tsx b/plugins/bazaar/src/components/InputField/InputField.tsx new file mode 100644 index 0000000000..4d284ff82c --- /dev/null +++ b/plugins/bazaar/src/components/InputField/InputField.tsx @@ -0,0 +1,63 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { Controller, Control, FieldError } from 'react-hook-form'; +import { TextField } from '@material-ui/core'; +import { FormValues } from '../../types'; + +type Props = { + inputType: 'announcement' | 'community'; + error?: FieldError | undefined; + control: Control; + helperText?: string; + placeholder?: string; + rules?: Object; +}; + +export const InputField = ({ + inputType, + error, + control, + helperText, + placeholder, + rules, +}: Props) => { + const label = + inputType.charAt(0).toLocaleUpperCase('en-US') + inputType.slice(1); + + return ( + ( + + )} + /> + ); +}; diff --git a/plugins/bazaar/src/components/InputField/index.ts b/plugins/bazaar/src/components/InputField/index.ts new file mode 100644 index 0000000000..d8c82eecbd --- /dev/null +++ b/plugins/bazaar/src/components/InputField/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { InputField } from './InputField'; diff --git a/plugins/bazaar/src/components/InputSelector/InputSelector.tsx b/plugins/bazaar/src/components/InputSelector/InputSelector.tsx new file mode 100644 index 0000000000..a41cedfcf3 --- /dev/null +++ b/plugins/bazaar/src/components/InputSelector/InputSelector.tsx @@ -0,0 +1,75 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import InputLabel from '@material-ui/core/InputLabel'; +import MenuItem from '@material-ui/core/MenuItem'; +import FormControl from '@material-ui/core/FormControl'; +import Select from '@material-ui/core/Select'; +import { Controller, Control, FieldError } from 'react-hook-form'; +import { FormValues } from '../../types'; + +type Props = { + options: string[]; + control: Control; + name: 'announcement' | 'status'; + error?: FieldError | undefined; +}; + +export const InputSelector = ({ name, options, control, error }: Props) => { + const label = name.charAt(0).toLocaleUpperCase('en-US') + name.slice(1); + + return ( + ( + + + {label} + + + + )} + /> + ); +}; diff --git a/plugins/bazaar/src/components/InputSelector/index.ts b/plugins/bazaar/src/components/InputSelector/index.ts new file mode 100644 index 0000000000..a4327991b0 --- /dev/null +++ b/plugins/bazaar/src/components/InputSelector/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { InputSelector } from './InputSelector'; diff --git a/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx b/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx new file mode 100644 index 0000000000..b3c8b3e2e7 --- /dev/null +++ b/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx @@ -0,0 +1,98 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { ItemCardHeader } from '@backstage/core-components'; +import { + Card, + CardActionArea, + CardContent, + makeStyles, + Typography, +} from '@material-ui/core'; +import { StatusTag } from '../StatusTag/StatusTag'; +import { Link as RouterLink } from 'react-router-dom'; +import { catalogRouteRef } from '@backstage/plugin-catalog-react'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import { BazaarProject } from '../../types'; +import { parseEntityName } from '@backstage/catalog-model'; +import { DateTime } from 'luxon'; + +const useStyles = makeStyles({ + statusTag: { + display: 'inline-block', + whiteSpace: 'nowrap', + marginBottom: '0.5rem', + }, + announcement: { + display: '-webkit-box', + WebkitLineClamp: 5, + WebkitBoxOrient: 'vertical', + marginBottom: '0.8rem', + overflow: 'hidden', + }, + memberCount: { + float: 'right', + }, +}); + +type Props = { + bazaarProject: BazaarProject; +}; + +export const ProjectCard = ({ bazaarProject }: Props) => { + const classes = useStyles(); + const { entityRef, name, status, updatedAt, announcement, membersCount } = + bazaarProject; + const catalogLink = useRouteRef(catalogRouteRef); + const { namespace, kind } = parseEntityName(entityRef); + + return ( + + + + + + + {membersCount === 1 + ? `${membersCount} member` + : `${membersCount} members`} + +
+ + {announcement} + +
+
+
+
+ ); +}; diff --git a/plugins/bazaar/src/components/ProjectCard/index.ts b/plugins/bazaar/src/components/ProjectCard/index.ts new file mode 100644 index 0000000000..00e62bebba --- /dev/null +++ b/plugins/bazaar/src/components/ProjectCard/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { ProjectCard } from './ProjectCard'; diff --git a/plugins/bazaar/src/components/ProjectDialog/ProjectDialog.tsx b/plugins/bazaar/src/components/ProjectDialog/ProjectDialog.tsx new file mode 100644 index 0000000000..cb76984f44 --- /dev/null +++ b/plugins/bazaar/src/components/ProjectDialog/ProjectDialog.tsx @@ -0,0 +1,186 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { + createStyles, + Theme, + withStyles, + WithStyles, +} from '@material-ui/core/styles'; +import MuiDialogTitle from '@material-ui/core/DialogTitle'; +import MuiDialogContent from '@material-ui/core/DialogContent'; +import MuiDialogActions from '@material-ui/core/DialogActions'; +import CloseIcon from '@material-ui/icons/Close'; +import { Button, Dialog, Typography, IconButton } from '@material-ui/core'; +import { useForm, SubmitHandler } from 'react-hook-form'; +import { InputField } from '../InputField/InputField'; +import { InputSelector } from '../InputSelector/InputSelector'; +import { FormValues } from '../../types'; + +const styles = (theme: Theme) => + createStyles({ + root: { + margin: 0, + padding: theme.spacing(2), + }, + closeButton: { + position: 'absolute', + right: theme.spacing(1), + top: theme.spacing(1), + color: theme.palette.grey[500], + }, + }); + +/* + DialogTitleProps, DialogTitle, DialogContent and DialogActions + are copied from the git-release plugin +*/ +export interface DialogTitleProps extends WithStyles { + id: string; + children: React.ReactNode; + onClose: () => void; +} + +const DialogTitle = withStyles(styles)((props: DialogTitleProps) => { + const { children, classes, onClose, ...other } = props; + return ( + + {children} + {onClose ? ( + + + + ) : null} + + ); +}); + +const DialogContent = withStyles((theme: Theme) => ({ + root: { + padding: theme.spacing(2), + }, +}))(MuiDialogContent); + +const DialogActions = withStyles((theme: Theme) => ({ + root: { + margin: 0, + padding: theme.spacing(1), + }, +}))(MuiDialogActions); + +type Props = { + handleSave: (getValues: any, reset: any) => SubmitHandler; + isAddForm: boolean; + title: string; + defaultValues: FormValues; + open: boolean; + projectSelector?: JSX.Element; + handleClose: () => void; +}; + +export const ProjectDialog = ({ + handleSave, + isAddForm, + title, + defaultValues, + open, + projectSelector, + handleClose, +}: Props) => { + const { + handleSubmit, + reset, + control, + getValues, + formState: { errors }, + } = useForm({ + mode: 'onChange', + defaultValues: defaultValues, + }); + + const handleCloseAndClear = () => { + handleClose(); + reset(defaultValues); + }; + + const handleSaveProject = () => { + handleSave(getValues, reset); + }; + + return ( +
+ + + {title} + + + + {isAddForm && projectSelector} + + + + + + + + + + + + +
+ ); +}; diff --git a/plugins/bazaar/src/components/ProjectDialog/index.ts b/plugins/bazaar/src/components/ProjectDialog/index.ts new file mode 100644 index 0000000000..bd85b6576b --- /dev/null +++ b/plugins/bazaar/src/components/ProjectDialog/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { ProjectDialog } from './ProjectDialog'; diff --git a/plugins/bazaar/src/components/ProjectPreview/ProjectPreview.tsx b/plugins/bazaar/src/components/ProjectPreview/ProjectPreview.tsx new file mode 100644 index 0000000000..0b01b86088 --- /dev/null +++ b/plugins/bazaar/src/components/ProjectPreview/ProjectPreview.tsx @@ -0,0 +1,109 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useState } from 'react'; +import { Content } from '@backstage/core-components'; +import { ProjectCard } from '../ProjectCard/ProjectCard'; +import { makeStyles, Grid } from '@material-ui/core'; +import Pagination from '@material-ui/lab/Pagination'; +import { BazaarProject } from '../../types'; + +type Props = { + bazaarProjects: BazaarProject[]; + sortingMethod: (arg0: BazaarProject, arg1: BazaarProject) => number; +}; + +const useStyles = makeStyles({ + content: { + width: '100%', + display: 'flex', + flexWrap: 'wrap', + alignItems: 'center', + }, + item: { + minWidth: '20.5rem', + maxWidth: '20.5rem', + width: '20%', + }, +}); + +export const ProjectPreview = ({ bazaarProjects, sortingMethod }: Props) => { + const classes = useStyles(); + const [currentPage, setCurrentPage] = useState(1); + const pageSize = 10; + const pageCount = Math.ceil(bazaarProjects.length / pageSize); + + const handlePageClick = (_: any, pageIndex: number) => { + setCurrentPage(pageIndex); + }; + + if (!bazaarProjects.length) { + return ( +
+ Please add projects to the Bazaar. +
+ ); + } + + bazaarProjects.sort(sortingMethod); + + return ( + + + {bazaarProjects + .slice(pageSize * (currentPage - 1), pageSize * currentPage) + .map((bazaarProject: BazaarProject) => { + const entityRef = bazaarProject.entityRef; + + return ( + + + + ); + })} + + + + ); +}; diff --git a/plugins/bazaar/src/components/ProjectPreview/index.ts b/plugins/bazaar/src/components/ProjectPreview/index.ts new file mode 100644 index 0000000000..74a954c595 --- /dev/null +++ b/plugins/bazaar/src/components/ProjectPreview/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { ProjectPreview } from './ProjectPreview'; diff --git a/plugins/bazaar/src/components/ProjectSelector/ProjectSelector.tsx b/plugins/bazaar/src/components/ProjectSelector/ProjectSelector.tsx new file mode 100644 index 0000000000..1c2718d403 --- /dev/null +++ b/plugins/bazaar/src/components/ProjectSelector/ProjectSelector.tsx @@ -0,0 +1,54 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { Entity } from '@backstage/catalog-model'; +import { Autocomplete } from '@material-ui/lab'; +import { TextField } from '@material-ui/core'; + +type Props = { + entities: Entity[]; + value: string; + onChange: (entity: Entity) => void; + isFormInvalid: boolean; +}; + +export const ProjectSelector = ({ + entities, + value, + onChange, + isFormInvalid, +}: Props) => { + return ( + option?.metadata?.name} + renderOption={option => {option?.metadata?.name}} + renderInput={params => ( + + )} + onChange={(_, data) => onChange(data!)} + /> + ); +}; diff --git a/plugins/bazaar/src/components/ProjectSelector/index.ts b/plugins/bazaar/src/components/ProjectSelector/index.ts new file mode 100644 index 0000000000..e81b825dbf --- /dev/null +++ b/plugins/bazaar/src/components/ProjectSelector/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { ProjectSelector } from './ProjectSelector'; diff --git a/plugins/bazaar/src/components/SortView/SortView.tsx b/plugins/bazaar/src/components/SortView/SortView.tsx new file mode 100644 index 0000000000..99d3f84ac3 --- /dev/null +++ b/plugins/bazaar/src/components/SortView/SortView.tsx @@ -0,0 +1,183 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useEffect, useState } from 'react'; +import { + Content, + ContentHeader, + SupportButton, + Progress, +} from '@backstage/core-components'; +import { AddProjectDialog } from '../AddProjectDialog'; +import { AlertBanner } from '../AlertBanner'; +import { ProjectPreview } from '../ProjectPreview/ProjectPreview'; +import { Button, makeStyles, Link } from '@material-ui/core'; +import { useAsyncFn } from 'react-use'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; +import { useApi } from '@backstage/core-plugin-api'; +import { catalogApiRef } from '@backstage/plugin-catalog-react'; +import { BazaarProject } from '../../types'; +import { bazaarApiRef } from '../../api'; +import { Alert } from '@material-ui/lab'; + +const useStyles = makeStyles({ + container: { + marginTop: '2rem', + }, +}); + +const filterCatalogEntities = ( + bazaarProjects: BazaarProject[], + catalogEntities: Entity[], +) => { + const bazaarProjectRefs = bazaarProjects.map( + (project: BazaarProject) => project.entityRef, + ); + + const filtered = catalogEntities.filter((entity: Entity) => { + return !bazaarProjectRefs?.includes(stringifyEntityRef(entity)); + }); + + return filtered; +}; + +export const SortView = () => { + const classes = useStyles(); + const [openAdd, setOpenAdd] = useState(false); + const [openNoProjects, setOpenNoProjects] = useState(false); + const bazaarApi = useApi(bazaarApiRef); + const catalogApi = useApi(catalogApiRef); + const [filteredCatalogEntites, setFilteredCatalogEntities] = + useState(); + + const compareProjectsByDate = ( + a: BazaarProject, + b: BazaarProject, + ): number => { + const dateA = new Date(a.updatedAt!).getTime(); + const dateB = new Date(b.updatedAt!).getTime(); + return dateB - dateA; + }; + + const handleCloseNoProjects = () => { + setOpenNoProjects(false); + }; + + const [catalogEntities, fetchCatalogEntities] = useAsyncFn(async () => { + const entities = await catalogApi.getEntities({ + filter: { + kind: ['Component', 'Resource'], + }, + fields: ['kind', 'metadata.name', 'metadata.namespace'], + }); + + return entities.items; + }); + + const [bazaarProjects, fetchBazaarProjects] = useAsyncFn(async () => { + const response = await bazaarApi.getEntities(); + const dbProjects: BazaarProject[] = []; + + response.data.forEach((project: any) => { + dbProjects.push({ + entityRef: project.entity_ref, + name: project.name, + status: project.status, + announcement: project.announcement, + community: project.community, + updatedAt: project.updated_at, + membersCount: project.members_count, + }); + }); + + return dbProjects; + }); + + useEffect(() => { + fetchCatalogEntities(); + fetchBazaarProjects(); + }, [fetchBazaarProjects, fetchCatalogEntities]); + + useEffect(() => { + const filteredCatalogEntities = filterCatalogEntities( + bazaarProjects.value || [], + catalogEntities.value || [], + ); + + if (filteredCatalogEntities) { + setFilteredCatalogEntities(filteredCatalogEntities); + } + }, [bazaarProjects, catalogEntities]); + + if (catalogEntities.loading || bazaarProjects.loading) return ; + + if (catalogEntities.error) + return {catalogEntities.error.message}; + + if (bazaarProjects.error) + return {bazaarProjects.error.message}; + + return ( + + + No project available. Please{' '} + + create a project + {' '} + from a template first. +
+ } + handleClose={handleCloseNoProjects} + /> + + + { + setOpenAdd(false); + }} + open={openAdd} + fetchBazaarProjects={fetchBazaarProjects} + fetchCatalogEntities={fetchCatalogEntities} + /> + + + + + + ); +}; diff --git a/plugins/bazaar/src/components/SortView/index.ts b/plugins/bazaar/src/components/SortView/index.ts new file mode 100644 index 0000000000..086c7c29d1 --- /dev/null +++ b/plugins/bazaar/src/components/SortView/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { SortView } from './SortView'; diff --git a/plugins/bazaar/src/components/StatusTag/StatusTag.tsx b/plugins/bazaar/src/components/StatusTag/StatusTag.tsx new file mode 100644 index 0000000000..eb01ea5350 --- /dev/null +++ b/plugins/bazaar/src/components/StatusTag/StatusTag.tsx @@ -0,0 +1,37 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { StatusOK, StatusWarning } from '@backstage/core-components'; +import { Status } from '../../types'; + +interface StatusComponent { + [key: string]: JSX.Element | undefined; +} + +const statuses: StatusComponent = { + proposed: proposed, + ongoing: ongoing, +}; + +type Props = { + status: Status; + styles?: string; +}; + +export const StatusTag = ({ status, styles }: Props) => { + return
{statuses[status]}
; +}; diff --git a/plugins/bazaar/src/components/StatusTag/index.ts b/plugins/bazaar/src/components/StatusTag/index.ts new file mode 100644 index 0000000000..33886382f8 --- /dev/null +++ b/plugins/bazaar/src/components/StatusTag/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { StatusTag } from './StatusTag'; diff --git a/plugins/bazaar/src/index.ts b/plugins/bazaar/src/index.ts new file mode 100644 index 0000000000..e9b388d736 --- /dev/null +++ b/plugins/bazaar/src/index.ts @@ -0,0 +1,18 @@ +/* + * 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 { bazaarPlugin, BazaarPage } from './plugin'; +export { EntityBazaarInfoCard } from './components/EntityBazaarInfoCard'; diff --git a/plugins/welcome/src/plugin.test.ts b/plugins/bazaar/src/plugin.test.ts similarity index 80% rename from plugins/welcome/src/plugin.test.ts rename to plugins/bazaar/src/plugin.test.ts index 8b429f0526..4d9964d1dd 100644 --- a/plugins/welcome/src/plugin.test.ts +++ b/plugins/bazaar/src/plugin.test.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * 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. @@ -14,10 +14,10 @@ * limitations under the License. */ -import { welcomePlugin } from './plugin'; +import { bazaarPlugin } from './plugin'; -describe('welcome', () => { +describe('bazaar', () => { it('should export plugin', () => { - expect(welcomePlugin).toBeDefined(); + expect(bazaarPlugin).toBeDefined(); }); }); diff --git a/plugins/bazaar/src/plugin.ts b/plugins/bazaar/src/plugin.ts new file mode 100644 index 0000000000..f96eaf522d --- /dev/null +++ b/plugins/bazaar/src/plugin.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { rootRouteRef } from './routes'; +import { + createApiFactory, + createPlugin, + createRoutableExtension, + identityApiRef, + discoveryApiRef, +} from '@backstage/core-plugin-api'; +import { bazaarApiRef, BazaarClient } from './api'; + +export const bazaarPlugin = createPlugin({ + id: 'bazaar', + routes: { + root: rootRouteRef, + }, + apis: [ + createApiFactory({ + api: bazaarApiRef, + deps: { + identityApi: identityApiRef, + discoveryApi: discoveryApiRef, + }, + factory: ({ identityApi, discoveryApi }) => + new BazaarClient({ identityApi, discoveryApi }), + }), + ], +}); + +export const BazaarPage = bazaarPlugin.provide( + createRoutableExtension({ + component: () => import('./components/HomePage').then(m => m.HomePage), + mountPoint: rootRouteRef, + }), +); diff --git a/plugins/welcome/src/index.ts b/plugins/bazaar/src/routes.ts similarity index 72% rename from plugins/welcome/src/index.ts rename to plugins/bazaar/src/routes.ts index df8f99b6ed..a3cc88d86f 100644 --- a/plugins/welcome/src/index.ts +++ b/plugins/bazaar/src/routes.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * 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. @@ -14,10 +14,8 @@ * limitations under the License. */ -/** - * An old Backstage plugin that provides a welcome page - * - * @packageDocumentation - */ +import { createRouteRef } from '@backstage/core-plugin-api'; -export { welcomePlugin, welcomePlugin as plugin, WelcomePage } from './plugin'; +export const rootRouteRef = createRouteRef({ + title: 'bazaar', +}); diff --git a/plugins/bazaar/src/setupTests.ts b/plugins/bazaar/src/setupTests.ts new file mode 100644 index 0000000000..427556fe26 --- /dev/null +++ b/plugins/bazaar/src/setupTests.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; diff --git a/plugins/bazaar/src/types.ts b/plugins/bazaar/src/types.ts new file mode 100644 index 0000000000..4050c6a2ee --- /dev/null +++ b/plugins/bazaar/src/types.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityRef } from '@backstage/catalog-model'; + +export type Member = { + entityRef: EntityRef; + userId: string; + joinDate?: string; + picture?: string; +}; + +export type Status = 'ongoing' | 'proposed'; + +export type BazaarProject = { + name: string; + entityRef: EntityRef; + community: string; + status: Status; + announcement: string; + updatedAt?: string; + membersCount: number; +}; + +export type FormValues = { + announcement: string; + community: string; + status: string; +}; diff --git a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx index 7e3149fef7..9fe482a30b 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx @@ -15,12 +15,14 @@ */ import { Entity } from '@backstage/catalog-model'; import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { analyticsApiRef } from '@backstage/core-plugin-api'; import { CatalogApi, catalogApiRef, EntityProvider, } from '@backstage/plugin-catalog-react'; -import { renderInTestApp } from '@backstage/test-utils'; +import { MockAnalyticsApi, renderInTestApp } from '@backstage/test-utils'; +import userEvent from '@testing-library/user-event'; import React from 'react'; import { catalogEntityRouteRef, catalogGraphRouteRef } from '../../routes'; import { CatalogGraphCard } from './CatalogGraphCard'; @@ -117,4 +119,30 @@ describe('', () => { '/catalog-graph?rootEntityRefs%5B%5D=b%3Ad%2Fc', ); }); + + test('captures analytics event on click', async () => { + const analyticsSpy = new MockAnalyticsApi(); + const { findByText } = await renderInTestApp( + + {wrapper} + , + { + mountedRoutes: { + '/entity/{kind}/{namespace}/{name}': catalogEntityRouteRef, + '/catalog-graph': catalogGraphRouteRef, + }, + }, + ); + + expect(await findByText('b:d/c')).toBeInTheDocument(); + userEvent.click(await findByText('b:d/c')); + + expect(analyticsSpy.getEvents()[0]).toMatchObject({ + action: 'click', + subject: 'b:d/c', + attributes: { + to: '/entity/{kind}/{namespace}/{name}', + }, + }); + }); }); diff --git a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx index 5ac8a89539..42e85a75ba 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx @@ -19,8 +19,11 @@ import { stringifyEntityRef, } from '@backstage/catalog-model'; import { InfoCard, InfoCardVariants } from '@backstage/core-components'; -import { useRouteRef } from '@backstage/core-plugin-api'; -import { useEntity } from '@backstage/plugin-catalog-react'; +import { useAnalytics, useRouteRef } from '@backstage/core-plugin-api'; +import { + formatEntityRefTitle, + useEntity, +} from '@backstage/plugin-catalog-react'; import { makeStyles, Theme } from '@material-ui/core'; import qs from 'qs'; import React, { MouseEvent, useCallback } from 'react'; @@ -78,6 +81,7 @@ export const CatalogGraphCard = ({ const catalogGraphRoute = useRouteRef(catalogGraphRouteRef); const navigate = useNavigate(); const classes = useStyles({ height }); + const analytics = useAnalytics(); const onNodeClick = useCallback( (node: EntityNode, _: MouseEvent) => { @@ -87,9 +91,14 @@ export const CatalogGraphCard = ({ namespace: nodeEntityName.namespace.toLocaleLowerCase('en-US'), name: nodeEntityName.name, }); + analytics.captureEvent( + 'click', + node.title ?? formatEntityRefTitle(nodeEntityName), + { attributes: { to: path } }, + ); navigate(path); }, - [catalogEntityRoute, navigate], + [catalogEntityRoute, navigate, analytics], ); const catalogGraphParams = qs.stringify( diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx index 8b467a017e..b992d0c976 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx @@ -15,8 +15,9 @@ */ import { RELATION_HAS_PART, RELATION_PART_OF } from '@backstage/catalog-model'; import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { analyticsApiRef } from '@backstage/core-plugin-api'; import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react'; -import { renderInTestApp } from '@backstage/test-utils'; +import { MockAnalyticsApi, renderInTestApp } from '@backstage/test-utils'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { catalogEntityRouteRef } from '../../routes'; @@ -167,4 +168,53 @@ describe('', () => { expect(navigate).toBeCalledWith('/entity/{kind}/{namespace}/{name}'); }); + + test('should capture analytics event when selecting other entity', async () => { + const analyticsSpy = new MockAnalyticsApi(); + const { getByText, findAllByTestId } = await renderInTestApp( + + {wrapper} + , + { + mountedRoutes: { + '/entity/{kind}/{namespace}/{name}': catalogEntityRouteRef, + }, + }, + ); + + expect(await findAllByTestId('node')).toHaveLength(2); + + userEvent.click(getByText('b:d/e')); + + expect(analyticsSpy.getEvents()[0]).toMatchObject({ + action: 'click', + subject: 'b:d/e', + }); + }); + + test('should capture analytics event when navigating to entity', async () => { + const analyticsSpy = new MockAnalyticsApi(); + const { getByText, findAllByTestId } = await renderInTestApp( + + {wrapper} + , + { + mountedRoutes: { + '/entity/{kind}/{namespace}/{name}': catalogEntityRouteRef, + }, + }, + ); + + expect(await findAllByTestId('node')).toHaveLength(2); + + userEvent.click(getByText('b:d/e'), { shiftKey: true }); + + expect(analyticsSpy.getEvents()[0]).toMatchObject({ + action: 'click', + subject: 'b:d/e', + attributes: { + to: '/entity/{kind}/{namespace}/{name}', + }, + }); + }); }); diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx index f5f1ced541..fdcb71d3ad 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx @@ -21,7 +21,7 @@ import { Page, SupportButton, } from '@backstage/core-components'; -import { useRouteRef } from '@backstage/core-plugin-api'; +import { useAnalytics, useRouteRef } from '@backstage/core-plugin-api'; import { formatEntityRefTitle } from '@backstage/plugin-catalog-react'; import { Grid, makeStyles, Paper, Typography } from '@material-ui/core'; import FilterListIcon from '@material-ui/icons/FilterList'; @@ -31,11 +31,11 @@ import React, { MouseEvent, useCallback } from 'react'; import { useNavigate } from 'react-router'; import { catalogEntityRouteRef } from '../../routes'; import { + ALL_RELATION_PAIRS, Direction, EntityNode, EntityRelationsGraph, RelationPairs, - ALL_RELATION_PAIRS, } from '../EntityRelationsGraph'; import { DirectionFilter } from './DirectionFilter'; import { MaxDepthFilter } from './MaxDepthFilter'; @@ -133,6 +133,7 @@ export const CatalogGraphPage = ({ showFilters, toggleShowFilters, } = useCatalogGraphPage({ initialState }); + const analytics = useAnalytics(); const onNodeClick = useCallback( (node: EntityNode, event: MouseEvent) => { const nodeEntityName = parseEntityRef(node.id); @@ -143,12 +144,22 @@ export const CatalogGraphPage = ({ namespace: nodeEntityName.namespace.toLocaleLowerCase('en-US'), name: nodeEntityName.name, }); + + analytics.captureEvent( + 'click', + node.title ?? formatEntityRefTitle(nodeEntityName), + { attributes: { to: path } }, + ); navigate(path); } else { + analytics.captureEvent( + 'click', + node.title ?? formatEntityRefTitle(nodeEntityName), + ); setRootEntityNames([nodeEntityName]); } }, - [catalogEntityRoute, navigate, setRootEntityNames], + [catalogEntityRoute, navigate, setRootEntityNames, analytics], ); return ( diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx index ea3ea3e96f..fe5e76c968 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx @@ -45,7 +45,7 @@ const entities: Entity[] = [ ]; describe('', () => { - it('renders without exploding', async () => { + it('renders without exploding', () => { render( ', () => { const repositoryUrl = screen.getByText( 'http://my-repository/a/catalog-info.yaml', ); - const kindText = await screen.findByText('Kind_2'); + const kindText = screen.getByText(/Kind_2/); expect(repositoryUrl).toBeInTheDocument(); expect(repositoryUrl).toBeVisible(); expect(kindText).toBeInTheDocument(); expect(kindText).toBeVisible(); }); - it('renders card with custom styles', async () => { + it('renders card with custom styles', () => { const { result } = renderHook(() => useStyles()); render( @@ -77,14 +77,14 @@ describe('', () => { const repositoryUrl = screen.getByText( 'http://my-repository/a/catalog-info.yaml', ); - const kindText = await screen.findByText('Kind_2'); + const kindText = screen.getByText(/Kind_2/); expect(repositoryUrl).toBeInTheDocument(); expect(repositoryUrl).not.toBeVisible(); expect(kindText).toBeInTheDocument(); expect(kindText).not.toBeVisible(); }); - it('renders with custom styles', async () => { + it('renders with custom styles', () => { const { result } = renderHook(() => useStyles()); render( @@ -98,7 +98,7 @@ describe('', () => { const repositoryUrl = screen.getByText( 'http://my-repository/a/catalog-info.yaml', ); - const kindText = await screen.findByText('Kind_2'); + const kindText = screen.getByText(/Kind_2/); expect(repositoryUrl).toBeInTheDocument(); expect(repositoryUrl).toBeVisible(); expect(kindText).toBeInTheDocument(); diff --git a/plugins/fossa/api-report.md b/plugins/fossa/api-report.md index 50e382d3d4..d6e229d3d6 100644 --- a/plugins/fossa/api-report.md +++ b/plugins/fossa/api-report.md @@ -21,7 +21,17 @@ export const EntityFossaCard: ({ // Warning: (ae-missing-release-tag) "FossaPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const FossaPage: () => JSX.Element; +export const FossaPage: ({ entitiesFilter }: FossaPageProps) => JSX.Element; + +// Warning: (ae-missing-release-tag) "FossaPageProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type FossaPageProps = { + entitiesFilter?: + | Record[] + | Record + | undefined; +}; // Warning: (ae-missing-release-tag) "fossaPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/plugins/fossa/src/components/FossaPage/FossaPage.test.tsx b/plugins/fossa/src/components/FossaPage/FossaPage.test.tsx index 78c8b7bb40..778f5ee9c6 100644 --- a/plugins/fossa/src/components/FossaPage/FossaPage.test.tsx +++ b/plugins/fossa/src/components/FossaPage/FossaPage.test.tsx @@ -142,4 +142,38 @@ describe('', () => { expect(getByText(/0 Issues/i)).toBeInTheDocument(); expect(getByText(/10 Issues/i)).toBeInTheDocument(); }); + + it('has configurable entity filter', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'API', + metadata: { + name: 'my-name-0', + annotations: { + 'fossa.io/project-name': 'my-name-0', + }, + }, + }; + + fossaApi.getFindingSummaries.mockResolvedValue(new Map()); + catalogApi.getEntities.mockResolvedValue({ items: [entity] }); + + const { getByText } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, + }, + }, + ); + + expect(catalogApi.getEntities).toBeCalledWith( + expect.objectContaining({ + filter: { kind: 'API' }, + }), + ); + expect(getByText(/my-name-0/i)).toBeInTheDocument(); + }); }); diff --git a/plugins/fossa/src/components/FossaPage/FossaPage.tsx b/plugins/fossa/src/components/FossaPage/FossaPage.tsx index 3e16085968..fbdaf85717 100644 --- a/plugins/fossa/src/components/FossaPage/FossaPage.tsx +++ b/plugins/fossa/src/components/FossaPage/FossaPage.tsx @@ -30,8 +30,8 @@ import { Tooltip } from '@material-ui/core'; import { Skeleton } from '@material-ui/lab'; import { DateTime } from 'luxon'; import * as React from 'react'; -import { useMemo } from 'react'; -import { useAsync } from 'react-use'; +import { useMemo, useState } from 'react'; +import { useAsync, useDeepCompareEffect } from 'react-use'; import { FindingSummary, fossaApiRef } from '../../api'; import { getProjectName } from '../getProjectName'; @@ -166,14 +166,25 @@ const filters: TableFilter[] = [ { column: 'Branch', type: 'select' }, ]; -export const FossaPage = () => { +export type FossaPageProps = { + entitiesFilter?: + | Record[] + | Record + | undefined; +}; + +export const FossaPage = ({ + entitiesFilter = { kind: 'Component' }, +}: FossaPageProps) => { const catalogApi = useApi(catalogApiRef); const fossaApi = useApi(fossaApiRef); + const [filter, setFilter] = useState(entitiesFilter); + useDeepCompareEffect(() => setFilter(entitiesFilter), [entitiesFilter]); // Get a list of all relevant entities const { value: entities, loading: entitiesLoading } = useAsync(() => { return catalogApi.getEntities({ - filter: { kind: 'Component' }, + filter, fields: [ 'kind', 'metadata.namespace', @@ -182,7 +193,7 @@ export const FossaPage = () => { 'relations', ], }); - }); + }, [filter]); // get the project names of all entities. the idx of both lists match. const projectNames = useMemo( diff --git a/plugins/fossa/src/components/FossaPage/index.ts b/plugins/fossa/src/components/FossaPage/index.ts index 7be77be441..13dd680a95 100644 --- a/plugins/fossa/src/components/FossaPage/index.ts +++ b/plugins/fossa/src/components/FossaPage/index.ts @@ -15,3 +15,4 @@ */ export { FossaPage } from './FossaPage'; +export type { FossaPageProps } from './FossaPage'; diff --git a/plugins/fossa/src/components/index.ts b/plugins/fossa/src/components/index.ts index 12632d4fff..f108626e88 100644 --- a/plugins/fossa/src/components/index.ts +++ b/plugins/fossa/src/components/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export * from './FossaCard'; +export type { FossaPageProps } from './FossaPage'; diff --git a/plugins/fossa/src/index.ts b/plugins/fossa/src/index.ts index 200b977f3b..7f75fad178 100644 --- a/plugins/fossa/src/index.ts +++ b/plugins/fossa/src/index.ts @@ -20,5 +20,6 @@ * @packageDocumentation */ -export { fossaPlugin } from './plugin'; +export * from './components'; export { EntityFossaCard, FossaPage } from './extensions'; +export { fossaPlugin } from './plugin'; diff --git a/plugins/git-release-manager/src/features/Patch/hooks/usePatchDryRun.tsx b/plugins/git-release-manager/src/features/Patch/hooks/usePatchDryRun.tsx index de3a67e874..3bd69b1028 100644 --- a/plugins/git-release-manager/src/features/Patch/hooks/usePatchDryRun.tsx +++ b/plugins/git-release-manager/src/features/Patch/hooks/usePatchDryRun.tsx @@ -391,6 +391,6 @@ export function usePatchDryRun({ asyncCatcher, abortIfError, selectedPatchCommit: latestCommitOnReleaseBranchRes.value - ?.selectedPatchCommit as any, + ?.selectedPatchCommit as GetRecentCommitsResultSingle, }; } diff --git a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx index 0519ed3816..ee17df8533 100644 --- a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -80,6 +80,12 @@ const FailSkippedWidget = ({ }; const generatedColumns: TableColumn[] = [ + { + title: 'Timestamp', + defaultSort: 'desc', + hidden: true, + field: 'lastBuild.timestamp', + }, { title: 'Build', field: 'fullName', diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 413c384929..160a0229d5 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-scaffolder-backend +## 0.15.9 + +### Patch Changes + +- 0f99f1170e: Make sure `sourcePath` of `publish:github:pull-request` can only be used to + retrieve files from the workspace. + ## 0.15.8 ### Patch Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 15f4cd40d2..05377123c0 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend", "description": "The Backstage backend plugin that helps you create new things", - "version": "0.15.8", + "version": "0.15.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts index 2ba30bb3db..ed72b14a65 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts @@ -14,21 +14,20 @@ * limitations under the License. */ +import { getRootLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; import mockFs from 'mock-fs'; -import { Writable } from 'stream'; import os from 'os'; import { resolve as resolvePath } from 'path'; -import { - PullRequestCreator, - GithubPullRequestActionInput, - createPublishGithubPullRequestAction, - ClientFactoryInput, -} from './githubPullRequest'; +import { Writable } from 'stream'; import { ActionContext, TemplateAction } from '../../types'; -import { getRootLogger } from '@backstage/backend-common'; - -import { ScmIntegrations } from '@backstage/integration'; -import { ConfigReader } from '@backstage/config'; +import { + ClientFactoryInput, + createPublishGithubPullRequestAction, + GithubPullRequestActionInput, + PullRequestCreator, +} from './githubPullRequest'; const root = os.platform() === 'win32' ? 'C:\\root' : '/root'; const workspacePath = resolvePath(root, 'my-workspace'); @@ -174,6 +173,14 @@ describe('createPublishGithubPullRequestAction', () => { ], }); }); + + it('should not allow to use files outside of the workspace', async () => { + input.sourcePath = '../../test'; + + await expect(instance.handler(ctx)).rejects.toThrow( + 'Relative path is not allowed to refer to a directory outside its parent', + ); + }); }); describe('with repoUrl', () => { diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts index e21ffc5cd0..253fd72b38 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts @@ -28,6 +28,7 @@ import { Octokit } from '@octokit/rest'; import { InputError, CustomErrorBase } from '@backstage/errors'; import { createPullRequest } from 'octokit-plugin-create-pull-request'; import globby from 'globby'; +import { resolveSafeChildPath } from '@backstage/backend-common'; class GithubResponseError extends CustomErrorBase {} @@ -183,7 +184,7 @@ export const createPublishGithubPullRequestAction = ({ const client = await clientFactory({ integrations, host, owner, repo }); const fileRoot = sourcePath - ? path.resolve(ctx.workspacePath, sourcePath) + ? resolveSafeChildPath(ctx.workspacePath, sourcePath) : ctx.workspacePath; const localFilePaths = await globby(['./**', './**/.*', '!.git'], { diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.test.ts new file mode 100644 index 0000000000..a99cd2f380 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.test.ts @@ -0,0 +1,43 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getRepoSourceDirectory } from './util'; + +describe('getRepoSourceDirectory', () => { + test('should return workspace root if no sub folder is given', () => { + expect(getRepoSourceDirectory('/var/workspace', undefined)).toEqual( + '/var/workspace', + ); + }); + + test('should return path in workspace if sub folder is given', () => { + expect( + getRepoSourceDirectory('/var/workspace', 'path/of/subfolder'), + ).toEqual('/var/workspace/path/of/subfolder'); + }); + + test('should not allow traversal outside the workspace root', () => { + expect(getRepoSourceDirectory('/var/workspace', '../secret')).toEqual( + '/var/workspace/secret', + ); + expect( + getRepoSourceDirectory('/var/workspace', './path/../../secret'), + ).toEqual('/var/workspace/secret'); + expect( + getRepoSourceDirectory('/var/workspace', '/absolute/secret'), + ).toEqual('/var/workspace/absolute/secret'); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts index dea180dc04..58ba5bfdd1 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts @@ -15,6 +15,7 @@ */ import { InputError } from '@backstage/errors'; +import { isChildPath } from '@backstage/backend-common'; import { join as joinPath, normalize as normalizePath } from 'path'; import { ScmIntegrationRegistry } from '@backstage/integration'; @@ -27,7 +28,11 @@ export const getRepoSourceDirectory = ( /^(\.\.(\/|\\|$))+/, '', ); - return joinPath(workspacePath, safeSuffix); + const path = joinPath(workspacePath, safeSuffix); + if (!isChildPath(workspacePath, path)) { + throw new Error('Invalid source path'); + } + return path; } return workspacePath; }; diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts index 3ecdd6dcd8..d6d1b0cfd2 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DefaultWorkflowRunner.test.ts @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import os from 'os'; + +import mockFs from 'mock-fs'; +import * as winston from 'winston'; + import { getVoidLogger } from '@backstage/backend-common'; import { DefaultWorkflowRunner } from './DefaultWorkflowRunner'; import { TemplateActionRegistry } from '../actions'; @@ -22,7 +25,6 @@ import { ConfigReader } from '@backstage/config'; import { Task, TaskSpec } from './types'; describe('DefaultWorkflowRunner', () => { - const workingDirectory = os.tmpdir(); const logger = getVoidLogger(); let actionRegistry = new TemplateActionRegistry(); let runner: DefaultWorkflowRunner; @@ -45,6 +47,11 @@ describe('DefaultWorkflowRunner', () => { }); beforeEach(() => { + winston.format.simple(); // put logform the require cache before mocking fs + mockFs({ + '/tmp': mockFs.directory(), + }); + jest.resetAllMocks(); actionRegistry = new TemplateActionRegistry(); fakeActionHandler = jest.fn(); @@ -84,11 +91,15 @@ describe('DefaultWorkflowRunner', () => { runner = new DefaultWorkflowRunner({ actionRegistry, integrations, - workingDirectory, + workingDirectory: '/tmp', logger, }); }); + afterEach(() => { + mockFs.restore(); + }); + it('should throw an error if the action does not exist', async () => { const task = createMockTaskWithSpec({ apiVersion: 'scaffolder.backstage.io/v1beta3', diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/LegacyWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/LegacyWorkflowRunner.test.ts index a82f6b25fa..2714f1b80c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/LegacyWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/LegacyWorkflowRunner.test.ts @@ -13,18 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import mockFs from 'mock-fs'; +import * as winston from 'winston'; import { createTemplateAction, TemplateActionRegistry } from '../actions'; import { ScmIntegrations } from '@backstage/integration'; import { ConfigReader } from '@backstage/config'; import { getVoidLogger } from '@backstage/backend-common'; import { LegacyWorkflowRunner } from './LegacyWorkflowRunner'; -import os from 'os'; import { Task, TaskSpec } from './types'; import { RepoSpec } from '../actions/builtin/publish/util'; describe('LegacyWorkflowRunner', () => { let runner: LegacyWorkflowRunner; - const workingDirectory = os.tmpdir(); const logger = getVoidLogger(); let actionRegistry = new TemplateActionRegistry(); @@ -45,6 +46,11 @@ describe('LegacyWorkflowRunner', () => { }); beforeEach(() => { + winston.format.simple(); // put logform the require cache before mocking fs + mockFs({ + '/tmp': mockFs.directory(), + }); + actionRegistry = new TemplateActionRegistry(); actionRegistry.register({ id: 'test-action', @@ -57,11 +63,15 @@ describe('LegacyWorkflowRunner', () => { runner = new LegacyWorkflowRunner({ actionRegistry, integrations, - workingDirectory, + workingDirectory: '/tmp', logger, }); }); + afterEach(() => { + mockFs.restore(); + }); + it('should fail when the action does not exist', async () => { const task = createMockTaskWithSpec({ apiVersion: 'backstage.io/v1beta2', diff --git a/plugins/tech-radar/src/sample.ts b/plugins/tech-radar/src/sample.ts index 9c89a1e73a..2b674f35c8 100644 --- a/plugins/tech-radar/src/sample.ts +++ b/plugins/tech-radar/src/sample.ts @@ -51,7 +51,7 @@ entries.push({ title: 'JavaScript', quadrant: 'languages', description: - 'Excepteur **sint** occaecat *cupidatat* non proident, sunt in culpa qui officia deserunt mollit anim id est laborum', + 'Excepteur **sint** occaecat *cupidatat* non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\n```ts\nconst x = "3";\n```\n', }); entries.push({ timeline: [ diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index 0cb6c039d0..7307961772 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -397,6 +397,11 @@ const TheReader = ({ const dom = useTechDocsReaderDom(); const shadowDomRef = useRef(null); + const onReadyRef = useRef<() => void>(onReady); + useEffect(() => { + onReadyRef.current = onReady; + }, [onReady]); + useEffect(() => { if (!dom || !shadowDomRef.current) return; const shadowDiv = shadowDomRef.current; @@ -406,8 +411,10 @@ const TheReader = ({ shadowRoot.removeChild(child), ); shadowRoot.appendChild(dom); - onReady(); - }, [dom, onReady]); + onReadyRef.current(); + + // this hook must ONLY be triggered by a changed dom + }, [dom]); return ( <> diff --git a/plugins/welcome/CHANGELOG.md b/plugins/welcome/CHANGELOG.md deleted file mode 100644 index 3f9412a073..0000000000 --- a/plugins/welcome/CHANGELOG.md +++ /dev/null @@ -1,212 +0,0 @@ -# @backstage/plugin-welcome - -## 0.3.8 - -### Patch Changes - -- Updated dependencies - - @backstage/core-components@0.7.0 - - @backstage/theme@0.2.11 - -## 0.3.7 - -### Patch Changes - -- 81a41ec249: Added a `name` key to all extensions in order to improve Analytics API metadata. -- Updated dependencies - - @backstage/core-components@0.6.1 - - @backstage/core-plugin-api@0.1.10 - -## 0.3.6 - -### Patch Changes - -- Updated dependencies - - @backstage/core-plugin-api@0.1.9 - - @backstage/core-components@0.6.0 - -## 0.3.5 - -### Patch Changes - -- Updated dependencies - - @backstage/core-components@0.5.0 - -## 0.3.4 - -### Patch Changes - -- 9f1362dcc1: Upgrade `@material-ui/lab` to `4.0.0-alpha.57`. -- Updated dependencies - - @backstage/core-components@0.4.2 - - @backstage/core-plugin-api@0.1.8 - -## 0.3.3 - -### Patch Changes - -- Updated dependencies - - @backstage/core-components@0.4.0 - -## 0.3.2 - -### Patch Changes - -- Updated dependencies - - @backstage/core-components@0.3.0 - - @backstage/core-plugin-api@0.1.5 - -## 0.3.1 - -### Patch Changes - -- 9d40fcb1e: - Bumping `material-ui/core` version to at least `4.12.2` as they made some breaking changes in later versions which broke `Pagination` of the `Table`. - - Switching out `material-table` to `@material-table/core` for support for the later versions of `material-ui/core` - - This causes a minor API change to `@backstage/core-components` as the interface for `Table` re-exports the `prop` from the underlying `Table` components. - - `onChangeRowsPerPage` has been renamed to `onRowsPerPageChange` - - `onChangePage` has been renamed to `onPageChange` - - Migration guide is here: https://material-table-core.com/docs/breaking-changes -- Updated dependencies - - @backstage/core-components@0.2.0 - - @backstage/core-plugin-api@0.1.4 - - @backstage/theme@0.2.9 - -## 0.3.0 - -### Minor Changes - -- d719926d2: **BREAKING CHANGE** Remove deprecated route registrations, meaning that it is no longer enough to only import the plugin in the app and the exported page extension must be used instead. - -### Patch Changes - -- 48c9fcd33: Migrated to use the new `@backstage/core-*` packages rather than `@backstage/core`. -- Updated dependencies - - @backstage/core-plugin-api@0.1.3 - -## 0.2.8 - -### Patch Changes - -- 062bbf90f: chore: bump `@testing-library/user-event` from 12.8.3 to 13.1.8 -- 675a569a9: chore: bump `react-use` dependency in all packages -- Updated dependencies [062bbf90f] -- Updated dependencies [889d89b6e] -- Updated dependencies [3f988cb63] -- Updated dependencies [675a569a9] - - @backstage/core@0.7.9 - -## 0.2.7 - -### Patch Changes - -- f85851837: Australian Greeting -- Updated dependencies [94da20976] -- Updated dependencies [d8cc7e67a] -- Updated dependencies [99fbef232] -- Updated dependencies [ab07d77f6] -- Updated dependencies [931b21a12] -- Updated dependencies [937ed39ce] -- Updated dependencies [9a9e7a42f] -- Updated dependencies [50ce875a0] - - @backstage/core@0.7.6 - - @backstage/theme@0.2.6 - -## 0.2.6 - -### Patch Changes - -- Updated dependencies [40c0fdbaa] -- Updated dependencies [2a271d89e] -- Updated dependencies [bece09057] -- Updated dependencies [169f48deb] -- Updated dependencies [8a1566719] -- Updated dependencies [4c049a1a1] - - @backstage/core@0.7.0 - -## 0.2.5 - -### Patch Changes - -- 8dfdec613: Migrated to new composability API, exporting the plugin as `welcomePlugin` and the page as `WelcomePage`. -- Updated dependencies [12ece98cd] -- Updated dependencies [d82246867] -- Updated dependencies [c810082ae] -- Updated dependencies [5fa3bdb55] -- Updated dependencies [21e624ba9] -- Updated dependencies [da9f53c60] -- Updated dependencies [32c95605f] -- Updated dependencies [54c7d02f7] - - @backstage/core@0.6.0 - - @backstage/theme@0.2.3 - -## 0.2.4 - -### Patch Changes - -- Updated dependencies [efd6ef753] -- Updated dependencies [a187b8ad0] - - @backstage/core@0.5.0 - -## 0.2.3 - -### Patch Changes - -- Updated dependencies [2527628e1] -- Updated dependencies [1c69d4716] -- Updated dependencies [1665ae8bb] -- Updated dependencies [04f26f88d] -- Updated dependencies [ff243ce96] - - @backstage/core@0.4.0 - - @backstage/theme@0.2.2 - -## 0.2.2 - -### Patch Changes - -- 303c5ea17: Refactor route registration to remove deprecating code - -## 0.2.1 - -### Patch Changes - -- Updated dependencies [7b37d65fd] -- Updated dependencies [4aca74e08] -- Updated dependencies [e8f69ba93] -- Updated dependencies [0c0798f08] -- Updated dependencies [0c0798f08] -- Updated dependencies [199237d2f] -- Updated dependencies [6627b626f] -- Updated dependencies [4577e377b] - - @backstage/core@0.3.0 - - @backstage/theme@0.2.1 - -## 0.2.0 - -### Minor Changes - -- 28edd7d29: Create backend plugin through CLI - -### Patch Changes - -- Updated dependencies [819a70229] -- Updated dependencies [ae5983387] -- Updated dependencies [0d4459c08] -- Updated dependencies [482b6313d] -- Updated dependencies [1c60f716e] -- Updated dependencies [144c66d50] -- Updated dependencies [b79017fd3] -- Updated dependencies [6d97d2d6f] -- Updated dependencies [93a3fa3ae] -- Updated dependencies [782f3b354] -- Updated dependencies [2713f28f4] -- Updated dependencies [406015b0d] -- Updated dependencies [82759d3e4] -- Updated dependencies [ac8d5d5c7] -- Updated dependencies [ebca83d48] -- Updated dependencies [aca79334f] -- Updated dependencies [c0d5242a0] -- Updated dependencies [3beb5c9fc] -- Updated dependencies [754e31db5] -- Updated dependencies [1611c6dbc] - - @backstage/core@0.2.0 - - @backstage/theme@0.2.0 diff --git a/plugins/welcome/README.md b/plugins/welcome/README.md deleted file mode 100644 index c40a760c7c..0000000000 --- a/plugins/welcome/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Title - -> This plugin formerly provided the "homepage" of early alpha versions of Backstage. -> It is no longer in active use and in most cases can be removed from your instance. - -Welcome to the welcome plugin! - -## Sub-section 1 - -## Sub-section 2 diff --git a/plugins/welcome/api-report.md b/plugins/welcome/api-report.md deleted file mode 100644 index 02a46615f3..0000000000 --- a/plugins/welcome/api-report.md +++ /dev/null @@ -1,21 +0,0 @@ -## API Report File for "@backstage/plugin-welcome" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -/// - -import { BackstagePlugin } from '@backstage/core-plugin-api'; - -// Warning: (ae-missing-release-tag) "WelcomePage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export const WelcomePage: () => JSX.Element; - -// Warning: (ae-missing-release-tag) "welcomePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -const welcomePlugin: BackstagePlugin<{}, {}>; -export { welcomePlugin as plugin }; -export { welcomePlugin }; -``` diff --git a/plugins/welcome/src/components/WelcomePage/WelcomePage.test.tsx b/plugins/welcome/src/components/WelcomePage/WelcomePage.test.tsx deleted file mode 100644 index 22be84a4bf..0000000000 --- a/plugins/welcome/src/components/WelcomePage/WelcomePage.test.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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. - */ - -import { renderInTestApp } from '@backstage/test-utils'; -import { lightTheme } from '@backstage/theme'; -import { ThemeProvider } from '@material-ui/core'; -import React from 'react'; -import WelcomePage from './WelcomePage'; - -import { - ApiProvider, - ApiRegistry, - ConfigReader, -} from '@backstage/core-app-api'; -import { configApiRef, errorApiRef } from '@backstage/core-plugin-api'; - -describe('WelcomePage', () => { - it('should render', async () => { - const { baseElement } = await renderInTestApp( - - - - - , - ); - expect(baseElement).toBeInTheDocument(); - }); -}); diff --git a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx b/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx deleted file mode 100644 index ce45b1b031..0000000000 --- a/plugins/welcome/src/components/WelcomePage/WelcomePage.tsx +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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. - */ - -import React from 'react'; -import { Link as RouterLink } from 'react-router-dom'; -import { - Typography, - Grid, - List, - ListItem, - ListItemText, - Link, -} from '@material-ui/core'; - -import { - Content, - InfoCard, - Header, - HomepageTimer, - Page, - ContentHeader, - SupportButton, - WarningPanel, -} from '@backstage/core-components'; - -import { useApi, configApiRef } from '@backstage/core-plugin-api'; - -const WelcomePage = () => { - const appTitle = - useApi(configApiRef).getOptionalString('app.title') ?? 'Backstage'; - const profile = { givenName: '' }; - - return ( - -
- -
- - - - - - - - - We created Backstage about 4 years ago. While Spotify's - internal version of Backstage has had the benefit of time to - mature and evolve, the first iteration of our open source - version is still nascent. We are envisioning three phases of - the project and we have already begun work on various aspects - of these phases. The best way to keep track of the progress is - through the  - - Milestones - - . - - } - /> - - - - - You now have a running instance of Backstage!  - - ๐ŸŽ‰ - -  Let's make sure you get the most out of this platform by - walking you through the basics. - - - The Setup - - - Backstage is put together from three base concepts: the core, - the app and the plugins. - - - - - - - - - - - - - - Build Your Plugins - - - We suggest you either check out the documentation for{' '} - - creating a plugin - {' '} - or have a look in the code for the{' '} - - existing plugins - {' '} - in the directory{' '} - - plugins/ - - . - - - - - - - - backstage.io - - - - Create a plugin - - - - Plugin gallery - - - - - - -
- ); -}; - -export default WelcomePage; diff --git a/yarn.lock b/yarn.lock index 9915fb6279..d4c8b7053a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2098,14 +2098,7 @@ core-js-pure "^3.16.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.14.8" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" - integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.12.1", "@babel/runtime@^7.2.0": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.8", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.15.4" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== @@ -2508,16 +2501,16 @@ resolved "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-5.0.2.tgz#b23080fa35520e993a8a37a0f5bca26aa4650a48" integrity sha512-NBEJlHWrhQucLhZGHtSxM2loSaNUMajC7KOYJLyfcdW/6goVoff2HoYI3bz8YCDN0wKGbxtUL0gx2dvHpvnWlw== -"@changesets/apply-release-plan@^5.0.0": - version "5.0.0" - resolved "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-5.0.0.tgz#11bf168acecbf4cfa2b0e6425160bac5ceeec1c3" - integrity sha512-SE+5nPNSKUyUociPnAvnjYSVF+diciEhX9ZHSqKWMlydswCDjiaq9gz67qwWCmwgEgEOz0TS7VrQBoOlzbitvA== +"@changesets/apply-release-plan@^5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-5.0.1.tgz#ed3e30550f787ef1b72f0a51e29a54d244123109" + integrity sha512-ltYLM/PPoL1Un9hnNCbUac25FWonJvIZ/9C3O4UyZ/k4rir9FGvH6KLtMOiPEAJWnXmaHeRDr06MzohuXOnmvw== dependencies: "@babel/runtime" "^7.10.4" - "@changesets/config" "^1.6.0" + "@changesets/config" "^1.6.1" "@changesets/get-version-range-type" "^0.3.2" - "@changesets/git" "^1.1.1" - "@changesets/types" "^4.0.0" + "@changesets/git" "^1.1.2" + "@changesets/types" "^4.0.1" "@manypkg/get-packages" "^1.0.1" detect-indent "^6.0.0" fs-extra "^7.0.1" @@ -2527,36 +2520,36 @@ resolve-from "^5.0.0" semver "^5.4.1" -"@changesets/assemble-release-plan@^5.0.0": - version "5.0.0" - resolved "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-5.0.0.tgz#3e57405e5c375e2d933f62e74d1874915e60cd61" - integrity sha512-LElDXTCBUkPSmdXlCisoUWw2paX48snatBmw/hKnGiSvnyZqdTIylLojAGQWG0/vOO9v3s/DvJ4hdagIquxJjg== +"@changesets/assemble-release-plan@^5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-5.0.1.tgz#80e9b750705677eb2d6356c581ed9c2e97fd68e7" + integrity sha512-KQqafvScTFQ/4Q2LpLmDYhU47LWvIGcgVS8tzKU8fBvRdKuLGQXe42VYbwVM0cHIkFd/b6YFn+H2QMdKC2MjIQ== dependencies: "@babel/runtime" "^7.10.4" "@changesets/errors" "^0.1.4" - "@changesets/get-dependents-graph" "^1.2.1" - "@changesets/types" "^4.0.0" + "@changesets/get-dependents-graph" "^1.2.2" + "@changesets/types" "^4.0.1" "@manypkg/get-packages" "^1.0.1" semver "^5.4.1" "@changesets/cli@^2.14.0": - version "2.16.0" - resolved "https://registry.npmjs.org/@changesets/cli/-/cli-2.16.0.tgz#9f794005d0503efba5e348b929821a1732fd0f0d" - integrity sha512-VFkXSyyk/WRjjUoBI7g7cDy09qBjPbBQOloPMEshTzMo/NY9muWHl2yB/FSSkV/6PxGimPtJ7aEJPYfk8HCfXw== + version "2.17.0" + resolved "https://registry.npmjs.org/@changesets/cli/-/cli-2.17.0.tgz#cc7ff4f64d029ddd6d87020a012c8cf8c7adde58" + integrity sha512-UyraYwYst1lTjef+8i80XQ6SqsLaGwi4Sgn9YuDf2xdHA9m+5qQXshHvHVjaTdPTA09rqMBk9yeO7vmAqF4+vQ== dependencies: "@babel/runtime" "^7.10.4" - "@changesets/apply-release-plan" "^5.0.0" - "@changesets/assemble-release-plan" "^5.0.0" - "@changesets/config" "^1.6.0" + "@changesets/apply-release-plan" "^5.0.1" + "@changesets/assemble-release-plan" "^5.0.1" + "@changesets/config" "^1.6.1" "@changesets/errors" "^0.1.4" - "@changesets/get-dependents-graph" "^1.2.1" - "@changesets/get-release-plan" "^3.0.0" - "@changesets/git" "^1.1.1" + "@changesets/get-dependents-graph" "^1.2.2" + "@changesets/get-release-plan" "^3.0.1" + "@changesets/git" "^1.1.2" "@changesets/logger" "^0.0.5" - "@changesets/pre" "^1.0.6" - "@changesets/read" "^0.4.7" - "@changesets/types" "^4.0.0" - "@changesets/write" "^0.1.4" + "@changesets/pre" "^1.0.7" + "@changesets/read" "^0.5.0" + "@changesets/types" "^4.0.1" + "@changesets/write" "^0.1.5" "@manypkg/get-packages" "^1.0.1" "@types/semver" "^6.0.0" boxen "^1.3.0" @@ -2575,15 +2568,15 @@ term-size "^2.1.0" tty-table "^2.8.10" -"@changesets/config@^1.6.0": - version "1.6.0" - resolved "https://registry.npmjs.org/@changesets/config/-/config-1.6.0.tgz#2cd9426b9d4212534d2b31c51de43280b76d3df4" - integrity sha512-vMY/OpMFSDC2crDKb5Nq2kMX9hozcXL4dY5Rr+a1JQ044Rz+jqjJPpdTP2yQ+j7qmeGcUTvwjJoEMeekYwfqhg== +"@changesets/config@^1.6.1": + version "1.6.1" + resolved "https://registry.npmjs.org/@changesets/config/-/config-1.6.1.tgz#e9b1636fd56a74411c493c924e6ffa07d7d26091" + integrity sha512-aQTo6ODvhsvnSFszMP1YbJyAi1DtE1Pes9rL+G+KYJiAOA6k5RzbiKOarjo+ZkKXpX0G3CBAbOO8jXOX4xG7cQ== dependencies: "@changesets/errors" "^0.1.4" - "@changesets/get-dependents-graph" "^1.2.1" + "@changesets/get-dependents-graph" "^1.2.2" "@changesets/logger" "^0.0.5" - "@changesets/types" "^4.0.0" + "@changesets/types" "^4.0.1" "@manypkg/get-packages" "^1.0.1" fs-extra "^7.0.1" micromatch "^4.0.2" @@ -2595,28 +2588,28 @@ dependencies: extendable-error "^0.1.5" -"@changesets/get-dependents-graph@^1.2.1": - version "1.2.1" - resolved "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-1.2.1.tgz#462908693dc3a354622e43f85a764b74b5bb53af" - integrity sha512-vJOibo9SkqhVbgfq5AHIlQ7tzkYQIXh3tPAnlNLy2bPZsU+SByd74GaxHYWt1zOBlncU25WKrIM6J7XBB+GVUg== +"@changesets/get-dependents-graph@^1.2.2": + version "1.2.2" + resolved "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-1.2.2.tgz#7a2238f3d1a023de83d37b727a0da15826e88d73" + integrity sha512-3zJRw6TcexmOrmIZNOXpIRsZtqtrdmlzbqp4+V0VgnBvTxz16rqCS9VBsBqFYeJDWFj3soOlHUMeTwLghr18DA== dependencies: - "@changesets/types" "^4.0.0" + "@changesets/types" "^4.0.1" "@manypkg/get-packages" "^1.0.1" chalk "^2.1.0" fs-extra "^7.0.1" semver "^5.4.1" -"@changesets/get-release-plan@^3.0.0": - version "3.0.0" - resolved "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-3.0.0.tgz#55efc01db2e24bd7a88e703956eb2f6c4a79054f" - integrity sha512-7VLiqpcWZyjwIXYgkubBC/9cdwqUJEhLMRT9/Y9+ctHqrpsXmJg15QQPTOh3HT9yGN5fJPL1WwuZkc1HXUhK0g== +"@changesets/get-release-plan@^3.0.1": + version "3.0.1" + resolved "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-3.0.1.tgz#c98a34321eac9e4187098893ff8dadb6f90ad89c" + integrity sha512-HTZeEPvLlcWMWKxLrzQNLQWKDDN1lUKvaOV+hl/yBhgtyJECljJJzd3IRaKqCSWMrYKNaaEcmunTtZ4oaeoK9w== dependencies: "@babel/runtime" "^7.10.4" - "@changesets/assemble-release-plan" "^5.0.0" - "@changesets/config" "^1.6.0" - "@changesets/pre" "^1.0.6" - "@changesets/read" "^0.4.7" - "@changesets/types" "^4.0.0" + "@changesets/assemble-release-plan" "^5.0.1" + "@changesets/config" "^1.6.1" + "@changesets/pre" "^1.0.7" + "@changesets/read" "^0.5.0" + "@changesets/types" "^4.0.1" "@manypkg/get-packages" "^1.0.1" "@changesets/get-version-range-type@^0.3.2": @@ -2624,14 +2617,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.1.1": - version "1.1.1" - resolved "https://registry.npmjs.org/@changesets/git/-/git-1.1.1.tgz#f444d3ff3604acb6949560656c9ef330485a5fa3" - integrity sha512-Z12TcKwgU33YE3r76cyU+X81RchOXljDZ5s3G2u0Zd+ODyrwlDb91IO55+6R0Ha6ouPz8ioont0gA70c1RFngg== +"@changesets/git@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@changesets/git/-/git-1.1.2.tgz#248d4418bcb2d4f198852409cfcbd06a1fcb0424" + integrity sha512-dfza8elsIwcYVa4fFzLaPs4+AkoCFiW3sfzkkC7WR+rG9j+zZh7CelzVpnoiAbEI2QOzeCbZKMoLSvBPgHhB1g== dependencies: "@babel/runtime" "^7.10.4" "@changesets/errors" "^0.1.4" - "@changesets/types" "^4.0.0" + "@changesets/types" "^4.0.1" "@manypkg/get-packages" "^1.0.1" is-subdir "^1.1.1" spawndamnit "^2.0.0" @@ -2643,51 +2636,51 @@ dependencies: chalk "^2.1.0" -"@changesets/parse@^0.3.8": - version "0.3.8" - resolved "https://registry.npmjs.org/@changesets/parse/-/parse-0.3.8.tgz#0bb244eccb35cb301168f85684bb03389c59341d" - integrity sha512-0S7Dc7XbMOKamBtd48vVuWL2aFZyaglw6lJsXNddn9forFf8oMKMmdyJ/HQPyeEChDDOhDF1/ya7m/zpt4Dk4w== +"@changesets/parse@^0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@changesets/parse/-/parse-0.3.9.tgz#c518792b05f15ab418d58dc1cea81601556e845e" + integrity sha512-XoTEkMpvRRVxSlhvOaK4YSFM+RZhYFTksxRh7ieNkb6pMxkpq8MOYSi/07BuqkODn4dJEMOoSy3RzL99P6FyqA== dependencies: - "@changesets/types" "^4.0.0" + "@changesets/types" "^4.0.1" js-yaml "^3.13.1" -"@changesets/pre@^1.0.6": - version "1.0.6" - resolved "https://registry.npmjs.org/@changesets/pre/-/pre-1.0.6.tgz#45700cf18274b35b2296000befe7fe4df8ff046f" - integrity sha512-ZwFFQLjhTmA4hj8+Cf9pm6nD9Tp+AiBz1wJLaGum4Ae1fPXMwDnJfHknFUTytqZBlC0gHkiGSj6QkUuetWvckg== +"@changesets/pre@^1.0.7": + version "1.0.7" + resolved "https://registry.npmjs.org/@changesets/pre/-/pre-1.0.7.tgz#caf6430c90b8ac6d58c9cd90a19558ab06b19b88" + integrity sha512-oUU6EL4z0AIyCv/EscQFxxJsQfc9/AcSpqAGbdZrLXwshUWTXsJHMWlE3/+iSIyQ+I+/xtxbBxnqDUpUU3TOOg== dependencies: "@babel/runtime" "^7.10.4" "@changesets/errors" "^0.1.4" - "@changesets/types" "^4.0.0" + "@changesets/types" "^4.0.1" "@manypkg/get-packages" "^1.0.1" fs-extra "^7.0.1" -"@changesets/read@^0.4.7": - version "0.4.7" - resolved "https://registry.npmjs.org/@changesets/read/-/read-0.4.7.tgz#5a32ae7092330fba31eaec4c83321bb936605766" - integrity sha512-E70QrYQpSCMF0nC0dlPU7i6A9zht+8zkQczrKMbOUwDVrfidcvgojxfuJSQbzptYSb9OKYh8GOLd+bsq9+uO9Q== +"@changesets/read@^0.5.0": + version "0.5.0" + resolved "https://registry.npmjs.org/@changesets/read/-/read-0.5.0.tgz#52f7a10f6baebf97172e62035ee8345652c5a1c0" + integrity sha512-A2OJ+vgfvbUaLx2yKyHH+tapa+DUd2NtpFpVuxjUqv0zirjqju20z1bziqaqpIQSf/rXPuoc09vp5w4VakraHg== dependencies: "@babel/runtime" "^7.10.4" - "@changesets/git" "^1.1.1" + "@changesets/git" "^1.1.2" "@changesets/logger" "^0.0.5" - "@changesets/parse" "^0.3.8" - "@changesets/types" "^4.0.0" + "@changesets/parse" "^0.3.9" + "@changesets/types" "^4.0.1" chalk "^2.1.0" fs-extra "^7.0.1" p-filter "^2.1.0" -"@changesets/types@^4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@changesets/types/-/types-4.0.0.tgz#635f804546b0a96ecc0ca3f26403a6782a3dc938" - integrity sha512-whLmPx2wgJRoOtxVZop+DJ71z1gTSkij7osiHgN+pe//FiE6bb4ffvBBb0rACs2cUPfAkWxgSPzqkECgKS1jvQ== +"@changesets/types@^4.0.1": + version "4.0.1" + resolved "https://registry.npmjs.org/@changesets/types/-/types-4.0.1.tgz#85cf3cc32baff0691112d9d15fc21fbe022c9f0a" + integrity sha512-zVfv752D8K2tjyFmxU/vnntQ+dPu+9NupOSguA/2Zuym4tVxRh0ylArgKZ1bOAi2eXfGlZMxJU/kj7uCSI15RQ== -"@changesets/write@^0.1.4": - version "0.1.4" - resolved "https://registry.npmjs.org/@changesets/write/-/write-0.1.4.tgz#5828ecc70c48d0e8696c5f13fe06b730cdfde6f2" - integrity sha512-uco+vS3mo2JqflLciIU707har+6AEFOeP8pgu3vVC1M2WcKukQgR1KylHFqZJxKQWahf8mQnuUSbgR4yJQuhmA== +"@changesets/write@^0.1.5": + version "0.1.5" + resolved "https://registry.npmjs.org/@changesets/write/-/write-0.1.5.tgz#97574d95c8e48c3bbb1173802672f9a64d1b7fef" + integrity sha512-AYVSCH7on/Cyzo/8lVfqlsXmyKl3JhbNu9yHApdLPhHAzv5wqoHiZlMDkmd+AA67SRqzK2lDs4BcIojK+uWeIA== dependencies: "@babel/runtime" "^7.10.4" - "@changesets/types" "^4.0.0" + "@changesets/types" "^4.0.1" fs-extra "^7.0.1" human-id "^1.0.2" prettier "^1.19.1" @@ -6708,6 +6701,13 @@ resolved "https://registry.npmjs.org/@types/dagre/-/dagre-0.7.44.tgz#8f4b796b118ca29c132da7068fbc0d0351ee5851" integrity sha512-N6HD+79w77ZVAaVO7JJDW5yJ9LAxM62FpgNGO9xEde+KVYjDRyhIMzfiErXpr1g0JPon9kwlBzoBK6s4fOww9Q== +"@types/debug@^4.0.0": + version "4.1.7" + resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" + integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg== + dependencies: + "@types/ms" "*" + "@types/diff@^5.0.0": version "5.0.0" resolved "https://registry.npmjs.org/@types/diff/-/diff-5.0.0.tgz#eb71e94feae62548282c4889308a3dfb57e36020" @@ -6851,9 +6851,9 @@ "@types/node" "*" "@types/hast@^2.0.0": - version "2.3.1" - resolved "https://registry.npmjs.org/@types/hast/-/hast-2.3.1.tgz#b16872f2a6144c7025f296fb9636a667ebb79cd9" - integrity sha512-viwwrB+6xGzw+G1eWpF9geV3fnsDgXqHG+cqgiHrvQfDUW5hzhCyV7Sy3UJxhfRFBsgky2SSW33qi/YrIkjX5Q== + version "2.3.4" + resolved "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" + integrity sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g== dependencies: "@types/unist" "*" @@ -7141,6 +7141,11 @@ dependencies: "@types/unist" "*" +"@types/mdurl@^1.0.0": + version "1.0.2" + resolved "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" + integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== + "@types/micromatch@^4.0.1": version "4.0.1" resolved "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.1.tgz#9381449dd659fc3823fd2a4190ceacc985083bc7" @@ -7189,6 +7194,11 @@ dependencies: "@types/node" "*" +"@types/ms@*": + version "0.7.31" + resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" + integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== + "@types/node-fetch@^2.5.0", "@types/node-fetch@^2.5.7": version "2.5.8" resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb" @@ -7708,9 +7718,9 @@ integrity sha512-vX1NPekXhrLquFWskH2thcvFAha187F/lM6xYOoEMZWwJ/6alSk0/ttmGP/YRqcqtCv0TMbZjYAdZyHAEcuU4g== "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": - version "2.0.3" - resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" - integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== + version "2.0.6" + resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" + integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== "@types/unzipper@^0.10.3": version "0.10.3" @@ -9531,6 +9541,11 @@ bail@^1.0.0: resolved "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== +bail@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/bail/-/bail-2.0.1.tgz#d676736373a374058a935aec81b94c12ba815771" + integrity sha512-d5FoTAr2S5DSUPKl85WNm2yUwsINN8eidIdIwsOge2t33DaOfOdSmmsI11jMN3GmALCXaw+Y6HMVHDzePshFAA== + balanced-match@^0.4.2: version "0.4.2" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" @@ -10355,6 +10370,11 @@ ccount@^1.0.0: resolved "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== +ccount@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.0.tgz#3d6fb55803832766a24c6f339abc507297eb5d25" + integrity sha512-VOR0NWFYX65n9gELQdcpqsie5L5ihBXuZGAgaPEp/U7IOSjnPMEH6geE+2f6lcekaNEfWzAHS45mPvSo5bqsUA== + chainsaw@~0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" @@ -10449,16 +10469,31 @@ character-entities-legacy@^1.0.0: resolved "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== +character-entities-legacy@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-2.0.0.tgz#57f4d00974c696e8f74e9f493e7fcb75b44d7ee7" + integrity sha512-YwaEtEvWLpFa6Wh3uVLrvirA/ahr9fki/NUd/Bd4OR6EdJ8D22hovYQEOUCBfQfcqnC4IAMGMsHXY1eXgL4ZZA== + character-entities@^1.0.0: version "1.2.4" resolved "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== +character-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/character-entities/-/character-entities-2.0.0.tgz#508355fcc8c73893e0909efc1a44d28da2b6fdf3" + integrity sha512-oHqMj3eAuJ77/P5PaIRcqk+C3hdfNwyCD2DAUcD5gyXkegAuF2USC40CEqPscDk4I8FRGMTojGJQkXDsN5QlJA== + character-reference-invalid@^1.0.0: version "1.1.4" resolved "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== +character-reference-invalid@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.0.tgz#a0bdeb89c051fe7ed5d3158b2f06af06984f2813" + integrity sha512-pE3Z15lLRxDzWJy7bBHBopRwfI20sbrMVLQTC7xsPglCHf4Wv1e167OgYAFP78co2XlhojDyAqA+IAJse27//g== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -10945,6 +10980,11 @@ comma-separated-tokens@^1.0.0: resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== +comma-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.2.tgz#d4c25abb679b7751c880be623c1179780fe1dd98" + integrity sha512-G5yTt3KQN4Yn7Yk4ed73hlZ1evrFKXeUW3086p3PRFNp7m2vIjI6Pg+Kgb+oyzhd9F2qdcoj67+y3SdxL5XWsg== + command-exists@^1.2.9: version "1.2.9" resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" @@ -13224,6 +13264,11 @@ escape-string-regexp@^4.0.0: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + escodegen@^1.14.1, escodegen@^1.9.1: version "1.14.3" resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" @@ -16348,6 +16393,11 @@ is-alphabetical@1.0.4, is-alphabetical@^1.0.0: resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== +is-alphabetical@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.0.tgz#ef6e2caea57c63450fffc7abb6cbdafc5eb96e96" + integrity sha512-5OV8Toyq3oh4eq6sbWTYzlGdnMT/DPI5I0zxUBxjiigQsZycpkKF3kskkao3JyYGuYDHvhgJF+DrjMQp9SX86w== + is-alphanumerical@^1.0.0: version "1.0.4" resolved "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" @@ -16356,6 +16406,14 @@ is-alphanumerical@^1.0.0: is-alphabetical "^1.0.0" is-decimal "^1.0.0" +is-alphanumerical@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.0.tgz#0fbfeb6a72d21d91143b3d182bf6cf5909ee66f6" + integrity sha512-t+2GlJ+hO9yagJ+jU3+HSh80VKvz/3cG2cxbGGm4S0hjKuhWQXgPVUVOZz3tqZzMjhmphZ+1TIJTlRZRoe6GCQ== + dependencies: + is-alphabetical "^2.0.0" + is-decimal "^2.0.0" + is-arguments@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" @@ -16469,6 +16527,11 @@ is-decimal@^1.0.0: resolved "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== +is-decimal@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.0.tgz#db1140337809fd043a056ae40a9bd1cdc563034c" + integrity sha512-QfrfjQV0LjoWQ1K1XSoEZkTAzSa14RKVMa5zg3SdAfzEmQzRM4+tbSFWb78creCeA9rNBzaZal92opi1TwPWZw== + is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -16590,6 +16653,11 @@ is-hexadecimal@^1.0.0: resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== +is-hexadecimal@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.0.tgz#8e1ec9f48fe3eabd90161109856a23e0907a65d5" + integrity sha512-vGOtYkiaxwIiR0+Ng/zNId+ZZehGfINwTzdrDqc6iubbnQWhnPuYymOzOKUDqa2cSl59yHnEh2h6MvRLQsyNug== + is-in-browser@^1.0.2, is-in-browser@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" @@ -16724,6 +16792,11 @@ is-plain-obj@^3.0.0: resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== +is-plain-obj@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.0.0.tgz#06c0999fd7574edf5a906ba5644ad0feb3a84d22" + integrity sha512-NXRbBtUdBioI73y/HmOhogw/U5msYPC9DAtGkJXeFcFWSFZw0mCUsPxk/snTuJHzNKA8kLBK4rH97RMB1BfCXw== + is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -18894,6 +18967,11 @@ longest-streak@^2.0.0: resolved "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== +longest-streak@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/longest-streak/-/longest-streak-3.0.0.tgz#f127e2bded83caa6a35ac5f7a2f2b2f94b36f3dc" + integrity sha512-XhUjWR5CFaQ03JOP+iSDS9koy8T5jfoImCZ4XprElw3BXsSk4MpVYOLw/6LTDKZhO13PlAXnB5gS4MHQTpkSOw== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -19152,6 +19230,11 @@ markdown-table@^2.0.0: dependencies: repeat-string "^1.0.0" +markdown-table@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.1.tgz#88c48957aaf2a8014ccb2ba026776a1d736fe3dc" + integrity sha512-CBbaYXKSGnE1uLRpKA1SWgIRb2PQrpkllNWpZtZe6VojOJ4ysqiq7/2glYcmKsOYN09QgH/HEBX5hIshAeiK6A== + markdown-to-jsx@^6.11.4: version "6.11.4" resolved "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.4.tgz#b4528b1ab668aef7fe61c1535c27e837819392c5" @@ -19218,6 +19301,24 @@ mdast-util-definitions@^4.0.0: dependencies: unist-util-visit "^2.0.0" +mdast-util-definitions@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.0.tgz#b6d10ef00a3c4cf191e8d9a5fa58d7f4a366f817" + integrity sha512-5hcR7FL2EuZ4q6lLMUK5w4lHT2H3vqL9quPvYZ/Ku5iifrirfMHiGdhxdXMUbUkDmz5I+TYMd7nbaxUhbQkfpQ== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + unist-util-visit "^3.0.0" + +mdast-util-find-and-replace@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.1.0.tgz#69728acd250749f8aac6e150e07d1fd15619e829" + integrity sha512-1w1jbqAd13oU78QPBf5223+xB+37ecNtQ1JElq2feWols5oEYAl+SgNDnOZipe7NfLemoEt362yUS15/wip4mw== + dependencies: + escape-string-regexp "^5.0.0" + unist-util-is "^5.0.0" + unist-util-visit-parents "^4.0.0" + mdast-util-from-markdown@^0.8.0: version "0.8.1" resolved "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.1.tgz#781371d493cac11212947226190270c15dc97116" @@ -19228,11 +19329,38 @@ mdast-util-from-markdown@^0.8.0: micromark "~2.10.0" parse-entities "^2.0.0" +mdast-util-from-markdown@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.0.2.tgz#7c793bb96b053d12f032e37382ae989efb70ee66" + integrity sha512-gXaxv/5fGdrr9TqSMlQK7FmshK8yR9DvW3+NapMBDm44inORxIZVJa1D3yjrUT9ISu8tB/jjblEkUzyzclquNg== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + mdast-util-to-string "^3.1.0" + micromark "^3.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + micromark-util-decode-string "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + parse-entities "^3.0.0" + unist-util-stringify-position "^3.0.0" + mdast-util-gfm-autolink-literal@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-0.1.1.tgz#94675074d725ed7254b3172fa7e7c3252960de39" integrity sha512-gJ2xSpqKCetSr22GEWpZH3f5ffb4pPn/72m4piY0v7T/S+O7n7rw+sfoPLhb2b4O7WdnERoYdALRcmD68FMtlw== +mdast-util-gfm-autolink-literal@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.2.tgz#4032dcbaddaef7d4f2f3768ed830475bb22d3970" + integrity sha512-FzopkOd4xTTBeGXhXSBU0OCDDh5lUj2rd+HQqG92Ld+jL4lpUfgX2AT2OHAVP9aEeDKp7G92fuooSZcYJA3cRg== + dependencies: + "@types/mdast" "^3.0.0" + ccount "^2.0.0" + mdast-util-find-and-replace "^2.0.0" + micromark-util-character "^1.0.0" + mdast-util-gfm-strikethrough@^0.2.0: version "0.2.2" resolved "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-0.2.2.tgz#6e9ddd33ce41b06a60463e817f6ef4cf7bfa0655" @@ -19240,6 +19368,14 @@ mdast-util-gfm-strikethrough@^0.2.0: dependencies: mdast-util-to-markdown "^0.5.0" +mdast-util-gfm-strikethrough@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.0.tgz#6cc72ef5d9539f4cee76af3f15dd0daa9e3af40f" + integrity sha512-gM9ipBUdRxYa6Yq1Hd8Otg6jEn/dRxFZ1F9ZX4QHosHOexLGqNZO2dh0A+YFbUEd10RcKjnjb4jOfJJzoXXUew== + dependencies: + "@types/mdast" "^3.0.3" + mdast-util-to-markdown "^1.0.0" + mdast-util-gfm-table@^0.1.0: version "0.1.4" resolved "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-0.1.4.tgz#5b3d71d16294c6fae1c2c424d3a081ffc7407b83" @@ -19248,6 +19384,14 @@ mdast-util-gfm-table@^0.1.0: markdown-table "^2.0.0" mdast-util-to-markdown "^0.5.0" +mdast-util-gfm-table@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.1.tgz#07c269a219d66ec2deb6de38aed0ba1d1f9442df" + integrity sha512-NByKuaSg5+M6r9DZBPXFUmhMHGFf9u+WE76EeStN01ghi8hpnydiWBXr+qj0XCRWI7SAMNtEjGvip6zci9axQA== + dependencies: + markdown-table "^3.0.0" + mdast-util-to-markdown "^1.0.0" + mdast-util-gfm-task-list-item@^0.1.0: version "0.1.4" resolved "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-0.1.4.tgz#5899b1320d1b826f7658ac3171babf4ae1e626a2" @@ -19255,6 +19399,14 @@ mdast-util-gfm-task-list-item@^0.1.0: dependencies: mdast-util-to-markdown "^0.5.0" +mdast-util-gfm-task-list-item@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.0.tgz#a0aa2a00c893f9f006d13ba096cbc64608559c7f" + integrity sha512-dwkzOTjQe8JCCHVE3Cb0pLHTYLudf7t9WCAnb20jI8/dW+VHjgWhjtIUVA3oigNkssgjEwX+i+3XesUdCnXGyA== + dependencies: + "@types/mdast" "^3.0.3" + mdast-util-to-markdown "^1.0.0" + mdast-util-gfm@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-0.1.0.tgz#bac0efe703670d1b40474e6be13dbdd887273a04" @@ -19265,6 +19417,16 @@ mdast-util-gfm@^0.1.0: mdast-util-gfm-table "^0.1.0" mdast-util-gfm-task-list-item "^0.1.0" +mdast-util-gfm@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-1.0.0.tgz#5cbedeabae72be2c3dff1ec958394e2f9553ec43" + integrity sha512-JY4qImsTqivQ0Gl3qvdaizCpomFaNrHnjEhNjNNKeNEA5jZHAJDYu1+yO4V9jn4/ti8GrKdAScaT4F71knoxsA== + dependencies: + mdast-util-gfm-autolink-literal "^1.0.0" + mdast-util-gfm-strikethrough "^1.0.0" + mdast-util-gfm-table "^1.0.0" + mdast-util-gfm-task-list-item "^1.0.0" + mdast-util-to-hast@10.0.1: version "10.0.1" resolved "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" @@ -19279,6 +19441,21 @@ mdast-util-to-hast@10.0.1: unist-util-position "^3.0.0" unist-util-visit "^2.0.0" +mdast-util-to-hast@^11.0.0: + version "11.3.0" + resolved "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-11.3.0.tgz#ea9220617a710e80aa5cc3ac7cc9d4bb0440ae7a" + integrity sha512-4o3Cli3hXPmm1LhB+6rqhfsIUBjnKFlIUZvudaermXB+4/KONdd/W4saWWkC+LBLbPMqhFSSTSRgafHsT5fVJw== + dependencies: + "@types/hast" "^2.0.0" + "@types/mdast" "^3.0.0" + "@types/mdurl" "^1.0.0" + mdast-util-definitions "^5.0.0" + mdurl "^1.0.0" + unist-builder "^3.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + mdast-util-to-markdown@^0.5.0: version "0.5.3" resolved "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.5.3.tgz#e05c54a3ccd239bab63c48a1e5b5747f0dcd5aca" @@ -19291,11 +19468,29 @@ mdast-util-to-markdown@^0.5.0: repeat-string "^1.0.0" zwitch "^1.0.0" +mdast-util-to-markdown@^1.0.0: + version "1.2.3" + resolved "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.2.3.tgz#2b3af92bf0e29080927eb59a8a10cd0a7398e093" + integrity sha512-040jJYtjOUdbvYAXCfPrpLJRdvMOmR33KRqlhT4r+fEbVM+jao1RMbA8RmGeRmw8RAj3vQ+HvhIaJPijvnOwCg== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + longest-streak "^3.0.0" + mdast-util-to-string "^3.0.0" + micromark-util-decode-string "^1.0.0" + unist-util-visit "^4.0.0" + zwitch "^2.0.0" + mdast-util-to-string@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== +mdast-util-to-string@^3.0.0, mdast-util-to-string@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz#56c506d065fbf769515235e577b5a261552d56e9" + integrity sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA== + mdn-data@2.0.14: version "2.0.14" resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" @@ -19503,6 +19698,37 @@ microevent.ts@~0.1.1: resolved "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== +micromark-core-commonmark@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.0.1.tgz#a64987cafe872e8b80bc8f2352a5d988586ac4f1" + integrity sha512-vEOw8hcQ3nwHkKKNIyP9wBi8M50zjNajtmI+cCUWcVfJS+v5/3WCh4PLKf7PPRZFUutjzl4ZjlHwBWUKfb/SkA== + dependencies: + micromark-factory-destination "^1.0.0" + micromark-factory-label "^1.0.0" + micromark-factory-space "^1.0.0" + micromark-factory-title "^1.0.0" + micromark-factory-whitespace "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-chunked "^1.0.0" + micromark-util-classify-character "^1.0.0" + micromark-util-html-tag-name "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-resolve-all "^1.0.0" + micromark-util-subtokenize "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.1" + parse-entities "^3.0.0" + +micromark-extension-gfm-autolink-literal@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.0.tgz#1a49a62bfcb00f9dff87ab39f3b21a108612dc24" + integrity sha512-t+K0aPK32mXypVTEKV+WRfoT/Rb7MERDgHZVRr56NXpyQQhgMk72QnK4NljYUlrgbuesH+MxiPQwThzqRDIwvA== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-sanitize-uri "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + micromark-extension-gfm-autolink-literal@~0.5.0: version "0.5.1" resolved "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-0.5.1.tgz#5326fc86f3ae0fbba57bb0bfc2f158c9456528ce" @@ -19510,6 +19736,17 @@ micromark-extension-gfm-autolink-literal@~0.5.0: dependencies: micromark "~2.10.0" +micromark-extension-gfm-strikethrough@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.1.tgz#9f53ab4f5dc8c0525a889850bae615f074a98a27" + integrity sha512-fzGYXWz9HPWH1uHqYwdyR8XpEtuoYVHUjTdPQTnl3ETVZOQe1NXMwE3RA7AMqeON52hG+kO9g1/P1+pLONBSMQ== + dependencies: + micromark-util-chunked "^1.0.0" + micromark-util-classify-character "^1.0.0" + micromark-util-resolve-all "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + micromark-extension-gfm-strikethrough@~0.6.0: version "0.6.2" resolved "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-0.6.2.tgz#754788bdd13046e7f69edaa0d3f3d555d23128d6" @@ -19517,6 +19754,16 @@ micromark-extension-gfm-strikethrough@~0.6.0: dependencies: micromark "~2.10.0" +micromark-extension-gfm-table@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.0.tgz#f0d35dbf008b6182311049f9137323d34a54c7a0" + integrity sha512-OATRuHDgEAT/aaJJRSdU12V+s01kNSnJ0jumdfLq5mPy0F5DkR3zbTSFLH4tjVYM0/kEG6umxIhHY62mFe4z5Q== + dependencies: + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + micromark-extension-gfm-table@~0.4.0: version "0.4.1" resolved "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-0.4.1.tgz#79cc37da82d6ae0cc3901c1c6264b97a72372fbd" @@ -19524,11 +19771,28 @@ micromark-extension-gfm-table@~0.4.0: dependencies: micromark "~2.10.0" +micromark-extension-gfm-tagfilter@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.0.tgz#a38c7c462c2007b534fcb485e9310165879654a7" + integrity sha512-GGUZhzQrOdHR8RHU2ru6K+4LMlj+pBdNuXRtw5prOflDOk2hHqDB0xEgej1AHJ2VETeycX7tzQh2EmaTUOmSKg== + dependencies: + micromark-util-types "^1.0.0" + micromark-extension-gfm-tagfilter@~0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-0.3.0.tgz#d9f26a65adee984c9ccdd7e182220493562841ad" integrity sha512-9GU0xBatryXifL//FJH+tAZ6i240xQuFrSL7mYi8f4oZSbc+NvXjkrHemeYP0+L4ZUT+Ptz3b95zhUZnMtoi/Q== +micromark-extension-gfm-task-list-item@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.0.tgz#ab38b2b4ead4e746189d6323c32cacab2c63599d" + integrity sha512-3tkHCq1NNwijtwpjYba9+rl1yvQ4xYg8iQpUAfTJRyq8MtIEsBUF/vW6B9Gh8Qwy1hE2FmpyHhP4jnFAt61zLg== + dependencies: + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + micromark-extension-gfm-task-list-item@~0.3.0: version "0.3.1" resolved "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-0.3.1.tgz#24b7f54936a609179595f87a0e5279d1c6cf346c" @@ -19546,6 +19810,187 @@ micromark-extension-gfm@^0.3.0: micromark-extension-gfm-tagfilter "~0.3.0" micromark-extension-gfm-task-list-item "~0.3.0" +micromark-extension-gfm@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-1.0.0.tgz#7a1cee0df200e3f2ab14cb63d7a5ea6820a82f1d" + integrity sha512-OjqbQPL1Vec/4l5hnC8WnMNmWwgrT9JvzR2udqIGrGKecZsdwY9GAWZ5482CuD12SXuHNj8aS8epni6ip0Pwog== + dependencies: + micromark-extension-gfm-autolink-literal "^1.0.0" + micromark-extension-gfm-strikethrough "^1.0.0" + micromark-extension-gfm-table "^1.0.0" + micromark-extension-gfm-tagfilter "^1.0.0" + micromark-extension-gfm-task-list-item "^1.0.0" + micromark-util-combine-extensions "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-factory-destination@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz#fef1cb59ad4997c496f887b6977aa3034a5a277e" + integrity sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-factory-label@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.0.0.tgz#b316ec479b474232973ff13b49b576f84a6f2cbb" + integrity sha512-XWEucVZb+qBCe2jmlOnWr6sWSY6NHx+wtpgYFsm4G+dufOf6tTQRRo0bdO7XSlGPu5fyjpJenth6Ksnc5Mwfww== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-factory-space@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz#cebff49968f2b9616c0fcb239e96685cb9497633" + integrity sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-factory-title@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.0.0.tgz#708f7a8044f34a898c0efdb4f55e4da66b537273" + integrity sha512-flvC7Gx0dWVWorXuBl09Cr3wB5FTuYec8pMGVySIp2ZlqTcIjN/lFohZcP0EG//krTptm34kozHk7aK/CleCfA== + dependencies: + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-factory-whitespace@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz#e991e043ad376c1ba52f4e49858ce0794678621c" + integrity sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A== + dependencies: + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-character@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.1.0.tgz#d97c54d5742a0d9611a68ca0cd4124331f264d86" + integrity sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg== + dependencies: + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-chunked@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz#5b40d83f3d53b84c4c6bce30ed4257e9a4c79d06" + integrity sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g== + dependencies: + micromark-util-symbol "^1.0.0" + +micromark-util-classify-character@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz#cbd7b447cb79ee6997dd274a46fc4eb806460a20" + integrity sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-combine-extensions@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz#91418e1e74fb893e3628b8d496085639124ff3d5" + integrity sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA== + dependencies: + micromark-util-chunked "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-decode-numeric-character-reference@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz#dcc85f13b5bd93ff8d2868c3dba28039d490b946" + integrity sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w== + dependencies: + micromark-util-symbol "^1.0.0" + +micromark-util-decode-string@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.0.0.tgz#f97946825231d9c97df767875064401774578a6e" + integrity sha512-4g5UJ8P/J8wuRKUXCcB7udQuOBXpLyvBQSLSuznfBLCG+thKG6UTwFnXfHkrr/1wddprkUbPatCzxDjrJ+5zDg== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + parse-entities "^3.0.0" + +micromark-util-encode@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.0.0.tgz#c409ecf751a28aa9564b599db35640fccec4c068" + integrity sha512-cJpFVM768h6zkd8qJ1LNRrITfY4gwFt+tziPcIf71Ui8yFzY9wG3snZQqiWVq93PG4Sw6YOtcNiKJfVIs9qfGg== + +micromark-util-html-tag-name@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.0.0.tgz#75737e92fef50af0c6212bd309bc5cb8dbd489ed" + integrity sha512-NenEKIshW2ZI/ERv9HtFNsrn3llSPZtY337LID/24WeLqMzeZhBEE6BQ0vS2ZBjshm5n40chKtJ3qjAbVV8S0g== + +micromark-util-normalize-identifier@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz#4a3539cb8db954bbec5203952bfe8cedadae7828" + integrity sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg== + dependencies: + micromark-util-symbol "^1.0.0" + +micromark-util-resolve-all@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz#a7c363f49a0162e931960c44f3127ab58f031d88" + integrity sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw== + dependencies: + micromark-util-types "^1.0.0" + +micromark-util-sanitize-uri@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz#27dc875397cd15102274c6c6da5585d34d4f12b2" + integrity sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-encode "^1.0.0" + micromark-util-symbol "^1.0.0" + +micromark-util-subtokenize@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.0.tgz#6f006fa719af92776c75a264daaede0fb3943c6a" + integrity sha512-EsnG2qscmcN5XhkqQBZni/4oQbLFjz9yk3ZM/P8a3YUjwV6+6On2wehr1ALx0MxK3+XXXLTzuBKHDFeDFYRdgQ== + dependencies: + micromark-util-chunked "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-symbol@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.0.0.tgz#91cdbcc9b2a827c0129a177d36241bcd3ccaa34d" + integrity sha512-NZA01jHRNCt4KlOROn8/bGi6vvpEmlXld7EHcRH+aYWUfL3Wc8JLUNNlqUMKa0hhz6GrpUWsHtzPmKof57v0gQ== + +micromark-util-types@^1.0.0, micromark-util-types@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.0.1.tgz#8bb8a092d93d326bd29fe29602799f2d0d922fd4" + integrity sha512-UT0ylWEEy80RFYzK9pEaugTqaxoD/j0Y9WhHpSyitxd99zjoQz7JJ+iKuhPAgOW2MiPSUAx+c09dcqokeyaROA== + +micromark@^3.0.0: + version "3.0.5" + resolved "https://registry.npmjs.org/micromark/-/micromark-3.0.5.tgz#d24792c8a06f201d5608c106dbfadef34c299684" + integrity sha512-QfjERBnPw0G9mxhOCkkbRP0n8SX8lIBLrEKeEVceviUukqVMv3hWE4AgNTOK/W6GWqtPvvIHg2Apl3j1Dxm6aQ== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + micromark-core-commonmark "^1.0.1" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-chunked "^1.0.0" + micromark-util-combine-extensions "^1.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + micromark-util-encode "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-resolve-all "^1.0.0" + micromark-util-sanitize-uri "^1.0.0" + micromark-util-subtokenize "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.1" + parse-entities "^3.0.0" + micromark@~2.10.0: version "2.10.1" resolved "https://registry.npmjs.org/micromark/-/micromark-2.10.1.tgz#cd73f54e0656f10e633073db26b663a221a442a7" @@ -21214,6 +21659,18 @@ parse-entities@^2.0.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" +parse-entities@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/parse-entities/-/parse-entities-3.0.0.tgz#9ed6d6569b6cfc95ade058d683ddef239dad60dc" + integrity sha512-AJlcIFDNPEP33KyJLguv0xJc83BNvjxwpuUIcetyXUsLpVXAUCePJ5kIoYtEN2R1ac0cYaRu/vk9dVFkewHQhQ== + dependencies: + character-entities "^2.0.0" + character-entities-legacy "^2.0.0" + character-reference-invalid "^2.0.0" + is-alphanumerical "^2.0.0" + is-decimal "^2.0.0" + is-hexadecimal "^2.0.0" + parse-filepath@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" @@ -22307,17 +22764,7 @@ precond@0.2: resolved "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= -preferred-pm@^3.0.0: - version "3.0.2" - resolved "https://registry.npmjs.org/preferred-pm/-/preferred-pm-3.0.2.tgz#bbdbef1014e34a7490349bf70d6d244b8d57a5e1" - integrity sha512-yGIxyBkK/OWOppgCXfOeOXOeNrddyK1DzqS6XpOokRZb2ogXTpHRhKDTO7d0pjF/2p2sV9pEkKL4e0tNZI1y2A== - dependencies: - find-up "^5.0.0" - find-yarn-workspace-root2 "1.2.16" - path-exists "^4.0.0" - which-pm "2.0.0" - -preferred-pm@^3.0.3: +preferred-pm@^3.0.0, preferred-pm@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/preferred-pm/-/preferred-pm-3.0.3.tgz#1b6338000371e3edbce52ef2e4f65eb2e73586d6" integrity sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ== @@ -22551,6 +22998,11 @@ property-information@^5.3.0: dependencies: xtend "^4.0.0" +property-information@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/property-information/-/property-information-6.0.1.tgz#7c668d9f2b9cb63bc3e105d8b8dfee7221a17800" + integrity sha512-F4WUUAF7fMeF4/JUFHNBWDaKDXi2jbvqBW/y6o5wsf3j19wTZ7S60TmtB5HoBhtgw7NKQRMWuz5vk2PR0CygUg== + proto-list@~1.2.1: version "1.2.4" resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" @@ -23017,6 +23469,11 @@ react-hook-form@^7.12.2: resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.16.1.tgz#669046df378a71949e5cf8a2398cbe20d5cb27bc" integrity sha512-kcLDmSmlyLUFx2UU5bG/o4+3NeK753fhKodJa8gkplXohGkpAq0/p+TR24OWjZmkEc3ES7ppC5v5d6KUk+fJTA== +react-hook-form@^7.13.0: + version "7.17.4" + resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.17.4.tgz#232b6aaccddb91eb4a228ac20b154abd90866fdb" + integrity sha512-7XhbCr7d9fDC1TgcK/BUbt7D3q0VJMu7jPErfsa0JrxVjv/nni41xWdJcy0Zb7R+Np8OsCkQ2lMyloAtE3DLiQ== + react-hot-loader@^4.12.21: version "4.13.0" resolved "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.13.0.tgz#c27e9408581c2a678f5316e69c061b226dc6a202" @@ -23057,7 +23514,7 @@ react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-i resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -"react-is@^16.8.0 || ^17.0.0", react-is@^17.0.1, react-is@^17.0.2: +"react-is@^16.8.0 || ^17.0.0", react-is@^17.0.0, react-is@^17.0.1, react-is@^17.0.2: version "17.0.2" resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== @@ -23098,6 +23555,25 @@ react-markdown@^5.0.2: unist-util-visit "^2.0.0" xtend "^4.0.1" +react-markdown@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/react-markdown/-/react-markdown-7.0.1.tgz#c7365fcd7d1813b3ae68f2200e8f92d47d865627" + integrity sha512-pthNPaoiwg0q7hukoE04F2ENwSzijIlWHJ4UMs/96LUe/G/P3FnbP4qHzx3FoNqae+2SqDG8vzniTLnJDeWneg== + dependencies: + "@types/hast" "^2.0.0" + "@types/unist" "^2.0.0" + comma-separated-tokens "^2.0.0" + prop-types "^15.0.0" + property-information "^6.0.0" + react-is "^17.0.0" + remark-parse "^10.0.0" + remark-rehype "^9.0.0" + space-separated-tokens "^2.0.0" + style-to-object "^0.3.0" + unified "^10.0.0" + unist-util-visit "^4.0.0" + vfile "^5.0.0" + react-popper-tooltip@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz#329569eb7b287008f04fcbddb6370452ad3f9eac" @@ -23801,6 +24277,16 @@ remark-gfm@^1.0.0: mdast-util-gfm "^0.1.0" micromark-extension-gfm "^0.3.0" +remark-gfm@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/remark-gfm/-/remark-gfm-2.0.0.tgz#4fef4d7b35afa2bd3cd0410d313d32169634051c" + integrity sha512-waIv4Tjcd2CTUDxKRYzuPyIHw1FoX4H2GjXAzXV9PxQWb+dU4fJivd/FZ+nxyzPARrqTjMIkwIwPoWNbpBhjcQ== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-gfm "^1.0.0" + micromark-extension-gfm "^1.0.0" + unified "^10.0.0" + remark-mdx@1.6.22: version "1.6.22" resolved "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz#06a8dab07dcfdd57f3373af7f86bd0e992108bbd" @@ -23837,6 +24323,15 @@ remark-parse@8.0.3: vfile-location "^3.0.0" xtend "^4.0.1" +remark-parse@^10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.0.tgz#65e2b2b34d8581d36b97f12a2926bb2126961cb4" + integrity sha512-07ei47p2Xl7Bqbn9H2VYQYirnAFJPwdMuypdozWsSbnmrkgA2e2sZLZdnDNrrsxR4onmIzH/J6KXqKxCuqHtPQ== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-from-markdown "^1.0.0" + unified "^10.0.0" + remark-parse@^9.0.0: version "9.0.0" resolved "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz#4d20a299665880e4f4af5d90b7c7b8a935853640" @@ -23844,6 +24339,16 @@ remark-parse@^9.0.0: dependencies: mdast-util-from-markdown "^0.8.0" +remark-rehype@^9.0.0: + version "9.1.0" + resolved "https://registry.npmjs.org/remark-rehype/-/remark-rehype-9.1.0.tgz#e4b5b6e19c125b3780343eb66c3e9b99b0f06a81" + integrity sha512-oLa6YmgAYg19zb0ZrBACh40hpBLteYROaPLhBXzLgjqyHQrN+gVP9N/FJvfzuNNuzCutktkroXEZBrxAxKhh7Q== + dependencies: + "@types/hast" "^2.0.0" + "@types/mdast" "^3.0.0" + mdast-util-to-hast "^11.0.0" + unified "^10.0.0" + remark-squeeze-paragraphs@4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz#76eb0e085295131c84748c8e43810159c5653ead" @@ -25065,6 +25570,11 @@ space-separated-tokens@^1.0.0: resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== +space-separated-tokens@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.1.tgz#43193cec4fb858a2ce934b7f98b7f2c18107098b" + integrity sha512-ekwEbFp5aqSPKaqeY1PGrlGQxPNaq+Cnx4+bE2D8sciBQrHpbwoBbawqTN2+6jPs9IdWxxiUcN0K2pkczD3zmw== + spawn-command@^0.0.2-1: version "0.0.2-1" resolved "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" @@ -26581,6 +27091,11 @@ trough@^1.0.0: resolved "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== +trough@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz#94a3aa9d5ce379fc561f6244905b3f36b7458d96" + integrity sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w== + tryer@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" @@ -26984,6 +27499,19 @@ unified@9.2.0, unified@^9.0.0: trough "^1.0.0" vfile "^4.0.0" +unified@^10.0.0: + version "10.1.0" + resolved "https://registry.npmjs.org/unified/-/unified-10.1.0.tgz#4e65eb38fc2448b1c5ee573a472340f52b9346fe" + integrity sha512-4U3ru/BRXYYhKbwXV6lU6bufLikoAavTwev89H5UxY8enDFaAT2VXmIXYNm6hb5oHPng/EXr77PVyDFcptbk5g== + dependencies: + "@types/unist" "^2.0.0" + bail "^2.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^5.0.0" + union-value@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -27030,21 +27558,43 @@ unist-builder@2.0.3, unist-builder@^2.0.0: resolved "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== +unist-builder@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/unist-builder/-/unist-builder-3.0.0.tgz#728baca4767c0e784e1e64bb44b5a5a753021a04" + integrity sha512-GFxmfEAa0vi9i5sd0R2kcrI9ks0r82NasRq5QHh2ysGngrc6GiqD5CDf1FjPenY4vApmFASBIIlk/jj5J5YbmQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-generated@^1.0.0: version "1.1.6" resolved "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== +unist-util-generated@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.0.tgz#86fafb77eb6ce9bfa6b663c3f5ad4f8e56a60113" + integrity sha512-TiWE6DVtVe7Ye2QxOVW9kqybs6cZexNwTwSMVgkfjEReqy/xwGpAXb99OxktoWwmL+Z+Epb0Dn8/GNDYP1wnUw== + unist-util-is@^4.0.0: version "4.0.3" resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.3.tgz#e8b44db55fc20c43752b3346c116344d45d7c91d" integrity sha512-bTofCFVx0iQM8Jqb1TBDVRIQW03YkD3p66JOd/aCWuqzlLyUtx1ZAGw/u+Zw+SttKvSVcvTiKYbfrtLoLefykw== +unist-util-is@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.1.1.tgz#e8aece0b102fa9bc097b0fef8f870c496d4a6236" + integrity sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ== + unist-util-position@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== +unist-util-position@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.1.tgz#f8484b2da19a897a0180556d160c28633070dbb9" + integrity sha512-mgy/zI9fQ2HlbOtTdr2w9lhVaiFUHWQnZrFF2EUoVOqtAUdzqMtNiD99qA5a1IcjWVR8O6aVYE9u7Z2z1v0SQA== + unist-util-remove-position@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" @@ -27066,6 +27616,13 @@ unist-util-stringify-position@^2.0.0: dependencies: "@types/unist" "^2.0.2" +unist-util-stringify-position@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz#d517d2883d74d0daa0b565adc3d10a02b4a8cde9" + integrity sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA== + dependencies: + "@types/unist" "^2.0.0" + unist-util-visit-parents@1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz#f6e3afee8bdbf961c0e6f028ea3c0480028c3d06" @@ -27079,6 +27636,22 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.0" unist-util-is "^4.0.0" +unist-util-visit-parents@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-4.1.1.tgz#e83559a4ad7e6048a46b1bdb22614f2f3f4724f2" + integrity sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + +unist-util-visit-parents@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.0.tgz#44bbc5d25f2411e7dfc5cecff12de43296aa8521" + integrity sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + unist-util-visit@2.0.3, unist-util-visit@^2.0.0: version "2.0.3" resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" @@ -27088,6 +27661,24 @@ unist-util-visit@2.0.3, unist-util-visit@^2.0.0: unist-util-is "^4.0.0" unist-util-visit-parents "^3.0.0" +unist-util-visit@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-3.1.0.tgz#9420d285e1aee938c7d9acbafc8e160186dbaf7b" + integrity sha512-Szoh+R/Ll68QWAyQyZZpQzZQm2UPbxibDvaY8Xc9SUtYgPsDzx5AWSk++UUt2hJuow8mvwR+rG+LQLw+KsuAKA== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + unist-util-visit-parents "^4.0.0" + +unist-util-visit@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.0.tgz#f41e407a9e94da31594e6b1c9811c51ab0b3d8f5" + integrity sha512-n7lyhFKJfVZ9MnKtqbsqkQEk5P1KShj0+//V7mAcoI6bpbUjh3C/OG8HVD+pBihfh6Ovl01m8dkcv9HNqYajmQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + unist-util-visit-parents "^5.0.0" + universal-github-app-jwt@^1.0.1: version "1.1.0" resolved "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.0.tgz#0abaa876101cdf1d3e4c546be2768841c0c1b514" @@ -27514,6 +28105,14 @@ vfile-message@^2.0.0: "@types/unist" "^2.0.0" unist-util-stringify-position "^2.0.0" +vfile-message@^3.0.0: + version "3.0.2" + resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-3.0.2.tgz#db7eaebe7fecb853010f2ef1664427f52baf8f74" + integrity sha512-UUjZYIOg9lDRwwiBAuezLIsu9KlXntdxwG+nXnjuQAHvBpcX3x0eN8h+I7TkY5nkCXj+cWVp4ZqebtGBvok8ww== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^3.0.0" + vfile@^4.0.0: version "4.2.0" resolved "https://registry.npmjs.org/vfile/-/vfile-4.2.0.tgz#26c78ac92eb70816b01d4565e003b7e65a2a0e01" @@ -27525,6 +28124,16 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" +vfile@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/vfile/-/vfile-5.1.0.tgz#18e78016f0f71e98d737d40f0fca921dc264a600" + integrity sha512-4o7/DJjEaFPYSh0ckv5kcYkJTHQgCKdL8ozMM1jLAxO9ox95IzveDPXCZp08HamdWq8JXTkClDvfAKaeLQeKtg== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + unist-util-stringify-position "^3.0.0" + vfile-message "^3.0.0" + vinyl-file@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/vinyl-file/-/vinyl-file-3.0.0.tgz#b104d9e4409ffa325faadd520642d0a3b488b365" @@ -28548,3 +29157,8 @@ zwitch@^1.0.0: version "1.0.5" resolved "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== + +zwitch@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.2.tgz#91f8d0e901ffa3d66599756dde7f57b17c95dce1" + integrity sha512-JZxotl7SxAJH0j7dN4pxsTV6ZLXoLdGME+PsjkL/DaBrVryK9kTGq06GfKrwcSOqypP+fdXGoCHE36b99fWVoA==