Remove SWR
This commit is contained in:
@@ -48,7 +48,6 @@
|
|||||||
"react-i18next": "^13.2.2",
|
"react-i18next": "^13.2.2",
|
||||||
"react-router-dom": "^6.16.0",
|
"react-router-dom": "^6.16.0",
|
||||||
"react-virtuoso": "^4.5.1",
|
"react-virtuoso": "^4.5.1",
|
||||||
"swr": "^2.2.2",
|
|
||||||
"use-query-params": "^2.2.1",
|
"use-query-params": "^2.2.1",
|
||||||
"vite": "^4.4.9",
|
"vite": "^4.4.9",
|
||||||
"vite-tsconfig-paths": "^4.2.1"
|
"vite-tsconfig-paths": "^4.2.1"
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
import { StyledEngineProvider, ThemeProvider } from '@mui/material/styles';
|
import { StyledEngineProvider, ThemeProvider } from '@mui/material/styles';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { BrowserRouter as Router } from 'react-router-dom';
|
import { BrowserRouter as Router } from 'react-router-dom';
|
||||||
import { SWRConfig } from 'swr';
|
|
||||||
import { QueryParamProvider } from 'use-query-params';
|
import { QueryParamProvider } from 'use-query-params';
|
||||||
import { ReactRouter6Adapter } from 'use-query-params/adapters/react-router-6';
|
import { ReactRouter6Adapter } from 'use-query-params/adapters/react-router-6';
|
||||||
import createTheme from '@/theme';
|
import createTheme from '@/theme';
|
||||||
@@ -36,21 +35,19 @@ const AppContext: React.FC<Props> = ({ children }) => {
|
|||||||
const theme = useMemo(() => createTheme(darkTheme), [darkTheme]);
|
const theme = useMemo(() => createTheme(darkTheme), [darkTheme]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SWRConfig>
|
<Router>
|
||||||
<Router>
|
<StyledEngineProvider injectFirst>
|
||||||
<StyledEngineProvider injectFirst>
|
<ThemeProvider theme={theme}>
|
||||||
<ThemeProvider theme={theme}>
|
<DarkTheme.Provider value={darkThemeContext}>
|
||||||
<DarkTheme.Provider value={darkThemeContext}>
|
<QueryParamProvider adapter={ReactRouter6Adapter}>
|
||||||
<QueryParamProvider adapter={ReactRouter6Adapter}>
|
<LibraryOptionsContextProvider>
|
||||||
<LibraryOptionsContextProvider>
|
<NavBarContextProvider>{children}</NavBarContextProvider>
|
||||||
<NavBarContextProvider>{children}</NavBarContextProvider>
|
</LibraryOptionsContextProvider>
|
||||||
</LibraryOptionsContextProvider>
|
</QueryParamProvider>
|
||||||
</QueryParamProvider>
|
</DarkTheme.Provider>
|
||||||
</DarkTheme.Provider>
|
</ThemeProvider>
|
||||||
</ThemeProvider>
|
</StyledEngineProvider>
|
||||||
</StyledEngineProvider>
|
</Router>
|
||||||
</Router>
|
|
||||||
</SWRConfig>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,7 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AxiosInstance, AxiosRequestConfig } from 'axios';
|
import { AxiosInstance } from 'axios';
|
||||||
import useSWR, { Middleware, SWRConfiguration, SWRResponse } from 'swr';
|
|
||||||
import useSWRInfinite, { SWRInfiniteConfiguration, SWRInfiniteResponse } from 'swr/infinite';
|
|
||||||
import {
|
import {
|
||||||
ApolloError,
|
ApolloError,
|
||||||
ApolloQueryResult,
|
ApolloQueryResult,
|
||||||
@@ -26,7 +24,7 @@ import {
|
|||||||
} from '@apollo/client';
|
} from '@apollo/client';
|
||||||
import { OperationVariables } from '@apollo/client/core';
|
import { OperationVariables } from '@apollo/client/core';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { HttpMethod as DefaultHttpMethod, IRestClient, RestClient } from '@/lib/requests/client/RestClient.ts';
|
import { IRestClient, RestClient } from '@/lib/requests/client/RestClient.ts';
|
||||||
import storage from '@/util/localStorage.tsx';
|
import storage from '@/util/localStorage.tsx';
|
||||||
import { GraphQLClient } from '@/lib/requests/client/GraphQLClient.ts';
|
import { GraphQLClient } from '@/lib/requests/client/GraphQLClient.ts';
|
||||||
import {
|
import {
|
||||||
@@ -191,13 +189,6 @@ import { CustomCache } from '@/lib/requests/CustomCache.ts';
|
|||||||
import { RESTORE_BACKUP } from '@/lib/graphql/mutations/BackupMutation.ts';
|
import { RESTORE_BACKUP } from '@/lib/graphql/mutations/BackupMutation.ts';
|
||||||
import { VALIDATE_BACKUP } from '@/lib/graphql/queries/BackupQuery.ts';
|
import { VALIDATE_BACKUP } from '@/lib/graphql/queries/BackupQuery.ts';
|
||||||
|
|
||||||
enum SWRHttpMethod {
|
|
||||||
SWR_GET,
|
|
||||||
SWR_GET_INFINITE,
|
|
||||||
SWR_POST,
|
|
||||||
SWR_POST_INFINITE,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum GQLMethod {
|
enum GQLMethod {
|
||||||
QUERY = 'QUERY',
|
QUERY = 'QUERY',
|
||||||
USE_QUERY = 'USE_QUERY',
|
USE_QUERY = 'USE_QUERY',
|
||||||
@@ -205,23 +196,6 @@ enum GQLMethod {
|
|||||||
MUTATION = 'MUTATION',
|
MUTATION = 'MUTATION',
|
||||||
}
|
}
|
||||||
|
|
||||||
type HttpMethodType = DefaultHttpMethod | SWRHttpMethod;
|
|
||||||
const HttpMethod = { ...SWRHttpMethod, ...DefaultHttpMethod };
|
|
||||||
|
|
||||||
type CustomSWROptions<Data> = {
|
|
||||||
skipRequest?: boolean;
|
|
||||||
getEndpoint?: (index: number, previousData: Data | null) => string | null;
|
|
||||||
disableCache?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type SWROptions<Data = any, Error = any> = SWRConfiguration<Data, Error> & CustomSWROptions<Data>;
|
|
||||||
type SWRInfiniteOptions<Data = any, Error = any> = SWRInfiniteConfiguration<Data, Error> & CustomSWROptions<Data>;
|
|
||||||
|
|
||||||
type SWRInfiniteResponseLoadInfo = {
|
|
||||||
isInitialLoad: boolean;
|
|
||||||
isLoadMore: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CustomApolloOptions = {
|
type CustomApolloOptions = {
|
||||||
/**
|
/**
|
||||||
* This is a workaround for an apollo bug (?).
|
* This is a workaround for an apollo bug (?).
|
||||||
@@ -259,11 +233,6 @@ type ApolloPaginatedMutationOptions<
|
|||||||
> = MutationHookOptions<Data, Variables> & { skipRequest?: boolean };
|
> = MutationHookOptions<Data, Variables> & { skipRequest?: boolean };
|
||||||
|
|
||||||
type AbortableRequest = { abortRequest: AbortController['abort'] };
|
type AbortableRequest = { abortRequest: AbortController['abort'] };
|
||||||
export type AbortableAxiosResponse<Data = any> = { response: Promise<Data> } & AbortableRequest;
|
|
||||||
export type AbortableSWRResponse<Data = any, Error = any> = SWRResponse<Data, Error> & AbortableRequest;
|
|
||||||
export type AbortableSWRInfiniteResponse<Data = any, Error = any> = SWRInfiniteResponse<Data, Error> &
|
|
||||||
AbortableRequest &
|
|
||||||
SWRInfiniteResponseLoadInfo;
|
|
||||||
|
|
||||||
export type AbortabaleApolloQueryResponse<Data = any> = {
|
export type AbortabaleApolloQueryResponse<Data = any> = {
|
||||||
response: Promise<ApolloQueryResult<Data>>;
|
response: Promise<ApolloQueryResult<Data>>;
|
||||||
@@ -301,36 +270,6 @@ export type AbortableApolloUseMutationPaginatedResponse<
|
|||||||
];
|
];
|
||||||
export type AbortableApolloMutationResponse<Data = any> = { response: Promise<FetchResult<Data>> } & AbortableRequest;
|
export type AbortableApolloMutationResponse<Data = any> = { response: Promise<FetchResult<Data>> } & AbortableRequest;
|
||||||
|
|
||||||
const isLoadingMore = (swrResult: SWRInfiniteResponse): boolean => {
|
|
||||||
const isNextPageMissing = !!swrResult.data && typeof swrResult.data[swrResult.size - 1] === 'undefined';
|
|
||||||
const isRequestActive = swrResult.isValidating;
|
|
||||||
// SWR "isLoading" state is only updated for the first load, for every subsequent load it's "false"
|
|
||||||
return !swrResult.isLoading && swrResult.size > 0 && isNextPageMissing && isRequestActive;
|
|
||||||
};
|
|
||||||
|
|
||||||
const disableSwrInfiniteCache: Middleware = (useSWRNext) => (key, fetcher, config) => {
|
|
||||||
const swr = useSWRNext(key, fetcher, config) as unknown as SWRInfiniteResponse;
|
|
||||||
const { size, data, isLoading, isValidating } = swr;
|
|
||||||
const isActuallyValidating = !isLoading && !isLoadingMore(swr) && isValidating;
|
|
||||||
return {
|
|
||||||
...swr,
|
|
||||||
isLoading: isActuallyValidating ? true : isLoading,
|
|
||||||
data: isActuallyValidating ? undefined : data,
|
|
||||||
size: isActuallyValidating ? 1 : size,
|
|
||||||
} as SWRResponse;
|
|
||||||
};
|
|
||||||
|
|
||||||
const disableSwrCache: Middleware = (useSWRNext) => (key, fetcher, config) => {
|
|
||||||
const swr = useSWRNext(key, fetcher, config);
|
|
||||||
const { data, isLoading, isValidating } = swr;
|
|
||||||
return { ...swr, isLoading: isValidating ? true : isLoading, data: isValidating ? undefined : data };
|
|
||||||
};
|
|
||||||
|
|
||||||
// the following endpoints have not been implemented:
|
|
||||||
// - PUT /api/v1/manga/{mangaId}/chapter/{chapterIndex} - modify chapter # PATCH endpoint used instead
|
|
||||||
// - POST /api/v1/backup/import - import backup # "import backup file" endpoint used instead
|
|
||||||
// - POST /api/v1/backup/validate - validate backup # "validate backup file" endpoint used instead
|
|
||||||
// - GET /api/v1/backup/export - export backup # no function needed, url gets called via link triggering the download
|
|
||||||
export class RequestManager {
|
export class RequestManager {
|
||||||
public static readonly API_VERSION = '/api/v1/';
|
public static readonly API_VERSION = '/api/v1/';
|
||||||
|
|
||||||
@@ -826,165 +765,6 @@ export class RequestManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private useSwr<
|
|
||||||
Data = any,
|
|
||||||
ErrorResponse = any,
|
|
||||||
OptionsSWR extends SWROptions<Data, ErrorResponse> = SWROptions<Data, ErrorResponse>,
|
|
||||||
>(
|
|
||||||
url: string,
|
|
||||||
httpMethod: DefaultHttpMethod,
|
|
||||||
{
|
|
||||||
data,
|
|
||||||
axiosOptions,
|
|
||||||
swrOptions,
|
|
||||||
}: {
|
|
||||||
data?: Data;
|
|
||||||
axiosOptions?: AxiosRequestConfig;
|
|
||||||
swrOptions?: OptionsSWR;
|
|
||||||
} = {},
|
|
||||||
): SWRResponse<Data, ErrorResponse> {
|
|
||||||
const { skipRequest, disableCache, ...swrConfig } = swrOptions ?? {};
|
|
||||||
|
|
||||||
// in case "null" gets passed as the url, SWR won't do the request
|
|
||||||
return useSWR(skipRequest ? null : url, {
|
|
||||||
fetcher: (path: string) => this.restClient.fetcher(path, { data, httpMethod, config: axiosOptions }),
|
|
||||||
use: disableCache ? [disableSwrCache] : undefined,
|
|
||||||
...swrConfig,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public useSwrInfinite<
|
|
||||||
Data = any,
|
|
||||||
ErrorResponse = any,
|
|
||||||
OptionsSWR extends SWRInfiniteOptions<Data, ErrorResponse> = SWRInfiniteOptions<Data, ErrorResponse>,
|
|
||||||
>(
|
|
||||||
getEndpoint: Required<CustomSWROptions<Data>>['getEndpoint'],
|
|
||||||
httpMethod: DefaultHttpMethod,
|
|
||||||
{
|
|
||||||
data,
|
|
||||||
axiosOptions,
|
|
||||||
swrOptions,
|
|
||||||
}: { data?: any; axiosOptions?: AxiosRequestConfig; swrOptions?: OptionsSWR } = {},
|
|
||||||
): SWRInfiniteResponse<Data, ErrorResponse> & SWRInfiniteResponseLoadInfo {
|
|
||||||
const { skipRequest, disableCache, ...swrConfig } = swrOptions ?? {};
|
|
||||||
|
|
||||||
// useSWRInfinite will (by default) revalidate the first page, to check if the other pages have to be revalidated as well
|
|
||||||
const swrResult = useSWRInfinite<Data, ErrorResponse>(
|
|
||||||
(index, previousData) => {
|
|
||||||
const pageEndpoint = getEndpoint(index, previousData);
|
|
||||||
return pageEndpoint !== null && !skipRequest ? this.getValidUrlFor(pageEndpoint) : null;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fetcher: (path: string) => this.restClient.fetcher(path, { httpMethod, data, config: axiosOptions }),
|
|
||||||
use: disableCache ? [disableSwrInfiniteCache] : undefined,
|
|
||||||
...swrConfig,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const customSwrResult = {
|
|
||||||
...swrResult,
|
|
||||||
isInitialLoad: swrResult.isLoading,
|
|
||||||
isLoadMore: isLoadingMore(swrResult),
|
|
||||||
};
|
|
||||||
customSwrResult.isLoading = customSwrResult.isInitialLoad || customSwrResult.isLoadMore;
|
|
||||||
|
|
||||||
return customSwrResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the actual server request.
|
|
||||||
*
|
|
||||||
* In case {@link HttpMethod.GET_SWR} gets passed, the "useSWR" hook gets called.
|
|
||||||
* In case {@link HttpMethod.GET_SWR_INFINITE} gets passed, the "useSWRInfinite" hook gets called.
|
|
||||||
* In that case "getEndpoint" has to be passed, which gets used over "endpoint"
|
|
||||||
*
|
|
||||||
* Pass "skipRequest" to make SWR skip sending the request to the server.
|
|
||||||
* In case "formData" is passed, "data" gets ignored.
|
|
||||||
*/
|
|
||||||
private doRequest<
|
|
||||||
Result extends AbortableAxiosResponse | AbortableSWRResponse | AbortableSWRInfiniteResponse,
|
|
||||||
OptionsSWR extends SWROptions | SWRInfiniteOptions,
|
|
||||||
>(
|
|
||||||
httpMethod: HttpMethodType,
|
|
||||||
endpoint: string,
|
|
||||||
{
|
|
||||||
apiVersion = RequestManager.API_VERSION,
|
|
||||||
data: dataToSend,
|
|
||||||
formData,
|
|
||||||
axiosOptions,
|
|
||||||
swrOptions,
|
|
||||||
}: {
|
|
||||||
apiVersion?: string;
|
|
||||||
data?: any;
|
|
||||||
formData?: { [key: string]: any };
|
|
||||||
axiosOptions?: AxiosRequestConfig;
|
|
||||||
swrOptions?: OptionsSWR;
|
|
||||||
} = {},
|
|
||||||
): Result {
|
|
||||||
const url = `${apiVersion}${endpoint}`;
|
|
||||||
|
|
||||||
let data = dataToSend;
|
|
||||||
if (formData) {
|
|
||||||
data = new FormData();
|
|
||||||
|
|
||||||
Object.entries(formData).forEach(([key, value]) => {
|
|
||||||
if (value !== undefined) data.append(key, value); // "append" automatically converts non string or blob values to strings
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const abortController = new AbortController();
|
|
||||||
const abortRequest = (reason?: any): void => {
|
|
||||||
if (!abortController.signal.aborted) {
|
|
||||||
abortController.abort(reason);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const axiosOptionsWithAbortController = { ...axiosOptions, signal: abortController.signal };
|
|
||||||
switch (httpMethod) {
|
|
||||||
case HttpMethod.SWR_GET:
|
|
||||||
return {
|
|
||||||
...(this.useSwr(url, HttpMethod.GET, { axiosOptions, swrOptions }) as Result),
|
|
||||||
abortRequest,
|
|
||||||
};
|
|
||||||
case HttpMethod.SWR_GET_INFINITE:
|
|
||||||
// throw TypeError in case options aren't correctly passed
|
|
||||||
return {
|
|
||||||
...(this.useSwrInfinite(swrOptions!.getEndpoint!, HttpMethod.GET, {
|
|
||||||
axiosOptions: axiosOptionsWithAbortController,
|
|
||||||
swrOptions,
|
|
||||||
}) as Result),
|
|
||||||
abortRequest,
|
|
||||||
};
|
|
||||||
case SWRHttpMethod.SWR_POST_INFINITE:
|
|
||||||
return {
|
|
||||||
...(this.useSwrInfinite(swrOptions!.getEndpoint!, HttpMethod.POST, {
|
|
||||||
data,
|
|
||||||
axiosOptions: axiosOptionsWithAbortController,
|
|
||||||
swrOptions,
|
|
||||||
}) as Result),
|
|
||||||
abortRequest,
|
|
||||||
};
|
|
||||||
case HttpMethod.SWR_POST:
|
|
||||||
return {
|
|
||||||
...(this.useSwr(url, HttpMethod.POST, {
|
|
||||||
data,
|
|
||||||
axiosOptions: axiosOptionsWithAbortController,
|
|
||||||
swrOptions,
|
|
||||||
}) as Result),
|
|
||||||
controller: abortController,
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
response: this.restClient.fetcher(url, {
|
|
||||||
data,
|
|
||||||
httpMethod,
|
|
||||||
config: axiosOptionsWithAbortController,
|
|
||||||
checkResponseIsJson: false,
|
|
||||||
}),
|
|
||||||
abortRequest,
|
|
||||||
} as Result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public useGetGlobalMeta(
|
public useGetGlobalMeta(
|
||||||
options?: QueryHookOptions<GetGlobalMetadatasQuery, GetGlobalMetadatasQueryVariables>,
|
options?: QueryHookOptions<GetGlobalMetadatasQuery, GetGlobalMetadatasQueryVariables>,
|
||||||
): AbortableApolloUseQueryResponse<GetGlobalMetadatasQuery, GetGlobalMetadatasQueryVariables> {
|
): AbortableApolloUseQueryResponse<GetGlobalMetadatasQuery, GetGlobalMetadatasQueryVariables> {
|
||||||
|
|||||||
18
yarn.lock
18
yarn.lock
@@ -2433,11 +2433,6 @@ cli-width@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
|
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
|
||||||
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
|
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
|
||||||
|
|
||||||
client-only@^0.0.1:
|
|
||||||
version "0.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
|
|
||||||
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
|
|
||||||
|
|
||||||
cliui@^6.0.0:
|
cliui@^6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
|
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
|
||||||
@@ -5298,14 +5293,6 @@ swap-case@^2.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.0.3"
|
tslib "^2.0.3"
|
||||||
|
|
||||||
swr@^2.2.2:
|
|
||||||
version "2.2.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/swr/-/swr-2.2.2.tgz#abcb1f9c97e10527789884169d58b878472d4c98"
|
|
||||||
integrity sha512-CbR41AoMD4TQBQw9ic3GTXspgfM9Y8Mdhb5Ob4uIKXhWqnRLItwA5fpGvB7SmSw3+zEjb0PdhiEumtUvYoQ+bQ==
|
|
||||||
dependencies:
|
|
||||||
client-only "^0.0.1"
|
|
||||||
use-sync-external-store "^1.2.0"
|
|
||||||
|
|
||||||
symbol-observable@^4.0.0:
|
symbol-observable@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"
|
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"
|
||||||
@@ -5588,11 +5575,6 @@ use-query-params@^2.2.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
serialize-query-params "^2.0.2"
|
serialize-query-params "^2.0.2"
|
||||||
|
|
||||||
use-sync-external-store@^1.2.0:
|
|
||||||
version "1.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
|
||||||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
|
||||||
|
|
||||||
util-deprecate@^1.0.1:
|
util-deprecate@^1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
|||||||
Reference in New Issue
Block a user