DevTools error boundary: Search for pre-existing GH issues (#21279)

This commit is contained in:
Brian Vaughn 2021-04-15 13:34:54 -04:00 committed by GitHub
parent 9eddfbf5af
commit f3337aa544
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 764 additions and 209 deletions

View File

@ -8,6 +8,7 @@
},
"dependencies": {
"@babel/runtime": "^7.11.2",
"@octokit/rest": "^18.5.2",
"@reach/menu-button": "^0.1.17",
"@reach/tooltip": "^0.2.2",
"clipboard-js": "^0.3.6",

View File

@ -14,3 +14,4 @@
************************************************************************/
export const enableProfilerChangedHookIndices = false;
export const isInternalFacebookBuild = false;

View File

@ -14,6 +14,7 @@
************************************************************************/
export const enableProfilerChangedHookIndices = true;
export const isInternalFacebookBuild = true;
/************************************************************************
* Do not edit the code below.

View File

@ -14,6 +14,7 @@
************************************************************************/
export const enableProfilerChangedHookIndices = false;
export const isInternalFacebookBuild = false;
/************************************************************************
* Do not edit the code below.

View File

@ -49,6 +49,9 @@ export const CHANGE_LOG_URL =
export const UNSUPPORTED_VERSION_URL =
'https://reactjs.org/blog/2019/08/15/new-react-devtools.html#how-do-i-get-the-old-version-back';
export const REACT_DEVTOOLS_WORKPLACE_URL =
'https://fburl.com/react-devtools-workplace-group';
// HACK
//
// Extracting during build time avoids a temporarily invalid state for the inline target.

View File

@ -1,54 +0,0 @@
.ErrorBoundary {
height: 100%;
width: 100%;
background-color: var(--color-background);
padding: 0.5rem;
overflow: auto;
}
.Header {
font-size: var(--font-size-sans-large);
font-weight: bold;
color: var(--color-error-text);
}
.Stack {
margin-top: 0.5rem;
white-space: pre-wrap;
font-family: var(--font-family-monospace);
font-size: var(--font-size-monospace-normal);
-webkit-font-smoothing: initial;
background-color: var(--color-error-background);
border: 1px solid var(--color-error-border);
color: var(--color-error-text);
border-radius: 0.25rem;
padding: 0.5rem;
}
.IconAndLinkRow {
display: flex;
align-items: center;
margin-top: 0.5rem;
color: var(--color-text);
}
.RetryIcon {
margin-right: 0.25rem;
color: var(--color-button-active);
}
.RetryButton {
margin-right: 0.25rem;
color: var(--color-text);
}
.RetryButton:hover {
color: var(--color-button-hover);
}
.ReportIcon {
margin-right: 0.25rem;
}
.ReportLink {
color: var(--color-link);
}

View File

@ -1,154 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import {Component} from 'react';
import Button from './Button';
import ButtonIcon from './ButtonIcon';
import Icon from './Icon';
import styles from './ErrorBoundary.css';
import Store from 'react-devtools-shared/src/devtools/store';
type Props = {|
children: React$Node,
onRetry?: (store: Store) => void,
store: Store,
|};
type State = {|
callStack: string | null,
componentStack: string | null,
errorMessage: string | null,
hasError: boolean,
|};
const InitialState: State = {
callStack: null,
componentStack: null,
errorMessage: null,
hasError: false,
};
export default class ErrorBoundary extends Component<Props, State> {
state: State = InitialState;
static getDerivedStateFromError(error: any) {
const errorMessage =
typeof error === 'object' &&
error !== null &&
error.hasOwnProperty('message')
? error.message
: error;
return {
errorMessage,
hasError: true,
};
}
componentDidCatch(error: any, {componentStack}: any) {
const callStack =
typeof error === 'object' &&
error !== null &&
error.hasOwnProperty('stack')
? error.stack
.split('\n')
.slice(1)
.join('\n')
: null;
this.setState({
callStack,
componentStack,
});
}
render() {
const {children} = this.props;
const {callStack, componentStack, errorMessage, hasError} = this.state;
let bugURL = process.env.GITHUB_URL;
if (bugURL) {
const title = `Error: "${errorMessage || ''}"`;
const label = 'Component: Developer Tools';
let body = 'Describe what you were doing when the bug occurred:';
body += '\n1. ';
body += '\n2. ';
body += '\n3. ';
body += '\n\n---------------------------------------------';
body += '\nPlease do not remove the text below this line';
body += '\n---------------------------------------------';
body += `\n\nDevTools version: ${process.env.DEVTOOLS_VERSION || ''}`;
if (callStack) {
body += `\n\nCall stack: ${callStack.trim()}`;
}
if (componentStack) {
body += `\n\nComponent stack: ${componentStack.trim()}`;
}
bugURL += `/issues/new?labels=${encodeURI(label)}&title=${encodeURI(
title,
)}&body=${encodeURI(body)}`;
}
if (hasError) {
return (
<div className={styles.ErrorBoundary}>
<div className={styles.Header}>
Uncaught Error: {errorMessage || ''}
</div>
<div className={styles.IconAndLinkRow}>
<Button
className={styles.RetryButton}
title="Retry"
onClick={this.handleRetry}>
<ButtonIcon className={styles.RetryIcon} type="reload" />
Retry
</Button>
{bugURL && (
<>
<Icon className={styles.ReportIcon} type="bug" />
<a
className={styles.ReportLink}
href={bugURL}
rel="noopener noreferrer"
target="_blank"
title="Report bug">
Report this issue
</a>
</>
)}
</div>
{!!callStack && (
<div className={styles.Stack}>
The error was thrown {callStack.trim()}
</div>
)}
{!!componentStack && (
<div className={styles.Stack}>
The error occurred {componentStack.trim()}
</div>
)}
</div>
);
}
return children;
}
handleRetry = () => {
const {onRetry, store} = this.props;
if (typeof onRetry === 'function') {
onRetry(store);
}
this.setState(InitialState);
};
}

View File

@ -0,0 +1,91 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import {Component, Suspense} from 'react';
import ErrorView from './ErrorView';
import SearchingGitHubIssues from './SearchingGitHubIssues';
import SuspendingErrorView from './SuspendingErrorView';
type Props = {|
children: React$Node,
|};
type State = {|
callStack: string | null,
componentStack: string | null,
errorMessage: string | null,
hasError: boolean,
|};
const InitialState: State = {
callStack: null,
componentStack: null,
errorMessage: null,
hasError: false,
};
export default class ErrorBoundary extends Component<Props, State> {
state: State = InitialState;
static getDerivedStateFromError(error: any) {
const errorMessage =
typeof error === 'object' &&
error !== null &&
error.hasOwnProperty('message')
? error.message
: '' + error;
return {
errorMessage,
hasError: true,
};
}
componentDidCatch(error: any, {componentStack}: any) {
const callStack =
typeof error === 'object' &&
error !== null &&
error.hasOwnProperty('stack')
? error.stack
.split('\n')
.slice(1)
.join('\n')
: null;
this.setState({
callStack,
componentStack,
});
}
render() {
const {children} = this.props;
const {callStack, componentStack, errorMessage, hasError} = this.state;
if (hasError) {
return (
<ErrorView
callStack={callStack}
componentStack={componentStack}
errorMessage={errorMessage}>
<Suspense fallback={<SearchingGitHubIssues />}>
<SuspendingErrorView
callStack={callStack}
componentStack={componentStack}
errorMessage={errorMessage}
/>
</Suspense>
</ErrorView>
);
}
return children;
}
}

View File

@ -0,0 +1,46 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import styles from './shared.css';
type Props = {|
callStack: string | null,
children: React$Node,
componentStack: string | null,
errorMessage: string | null,
|};
export default function ErrorView({
callStack,
children,
componentStack,
errorMessage,
}: Props) {
return (
<div className={styles.ErrorBoundary}>
{children}
<div className={styles.ErrorInfo}>
<div className={styles.Header}>
Uncaught Error: {errorMessage || ''}
</div>
{!!callStack && (
<div className={styles.Stack}>
The error was thrown {callStack.trim()}
</div>
)}
{!!componentStack && (
<div className={styles.Stack}>
The error occurred {componentStack.trim()}
</div>
)}
</div>
</div>
);
}

View File

@ -0,0 +1,68 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import Icon from '../Icon';
import styles from './shared.css';
type Props = {|
callStack: string | null,
componentStack: string | null,
errorMessage: string | null,
|};
export default function ReportNewIssue({
callStack,
componentStack,
errorMessage,
}: Props) {
let bugURL = process.env.GITHUB_URL;
if (!bugURL) {
return null;
}
const title = `Error: "${errorMessage || ''}"`;
const label = 'Component: Developer Tools';
let body = 'Describe what you were doing when the bug occurred:';
body += '\n1. ';
body += '\n2. ';
body += '\n3. ';
body += '\n\n---------------------------------------------';
body += '\nPlease do not remove the text below this line';
body += '\n---------------------------------------------';
body += `\n\nDevTools version: ${process.env.DEVTOOLS_VERSION || ''}`;
if (callStack) {
body += `\n\nCall stack: ${callStack.trim()}`;
}
if (componentStack) {
body += `\n\nComponent stack: ${componentStack.trim()}`;
}
bugURL += `/issues/new?labels=${encodeURI(label)}&title=${encodeURI(
title,
)}&body=${encodeURI(body)}`;
return (
<div className={styles.GitHubLinkRow}>
<Icon className={styles.ReportIcon} type="bug" />
<a
className={styles.ReportLink}
href={bugURL}
rel="noopener noreferrer"
target="_blank"
title="Report bug">
Report this issue
</a>
<div className={styles.ReproSteps}>
(Please include steps on how to reproduce it and the components used.)
</div>
</div>
);
}

View File

@ -0,0 +1,21 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import LoadingAnimation from 'react-devtools-shared/src/devtools/views/Components/LoadingAnimation';
import styles from './shared.css';
export default function SearchingGitHubIssues() {
return (
<div className={styles.GitHubLinkRow}>
<LoadingAnimation className={styles.LoadingIcon} />
Searching GitHub for reports of this error...
</div>
);
}

View File

@ -0,0 +1,49 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import {findGitHubIssue} from './cache';
import UpdateExistingIssue from './UpdateExistingIssue';
import ReportNewIssue from './ReportNewIssue';
import WorkplaceGroup from './WorkplaceGroup';
type Props = {|
callStack: string | null,
componentStack: string | null,
errorMessage: string | null,
|};
export default function SuspendingErrorView({
callStack,
componentStack,
errorMessage,
}: Props) {
const maybeItem =
errorMessage !== null ? findGitHubIssue(errorMessage) : null;
let GitHubUI;
if (maybeItem != null) {
GitHubUI = <UpdateExistingIssue gitHubIssue={maybeItem} />;
} else {
GitHubUI = (
<ReportNewIssue
callStack={callStack}
componentStack={componentStack}
errorMessage={errorMessage}
/>
);
}
return (
<>
{GitHubUI}
<WorkplaceGroup />
</>
);
}

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {GitHubIssue} from './githubAPI';
import * as React from 'react';
import Icon from '../Icon';
import styles from './shared.css';
export default function UpdateExistingIssue({
gitHubIssue,
}: {|
gitHubIssue: GitHubIssue,
|}) {
const {title, url} = gitHubIssue;
return (
<div className={styles.GitHubLinkRow}>
<Icon className={styles.ReportIcon} type="bug" />
<div className={styles.UpdateExistingIssuePrompt}>
Update existing issue:
</div>
<a
className={styles.ReportLink}
href={url}
rel="noopener noreferrer"
target="_blank"
title="Report bug">
{title}
</a>
</div>
);
}

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import {isInternalFacebookBuild} from 'react-devtools-feature-flags';
import {REACT_DEVTOOLS_WORKPLACE_URL} from 'react-devtools-shared/src/constants';
import Icon from '../Icon';
import styles from './shared.css';
export default function WorkplaceGroup() {
if (!isInternalFacebookBuild) {
return null;
}
return (
<div className={styles.WorkplaceGroupRow}>
<Icon className={styles.ReportIcon} type="facebook" />
<a
className={styles.ReportLink}
href={REACT_DEVTOOLS_WORKPLACE_URL}
rel="noopener noreferrer"
target="_blank"
title="Report bug">
Report this on Workplace
</a>
<div className={styles.FacebookOnly}>(Facebook employees only.)</div>
</div>
);
}

View File

@ -0,0 +1,134 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {Wakeable} from 'shared/ReactTypes';
import type {GitHubIssue} from './githubAPI';
import {unstable_getCacheForType} from 'react';
import {searchGitHubIssues} from './githubAPI';
const API_TIMEOUT = 3000;
const Pending = 0;
const Resolved = 1;
const Rejected = 2;
type PendingRecord = {|
status: 0,
value: Wakeable,
|};
type ResolvedRecord<T> = {|
status: 1,
value: T,
|};
type RejectedRecord = {|
status: 2,
value: null,
|};
type Record<T> = PendingRecord | ResolvedRecord<T> | RejectedRecord;
function readRecord<T>(record: Record<T>): ResolvedRecord<T> | RejectedRecord {
if (record.status === Resolved) {
// This is just a type refinement.
return record;
} else if (record.status === Rejected) {
// This is just a type refinement.
return record;
} else {
throw record.value;
}
}
type GitHubIssueMap = Map<string, Record<GitHubIssue>>;
function createMap(): GitHubIssueMap {
return new Map();
}
function getRecordMap(): Map<string, Record<GitHubIssue>> {
return unstable_getCacheForType(createMap);
}
export function findGitHubIssue(errorMessage: string): GitHubIssue | null {
errorMessage = normalizeErrorMessage(errorMessage);
const map = getRecordMap();
let record = map.get(errorMessage);
if (!record) {
const callbacks = new Set();
const wakeable: Wakeable = {
then(callback) {
callbacks.add(callback);
},
};
const wake = () => {
// This assumes they won't throw.
callbacks.forEach(callback => callback());
callbacks.clear();
};
const newRecord: Record<GitHubIssue> = (record = {
status: Pending,
value: wakeable,
});
let didTimeout = false;
searchGitHubIssues(errorMessage)
.then(maybeItem => {
if (didTimeout) {
return;
}
if (maybeItem) {
const resolvedRecord = ((newRecord: any): ResolvedRecord<GitHubIssue>);
resolvedRecord.status = Resolved;
resolvedRecord.value = maybeItem;
} else {
const notFoundRecord = ((newRecord: any): RejectedRecord);
notFoundRecord.status = Rejected;
notFoundRecord.value = null;
}
wake();
})
.catch(error => {
const thrownRecord = ((newRecord: any): RejectedRecord);
thrownRecord.status = Rejected;
thrownRecord.value = null;
wake();
});
// Only wait a little while for GitHub results before showing a fallback.
setTimeout(() => {
didTimeout = true;
const timedoutRecord = ((newRecord: any): RejectedRecord);
timedoutRecord.status = Rejected;
timedoutRecord.value = null;
wake();
}, API_TIMEOUT);
map.set(errorMessage, record);
}
const response = readRecord(record).value;
return response;
}
function normalizeErrorMessage(errorMessage: string): string {
// Remove Fiber IDs from error message (as those will be unique).
errorMessage = errorMessage.replace(/"[0-9]+"/, '');
return errorMessage;
}

View File

@ -0,0 +1,50 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import {Octokit} from '@octokit/rest';
export type GitHubIssue = {|
title: string,
url: string,
|};
export async function searchGitHubIssues(
message: string,
): Promise<GitHubIssue | null> {
// Remove Fiber IDs from error message (as those will be unique).
message = message.replace(/"[0-9]+"/, '');
const filters = [
// Unfortunately "repo" and "org" filters don't work
// Hopefully the label filter will be sufficient.
'in:title',
'is:issue',
'is:open',
'is:public',
'label:"Component: Developer Tools"',
];
const octokit = new Octokit();
const {data} = await octokit.search.issuesAndPullRequests({
q: message + ' ' + filters.join(' '),
});
const maybeItem = data.items.find(item =>
item.html_url.startsWith('https://github.com/facebook/react/'),
);
if (maybeItem) {
return {
title: maybeItem.title,
url: maybeItem.html_url,
};
} else {
return null;
}
}

View File

@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import ErrorBoundary from './ErrorBoundary';
export default ErrorBoundary;

View File

@ -0,0 +1,83 @@
.GitHubLinkRow {
flex: 0 0 auto;
display: flex;
align-items: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: auto;
padding: 0.25rem 0.5rem;
background: var(--color-console-warning-background);
color: var(--color-text);
border-bottom: 1px solid var(--color-console-warning-border);
border-top: 1px solid var(--color-console-warning-border);
}
.WorkplaceGroupRow {
flex: 0 0 auto;
display: flex;
align-items: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: auto;
padding: 0.25rem 0.5rem;
background: var(--color-background-hover);
border-bottom: 1px solid var(--color-border);
}
.ErrorBoundary {
height: 100%;
width: 100%;
background-color: var(--color-background);
display: flex;
flex-direction: column;
}
.ErrorInfo {
padding: 0.5rem;
overflow: auto;
}
.Header {
font-size: var(--font-size-sans-large);
font-weight: bold;
color: var(--color-error-text);
}
.Stack {
margin-top: 0.5rem;
white-space: pre-wrap;
font-family: var(--font-family-monospace);
font-size: var(--font-size-monospace-normal);
-webkit-font-smoothing: initial;
background-color: var(--color-error-background);
border: 1px solid var(--color-error-border);
color: var(--color-error-text);
border-radius: 0.25rem;
padding: 0.5rem;
}
.LoadingIcon {
margin-right: 0.25rem;
}
.ReportIcon {
margin-right: 0.25rem;
}
.ReportLink {
color: var(--color-link);
}
.FacebookOnly {
margin-left: 0.25rem;
}
.ReproSteps {
margin-left: 0.25rem;
color: var(--color-console-warning-text);
}
.UpdateExistingIssuePrompt {
margin-right: 0.25rem;
color: var(--color-console-warning-text);
}

View File

@ -17,6 +17,7 @@ export type IconType =
| 'components'
| 'copy'
| 'error'
| 'facebook'
| 'flame-chart'
| 'interactions'
| 'profiler'
@ -52,6 +53,9 @@ export default function Icon({className = '', type}: Props) {
case 'error':
pathData = PATH_ERROR;
break;
case 'facebook':
pathData = PATH_FACEBOOK;
break;
case 'flame-chart':
pathData = PATH_FLAME_CHART;
break;
@ -117,6 +121,10 @@ const PATH_COPY = `
const PATH_ERROR = `M16.971 0h-9.942l-7.029 7.029v9.941l7.029 7.03h9.941l7.03-7.029v-9.942l-7.029-7.029zm-1.402 16.945l-3.554-3.521-3.518 3.568-1.418-1.418 3.507-3.566-3.586-3.472 1.418-1.417 3.581 3.458 3.539-3.583 1.431 1.431-3.535 3.568 3.566 3.522-1.431 1.43z`;
const PATH_FACEBOOK = `
M22,12c0-5.52-4.48-10-10-10S2,6.48,2,12c0,4.84,3.44,8.87,8,9.8V15H8v-3h2V9.5C10,7.57,11.57,6,13.5,6H16v3h-2 c-0.55,0-1,0.45-1,1v2h3v3h-3v6.95C18.05,21.45,22,17.19,22,12z
`;
const PATH_FLAME_CHART = `
M10.0650893,21.5040462 C7.14020814,20.6850349 5,18.0558698 5,14.9390244 C5,14.017627
5,9.81707317 7.83333333,7.37804878 C7.83333333,7.37804878 7.58333333,11.199187 10,

123
yarn.lock
View File

@ -1814,6 +1814,26 @@
read-package-json-fast "^1.1.1"
readdir-scoped-modules "^1.1.0"
"@octokit/auth-token@^2.4.4":
version "2.4.5"
resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3"
integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==
dependencies:
"@octokit/types" "^6.0.3"
"@octokit/core@^3.2.3":
version "3.4.0"
resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.4.0.tgz#b48aa27d755b339fe7550548b340dcc2b513b742"
integrity sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg==
dependencies:
"@octokit/auth-token" "^2.4.4"
"@octokit/graphql" "^4.5.8"
"@octokit/request" "^5.4.12"
"@octokit/request-error" "^2.0.5"
"@octokit/types" "^6.0.3"
before-after-hook "^2.2.0"
universal-user-agent "^6.0.0"
"@octokit/endpoint@^5.1.0":
version "5.3.2"
resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.3.2.tgz#2deda2d869cac9ba7f370287d55667be2a808d4b"
@ -1824,6 +1844,49 @@
universal-user-agent "^3.0.0"
url-template "^2.0.8"
"@octokit/endpoint@^6.0.1":
version "6.0.11"
resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.11.tgz#082adc2aebca6dcefa1fb383f5efb3ed081949d1"
integrity sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==
dependencies:
"@octokit/types" "^6.0.3"
is-plain-object "^5.0.0"
universal-user-agent "^6.0.0"
"@octokit/graphql@^4.5.8":
version "4.6.1"
resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.1.tgz#f975486a46c94b7dbe58a0ca751935edc7e32cc9"
integrity sha512-2lYlvf4YTDgZCTXTW4+OX+9WTLFtEUc6hGm4qM1nlZjzxj+arizM4aHWzBVBCxY9glh7GIs0WEuiSgbVzv8cmA==
dependencies:
"@octokit/request" "^5.3.0"
"@octokit/types" "^6.0.3"
universal-user-agent "^6.0.0"
"@octokit/openapi-types@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-6.0.0.tgz#7da8d7d5a72d3282c1a3ff9f951c8133a707480d"
integrity sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ==
"@octokit/plugin-paginate-rest@^2.6.2":
version "2.13.3"
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz#f0f1792230805108762d87906fb02d573b9e070a"
integrity sha512-46lptzM9lTeSmIBt/sVP/FLSTPGx6DCzAdSX3PfeJ3mTf4h9sGC26WpaQzMEq/Z44cOcmx8VsOhO+uEgE3cjYg==
dependencies:
"@octokit/types" "^6.11.0"
"@octokit/plugin-request-log@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d"
integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ==
"@octokit/plugin-rest-endpoint-methods@5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz#cf2cdeb24ea829c31688216a5b165010b61f9a98"
integrity sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg==
dependencies:
"@octokit/types" "^6.13.0"
deprecation "^2.3.1"
"@octokit/request-error@^1.0.1", "@octokit/request-error@^1.0.2":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.0.4.tgz#15e1dc22123ba4a9a4391914d80ec1e5303a23be"
@ -1832,6 +1895,15 @@
deprecation "^2.0.0"
once "^1.4.0"
"@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143"
integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg==
dependencies:
"@octokit/types" "^6.0.3"
deprecation "^2.0.0"
once "^1.4.0"
"@octokit/request@^5.0.0":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.0.2.tgz#59a920451f24811c016ddc507adcc41aafb2dca5"
@ -1845,6 +1917,18 @@
once "^1.4.0"
universal-user-agent "^3.0.0"
"@octokit/request@^5.3.0", "@octokit/request@^5.4.12":
version "5.4.15"
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.15.tgz#829da413dc7dd3aa5e2cdbb1c7d0ebe1f146a128"
integrity sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag==
dependencies:
"@octokit/endpoint" "^6.0.1"
"@octokit/request-error" "^2.0.0"
"@octokit/types" "^6.7.1"
is-plain-object "^5.0.0"
node-fetch "^2.6.1"
universal-user-agent "^6.0.0"
"@octokit/rest@^16.14.1":
version "16.28.7"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.28.7.tgz#a2c2db5b318da84144beba82d19c1a9dbdb1a1fa"
@ -1864,6 +1948,23 @@
universal-user-agent "^3.0.0"
url-template "^2.0.8"
"@octokit/rest@^18.5.2":
version "18.5.2"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.2.tgz#0369e554b7076e3749005147be94c661c7a5a74b"
integrity sha512-Kz03XYfKS0yYdi61BkL9/aJ0pP2A/WK5vF/syhu9/kY30J8He3P68hv9GRpn8bULFx2K0A9MEErn4v3QEdbZcw==
dependencies:
"@octokit/core" "^3.2.3"
"@octokit/plugin-paginate-rest" "^2.6.2"
"@octokit/plugin-request-log" "^1.0.2"
"@octokit/plugin-rest-endpoint-methods" "5.0.0"
"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.13.0", "@octokit/types@^6.7.1":
version "6.13.0"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.13.0.tgz#779e5b7566c8dde68f2f6273861dd2f0409480d0"
integrity sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA==
dependencies:
"@octokit/openapi-types" "^6.0.0"
"@pmmmwh/react-refresh-webpack-plugin@^0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.4.1.tgz#a4db0ed8e58c2f8566161c9a8cdf1d095c9a891b"
@ -3478,6 +3579,11 @@ before-after-hook@^2.0.0:
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635"
integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==
before-after-hook@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.1.tgz#73540563558687586b52ed217dad6a802ab1549c"
integrity sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw==
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
@ -5140,7 +5246,7 @@ depd@^1.1.2, depd@~1.1.2:
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
deprecation@^2.0.0:
deprecation@^2.0.0, deprecation@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==
@ -8128,6 +8234,11 @@ is-plain-object@^3.0.0:
dependencies:
isobject "^4.0.0"
is-plain-object@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
is-posix-bracket@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
@ -10042,6 +10153,11 @@ node-fetch@^2.3.0, node-fetch@^2.6.0:
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
node-fetch@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-forge@0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
@ -13801,6 +13917,11 @@ universal-user-agent@^3.0.0:
dependencies:
os-name "^3.0.0"
universal-user-agent@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee"
integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"