Replace Axios with the Fetch API (#3899)
This commit is contained in:
parent
4b3eb4a9e4
commit
7eb77cc0a7
12 changed files with 159 additions and 135 deletions
|
@ -1,6 +1,5 @@
|
|||
import { NetworkError } from "@keycloak/keycloak-admin-client";
|
||||
import { AlertVariant } from "@patternfly/react-core";
|
||||
import type { AxiosError } from "axios";
|
||||
import axios from "axios";
|
||||
import { FunctionComponent, useCallback, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
|
@ -85,8 +84,8 @@ function getErrorMessage(error: unknown) {
|
|||
return error;
|
||||
}
|
||||
|
||||
if (axios.isAxiosError(error)) {
|
||||
return getErrorMessageAxios(error);
|
||||
if (error instanceof NetworkError) {
|
||||
return getNetworkErrorMessage(error);
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
|
@ -96,8 +95,8 @@ function getErrorMessage(error: unknown) {
|
|||
throw new Error("Unable to determine error message.");
|
||||
}
|
||||
|
||||
function getErrorMessageAxios(error: AxiosError) {
|
||||
const data = (error.response?.data ?? {}) as Record<string, unknown>;
|
||||
function getNetworkErrorMessage({ responseData }: NetworkError) {
|
||||
const data = responseData as Record<string, unknown>;
|
||||
|
||||
for (const key of ["error_description", "errorMessage", "error"]) {
|
||||
const value = data[key];
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { NetworkError } from "@keycloak/keycloak-admin-client";
|
||||
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||
import { sortBy } from "lodash-es";
|
||||
import {
|
||||
|
@ -7,7 +8,6 @@ import {
|
|||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import axios from "axios";
|
||||
|
||||
import { RecentUsed } from "../components/realm-selector/recent-used";
|
||||
import { createNamedContext } from "../utils/createNamedContext";
|
||||
|
@ -46,11 +46,7 @@ export const RealmsProvider: FunctionComponent = ({ children }) => {
|
|||
try {
|
||||
return await adminClient.realms.find({ briefRepresentation: true });
|
||||
} catch (error) {
|
||||
if (
|
||||
axios.isAxiosError(error) &&
|
||||
error.response &&
|
||||
error.response.status < 500
|
||||
) {
|
||||
if (error instanceof NetworkError && error.response.status < 500) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import KeycloakAdminClient from "@keycloak/keycloak-admin-client";
|
||||
import axios from "axios";
|
||||
import Keycloak from "keycloak-js";
|
||||
import { DependencyList, useEffect } from "react";
|
||||
import { useErrorHandler } from "react-error-boundary";
|
||||
|
@ -39,35 +38,24 @@ export function useFetch<T>(
|
|||
callback: (param: T) => void,
|
||||
deps?: DependencyList
|
||||
) {
|
||||
const { adminClient } = useAdminClient();
|
||||
const onError = useErrorHandler();
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
|
||||
useEffect(() => {
|
||||
const source = axios.CancelToken.source();
|
||||
|
||||
adminClient.setConfig({
|
||||
requestConfig: { cancelToken: source.token },
|
||||
});
|
||||
|
||||
adminClientCall()
|
||||
.then((result) => {
|
||||
if (!source.token.reason) {
|
||||
if (!signal.aborted) {
|
||||
callback(result);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!axios.isCancel(error)) {
|
||||
if (!signal.aborted) {
|
||||
onError(error);
|
||||
}
|
||||
});
|
||||
|
||||
adminClient.setConfig({
|
||||
requestConfig: { cancelToken: undefined },
|
||||
});
|
||||
|
||||
return () => {
|
||||
source.cancel();
|
||||
};
|
||||
return () => controller.abort();
|
||||
}, deps);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ import KcAdminClient from '@keycloak/keycloak-admin-client';
|
|||
// {
|
||||
// baseUrl: 'http://127.0.0.1:8080',
|
||||
// realmName: 'master',
|
||||
// requestConfig: {
|
||||
// /* Axios request config options https://github.com/axios/axios#request-config */
|
||||
// requestOptions: {
|
||||
// /* Fetch request options https://developer.mozilla.org/en-US/docs/Web/API/fetch#options */
|
||||
// },
|
||||
// }
|
||||
const kcAdminClient = new KcAdminClient();
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
"camelize-ts": "^2.1.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"url-join": "^5.0.0",
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import type { AxiosRequestConfig } from "axios";
|
||||
import type { RequestArgs } from "./resources/agent.js";
|
||||
import { AttackDetection } from "./resources/attackDetection.js";
|
||||
import { AuthenticationManagement } from "./resources/authenticationManagement.js";
|
||||
|
@ -26,7 +25,7 @@ export interface TokenProvider {
|
|||
export interface ConnectionConfig {
|
||||
baseUrl?: string;
|
||||
realmName?: string;
|
||||
requestConfig?: AxiosRequestConfig;
|
||||
requestOptions?: RequestInit;
|
||||
requestArgOptions?: Pick<RequestArgs, "catchNotFound">;
|
||||
}
|
||||
|
||||
|
@ -55,14 +54,14 @@ export class KeycloakAdminClient {
|
|||
public accessToken?: string;
|
||||
public refreshToken?: string;
|
||||
|
||||
private requestConfig?: AxiosRequestConfig;
|
||||
private requestOptions?: RequestInit;
|
||||
private globalRequestArgOptions?: Pick<RequestArgs, "catchNotFound">;
|
||||
private tokenProvider?: TokenProvider;
|
||||
|
||||
constructor(connectionConfig?: ConnectionConfig) {
|
||||
this.baseUrl = connectionConfig?.baseUrl || defaultBaseUrl;
|
||||
this.realmName = connectionConfig?.realmName || defaultRealm;
|
||||
this.requestConfig = connectionConfig?.requestConfig;
|
||||
this.requestOptions = connectionConfig?.requestOptions;
|
||||
this.globalRequestArgOptions = connectionConfig?.requestArgOptions;
|
||||
|
||||
// Initialize resources
|
||||
|
@ -89,7 +88,7 @@ export class KeycloakAdminClient {
|
|||
baseUrl: this.baseUrl,
|
||||
realmName: this.realmName,
|
||||
credentials,
|
||||
requestConfig: this.requestConfig,
|
||||
requestOptions: this.requestOptions,
|
||||
});
|
||||
this.accessToken = accessToken;
|
||||
this.refreshToken = refreshToken;
|
||||
|
@ -115,8 +114,8 @@ export class KeycloakAdminClient {
|
|||
return this.accessToken;
|
||||
}
|
||||
|
||||
public getRequestConfig() {
|
||||
return this.requestConfig;
|
||||
public getRequestOptions() {
|
||||
return this.requestOptions;
|
||||
}
|
||||
|
||||
public getGlobalRequestArgOptions():
|
||||
|
@ -139,6 +138,6 @@ export class KeycloakAdminClient {
|
|||
) {
|
||||
this.realmName = connectionConfig.realmName;
|
||||
}
|
||||
this.requestConfig = connectionConfig.requestConfig;
|
||||
this.requestOptions = connectionConfig.requestOptions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { RequiredActionAlias } from "./defs/requiredActionProviderRepresentation.js";
|
||||
import { KeycloakAdminClient } from "./client.js";
|
||||
import { RequiredActionAlias } from "./defs/requiredActionProviderRepresentation.js";
|
||||
|
||||
export const requiredAction = RequiredActionAlias;
|
||||
export default KeycloakAdminClient;
|
||||
export { NetworkError } from "./utils/fetchWithError.js";
|
||||
export type { NetworkErrorOptions } from "./utils/fetchWithError.js";
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import axios, { AxiosRequestConfig, AxiosRequestHeaders, Method } from "axios";
|
||||
import { isUndefined, last, omit, pick } from "lodash-es";
|
||||
import urlJoin from "url-join";
|
||||
import { parseTemplate } from "url-template";
|
||||
import type { KeycloakAdminClient } from "../client.js";
|
||||
import {
|
||||
fetchWithError,
|
||||
NetworkError,
|
||||
parseResponse,
|
||||
} from "../utils/fetchWithError.js";
|
||||
import { stringifyQueryParams } from "../utils/stringifyQueryParams.js";
|
||||
|
||||
// constants
|
||||
const SLASH = "/";
|
||||
|
||||
type Method = "GET" | "POST" | "PUT" | "DELETE";
|
||||
|
||||
// interface
|
||||
export interface RequestArgs {
|
||||
method: Method;
|
||||
|
@ -31,7 +37,7 @@ export interface RequestArgs {
|
|||
* Keys to be ignored, meaning that they will not be filtered out of the request payload even if they are a part of `urlParamKeys` or `queryParamKeys`,
|
||||
*/
|
||||
ignoredKeys?: string[];
|
||||
headers?: AxiosRequestHeaders;
|
||||
headers?: HeadersInit;
|
||||
}
|
||||
|
||||
export class Agent {
|
||||
|
@ -76,7 +82,9 @@ export class Agent {
|
|||
const baseParams = this.getBaseParams?.() ?? {};
|
||||
|
||||
// Filter query parameters by queryParamKeys
|
||||
const queryParams = queryParamKeys ? pick(payload, queryParamKeys) : null;
|
||||
const queryParams = queryParamKeys
|
||||
? pick(payload, queryParamKeys)
|
||||
: undefined;
|
||||
|
||||
// Add filtered payload parameters to base parameters
|
||||
const allUrlParamKeys = [...Object.keys(baseParams), ...urlParamKeys];
|
||||
|
@ -128,7 +136,9 @@ export class Agent {
|
|||
const baseParams = this.getBaseParams?.() ?? {};
|
||||
|
||||
// Filter query parameters by queryParamKeys
|
||||
const queryParams = queryParamKeys ? pick(query, queryParamKeys) : null;
|
||||
const queryParams = queryParamKeys
|
||||
? pick(query, queryParamKeys)
|
||||
: undefined;
|
||||
|
||||
// Add filtered query parameters to base parameters
|
||||
const allUrlParamKeys = [...Object.keys(baseParams), ...urlParamKeys];
|
||||
|
@ -171,64 +181,72 @@ export class Agent {
|
|||
path: string;
|
||||
payload: any;
|
||||
urlParams: any;
|
||||
queryParams?: Record<string, any> | null;
|
||||
queryParams?: Record<string, string>;
|
||||
catchNotFound: boolean;
|
||||
payloadKey?: string;
|
||||
returnResourceIdInLocationHeader?: { field: string };
|
||||
headers?: AxiosRequestHeaders;
|
||||
headers?: HeadersInit;
|
||||
}) {
|
||||
const newPath = urlJoin(this.basePath, path);
|
||||
|
||||
// Parse template and replace with values from urlParams
|
||||
const pathTemplate = parseTemplate(newPath);
|
||||
const parsedPath = pathTemplate.expand(urlParams);
|
||||
const url = `${this.getBaseUrl?.() ?? ""}${parsedPath}`;
|
||||
const url = new URL(`${this.getBaseUrl?.() ?? ""}${parsedPath}`);
|
||||
const requestOptions = { ...this.client.getRequestOptions() };
|
||||
const requestHeaders = new Headers([
|
||||
...new Headers(requestOptions.headers).entries(),
|
||||
["authorization", `Bearer ${await this.client.getAccessToken()}`],
|
||||
["accept", "application/json, text/plain, */*"],
|
||||
...new Headers(headers).entries(),
|
||||
]);
|
||||
|
||||
// Prepare request config
|
||||
const requestConfig: AxiosRequestConfig = {
|
||||
paramsSerializer: (params) => stringifyQueryParams(params),
|
||||
...(this.client.getRequestConfig() || {}),
|
||||
method,
|
||||
url,
|
||||
};
|
||||
const searchParams: Record<string, string> = {};
|
||||
|
||||
// Headers
|
||||
requestConfig.headers = {
|
||||
...requestConfig.headers,
|
||||
Authorization: `bearer ${await this.client.getAccessToken()}`,
|
||||
...headers,
|
||||
};
|
||||
|
||||
// Put payload into querystring if method is GET
|
||||
// Add payload parameters to search params if method is 'GET'.
|
||||
if (method === "GET") {
|
||||
requestConfig.params = payload;
|
||||
Object.assign(searchParams, payload);
|
||||
} else if (requestHeaders.get("content-type") === "text/plain") {
|
||||
// Pass the payload as a plain string if the content type is 'text/plain'.
|
||||
requestOptions.body = payload as unknown as string;
|
||||
} else {
|
||||
// Set the request data to the payload, or the value corresponding to the payloadKey, if it's defined
|
||||
requestConfig.data = payloadKey ? payload[payloadKey] : payload;
|
||||
// Otherwise assume it's JSON and stringify it.
|
||||
requestOptions.body = JSON.stringify(
|
||||
payloadKey ? payload[payloadKey] : payload
|
||||
);
|
||||
}
|
||||
|
||||
if (!requestHeaders.has("content-type")) {
|
||||
requestHeaders.set("content-type", "application/json");
|
||||
}
|
||||
|
||||
// Concat to existing queryParams
|
||||
if (queryParams) {
|
||||
requestConfig.params = requestConfig.params
|
||||
? {
|
||||
...requestConfig.params,
|
||||
...queryParams,
|
||||
}
|
||||
: queryParams;
|
||||
Object.assign(searchParams, queryParams);
|
||||
}
|
||||
|
||||
url.search = stringifyQueryParams(searchParams);
|
||||
|
||||
if (!requestHeaders.has("content-type")) {
|
||||
requestHeaders.set("content-type", "application/x-www-form-urlencoded");
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await axios.default(requestConfig);
|
||||
const res = await fetchWithError(url, {
|
||||
...requestOptions,
|
||||
headers: requestHeaders,
|
||||
method,
|
||||
});
|
||||
|
||||
// now we get the response of the http request
|
||||
// if `resourceIdInLocationHeader` is true, we'll get the resourceId from the location header field
|
||||
// todo: find a better way to find the id in path, maybe some kind of pattern matching
|
||||
// for now, we simply split the last sub-path of the path returned in location header field
|
||||
if (returnResourceIdInLocationHeader) {
|
||||
const locationHeader = res.headers.location;
|
||||
const locationHeader = res.headers.get("location");
|
||||
|
||||
if (typeof locationHeader !== "string") {
|
||||
throw new Error(
|
||||
`location header is not found in request: ${res.config.url}`
|
||||
`location header is not found in request: ${res.url}`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -236,7 +254,7 @@ export class Agent {
|
|||
if (!resourceId) {
|
||||
// throw an error to let users know the response is not expected
|
||||
throw new Error(
|
||||
`resourceId is not found in Location header from request: ${res.config.url}`
|
||||
`resourceId is not found in Location header from request: ${res.url}`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -244,11 +262,12 @@ export class Agent {
|
|||
const { field } = returnResourceIdInLocationHeader;
|
||||
return { [field]: resourceId };
|
||||
}
|
||||
return res.data;
|
||||
|
||||
return parseResponse(res);
|
||||
} catch (err) {
|
||||
if (
|
||||
axios.default.isAxiosError(err) &&
|
||||
err.response?.status === 404 &&
|
||||
err instanceof NetworkError &&
|
||||
err.response.status === 404 &&
|
||||
catchNotFound
|
||||
) {
|
||||
return null;
|
||||
|
|
|
@ -223,7 +223,6 @@ export class Users extends Resource<{ realm?: string }> {
|
|||
urlParamKeys: ["id"],
|
||||
payloadKey: "actions",
|
||||
queryParamKeys: ["lifespan", "redirectUri", "clientId"],
|
||||
headers: { "content-type": "application/json" },
|
||||
keyTransform: {
|
||||
clientId: "client_id",
|
||||
redirectUri: "redirect_uri",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
import camelize from "camelize-ts";
|
||||
import { defaultBaseUrl, defaultRealm } from "./constants.js";
|
||||
import { fetchWithError } from "./fetchWithError.js";
|
||||
import { stringifyQueryParams } from "./stringifyQueryParams.js";
|
||||
|
||||
export type GrantTypes = "client_credentials" | "password" | "refresh_token";
|
||||
|
@ -20,7 +20,7 @@ export interface Settings {
|
|||
realmName?: string;
|
||||
baseUrl?: string;
|
||||
credentials: Credentials;
|
||||
requestConfig?: AxiosRequestConfig;
|
||||
requestOptions?: RequestInit;
|
||||
}
|
||||
|
||||
export interface TokenResponseRaw {
|
||||
|
@ -69,20 +69,25 @@ export const getToken = async (settings: Settings): Promise<TokenResponse> => {
|
|||
: {}),
|
||||
});
|
||||
|
||||
const config: AxiosRequestConfig = {
|
||||
...settings.requestConfig,
|
||||
};
|
||||
const options = settings.requestOptions ?? {};
|
||||
const headers = new Headers(options.headers);
|
||||
|
||||
if (credentials.clientSecret) {
|
||||
config.auth = {
|
||||
username: credentials.clientId,
|
||||
password: credentials.clientSecret,
|
||||
};
|
||||
headers.set(
|
||||
"Authorization",
|
||||
atob(credentials.clientId + ":" + credentials.clientSecret)
|
||||
);
|
||||
}
|
||||
|
||||
const { data } = await axios.default.post<
|
||||
any,
|
||||
AxiosResponse<TokenResponseRaw>
|
||||
>(url, payload, config);
|
||||
headers.set("content-type", "application/x-www-form-urlencoded");
|
||||
|
||||
const response = await fetchWithError(url, {
|
||||
...options,
|
||||
method: "POST",
|
||||
headers,
|
||||
body: payload,
|
||||
});
|
||||
|
||||
const data: TokenResponseRaw = await response.json();
|
||||
return camelize(data);
|
||||
};
|
||||
|
|
44
libs/keycloak-admin-client/src/utils/fetchWithError.ts
Normal file
44
libs/keycloak-admin-client/src/utils/fetchWithError.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
export type NetworkErrorOptions = { response: Response; responseData: unknown };
|
||||
|
||||
export class NetworkError extends Error {
|
||||
response: Response;
|
||||
responseData: unknown;
|
||||
|
||||
constructor(message: string, options: NetworkErrorOptions) {
|
||||
super(message);
|
||||
this.response = options.response;
|
||||
this.responseData = options.responseData;
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchWithError(
|
||||
input: RequestInfo | URL,
|
||||
init?: RequestInit
|
||||
) {
|
||||
const response = await fetch(input, init);
|
||||
|
||||
if (!response.ok) {
|
||||
const responseData = await parseResponse(response);
|
||||
throw new NetworkError("Network response was not OK.", {
|
||||
response,
|
||||
responseData,
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function parseResponse(response: Response): Promise<any> {
|
||||
if (!response.body) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const data = await response.text();
|
||||
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (error) {}
|
||||
|
||||
return data;
|
||||
}
|
56
package-lock.json
generated
56
package-lock.json
generated
|
@ -548,7 +548,6 @@
|
|||
"version": "999.0.0-dev",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
"camelize-ts": "^2.1.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"url-join": "^5.0.0",
|
||||
|
@ -5691,6 +5690,7 @@
|
|||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/at-least-node": {
|
||||
|
@ -5732,14 +5732,6 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.27.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.9",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-loader": {
|
||||
"version": "8.2.5",
|
||||
"dev": true,
|
||||
|
@ -6685,6 +6677,7 @@
|
|||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
|
@ -7597,6 +7590,7 @@
|
|||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
|
@ -9065,24 +9059,6 @@
|
|||
"tabbable": "^5.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.1",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/for-in": {
|
||||
"version": "1.0.2",
|
||||
"dev": true,
|
||||
|
@ -9101,6 +9077,7 @@
|
|||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
|
@ -11233,6 +11210,7 @@
|
|||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
|
@ -11240,6 +11218,7 @@
|
|||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
|
@ -17819,7 +17798,6 @@
|
|||
"@types/lodash-es": "^4.17.5",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "^18.0.3",
|
||||
"axios": "^0.27.2",
|
||||
"camelize-ts": "^2.1.1",
|
||||
"chai": "^4.3.7",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
@ -19854,7 +19832,8 @@
|
|||
"optional": true
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0"
|
||||
"version": "0.4.0",
|
||||
"dev": true
|
||||
},
|
||||
"at-least-node": {
|
||||
"version": "1.0.0",
|
||||
|
@ -19875,13 +19854,6 @@
|
|||
"version": "1.11.0",
|
||||
"dev": true
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.27.2",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.9",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"babel-loader": {
|
||||
"version": "8.2.5",
|
||||
"dev": true,
|
||||
|
@ -20516,6 +20488,7 @@
|
|||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
|
@ -21123,7 +21096,8 @@
|
|||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.0",
|
||||
"dev": true
|
||||
},
|
||||
"deprecation": {
|
||||
"version": "2.3.1"
|
||||
|
@ -22140,9 +22114,6 @@
|
|||
"tabbable": "^5.3.2"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.1"
|
||||
},
|
||||
"for-in": {
|
||||
"version": "1.0.2",
|
||||
"dev": true
|
||||
|
@ -22153,6 +22124,7 @@
|
|||
},
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
|
@ -23570,10 +23542,12 @@
|
|||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.52.0"
|
||||
"version": "1.52.0",
|
||||
"dev": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.35",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mime-db": "1.52.0"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue