Merge branch 'master' of github.com:spotify/backstage into shmidt-i/load-scaffolder-templates-from-api

This commit is contained in:
Ivan Shmidt
2020-06-26 09:25:20 +02:00
33 changed files with 916 additions and 725 deletions
+1
View File
@@ -5,3 +5,4 @@
registry "https://registry.npmjs.org/"
lastUpdateCheck 1580389148099
yarn-path ".yarn/releases/yarn-1.22.1.js"
network-timeout 600000
+23 -14
View File
@@ -10,29 +10,39 @@
## What is Backstage?
Backstage is an open platform for building developer portals.
The philosophy behind Backstage is simple: Don't expose your engineers to the full complexity of your infrastructure tooling. Engineers should be shipping code — not figuring out a whole new toolset every time they want to implement the basics. Backstage allows you add "stuff" (tooling, services, features, etc.) by adding a plugin, instead of building a new tool. This saves you work and avoids the need of your team to learn how to use and support yet another tool.
[Backstage](https://backstage.io/) is an open platform for building developer portals. Its based on the developer portal weve been using internally at Spotify for over four years. Backstage can be as simple as a services catalog or as powerful as the UX layer for your entire tech infrastructure.
For more information go to [backstage.io](https://backstage.io) or join our [Discord chatroom](https://discord.gg/EBHEGzX).
## What problem does Backstage solve?
### Features
* Create and manage all of your organizations software and microservices in one place
* Services catalog keeps track of all software and its ownership
* Visualizations provide information about your backend services and tooling, and help you monitor them
* A unified method for managing microservices offers both visibility and control
* Preset templates allow engineers to quickly create microservices in a standardized way ([coming soon](https://github.com/spotify/backstage/milestone/11))
* Centralized, full-featured technical documentation with integrated tooling that makes it easy for developers to set up, publish, and maintain alongside their code ([coming soon](https://github.com/spotify/backstage/milestone/15))
As companies grow, their infrastructure systems get messier. Backstage unifies all your infrastructure tooling, services, and documentation with a single, consistent UI.
### Benefits
* For engineering managers, it allows you to maintain standards and best practices across the organization, and can help you manage your whole tech ecosystem, from migrations to test certification.
* For end users (developers), it makes it fast and simple to build software components in a standardized way, and it provides a central place to manage all projects and documentation.
* For platform engineers, it enables extensibility and scalability by letting you easily integrate new tools and services (via plugins), as well as extending the functionality of existing ones.
* For everyone, its a single, consistent experience that ties all your infrastructure tooling, resources, standards, owners, contributors, and administrators together in one place.
This blog post provides more examples of how Backstage is used inside Spotify:
## Backstage Service Catalog (alpha)
https://labs.spotify.com/2020/03/17/what-the-heck-is-backstage-anyway/
The Backstage Service Catalog — actually, a software catalog, since it includes more than just services — is a centralized system that keeps track of ownership and metadata for all the software in your ecosystem (services, websites, libraries, data pipelines, etc). The catalog is built around the concept of [metadata yaml files](https://github.com/spotify/backstage/blob/master/docs/architecture-decisions/adr002-default-catalog-file-format.md#format) stored together with the code, which are then harvested and visualized in Backstage.
https://backstage.io/demos
![servce-catalog](https://backstage.io/blog/assets/6/header.png)
We have also found that the service catalog is a great way to organise the infrastructure tools you use to manage the software as well. This is how Backstage creates one developer portal for all your tools. Rather than asking teams to jump between different infrastructure UIs (and incurring additional cognitive overhead each time they make a context switch), most of these tools can be organised around the entities in the catalog.
## Project roadmap
We created Backstage about 4 years ago. While our 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:
- 🐣 **Phase 1:** Extensible frontend platform (Done ✅) - You will be able to easily create a single consistent UI layer for your internal infrastructure and tools. A set of reusable UX patterns and components help ensure a consistent experience between tools.
- 🐣 **Phase 1:** Extensible frontend platform (Done ✅) - You will be able to easily create a single consistent UI layer for your internal infrastructure and tools. A set of reusable [UX patterns and components](http://storybook.backstage.io) help ensure a consistent experience between tools.
- 🐢 **Phase 2:** Manage your stuff ([current focus](https://backstage.io/blog/2020/05/22/phase-2-service-catalog)) - Manage anything from microservices to software components to infrastructure and your service catalog. Regardless of whether you want to create a new library, view service deployment status in Kubernetes, or check the test coverage for a website -- Backstage will provide all of those tools - and many more - in a single developer portal.
- 🐢 **Phase 2:** Service Catalog ([alpha released](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha)) - With a single catalog, Backstage makes it easy for a team to manage ten services — and makes it possible for your company to manage thousands of them. Developers can get a uniform overview of all their software and related resources, regardless of how and where they are running, as well as an easy way to onboard and manage those resources.
- 🐇 **Phase 3:** Ecosystem (later) - Everyone's infrastructure stack is different. By fostering a vibrant community of contributors we hope to provide an ecosystem of Open Source plugins/integrations that allows you to pick the tools that match your stack.
@@ -46,14 +56,12 @@ The Backstage platform consists of a number of different components:
- **app** - Main web application that users interact with. It's built up by a number of different _Plugins_. This repo contains an example implementation of an app (located in `packages/example-app`) and you can easily get started with your own app by [creating one](docs/create-an-app.md).
- [**plugins**](https://github.com/spotify/backstage/tree/master/plugins) - Each plugin is treated as a self-contained web app and can include almost any type of content. Plugins all use a common set of platform API's and reusable UI components. Plugins can fetch data either from the _backend_ or through any RESTful API exposed through the _proxy_.
- [**backend**](https://github.com/spotify/backstage/tree/master/packages/backend) - GraphQL aggregation service that holds the model of your software ecosystem, including organisational information and what team owns what software. The backend also has a Plugin model for extending its graph.
- [**service catalog**](https://github.com/spotify/backstage/tree/master/packages/backend) - Service that holds the model of your software ecosystem, including organisational information and what team owns what software. The backend also has a Plugin model for extending its graph.
- **proxy** \* - Terminates HTTPS and exposes any RESTful API to Plugins.
- **identity** \* - A backend service that holds your organisation's metadata.
- **identity** - A backend service that holds your organisation's metadata.
_\* not yet released_
![overview](backstage_overview.png)
## Getting started
To run a Backstage app, you will need to have the following installed:
@@ -100,6 +108,7 @@ We would love your help in building Backstage! See [CONTRIBUTING](CONTRIBUTING.m
- [FAQ](docs/FAQ.md) - Frequently Asked Questions
- [Code of Conduct](CODE_OF_CONDUCT.md) - This is how we roll
- [Blog](https://backstage.io/blog/) - Announcements and updates
- [Newsletter](https://mailchi.mp/spotify/backstage-community)
- Give us a star ⭐️ - If you are using Backstage or think it is an interesting project, we would love a star ❤️
Or, if you are an open source developer and are interested in joining our team, please reach out to [foss-opportunities@spotify.com ](mailto:foss-opportunities@spotify.com)
+52 -55
View File
@@ -15,11 +15,11 @@ No, but it can be! Backstage is designed to be a developer portal for all your
infrastructure tooling, services, and documentation. So, it's not a monitoring
platform — but that doesn't mean you can't integrate a monitoring tool into
Backstage by writing
[a plugin](https://github.com/spotify/backstage/blob/master/docs/FAQ.md#what-is-a-plugin-in-backstage).
[a plugin](/docs/FAQ.md#what-is-a-plugin-in-backstage).
### How is Backstage licensed?
Backstage was released as open sourced software by Spotify and is licensed under
Backstage was released as open source software by Spotify and is licensed under
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
### Why did we open source Backstage?
@@ -36,16 +36,16 @@ more, read our blog post,
Yes, we've already started releasing open source versions of some of the plugins
we use here, and we'll continue to do so.
[Plugins](https://github.com/spotify/faq#what-is-a-plugin-in-backstage) are the
[Plugins](/docs/FAQ.md#what-is-a-plugin-in-backstage) are the
building blocks of functionality in Backstage. We have over 120 plugins inside
Spotify — many of those are specialized for our use, so will remain internal and
proprietary to us. But we estimate that about a third of our existing plugins
make good open source candidates. (And we'll probably end up writing some brand
new ones, too.)
new ones, too.)
### What's the roadmap for Backstage?
We envision three phases, which you can learn about in
We envision three phases, which you can learn about in
[our project roadmap](https://github.com/spotify/backstage#project-roadmap).
Even though the open source version of Backstage is relatively new compared to
our internal version, we have already begun work on various aspects of all three
@@ -80,141 +80,138 @@ knew that we would like to use.
It strikes a good balance between power, customizability, and ease of use. A
core focus of Backstage is to make plugin developers productive with as few
hurdles as possible. Material-UI lets plugin makers get going easily with both
well-known tech and a large flora of components.
well-known tech and a large flora of components.
### What technology does Backstage use?
The code base is a large-scale React application that uses TypeScript. For
The code base is a large-scale React application that uses TypeScript. For
[Phase 2](https://github.com/spotify/backstage#project-roadmap), we plan to use
Node.js and GraphQL.
Node.js and GraphQL.
### What is the end-to-end user flow? The happy path story.
There are three main user profiles for Backstage: the integrator, the
contributor, and the software engineer. The **integrator** hosts the Backstage
app and configures which plugins are available to use in the app. The
**contributor** adds functionality to the app by writing plugins. The
**software engineer** uses the app's functionality and interacts with its
plugins.
There are three main user profiles for Backstage: the integrator, the
contributor, and the software engineer.
### What is the use of a "plugin" in Backstage?
The **integrator** hosts the Backstage app and configures which plugins are available to use in the app.
A Backstage Plugin adds functionality to Backstage.
The **contributor** adds functionality to the app by writing plugins.
The **software engineer** uses the app's functionality and interacts with its plugins.
### What is a "plugin" in Backstage?
Plugins are what provide the feature functionality in Backstage. They are used to integrate different systems into Backstage's frontend, so that the developer gets a consistent UX, no matter what tool or service is being accessed on the other side.
Each plugin is treated as a self-contained web app and can include almost any type of content. Plugins all use a common set of platform APIs and reusable UI components. Plugins can fetch data either from the backend or an API exposed through the proxy.
Learn more about [the different components](https://github.com/spotify/backstage#overview) that make up Backstage.
### Do I have to write plugins in TypeScript?
No, you can use JavaScript if you prefer. We want to keep the Backstage core
APIs in TypeScript, but aren't forcing it on individual plugins.
No, you can use JavaScript if you prefer. We want to keep the Backstage core
APIs in TypeScript, but aren't forcing it on individual plugins.
### How do I find out if a plugin already exists?
Before you write a plugin,
Before you write a plugin,
[search the plugin issues](https://github.com/spotify/backstage/issues?q=is%3Aissue+label%3Aplugin+)
to see if it already exists or is in the works. If no one's thought of it yet,
great! Open a new issue as
[a plugin suggestion](https://github.com/spotify/backstage/issues/new/choose)
and describe what your plugin will do. This will help coordinate our
contributors' efforts and avoid duplicating existing functionality. In the
contributors' efforts and avoid duplicating existing functionality. In the
future, we will create
[a plugin gallery](https://github.com/spotify/backstage/issues/260) where people
can browse and search for all available plugins.
can browse and search for all available plugins.
### Which plugin is used the most at Spotify?
By far, our most-used plugin is our TechDocs plugin, which we use for creating
technical documentation. Our philosophy at Spotify is to treat "docs like code",
where you write documentation using the same workflow as you write your code.
This makes it easier to create, find, and update documentation. We hope to
release
[the open source version](https://github.com/spotify/backstage/issues/687) in
the future. (See also:
"[Will Spotify's internal plugins be open sourced, too?](https://github.com/spotify/faq#will-spotifys-internal-plugins-be-open-sourced-too)"
above)
By far, our most-used plugin is our TechDocs plugin, which we use for creating technical documentation. Our philosophy at Spotify is to treat "docs like code", where you write documentation using the same workflow as you write your code. This makes it easier to create, find, and update documentation. We hope to release [the open source version](https://github.com/spotify/backstage/issues/687) in the future. (See also: "[Will Spotify's internal plugins be open sourced, too?](/docs/FAQ.md#will-spotifys-internal-plugins-be-open-sourced-too)" above)
### Are you planning to have plugins baked into the repo? Or should they be developed in separate repos?
Contributors can add open source plugins to the plugins directory in
Contributors can add open source plugins to the plugins directory in
[this monorepo](https://github.com/spotify/backstage). Integrators can then
configure which open source plugins are available to use in their instance of
the app. Open source plugins are downloaded as npm packages published in the
open source repository. While we encourage using the open source model, we
open source repository. While we encourage using the open source model, we
know there are cases where contributors might want to experiment internally or
keep their plugins closed source. Contributors writing closed source plugins
should develop them in the plugins directory in their own Backstage repository.
Integrators also configure closed source plugins locally from the monorepo.
Integrators also configure closed source plugins locally from the monorepo.
### Any plans for integrating with other repository managers, such as GitLab or Bitbucket?
We chose GitHub because it is the tool that we are most familiar with, so that
We chose GitHub because it is the tool that we are most familiar with, so that
will naturally lead to integrations for GitHub being developed at an early
stage. Hosting this project on GitHub does not exclude integrations with
stage. Hosting this project on GitHub does not exclude integrations with
alternatives, such as
[GitLab](https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+GitLab)
or Bitbucket. We believe that in time there will be plugins that will provide
functionality for these tools as well. Hopefully, contributed by the community!
Also note, implementations of Backstage can be hosted wherever you feel suits
your needs best.
functionality for these tools as well. Hopefully, contributed by the community! Also note, implementations of Backstage can be hosted wherever you feel suits
your needs best.
### Who maintains Backstage?
Spotify will maintain the open source core, but we envision different parts of
Spotify will maintain the open source core, but we envision different parts of
the project being maintained by various companies and contributors. We also
envision a large, diverse ecosystem of open source plugins, which would be
maintained by their original authors/contributors or by the community. When it
maintained by their original authors/contributors or by the community. When it
comes to
[deployment](https://github.com/spotify/backstage/blob/master/DEPLOYMENT.md),
the system integrator (typically, the infrastructure team in your organization)
maintains Backstage in your own environment.
maintains Backstage in your own environment.
### Does Spotify provide a managed version of Backstage?
No, this is not a service offering. We build the piece of software, and
No, this is not a service offering. We build the piece of software, and
someone in your infrastructure team is responsible for
[deploying](https://github.com/spotify/backstage/blob/master/DEPLOYMENT.md) and
maintaining it.
maintaining it.
### How secure is Backstage?
We take security seriously. When it comes to packages and code we scan our
We take security seriously. When it comes to packages and code we scan our
repositories periodically and update our packages to the latest versions. When
it comes to deployment of Backstage within an organisation it depends on the
deployment and security setup in your organisation. Reach out to us on
[Discord](https://discord.gg/MUpMjP2) if you have specific queries.
Please report sensitive security issues via Spotify's
[bug-bounty program](https://hackerone.com/spotify) rather than GitHub.
[bug-bounty program](https://hackerone.com/spotify) rather than GitHub.
### Does Backstage collect any information that is shared with Spotify?
No. Backstage does not collect any telemetry from any third party using the
No. Backstage does not collect any telemetry from any third party using the
platform. Spotify, and the open source community, does have access to
[GitHub Insights](https://github.com/features/insights), which contains
information such as contributors, commits, traffic, and dependencies.
information such as contributors, commits, traffic, and dependencies.
Backstage is an open platform, but you are in control of your own data. You
control who has access to any data you provide to your version of Backstage and
who that data is shared with.
who that data is shared with.
### Can Backstage be used to build something other than a developer portal?
Yes. The core frontend framework could be used for building any large-scale
Yes. The core frontend framework could be used for building any large-scale
web application where (1) multiple teams are building separate parts of the app,
and (2) you want the overall experience to be consistent. That being said, in
and (2) you want the overall experience to be consistent. That being said, in
[Phase 2](https://github.com/spotify/backstage#project-roadmap) of the project
we will add features that are needed for developer portals and systems for
managing software ecosystems. Our ambition will be to keep Backstage modular.
managing software ecosystems. Our ambition will be to keep Backstage modular.
### How can I get involved?
Jump right in! Come help us fix some of the
Jump right in! Come help us fix some of the
[early bugs and first issues](https://github.com/spotify/backstage/labels/good%20first%20issue)
or reach [a new milestone](https://github.com/spotify/backstage/milestones). Or
write an open source plugin for Backstage, like this
[Lighthouse plugin](https://github.com/spotify/backstage/tree/master/plugins/lighthouse).
See all the ways you can
See all the ways you can
[contribute here](https://github.com/spotify/backstage/blob/master/CONTRIBUTING.md).
We'd love to have you as part of the community.
We'd love to have you as part of the community.
### Can I join the Backstage team?
If you're interested in being part of the Backstage team, reach out to
If you're interested in being part of the Backstage team, reach out to
[fossopportunities@spotify.com](mailto:fossopportunities@spotify.com)
+2 -2
View File
@@ -80,7 +80,7 @@ sign-in methods.
More details are provided in dedicated sections of the documentation.
- [OAuth](./oauth): Description of the generic OAuth flow implemented by the
- [OAuth](./oauth.md): Description of the generic OAuth flow implemented by the
[auth-backend](../../plugins/auth-backend).
- [Glossary](./glossary): Glossary of some common terms related to the auth
- [Glossary](./glossary.md): Glossary of some common terms related to the auth
flows.
+3 -1
View File
@@ -26,6 +26,7 @@
"@backstage/plugin-identity-backend": "^0.1.1-alpha.12",
"@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.12",
"@backstage/plugin-sentry-backend": "^0.1.1-alpha.12",
"dockerode": "^3.2.0",
"express": "^4.17.1",
"knex": "^0.21.1",
"sqlite3": "^4.2.0",
@@ -35,6 +36,7 @@
"@backstage/cli": "^0.1.1-alpha.12",
"@types/express": "^4.17.6",
"@types/express-serve-static-core": "^4.17.5",
"@types/helmet": "^0.0.47"
"@types/helmet": "^0.0.47",
"@types/dockerode": "^2.5.32"
}
}
+3 -1
View File
@@ -22,15 +22,17 @@ import {
Preparers,
} from '@backstage/plugin-scaffolder-backend';
import type { PluginEnvironment } from '../types';
import Docker from 'dockerode';
export default async function createPlugin({ logger }: PluginEnvironment) {
const templater = new CookieCutter();
const filePreparer = new FilePreparer();
const githubPreparer = new GithubPreparer();
const preparers = new Preparers();
const dockerClient = new Docker();
preparers.register('file', filePreparer);
preparers.register('github', githubPreparer);
return await createRouter({ preparers, templater, logger });
return await createRouter({ preparers, templater, logger, dockerClient });
}
@@ -26,6 +26,7 @@ import { AnnotateLocationEntityProcessor } from './processors/AnnotateLocationEn
import { EntityPolicyProcessor } from './processors/EntityPolicyProcessor';
import { FileReaderProcessor } from './processors/FileReaderProcessor';
import { GithubReaderProcessor } from './processors/GithubReaderProcessor';
import { GitlabReaderProcessor } from './processors/GitlabReaderProcessor';
import { LocationRefProcessor } from './processors/LocationEntityProcessor';
import * as result from './processors/results';
import {
@@ -56,6 +57,7 @@ export class LocationReaders implements LocationReader {
return [
new FileReaderProcessor(),
new GithubReaderProcessor(),
new GitlabReaderProcessor(),
new YamlProcessor(),
new EntityPolicyProcessor(entityPolicy),
new LocationRefProcessor(),
@@ -0,0 +1,93 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { LocationSpec } from '@backstage/catalog-model';
import fetch from 'node-fetch';
import * as result from './results';
import { LocationProcessor, LocationProcessorEmit } from './types';
export class GitlabReaderProcessor implements LocationProcessor {
async readLocation(
location: LocationSpec,
optional: boolean,
emit: LocationProcessorEmit,
): Promise<boolean> {
if (location.type !== 'gitlab') {
return false;
}
try {
const url = this.buildRawUrl(location.target);
const response = await fetch(url.toString());
if (response.ok) {
const data = await response.buffer();
emit(result.data(location, data));
} else {
const message = `${location.target} could not be read as ${url}, ${response.status} ${response.statusText}`;
if (response.status === 404) {
if (!optional) {
throw result.notFoundError(location, message);
}
} else {
throw result.generalError(location, message);
}
}
} catch (e) {
const message = `Unable to read ${location.type} ${location.target}, ${e}`;
emit(result.generalError(location, message));
}
return true;
}
// Converts
// from: https://gitlab.example.com/a/b/blob/master/c.yaml
// to: https://gitlab.example.com/a/b/raw/master/c.yaml
private buildRawUrl(target: string): URL {
try {
const url = new URL(target);
const [
empty,
userOrOrg,
repoName,
blobKeyword,
...restOfPath
] = url.pathname.split('/');
if (
empty !== '' ||
userOrOrg === '' ||
repoName === '' ||
blobKeyword !== 'blob' ||
!restOfPath.join('/').match(/\.yaml$/)
) {
throw new Error('Wrong Gitlab URL');
}
// Replace 'blob' with 'raw'
url.pathname = [empty, userOrOrg, repoName, 'raw', ...restOfPath].join(
'/',
);
return url;
} catch (e) {
throw new Error(`Incorrect url: ${target}, ${e}`);
}
}
}
+2
View File
@@ -23,7 +23,9 @@
"dependencies": {
"@backstage/backend-common": "^0.1.1-alpha.12",
"@backstage/catalog-model": "^0.1.1-alpha.12",
"@backstage/config": "^0.1.1-alpha.12",
"@types/express": "^4.17.6",
"@types/dockerode": "^2.5.32",
"compression": "^1.7.4",
"cors": "^2.8.5",
"dockerode": "^3.2.0",
@@ -0,0 +1,3 @@
{
"_copy_without_render": [".github/workflows/*"]
}
@@ -1,12 +1,7 @@
#!/bin/bash
#!/bin/sh
# package name is "__component_id__" so that yarn doesn't throw an error
# about invalid characters when running yarn commands. here we replace it with the actual name
sed -i -e "s/__component_id__/{{ cookiecutter.component_id }}/g" package.json
# node_modules was moved out of the template folder, during the pre_gen hook,
# to avoid cookie_cutter from copying all of them. time to move it back
mv ../../node_modules.tmp ../../\{\{cookiecutter.component_id\}\}/node_modules 2>/dev/null ||:
# move back the build directory that was moved out in the pre_gen hook (if it exists)
mv ../../build.tmp ../../\{\{cookiecutter.component_id\}\}/build 2>/dev/null ||:
# Move all template files to the root folder
mv ./* ../
cd ..
rm -rf {{cookiecutter.component_id}}
# # # # # # # #
@@ -1,9 +0,0 @@
#!/bin/bash
# no way to ignore files in cookiecutter, so move node_modules out while building
# to avoid cookiecutter from copying all of them
mv ../../\{\{cookiecutter.component_id\}\}/node_modules ../../node_modules.tmp 2>/dev/null ||:
# cookicutter really doesn't like the next.js build directory, so if the app has
# been built from inside the template folder, that folders needs to be moved out as well
mv ../../\{\{cookiecutter.component_id\}\}/build ../../build.tmp 2>/dev/null ||:
@@ -1,5 +1,5 @@
{
"name": "__component_id__",
"name": "{{ cookiecutter.component_id }}",
"version": "0.0.0",
"description": "{{ cookiecutter.description }}",
"license": "UNLICENSED",
@@ -0,0 +1,11 @@
FROM alpine:3.7
RUN apk add --update \
git \
python \
python-dev \
py-pip \
g++ && \
pip install cookiecutter && \
apk del g++ py-pip python-dev && \
rm -rf /var/cache/apk/*
+1
View File
@@ -16,3 +16,4 @@
export * from './scaffolder';
export * from './service/router';
@@ -0,0 +1,151 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
jest.mock('./helpers', () => ({ runDockerContainer: jest.fn() }));
import { CookieCutter } from './cookiecutter';
import fs from 'fs-extra';
import os from 'os';
import { RunDockerContainerOptions } from './helpers';
import { PassThrough } from 'stream';
import Docker from 'dockerode';
describe('CookieCutter Templater', () => {
const cookie = new CookieCutter();
const mockDocker = {} as Docker;
const {
runDockerContainer,
}: {
runDockerContainer: jest.Mock<RunDockerContainerOptions>;
} = require('./helpers');
beforeEach(async () => {
jest.clearAllMocks();
await fs.remove(`${os.tmpdir()}/cookiecutter.json`);
});
it('should write a cookiecutter.json file with the values from the entitiy', async () => {
const tempdir = os.tmpdir();
const values = {
component_id: 'test',
description: 'description',
};
await cookie.run({ directory: tempdir, values, dockerClient: mockDocker });
const cookieCutterJson = await fs.readJSON(`${tempdir}/cookiecutter.json`);
expect(cookieCutterJson).toEqual(expect.objectContaining(values));
});
it('should merge any value that is in the cookiecutter.json path already', async () => {
const tempdir = os.tmpdir();
const existingJson = {
_copy_without_render: ['./github/workflows/*'],
};
await fs.writeJSON(`${tempdir}/cookiecutter.json`, existingJson);
const values = {
component_id: 'hello',
description: 'im something cool',
};
await cookie.run({ directory: tempdir, values, dockerClient: mockDocker });
const cookieCutterJson = await fs.readJSON(`${tempdir}/cookiecutter.json`);
expect(cookieCutterJson).toEqual({ ...existingJson, ...values });
});
it('should throw an error if the cookiecutter json is malformed and not missing', async () => {
const tempdir = os.tmpdir();
await fs.writeFile(`${tempdir}/cookiecutter.json`, "{'");
const values = {
component_id: 'hello',
description: 'im something cool',
};
await expect(
cookie.run({ directory: tempdir, values, dockerClient: mockDocker }),
).rejects.toThrow(/Unexpected token ' in JSON at position 1/);
});
it('should run the correct docker container with the correct bindings for the volumes', async () => {
const tempdir = os.tmpdir();
const values = {
component_id: 'test',
description: 'description',
};
await cookie.run({ directory: tempdir, values, dockerClient: mockDocker });
expect(runDockerContainer).toHaveBeenCalledWith({
imageName: 'backstage/cookiecutter',
args: ['cookiecutter', '--no-input', '-o', '/result', '/template'],
templateDir: tempdir,
resultDir: `${tempdir}/result`,
logStream: undefined,
dockerClient: mockDocker,
});
});
it('should return the result path to the end templated folder', async () => {
const tempdir = os.tmpdir();
const values = {
component_id: 'test',
description: 'description',
};
const path = await cookie.run({
directory: tempdir,
values,
dockerClient: mockDocker,
});
expect(path).toBe(`${tempdir}/result`);
});
it('should pass through the streamer to the run docker helper', async () => {
const stream = new PassThrough();
const tempdir = os.tmpdir();
const values = {
component_id: 'test',
description: 'description',
};
await cookie.run({
directory: tempdir,
values,
logStream: stream,
dockerClient: mockDocker,
});
expect(runDockerContainer).toHaveBeenCalledWith({
imageName: 'backstage/cookiecutter',
args: ['cookiecutter', '--no-input', '-o', '/result', '/template'],
templateDir: tempdir,
resultDir: `${tempdir}/result`,
logStream: stream,
dockerClient: mockDocker,
});
});
});
@@ -16,17 +16,52 @@ import { TemplaterBase, TemplaterRunOptions } from '.';
* limitations under the License.
*/
import fs from 'fs-extra';
import { JsonValue } from '@backstage/config';
import { runDockerContainer } from './helpers';
export class CookieCutter implements TemplaterBase {
private async fetchTemplateCookieCutter(
directory: string,
): Promise<Record<string, JsonValue>> {
try {
return await fs.readJSON(`${directory}/cookiecutter.json`);
} catch (ex) {
if (ex.code !== 'ENOENT') {
throw ex;
}
return {};
}
}
public async run(options: TemplaterRunOptions): Promise<string> {
// first we need to make cookiecutter.json in the directory provided with the input values.
// First lets grab the default cookiecutter.json file
const cookieCutterJson = await this.fetchTemplateCookieCutter(
options.directory,
);
const cookieInfo = {
_copy_without_render: ['.github/workflows/*'],
...cookieCutterJson,
...options.values,
};
await fs.writeJSON(`${options.directory}/cookiecutter.json`, cookieInfo);
return '';
// run cookie cutter with new json
const templateDir = options.directory;
// TODO(blam): This should be an entirely different directory on the host machine
// not in the template directory
const resultDir = `${templateDir}/result`;
await runDockerContainer({
imageName: 'backstage/cookiecutter',
args: ['cookiecutter', '--no-input', '-o', '/result', '/template'],
templateDir,
resultDir,
logStream: options.logStream,
dockerClient: options.dockerClient,
});
return resultDir;
}
}
@@ -0,0 +1,135 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Stream, { PassThrough } from 'stream';
import os from 'os';
import fs from 'fs';
import Docker from 'dockerode';
import { runDockerContainer } from './helpers';
describe('helpers', () => {
const mockDocker = new Docker() as jest.Mocked<Docker>;
beforeEach(() => {
jest
.spyOn(mockDocker, 'run')
.mockResolvedValue([{ Error: null, StatusCode: 0 }]);
});
describe('runDockerContainer', () => {
const imageName = 'blam/github:ben';
const args = ['bash', '-c', 'echo lol'];
const templateDir = os.tmpdir();
const resultDir = os.tmpdir();
it('should call the dockerClient run command with the correct arguments passed through', async () => {
await runDockerContainer({
imageName,
args,
templateDir,
resultDir,
dockerClient: mockDocker,
});
expect(mockDocker.run).toHaveBeenCalledWith(
imageName,
args,
expect.any(Stream),
expect.objectContaining({
HostConfig: {
Binds: expect.arrayContaining([
`${await fs.promises.realpath(templateDir)}:/template`,
`${await fs.promises.realpath(resultDir)}:/result`,
]),
},
Volumes: {
'/template': {},
'/result': {},
},
}),
);
});
it('throws a correct error if the templating fails in docker', async () => {
mockDocker.run.mockResolvedValueOnce([
{
Error: new Error('Something went wrong with docker'),
StatusCode: 0,
},
]);
await expect(
runDockerContainer({
imageName,
args,
templateDir,
resultDir,
dockerClient: mockDocker,
}),
).rejects.toThrow(/Something went wrong with docker/);
});
it('throws a correct error when the response code of the container is non-zero', async () => {
mockDocker.run.mockResolvedValueOnce([
{
Error: null,
StatusCode: 123,
},
]);
await expect(
runDockerContainer({
imageName,
args,
templateDir,
resultDir,
dockerClient: mockDocker,
}),
).rejects.toThrow(
/Docker container returned a non-zero exit code \(123\)/,
);
});
it('should pass through the log stream to the docker client', async () => {
const logStream = new PassThrough();
await runDockerContainer({
imageName,
args,
templateDir,
resultDir,
logStream,
dockerClient: mockDocker,
});
expect(mockDocker.run).toHaveBeenCalledWith(
imageName,
args,
logStream,
expect.objectContaining({
HostConfig: {
Binds: expect.arrayContaining([
`${await fs.promises.realpath(templateDir)}:/template`,
`${await fs.promises.realpath(resultDir)}:/result`,
]),
},
Volumes: {
'/template': {},
'/result': {},
},
}),
);
});
});
});
@@ -0,0 +1,77 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Writable, PassThrough } from 'stream';
import Docker from 'dockerode';
import fs from 'fs';
export type RunDockerContainerOptions = {
imageName: string;
args: string[];
logStream?: Writable;
resultDir: string;
templateDir: string;
dockerClient: Docker;
};
/**
*
* @param options the options object
* @param options.imageName the image to run
* @param options.args the arguments to pass the container
* @param options.logStream the log streamer to capture log messages
* @param options.resultDir the /result path inside the container
* @param options.templateDir the /template path inside the container
* @param options.dockerClient the dockerClient to use
*/
export const runDockerContainer = async ({
imageName,
args,
logStream = new PassThrough(),
resultDir,
templateDir,
dockerClient,
}: RunDockerContainerOptions) => {
const [{ Error: error, StatusCode: statusCode }] = await dockerClient.run(
imageName,
args,
logStream,
{
Volumes: { '/result': {}, '/template': {} },
HostConfig: {
Binds: [
// Need to use realpath here as Docker mounting does not like
// symlinks for binding volumes
`${await fs.promises.realpath(resultDir)}:/result`,
`${await fs.promises.realpath(templateDir)}:/template`,
],
},
},
);
if (error) {
throw new Error(
`Docker failed to run with the following error message: ${error}`,
);
}
if (statusCode !== 0) {
throw new Error(
`Docker container returned a non-zero exit code (${statusCode})`,
);
}
return { error, statusCode };
};
@@ -14,6 +14,9 @@
* limitations under the License.
*/
import type { Writable } from 'stream';
import Docker from 'dockerode';
export interface RequiredTemplateValues {
component_id: string;
}
@@ -21,12 +24,14 @@ export interface RequiredTemplateValues {
export interface TemplaterRunOptions {
directory: string;
values: RequiredTemplateValues & object;
logStream?: Writable;
dockerClient: Docker;
}
export abstract class TemplaterBase {
export type TemplaterBase = {
// runs the templating with the values and returns the directory to push the VCS
abstract async run(opts: TemplaterRunOptions): Promise<string>;
}
run(opts: TemplaterRunOptions): Promise<string>;
};
export interface TemplaterConfig {
templater?: TemplaterBase;
@@ -19,18 +19,20 @@ import Router from 'express-promise-router';
import express from 'express';
import { PreparerBuilder, TemplaterBase } from '../scaffolder';
import { TemplateEntityV1alpha1 } from '@backstage/catalog-model';
import Docker from 'dockerode';
export interface RouterOptions {
preparers: PreparerBuilder;
templater: TemplaterBase;
logger: Logger;
dockerClient: Docker;
}
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const router = Router();
const { preparers, templater, logger: parentLogger } = options;
const { preparers, templater, logger: parentLogger, dockerClient } = options;
const logger = parentLogger.child({ plugin: 'scaffolder' });
router.post('/v1/jobs', async (_, res) => {
@@ -66,12 +68,16 @@ export async function createRouter(
const preparer = preparers.get(mockEntity);
// Run the preparer for the mock entity to produce a temporary directory with template in
const path = await preparer.prepare(mockEntity);
const skeletonPath = await preparer.prepare(mockEntity);
// Run the templater on the mock directory with values from the post body
await templater.run({ directory: path, values: { component_id: 'test' } });
const templatedPath = await templater.run({
directory: skeletonPath,
values: { component_id: 'test' },
dockerClient,
});
console.warn(path);
console.warn(templatedPath);
});
const app = express();
@@ -44,42 +44,42 @@ class TechDocsCore(BasePlugin):
config["plugins"]["search"] = search_plugin
# Markdown Extensions
config['markdown_extensions'].append('admonition')
config['markdown_extensions'].append('abbr')
config['markdown_extensions'].append('attr_list')
config['markdown_extensions'].append('def_list')
config['markdown_extensions'].append('codehilite')
config['mdx_configs']['codehilite'] = {
'linenums': True,
'guess_lang': False,
'pygments_style': 'friendly',
config["markdown_extensions"].append("admonition")
config["markdown_extensions"].append("abbr")
config["markdown_extensions"].append("attr_list")
config["markdown_extensions"].append("def_list")
config["markdown_extensions"].append("codehilite")
config["mdx_configs"]["codehilite"] = {
"linenums": True,
"guess_lang": False,
"pygments_style": "friendly",
}
config['markdown_extensions'].append('toc')
config['mdx_configs']['toc'] = {
'permalink': True,
config["markdown_extensions"].append("toc")
config["mdx_configs"]["toc"] = {
"permalink": True,
}
config['markdown_extensions'].append('footnotes')
config['markdown_extensions'].append('markdown.extensions.tables')
config['markdown_extensions'].append('pymdownx.betterem')
config['mdx_configs']['pymdownx.betterem'] = {
'smart_enable': 'all',
config["markdown_extensions"].append("footnotes")
config["markdown_extensions"].append("markdown.extensions.tables")
config["markdown_extensions"].append("pymdownx.betterem")
config["mdx_configs"]["pymdownx.betterem"] = {
"smart_enable": "all",
}
config['markdown_extensions'].append('pymdownx.caret')
config['markdown_extensions'].append('pymdownx.critic')
config['markdown_extensions'].append('pymdownx.details')
config['markdown_extensions'].append('pymdownx.emoji')
config['mdx_configs']['pymdownx.emoji'] = {
'emoji_generator': '!!python/name:pymdownx.emoji.to_svg',
config["markdown_extensions"].append("pymdownx.caret")
config["markdown_extensions"].append("pymdownx.critic")
config["markdown_extensions"].append("pymdownx.details")
config["markdown_extensions"].append("pymdownx.emoji")
config["mdx_configs"]["pymdownx.emoji"] = {
"emoji_generator": "!!python/name:pymdownx.emoji.to_svg",
}
config['markdown_extensions'].append('pymdownx.inlinehilite')
config['markdown_extensions'].append('pymdownx.magiclink')
config['markdown_extensions'].append('pymdownx.mark')
config['markdown_extensions'].append('pymdownx.smartsymbols')
config['markdown_extensions'].append('pymdownx.superfences')
config['markdown_extensions'].append('pymdownx.tasklist')
config['mdx_configs']['pymdownx.tasklist'] = {
'custom_checkbox': True,
config["markdown_extensions"].append("pymdownx.inlinehilite")
config["markdown_extensions"].append("pymdownx.magiclink")
config["markdown_extensions"].append("pymdownx.mark")
config["markdown_extensions"].append("pymdownx.smartsymbols")
config["markdown_extensions"].append("pymdownx.superfences")
config["markdown_extensions"].append("pymdownx.tasklist")
config["mdx_configs"]["pymdownx.tasklist"] = {
"custom_checkbox": True,
}
config['markdown_extensions'].append('pymdownx.tilde')
config["markdown_extensions"].append("pymdownx.tilde")
return config
+1
View File
@@ -22,6 +22,7 @@
},
"dependencies": {
"@backstage/core": "^0.1.1-alpha.12",
"@backstage/test-utils": "^0.1.1-alpha.12",
"@backstage/theme": "^0.1.1-alpha.12",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
@@ -1,34 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { render } from '@testing-library/react';
import mockFetch from 'jest-fetch-mock';
import ExampleComponent from './ExampleComponent';
import { ThemeProvider } from '@material-ui/core';
import { lightTheme } from '@backstage/theme';
describe('ExampleComponent', () => {
it('should render', () => {
mockFetch.mockResponse(() => new Promise(() => {}));
const rendered = render(
<ThemeProvider theme={lightTheme}>
<ExampleComponent />
</ThemeProvider>,
);
expect(rendered.getByText('Welcome to techdocs!')).toBeInTheDocument();
});
});
@@ -1,57 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { FC } from 'react';
import { Typography, Grid } from '@material-ui/core';
import {
InfoCard,
Header,
Page,
pageTheme,
Content,
ContentHeader,
HeaderLabel,
SupportButton,
} from '@backstage/core';
import ExampleFetchComponent from '../ExampleFetchComponent';
const ExampleComponent: FC<{}> = () => (
<Page theme={pageTheme.tool}>
<Header title="Welcome to techdocs!" subtitle="Optional subtitle">
<HeaderLabel label="Owner" value="Team X" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Content>
<ContentHeader title="Plugin title">
<SupportButton>A description of your plugin goes here.</SupportButton>
</ContentHeader>
<Grid container spacing={3} direction="column">
<Grid item>
<InfoCard title="Information card">
<Typography variant="body1">
All content should be wrapped in a card like this.
</Typography>
</InfoCard>
</Grid>
<Grid item>
<ExampleFetchComponent />
</Grid>
</Grid>
</Content>
</Page>
);
export default ExampleComponent;
@@ -1,108 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { FC } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { Table, TableColumn, Progress } from '@backstage/core';
import Alert from '@material-ui/lab/Alert';
import { useAsync } from 'react-use';
const useStyles = makeStyles({
avatar: {
height: 32,
width: 32,
borderRadius: '50%',
},
});
type User = {
gender: string; // "male"
name: {
title: string; // "Mr",
first: string; // "Duane",
last: string; // "Reed"
};
location: object; // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…}
email: string; // "duane.reed@example.com"
login: object; // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…}
dob: object; // {date: "1983-06-22T12:30:23.016Z", age: 37}
registered: object; // {date: "2006-06-13T18:48:28.037Z", age: 14}
phone: string; // "07-2154-5651"
cell: string; // "0405-592-879"
id: {
name: string; // "TFN",
value: string; // "796260432"
};
picture: { medium: string }; // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…}
nat: string; // "AU"
};
type DenseTableProps = {
users: User[];
};
export const DenseTable: FC<DenseTableProps> = ({ users }) => {
const classes = useStyles();
const columns: TableColumn[] = [
{ title: 'Avatar', field: 'avatar' },
{ title: 'Name', field: 'name' },
{ title: 'Email', field: 'email' },
{ title: 'Nationality', field: 'nationality' },
];
const data = users.map(user => {
return {
avatar: (
<img
src={user.picture.medium}
className={classes.avatar}
alt={user.name.first}
/>
),
name: `${user.name.first} ${user.name.last}`,
email: user.email,
nationality: user.nat,
};
});
return (
<Table
title="Example User List (fetching data from randomuser.me)"
options={{ search: false, paging: false }}
columns={columns}
data={data}
/>
);
};
const ExampleFetchComponent: FC<{}> = () => {
const { value, loading, error } = useAsync(async (): Promise<User[]> => {
const response = await fetch('https://randomuser.me/api/?results=20');
const data = await response.json();
return data.results;
}, []);
if (loading) {
return <Progress />;
} else if (error) {
return <Alert severity="error">{error.message}</Alert>;
}
return <DenseTable users={value || []} />;
};
export default ExampleFetchComponent;
+4 -4
View File
@@ -30,16 +30,16 @@
*/
import { createPlugin, createRouteRef } from '@backstage/core';
import ExampleComponent from './components/ExampleComponent';
import { Reader } from './reader/components/Reader';
export const rootRouteRef = createRouteRef({
path: '/techdocs',
title: 'techdocs',
path: '/docs',
title: 'Docs',
});
export const plugin = createPlugin({
id: 'techdocs',
register({ router }) {
router.addRoute(rootRouteRef, ExampleComponent);
router.addRoute(rootRouteRef, Reader);
},
});
@@ -0,0 +1,82 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { useShadowDom } from '..';
import { useAsync } from 'react-use';
const useFetch = (url: string) => {
const state = useAsync(async () => {
const response = await fetch(url);
const raw = await response.text();
return raw;
}, [url]);
return state;
};
const addBaseUrl = (htmlString: string, baseUrl: string): string => {
const domParser = new DOMParser().parseFromString(htmlString, 'text/html');
const updateDom = <T extends Element>(
list: Array<T>,
attributeName: string,
): void => {
Array.from(list).forEach((elem: T) => {
const newUrl = new URL(
elem.getAttribute(attributeName)!,
baseUrl,
).toString();
elem.setAttribute(attributeName, newUrl);
});
};
updateDom<HTMLImageElement>(Array.from(domParser.images), 'src');
updateDom<HTMLAnchorElement | HTMLAreaElement>(
Array.from(domParser.links),
'href',
);
updateDom<HTMLLinkElement>(
Array.from(domParser.querySelectorAll('link')),
'href',
);
return domParser.body.parentElement?.outerHTML || htmlString;
};
export const Reader = () => {
const shadowDomRef = useShadowDom();
const state = useFetch(
'https://techdocs-mock-sites.storage.googleapis.com/mkdocs/index.html',
);
React.useEffect(() => {
const divElement = shadowDomRef.current;
if (divElement?.shadowRoot && state.value) {
divElement.shadowRoot.innerHTML = addBaseUrl(
state.value,
'https://techdocs-mock-sites.storage.googleapis.com/mkdocs/',
);
}
}, [shadowDomRef, state]);
return (
<>
<h3>Shadow DOM should be underneath</h3>
<div ref={shadowDomRef} />
</>
);
};
@@ -14,4 +14,4 @@
* limitations under the License.
*/
export { default } from './ExampleComponent';
export { useShadowDom } from './shadowDom';
@@ -0,0 +1,44 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { renderWithEffects } from '@backstage/test-utils';
import { useShadowDom } from './shadowDom';
const ComponentWithoutHook = () => {
return <div data-testid="shadow-dom" />;
};
const ComponentWithHook = () => {
const ref = useShadowDom();
return <div data-testid="shadow-dom" ref={ref} />;
};
describe('useShadowDom', () => {
it('does not create a Shadow DOM instance', async () => {
const rendered = await renderWithEffects(<ComponentWithoutHook />);
const divElement = rendered.getByTestId('shadow-dom');
expect(divElement.shadowRoot).not.toBeInstanceOf(ShadowRoot);
});
it('create a Shadow DOM instance', async () => {
const rendered = await renderWithEffects(<ComponentWithHook />);
const divElement = rendered.getByTestId('shadow-dom');
expect(divElement.shadowRoot).toBeInstanceOf(ShadowRoot);
});
});
@@ -14,15 +14,17 @@
* limitations under the License.
*/
import React from 'react';
import { render } from '@testing-library/react';
import mockFetch from 'jest-fetch-mock';
import ExampleFetchComponent from './ExampleFetchComponent';
import { useEffect, useRef } from 'react';
import type { RefObject } from 'react';
describe('ExampleFetchComponent', () => {
it('should render', async () => {
mockFetch.mockResponse(() => new Promise(() => {}));
const rendered = render(<ExampleFetchComponent />);
expect(await rendered.findByTestId('progress')).toBeInTheDocument();
});
});
type IShadowDOMRefObject = RefObject<HTMLDivElement>;
export const useShadowDom: () => IShadowDOMRefObject = () => {
const ref: IShadowDOMRefObject = useRef(null);
useEffect(() => {
const divElement = ref.current;
divElement?.attachShadow({ mode: 'open' });
}, [ref]);
return ref;
};
@@ -14,4 +14,4 @@
* limitations under the License.
*/
export { default } from './ExampleFetchComponent';
export * from './hooks';
+115 -372
View File
@@ -1184,71 +1184,6 @@
unique-filename "^1.1.1"
which "^1.3.1"
"@graphql-toolkit/common@0.10.7", "@graphql-toolkit/common@~0.10.2":
version "0.10.7"
resolved "https://registry.npmjs.org/@graphql-toolkit/common/-/common-0.10.7.tgz#e5d4ddeae080c4f7357bc7d6a8d02e75578e9bd1"
integrity sha512-epcJvmIAo+vSEY76F0Dj1Ef6oeewT5pdMe1obHj7LHXN9V22O86aQzwdEEm1iG91qROqSw/apcDnSCMjuVeQVA==
dependencies:
aggregate-error "3.0.1"
camel-case "4.1.1"
graphql-tools "5.0.0"
lodash "4.17.15"
"@graphql-toolkit/core@~0.10.2":
version "0.10.7"
resolved "https://registry.npmjs.org/@graphql-toolkit/core/-/core-0.10.7.tgz#e4d86d9e439fbb05b634054b0865c16d488fad97"
integrity sha512-LXcFLG7XcRJrPz/xD+0cExzLx/ptVynDM20650/FbmHbKOU50d9mSbcsrzAOq/3f4q3HrRDssvn0f6pPm0EHMg==
dependencies:
"@graphql-toolkit/common" "0.10.7"
"@graphql-toolkit/schema-merging" "0.10.7"
aggregate-error "3.0.1"
globby "11.0.0"
import-from "^3.0.0"
is-glob "4.0.1"
lodash "4.17.15"
p-limit "2.3.0"
resolve-from "5.0.0"
tslib "1.11.2"
unixify "1.0.0"
valid-url "1.0.9"
"@graphql-toolkit/graphql-file-loader@~0.10.2":
version "0.10.7"
resolved "https://registry.npmjs.org/@graphql-toolkit/graphql-file-loader/-/graphql-file-loader-0.10.7.tgz#496e49712def12449b339739d3583ed1bda7a24f"
integrity sha512-6tUIuw/YBlm0VyVgXgMrOXsEQ+WpXVgr2NQwHNzmZo82kPGqImveq7A2D3gBWLyVTcinDScRcKJMxM4kCF5T0A==
dependencies:
"@graphql-toolkit/common" "0.10.7"
tslib "1.11.2"
"@graphql-toolkit/json-file-loader@~0.10.2":
version "0.10.7"
resolved "https://registry.npmjs.org/@graphql-toolkit/json-file-loader/-/json-file-loader-0.10.7.tgz#6ac38258bc3da8540b38232b71bb5820bf02cff6"
integrity sha512-nVISrODqvn5LiQ4nKL5pz1Let/W1tuj2viEwrNyTS+9mcjaCE2nhV5MOK/7ZY0cR+XeA4N2u65EH1lQd63U3Cw==
dependencies:
"@graphql-toolkit/common" "0.10.7"
tslib "1.11.2"
"@graphql-toolkit/schema-merging@0.10.7", "@graphql-toolkit/schema-merging@~0.10.2":
version "0.10.7"
resolved "https://registry.npmjs.org/@graphql-toolkit/schema-merging/-/schema-merging-0.10.7.tgz#7b33becd48629bc656d602405c0b5c17d33ebd85"
integrity sha512-VngxJbVdRfXYhdMLhL90pqN+hD/2XTZwhHPGvpWqmGQhT6roc98yN3xyDyrWFYYsuiY4gTexdmrHQ3d7mzitwA==
dependencies:
"@graphql-toolkit/common" "0.10.7"
deepmerge "4.2.2"
graphql-tools "5.0.0"
tslib "1.11.2"
"@graphql-toolkit/url-loader@~0.10.2":
version "0.10.7"
resolved "https://registry.npmjs.org/@graphql-toolkit/url-loader/-/url-loader-0.10.7.tgz#0de998eeeaebcbf918526a26cb5f30f8250211f0"
integrity sha512-Ec3T4Zuo63LwG+RfK2ryz8ChPfncBf8fiSJ1xr68FtLDVznDNulvlNKFbfREE5koWejwsnJrjLCv6IX5IbhExg==
dependencies:
"@graphql-toolkit/common" "0.10.7"
cross-fetch "3.0.4"
graphql-tools "5.0.0"
tslib "1.11.2"
valid-url "1.0.9"
"@hapi/address@^2.1.2":
version "2.1.4"
resolved "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
@@ -3433,6 +3368,13 @@
resolved "https://registry.npmjs.org/@types/diff/-/diff-4.0.2.tgz#2e9bb89f9acc3ab0108f0f3dc4dbdcf2fff8a99c"
integrity sha512-mIenTfsIe586/yzsyfql69KRnA75S8SVXQbTLpDejRrjH0QSJcpu3AUOi/Vjnt9IOsXKxPhJfGpQUNMueIU1fQ==
"@types/dockerode@^2.5.32":
version "2.5.32"
resolved "https://registry.npmjs.org/@types/dockerode/-/dockerode-2.5.32.tgz#52d3628f605f8ea65202541c59a8a6dd166384fd"
integrity sha512-TfaGOoOHxsjkWRj2sPoQ3FLmTC5mVMhZ4kzZy13U7mjtIDoloE4e7AMj5jPLbffWB6Csy5DF5e0lC9M+tnKz/A==
dependencies:
"@types/node" "*"
"@types/eslint-visitor-keys@^1.0.0":
version "1.0.0"
resolved "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
@@ -4324,13 +4266,6 @@
"@webassemblyjs/wast-parser" "1.9.0"
"@xtuc/long" "4.2.2"
"@wry/equality@^0.1.2":
version "0.1.11"
resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.1.11.tgz#35cb156e4a96695aa81a9ecc4d03787bc17f1790"
integrity sha512-mwEVBDUVODlsQQ5dfuLUS5/Tf7jqUKyhKYHmVi4fPB6bDMOfWvUPJmKgS1Z7Za/sOI3vzWt4+O7yCiL/70MogA==
dependencies:
tslib "^1.9.3"
"@xobotyi/scrollbar-width@1.9.5":
version "1.9.5"
resolved "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d"
@@ -4458,7 +4393,7 @@ agentkeepalive@^3.4.1:
dependencies:
humanize-ms "^1.2.1"
aggregate-error@3.0.1, aggregate-error@^3.0.0:
aggregate-error@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0"
integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==
@@ -4616,45 +4551,6 @@ anymatch@^3.0.3, anymatch@~3.1.1:
normalize-path "^3.0.0"
picomatch "^2.0.4"
apollo-link-http-common@^0.2.14:
version "0.2.16"
resolved "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz#756749dafc732792c8ca0923f9a40564b7c59ecc"
integrity sha512-2tIhOIrnaF4UbQHf7kjeQA/EmSorB7+HyJIIrUjJOKBgnXwuexi8aMecRlqTIDWcyVXCeqLhUnztMa6bOH/jTg==
dependencies:
apollo-link "^1.2.14"
ts-invariant "^0.4.0"
tslib "^1.9.3"
apollo-link@^1.2.12, apollo-link@^1.2.14:
version "1.2.14"
resolved "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.14.tgz#3feda4b47f9ebba7f4160bef8b977ba725b684d9"
integrity sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg==
dependencies:
apollo-utilities "^1.3.0"
ts-invariant "^0.4.0"
tslib "^1.9.3"
zen-observable-ts "^0.8.21"
apollo-upload-client@^13.0.0:
version "13.0.0"
resolved "https://registry.npmjs.org/apollo-upload-client/-/apollo-upload-client-13.0.0.tgz#146d1ddd85d711fcac8ca97a72d3ca6787f2b71b"
integrity sha512-lJ9/bk1BH1lD15WhWRha2J3+LrXrPIX5LP5EwiOUHv8PCORp4EUrcujrA3rI5hZeZygrTX8bshcuMdpqpSrvtA==
dependencies:
"@babel/runtime" "^7.9.2"
apollo-link "^1.2.12"
apollo-link-http-common "^0.2.14"
extract-files "^8.0.0"
apollo-utilities@^1.3.0:
version "1.3.4"
resolved "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz#6129e438e8be201b6c55b0f13ce49d2c7175c9cf"
integrity sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig==
dependencies:
"@wry/equality" "^0.1.2"
fast-json-stable-stringify "^2.0.0"
ts-invariant "^0.4.0"
tslib "^1.10.0"
app-root-dir@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118"
@@ -4670,10 +4566,10 @@ aproba@^2.0.0:
resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
arch@2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e"
integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==
arch@2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz#0c52bbe7344bb4fa260c443d2cbad9c00ff2f0bf"
integrity sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==
are-we-there-yet@~1.1.2:
version "1.1.5"
@@ -4910,7 +4806,7 @@ async@^2.6.1, async@^2.6.2:
dependencies:
lodash "^4.17.14"
async@^3.1.0:
async@^3.2.0:
version "3.2.0"
resolved "https://registry.npmjs.org/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
@@ -5813,7 +5709,7 @@ callsites@^3.0.0:
resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
camel-case@4.1.1, camel-case@^4.1.1:
camel-case@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547"
integrity sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==
@@ -6214,13 +6110,13 @@ code-point-at@^1.0.0:
resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
codemirror-graphql@^0.12.0-alpha.8:
version "0.12.0-alpha.8"
resolved "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-0.12.0-alpha.8.tgz#cd7653299baefcdff2349bc9c3def9549c78775c"
integrity sha512-leCFSjrwtdKQE70ROWIUoc1U6F8fy6i+cd9/0D1dRbEecoleCt5+zODdANETw9map50kxNPf3EcGW4BPYj14hw==
codemirror-graphql@^0.12.0:
version "0.12.0"
resolved "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-0.12.0.tgz#55f782ffaaea7ff8dbda037e6b97b584dff2402a"
integrity sha512-C/0vKzQT5Uw+DLz6/MwKer29FQM52sh9Bem+VdDW42094j8nES1sdnuqj4k5ahNdQpW4FmVeoj/ngn2g3AWmgg==
dependencies:
graphql-language-service-interface "^2.4.0-alpha.8"
graphql-language-service-parser "^1.6.0-alpha.4"
graphql-language-service-interface "^2.4.0"
graphql-language-service-parser "^1.6.0"
codemirror@^5.52.2:
version "5.53.2"
@@ -6336,21 +6232,16 @@ 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==
commander@4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/commander/-/commander-4.1.0.tgz#545983a0603fe425bc672d66c9e3c89c42121a83"
integrity sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw==
commander@4.1.1, commander@^4.0.0, commander@^4.0.1, commander@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.3:
version "2.20.3"
resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@^4.0.0, commander@^4.0.1, commander@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
commander@^5.1.0:
version "5.1.0"
resolved "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
@@ -6684,17 +6575,6 @@ cors@^2.8.5:
object-assign "^4"
vary "^1"
cosmiconfig@6.0.0, cosmiconfig@^6.0.0:
version "6.0.0"
resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982"
integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==
dependencies:
"@types/parse-json" "^4.0.0"
import-fresh "^3.1.0"
parse-json "^5.0.0"
path-type "^4.0.0"
yaml "^1.7.2"
cosmiconfig@^5.0.0, cosmiconfig@^5.1.0, cosmiconfig@^5.2.1:
version "5.2.1"
resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
@@ -6705,6 +6585,17 @@ cosmiconfig@^5.0.0, cosmiconfig@^5.1.0, cosmiconfig@^5.2.1:
js-yaml "^3.13.1"
parse-json "^4.0.0"
cosmiconfig@^6.0.0:
version "6.0.0"
resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982"
integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==
dependencies:
"@types/parse-json" "^4.0.0"
import-fresh "^3.1.0"
parse-json "^5.0.0"
path-type "^4.0.0"
yaml "^1.7.2"
create-ecdh@^4.0.0:
version "4.0.3"
resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
@@ -6751,7 +6642,7 @@ cross-env@^7.0.0:
dependencies:
cross-spawn "^7.0.1"
cross-fetch@3.0.4, cross-fetch@^3.0.4:
cross-fetch@^3.0.4:
version "3.0.4"
resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.4.tgz#7bef7020207e684a7638ef5f2f698e24d9eb283c"
integrity sha512-MSHgpjQqgbT/94D4CyADeNoYh52zMkCX4pcJvPP5WqPsLFMKjr2TCMg381ox5qI0ii2dPwaLx/00477knXqXVw==
@@ -7092,38 +6983,38 @@ cyclist@^1.0.1:
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
cypress@*, cypress@^4.2.0:
version "4.7.0"
resolved "https://registry.npmjs.org/cypress/-/cypress-4.7.0.tgz#3ea29bddaf9a1faeaa5b8d54b60a84ed1cafa83d"
integrity sha512-Vav6wUFhPRlImIND/2lOQlUnAWzgCC/iXyJlJjX9nJOJul5LC1vUpf/m8Oiae870PFPyT0ZLLwPHKTXZNdXmHw==
version "4.9.0"
resolved "https://registry.npmjs.org/cypress/-/cypress-4.9.0.tgz#c188a3864ddf841c0fdc81a9e4eff5cf539cd1c1"
integrity sha512-qGxT5E0j21FPryzhb0OBjCdhoR/n1jXtumpFFSBPYWsaZZhNaBvc3XlBUDEZKkkXPsqUFYiyhWdHN/zo0t5FcA==
dependencies:
"@cypress/listr-verbose-renderer" "0.4.1"
"@cypress/request" "2.88.5"
"@cypress/xvfb" "1.2.4"
"@types/sinonjs__fake-timers" "6.0.1"
"@types/sizzle" "2.3.2"
arch "2.1.1"
arch "2.1.2"
bluebird "3.7.2"
cachedir "2.3.0"
chalk "2.4.2"
check-more-types "2.24.0"
cli-table3 "0.5.1"
commander "4.1.0"
commander "4.1.1"
common-tags "1.8.0"
debug "4.1.1"
eventemitter2 "4.1.2"
eventemitter2 "6.4.2"
execa "1.0.0"
executable "4.1.1"
extract-zip "1.7.0"
fs-extra "8.1.0"
getos "3.1.4"
getos "3.2.1"
is-ci "2.0.0"
is-installed-globally "0.1.0"
is-installed-globally "0.3.2"
lazy-ass "1.6.0"
listr "0.14.3"
lodash "4.17.15"
log-symbols "3.0.0"
minimist "1.2.5"
moment "2.24.0"
moment "2.26.0"
ospath "1.2.2"
pretty-bytes "5.3.0"
ramda "0.26.1"
@@ -7325,7 +7216,7 @@ deep-object-diff@^1.1.0:
resolved "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.0.tgz#d6fabf476c2ed1751fc94d5ca693d2ed8c18bc5a"
integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw==
deepmerge@4.2.2, deepmerge@^4.2.2:
deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
@@ -7431,11 +7322,6 @@ depd@~1.1.2:
resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
deprecated-decorator@^0.1.6:
version "0.1.6"
resolved "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz#00966317b7a12fe92f3cc831f7583af329b86c37"
integrity sha1-AJZjF7ehL+kvPMgx91g68ym4bDc=
deprecation@^2.0.0, deprecation@^2.3.1:
version "2.3.1"
resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
@@ -7571,11 +7457,6 @@ dns-packet@^1.3.1:
ip "^1.1.0"
safe-buffer "^5.0.1"
dns-prefetch-control@0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz#73988161841f3dcc81f47686d539a2c702c88624"
integrity sha512-hvSnros73+qyZXhHFjx2CMLwoj3Fe7eR9EJsFsqmcI1bB2OBWL/+0YzaEaKssCHnj/6crawNnUyw74Gm2EKe+Q==
dns-txt@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6"
@@ -8358,10 +8239,10 @@ event-stream@=3.3.4:
stream-combiner "~0.0.4"
through "~2.3.1"
eventemitter2@4.1.2:
version "4.1.2"
resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-4.1.2.tgz#0e1a8477af821a6ef3995b311bf74c23a5247f15"
integrity sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU=
eventemitter2@6.4.2:
version "6.4.2"
resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.2.tgz#f31f8b99d45245f0edbc5b00797830ff3b388970"
integrity sha512-r/Pwupa5RIzxIHbEKCkNXqpEQIIT4uQDxmP4G/Lug/NokVUWj0joz/WzWl3OxRpC5kDrH/WdiUJoR+IrwvXJEw==
eventemitter3@^3.1.0:
version "3.1.2"
@@ -8584,11 +8465,6 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
extract-files@^8.0.0:
version "8.1.0"
resolved "https://registry.npmjs.org/extract-files/-/extract-files-8.1.0.tgz#46a0690d0fe77411a2e3804852adeaa65cd59288"
integrity sha512-PTGtfthZK79WUMk+avLmwx3NGdU8+iVFXC2NMGxKsn0MnihOG2lvumj+AZo8CTwTrwjXDgZ5tztbRlEdRjBonQ==
extract-zip@1.7.0:
version "1.7.0"
resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
@@ -9324,12 +9200,12 @@ getopts@2.2.5:
resolved "https://registry.npmjs.org/getopts/-/getopts-2.2.5.tgz#67a0fe471cacb9c687d817cab6450b96dde8313b"
integrity sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA==
getos@3.1.4:
version "3.1.4"
resolved "https://registry.npmjs.org/getos/-/getos-3.1.4.tgz#29cdf240ed10a70c049add7b6f8cb08c81876faf"
integrity sha512-UORPzguEB/7UG5hqiZai8f0vQ7hzynMQyJLxStoQ8dPGAcmgsfXOPA4iE/fGtweHYkK+z4zc9V0g+CIFRf5HYw==
getos@3.2.1:
version "3.2.1"
resolved "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5"
integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==
dependencies:
async "^3.1.0"
async "^3.2.0"
getpass@^0.1.1:
version "0.1.7"
@@ -9434,13 +9310,6 @@ glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glo
once "^1.3.0"
path-is-absolute "^1.0.0"
global-dirs@^0.1.0:
version "0.1.1"
resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445"
integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=
dependencies:
ini "^1.3.4"
global-dirs@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201"
@@ -9511,18 +9380,6 @@ globalthis@^1.0.0:
dependencies:
define-properties "^1.1.3"
globby@11.0.0:
version "11.0.0"
resolved "https://registry.npmjs.org/globby/-/globby-11.0.0.tgz#56fd0e9f0d4f8fb0c456f1ab0dee96e1380bc154"
integrity sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg==
dependencies:
array-union "^2.1.0"
dir-glob "^3.0.1"
fast-glob "^3.1.1"
ignore "^5.1.4"
merge2 "^1.3.0"
slash "^3.0.0"
globby@8.0.2:
version "8.0.2"
resolved "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d"
@@ -9634,86 +9491,55 @@ graceful-fs@^4.2.4:
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
graphiql@^1.0.0-alpha.10:
version "1.0.0-alpha.10"
resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.0.0-alpha.10.tgz#9b6b5e554c6586e9bc09ebc863df8bcfd844110d"
integrity sha512-fKyYXQQRjsxOQw0rZMvICA2+P7ieUjj8AyfMaYlQeXGsGkZo0FUvBXVsN6LTraAPRBvVFo2jH9MSH68ujDXoeQ==
version "1.0.3"
resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.0.3.tgz#f6b5d5c417d8f1a28786d3228a69883426ba74ad"
integrity sha512-6YW76m2exdpGiVS75I0QsbM4NanvenKFR9FtW/IFbui0mWv89iVa1FbdjhUt+kxa+LX/WlqTshiqiZRz7E5Bgg==
dependencies:
"@emotion/core" "^10.0.28"
"@mdx-js/react" "^1.5.2"
codemirror "^5.52.2"
codemirror-graphql "^0.12.0-alpha.8"
codemirror-graphql "^0.12.0"
copy-to-clipboard "^3.2.0"
entities "^2.0.0"
markdown-it "^10.0.0"
monaco-graphql "^0.1.1"
monaco-graphql "^0.2.0"
regenerator-runtime "^0.13.5"
theme-ui "^0.3.1"
graphql-config@3.0.0-rc.2:
version "3.0.0-rc.2"
resolved "https://registry.npmjs.org/graphql-config/-/graphql-config-3.0.0-rc.2.tgz#760a1d7bcf8b114b7c1675c5a6ac624976d62e09"
integrity sha512-fb14mSDRatKsz2XXWgQjupbRsur/5vfQqHFFKo+N7hZNzRdT4hm+52HG8gn57mHQNZsN18q4aROwRQQRICMqZA==
graphql-language-service-interface@^2.4.0:
version "2.4.0"
resolved "https://registry.npmjs.org/graphql-language-service-interface/-/graphql-language-service-interface-2.4.0.tgz#4e2e63242c76197c4f56c61122db68187ea566b3"
integrity sha512-r7DQPyhCFY5TlpEukdh9tekJ9hAc7MD9TdOsb5CfAPlsIb1/faVVo2Ty19PxGSYDxygXjwpKLOQD0LqqFuw63A==
dependencies:
"@graphql-toolkit/common" "~0.10.2"
"@graphql-toolkit/core" "~0.10.2"
"@graphql-toolkit/graphql-file-loader" "~0.10.2"
"@graphql-toolkit/json-file-loader" "~0.10.2"
"@graphql-toolkit/schema-merging" "~0.10.2"
"@graphql-toolkit/url-loader" "~0.10.2"
cosmiconfig "6.0.0"
globby "11.0.0"
minimatch "3.0.4"
tslib "^1.11.1"
graphql-language-service-interface@^2.4.0-alpha.8:
version "2.4.0-alpha.8"
resolved "https://registry.npmjs.org/graphql-language-service-interface/-/graphql-language-service-interface-2.4.0-alpha.8.tgz#2f50b107c72be01796417bcfec2669d42f3b57c7"
integrity sha512-HT77H0GQk2jtpE9NAE+f8aNKvCkv5e7cZ32RZd+lCL23qNmuLJYpQcy+pbuRxxFKOWpiZLa5lc17T010gd64rQ==
dependencies:
graphql-language-service-parser "^1.6.0-alpha.4"
graphql-language-service-types "^1.6.0-alpha.6"
graphql-language-service-utils "^2.4.0-alpha.7"
graphql-language-service-parser "^1.6.0"
graphql-language-service-types "^1.6.0"
graphql-language-service-utils "^2.4.0"
vscode-languageserver-types "^3.15.1"
graphql-language-service-parser@^1.6.0-alpha.4:
version "1.6.0-alpha.4"
resolved "https://registry.npmjs.org/graphql-language-service-parser/-/graphql-language-service-parser-1.6.0-alpha.4.tgz#5d80046b1a3848a655d8f514ef65f40261b74897"
integrity sha512-l76uie88SNsa37Noc0/Bpw3HT1UxLdIrrs5A3NDj6Fm4FHmri+0Mvgjygpa3y4Y0W3AGje2MILtuJUmUSjiriA==
graphql-language-service-parser@^1.6.0:
version "1.6.0"
resolved "https://registry.npmjs.org/graphql-language-service-parser/-/graphql-language-service-parser-1.6.0.tgz#53a97619461ed41ae237b9070e7e20cfaca56118"
integrity sha512-tkfYXl6WBECWNcsyw7O09c0oCMrxqr3JGyDNvdISDSDhvk8EwEk+AOweBEJkycJwoiv1lVlM+EdLfj7dzw4/6w==
graphql-language-service-types@^1.6.0-alpha.6:
version "1.6.0-alpha.6"
resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.6.0-alpha.6.tgz#a7a50abbdfaf41074019d107754fc9c00ed824e2"
integrity sha512-cQfLC4CYDBVKcr3C53apqqd9imT+el3oEvIPz5/MocClWAZa4EsoEr2Z4WP9X5L4QrLk3hhFOJHor1aPzQRsLw==
graphql-language-service-types@^1.6.0:
version "1.6.0"
resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.6.0.tgz#32529bb9d2e3b8ae9dd6d9646e15481ac5dfe9ad"
integrity sha512-7y4pkhTL+MtarC8CeHpvRYVc6pkT6ITdQXs+Gr7OClnk4Lz5nQEK0eO29kUdd0jEnYfHyh5iqhnL5Owfy+wT0A==
graphql-language-service-utils@^2.4.0-alpha.7:
version "2.4.0-alpha.7"
resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.4.0-alpha.7.tgz#a78150eb57a079af52eaffadf9f21a9b3c82cea9"
integrity sha512-iPw9aFUWAJJx+aWY9DUf4rvIKnJVB1Qoaf7NzHegEC7LMIg7052i8ealMFKiSqk0q/zP6PCmiQblCMnGCjnEwg==
graphql-language-service-utils@^2.4.0:
version "2.4.0"
resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.4.0.tgz#d4992a1968860abc43a118de498fec84361a4b87"
integrity sha512-aLa+8iqb7AJYgdcawsKH8/KLc14DHcRsnveshOm8hN6bBVT0YiQP6mEfJoci741O74uaDF2zj1J3c02vorichw==
dependencies:
graphql-language-service-types "^1.6.0-alpha.6"
graphql-language-service-types "^1.6.0"
graphql-language-service@^3.0.0-alpha.1:
version "3.0.0-alpha.1"
resolved "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-3.0.0-alpha.1.tgz#dc04c0ca3f35ce322dc38548aaa023cde867ca1b"
integrity sha512-JkkONXR7+yYcm8Q4bohBJtRl6hLodgogt01lS0ROX8lcwkaihKaU+dyoSVgrRaNAibahjFrDLo0j7iAOarJ5ow==
graphql-language-service@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-3.0.0.tgz#953c3bbf4cc35ff9a7bd54e428bca17832367490"
integrity sha512-ySPrbecxtjQtzIFdCIOoSJzaco9dQvZK+wZN/tTjcSEEWh3fgMYXmctAG+WddJiZg3AB7I4Xsb2AsO5yFBbLHg==
dependencies:
graphql-config "3.0.0-rc.2"
graphql-language-service-interface "^2.4.0-alpha.8"
graphql-language-service-types "^1.6.0-alpha.6"
graphql-tools@5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/graphql-tools/-/graphql-tools-5.0.0.tgz#67281c834a0e29f458adba8018f424816fa627e9"
integrity sha512-5zn3vtn//382b7G3Wzz3d5q/sh+f7tVrnxeuhTMTJ7pWJijNqLxH7VEzv8VwXCq19zAzHYEosFHfXiK7qzvk7w==
dependencies:
apollo-link "^1.2.14"
apollo-upload-client "^13.0.0"
deprecated-decorator "^0.1.6"
form-data "^3.0.0"
iterall "^1.3.0"
node-fetch "^2.6.0"
tslib "^1.11.1"
uuid "^7.0.3"
graphql-language-service-interface "^2.4.0"
graphql-language-service-types "^1.6.0"
graphql@15.1.0, graphql@^15.0.0:
version "15.1.0"
@@ -9905,12 +9731,11 @@ helmet-csp@2.10.0:
dasherize "2.0.0"
helmet@^3.22.0:
version "3.22.0"
resolved "https://registry.npmjs.org/helmet/-/helmet-3.22.0.tgz#3a6f11d931799145f0aff15dbc563cff9e13131f"
integrity sha512-Xrqicn2nm1ZIUxP3YGuTBmbDL04neKsIT583Sjh0FkiwKDXYCMUqGqC88w3NUvVXtA75JyR2Jn6jw6ZEMOD+ZA==
version "3.23.2"
resolved "https://registry.npmjs.org/helmet/-/helmet-3.23.2.tgz#390bb6f3f5e593f90f441bdd91000912c543a898"
integrity sha512-pe0UiHw3aHbP8Lon9McCq4AN2XLUMSbhwxJnUY6U2t8wTda7F1SsYg0/pBa1BPugaRqAtx9e1/FyF6E9PsUU5A==
dependencies:
depd "2.0.0"
dns-prefetch-control "0.2.0"
dont-sniff-mimetype "1.1.0"
expect-ct "0.2.0"
feature-policy "0.3.0"
@@ -9920,7 +9745,6 @@ helmet@^3.22.0:
hide-powered-by "1.1.0"
hpkp "2.0.0"
hsts "2.2.0"
ienoopen "1.1.0"
nocache "2.1.0"
referrer-policy "1.2.0"
x-xss-protection "1.3.0"
@@ -10283,11 +10107,6 @@ ieee754@^1.1.4:
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
ienoopen@1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/ienoopen/-/ienoopen-1.1.0.tgz#411e5d530c982287dbdc3bb31e7a9c9e32630974"
integrity sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ==
iferr@^0.1.5:
version "0.1.5"
resolved "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
@@ -10535,9 +10354,9 @@ inquirer@^6.2.0:
through "^2.3.6"
inquirer@^7.0.0, inquirer@^7.0.4:
version "7.1.0"
resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29"
integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==
version "7.2.0"
resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.2.0.tgz#63ce99d823090de7eb420e4bb05e6f3449aa389a"
integrity sha512-E0c4rPwr9ByePfNlTIB8z51kK1s2n6jrHuJeEHENl/sbq2G/S1auvibgEwNR4uSyiU+PiYHqSwsgGiXjG8p5ZQ==
dependencies:
ansi-escapes "^4.2.1"
chalk "^3.0.0"
@@ -10819,13 +10638,6 @@ is-generator-fn@^2.0.0:
resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
is-glob@4.0.1, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
dependencies:
is-extglob "^2.1.1"
is-glob@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
@@ -10840,6 +10652,13 @@ is-glob@^3.1.0:
dependencies:
is-extglob "^2.1.0"
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
dependencies:
is-extglob "^2.1.1"
is-hexadecimal@^1.0.0:
version "1.0.4"
resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7"
@@ -10850,15 +10669,7 @@ is-in-browser@^1.0.2, is-in-browser@^1.1.3:
resolved "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=
is-installed-globally@0.1.0:
version "0.1.0"
resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=
dependencies:
global-dirs "^0.1.0"
is-path-inside "^1.0.0"
is-installed-globally@^0.3.1:
is-installed-globally@0.3.2, is-installed-globally@^0.3.1:
version "0.3.2"
resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141"
integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==
@@ -10932,13 +10743,6 @@ is-path-in-cwd@^2.0.0:
dependencies:
is-path-inside "^2.1.0"
is-path-inside@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
dependencies:
path-is-inside "^1.0.1"
is-path-inside@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2"
@@ -11202,11 +11006,6 @@ istanbul-reports@^3.0.2:
html-escaper "^2.0.0"
istanbul-lib-report "^3.0.0"
iterall@^1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea"
integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==
iterate-iterator@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz#1693a768c1ddd79c969051459453f082fe82e9f6"
@@ -13064,12 +12863,7 @@ modify-values@^1.0.0:
resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
moment@2.24.0:
version "2.24.0"
resolved "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
moment@^2.25.3, moment@^2.26.0:
moment@2.26.0, moment@^2.25.3, moment@^2.26.0:
version "2.26.0"
resolved "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"
integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==
@@ -13079,13 +12873,13 @@ monaco-editor@^0.20.0:
resolved "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.20.0.tgz#5d5009343a550124426cb4d965a4d27a348b4dea"
integrity sha512-hkvf4EtPJRMQlPC3UbMoRs0vTAFAYdzFQ+gpMb8A+9znae1c43q8Mab9iVsgTcg/4PNiLGGn3SlDIa8uvK1FIQ==
monaco-graphql@^0.1.1:
version "0.1.1"
resolved "https://registry.npmjs.org/monaco-graphql/-/monaco-graphql-0.1.1.tgz#d74b03a14d60805841157efe99c3ddec6e8c1407"
integrity sha512-P6K03iP2hme9geA9TR2zYkSrdhVR0/YcMBQjAaTALGvI4dIILTieGxuBeElZAZFEhsj38CNZZKUF8pKXapA86Q==
monaco-graphql@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/monaco-graphql/-/monaco-graphql-0.2.0.tgz#be6bd165b6cbdc624fde6c108fe9ee32f50f2e80"
integrity sha512-IwpbJvrNlFxq6xjnQQ8eC72ep+q2vfiUaHBNkfqe314CXYnUCcXYfoMBfR5EuLAXRBOTf4CKl6BCN1/9eHrq7A==
dependencies:
graphql-language-service "^3.0.0-alpha.1"
graphql-language-service-utils "^2.4.0-alpha.7"
graphql-language-service "^3.0.0"
graphql-language-service-utils "^2.4.0"
monaco-editor "^0.20.0"
morgan@^1.10.0:
@@ -13943,13 +13737,6 @@ p-finally@^2.0.0:
resolved "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561"
integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==
p-limit@2.3.0:
version "2.3.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
dependencies:
p-try "^2.0.0"
p-limit@^1.1.0:
version "1.3.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
@@ -14309,7 +14096,7 @@ path-is-absolute@^1.0.0:
resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
path-is-inside@^1.0.1, path-is-inside@^1.0.2:
path-is-inside@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
@@ -16306,11 +16093,6 @@ resolve-dir@^1.0.0, resolve-dir@^1.0.1:
expand-tilde "^2.0.0"
global-modules "^1.0.0"
resolve-from@5.0.0, resolve-from@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve-from@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
@@ -16321,6 +16103,11 @@ resolve-from@^4.0.0:
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve-from@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve-pathname@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
@@ -16570,20 +16357,13 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies:
aproba "^1.1.1"
rxjs@^6.3.3, rxjs@^6.5.5:
rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3, rxjs@^6.5.4, rxjs@^6.5.5:
version "6.5.5"
resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec"
integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==
dependencies:
tslib "^1.9.0"
rxjs@^6.4.0, rxjs@^6.5.3, rxjs@^6.5.4:
version "6.5.4"
resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
dependencies:
tslib "^1.9.0"
safe-buffer@5.1.1:
version "5.1.1"
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
@@ -18326,13 +18106,6 @@ ts-interface-checker@^0.1.9:
resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.10.tgz#b68a49e37e90a05797e590f08494dd528bf383cf"
integrity sha512-UJYuKET7ez7ry0CnvfY6fPIUIZDw+UI3qvTUQeS2MyI4TgEeWAUBqy185LeaHcdJ9zG2dgFpPJU/AecXU0Afug==
ts-invariant@^0.4.0:
version "0.4.4"
resolved "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.4.4.tgz#97a523518688f93aafad01b0e80eb803eb2abd86"
integrity sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA==
dependencies:
tslib "^1.9.3"
ts-jest@^26.0.0:
version "26.1.0"
resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-26.1.0.tgz#e9070fc97b3ea5557a48b67c631c74eb35e15417"
@@ -18391,21 +18164,11 @@ tslib@1.10.0:
resolved "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
tslib@1.11.2:
version "1.11.2"
resolved "https://registry.npmjs.org/tslib/-/tslib-1.11.2.tgz#9c79d83272c9a7aaf166f73915c9667ecdde3cc9"
integrity sha512-tTSkux6IGPnUGUd1XAZHcpu85MOkIl5zX49pO+jfsie3eP0B6pyhOlLXm3cAC6T7s+euSDDUUV+Acop5WmtkVg==
tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
version "1.11.1"
resolved "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
tslib@^1.11.1:
version "1.13.0"
resolved "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
tsutils@^3.17.1:
version "3.17.1"
resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
@@ -18688,13 +18451,6 @@ universalify@^1.0.0:
resolved "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d"
integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==
unixify@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090"
integrity sha1-OmQcjC/7zk2mg6XHDwOkYpQMIJA=
dependencies:
normalize-path "^2.1.1"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@@ -18908,11 +18664,6 @@ v8flags@^3.1.3:
dependencies:
homedir-polyfill "^1.0.1"
valid-url@1.0.9:
version "1.0.9"
resolved "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200"
integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=
validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3:
version "3.0.4"
resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
@@ -19686,15 +19437,7 @@ yup@^0.29.1:
synchronous-promise "^2.0.10"
toposort "^2.0.2"
zen-observable-ts@^0.8.21:
version "0.8.21"
resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz#85d0031fbbde1eba3cd07d3ba90da241215f421d"
integrity sha512-Yj3yXweRc8LdRMrCC8nIc4kkjWecPAUVh0TI0OUrWXx6aX790vLcDlWca6I4vsyCGH3LpWxq0dJRcMOFoVqmeg==
dependencies:
tslib "^1.9.3"
zen-observable "^0.8.0"
zen-observable@^0.8.0, zen-observable@^0.8.15:
zen-observable@^0.8.15:
version "0.8.15"
resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==