Merge branch 'master' of github.com:spotify/backstage into mob/fetching-mock-docs

This commit is contained in:
Sebastian Qvarfordt
2020-06-26 13:15:22 +02:00
42 changed files with 1283 additions and 512 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
+51 -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,142 +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.
+1 -1
View File
@@ -31,7 +31,7 @@ const app = createApp({
plugins: Object.values(plugins),
components: {
SignInPage: props => (
<SignInPage {...props} providers={['guest', 'google', 'custom']} />
<SignInPage {...props} providers={['guest', 'google', 'custom', 'okta']} />
),
},
});
+11
View File
@@ -26,10 +26,12 @@ import {
FeatureFlags,
GoogleAuth,
GithubAuth,
OktaAuth,
oauthRequestApiRef,
OAuthRequestManager,
googleAuthApiRef,
githubAuthApiRef,
oktaAuthApiRef,
storageApiRef,
WebStorage,
} from '@backstage/core';
@@ -91,6 +93,15 @@ export const apis = (config: ConfigApi) => {
}),
);
builder.add(
oktaAuthApiRef,
OktaAuth.create({
apiOrigin: 'http://localhost:7000',
basePath: '/auth/',
oauthRequestApi,
}),
);
builder.add(
techRadarApiRef,
new TechRadar({
+9
View File
@@ -0,0 +1,9 @@
FROM node:12
WORKDIR /usr/src/app
COPY . .
RUN yarn install --frozen-lockfile --production
CMD ["node", "packages/backend"]
+1
View File
@@ -10,6 +10,7 @@
},
"scripts": {
"build": "backstage-cli backend:build",
"build-image": "backstage-cli backend:build-image example-backend",
"start": "backstage-cli backend:dev",
"lint": "backstage-cli lint",
"test": "backstage-cli test",
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { buildPackage, Output } from '../../lib/packager';
import { buildPackage, Output } from '../../lib/builder';
export default async () => {
await buildPackage({
@@ -0,0 +1,39 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from 'fs-extra';
import { createDistWorkspace } from '../../lib/packager';
import { paths } from '../../lib/paths';
import { run } from '../../lib/run';
export default async (imageTag: string) => {
const tempDistWorkspace = await createDistWorkspace(['example-backend'], {
files: [
'package.json',
'yarn.lock',
'app-config.yaml',
{ src: paths.resolveTarget('Dockerfile'), dest: 'Dockerfile' },
],
});
console.log(`Dist workspace ready at ${tempDistWorkspace}`);
await run('docker', ['build', '.', '-t', imageTag], {
cwd: tempDistWorkspace,
});
await fs.remove(tempDistWorkspace);
};
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { buildPackage, Output } from '../lib/packager';
import { buildPackage, Output } from '../lib/builder';
import { Command } from 'commander';
export default async (cmd: Command) => {
+10 -4
View File
@@ -19,10 +19,15 @@ import { paths } from '../lib/paths';
const SKIPPED_KEYS = ['access', 'registry', 'tag'];
export const pre = async () => {
const pkgPath = paths.resolveTarget('package.json');
const PKG_PATH = 'package.json';
const PKG_BACKUP_PATH = 'package.json-prepack';
const pkg = await fs.readJson(pkgPath);
export const pre = async () => {
const pkgPath = paths.resolveTarget(PKG_PATH);
const pkgContent = await fs.readFile(pkgPath, 'utf8');
const pkg = JSON.parse(pkgContent);
await fs.writeFile(PKG_BACKUP_PATH, pkgContent);
for (const key of Object.keys(pkg.publishConfig ?? {})) {
if (!SKIPPED_KEYS.includes(key)) {
@@ -33,5 +38,6 @@ export const pre = async () => {
};
export const post = async () => {
// postpack is a noop for now, since it's not called anyway
// postpack isn't called by yarn right now, so it needs to be called manually
await fs.move(PKG_BACKUP_PATH, PKG_PATH, { overwrite: true });
};
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { buildPackage, Output } from '../../lib/packager';
import { buildPackage, Output } from '../../lib/builder';
export default async () => {
await buildPackage({
+9
View File
@@ -46,6 +46,15 @@ const main = (argv: string[]) => {
.description('Build a backend plugin')
.action(lazyAction(() => import('./commands/backend/build'), 'default'));
program
.command('backend:build-image <image-tag>')
.description(
'Builds a docker image from the package, with all local deps included',
)
.action(
lazyAction(() => import('./commands/backend/buildImage'), 'default'),
);
program
.command('backend:dev')
.description('Start local development server with HMR for the backend')
+19
View File
@@ -0,0 +1,19 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { buildPackage } from './packager';
export { Output } from './types';
export type { BuildOptions } from './types';
+130 -3
View File
@@ -14,6 +14,133 @@
* limitations under the License.
*/
export { buildPackage } from './packager';
export { Output } from './types';
export type { BuildOptions } from './types';
import fs from 'fs-extra';
import { resolve as resolvePath, relative as relativePath } from 'path';
import { paths } from '../paths';
import { run } from '../run';
import tar from 'tar';
import { tmpdir } from 'os';
type LernaPackage = {
name: string;
private: boolean;
location: string;
scripts: Record<string, string>;
};
type FileEntry =
| string
| {
src: string;
dest: string;
};
type Options = {
/**
* Target directory for the dist workspace, defaults to a temporary directory
*/
targetDir?: string;
/**
* Files to copy into the target workspace.
*
* Defaults to ['yarn.lock', 'package.json'].
*/
files?: FileEntry[];
};
/**
* Uses `yarn pack` to package local packages and unpacks them into a dist workspace.
* The target workspace will end up containing dist version of each package and
* will be suitable for packaging e.g. into a docker image.
*
* This creates a structure that is functionally similar to if the packages where
* installed from NPM, but uses yarn workspaces to link to them at runtime.
*/
export async function createDistWorkspace(
packageNames: string[],
options: Options,
) {
const targetDir =
options.targetDir ??
(await fs.mkdtemp(resolvePath(tmpdir(), 'dist-workspace')));
const targets = await findTargetPackages(packageNames);
await moveToDistWorkspace(targetDir, targets);
const files: FileEntry[] = options.files ?? ['yarn.lock', 'package.json'];
for (const file of files) {
const src = typeof file === 'string' ? file : file.src;
const dest = typeof file === 'string' ? file : file.dest;
await fs.copy(paths.resolveTargetRoot(src), resolvePath(targetDir, dest));
}
return targetDir;
}
async function moveToDistWorkspace(
workspaceDir: string,
localPackages: LernaPackage[],
): Promise<void> {
await Promise.all(
localPackages.map(async (target, index) => {
console.log(`Repacking ${target.name} into dist workspace`);
const archive = `temp-package-${index}.tgz`;
const archivePath = resolvePath(workspaceDir, archive);
await run('yarn', ['pack', '--filename', archivePath], {
cwd: target.location,
});
// TODO(Rugvip): yarn pack doesn't call postpack, once the bug is fixed this can be removed
if (target.scripts.postpack) {
await run('yarn', ['postpack'], { cwd: target.location });
}
const outputDir = relativePath(paths.targetRoot, target.location);
const absoluteOutputPath = resolvePath(workspaceDir, outputDir);
await fs.ensureDir(absoluteOutputPath);
await tar.extract({
file: archivePath,
cwd: absoluteOutputPath,
strip: 1,
});
await fs.remove(archivePath);
}),
);
}
async function findTargetPackages(pkgNames: string[]): Promise<LernaPackage[]> {
const LernaProject = require('@lerna/project');
const PackageGraph = require('@lerna/package-graph');
const project = new LernaProject(paths.targetDir);
const packages = await project.getPackages();
const graph = new PackageGraph(packages);
const targets = new Map<string, any>();
const searchNames = pkgNames.slice();
while (searchNames.length) {
const name = searchNames.pop()!;
if (targets.has(name)) {
continue;
}
const node = graph.get(name);
if (!node) {
throw new Error(`Package '${name}' not found`);
}
const pkgDeps = Object.keys(node.pkg.dependencies);
const localDeps: string[] = Array.from(node.localDependencies.keys());
const filteredDeps = localDeps.filter(dep => pkgDeps.includes(dep));
searchNames.push(...filteredDeps);
targets.set(name, node.pkg);
}
return Array.from(targets.values());
}
@@ -233,3 +233,20 @@ export const githubAuthApiRef = createApiRef<
id: 'core.auth.github',
description: 'Provides authentication towards Github APIs',
});
/**
* Provides authentication towards Okta APIs.
*
* See https://developer.okta.com/docs/guides/implement-oauth-for-okta/scopes/
* for a full list of supported scopes.
*/
export const oktaAuthApiRef = createApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionStateApi
>({
id: 'core.auth.okta',
description: 'Provides authentication towards Okta APIs',
});
@@ -16,3 +16,4 @@
export * from './google';
export * from './github';
export * from './okta';
@@ -0,0 +1,129 @@
/*
* 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 OktaAuth from './OktaAuth';
const theFuture = new Date(Date.now() + 3600000);
const thePast = new Date(Date.now() - 10);
const PREFIX = 'okta.';
describe('OktaAuth', () => {
it('should get refreshed access token', async () => {
const getSession = jest.fn().mockResolvedValue({
providerInfo: { accessToken: 'access-token', expiresAt: theFuture },
});
const oktaAuth = new OktaAuth({ getSession } as any);
expect(await oktaAuth.getAccessToken()).toBe('access-token');
expect(getSession).toBeCalledTimes(1);
});
it('should get refreshed id token', async () => {
const getSession = jest.fn().mockResolvedValue({
providerInfo: { idToken: 'id-token', expiresAt: theFuture },
});
const oktaAuth = new OktaAuth({ getSession } as any);
expect(await oktaAuth.getIdToken()).toBe('id-token');
expect(getSession).toBeCalledTimes(1);
});
it('should get optional id token', async () => {
const getSession = jest.fn().mockResolvedValue({
providerInfo: { idToken: 'id-token', expiresAt: theFuture },
});
const oktaAuth = new OktaAuth({ getSession } as any);
expect(await oktaAuth.getIdToken({ optional: true })).toBe('id-token');
expect(getSession).toBeCalledTimes(1);
});
it('should share popup closed errors', async () => {
const error = new Error('NOPE');
error.name = 'RejectedError';
const getSession = jest
.fn()
.mockResolvedValueOnce({
providerInfo: {
accessToken: 'access-token',
expiresAt: theFuture,
scopes: new Set([`not-a-scope`]),
},
})
.mockRejectedValue(error);
const oktaAuth = new OktaAuth({ getSession } as any);
// Make sure we have a session before we do the double request, so that we get past the !this.currentSession check
await expect(oktaAuth.getAccessToken()).resolves.toBe('access-token');
const promise1 = oktaAuth.getAccessToken('more');
const promise2 = oktaAuth.getAccessToken('more');
await expect(promise1).rejects.toBe(error);
await expect(promise2).rejects.toBe(error);
expect(getSession).toBeCalledTimes(3);
});
it('should wait for all session refreshes', async () => {
const initialSession = {
providerInfo: {
idToken: 'token1',
expiresAt: theFuture,
scopes: new Set(),
},
};
const getSession = jest
.fn()
.mockResolvedValueOnce(initialSession)
.mockResolvedValue({
providerInfo: {
idToken: 'token2',
expiresAt: theFuture,
scopes: new Set(),
},
});
const oktaAuth = new OktaAuth({ getSession } as any);
// Grab the expired session first
await expect(oktaAuth.getIdToken()).resolves.toBe('token1');
expect(getSession).toBeCalledTimes(1);
initialSession.providerInfo.expiresAt = thePast;
const promise1 = oktaAuth.getIdToken();
const promise2 = oktaAuth.getIdToken();
const promise3 = oktaAuth.getIdToken();
await expect(promise1).resolves.toBe('token2');
await expect(promise2).resolves.toBe('token2');
await expect(promise3).resolves.toBe('token2');
expect(getSession).toBeCalledTimes(4); // De-duping of session requests happens in client
});
it.each([
['openid', ['openid']],
['profile email', ['profile', 'email']],
[`${PREFIX}groups.manage`, [`${PREFIX}groups.manage`]],
['groups.read', [`${PREFIX}groups.read`]],
[`${PREFIX}groups.manage groups.read, openid`, [`${PREFIX}groups.manage`, `${PREFIX}groups.read`, 'openid']],
[`email\t ${PREFIX}groups.read`, ['email', `${PREFIX}groups.read`]],
// Some incorrect scopes that we don't try to fix
[`${PREFIX}email`, [`${PREFIX}email`]],
[`${PREFIX}profile`, [`${PREFIX}profile`]],
[`${PREFIX}openid`, [`${PREFIX}openid`]],
])(`should normalize scopes correctly - %p`, (scope, scopes) => {
expect(OktaAuth.normalizeScopes(scope)).toEqual(new Set(scopes));
});
});
@@ -0,0 +1,186 @@
/*
* 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 OktaIcon from '@material-ui/icons/AcUnit';
import { DefaultAuthConnector } from '../../../../lib/AuthConnector';
import { OktaSession } from './types';
import {
OAuthApi,
OpenIdConnectApi,
ProfileInfoApi,
ProfileInfo,
SessionStateApi,
SessionState,
BackstageIdentityApi,
AuthRequestOptions,
BackstageIdentity,
} from '../../../definitions/auth';
import { OAuthRequestApi, AuthProvider } from '../../../definitions';
import { SessionManager } from '../../../../lib/AuthSessionManager/types';
import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager';
import { Observable } from '../../../../types';
type CreateOptions = {
apiOrigin: string;
basePath: string;
oauthRequestApi: OAuthRequestApi;
environment?: string;
provider?: AuthProvider & { id: string };
};
export type OktaAuthResponse = {
providerInfo: {
accessToken: string;
idToken: string;
scope: string;
expiresInSeconds: number;
};
profile: ProfileInfo;
backstageIdentity: BackstageIdentity;
};
const DEFAULT_PROVIDER = {
id: 'okta',
title: 'Okta',
icon: OktaIcon,
};
const OKTA_OIDC_SCOPES: Set<String> = new Set(
['openid', 'profile', 'email', 'phone', 'address', 'groups', 'offline_access']
)
const OKTA_SCOPE_PREFIX: string = 'okta.'
class OktaAuth implements
OAuthApi,
OpenIdConnectApi,
ProfileInfoApi,
BackstageIdentityApi,
SessionStateApi
{
static create({
apiOrigin,
basePath,
environment = 'development',
provider = DEFAULT_PROVIDER,
oauthRequestApi,
}: CreateOptions) {
const connector = new DefaultAuthConnector({
apiOrigin,
basePath,
environment,
provider,
oauthRequestApi: oauthRequestApi,
sessionTransform(res: OktaAuthResponse): OktaSession {
return {
...res,
providerInfo: {
idToken: res.providerInfo.idToken,
accessToken: res.providerInfo.accessToken,
scopes: OktaAuth.normalizeScopes(res.providerInfo.scope),
expiresAt: new Date(
Date.now() + res.providerInfo.expiresInSeconds * 1000,
),
},
};
},
});
const sessionManager = new RefreshingAuthSessionManager({
connector,
defaultScopes: new Set([
'openid',
'email',
'profile',
'offline_access',
]),
sessionScopes: session => session.scopes,
sessionShouldRefresh: session => {
const expiresInSec =
(session.providerInfo.expiresAt.getTime() - Date.now()) / 1000;
return expiresInSec < 60 * 5;
},
});
return new OktaAuth(sessionManager);
}
sessionState$(): Observable<SessionState> {
return this.sessionManager.sessionState$();
}
constructor(private readonly sessionManager: SessionManager<OktaSession>) {}
async getAccessToken(
scope?: string,
options?: AuthRequestOptions
) {
const session = await this.sessionManager.getSession({
...options,
scopes: OktaAuth.normalizeScopes(scope),
});
return session?.providerInfo.accessToken ?? '';
}
async getIdToken(options: AuthRequestOptions = {}) {
const session = await this.sessionManager.getSession(options);
return session?.providerInfo.idToken ?? '';
}
async logout() {
await this.sessionManager.removeSession();
}
async getBackstageIdentity(
options: AuthRequestOptions = {},
): Promise<BackstageIdentity | undefined> {
const session = await this.sessionManager.getSession(options);
return session?.backstageIdentity;
}
async getProfile(options: AuthRequestOptions = {}) {
const session = await this.sessionManager.getSession(options);
return session?.profile;
}
static normalizeScopes(scopes?: string | string[]): Set<string> {
if (!scopes) {
return new Set();
}
const scopeList = Array.isArray(scopes)
? scopes
: scopes.split(/[\s|,]/).filter(Boolean);
const normalizedScopes = scopeList.map(scope => {
if (OKTA_OIDC_SCOPES.has(scope)) {
return scope;
}
if (scope.startsWith(OKTA_SCOPE_PREFIX)) {
return scope;
}
return `${OKTA_SCOPE_PREFIX}${scope}`
});
return new Set(normalizedScopes);
}
}
export default OktaAuth;
@@ -0,0 +1,18 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './types';
export { default as OktaAuth } from './OktaAuth';
@@ -0,0 +1,27 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ProfileInfo, BackstageIdentity } from '../../../definitions';
export type OktaSession = {
providerInfo: {
idToken: string;
accessToken: string;
scopes: Set<string>;
expiresAt: Date;
};
profile: ProfileInfo;
backstageIdentity: BackstageIdentity;
};
@@ -23,6 +23,7 @@ import {
googleAuthApiRef,
githubAuthApiRef,
identityApiRef,
oktaAuthApiRef,
useApi,
} from '@backstage/core-api';
import {
@@ -56,6 +57,11 @@ export function SidebarUserSettings() {
apiRef={githubAuthApiRef}
icon={Star}
/>
<OIDCProviderSettings
title="Okta"
apiRef={oktaAuthApiRef}
icon={Star}
/>
<SidebarItem
icon={SignOutIcon}
text="Sign Out"
@@ -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 React from 'react';
import { Grid, Typography, Button } from '@material-ui/core';
import { InfoCard } from '../InfoCard/InfoCard';
import { ProviderComponent, ProviderLoader, SignInProvider } from './types';
import {
useApi,
oktaAuthApiRef,
errorApiRef,
} from '@backstage/core-api';
const Component: ProviderComponent = ({ onResult }) => {
const oktaAuthApi = useApi(oktaAuthApiRef);
const errorApi = useApi(errorApiRef);
const handleLogin = async () => {
try {
const identity = await oktaAuthApi.getBackstageIdentity({
instantPopup: true,
});
const profile = await oktaAuthApi.getProfile();
onResult({
userId: identity!.id,
profile: profile!,
getIdToken: () =>
oktaAuthApi.getBackstageIdentity().then(i => i!.idToken),
logout: async () => {
await oktaAuthApi.logout();
},
});
} catch (error) {
errorApi.post(error);
}
};
return (
<Grid item>
<InfoCard
title="Okta"
actions={
<Button color="primary" variant="outlined" onClick={handleLogin}>
Sign In
</Button>
}
>
<Typography variant="body1">Sign In using Okta</Typography>
</InfoCard>
</Grid>
);
};
const loader: ProviderLoader = async apis => {
const oktaAuthApi = apis.get(oktaAuthApiRef)!;
const identity = await oktaAuthApi.getBackstageIdentity({
optional: true,
});
if (!identity) {
return undefined;
}
const profile = await oktaAuthApi.getProfile();
return {
userId: identity.id,
profile: profile!,
getIdToken: () =>
oktaAuthApi.getBackstageIdentity().then(i => i!.idToken),
logout: async () => {
await oktaAuthApi.logout();
},
};
};
export const oktaProvider: SignInProvider = { Component, loader };
@@ -18,6 +18,7 @@ import React, { useLayoutEffect, useState, useMemo, useCallback } from 'react';
import { guestProvider } from './guestProvider';
import { googleProvider } from './googleProvider';
import { customProvider } from './customProvider';
import { oktaProvider } from './oktaProvider';
import {
SignInPageProps,
SignInResult,
@@ -30,12 +31,13 @@ import { SignInProvider } from './types';
const PROVIDER_STORAGE_KEY = '@backstage/core:SignInPage:provider';
// Separate list here to avoid exporting internal types
export type SignInProviderId = 'guest' | 'google' | 'custom';
export type SignInProviderId = 'guest' | 'google' | 'custom' | 'okta';
const signInProviders: { [id in SignInProviderId]: SignInProvider } = {
guest: guestProvider,
google: googleProvider,
custom: customProvider,
okta: oktaProvider,
};
export const useSignInProviders = (
+11
View File
@@ -6,11 +6,13 @@ import {
OAuthRequestManager,
googleAuthApiRef,
githubAuthApiRef,
oktaAuthApiRef,
AlertApiForwarder,
ErrorApiForwarder,
ErrorAlerter,
GoogleAuth,
GithubAuth,
OktaAuth,
identityApiRef,
} from '@backstage/core';
@@ -50,4 +52,13 @@ builder.add(
}),
);
builder.add(
oktaAuthApiRef,
OktaAuth.create({
apiOrigin: 'http://localhost:7000',
basePath: '/auth/',
oauthRequestApi,
}),
);
export const apis = builder.build();
+4 -1
View File
@@ -40,6 +40,8 @@
"passport": "^0.4.1",
"passport-github2": "^0.1.12",
"passport-google-oauth20": "^2.0.0",
"passport-oauth2": "^1.5.0",
"passport-okta-oauth": "^0.0.1",
"passport-saml": "^1.3.3",
"uuid": "^8.0.0",
"winston": "^3.2.1",
@@ -57,6 +59,7 @@
"jest-fetch-mock": "^3.0.3"
},
"files": [
"dist"
"dist",
"migrations"
]
}
@@ -18,6 +18,7 @@ import Router from 'express-promise-router';
import { createGithubProvider } from './github';
import { createGoogleProvider } from './google';
import { createSamlProvider } from './saml';
import { createOktaProvider } from './okta';
import { AuthProviderFactory, AuthProviderConfig } from './types';
import { Logger } from 'winston';
import { TokenIssuer } from '../identity';
@@ -26,6 +27,7 @@ const factories: { [providerId: string]: AuthProviderFactory } = {
google: createGoogleProvider,
github: createGithubProvider,
saml: createSamlProvider,
okta: createOktaProvider,
};
export const createAuthProviderRouter = (
@@ -0,0 +1,16 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { createOktaProvider } from './provider';
@@ -0,0 +1,213 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import express from 'express';
import { OAuthProvider } from '../../lib/OAuthProvider';
import { Strategy as OktaStrategy } from 'passport-okta-oauth';
import passport from 'passport';
import {
executeFrameHandlerStrategy,
executeRedirectStrategy,
executeRefreshTokenStrategy,
makeProfileInfo,
executeFetchUserProfileStrategy,
} from '../../lib/PassportStrategyHelper';
import {
OAuthProviderHandlers,
RedirectInfo,
AuthProviderConfig,
EnvironmentProviderConfig,
OAuthProviderOptions,
OAuthProviderConfig,
OAuthResponse,
PassportDoneCallback,
} from '../types';
import {
EnvironmentHandler,
EnvironmentHandlers,
} from '../../lib/EnvironmentHandler';
import { Logger } from 'winston';
import { StateStore } from 'passport-oauth2';
import { TokenIssuer } from '../../identity';
type PrivateInfo = {
refreshToken: string;
};
export class OktaAuthProvider implements OAuthProviderHandlers {
private readonly _strategy: any;
/**
* Due to passport-okta-oauth forcing options.state = true,
* passport-oauth2 requires express-session to be installed
* so that the 'state' parameter of the oauth2 flow can be stored.
* This implementation of StateStore matches the NullStore found within
* passport-oauth2, which is the StateStore implementation used when options.state = false,
* allowing us to avoid using express-session in order to integrate with Okta.
*/
private _store: StateStore = {
store(_req: express.Request, cb: any) {
cb(null, null);
},
verify(_req: express.Request, _state: string, cb: any) {
cb(null, true);
},
}
constructor(options: OAuthProviderOptions) {
this._strategy = new OktaStrategy({
passReqToCallback: false as true,
...options,
store: this._store,
response_type: 'code',
}, (
accessToken: any,
refreshToken: any,
params: any,
rawProfile: passport.Profile,
done: PassportDoneCallback<OAuthResponse, PrivateInfo>,
) => {
const profile = makeProfileInfo(rawProfile, params.id_token);
done(
undefined,
{
providerInfo: {
idToken: params.id_token,
accessToken,
scope: params.scope,
expiresInSeconds: params.expires_in,
},
profile,
},
{
refreshToken,
},
)
});
}
async start(
req: express.Request,
options: Record<string, string>
): Promise<RedirectInfo> {
const providerOptions = {
...options,
accessType: 'offline',
prompt: 'consent',
};
return await executeRedirectStrategy(req, this._strategy, providerOptions);
}
async handler(
req: express.Request
): Promise<{ response: OAuthResponse; refreshToken: string }> {
const { response, privateInfo } = await executeFrameHandlerStrategy<
OAuthResponse,
PrivateInfo
>(req, this._strategy);
return {
response: await this.populateIdentity(response),
refreshToken: privateInfo.refreshToken,
};
}
async refresh(refreshToken: string, scope: string): Promise<OAuthResponse> {
const { accessToken, params } = await executeRefreshTokenStrategy(
this._strategy,
refreshToken,
scope,
);
const profile = await executeFetchUserProfileStrategy(
this._strategy,
accessToken,
params.id_token,
);
return this.populateIdentity({
providerInfo: {
accessToken,
idToken: params.id_token,
expiresInSeconds: params.expires_in,
scope: params.scope,
},
profile,
});
}
private async populateIdentity(
response: OAuthResponse,
): Promise<OAuthResponse> {
const { profile } = response;
if (!profile.email) {
throw new Error('Okta profile contained no email');
}
// TODO(Rugvip): Hardcoded to the local part of the email for now
const id = profile.email.split('@')[0];
return { ...response, backstageIdentity: { id } };
}
}
export function createOktaProvider(
{ baseUrl }: AuthProviderConfig,
providerConfig: EnvironmentProviderConfig,
logger: Logger,
tokenIssuer: TokenIssuer,
) {
const envProviders: EnvironmentHandlers = {};
for (const [env, envConfig] of Object.entries(providerConfig)) {
const config = (envConfig as unknown) as OAuthProviderConfig;
const { secure, appOrigin } = config;
const callbackURLParam = `?env=${env}`;
const opts = {
audience: config.audience,
clientID: config.clientId,
clientSecret: config.clientSecret,
callbackURL: `${baseUrl}/okta/handler/frame${callbackURLParam}`,
};
if (!opts.clientID || !opts.clientSecret || !opts.audience) {
if (process.env.NODE_ENV !== 'development') {
throw new Error(
'Failed to initialize Okta auth provider, set AUTH_OKTA_CLIENT_ID, AUTH_OKTA_CLIENT_SECRET, and AUTH_OKTA_AUDIENCE env vars',
);
}
logger.warn(
'Okta auth provider disabled, set AUTH_OKTA_CLIENT_ID, AUTH_OKTA_CLIENT_SECRET, and AUTH_OKTA_AUDIENCE env vars to enable',
);
continue;
}
envProviders[env] = new OAuthProvider(new OktaAuthProvider(opts), {
disableRefresh: false,
providerId: 'okta',
secure,
baseUrl,
appOrigin,
tokenIssuer,
});
}
return new EnvironmentHandler(envProviders);
}
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare module 'passport-okta-oauth' {
export class Strategy {
constructor(options: any, verify: any)
}
}
@@ -55,6 +55,10 @@ export type OAuthProviderConfig = {
* Client Secret of the auth provider.
*/
clientSecret: string;
/**
* The location of the OAuth Authorization Server
*/
audience?: string;
};
export type EnvironmentProviderConfig = {
@@ -82,6 +82,15 @@ export async function createRouter(
issuer: 'passport-saml',
},
},
okta: {
development: {
appOrigin: 'http://localhost:3000',
secure: false,
clientId: process.env.AUTH_OKTA_CLIENT_ID!,
clientSecret: process.env.AUTH_OKTA_CLIENT_SECRET!,
audience: process.env.AUTH_OKTA_AUDIENCE,
}
}
},
},
};
@@ -4,6 +4,10 @@ metadata:
name: react-ssr-template
title: React SSR Template
description: Next.js application skeleton for creating isomorphic web applications.
tags:
- Recommended
- React
spec:
type: cookiecutter
processor: cookiecutter
type: website
path: '.'
@@ -0,0 +1,13 @@
apiVersion: backstage.io/v1alpha1
kind: Template
metadata:
name: springboot-template
title: Spring Boot Service
description: Standard Spring Boot (Java) microservice with recommended configuration.
tags:
- Recommended
- Java
spec:
processor: cookiecutter
type: service
path: '.'
+10 -6
View File
@@ -1,8 +1,12 @@
#!/usr/bin/env bash
curl \
--location \
--request POST 'localhost:7000/catalog/locations' \
--header 'Content-Type: application/json' \
--data-raw "{\"type\": \"file\", \"target\": \"$(pwd)/sample-templates/react-ssr-template/template.yaml\"}"
for URL in \
'react-ssr-template' \
'springboot-template' \
; do \
curl \
--location \
--request POST 'localhost:7000/catalog/locations' \
--header 'Content-Type: application/json' \
--data-raw "{\"type\": \"file\", \"target\": \"$(pwd)/sample-templates/${URL}/template.yaml\"}"
done
+4 -1
View File
@@ -21,6 +21,8 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.1.1-alpha.12",
"@backstage/plugin-catalog": "^0.1.1-alpha.12",
"@backstage/core": "^0.1.1-alpha.12",
"@backstage/theme": "^0.1.1-alpha.12",
"@material-ui/core": "^4.9.1",
@@ -29,7 +31,8 @@
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "6.0.0-alpha.5",
"react-use": "^14.2.0"
"react-use": "^14.2.0",
"swr": "^0.2.2"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.12",
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import React from 'react';
import React, { useEffect } from 'react';
import {
Lifecycle,
Content,
@@ -23,33 +23,39 @@ import {
SupportButton,
Page,
pageTheme,
useApi,
errorApiRef,
} from '@backstage/core';
import { Button, Grid, Link, Typography } from '@material-ui/core';
import { catalogApiRef } from '@backstage/plugin-catalog';
import {
Typography,
Link,
Button,
Grid,
LinearProgress,
} from '@material-ui/core';
import { Link as RouterLink } from 'react-router-dom';
import TemplateCard from '../TemplateCard';
import useStaleWhileRevalidate from 'swr';
import { TemplateEntityV1alpha1 } from '@backstage/catalog-model';
// TODO(blam): Connect to backend
const STATIC_DATA = [
{
id: 'springboot-template',
type: 'service',
name: 'Spring Boot Service',
tags: ['Recommended', 'Java'],
description:
'Standard Spring Boot (Java) microservice with recommended configuration.',
ownerId: 'spotify',
},
{
id: 'react-ssr-template',
type: 'website',
name: 'SSR React Website',
tags: ['Recommended', 'React'],
description:
'Next.js application skeleton for creating isomorphic web applications.',
ownerId: 'spotify',
},
];
const ScaffolderPage: React.FC<{}> = () => {
const catalogApi = useApi(catalogApiRef);
const errorApi = useApi(errorApiRef);
const { data: templates, isValidating, error } = useStaleWhileRevalidate(
'templates/all',
async () =>
catalogApi.getEntities({ kind: 'Template' }) as Promise<
TemplateEntityV1alpha1[]
>,
);
useEffect(() => {
if (!error) return;
errorApi.post(error);
}, [error, errorApi]);
return (
<Page theme={pageTheme.home}>
<Header
@@ -84,18 +90,24 @@ const ScaffolderPage: React.FC<{}> = () => {
</Link>
.
</Typography>
{!templates && isValidating && <LinearProgress />}
<Grid container>
{STATIC_DATA.map(item => {
return (
<TemplateCard
key={item.id}
title={item.name}
type={item.type}
description={item.description}
tags={item.tags}
/>
);
})}
{templates &&
templates.map(template => {
return (
<Grid item xs={12} sm={6} md={3}>
<TemplateCard
key={template.metadata.uid}
title={`${
(template.metadata.title || template.metadata.name) ?? ''
}`}
type={template.spec.type ?? ''}
description={template.metadata.description ?? '-'}
tags={(template.metadata?.tags as string[]) ?? []}
/>
</Grid>
);
})}
</Grid>
</Content>
</Page>
@@ -14,14 +14,7 @@
* limitations under the License.
*/
import React, { FC } from 'react';
import {
Button,
Card,
Chip,
Grid,
Typography,
makeStyles,
} from '@material-ui/core';
import { Button, Card, Chip, Typography, makeStyles } from '@material-ui/core';
const useStyles = makeStyles(theme => ({
header: {
@@ -59,25 +52,23 @@ const TemplateCard: FC<TemplateCardProps> = ({
const classes = useStyles();
return (
<Grid item xs={12} sm={6} md={3}>
<Card>
<div className={classes.header}>
<Typography variant="subtitle2">{type}</Typography>
<Typography variant="h6">{title}</Typography>
<Card>
<div className={classes.header}>
<Typography variant="subtitle2">{type}</Typography>
<Typography variant="h6">{title}</Typography>
</div>
<div className={classes.content}>
{tags?.map(tag => (
<Chip label={tag} key={tag} />
))}
<Typography variant="body2" paragraph className={classes.description}>
{description}
</Typography>
<div className={classes.footer}>
<Button color="primary">Choose</Button>
</div>
<div className={classes.content}>
{tags?.map(tag => (
<Chip label={tag} />
))}
<Typography variant="body2" paragraph className={classes.description}>
{description}
</Typography>
<div className={classes.footer}>
<Button color="primary">Choose</Button>
</div>
</div>
</Card>
</Grid>
</div>
</Card>
);
};
+141 -374
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"
@@ -4331,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"
@@ -4465,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==
@@ -4623,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"
@@ -4677,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"
@@ -4917,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==
@@ -5820,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==
@@ -6221,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"
@@ -6343,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"
@@ -6691,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"
@@ -6712,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"
@@ -6758,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==
@@ -7099,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"
@@ -7332,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==
@@ -7438,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"
@@ -7578,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"
@@ -8365,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"
@@ -8591,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"
@@ -9331,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"
@@ -9441,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"
@@ -9518,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"
@@ -9641,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"
@@ -9912,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"
@@ -9927,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"
@@ -10290,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"
@@ -10542,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"
@@ -10826,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"
@@ -10847,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"
@@ -10857,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==
@@ -10939,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"
@@ -11209,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"
@@ -13071,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==
@@ -13086,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:
@@ -13950,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"
@@ -14246,7 +14026,16 @@ passport-google-oauth20@^2.0.0:
dependencies:
passport-oauth2 "1.x.x"
passport-oauth2@1.x.x:
passport-oauth1@1.x.x:
version "1.1.0"
resolved "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.1.0.tgz#a7de988a211f9cf4687377130ea74df32730c918"
integrity sha1-p96YiiEfnPRoc3cTDqdN8ycwyRg=
dependencies:
oauth "0.9.x"
passport-strategy "1.x.x"
utils-merge "1.x.x"
passport-oauth2@1.x.x, passport-oauth2@^1.5.0:
version "1.5.0"
resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.5.0.tgz#64babbb54ac46a4dcab35e7f266ed5294e3c4108"
integrity sha512-kqBt6vR/5VlCK8iCx1/KpY42kQ+NEHZwsSyt4Y6STiNjU+wWICG1i8ucc1FapXDGO15C5O5VZz7+7vRzrDPXXQ==
@@ -14257,6 +14046,23 @@ passport-oauth2@1.x.x:
uid2 "0.0.x"
utils-merge "1.x.x"
passport-oauth@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/passport-oauth/-/passport-oauth-1.0.0.tgz#90aff63387540f02089af28cdad39ea7f80d77df"
integrity sha1-kK/2M4dUDwIImvKM2tOep/gNd98=
dependencies:
passport-oauth1 "1.x.x"
passport-oauth2 "1.x.x"
passport-okta-oauth@^0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/passport-okta-oauth/-/passport-okta-oauth-0.0.1.tgz#c8bcee02af3d56ca79d3cca776f2df7cf15a5748"
integrity sha1-yLzuAq89Vsp508yndvLffPFaV0g=
dependencies:
passport-oauth "1.0.0"
pkginfo "0.2.x"
uid2 "0.0.3"
passport-saml@^1.3.3:
version "1.3.3"
resolved "https://registry.npmjs.org/passport-saml/-/passport-saml-1.3.3.tgz#cbea1a2b21ff32b3bc4bfd84dc39c3a370df9935"
@@ -14316,7 +14122,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=
@@ -14505,6 +14311,11 @@ pkg-up@3.1.0, pkg-up@^3.1.0:
dependencies:
find-up "^3.0.0"
pkginfo@0.2.x:
version "0.2.3"
resolved "https://registry.npmjs.org/pkginfo/-/pkginfo-0.2.3.tgz#7239c42a5ef6c30b8f328439d9b9ff71042490f8"
integrity sha1-cjnEKl72wwuPMoQ52bn/cQQkkPg=
please-upgrade-node@^3.2.0:
version "3.2.0"
resolved "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
@@ -16313,11 +16124,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"
@@ -16328,6 +16134,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"
@@ -16577,20 +16388,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"
@@ -18333,13 +18137,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"
@@ -18398,21 +18195,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"
@@ -18524,7 +18311,7 @@ uid-number@0.0.6:
resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=
uid2@0.0.x:
uid2@0.0.3, uid2@0.0.x:
version "0.0.3"
resolved "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82"
integrity sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=
@@ -18695,13 +18482,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"
@@ -18915,11 +18695,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"
@@ -19693,15 +19468,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==