diff --git a/babel.config.js b/babel.config.js index c8d4f761..c77e3adc 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,21 +1,57 @@ -module.exports = { - presets: [ - [ - "@babel/preset-env", - { targets: { firefox: "70", chrome: "78", electron: "8" } }, +module.exports = (api) => { + const target = api.env(); + + // Browser extension (full) + if (target === "extension") { + return { + presets: [ + ["@babel/preset-env", { targets: { firefox: "70", chrome: "78" } }], + ...guiPresets, + ], + plugins: [ + ...guiPlugins, + [ + "babel-plugin-transform-remove-imports", + { test: /^(electron|fs|path)\/?/ }, + ], + ], + }; + } + + // Electron (panel) + if (target === "electron") { + return { + presets: [ + ["@babel/preset-env", { targets: { electron: "9" } }], + ...guiPresets, + ], + plugins: [...guiPlugins], + }; + } + + // Electron (main.js) + return { + presets: [ + ["@babel/preset-env", { targets: { node: "10" } }], + "@babel/preset-typescript", ], - "@babel/preset-react", - "@babel/preset-typescript", - ], - plugins: [ - "inline-react-svg", - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-optional-chaining", - [ - "babel-plugin-styled-components", - { - fileName: false, - }, + plugins: [ + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-optional-chaining", ], - ], + }; }; + +const guiPresets = ["@babel/preset-react", "@babel/preset-typescript"]; + +const guiPlugins = [ + "inline-react-svg", + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-optional-chaining", + [ + "babel-plugin-styled-components", + { + fileName: false, + }, + ], +]; diff --git a/package.json b/package.json index 23adb105..a14c2d14 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "@types/enzyme": "^3.10.5", "@types/enzyme-adapter-react-16": "^1.0.6", "@types/fast-json-stable-stringify": "^2.0.0", + "@types/google.analytics": "^0.0.40", "@types/graphql": "^14.5.0", "@types/jest": "^25.2.2", "@types/jest-environment-puppeteer": "^4.3.1", @@ -98,6 +99,7 @@ "babel-loader": "^8.1.0", "babel-plugin-inline-react-svg": "andyrichardson/babel-plugin-inline-react-svg#refs", "babel-plugin-styled-components": "^1.10.7", + "babel-plugin-transform-remove-imports": "^1.3.2", "bestzip": "^2.1.5", "clean-webpack-plugin": "^3.0.0", "codemirror": "^5.53.2", diff --git a/src/extension/content_script.ts b/src/extension/content_script.ts index a163bf01..3d58caee 100644 --- a/src/extension/content_script.ts +++ b/src/extension/content_script.ts @@ -42,3 +42,5 @@ const handleMessage = (message: ExchangeMessage) => { const handleDisconnect = () => { connection = undefined; }; + +window.addEventListener("keypress", console.log); diff --git a/src/extension/manifest.json b/src/extension/manifest.json index bb0a72ea..f9ff761e 100644 --- a/src/extension/manifest.json +++ b/src/extension/manifest.json @@ -31,8 +31,8 @@ } }, "devtools_page": "devtools.html", - "permissions": ["file:///*", "http://*/*", "https://*/*"], - "content_security_policy": "script-src 'self'; object-src 'self'", + "permissions": ["file:///*", "http://*/*", "https://*/*", "storage"], + "content_security_policy": "script-src 'self' https://www.google-analytics.com/analytics.js; object-src 'self'", "application": { "gecko": { "id": "{c11f3a69-f159-4708-b044-853066c2d2fe}", diff --git a/src/panel/App.tsx b/src/panel/App.tsx index fa2c9fd2..8d07a708 100644 --- a/src/panel/App.tsx +++ b/src/panel/App.tsx @@ -18,16 +18,19 @@ import { ExplorerProvider, useDevtoolsContext, TimelineProvider, + TelemetryProvider, } from "./context"; export const App = () => { return ( - - - - - + + + + + + + ); diff --git a/src/panel/context/Telemetry.tsx b/src/panel/context/Telemetry.tsx new file mode 100644 index 00000000..84c047a2 --- /dev/null +++ b/src/panel/context/Telemetry.tsx @@ -0,0 +1,138 @@ +import React, { + createContext, + useContext, + useLayoutEffect, + useState, + useCallback, + useMemo, + FC, + useEffect, + useRef, +} from "react"; +import { nanoid } from "nanoid"; +import * as storage from "../util/storage"; + +type Page = + | "explorer" + | "events" + | "request" + | "disconnected" + | "error" + | "mismatch"; +interface TelemetryContextValue { + state: "enabled" | "disabled" | "pending"; + disableTelemetry: () => void; + enableTelemetry: () => void; + setPage: (page: Page) => void; +} + +const TelemetryContext = createContext(null as any); + +export const useTelemetry = () => useContext(TelemetryContext); + +export const usePageTelemetry = (page: Page) => { + const { setPage } = useTelemetry(); + + useEffect(() => { + setPage(page); + }, []); +}; + +export const TelemetryProvider: FC = ({ children }) => { + const active = useRef(false); + const [state, setState] = useState(); + + useLayoutEffect(() => { + storage.get("allowTelemetry").then((telemetryEnabled) => + setState(() => { + if (telemetryEnabled === undefined) { + return "pending"; + } + + if (telemetryEnabled === false) { + return "disabled"; + } + + return "enabled"; + }) + ); + }, []); + + useEffect(() => { + if (state !== "enabled") { + return; + } + + (async () => { + startAnalytics({ clientId: await storage.get("userId") }); + active.current = true; + })(); + }, [state]); + + const ga = useCallback((...args: Parameters) => { + if (!active.current) { + return; + } + + window.ga(...args); + }, []); + + const enableTelemetry = useCallback(async () => { + await storage.set("allowTelemetry", true); + await storage.set("userId", nanoid()); + setState("enabled"); + }, []); + + const disableTelemetry = useCallback(async () => { + await storage.set("allowTelemetry", false); + setState("disabled"); + }, []); + + const setPage = useCallback( + (page) => { + console.log(active.current); + ga("set", "page", page); + ga("send", { hitType: "pageview" }); + }, + [ga] + ); + + const value = useMemo( + () => ({ + state: state as TelemetryContextValue["state"], + enableTelemetry, + disableTelemetry, + setPage, + }), + [state, enableTelemetry, disableTelemetry, setPage] + ); + + if (state === undefined) { + return null; + } + + return ( + + {children} + + ); +}; + +/** Global script to enable google analytics. */ +const startAnalytics = ({ clientId }: { clientId: string }) => { + window.ga = + window.ga || ((...args) => (window.ga.q = window.ga.q || []).push(args)); + window.ga.l = +new Date(); + + ga("create", "UA-98443810-2", { + storage: "none", + clientId, + }); + /* + * See here for explanation - https://stackoverflow.com/questions/3591847/google-analytics-from-a-file-url + */ + ga("set", "checkProtocolTask", null); + ga("set", "checkStorageTask", null); + ga("set", "historyImportTask", null); + ga("set", "dataSource", "devtools"); +}; diff --git a/src/panel/context/index.ts b/src/panel/context/index.ts index a6280f65..708cfd04 100644 --- a/src/panel/context/index.ts +++ b/src/panel/context/index.ts @@ -2,3 +2,4 @@ export * from "./Devtools"; export * from "./Request"; export * from "./Explorer"; export * from "./Timeline"; +export * from "./Telemetry"; diff --git a/src/panel/pages/disconnected/Disconnected.tsx b/src/panel/pages/disconnected/Disconnected.tsx index 73723b65..b0ddea64 100644 --- a/src/panel/pages/disconnected/Disconnected.tsx +++ b/src/panel/pages/disconnected/Disconnected.tsx @@ -1,17 +1,22 @@ import React, { FC, ComponentProps } from "react"; import styled, { createGlobalStyle } from "styled-components"; +import { usePageTelemetry } from "../../context"; import Icon from "../../../assets/icon.svg"; -export const Disconnected: FC> = (props) => ( - <> - - - -
Waiting for exchange
- Make sure {"you're"} using the Urql Devtools exchange! -
- -); +export const Disconnected: FC> = (props) => { + usePageTelemetry("disconnected"); + + return ( + <> + + + +
Waiting for exchange
+ Make sure {"you're"} using the Urql Devtools exchange! +
+ + ); +}; const GlobalStyle = createGlobalStyle` body { diff --git a/src/panel/pages/error/ErrorBoundary.tsx b/src/panel/pages/error/ErrorBoundary.tsx index 339fb52e..4d9600d6 100644 --- a/src/panel/pages/error/ErrorBoundary.tsx +++ b/src/panel/pages/error/ErrorBoundary.tsx @@ -1,9 +1,10 @@ -import React, { Component, ComponentProps } from "react"; +import React, { Component, ComponentProps, useCallback, FC } from "react"; import { faBug, faRedoAlt } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import styled from "styled-components"; import { CodeHighlight } from "../../components"; import { openExternalUrl } from "../../util"; +import { usePageTelemetry } from "../../context"; export class ErrorBoundary extends Component< ComponentProps, @@ -17,47 +18,46 @@ export class ErrorBoundary extends Component< }); } - private handleReloadClick = () => { - window.location.reload(); - }; - - private handleReportClick = () => { - if (!this.state.error) { - return; - } - - const url = createIssueUrl(this.state.error); - openExternalUrl(url); - }; - render() { if (!this.state.error) { return this.props.children; } - return ( - - - -
Unexpected Error
- - Something went wrong and {"we're"} not totally sure why... - - - - - -
- - - -
- ); + return ; } } +const ErrorPage: FC<{ error: Error } & ComponentProps> = ({ + error, + props, +}) => { + usePageTelemetry("error"); + + const handleReportClick = useCallback(() => { + const url = createIssueUrl(error); + openExternalUrl(url); + }, []); + + return ( + + + +
Unexpected Error
+ Something went wrong and {"we're"} not totally sure why... + + + + +
+ + + +
+ ); +}; + const createIssueUrl = (err: Error) => { const uri = `https://github.com/FormidableLabs/urql-devtools/issues/new`; const params = new URLSearchParams({ diff --git a/src/panel/pages/events/Timeline.tsx b/src/panel/pages/events/Timeline.tsx index 891ff720..b08fe6a4 100644 --- a/src/panel/pages/events/Timeline.tsx +++ b/src/panel/pages/events/Timeline.tsx @@ -8,7 +8,11 @@ import React, { } from "react"; import styled from "styled-components"; import { Operation } from "@urql/core"; -import { useTimelineContext, START_PADDING } from "../../context"; +import { + useTimelineContext, + START_PADDING, + usePageTelemetry, +} from "../../context"; import { Background } from "../../components/Background"; import { TimelineRow, @@ -32,6 +36,7 @@ export const Timeline: FC> = (props) => { filter, } = useTimelineContext(); const [selectedSource, setSelectedSource] = useState(); + usePageTelemetry("events"); // Unmount source pane on event select useEffect(() => { diff --git a/src/panel/pages/explorer/Explorer.tsx b/src/panel/pages/explorer/Explorer.tsx index f317a807..98c2f8e4 100644 --- a/src/panel/pages/explorer/Explorer.tsx +++ b/src/panel/pages/explorer/Explorer.tsx @@ -1,11 +1,12 @@ import React, { useContext, FC, ComponentProps } from "react"; import styled from "styled-components"; import { Background } from "../../components"; -import { ExplorerContext } from "../../context"; +import { ExplorerContext, usePageTelemetry } from "../../context"; import { Tree, NodeInfoPane } from "./components"; export const Explorer: FC> = (props) => { const { operations } = useContext(ExplorerContext); + usePageTelemetry("explorer"); return ( diff --git a/src/panel/pages/mismatch/Mismatch.tsx b/src/panel/pages/mismatch/Mismatch.tsx index 3e23bc9f..94ee916e 100644 --- a/src/panel/pages/mismatch/Mismatch.tsx +++ b/src/panel/pages/mismatch/Mismatch.tsx @@ -3,10 +3,11 @@ import styled from "styled-components"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faBomb } from "@fortawesome/free-solid-svg-icons"; import { CodeHighlight } from "../../components"; -import { useDevtoolsContext } from "../../context"; +import { useDevtoolsContext, usePageTelemetry } from "../../context"; export const Mismatch: FC> = (props) => { const { client } = useDevtoolsContext(); + usePageTelemetry("mismatch"); if (!client.connected) { return null; diff --git a/src/panel/pages/request/Request.tsx b/src/panel/pages/request/Request.tsx index 4e1e21a2..20c6b63a 100644 --- a/src/panel/pages/request/Request.tsx +++ b/src/panel/pages/request/Request.tsx @@ -1,9 +1,12 @@ import React, { ComponentProps, FC } from "react"; import styled from "styled-components"; import { Background } from "../../components/Background"; +import { usePageTelemetry } from "../../context"; import { Query, Response, Settings } from "./components"; export const Request: FC> = (props) => { + usePageTelemetry("request"); + return ( diff --git a/src/panel/panel.html b/src/panel/panel.html index 78dce54d..38ccfc51 100644 --- a/src/panel/panel.html +++ b/src/panel/panel.html @@ -2,6 +2,7 @@ Urql Devtools +
diff --git a/src/panel/types/analytics.d.ts b/src/panel/types/analytics.d.ts new file mode 100644 index 00000000..8613e86e --- /dev/null +++ b/src/panel/types/analytics.d.ts @@ -0,0 +1,13 @@ +declare const ga: GoogleAnalytics; + +interface Window { + dataLayer: any[]; + _gaq: any[]; + ga: GoogleAnalytics; +} + +interface GoogleAnalytics { + (...args: any[]): void; + q: any[]; + l: number; +} diff --git a/src/panel/util/Connection.ts b/src/panel/util/Connection.ts index 412feb75..599785ad 100644 --- a/src/panel/util/Connection.ts +++ b/src/panel/util/Connection.ts @@ -1,4 +1,5 @@ import { ExchangeMessage, DevtoolsMessage } from "@urql/devtools"; +import { ipcRenderer } from "electron"; import { DevtoolsPanelConnectionName } from "../../types"; export interface ConnectionType { @@ -21,9 +22,6 @@ export const createConnection = (): ConnectionType => { } let listeners: Function[] = []; - const ipcRenderer = require("electron") - .ipcRenderer as import("electron").IpcRenderer; - ipcRenderer.on("message", (event, message) => listeners.forEach((l) => l(message)) ); diff --git a/src/panel/util/index.ts b/src/panel/util/index.ts index cb712d53..da917b60 100644 --- a/src/panel/util/index.ts +++ b/src/panel/util/index.ts @@ -1,2 +1,3 @@ export * from "./Connection"; export * from "./openExternalUrl"; +export * from "./storage"; diff --git a/src/panel/util/openExternalUrl.ts b/src/panel/util/openExternalUrl.ts index 121b94a6..bd6ca02f 100644 --- a/src/panel/util/openExternalUrl.ts +++ b/src/panel/util/openExternalUrl.ts @@ -1,8 +1,9 @@ +import electron from "electron"; + export const openExternalUrl = (url: string) => { if (process.env.BUILD_ENV === "extension") { return window.open(url, "_blank"); } - const shell = require("electron").shell as import("electron").Shell; - shell.openExternal(url); + electron.shell.openExternal(url); }; diff --git a/src/panel/util/storage.test.ts b/src/panel/util/storage.test.ts new file mode 100644 index 00000000..70c18f8d --- /dev/null +++ b/src/panel/util/storage.test.ts @@ -0,0 +1,63 @@ +jest.mock("electron", () => ({ + remote: { app: { getPath: jest.fn(() => "/tmp/") } }, +})); +import { promises as fs } from "fs"; +import { get, set } from "./storage"; + +const configFile = "/tmp/.urql-devtools.json"; + +describe("electron", () => { + beforeEach(async () => { + process.env.BUILD_ENV = "electron"; + await fs.unlink(configFile).catch(() => false); + }); + + describe("get", () => { + describe("on empty file", () => { + it("returns undefined", async () => { + expect(await get("version")).toBe(undefined); + }); + }); + + describe("on populated file", () => { + const data = { somekey: { value: 1234 } }; + beforeEach(() => { + fs.writeFile(configFile, JSON.stringify(data), "utf-8"); + }); + it("returns value", async () => { + expect(await get("somekey")).toEqual(data.somekey); + }); + }); + }); + + describe("set", () => { + describe("on empty file", () => { + it("sets new file value", async () => { + const key = "mykey"; + const value = { someValue: "1234" }; + + await set(key, value); + const file = await fs.readFile(configFile, "utf-8"); + expect(file).toEqual(JSON.stringify({ [key]: value }, null, 2)); + }); + }); + + describe("on populated file", () => { + const data = { somekey: { value: 1234 } }; + beforeEach(() => { + fs.writeFile("/tmp/.urql-devtools.json", JSON.stringify(data), "utf-8"); + }); + + it("merges object", async () => { + const key = "newkey"; + const value = 1234; + + await set(key, value); + const file = await fs.readFile(configFile, "utf-8"); + expect(file).toEqual( + JSON.stringify({ ...data, [key]: value }, null, 2) + ); + }); + }); + }); +}); diff --git a/src/panel/util/storage.ts b/src/panel/util/storage.ts new file mode 100644 index 00000000..9f62b6e0 --- /dev/null +++ b/src/panel/util/storage.ts @@ -0,0 +1,53 @@ +import { promises as fs } from "fs"; +import path from "path"; +import electron from "electron"; + +export const get: ( + key: T +) => Promise = async (key) => { + if (process.env.BUILD_ENV === "extension") { + return new Promise((resolve) => + chrome.storage.sync.get(key, (i) => resolve(i[key])) + ); + } + + const filePath = getFilePath(); + try { + return JSON.parse(await fs.readFile(filePath, "utf-8"))[key]; + } catch (err) { + return undefined; + } +}; + +export const set: ( + key: T, + value: StorageSchema[T] +) => Promise = async (key, value) => { + if (process.env.BUILD_ENV === "extension") { + return new Promise((resolve) => + chrome.storage.sync.set({ [key]: value }, resolve) + ); + } + + const filePath = getFilePath(); + const oldState = JSON.parse( + await fs.readFile(filePath, "utf-8").catch(() => "{}") + ); + await fs.writeFile( + filePath, + JSON.stringify({ ...oldState, [key]: value }, null, 2), + "utf-8" + ); +}; + +const getFilePath = () => { + return path.join( + electron.remote.app.getPath("appData"), + ".urql-devtools.json" + ); +}; + +type StorageSchema = { + allowTelemetry: boolean; + userId: string; +}; diff --git a/webpack/webpack.electron.config.js b/webpack/webpack.electron.config.js index e6c553e7..9f790559 100644 --- a/webpack/webpack.electron.config.js +++ b/webpack/webpack.electron.config.js @@ -3,7 +3,7 @@ const nodeExternals = require("webpack-node-externals"); const root = `${__dirname}/..`; module.exports = { - target: "node", + target: "electron-main", devtool: "source-map", mode: process.env.NODE_ENV === "production" ? "production" : "development", entry: { @@ -24,14 +24,7 @@ module.exports = { test: /\.*ts$/, loader: "babel-loader", options: { - presets: [ - ["@babel/preset-env", { targets: { node: "10" } }], - "@babel/preset-typescript", - ], - plugins: [ - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-optional-chaining", - ], + envName: "electron-node", }, }, ], diff --git a/webpack/webpack.extension.config.js b/webpack/webpack.extension.config.js index b0ffc22f..be9c3f78 100644 --- a/webpack/webpack.extension.config.js +++ b/webpack/webpack.extension.config.js @@ -11,6 +11,7 @@ const isExtension = process.env.BUILD_ENV !== "electron"; const inOutConfig = isExtension ? { + target: "web", entry: { background: `${root}/src/extension/background.ts`, devtools: `${root}/src/extension/devtools.ts`, @@ -24,6 +25,7 @@ const inOutConfig = isExtension }, } : { + target: "electron-renderer", entry: { panel: `${root}/src/panel/panel.tsx`, }, @@ -56,17 +58,14 @@ module.exports = { resolve: { extensions: [".ts", ".tsx", ".mjs", ".js", ".jsx"], }, - externals: isExtension - ? undefined - : { - electron: "commonjs2 electron", - }, - node: false, module: { rules: [ { test: /\.*tsx?$/, loader: "babel-loader", + options: { + envName: process.env.BUILD_ENV, + }, }, { test: /\.css$/, @@ -110,7 +109,6 @@ module.exports = { }, ].filter(Boolean), }), - isExtension && new webpack.IgnorePlugin(/electron/), isExtension && new HtmlWebpackPlugin({ template: `${root}/src/extension/devtools.html`, @@ -122,12 +120,16 @@ module.exports = { filename: "panel.html", chunks: ["panel"], }), - new CspHtmlWebpackPlugin({ - "default-src": "'self'", - "script-src": "'self'", - "style-src": "'self' 'unsafe-inline' https://fonts.googleapis.com", - "font-src": "'self' https://fonts.gstatic.com", - "img-src": "'self' data:", - }), + // new CspHtmlWebpackPlugin( + // { + // "default-src": "'self'", + // "script-src": + // "'self' 'unsafe-inline' https://www.googletagmanager.com/", + // "style-src": "'self' 'unsafe-inline' https://fonts.googleapis.com", + // "font-src": "'self' https://fonts.gstatic.com", + // "img-src": "'self' data:", + // }, + // { nonceEnabled: true } + // ), ].filter(Boolean), }; diff --git a/yarn.lock b/yarn.lock index c37935c7..bda48d97 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1015,12 +1015,13 @@ prop-types "^15.7.2" "@istanbuljs/load-nyc-config@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b" - integrity sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg== + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: camelcase "^5.3.1" find-up "^4.1.0" + get-package-type "^0.1.0" js-yaml "^3.13.1" resolve-from "^5.0.0" @@ -1316,9 +1317,9 @@ integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== "@sinonjs/commons@^1.7.0": - version "1.7.2" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2" - integrity sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw== + version "1.8.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d" + integrity sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q== dependencies: type-detect "4.0.8" @@ -1487,6 +1488,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/google.analytics@^0.0.40": + version "0.0.40" + resolved "https://registry.yarnpkg.com/@types/google.analytics/-/google.analytics-0.0.40.tgz#35526e9d78333423c430ade1c821ce54d0f0f850" + integrity sha512-R3HpnLkqmKxhUAf8kIVvDVGJqPtaaZlW4yowNwjOZUTmYUQEgHh8Nh5wkSXKMroNAuQM8gbXJHmNbbgA8tdb7Q== + "@types/graceful-fs@^4.1.2": version "4.1.3" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f" @@ -1595,14 +1601,14 @@ "@types/node" "*" "@types/node@*": - version "14.0.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.1.tgz#5d93e0a099cd0acd5ef3d5bde3c086e1f49ff68c" - integrity sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA== + version "14.0.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.5.tgz#3d03acd3b3414cf67faf999aed11682ed121f22b" + integrity sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA== "@types/node@^12.0.12": - version "12.12.39" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.39.tgz#532d25c1e639d89dd6f3aa1d7b3962e3e7fa943d" - integrity sha512-pADGfwnDkr6zagDwEiCVE4yQrv7XDkoeVa4OfA9Ju/zRTk6YNDLGtQbkdL4/56mCQQCs4AhNrBIag6jrp7ZuOg== + version "12.12.42" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.42.tgz#d0d1149336bd07540dd1ea576692829d575dec34" + integrity sha512-R/9QdYFLL9dE9l5cWWzWIZByVGFd7lk7JVOJ7KD+E1SJ4gni7XJRLz9QTjyYQiHIqEAgku9VgxdLjMlhhUaAFg== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -1615,19 +1621,26 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.0.tgz#dc85454b953178cc6043df5208b9e949b54a3bc4" - integrity sha512-/rM+sWiuOZ5dvuVzV37sUuklsbg+JPOP8d+nNFlo2ZtfpzPiPvh1/gc8liWOLBqe+sR+ZM7guPaIcTt6UZTo7Q== + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.1.tgz#b6e98083f13faa1e5231bfa3bdb1b0feff536b6d" + integrity sha512-boy4xPNEtiw6N3abRhBi/e7hNvy3Tt8E9ZRAQrwAGzoCGZS/1wjo9KY7JHhnfnEsG5wSjDbymCozUM9a3ea7OQ== "@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== -"@types/puppeteer@*", "@types/puppeteer@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-2.1.0.tgz#31367580654632f87f86df565f1bde0533577401" - integrity sha512-QIRQXl0VaSgnwOZ1LwxD321Tfb1jLOzCWuF2BrwjEkWq2IhxSicPOddUywLV7dRSO6mcU4sWKRdoGdci6gk0Aw== +"@types/puppeteer@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-3.0.0.tgz#24cdcc131e319477608d893f0017e08befd70423" + integrity sha512-59+fkfHHXHzX5rgoXIMnZyzum7ZLx/Wc3fhsOduFThpTpKbzzdBHMZsrkKGLunimB4Ds/tI5lXTRLALK8Mmnhg== + dependencies: + "@types/node" "*" + +"@types/puppeteer@^2.1.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-2.1.1.tgz#dfbec9de3db4328ec9b66ab2cbb1875033bc22f6" + integrity sha512-FqPZvUtnpTGrqbHvPUn76pvVcBPEVEqZftrdOjr6YRkaaxkjKQ8dQLNaQBjER7Lvd1Q6+0R0XR+N3tYGWBSzNw== dependencies: "@types/node" "*" @@ -1715,9 +1728,9 @@ "@types/estree" "*" "@types/uglify-js@*": - version "3.9.1" - resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.9.1.tgz#0ad39d6a72979593f669acdfc7e980d590d3fb94" - integrity sha512-rdBIeMQyRBOXogop/EYBvSkYFn9D9yGxUa5hagBVG55KIdSUbp22EACJSHCs6kmmfunojAhf7zJH+Ds06/qLaQ== + version "3.9.2" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.9.2.tgz#01992579debba674e1e359cd6bcb1a1d0ab2e02b" + integrity sha512-d6dIfpPbF+8B7WiCi2ELY7m0w1joD8cRW4ms88Emdb2w062NeEpbNCeWwVCgzLRpVG+5e74VFSg4rgJ2xXjEiQ== dependencies: source-map "^0.6.1" @@ -2694,6 +2707,11 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= +babel-plugin-transform-remove-imports@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-remove-imports/-/babel-plugin-transform-remove-imports-1.3.2.tgz#a48201a8b6e60d4f95028914ace843f3ab762a4d" + integrity sha512-mm+/CyaY6mMoZUmwXOyoSRDPbcE2euXkw9WM11KGohv9oppM2eQ9kJfmYGpWGv6Ao3rvSbn2RshihXQaR6TrnA== + babel-preset-current-node-syntax@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz#fb4a4c51fe38ca60fede1dc74ab35eb843cb41d6" @@ -2836,14 +2854,14 @@ bluebird@~2.9.24: integrity sha1-L3tOyAIWMoqf3evfacjUlC/v99g= bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" - integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== bn.js@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.1.tgz#48efc4031a9c4041b9c99c6941d903463ab62eb5" - integrity sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA== + version "5.1.2" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.2.tgz#c9686902d3c9a27729f43ab10f9d79c2004da7b0" + integrity sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA== body-parser@1.19.0: version "1.19.0" @@ -3421,7 +3439,7 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-truncate@^2.1.0: +cli-truncate@2.1.0, cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== @@ -3490,9 +3508,9 @@ codemirror-graphql@^0.11.6: graphql-language-service-parser "^1.5.2" codemirror@^5.53.2: - version "5.53.2" - resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.53.2.tgz#9799121cf8c50809cca487304e9de3a74d33f428" - integrity sha512-wvSQKS4E+P8Fxn/AQ+tQtJnF1qH5UOlxtugFLpubEZ5jcdH2iXTVinb+Xc/4QjshuOxRm4fUsU2QPF1JJKiyXA== + version "5.54.0" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.54.0.tgz#82b6adf662b29eeb7b867fe7839d49e25e4a0b38" + integrity sha512-Pgf3surv4zvw+KaW3doUU7pGjF0BPU8/sj7eglWJjzni46U/DDW8pu3nZY0QgQKUcICDXRkq8jZmq0y6KhxM3Q== collect-v8-coverage@^1.0.0: version "1.0.1" @@ -4485,9 +4503,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.413: - version "1.3.443" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.443.tgz#b01c8933e820a728afc5dde7dd535c53ff55ab11" - integrity sha512-ty0LEroTap/xfMy31oQ3XX+Q377QvWvJaha2cmuXcbmBiX2EIB5SNDQ0hp8lhvxj63WG0zOgm/4MQ/oUBdfQqg== + version "1.3.451" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.451.tgz#0c075af3e2f06d706670bde0279432802ca8c83f" + integrity sha512-2fvco0F2bBIgqzO8GRP0Jt/91pdrf9KfZ5FsmkYkjERmIJG585cFeFZV4+CO6oTmU3HmCTgfcZuEa7kW8VUh3A== electron@^8.3.0: version "8.3.0" @@ -5224,9 +5242,9 @@ execa@^1.0.0: strip-eof "^1.0.0" execa@^4.0.0, execa@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.1.tgz#988488781f1f0238cd156f7aaede11c3e853b4c1" - integrity sha512-SCjM/zlBdOK8Q5TIjOn6iEHZaPHFsMoTxXQ2nvUvtPnuohz3H2dIozSg+etNR98dGoYUp2ENSKLL/XaMmbxVgw== + version "4.0.2" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.2.tgz#ad87fb7b2d9d564f70d2b62d511bee41d5cbb240" + integrity sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q== dependencies: cross-spawn "^7.0.0" get-stream "^5.0.0" @@ -5844,6 +5862,11 @@ get-own-enumerable-property-symbols@^3.0.0: resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + get-stdin@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" @@ -6511,9 +6534,9 @@ ignore@^4.0.6: integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" - integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== + version "5.1.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.5.tgz#4e2f418f16d019e7fb97d0ea6ed1dcd67864d96b" + integrity sha512-QD4ngLBjCwACR+Wq7ZPzjgCpOWOOBm+5+qwXN0rQfiht0kjTGmBcLXlJRxiyL9rpyq19Z8tuLyG5LLWEEfjXWw== import-fresh@3.2.1, import-fresh@^3.0.0, import-fresh@^3.1.0: version "3.2.1" @@ -7967,11 +7990,12 @@ lines-and-columns@^1.1.6: integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= lint-staged@^10.2.2: - version "10.2.4" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.4.tgz#0ed5d1cf06bdac0d3fbb003931bb6df3771fbf42" - integrity sha512-doTMGKXQAT34c3S3gwDrTnXmCZp/z1/92D8suPqqh755sKPT18ew1NoPNHxJdrvv1D4WrJ7CEnx79Ns3EdEFbg== + version "10.2.6" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.6.tgz#7d9658bd89dee946a859cbfc6e09566a9fb50b53" + integrity sha512-2oEBWyPZHkdyjKcIv2U6ay80Q52ZMlZZrUnfsV0WTVcgzPlt3o2t5UFy2v8ETUTsIDZ0xSJVnffWCgD3LF6xTQ== dependencies: chalk "^4.0.0" + cli-truncate "2.1.0" commander "^5.1.0" cosmiconfig "^6.0.0" debug "^4.1.1" @@ -7986,9 +8010,9 @@ lint-staged@^10.2.2: stringify-object "^3.3.0" listr2@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.0.2.tgz#35e11e742ee151a8c446d1649792cadf7eb1d780" - integrity sha512-HkbraLsbHRFtuT0p1g9KUiMoJeqlPdgsi4Q3mCvBlYnVK+2I1vPdCxBvJ+nAxwpL7SZiyaICWMvLOyMBtu+VKw== + version "2.0.4" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.0.4.tgz#b39100b0a227ec5659dcf76ddc516211fc168d61" + integrity sha512-oJaAcplPsa72rKW0eg4P4LbEJjhH+UO2I8uqR/I2wzHrVg16ohSfUy0SlcHS21zfYXxtsUpL8YXGHjyfWMR0cg== dependencies: "@samverschueren/stream-to-observable" "^0.3.0" chalk "^4.0.0" @@ -8546,9 +8570,9 @@ mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@~0.5.1: minimist "^1.2.5" moment@^2.10.6: - version "2.25.3" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.25.3.tgz#252ff41319cf41e47761a1a88cab30edfe9808c0" - integrity sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg== + version "2.26.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a" + integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw== moo@^0.5.0: version "0.5.1" @@ -8792,9 +8816,9 @@ node-notifier@^7.0.0: which "^2.0.2" node-releases@^1.1.53: - version "1.1.55" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.55.tgz#8af23b7c561d8e2e6e36a46637bab84633b07cee" - integrity sha512-H3R3YR/8TjT5WPin/wOoHOUPHgvj8leuU/Keta/rwelEQN9pA/S2Dx8/se4pZ2LBxSd0nAGzsNzhqwa77v7F1w== + version "1.1.56" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.56.tgz#bc054a417d316e3adac90eafb7e1932802f28705" + integrity sha512-EVo605FhWLygH8a64TjgpjyHYOihkxECwX1bHHr8tETJKWEiWS2YJjPbvsX2jFjnjTNEgBCmk9mLjKG1Mf11cw== normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" @@ -8922,13 +8946,12 @@ object.assign@^4.1.0: object-keys "^1.0.11" object.entries@^1.1.0, object.entries@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" - integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add" + integrity sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA== dependencies: define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" + es-abstract "^1.17.5" has "^1.0.3" object.fromentries@^2.0.2: @@ -9645,9 +9668,9 @@ pretty-format@^26.0.1: react-is "^16.12.0" prism-react-renderer@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.1.0.tgz#6fe1b33f1de1b23afbdb07663d135f9026eef4ad" - integrity sha512-WZAw+mBoxk1qZDD1h1WOg0BVHgyk9zqbuIBFNgP+Z71i515jGL0WZIN1FIF8EgOyh06x8Rr7HAUXxsRsoUZKyg== + version "1.1.1" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.1.1.tgz#1c1be61b1eb9446a146ca7a50b7bcf36f2a70a44" + integrity sha512-MgMhSdHuHymNRqD6KM3eGS0PNqgK9q4QF5P0yoQQvpB6jNjeSAi3jcSAz0Sua/t9fa4xDOMar9HJbLa08gl9ug== private@^0.1.8: version "0.1.8" @@ -11993,9 +12016,9 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@^3.9.2: - version "3.9.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.2.tgz#64e9c8e9be6ea583c54607677dd4680a1cf35db9" - integrity sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw== + version "3.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a" + integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ== unbzip2-stream@^1.3.3: version "1.4.2"