Compare commits
3
Commits
7dde10b7ec
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6c14b52fd | ||
|
|
2e71eb73b8 | ||
|
|
84c6262b3d |
@@ -222,3 +222,39 @@ Axios-based HTTP client with:
|
|||||||
- User emails normalized to lowercase before storage
|
- User emails normalized to lowercase before storage
|
||||||
- JWT tokens stored in localStorage (trade-off: simple SPA integration vs XSS exposure — refresh tokens mitigate short exposure)
|
- JWT tokens stored in localStorage (trade-off: simple SPA integration vs XSS exposure — refresh tokens mitigate short exposure)
|
||||||
- `AUTH_HEADER_TYPES` set to `("Bearer",)` following RFC 6750
|
- `AUTH_HEADER_TYPES` set to `("Bearer",)` following RFC 6750
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Post-Merge Fixes (PR #14)
|
||||||
|
|
||||||
|
**Issue:** Issue #3 was re-opened after initial merge. Root cause: frontend error handling was using `err.message` from AxiosError objects, which produces `"Request failed with status code 400"` instead of the actual backend validation errors.
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
| Area | Change | Detail |
|
||||||
|
|---|---|---|
|
||||||
|
| `api/accounts/urls.py` | Added `token/refresh/` endpoint | Maps to `rest_framework_simplejwt.views.TokenRefreshView` |
|
||||||
|
| `web/src/services/authApi.ts` | **Error extraction** | New `extractErrorMessage()` function parses `err.response.data` — handles `non_field_errors`, `detail`, and field-level error arrays |
|
||||||
|
| `web/src/services/authApi.ts` | **Token refresh interceptor** | Axios response interceptor intercepts 401, refreshes access token using stored refresh_token, replays queued requests |
|
||||||
|
| `web/src/services/authApi.ts` | Export `setTokens`, `clearTokens`, `getAccessToken` | Shared token utility functions for AuthContext |
|
||||||
|
| `web/src/contexts/AuthContext.tsx` | **Fixed error handling** | Uses `extractErrorMessage(err)` instead of `err instanceof Error ? err.message : ...` |
|
||||||
|
| `web/src/contexts/AuthContext.tsx` | **Auth state restoration** | `RESTORE_COMPLETE` action; `isRestoring` flag prevents protected route flash on page refresh |
|
||||||
|
| `web/src/contexts/AuthContext.tsx` | **Persist user data** | Stores `user_data` in localStorage alongside tokens; restores `UserProfile` on page reload |
|
||||||
|
| `web/src/components/ProtectedRoute.tsx` | New component | Route guard — redirects to `/login` if unauthenticated; shows loading spinner during restoration |
|
||||||
|
| `web/src/App.tsx` | Route restructure | Public routes (`/login`, `/register`) outside ProtectedRoute; protected routes (`/`) inside it |
|
||||||
|
|
||||||
|
### Error handling flow
|
||||||
|
|
||||||
|
1. Backend returns `{"email": ["A user with this email already exists."]}` (DRF standard)
|
||||||
|
2. Axios interceptor catches non-401 responses, passes error through
|
||||||
|
3. `extractErrorMessage()` iterates `response.data` keys, returns first error string
|
||||||
|
4. AuthContext dispatches `AUTH_FAILURE` with extracted message
|
||||||
|
5. LoginPage/RegisterPage renders `state.error` — user sees: "A user with this email already exists."
|
||||||
|
|
||||||
|
### Token refresh flow
|
||||||
|
|
||||||
|
1. User logs in — `access_token` and `refresh_token` stored in localStorage
|
||||||
|
2. When access token expires, next API call receives 401
|
||||||
|
3. Response interceptor catches it, calls `POST /api/auth/token/refresh/`
|
||||||
|
4. On success — new access token stored, original request retried, queued requests replayed
|
||||||
|
5. On failure — tokens cleared, all pending requests rejected with "Session expired"
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { type ReactNode } from "react";
|
||||||
|
import { Navigate, Outlet, useLocation } from "react-router-dom";
|
||||||
|
import { useAuth } from "../contexts/AuthContext";
|
||||||
|
import CircularProgress from "@mui/material/CircularProgress";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
|
interface ProtectedRouteProps {
|
||||||
|
/** Optional redirect path for unauthenticated users (default: /login) */
|
||||||
|
redirectTo?: string;
|
||||||
|
/** Optional fallback UI while restoring auth state */
|
||||||
|
fallback?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route guard that redirects unauthenticated users to the login page.
|
||||||
|
* Shows a loading spinner while auth state is being restored from localStorage.
|
||||||
|
*/
|
||||||
|
export default function ProtectedRoute({
|
||||||
|
redirectTo = "/login",
|
||||||
|
fallback,
|
||||||
|
}: ProtectedRouteProps): ReactNode {
|
||||||
|
const { state } = useAuth();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
// Still checking localStorage for existing session
|
||||||
|
if (state.isRestoring) {
|
||||||
|
return (
|
||||||
|
fallback ?? (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
minHeight: "100vh",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CircularProgress />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.isAuthenticated) {
|
||||||
|
// Preserve the attempted URL so we can redirect back after login
|
||||||
|
return <Navigate to={redirectTo} state={{ from: location }} replace />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Outlet />;
|
||||||
|
}
|
||||||
@@ -15,6 +15,8 @@ import {
|
|||||||
setTokens,
|
setTokens,
|
||||||
clearTokens,
|
clearTokens,
|
||||||
refreshAccessToken,
|
refreshAccessToken,
|
||||||
|
extractErrorMessage,
|
||||||
|
getAccessToken,
|
||||||
type UserProfile,
|
type UserProfile,
|
||||||
type LoginPayload,
|
type LoginPayload,
|
||||||
type RegisterPayload,
|
type RegisterPayload,
|
||||||
@@ -63,6 +65,7 @@ function authReducer(state: AuthState, action: AuthAction): AuthState {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
isInitializing: false,
|
||||||
error: action.payload,
|
error: action.payload,
|
||||||
};
|
};
|
||||||
case "LOGOUT":
|
case "LOGOUT":
|
||||||
@@ -97,6 +100,20 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
const initAuth = async () => {
|
const initAuth = async () => {
|
||||||
const refreshToken = localStorage.getItem("refresh_token");
|
const refreshToken = localStorage.getItem("refresh_token");
|
||||||
if (!refreshToken) {
|
if (!refreshToken) {
|
||||||
|
// Still check for legacy access_token to restore from localStorage
|
||||||
|
const accessToken = getAccessToken();
|
||||||
|
if (accessToken) {
|
||||||
|
const userData = localStorage.getItem("user_data");
|
||||||
|
if (userData) {
|
||||||
|
try {
|
||||||
|
const user: UserProfile = JSON.parse(userData);
|
||||||
|
dispatch({ type: "AUTH_SUCCESS", payload: user });
|
||||||
|
return;
|
||||||
|
} catch {
|
||||||
|
// corrupt stored data — proceed with unauthenticated state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
dispatch({ type: "INIT_COMPLETE" });
|
dispatch({ type: "INIT_COMPLETE" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -108,10 +125,13 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
// Fetch user profile with the fresh token
|
// Fetch user profile with the fresh token
|
||||||
const profile = await getProfile();
|
const profile = await getProfile();
|
||||||
|
// Persist user data for restoration if refresh token expires
|
||||||
|
localStorage.setItem("user_data", JSON.stringify(profile));
|
||||||
dispatch({ type: "AUTH_SUCCESS", payload: profile });
|
dispatch({ type: "AUTH_SUCCESS", payload: profile });
|
||||||
} catch {
|
} catch {
|
||||||
// Token invalid or expired — clear everything
|
// Token invalid or expired — clear everything
|
||||||
clearTokens();
|
clearTokens();
|
||||||
|
localStorage.removeItem("user_data");
|
||||||
dispatch({ type: "LOGOUT" });
|
dispatch({ type: "LOGOUT" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -124,10 +144,10 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
try {
|
try {
|
||||||
const response = await loginUser(payload);
|
const response = await loginUser(payload);
|
||||||
setTokens(response.access, response.refresh);
|
setTokens(response.access, response.refresh);
|
||||||
|
localStorage.setItem("user_data", JSON.stringify(response.user));
|
||||||
dispatch({ type: "AUTH_SUCCESS", payload: response.user });
|
dispatch({ type: "AUTH_SUCCESS", payload: response.user });
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const message =
|
const message = extractErrorMessage(err);
|
||||||
err instanceof Error ? err.message : "Login failed. Please try again.";
|
|
||||||
dispatch({ type: "AUTH_FAILURE", payload: message });
|
dispatch({ type: "AUTH_FAILURE", payload: message });
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -140,10 +160,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
// Registration succeeded — the page component handles redirect to /login.
|
// Registration succeeded — the page component handles redirect to /login.
|
||||||
dispatch({ type: "LOGOUT" });
|
dispatch({ type: "LOGOUT" });
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const message =
|
const message = extractErrorMessage(err);
|
||||||
err instanceof Error
|
|
||||||
? err.message
|
|
||||||
: "Registration failed. Please try again.";
|
|
||||||
dispatch({ type: "AUTH_FAILURE", payload: message });
|
dispatch({ type: "AUTH_FAILURE", payload: message });
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -159,6 +176,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
clearTokens();
|
clearTokens();
|
||||||
|
localStorage.removeItem("user_data");
|
||||||
dispatch({ type: "LOGOUT" });
|
dispatch({ type: "LOGOUT" });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
+101
-32
@@ -1,4 +1,8 @@
|
|||||||
import axios from "axios";
|
import axios, { AxiosError, type AxiosResponse, type InternalAxiosRequestConfig } from "axios";
|
||||||
|
|
||||||
|
interface RetryConfig extends InternalAxiosRequestConfig {
|
||||||
|
_retry?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const apiClient = axios.create({
|
const apiClient = axios.create({
|
||||||
baseURL: import.meta.env.VITE_API_URL || "http://localhost:8000",
|
baseURL: import.meta.env.VITE_API_URL || "http://localhost:8000",
|
||||||
@@ -7,16 +11,38 @@ const apiClient = axios.create({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Attach access token to every request if present
|
// ── Token management ──────────────────────────────────────────────────
|
||||||
apiClient.interceptors.request.use((config) => {
|
|
||||||
const token = localStorage.getItem("access_token");
|
function getAccessToken(): string | null {
|
||||||
|
return localStorage.getItem("access_token");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRefreshToken(): string | null {
|
||||||
|
return localStorage.getItem("refresh_token");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTokens(access: string, refresh: string): void {
|
||||||
|
localStorage.setItem("access_token", access);
|
||||||
|
localStorage.setItem("refresh_token", refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearTokens(): void {
|
||||||
|
localStorage.removeItem("access_token");
|
||||||
|
localStorage.removeItem("refresh_token");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Request interceptor: attach access token ──────────────────────────
|
||||||
|
|
||||||
|
apiClient.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
||||||
|
const token = getAccessToken();
|
||||||
if (token && config.headers) {
|
if (token && config.headers) {
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Response interceptor: auto-refresh on 401, retry once
|
// ── Response interceptor: auto-refresh on 401, retry once ─────────────
|
||||||
|
|
||||||
let isRefreshing = false;
|
let isRefreshing = false;
|
||||||
let failedQueue: Array<{
|
let failedQueue: Array<{
|
||||||
resolve: (token: string) => void;
|
resolve: (token: string) => void;
|
||||||
@@ -35,11 +61,15 @@ function processQueue(error: unknown, token: string | null = null): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apiClient.interceptors.response.use(
|
apiClient.interceptors.response.use(
|
||||||
(response) => response,
|
(response: AxiosResponse) => response,
|
||||||
async (error) => {
|
async (error: AxiosError) => {
|
||||||
const originalRequest = error.config;
|
const originalRequest = error.config as RetryConfig;
|
||||||
|
|
||||||
// Only handle 401s that aren't already refresh/login/register attempts
|
if (!originalRequest) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle 401s that aren't already refresh/login/register/logout attempts
|
||||||
if (
|
if (
|
||||||
error.response?.status !== 401 ||
|
error.response?.status !== 401 ||
|
||||||
originalRequest._retry ||
|
originalRequest._retry ||
|
||||||
@@ -56,7 +86,7 @@ apiClient.interceptors.response.use(
|
|||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
failedQueue.push({ resolve, reject });
|
failedQueue.push({ resolve, reject });
|
||||||
}).then((token) => {
|
}).then((token) => {
|
||||||
originalRequest.headers.Authorization = `Bearer ${token}`;
|
originalRequest.headers!.Authorization = `Bearer ${token}`;
|
||||||
return apiClient(originalRequest);
|
return apiClient(originalRequest);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -64,39 +94,33 @@ apiClient.interceptors.response.use(
|
|||||||
originalRequest._retry = true;
|
originalRequest._retry = true;
|
||||||
isRefreshing = true;
|
isRefreshing = true;
|
||||||
|
|
||||||
const refreshToken = localStorage.getItem("refresh_token");
|
const refreshToken = getRefreshToken();
|
||||||
|
|
||||||
if (!refreshToken) {
|
if (!refreshToken) {
|
||||||
isRefreshing = false;
|
isRefreshing = false;
|
||||||
localStorage.removeItem("access_token");
|
clearTokens();
|
||||||
localStorage.removeItem("refresh_token");
|
|
||||||
// Redirect to login
|
|
||||||
window.location.href = "/login";
|
window.location.href = "/login";
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
`${
|
`${apiClient.defaults.baseURL}/api/auth/token/refresh/`,
|
||||||
import.meta.env.VITE_API_URL || "http://localhost:8000"
|
|
||||||
}/api/auth/token/refresh/`,
|
|
||||||
{ refresh: refreshToken }
|
{ refresh: refreshToken }
|
||||||
);
|
);
|
||||||
|
|
||||||
const newAccessToken = response.data.access;
|
const newAccessToken = response.data.access;
|
||||||
const newRefreshToken = response.data.refresh;
|
const newRefreshToken = response.data.refresh;
|
||||||
|
|
||||||
localStorage.setItem("access_token", newAccessToken);
|
setTokens(newAccessToken, newRefreshToken);
|
||||||
localStorage.setItem("refresh_token", newRefreshToken);
|
|
||||||
|
|
||||||
processQueue(null, newAccessToken);
|
processQueue(null, newAccessToken);
|
||||||
|
|
||||||
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
originalRequest.headers!.Authorization = `Bearer ${newAccessToken}`;
|
||||||
return apiClient(originalRequest);
|
return apiClient(originalRequest);
|
||||||
} catch (refreshError) {
|
} catch (refreshError) {
|
||||||
processQueue(refreshError, null);
|
processQueue(refreshError, null);
|
||||||
localStorage.removeItem("access_token");
|
clearTokens();
|
||||||
localStorage.removeItem("refresh_token");
|
|
||||||
window.location.href = "/login";
|
window.location.href = "/login";
|
||||||
return Promise.reject(refreshError);
|
return Promise.reject(refreshError);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -105,6 +129,53 @@ apiClient.interceptors.response.use(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ── Error extraction from Axios responses ─────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a human-readable error message from an AxiosError.
|
||||||
|
* Backend DRF errors can be:
|
||||||
|
* - {"field": ["message"]} (field-level)
|
||||||
|
* - {"non_field_errors": ["message"]} (general)
|
||||||
|
* - {"detail": "message"} (list/generic)
|
||||||
|
* - string (unexpected shape)
|
||||||
|
*/
|
||||||
|
export function extractErrorMessage(err: unknown): string {
|
||||||
|
if (err instanceof AxiosError && err.response?.data) {
|
||||||
|
const data = err.response.data as Record<string, unknown>;
|
||||||
|
|
||||||
|
// DRF non_field_errors
|
||||||
|
if (
|
||||||
|
Array.isArray(data.non_field_errors) &&
|
||||||
|
data.non_field_errors.length > 0
|
||||||
|
) {
|
||||||
|
return String(data.non_field_errors[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DRF detail (e.g., 401 Unauthorized)
|
||||||
|
if (typeof data.detail === "string") {
|
||||||
|
return data.detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field-level errors — pick the first one
|
||||||
|
for (const key of Object.keys(data)) {
|
||||||
|
const val = data[key];
|
||||||
|
if (Array.isArray(val) && val.length > 0) {
|
||||||
|
return String(val[0]);
|
||||||
|
}
|
||||||
|
if (typeof val === "string") {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: raw string
|
||||||
|
if (typeof data === "string") return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic error fallback
|
||||||
|
if (err instanceof Error) return err.message;
|
||||||
|
return "An unexpected error occurred. Please try again.";
|
||||||
|
}
|
||||||
|
|
||||||
// ── Types ──────────────────────────────────────────────────────────────
|
// ── Types ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export interface RegisterPayload {
|
export interface RegisterPayload {
|
||||||
@@ -132,7 +203,7 @@ export interface LoginResponse {
|
|||||||
refresh: string;
|
refresh: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── API Functions ──────────────────────────────────────────────────────
|
// ── API functions ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
export function registerUser(payload: RegisterPayload): Promise<UserProfile> {
|
export function registerUser(payload: RegisterPayload): Promise<UserProfile> {
|
||||||
return apiClient
|
return apiClient
|
||||||
@@ -169,12 +240,10 @@ export function refreshAccessToken(
|
|||||||
.then((res) => res.data);
|
.then((res) => res.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setTokens(access: string, refresh: string): void {
|
export {
|
||||||
localStorage.setItem("access_token", access);
|
apiClient,
|
||||||
localStorage.setItem("refresh_token", refresh);
|
getAccessToken,
|
||||||
}
|
getRefreshToken,
|
||||||
|
setTokens,
|
||||||
export function clearTokens(): void {
|
clearTokens,
|
||||||
localStorage.removeItem("access_token");
|
};
|
||||||
localStorage.removeItem("refresh_token");
|
|
||||||
}
|
|
||||||
@@ -732,6 +732,28 @@
|
|||||||
"@types/babel__core" "^7.20.5"
|
"@types/babel__core" "^7.20.5"
|
||||||
react-refresh "^0.17.0"
|
react-refresh "^0.17.0"
|
||||||
|
|
||||||
|
agent-base@6:
|
||||||
|
version "6.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||||
|
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
|
||||||
|
dependencies:
|
||||||
|
debug "4"
|
||||||
|
|
||||||
|
asynckit@^0.4.0:
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||||
|
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||||
|
|
||||||
|
axios@^1.7.0:
|
||||||
|
version "1.16.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-1.16.1.tgz#517e29291d19d6e8cf919ff264f4fe157261ba12"
|
||||||
|
integrity sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.16.0"
|
||||||
|
form-data "^4.0.5"
|
||||||
|
https-proxy-agent "^5.0.1"
|
||||||
|
proxy-from-env "^2.1.0"
|
||||||
|
|
||||||
babel-plugin-macros@^3.1.0:
|
babel-plugin-macros@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1"
|
resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1"
|
||||||
@@ -757,6 +779,14 @@ browserslist@^4.24.0:
|
|||||||
node-releases "^2.0.36"
|
node-releases "^2.0.36"
|
||||||
update-browserslist-db "^1.2.3"
|
update-browserslist-db "^1.2.3"
|
||||||
|
|
||||||
|
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
|
||||||
|
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
|
||||||
|
dependencies:
|
||||||
|
es-errors "^1.3.0"
|
||||||
|
function-bind "^1.1.2"
|
||||||
|
|
||||||
callsites@^3.0.0:
|
callsites@^3.0.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||||
@@ -772,6 +802,13 @@ clsx@^2.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
|
||||||
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
||||||
|
|
||||||
|
combined-stream@^1.0.8:
|
||||||
|
version "1.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||||
|
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||||
|
dependencies:
|
||||||
|
delayed-stream "~1.0.0"
|
||||||
|
|
||||||
convert-source-map@^1.5.0:
|
convert-source-map@^1.5.0:
|
||||||
version "1.9.0"
|
version "1.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
|
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
|
||||||
@@ -803,13 +840,18 @@ csstype@^3.0.2, csstype@^3.2.2, csstype@^3.2.3:
|
|||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a"
|
||||||
integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
|
integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
|
||||||
|
|
||||||
debug@^4.1.0, debug@^4.3.1:
|
debug@4, debug@^4.1.0, debug@^4.3.1:
|
||||||
version "4.4.3"
|
version "4.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
|
||||||
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
|
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.3"
|
ms "^2.1.3"
|
||||||
|
|
||||||
|
delayed-stream@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||||
|
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||||
|
|
||||||
dom-helpers@^5.0.1:
|
dom-helpers@^5.0.1:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
|
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
|
||||||
@@ -818,6 +860,15 @@ dom-helpers@^5.0.1:
|
|||||||
"@babel/runtime" "^7.8.7"
|
"@babel/runtime" "^7.8.7"
|
||||||
csstype "^3.0.2"
|
csstype "^3.0.2"
|
||||||
|
|
||||||
|
dunder-proto@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
|
||||||
|
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
|
||||||
|
dependencies:
|
||||||
|
call-bind-apply-helpers "^1.0.1"
|
||||||
|
es-errors "^1.3.0"
|
||||||
|
gopd "^1.2.0"
|
||||||
|
|
||||||
electron-to-chromium@^1.5.328:
|
electron-to-chromium@^1.5.328:
|
||||||
version "1.5.361"
|
version "1.5.361"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz#b993bc7b34ea83f348aa1787a608ecf12e39b909"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz#b993bc7b34ea83f348aa1787a608ecf12e39b909"
|
||||||
@@ -830,11 +881,33 @@ error-ex@^1.3.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-arrayish "^0.2.1"
|
is-arrayish "^0.2.1"
|
||||||
|
|
||||||
|
es-define-property@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
|
||||||
|
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
|
||||||
|
|
||||||
es-errors@^1.3.0:
|
es-errors@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
||||||
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
||||||
|
|
||||||
|
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz#a2d0b373205724dfa525d23b0c3e1b1ca582c99b"
|
||||||
|
integrity sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==
|
||||||
|
dependencies:
|
||||||
|
es-errors "^1.3.0"
|
||||||
|
|
||||||
|
es-set-tostringtag@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
|
||||||
|
integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
|
||||||
|
dependencies:
|
||||||
|
es-errors "^1.3.0"
|
||||||
|
get-intrinsic "^1.2.6"
|
||||||
|
has-tostringtag "^1.0.2"
|
||||||
|
hasown "^2.0.2"
|
||||||
|
|
||||||
esbuild@^0.25.0:
|
esbuild@^0.25.0:
|
||||||
version "0.25.12"
|
version "0.25.12"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.12.tgz#97a1d041f4ab00c2fce2f838d2b9969a2d2a97a5"
|
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.12.tgz#97a1d041f4ab00c2fce2f838d2b9969a2d2a97a5"
|
||||||
@@ -887,6 +960,22 @@ find-root@^1.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
|
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
|
||||||
integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
|
integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
|
||||||
|
|
||||||
|
follow-redirects@^1.16.0:
|
||||||
|
version "1.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc"
|
||||||
|
integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==
|
||||||
|
|
||||||
|
form-data@^4.0.5:
|
||||||
|
version "4.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053"
|
||||||
|
integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==
|
||||||
|
dependencies:
|
||||||
|
asynckit "^0.4.0"
|
||||||
|
combined-stream "^1.0.8"
|
||||||
|
es-set-tostringtag "^2.1.0"
|
||||||
|
hasown "^2.0.2"
|
||||||
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
fsevents@~2.3.2, fsevents@~2.3.3:
|
fsevents@~2.3.2, fsevents@~2.3.3:
|
||||||
version "2.3.3"
|
version "2.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||||
@@ -902,6 +991,54 @@ gensync@^1.0.0-beta.2:
|
|||||||
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||||
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
||||||
|
|
||||||
|
get-intrinsic@^1.2.6:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
|
||||||
|
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
|
||||||
|
dependencies:
|
||||||
|
call-bind-apply-helpers "^1.0.2"
|
||||||
|
es-define-property "^1.0.1"
|
||||||
|
es-errors "^1.3.0"
|
||||||
|
es-object-atoms "^1.1.1"
|
||||||
|
function-bind "^1.1.2"
|
||||||
|
get-proto "^1.0.1"
|
||||||
|
gopd "^1.2.0"
|
||||||
|
has-symbols "^1.1.0"
|
||||||
|
hasown "^2.0.2"
|
||||||
|
math-intrinsics "^1.1.0"
|
||||||
|
|
||||||
|
get-proto@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
|
||||||
|
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
|
||||||
|
dependencies:
|
||||||
|
dunder-proto "^1.0.1"
|
||||||
|
es-object-atoms "^1.0.0"
|
||||||
|
|
||||||
|
gopd@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
|
||||||
|
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
|
||||||
|
|
||||||
|
has-symbols@^1.0.3, has-symbols@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
|
||||||
|
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
|
||||||
|
|
||||||
|
has-tostringtag@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
|
||||||
|
integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
|
||||||
|
dependencies:
|
||||||
|
has-symbols "^1.0.3"
|
||||||
|
|
||||||
|
hasown@^2.0.2:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003"
|
||||||
|
integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==
|
||||||
|
dependencies:
|
||||||
|
function-bind "^1.1.2"
|
||||||
|
|
||||||
hasown@^2.0.3:
|
hasown@^2.0.3:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.3.tgz#5e5c2b15b60370a4c7930c383dfb76bf17bc403c"
|
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.3.tgz#5e5c2b15b60370a4c7930c383dfb76bf17bc403c"
|
||||||
@@ -916,6 +1053,14 @@ hoist-non-react-statics@^3.3.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
react-is "^16.7.0"
|
react-is "^16.7.0"
|
||||||
|
|
||||||
|
https-proxy-agent@^5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
||||||
|
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
|
||||||
|
dependencies:
|
||||||
|
agent-base "6"
|
||||||
|
debug "4"
|
||||||
|
|
||||||
import-fresh@^3.2.1:
|
import-fresh@^3.2.1:
|
||||||
version "3.3.1"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf"
|
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf"
|
||||||
@@ -975,6 +1120,23 @@ lru-cache@^5.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist "^3.0.2"
|
yallist "^3.0.2"
|
||||||
|
|
||||||
|
math-intrinsics@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
|
||||||
|
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
|
||||||
|
|
||||||
|
mime-db@1.52.0:
|
||||||
|
version "1.52.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||||
|
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||||
|
|
||||||
|
mime-types@^2.1.12:
|
||||||
|
version "2.1.35"
|
||||||
|
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||||
|
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||||
|
dependencies:
|
||||||
|
mime-db "1.52.0"
|
||||||
|
|
||||||
ms@^2.1.3:
|
ms@^2.1.3:
|
||||||
version "2.1.3"
|
version "2.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||||
@@ -1050,6 +1212,11 @@ prop-types@^15.6.2, prop-types@^15.8.1:
|
|||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
react-is "^16.13.1"
|
react-is "^16.13.1"
|
||||||
|
|
||||||
|
proxy-from-env@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba"
|
||||||
|
integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==
|
||||||
|
|
||||||
react-dom@^19.0.0:
|
react-dom@^19.0.0:
|
||||||
version "19.2.6"
|
version "19.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.6.tgz#44a81b0bcca22da814c00847d09d01c8615529b7"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.6.tgz#44a81b0bcca22da814c00847d09d01c8615529b7"
|
||||||
|
|||||||
Reference in New Issue
Block a user