extract auth to lib
This commit is contained in:
50
.gitignore
vendored
Normal file
50
.gitignore
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Builds
|
||||
dist/
|
||||
build/
|
||||
coverage/
|
||||
|
||||
# Vite / tooling caches
|
||||
.vite/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
bun-debug.log*
|
||||
|
||||
# Env files (keep .env.example committed)
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# TypeScript incremental build info
|
||||
*.tsbuildinfo
|
||||
|
||||
# OS / editor cruft
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# JetBrains (either ignore all, or see "optional" note below)
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based HTTP Client
|
||||
http-client.private.env.json
|
||||
2
.npmrc
Normal file
2
.npmrc
Normal file
@@ -0,0 +1,2 @@
|
||||
registry=https://nexus.beatrice.wtf/repository/npm-group/
|
||||
@panic:registry=https://nexus.beatrice.wtf/repository/npm-hosted/
|
||||
38
package.json
Normal file
38
package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@panic/web-core",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist",
|
||||
"build": "yarn clean && vite build && tsc -p tsconfig.build.json",
|
||||
"prepublishOnly": "yarn build",
|
||||
"publish:nexus": "npm publish --registry ${NEXUS_NPM_REGISTRY:-https://nexus.beatrice.wtf/repository/npm-hosted/}"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.beatrice.wtf/repository/npm-hosted/",
|
||||
"access": "restricted"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.0.0",
|
||||
"@vitejs/plugin-react": "^5.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"typescript": "^5.6.2",
|
||||
"vite": "^7.0.0"
|
||||
}
|
||||
}
|
||||
134
src/api/createApiClient.ts
Normal file
134
src/api/createApiClient.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
export type RequestOptions = {
|
||||
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
||||
token?: string | null;
|
||||
body?: unknown;
|
||||
};
|
||||
|
||||
export type ResolveErrorInput = {
|
||||
code?: string;
|
||||
status?: number;
|
||||
fallbackMessage?: string;
|
||||
};
|
||||
|
||||
export type CreateApiClientConfig = {
|
||||
baseUrl: string;
|
||||
resolveError?: (input: ResolveErrorInput) => string;
|
||||
inferErrorCodeFromStatus?: (status?: number | null) => string | undefined;
|
||||
fetchImpl?: typeof fetch;
|
||||
};
|
||||
|
||||
export class ApiError extends Error {
|
||||
status: number;
|
||||
code?: string;
|
||||
requestId?: string;
|
||||
details?: unknown;
|
||||
rawMessage?: string;
|
||||
|
||||
constructor({
|
||||
message,
|
||||
status,
|
||||
code,
|
||||
requestId,
|
||||
details,
|
||||
rawMessage
|
||||
}: {
|
||||
message: string;
|
||||
status: number;
|
||||
code?: string;
|
||||
requestId?: string;
|
||||
details?: unknown;
|
||||
rawMessage?: string;
|
||||
}) {
|
||||
super(message);
|
||||
this.name = 'ApiError';
|
||||
this.status = status;
|
||||
this.code = code;
|
||||
this.requestId = requestId;
|
||||
this.details = details;
|
||||
this.rawMessage = rawMessage;
|
||||
}
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value != null;
|
||||
}
|
||||
|
||||
function parseErrorPayload(data: unknown) {
|
||||
if (!isRecord(data)) {
|
||||
return {
|
||||
code: undefined as string | undefined,
|
||||
rawMessage: undefined as string | undefined,
|
||||
requestId: undefined as string | undefined,
|
||||
details: undefined as unknown
|
||||
};
|
||||
}
|
||||
|
||||
const code = typeof data.code === 'string' ? data.code : undefined;
|
||||
const rawMessage = typeof data.error === 'string' ? data.error : undefined;
|
||||
const requestId = typeof data.requestId === 'string' ? data.requestId : undefined;
|
||||
const details = data.details;
|
||||
|
||||
return { code, rawMessage, requestId, details };
|
||||
}
|
||||
|
||||
function defaultResolveError({ status, fallbackMessage }: ResolveErrorInput): string {
|
||||
if (fallbackMessage) {
|
||||
return fallbackMessage;
|
||||
}
|
||||
|
||||
if (status != null) {
|
||||
return `Request failed (${status}).`;
|
||||
}
|
||||
|
||||
return 'Request failed. Please try again.';
|
||||
}
|
||||
|
||||
export function createApiClient(config: CreateApiClientConfig) {
|
||||
const {
|
||||
baseUrl,
|
||||
resolveError = defaultResolveError,
|
||||
inferErrorCodeFromStatus,
|
||||
fetchImpl
|
||||
} = config;
|
||||
|
||||
async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {
|
||||
const { method = 'GET', token, body } = options;
|
||||
const runFetch = fetchImpl ?? fetch;
|
||||
|
||||
const response = await runFetch(`${baseUrl}${path}`, {
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {})
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined
|
||||
});
|
||||
|
||||
const data = await response.json().catch(() => null);
|
||||
|
||||
if (!response.ok) {
|
||||
const parsed = parseErrorPayload(data);
|
||||
const code = parsed.code ?? inferErrorCodeFromStatus?.(response.status);
|
||||
const message = resolveError({
|
||||
code,
|
||||
status: response.status,
|
||||
fallbackMessage: parsed.rawMessage
|
||||
});
|
||||
|
||||
throw new ApiError({
|
||||
message,
|
||||
status: response.status,
|
||||
code,
|
||||
requestId: parsed.requestId,
|
||||
details: parsed.details,
|
||||
rawMessage: parsed.rawMessage
|
||||
});
|
||||
}
|
||||
|
||||
return data as T;
|
||||
}
|
||||
|
||||
return {
|
||||
request
|
||||
};
|
||||
}
|
||||
29
src/api/query.ts
Normal file
29
src/api/query.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
type BuildListQueryOptions = {
|
||||
q?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
sort?: string;
|
||||
defaultSort: string;
|
||||
};
|
||||
|
||||
export function buildListQuery({
|
||||
q,
|
||||
page = 1,
|
||||
pageSize = 10,
|
||||
sort,
|
||||
defaultSort
|
||||
}: BuildListQueryOptions): string {
|
||||
const query = new URLSearchParams();
|
||||
const normalizedQuery = q?.trim();
|
||||
const normalizedSort = sort?.trim();
|
||||
|
||||
if (normalizedQuery) {
|
||||
query.set('q', normalizedQuery);
|
||||
}
|
||||
|
||||
query.set('page', String(page));
|
||||
query.set('pageSize', String(pageSize));
|
||||
query.set('sort', normalizedSort || defaultSort);
|
||||
|
||||
return query.toString();
|
||||
}
|
||||
90
src/auth/createAuthContext.tsx
Normal file
90
src/auth/createAuthContext.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
const DEFAULT_AUTH_TOKEN_KEY = 'authToken';
|
||||
const DEFAULT_REFRESH_TOKEN_KEY = 'refreshToken';
|
||||
const DEFAULT_LEGACY_KEYS = ['auth_token', 'auth_user', 'token'];
|
||||
|
||||
export type AuthState<TUser> = {
|
||||
authToken: string | null;
|
||||
refreshToken: string | null;
|
||||
currentUser: TUser | null;
|
||||
};
|
||||
|
||||
export type AuthContextValue<TUser> = AuthState<TUser> & {
|
||||
setSession: (authToken: string, refreshToken: string, currentUser: TUser) => void;
|
||||
setCurrentUser: (currentUser: TUser | null) => void;
|
||||
clearSession: () => void;
|
||||
};
|
||||
|
||||
export type CreateAuthContextOptions = {
|
||||
authTokenKey?: string;
|
||||
refreshTokenKey?: string;
|
||||
legacyKeys?: string[];
|
||||
};
|
||||
|
||||
export function createAuthContext<TUser>(options: CreateAuthContextOptions = {}) {
|
||||
const authTokenKey = options.authTokenKey ?? DEFAULT_AUTH_TOKEN_KEY;
|
||||
const refreshTokenKey = options.refreshTokenKey ?? DEFAULT_REFRESH_TOKEN_KEY;
|
||||
const legacyKeys = options.legacyKeys ?? DEFAULT_LEGACY_KEYS;
|
||||
|
||||
const AuthContext = createContext<AuthContextValue<TUser> | undefined>(undefined);
|
||||
|
||||
function readStoredSession(): AuthState<TUser> {
|
||||
for (const key of legacyKeys) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
const authToken = localStorage.getItem(authTokenKey);
|
||||
const refreshToken = localStorage.getItem(refreshTokenKey);
|
||||
|
||||
return {
|
||||
authToken,
|
||||
refreshToken,
|
||||
currentUser: null
|
||||
};
|
||||
}
|
||||
|
||||
function AuthProvider({ children }: Readonly<{ children: ReactNode }>) {
|
||||
const [state, setState] = useState<AuthState<TUser>>(readStoredSession);
|
||||
|
||||
const setSession = useCallback((authToken: string, refreshToken: string, currentUser: TUser) => {
|
||||
localStorage.setItem(authTokenKey, authToken);
|
||||
localStorage.setItem(refreshTokenKey, refreshToken);
|
||||
setState({ authToken, refreshToken, currentUser });
|
||||
}, []);
|
||||
|
||||
const clearSession = useCallback(() => {
|
||||
localStorage.removeItem(authTokenKey);
|
||||
localStorage.removeItem(refreshTokenKey);
|
||||
setState({ authToken: null, refreshToken: null, currentUser: null });
|
||||
}, []);
|
||||
|
||||
const setCurrentUser = useCallback((currentUser: TUser | null) => {
|
||||
setState((prev) => ({ ...prev, currentUser }));
|
||||
}, []);
|
||||
|
||||
const value = useMemo<AuthContextValue<TUser>>(() => ({
|
||||
...state,
|
||||
setSession,
|
||||
setCurrentUser,
|
||||
clearSession
|
||||
}), [state, setSession, setCurrentUser, clearSession]);
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
}
|
||||
|
||||
function useAuth() {
|
||||
const ctx = useContext(AuthContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useAuth must be used within AuthProvider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
return {
|
||||
AuthProvider,
|
||||
useAuth,
|
||||
AuthContext
|
||||
};
|
||||
}
|
||||
35
src/auth/jwt.ts
Normal file
35
src/auth/jwt.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
const MILLISECONDS_PER_SECOND = 1000;
|
||||
|
||||
export function decodeJwtPayload(token: string): Record<string, unknown> | null {
|
||||
const parts = token.split('.');
|
||||
if (parts.length !== 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const base64Url = parts[1];
|
||||
const base64 = base64Url.replaceAll('-', '+').replaceAll('_', '/');
|
||||
const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');
|
||||
|
||||
try {
|
||||
const payload = JSON.parse(atob(padded));
|
||||
if (payload && typeof payload === 'object') {
|
||||
return payload as Record<string, unknown>;
|
||||
}
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isJwtExpired(token: string, skewSeconds = 0) {
|
||||
const payload = decodeJwtPayload(token);
|
||||
const exp = payload?.exp;
|
||||
if (typeof exp !== 'number' || !Number.isFinite(exp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const expiresAt = exp * MILLISECONDS_PER_SECOND;
|
||||
const now = Date.now();
|
||||
return expiresAt <= now + skewSeconds * MILLISECONDS_PER_SECOND;
|
||||
}
|
||||
217
src/contexts/LeftMenuContext.tsx
Normal file
217
src/contexts/LeftMenuContext.tsx
Normal file
@@ -0,0 +1,217 @@
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
type CSSProperties,
|
||||
type ReactNode
|
||||
} from 'react';
|
||||
import { isDesktopViewport, useSidePanelMachine } from '../panels/useSidePanelMachine';
|
||||
|
||||
const SIDEBAR_WIDTH_KEY = 'authSidebarWidth';
|
||||
const SIDEBAR_COLLAPSED_KEY = 'authSidebarCollapsed';
|
||||
const SIDEBAR_DEFAULT_WIDTH = 280;
|
||||
const SIDEBAR_MIN_WIDTH = 220;
|
||||
const SIDEBAR_MAX_WIDTH = 420;
|
||||
const SIDEBAR_COLLAPSED_WIDTH = 56;
|
||||
|
||||
export type LeftMenuRenderState = {
|
||||
collapsed: boolean;
|
||||
mobileOpen: boolean;
|
||||
isDesktop: boolean;
|
||||
closeMenu: () => void;
|
||||
};
|
||||
|
||||
export type LeftMenuContent = {
|
||||
ariaLabel?: string;
|
||||
render: (state: LeftMenuRenderState) => ReactNode;
|
||||
};
|
||||
|
||||
export type LeftMenuStyle = CSSProperties & {
|
||||
'--auth-sidebar-width': string;
|
||||
};
|
||||
|
||||
type LeftMenuContextValue = {
|
||||
collapsed: boolean;
|
||||
mobileOpen: boolean;
|
||||
content: LeftMenuContent;
|
||||
desktopMenuStyle: LeftMenuStyle;
|
||||
openMenu: (content?: LeftMenuContent) => void;
|
||||
closeMenu: () => void;
|
||||
toggleMenu: (content?: LeftMenuContent) => void;
|
||||
expandMenu: () => void;
|
||||
collapseMenu: () => void;
|
||||
toggleCollapsed: () => void;
|
||||
setMenuContent: (content: LeftMenuContent | null) => void;
|
||||
startResize: ReturnType<typeof useSidePanelMachine>['startResize'];
|
||||
};
|
||||
|
||||
type LeftMenuProviderProps = {
|
||||
children: ReactNode;
|
||||
defaultContent: LeftMenuContent;
|
||||
closeOnPathname?: string;
|
||||
};
|
||||
|
||||
const LeftMenuContext = createContext<LeftMenuContextValue | undefined>(undefined);
|
||||
|
||||
function readStoredCollapsed(): boolean {
|
||||
if (!globalThis.window) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === '1';
|
||||
}
|
||||
|
||||
export function LeftMenuProvider({
|
||||
children,
|
||||
defaultContent,
|
||||
closeOnPathname
|
||||
}: Readonly<LeftMenuProviderProps>) {
|
||||
const [collapsed, setCollapsed] = useState<boolean>(() => readStoredCollapsed());
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
const [content, setContent] = useState<LeftMenuContent>(defaultContent);
|
||||
const defaultContentRef = useRef(defaultContent);
|
||||
|
||||
useEffect(() => {
|
||||
const previousDefaultContent = defaultContentRef.current;
|
||||
defaultContentRef.current = defaultContent;
|
||||
setContent((currentContent) => {
|
||||
if (currentContent === previousDefaultContent) {
|
||||
return defaultContent;
|
||||
}
|
||||
return currentContent;
|
||||
});
|
||||
}, [defaultContent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!globalThis.window) {
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem(SIDEBAR_COLLAPSED_KEY, collapsed ? '1' : '0');
|
||||
}, [collapsed]);
|
||||
|
||||
const expandMenu = useCallback(() => {
|
||||
setCollapsed(false);
|
||||
}, []);
|
||||
|
||||
const collapseMenu = useCallback(() => {
|
||||
setCollapsed(true);
|
||||
}, []);
|
||||
|
||||
const toggleCollapsed = useCallback(() => {
|
||||
setCollapsed((previous) => !previous);
|
||||
}, []);
|
||||
|
||||
const closeMobile = useCallback(() => {
|
||||
setMobileOpen(false);
|
||||
}, []);
|
||||
|
||||
const setMenuContent = useCallback((nextContent: LeftMenuContent | null) => {
|
||||
setContent(nextContent ?? defaultContentRef.current);
|
||||
}, []);
|
||||
|
||||
const closeMenu = useCallback(() => {
|
||||
if (isDesktopViewport()) {
|
||||
collapseMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
closeMobile();
|
||||
}, [collapseMenu, closeMobile]);
|
||||
|
||||
const openMenu = useCallback((nextContent?: LeftMenuContent) => {
|
||||
if (nextContent) {
|
||||
setContent(nextContent);
|
||||
}
|
||||
|
||||
if (isDesktopViewport()) {
|
||||
expandMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
setMobileOpen(true);
|
||||
}, [expandMenu]);
|
||||
|
||||
const toggleMenu = useCallback((nextContent?: LeftMenuContent) => {
|
||||
if (nextContent) {
|
||||
setContent(nextContent);
|
||||
}
|
||||
|
||||
if (isDesktopViewport()) {
|
||||
toggleCollapsed();
|
||||
return;
|
||||
}
|
||||
|
||||
setMobileOpen((previous) => !previous);
|
||||
}, [toggleCollapsed]);
|
||||
|
||||
const handleCloseOnPathname = useCallback(() => {
|
||||
setMobileOpen(false);
|
||||
setContent(defaultContentRef.current);
|
||||
}, []);
|
||||
|
||||
const { width, startResize } = useSidePanelMachine({
|
||||
storageKey: SIDEBAR_WIDTH_KEY,
|
||||
defaultWidth: SIDEBAR_DEFAULT_WIDTH,
|
||||
minWidth: SIDEBAR_MIN_WIDTH,
|
||||
maxWidth: SIDEBAR_MAX_WIDTH,
|
||||
resizeAxis: 'from-left',
|
||||
resizingBodyClass: 'auth-sidebar-resizing',
|
||||
isOpen: mobileOpen,
|
||||
canResize: !collapsed,
|
||||
shouldPersistWidth: !collapsed,
|
||||
closeOnPathname,
|
||||
onCloseOnPathname: handleCloseOnPathname,
|
||||
onEscape: closeMobile
|
||||
});
|
||||
|
||||
const desktopMenuStyle = useMemo<LeftMenuStyle>(() => ({
|
||||
'--auth-sidebar-width': `${collapsed ? SIDEBAR_COLLAPSED_WIDTH : width}px`
|
||||
}), [collapsed, width]);
|
||||
|
||||
const value = useMemo<LeftMenuContextValue>(() => ({
|
||||
collapsed,
|
||||
mobileOpen,
|
||||
content,
|
||||
desktopMenuStyle,
|
||||
openMenu,
|
||||
closeMenu,
|
||||
toggleMenu,
|
||||
expandMenu,
|
||||
collapseMenu,
|
||||
toggleCollapsed,
|
||||
setMenuContent,
|
||||
startResize
|
||||
}), [
|
||||
collapsed,
|
||||
mobileOpen,
|
||||
content,
|
||||
desktopMenuStyle,
|
||||
openMenu,
|
||||
closeMenu,
|
||||
toggleMenu,
|
||||
expandMenu,
|
||||
collapseMenu,
|
||||
toggleCollapsed,
|
||||
setMenuContent,
|
||||
startResize
|
||||
]);
|
||||
|
||||
return (
|
||||
<LeftMenuContext.Provider value={value}>
|
||||
{children}
|
||||
</LeftMenuContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useLeftMenu() {
|
||||
const ctx = useContext(LeftMenuContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useLeftMenu must be used within LeftMenuProvider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
139
src/contexts/RightSidebarContext.tsx
Normal file
139
src/contexts/RightSidebarContext.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
useState,
|
||||
type CSSProperties,
|
||||
type ReactNode
|
||||
} from 'react';
|
||||
import { isDesktopViewport, useSidePanelMachine } from '../panels/useSidePanelMachine';
|
||||
|
||||
const RIGHT_SIDEBAR_WIDTH_KEY = 'authRightSidebarWidth';
|
||||
const RIGHT_SIDEBAR_DEFAULT_WIDTH = 320;
|
||||
const RIGHT_SIDEBAR_MIN_WIDTH = 260;
|
||||
const RIGHT_SIDEBAR_MAX_WIDTH = 480;
|
||||
|
||||
export type RightSidebarContent = {
|
||||
title: string;
|
||||
content: ReactNode;
|
||||
ariaLabel?: string;
|
||||
};
|
||||
|
||||
export type RightSidebarStyle = CSSProperties & {
|
||||
'--auth-right-sidebar-width': string;
|
||||
};
|
||||
|
||||
type RightSidebarContextValue = {
|
||||
isOpen: boolean;
|
||||
content: RightSidebarContent | null;
|
||||
openSidebar: (content?: RightSidebarContent) => void;
|
||||
closeSidebar: () => void;
|
||||
toggleSidebar: (content?: RightSidebarContent) => void;
|
||||
setSidebarContent: (content: RightSidebarContent | null) => void;
|
||||
desktopSidebarStyle: RightSidebarStyle;
|
||||
startResize: ReturnType<typeof useSidePanelMachine>['startResize'];
|
||||
};
|
||||
|
||||
type RightSidebarProviderProps = {
|
||||
children: ReactNode;
|
||||
closeOnPathname?: string;
|
||||
onMobileOpenRequest?: () => void;
|
||||
};
|
||||
|
||||
const RightSidebarContext = createContext<RightSidebarContextValue | undefined>(undefined);
|
||||
|
||||
export function RightSidebarProvider({
|
||||
children,
|
||||
closeOnPathname,
|
||||
onMobileOpenRequest
|
||||
}: Readonly<RightSidebarProviderProps>) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [content, setContent] = useState<RightSidebarContent | null>(null);
|
||||
|
||||
const closeSidebar = useCallback(() => {
|
||||
setIsOpen(false);
|
||||
setContent(null);
|
||||
}, []);
|
||||
|
||||
const setSidebarContent = useCallback((nextContent: RightSidebarContent | null) => {
|
||||
setContent(nextContent);
|
||||
}, []);
|
||||
|
||||
const openSidebar = useCallback((nextContent?: RightSidebarContent) => {
|
||||
const resolvedContent = nextContent ?? content;
|
||||
if (!resolvedContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextContent) {
|
||||
setContent(nextContent);
|
||||
}
|
||||
if (!isDesktopViewport()) {
|
||||
onMobileOpenRequest?.();
|
||||
}
|
||||
setIsOpen(true);
|
||||
}, [content, onMobileOpenRequest]);
|
||||
|
||||
const toggleSidebar = useCallback((nextContent?: RightSidebarContent) => {
|
||||
if (isOpen) {
|
||||
closeSidebar();
|
||||
return;
|
||||
}
|
||||
|
||||
openSidebar(nextContent);
|
||||
}, [isOpen, closeSidebar, openSidebar]);
|
||||
|
||||
const { width, startResize } = useSidePanelMachine({
|
||||
storageKey: RIGHT_SIDEBAR_WIDTH_KEY,
|
||||
defaultWidth: RIGHT_SIDEBAR_DEFAULT_WIDTH,
|
||||
minWidth: RIGHT_SIDEBAR_MIN_WIDTH,
|
||||
maxWidth: RIGHT_SIDEBAR_MAX_WIDTH,
|
||||
resizeAxis: 'from-right',
|
||||
resizingBodyClass: 'auth-right-sidebar-resizing',
|
||||
isOpen,
|
||||
canResize: isOpen,
|
||||
shouldPersistWidth: true,
|
||||
closeOnPathname,
|
||||
onCloseOnPathname: closeSidebar,
|
||||
onEscape: closeSidebar
|
||||
});
|
||||
|
||||
const desktopSidebarStyle = useMemo<RightSidebarStyle>(() => ({
|
||||
'--auth-right-sidebar-width': `${width}px`
|
||||
}), [width]);
|
||||
|
||||
const value = useMemo<RightSidebarContextValue>(() => ({
|
||||
isOpen,
|
||||
content,
|
||||
openSidebar,
|
||||
closeSidebar,
|
||||
toggleSidebar,
|
||||
setSidebarContent,
|
||||
desktopSidebarStyle,
|
||||
startResize
|
||||
}), [
|
||||
isOpen,
|
||||
content,
|
||||
openSidebar,
|
||||
closeSidebar,
|
||||
toggleSidebar,
|
||||
setSidebarContent,
|
||||
desktopSidebarStyle,
|
||||
startResize
|
||||
]);
|
||||
|
||||
return (
|
||||
<RightSidebarContext.Provider value={value}>
|
||||
{children}
|
||||
</RightSidebarContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useRightSidebar() {
|
||||
const ctx = useContext(RightSidebarContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useRightSidebar must be used within RightSidebarProvider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
132
src/errors/createErrorResolver.ts
Normal file
132
src/errors/createErrorResolver.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
export type ErrorCatalog = Record<string, string>;
|
||||
|
||||
type ErrorLike = {
|
||||
code?: unknown;
|
||||
status?: unknown;
|
||||
message?: unknown;
|
||||
rawMessage?: unknown;
|
||||
};
|
||||
|
||||
export type ResolveErrorMessageOptions = {
|
||||
code?: string | null;
|
||||
status?: number | null;
|
||||
context?: string;
|
||||
fallbackMessage?: string | null;
|
||||
};
|
||||
|
||||
export type CreateErrorResolverConfig = {
|
||||
catalog: ErrorCatalog;
|
||||
fallbackCode?: string;
|
||||
defaultContext?: string;
|
||||
contextOverrides?: Record<string, Partial<Record<string, string>>>;
|
||||
inferCodeFromStatus?: (status?: number | null) => string | undefined;
|
||||
inferCodeFromLegacyMessage?: (message?: string | null) => string | undefined;
|
||||
};
|
||||
|
||||
export function createErrorResolver(config: CreateErrorResolverConfig) {
|
||||
const {
|
||||
catalog,
|
||||
fallbackCode,
|
||||
defaultContext = 'default',
|
||||
contextOverrides = {},
|
||||
inferCodeFromStatus,
|
||||
inferCodeFromLegacyMessage
|
||||
} = config;
|
||||
|
||||
const knownCodes = new Set(Object.keys(catalog));
|
||||
|
||||
function isKnownErrorCode(value: string): boolean {
|
||||
return knownCodes.has(value);
|
||||
}
|
||||
|
||||
function normalizeErrorCode(code?: string | null): string | undefined {
|
||||
if (!code) {
|
||||
return undefined;
|
||||
}
|
||||
return isKnownErrorCode(code) ? code : undefined;
|
||||
}
|
||||
|
||||
function inferErrorCodeFromStatus(status?: number | null): string | undefined {
|
||||
return inferCodeFromStatus?.(status);
|
||||
}
|
||||
|
||||
function resolveErrorMessage(options: ResolveErrorMessageOptions): string {
|
||||
const {
|
||||
code,
|
||||
status,
|
||||
context = defaultContext,
|
||||
fallbackMessage
|
||||
} = options;
|
||||
|
||||
const resolvedCode = normalizeErrorCode(code)
|
||||
?? inferCodeFromLegacyMessage?.(fallbackMessage)
|
||||
?? inferErrorCodeFromStatus(status);
|
||||
|
||||
if (resolvedCode) {
|
||||
const contextMessage = contextOverrides[context]?.[resolvedCode];
|
||||
if (contextMessage) {
|
||||
return contextMessage;
|
||||
}
|
||||
const catalogMessage = catalog[resolvedCode];
|
||||
if (catalogMessage) {
|
||||
return catalogMessage;
|
||||
}
|
||||
}
|
||||
|
||||
const statusCode = inferErrorCodeFromStatus(status);
|
||||
if (statusCode) {
|
||||
const contextMessage = contextOverrides[context]?.[statusCode];
|
||||
if (contextMessage) {
|
||||
return contextMessage;
|
||||
}
|
||||
const catalogMessage = catalog[statusCode];
|
||||
if (catalogMessage) {
|
||||
return catalogMessage;
|
||||
}
|
||||
}
|
||||
|
||||
if (fallbackCode && catalog[fallbackCode]) {
|
||||
return catalog[fallbackCode];
|
||||
}
|
||||
|
||||
if (fallbackMessage) {
|
||||
return fallbackMessage;
|
||||
}
|
||||
|
||||
return 'Request failed. Please try again.';
|
||||
}
|
||||
|
||||
function resolveOptionalErrorMessage(code?: string | null, context: string = defaultContext): string | undefined {
|
||||
if (!code) {
|
||||
return undefined;
|
||||
}
|
||||
return resolveErrorMessage({ code, context });
|
||||
}
|
||||
|
||||
function toErrorMessage(err: unknown, context: string = defaultContext): string {
|
||||
if (err && typeof err === 'object') {
|
||||
const errorLike = err as ErrorLike;
|
||||
const code = typeof errorLike.code === 'string' ? errorLike.code : undefined;
|
||||
const status = typeof errorLike.status === 'number' ? errorLike.status : undefined;
|
||||
const rawMessage = typeof errorLike.rawMessage === 'string' ? errorLike.rawMessage : undefined;
|
||||
const message = typeof errorLike.message === 'string' ? errorLike.message : undefined;
|
||||
|
||||
return resolveErrorMessage({
|
||||
code,
|
||||
status,
|
||||
context,
|
||||
fallbackMessage: rawMessage ?? message
|
||||
});
|
||||
}
|
||||
|
||||
return resolveErrorMessage({ context });
|
||||
}
|
||||
|
||||
return {
|
||||
isKnownErrorCode,
|
||||
inferErrorCodeFromStatus,
|
||||
resolveErrorMessage,
|
||||
resolveOptionalErrorMessage,
|
||||
toErrorMessage
|
||||
};
|
||||
}
|
||||
28
src/hooks/useCooldownTimer.ts
Normal file
28
src/hooks/useCooldownTimer.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
export function useCooldownTimer(seconds = 0, enabled = true) {
|
||||
const [cooldown, setCooldown] = useState(seconds);
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled || cooldown <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timer = globalThis.setInterval(() => {
|
||||
setCooldown((prev) => (prev > 0 ? prev - 1 : 0));
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
globalThis.clearInterval(timer);
|
||||
};
|
||||
}, [enabled, cooldown]);
|
||||
|
||||
const startCooldown = useCallback((seconds: number) => {
|
||||
setCooldown(Math.max(0, Math.floor(seconds)));
|
||||
}, []);
|
||||
|
||||
return {
|
||||
cooldown,
|
||||
startCooldown
|
||||
};
|
||||
}
|
||||
68
src/hooks/useEditableForm.ts
Normal file
68
src/hooks/useEditableForm.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useValidatedFields } from './useValidatedFields';
|
||||
|
||||
type FieldErrors<TValues> = Partial<Record<keyof TValues, string | undefined>>;
|
||||
|
||||
type UseEditableFormOptions<TValues> = {
|
||||
initialValues: TValues;
|
||||
validate: (values: TValues) => FieldErrors<TValues>;
|
||||
};
|
||||
|
||||
export function useEditableForm<TValues extends Record<string, string>>({
|
||||
initialValues,
|
||||
validate
|
||||
}: UseEditableFormOptions<TValues>) {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
const {
|
||||
values,
|
||||
errors,
|
||||
isValid,
|
||||
setValues,
|
||||
setFieldValue,
|
||||
validateAll,
|
||||
setFieldError,
|
||||
setErrors,
|
||||
clearErrors
|
||||
} = useValidatedFields({
|
||||
initialValues,
|
||||
validate
|
||||
});
|
||||
|
||||
const startEditing = useCallback((sourceValues: TValues) => {
|
||||
setValues(sourceValues, { validate: true });
|
||||
setIsEditing(true);
|
||||
}, [setValues]);
|
||||
|
||||
const discardChanges = useCallback((sourceValues: TValues) => {
|
||||
setValues(sourceValues, { clearErrors: true });
|
||||
setIsEditing(false);
|
||||
}, [setValues]);
|
||||
|
||||
const loadFromSource = useCallback((sourceValues: TValues) => {
|
||||
setValues(sourceValues, { clearErrors: true });
|
||||
}, [setValues]);
|
||||
|
||||
const commitSaved = useCallback((sourceValues: TValues) => {
|
||||
setValues(sourceValues, { clearErrors: true });
|
||||
setIsEditing(false);
|
||||
}, [setValues]);
|
||||
|
||||
return {
|
||||
values,
|
||||
errors,
|
||||
isValid,
|
||||
setValues,
|
||||
setFieldValue,
|
||||
validateAll,
|
||||
setFieldError,
|
||||
setErrors,
|
||||
clearErrors,
|
||||
isEditing,
|
||||
startEditing,
|
||||
discardChanges,
|
||||
loadFromSource,
|
||||
commitSaved,
|
||||
setIsEditing
|
||||
};
|
||||
}
|
||||
102
src/hooks/usePaginatedResource.ts
Normal file
102
src/hooks/usePaginatedResource.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
type PaginatedResourceResponse<TItem> = {
|
||||
items: TItem[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
};
|
||||
|
||||
type UsePaginatedResourceOptions<TItem> = {
|
||||
load: (params: { q: string; page: number; pageSize: number; sort?: string }) => Promise<PaginatedResourceResponse<TItem>>;
|
||||
sort?: string;
|
||||
debounceMs?: number;
|
||||
initialQuery?: string;
|
||||
initialPage?: number;
|
||||
initialPageSize?: number;
|
||||
};
|
||||
|
||||
export function usePaginatedResource<TItem>({
|
||||
load,
|
||||
sort,
|
||||
debounceMs = 250,
|
||||
initialQuery = '',
|
||||
initialPage = 1,
|
||||
initialPageSize = 10
|
||||
}: UsePaginatedResourceOptions<TItem>) {
|
||||
const [items, setItems] = useState<TItem[]>([]);
|
||||
const [q, setQ] = useState(initialQuery);
|
||||
const [page, setPage] = useState(initialPage);
|
||||
const [pageSize, setPageSize] = useState(initialPageSize);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [totalPages, setTotalPages] = useState(0);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
void (async () => {
|
||||
try {
|
||||
const response = await load({
|
||||
q,
|
||||
page,
|
||||
pageSize,
|
||||
sort
|
||||
});
|
||||
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
setItems(response.items);
|
||||
setTotal(response.total);
|
||||
setTotalPages(response.totalPages);
|
||||
setPage(response.page);
|
||||
setPageSize(response.pageSize);
|
||||
} catch (err) {
|
||||
if (!cancelled) {
|
||||
setError(err instanceof Error ? err.message : 'Request failed. Please try again.');
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, debounceMs);
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [q, page, pageSize, sort, load, debounceMs]);
|
||||
|
||||
const setQuery = useCallback((value: string) => {
|
||||
setQ(value);
|
||||
setPage(1);
|
||||
}, []);
|
||||
|
||||
const setPageSizeAndResetPage = useCallback((value: number) => {
|
||||
setPageSize(value);
|
||||
setPage(1);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
items,
|
||||
q,
|
||||
page,
|
||||
pageSize,
|
||||
total,
|
||||
totalPages,
|
||||
error,
|
||||
isLoading,
|
||||
setQuery,
|
||||
setPage,
|
||||
setPageSize: setPageSizeAndResetPage
|
||||
};
|
||||
}
|
||||
78
src/hooks/useSorting.ts
Normal file
78
src/hooks/useSorting.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
export type SortDirection = 'asc' | 'desc';
|
||||
|
||||
export type SortState = {
|
||||
field: string;
|
||||
direction: SortDirection;
|
||||
};
|
||||
|
||||
function invertDirection(direction: SortDirection): SortDirection {
|
||||
return direction === 'asc' ? 'desc' : 'asc';
|
||||
}
|
||||
|
||||
export function formatSortParam(sort: SortState | null | undefined): string | undefined {
|
||||
if (!sort) {
|
||||
return undefined;
|
||||
}
|
||||
return sort.direction === 'desc' ? `-${sort.field}` : sort.field;
|
||||
}
|
||||
|
||||
type UseSortingResult = {
|
||||
activeSort: SortState | null;
|
||||
sortParam: string | undefined;
|
||||
toggleSort: (field: string) => void;
|
||||
setSort: (next: SortState | null) => void;
|
||||
resetSort: () => void;
|
||||
};
|
||||
|
||||
export function useSorting(defaultSort?: SortState | null): UseSortingResult {
|
||||
const [overrideSort, setOverrideSort] = useState<SortState | null>(null);
|
||||
|
||||
const activeSort = overrideSort ?? defaultSort ?? null;
|
||||
|
||||
const toggleSort = useCallback((field: string) => {
|
||||
setOverrideSort((previousOverride) => {
|
||||
const baselineSort = defaultSort ?? null;
|
||||
const currentSort = previousOverride ?? baselineSort;
|
||||
|
||||
if (!currentSort || currentSort.field !== field) {
|
||||
return { field, direction: 'asc' };
|
||||
}
|
||||
|
||||
if (baselineSort && baselineSort.field === field) {
|
||||
if (previousOverride == null) {
|
||||
return { field, direction: invertDirection(baselineSort.direction) };
|
||||
}
|
||||
if (previousOverride.direction === baselineSort.direction) {
|
||||
return { field, direction: invertDirection(baselineSort.direction) };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (previousOverride == null || previousOverride.direction === 'desc') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { field, direction: 'desc' };
|
||||
});
|
||||
}, [defaultSort]);
|
||||
|
||||
const setSort = useCallback((next: SortState | null) => {
|
||||
setOverrideSort(next);
|
||||
}, []);
|
||||
|
||||
const resetSort = useCallback(() => {
|
||||
setOverrideSort(null);
|
||||
}, []);
|
||||
|
||||
const sortParam = useMemo(() => formatSortParam(activeSort), [activeSort]);
|
||||
|
||||
return {
|
||||
activeSort,
|
||||
sortParam,
|
||||
toggleSort,
|
||||
setSort,
|
||||
resetSort
|
||||
};
|
||||
}
|
||||
31
src/hooks/useSubmitState.ts
Normal file
31
src/hooks/useSubmitState.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
export function useSubmitState<TStatus = string | null>(initialStatus: TStatus) {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [submitError, setSubmitError] = useState<string | null>(null);
|
||||
const [status, setStatus] = useState<TStatus>(initialStatus);
|
||||
|
||||
const startSubmitting = useCallback(() => {
|
||||
setIsSubmitting(true);
|
||||
}, []);
|
||||
|
||||
const finishSubmitting = useCallback(() => {
|
||||
setIsSubmitting(false);
|
||||
}, []);
|
||||
|
||||
const clearFeedback = useCallback(() => {
|
||||
setSubmitError(null);
|
||||
setStatus(initialStatus);
|
||||
}, [initialStatus]);
|
||||
|
||||
return {
|
||||
isSubmitting,
|
||||
submitError,
|
||||
status,
|
||||
startSubmitting,
|
||||
finishSubmitting,
|
||||
setSubmitError,
|
||||
setStatus,
|
||||
clearFeedback
|
||||
};
|
||||
}
|
||||
166
src/hooks/useValidatedFields.ts
Normal file
166
src/hooks/useValidatedFields.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
type FieldErrors<TValues> = Partial<Record<keyof TValues, string | undefined>>;
|
||||
type TouchedFields<TValues> = Partial<Record<keyof TValues, boolean>>;
|
||||
|
||||
type SetValuesOptions = {
|
||||
validate?: boolean;
|
||||
clearErrors?: boolean;
|
||||
};
|
||||
|
||||
type SetFieldValueOptions = {
|
||||
validate?: boolean;
|
||||
touch?: boolean;
|
||||
};
|
||||
|
||||
type ValidateAllOptions = {
|
||||
touchAll?: boolean;
|
||||
};
|
||||
|
||||
type UseValidatedFieldsOptions<TValues> = {
|
||||
initialValues: TValues;
|
||||
validate: (values: TValues) => FieldErrors<TValues>;
|
||||
};
|
||||
|
||||
function hasErrors<TValues>(errors: FieldErrors<TValues>): boolean {
|
||||
return Object.values(errors).some(Boolean);
|
||||
}
|
||||
|
||||
function pickTouchedErrors<TValues>(
|
||||
errors: FieldErrors<TValues>,
|
||||
touched: TouchedFields<TValues>
|
||||
): FieldErrors<TValues> {
|
||||
const next: FieldErrors<TValues> = {};
|
||||
|
||||
for (const key of Object.keys(errors) as Array<keyof TValues>) {
|
||||
if (touched[key]) {
|
||||
next[key] = errors[key];
|
||||
}
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
function touchAll<TValues extends Record<string, string>>(values: TValues): TouchedFields<TValues> {
|
||||
const touched: TouchedFields<TValues> = {};
|
||||
|
||||
for (const key of Object.keys(values) as Array<keyof TValues>) {
|
||||
touched[key] = true;
|
||||
}
|
||||
|
||||
return touched;
|
||||
}
|
||||
|
||||
export function useValidatedFields<TValues extends Record<string, string>>({
|
||||
initialValues,
|
||||
validate
|
||||
}: UseValidatedFieldsOptions<TValues>) {
|
||||
const [values, setValues] = useState<TValues>(initialValues);
|
||||
const [allErrors, setAllErrors] = useState<FieldErrors<TValues>>(() => validate(initialValues));
|
||||
const [touched, setTouched] = useState<TouchedFields<TValues>>({});
|
||||
|
||||
const updateValues = useCallback((nextValues: TValues, options: SetValuesOptions = {}) => {
|
||||
const { validate: shouldValidate = false, clearErrors = false } = options;
|
||||
setValues(nextValues);
|
||||
|
||||
if (shouldValidate || clearErrors) {
|
||||
setAllErrors(validate(nextValues));
|
||||
}
|
||||
|
||||
if (clearErrors) {
|
||||
setTouched({});
|
||||
}
|
||||
}, [validate]);
|
||||
|
||||
const setFieldValue = useCallback(<K extends keyof TValues>(
|
||||
key: K,
|
||||
value: TValues[K],
|
||||
options: SetFieldValueOptions = {}
|
||||
) => {
|
||||
const { validate: shouldValidate = true, touch = true } = options;
|
||||
|
||||
if (touch) {
|
||||
setTouched((current) => ({
|
||||
...current,
|
||||
[key]: true
|
||||
}));
|
||||
}
|
||||
|
||||
setValues((current) => {
|
||||
const nextValues = {
|
||||
...current,
|
||||
[key]: value
|
||||
};
|
||||
|
||||
if (shouldValidate) {
|
||||
setAllErrors(validate(nextValues));
|
||||
}
|
||||
|
||||
return nextValues;
|
||||
});
|
||||
}, [validate]);
|
||||
|
||||
const validateAll = useCallback((options: ValidateAllOptions = {}) => {
|
||||
const { touchAll: shouldTouchAll = true } = options;
|
||||
const nextErrors = validate(values);
|
||||
|
||||
setAllErrors(nextErrors);
|
||||
|
||||
if (shouldTouchAll) {
|
||||
setTouched(touchAll(values));
|
||||
}
|
||||
|
||||
return nextErrors;
|
||||
}, [validate, values]);
|
||||
|
||||
const setFieldError = useCallback(<K extends keyof TValues>(key: K, message?: string) => {
|
||||
setTouched((current) => ({
|
||||
...current,
|
||||
[key]: true
|
||||
}));
|
||||
|
||||
setAllErrors((current) => ({
|
||||
...current,
|
||||
[key]: message
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const updateErrors = useCallback((nextErrors: FieldErrors<TValues>) => {
|
||||
const nextTouched: TouchedFields<TValues> = {};
|
||||
|
||||
for (const key of Object.keys(nextErrors) as Array<keyof TValues>) {
|
||||
if (nextErrors[key]) {
|
||||
nextTouched[key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
setTouched((current) => ({
|
||||
...current,
|
||||
...nextTouched
|
||||
}));
|
||||
setAllErrors(nextErrors);
|
||||
}, []);
|
||||
|
||||
const clearErrors = useCallback(() => {
|
||||
setAllErrors(validate(values));
|
||||
setTouched({});
|
||||
}, [validate, values]);
|
||||
|
||||
const errors = useMemo(() => pickTouchedErrors(allErrors, touched), [allErrors, touched]);
|
||||
|
||||
const isValid = useMemo(() => {
|
||||
return !hasErrors(validate(values));
|
||||
}, [validate, values]);
|
||||
|
||||
return {
|
||||
values,
|
||||
errors,
|
||||
isValid,
|
||||
setValues: updateValues,
|
||||
setFieldValue,
|
||||
validateAll,
|
||||
setFieldError,
|
||||
setErrors: updateErrors,
|
||||
clearErrors
|
||||
};
|
||||
}
|
||||
29
src/index.ts
Normal file
29
src/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
export { createAuthContext } from './auth/createAuthContext';
|
||||
export type { AuthContextValue, AuthState, CreateAuthContextOptions } from './auth/createAuthContext';
|
||||
|
||||
export { decodeJwtPayload, isJwtExpired } from './auth/jwt';
|
||||
|
||||
export { createApiClient, ApiError } from './api/createApiClient';
|
||||
export type { CreateApiClientConfig, RequestOptions, ResolveErrorInput } from './api/createApiClient';
|
||||
|
||||
export { buildListQuery } from './api/query';
|
||||
|
||||
export { createErrorResolver } from './errors/createErrorResolver';
|
||||
export type { CreateErrorResolverConfig, ErrorCatalog, ResolveErrorMessageOptions } from './errors/createErrorResolver';
|
||||
|
||||
export { useValidatedFields } from './hooks/useValidatedFields';
|
||||
export { useEditableForm } from './hooks/useEditableForm';
|
||||
export { useSubmitState } from './hooks/useSubmitState';
|
||||
export { usePaginatedResource } from './hooks/usePaginatedResource';
|
||||
export { useSorting, formatSortParam } from './hooks/useSorting';
|
||||
export type { SortDirection, SortState } from './hooks/useSorting';
|
||||
export { useCooldownTimer } from './hooks/useCooldownTimer';
|
||||
|
||||
export { LeftMenuProvider, useLeftMenu } from './contexts/LeftMenuContext';
|
||||
export type { LeftMenuContent, LeftMenuRenderState, LeftMenuStyle } from './contexts/LeftMenuContext';
|
||||
export { RightSidebarProvider, useRightSidebar } from './contexts/RightSidebarContext';
|
||||
export type { RightSidebarContent, RightSidebarStyle } from './contexts/RightSidebarContext';
|
||||
|
||||
export { formatDate, capitalize, splitAndCapitalize } from './utils/formatting';
|
||||
export type { SplitMode } from './utils/formatting';
|
||||
export { shouldShowVerifiedEmailBadge } from './utils/verifiedEmail';
|
||||
174
src/panels/useSidePanelMachine.ts
Normal file
174
src/panels/useSidePanelMachine.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { useCallback, useEffect, useRef, useState, type PointerEvent as ReactPointerEvent } from 'react';
|
||||
|
||||
const DEFAULT_DESKTOP_BREAKPOINT = 1024;
|
||||
|
||||
type ResizeAxis = 'from-left' | 'from-right';
|
||||
|
||||
export type SidePanelMachineOptions = {
|
||||
storageKey: string;
|
||||
defaultWidth: number;
|
||||
minWidth: number;
|
||||
maxWidth: number;
|
||||
resizeAxis: ResizeAxis;
|
||||
resizingBodyClass: string;
|
||||
isOpen: boolean;
|
||||
canResize: boolean;
|
||||
shouldPersistWidth: boolean;
|
||||
closeOnPathname?: string;
|
||||
onCloseOnPathname?: () => void;
|
||||
onEscape?: () => void;
|
||||
desktopBreakpoint?: number;
|
||||
};
|
||||
|
||||
export type SidePanelMachineResult = {
|
||||
width: number;
|
||||
isDesktop: boolean;
|
||||
startResize: (event: ReactPointerEvent<HTMLDivElement>) => void;
|
||||
};
|
||||
|
||||
export function isDesktopViewport(breakpoint = DEFAULT_DESKTOP_BREAKPOINT): boolean {
|
||||
if (!globalThis.window) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof globalThis.window.matchMedia === 'function') {
|
||||
return globalThis.window.matchMedia(`(min-width: ${breakpoint}px)`).matches;
|
||||
}
|
||||
|
||||
return window.innerWidth >= breakpoint;
|
||||
}
|
||||
|
||||
export function useSidePanelMachine({
|
||||
storageKey,
|
||||
defaultWidth,
|
||||
minWidth,
|
||||
maxWidth,
|
||||
resizeAxis,
|
||||
resizingBodyClass,
|
||||
isOpen,
|
||||
canResize,
|
||||
shouldPersistWidth,
|
||||
closeOnPathname,
|
||||
onCloseOnPathname,
|
||||
onEscape,
|
||||
desktopBreakpoint = DEFAULT_DESKTOP_BREAKPOINT
|
||||
}: SidePanelMachineOptions): SidePanelMachineResult {
|
||||
const isResizingRef = useRef(false);
|
||||
const resizeStartXRef = useRef(0);
|
||||
const resizeStartWidthRef = useRef(0);
|
||||
|
||||
const clampWidth = useCallback((value: number) => {
|
||||
return Math.min(maxWidth, Math.max(minWidth, value));
|
||||
}, [maxWidth, minWidth]);
|
||||
|
||||
const readStoredWidth = useCallback(() => {
|
||||
if (!globalThis.window) {
|
||||
return defaultWidth;
|
||||
}
|
||||
|
||||
const storedValue = localStorage.getItem(storageKey);
|
||||
const parsed = Number(storedValue);
|
||||
if (!Number.isFinite(parsed)) {
|
||||
return defaultWidth;
|
||||
}
|
||||
|
||||
return clampWidth(parsed);
|
||||
}, [defaultWidth, storageKey, clampWidth]);
|
||||
|
||||
const [width, setWidth] = useState<number>(() => readStoredWidth());
|
||||
|
||||
useEffect(() => {
|
||||
if (closeOnPathname == null || !onCloseOnPathname) {
|
||||
return;
|
||||
}
|
||||
|
||||
onCloseOnPathname();
|
||||
}, [closeOnPathname, onCloseOnPathname]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldPersistWidth || !globalThis.window) {
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem(storageKey, String(width));
|
||||
}, [shouldPersistWidth, storageKey, width]);
|
||||
|
||||
useEffect(() => {
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
if (!isResizingRef.current || !canResize) {
|
||||
return;
|
||||
}
|
||||
|
||||
const deltaX = event.clientX - resizeStartXRef.current;
|
||||
const delta = resizeAxis === 'from-left' ? deltaX : -deltaX;
|
||||
const nextWidth = clampWidth(resizeStartWidthRef.current + delta);
|
||||
setWidth(nextWidth);
|
||||
}
|
||||
|
||||
function stopResizing() {
|
||||
if (!isResizingRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
isResizingRef.current = false;
|
||||
document.body.classList.remove(resizingBodyClass);
|
||||
}
|
||||
|
||||
globalThis.addEventListener('pointermove', handlePointerMove);
|
||||
globalThis.addEventListener('pointerup', stopResizing);
|
||||
|
||||
return () => {
|
||||
globalThis.removeEventListener('pointermove', handlePointerMove);
|
||||
globalThis.removeEventListener('pointerup', stopResizing);
|
||||
document.body.classList.remove(resizingBodyClass);
|
||||
};
|
||||
}, [canResize, clampWidth, resizeAxis, resizingBodyClass]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || isDesktopViewport(desktopBreakpoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previousOverflow = document.body.style.overflow;
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
||||
return () => {
|
||||
document.body.style.overflow = previousOverflow;
|
||||
};
|
||||
}, [isOpen, desktopBreakpoint]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || !onEscape) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleEscape = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
onEscape();
|
||||
}
|
||||
};
|
||||
|
||||
globalThis.addEventListener('keydown', handleEscape);
|
||||
return () => {
|
||||
globalThis.removeEventListener('keydown', handleEscape);
|
||||
};
|
||||
}, [isOpen, onEscape]);
|
||||
|
||||
const startResize = useCallback((event: ReactPointerEvent<HTMLDivElement>) => {
|
||||
if (!canResize || !isDesktopViewport(desktopBreakpoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
isResizingRef.current = true;
|
||||
resizeStartXRef.current = event.clientX;
|
||||
resizeStartWidthRef.current = width;
|
||||
document.body.classList.add(resizingBodyClass);
|
||||
event.preventDefault();
|
||||
}, [canResize, desktopBreakpoint, resizingBodyClass, width]);
|
||||
|
||||
return {
|
||||
width,
|
||||
isDesktop: isDesktopViewport(desktopBreakpoint),
|
||||
startResize
|
||||
};
|
||||
}
|
||||
64
src/utils/formatting.ts
Normal file
64
src/utils/formatting.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
export function formatDate(value: string, seconds = false): string {
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
...(seconds ? { second: "2-digit" } : {}),
|
||||
};
|
||||
|
||||
return new Date(value).toLocaleString("it-IT", options);
|
||||
}
|
||||
|
||||
export const capitalize = (str: string) =>
|
||||
str.toLowerCase().split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
||||
|
||||
export type SplitMode = "underscore" | "camel" | "auto";
|
||||
|
||||
/** Title-case a string while preserving short all-caps acronyms (e.g., XML) */
|
||||
const toTitleCase = (s: string) =>
|
||||
s
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(/\s+/)
|
||||
.map(w =>
|
||||
/^[A-Z]{2,4}$/.test(w) ? w : w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()
|
||||
)
|
||||
.join(" ");
|
||||
|
||||
const splitUnderscoreHyphen = (s: string) => s.replaceAll(/[_-]+/g, " ");
|
||||
|
||||
/** Insert spaces at camelCase boundaries and around digit/letter edges */
|
||||
const splitCamel = (s: string) =>
|
||||
s
|
||||
// fooBar -> foo Bar ; foo2D -> foo 2D
|
||||
.replaceAll(/([a-z0-9])([A-Z])/g, "$1 $2")
|
||||
// XMLHttp -> XML Http (acronym + word)
|
||||
.replaceAll(/([A-Z])([A-Z][a-z])/g, "$1 $2")
|
||||
// letter<->digit boundaries
|
||||
.replaceAll(/([a-zA-Z])([0-9])/g, "$1 $2")
|
||||
.replaceAll(/([0-9])([a-zA-Z])/g, "$1 $2");
|
||||
|
||||
/**
|
||||
* Split and capitalize either by underscores/hyphens or camelCase.
|
||||
* mode:
|
||||
* - "underscore": split on _ or -
|
||||
* - "camel": split on camelCase boundaries
|
||||
* - "auto": pick underscore if present, otherwise camel
|
||||
*/
|
||||
export function splitAndCapitalize(str?: string, mode: SplitMode = "auto"): string {
|
||||
if (!str) return "";
|
||||
|
||||
// normalize underscores/hyphens first for auto decision
|
||||
const hasUnderscoreLike = /[_-]/.test(str);
|
||||
const chosen: SplitMode =
|
||||
mode === "auto" ? (hasUnderscoreLike ? "underscore" : "camel") : mode;
|
||||
|
||||
const spaced =
|
||||
chosen === "underscore" ? splitUnderscoreHyphen(str) : splitCamel(str);
|
||||
|
||||
// collapse extra spaces, then title-case
|
||||
return toTitleCase(spaced.replaceAll(/\s+/g, " ").trim());
|
||||
}
|
||||
20
src/utils/verifiedEmail.ts
Normal file
20
src/utils/verifiedEmail.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
type VerifiedEmailVisibilityOptions = {
|
||||
verifiedAt: string | null;
|
||||
persistedEmail: string;
|
||||
currentEmail: string;
|
||||
isEditing: boolean;
|
||||
};
|
||||
|
||||
export function shouldShowVerifiedEmailBadge(options: VerifiedEmailVisibilityOptions): boolean {
|
||||
const { verifiedAt, persistedEmail, currentEmail, isEditing } = options;
|
||||
|
||||
if (!verifiedAt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isEditing) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return persistedEmail.trim() === currentEmail.trim();
|
||||
}
|
||||
12
tsconfig.build.json
Normal file
12
tsconfig.build.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"declarationMap": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"allowImportingTsExtensions": false,
|
||||
"noEmit": true,
|
||||
"types": ["react"]
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
18
vite.config.ts
Normal file
18
vite.config.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'src/index.ts'),
|
||||
name: 'PanicCore',
|
||||
formats: ['es'],
|
||||
fileName: () => 'index.js'
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ['react']
|
||||
}
|
||||
}
|
||||
});
|
||||
769
yarn.lock
Normal file
769
yarn.lock
Normal file
@@ -0,0 +1,769 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0":
|
||||
version "7.29.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c"
|
||||
integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.28.5"
|
||||
js-tokens "^4.0.0"
|
||||
picocolors "^1.1.1"
|
||||
|
||||
"@babel/compat-data@^7.28.6":
|
||||
version "7.29.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d"
|
||||
integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==
|
||||
|
||||
"@babel/core@^7.29.0":
|
||||
version "7.29.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322"
|
||||
integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.29.0"
|
||||
"@babel/generator" "^7.29.0"
|
||||
"@babel/helper-compilation-targets" "^7.28.6"
|
||||
"@babel/helper-module-transforms" "^7.28.6"
|
||||
"@babel/helpers" "^7.28.6"
|
||||
"@babel/parser" "^7.29.0"
|
||||
"@babel/template" "^7.28.6"
|
||||
"@babel/traverse" "^7.29.0"
|
||||
"@babel/types" "^7.29.0"
|
||||
"@jridgewell/remapping" "^2.3.5"
|
||||
convert-source-map "^2.0.0"
|
||||
debug "^4.1.0"
|
||||
gensync "^1.0.0-beta.2"
|
||||
json5 "^2.2.3"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/generator@^7.29.0":
|
||||
version "7.29.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50"
|
||||
integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.29.0"
|
||||
"@babel/types" "^7.29.0"
|
||||
"@jridgewell/gen-mapping" "^0.3.12"
|
||||
"@jridgewell/trace-mapping" "^0.3.28"
|
||||
jsesc "^3.0.2"
|
||||
|
||||
"@babel/helper-compilation-targets@^7.28.6":
|
||||
version "7.28.6"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25"
|
||||
integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.28.6"
|
||||
"@babel/helper-validator-option" "^7.27.1"
|
||||
browserslist "^4.24.0"
|
||||
lru-cache "^5.1.1"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/helper-globals@^7.28.0":
|
||||
version "7.28.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
|
||||
integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
|
||||
|
||||
"@babel/helper-module-imports@^7.28.6":
|
||||
version "7.28.6"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c"
|
||||
integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.28.6"
|
||||
"@babel/types" "^7.28.6"
|
||||
|
||||
"@babel/helper-module-transforms@^7.28.6":
|
||||
version "7.28.6"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e"
|
||||
integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.28.6"
|
||||
"@babel/helper-validator-identifier" "^7.28.5"
|
||||
"@babel/traverse" "^7.28.6"
|
||||
|
||||
"@babel/helper-plugin-utils@^7.27.1":
|
||||
version "7.28.6"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8"
|
||||
integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==
|
||||
|
||||
"@babel/helper-string-parser@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
|
||||
integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.28.5":
|
||||
version "7.28.5"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
|
||||
integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==
|
||||
|
||||
"@babel/helper-validator-option@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f"
|
||||
integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==
|
||||
|
||||
"@babel/helpers@^7.28.6":
|
||||
version "7.28.6"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/helpers/-/helpers-7.28.6.tgz#fca903a313ae675617936e8998b814c415cbf5d7"
|
||||
integrity sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==
|
||||
dependencies:
|
||||
"@babel/template" "^7.28.6"
|
||||
"@babel/types" "^7.28.6"
|
||||
|
||||
"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0":
|
||||
version "7.29.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6"
|
||||
integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==
|
||||
dependencies:
|
||||
"@babel/types" "^7.29.0"
|
||||
|
||||
"@babel/plugin-transform-react-jsx-self@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz#af678d8506acf52c577cac73ff7fe6615c85fc92"
|
||||
integrity sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.27.1"
|
||||
|
||||
"@babel/plugin-transform-react-jsx-source@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz#dcfe2c24094bb757bf73960374e7c55e434f19f0"
|
||||
integrity sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.27.1"
|
||||
|
||||
"@babel/template@^7.28.6":
|
||||
version "7.28.6"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57"
|
||||
integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.28.6"
|
||||
"@babel/parser" "^7.28.6"
|
||||
"@babel/types" "^7.28.6"
|
||||
|
||||
"@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0":
|
||||
version "7.29.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a"
|
||||
integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.29.0"
|
||||
"@babel/generator" "^7.29.0"
|
||||
"@babel/helper-globals" "^7.28.0"
|
||||
"@babel/parser" "^7.29.0"
|
||||
"@babel/template" "^7.28.6"
|
||||
"@babel/types" "^7.29.0"
|
||||
debug "^4.3.1"
|
||||
|
||||
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.28.2", "@babel/types@^7.28.6", "@babel/types@^7.29.0":
|
||||
version "7.29.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7"
|
||||
integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.27.1"
|
||||
"@babel/helper-validator-identifier" "^7.28.5"
|
||||
|
||||
"@esbuild/aix-ppc64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz#815b39267f9bffd3407ea6c376ac32946e24f8d2"
|
||||
integrity sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==
|
||||
|
||||
"@esbuild/android-arm64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz#19b882408829ad8e12b10aff2840711b2da361e8"
|
||||
integrity sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==
|
||||
|
||||
"@esbuild/android-arm@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/android-arm/-/android-arm-0.27.3.tgz#90be58de27915efa27b767fcbdb37a4470627d7b"
|
||||
integrity sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==
|
||||
|
||||
"@esbuild/android-x64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/android-x64/-/android-x64-0.27.3.tgz#d7dcc976f16e01a9aaa2f9b938fbec7389f895ac"
|
||||
integrity sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==
|
||||
|
||||
"@esbuild/darwin-arm64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz#9f6cac72b3a8532298a6a4493ed639a8988e8abd"
|
||||
integrity sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==
|
||||
|
||||
"@esbuild/darwin-x64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz#ac61d645faa37fd650340f1866b0812e1fb14d6a"
|
||||
integrity sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==
|
||||
|
||||
"@esbuild/freebsd-arm64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz#b8625689d73cf1830fe58c39051acdc12474ea1b"
|
||||
integrity sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==
|
||||
|
||||
"@esbuild/freebsd-x64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz#07be7dd3c9d42fe0eccd2ab9f9ded780bc53bead"
|
||||
integrity sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==
|
||||
|
||||
"@esbuild/linux-arm64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz#bf31918fe5c798586460d2b3d6c46ed2c01ca0b6"
|
||||
integrity sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==
|
||||
|
||||
"@esbuild/linux-arm@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz#28493ee46abec1dc3f500223cd9f8d2df08f9d11"
|
||||
integrity sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==
|
||||
|
||||
"@esbuild/linux-ia32@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz#750752a8b30b43647402561eea764d0a41d0ee29"
|
||||
integrity sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==
|
||||
|
||||
"@esbuild/linux-loong64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz#a5a92813a04e71198c50f05adfaf18fc1e95b9ed"
|
||||
integrity sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==
|
||||
|
||||
"@esbuild/linux-mips64el@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz#deb45d7fd2d2161eadf1fbc593637ed766d50bb1"
|
||||
integrity sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==
|
||||
|
||||
"@esbuild/linux-ppc64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz#6f39ae0b8c4d3d2d61a65b26df79f6e12a1c3d78"
|
||||
integrity sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==
|
||||
|
||||
"@esbuild/linux-riscv64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz#4c5c19c3916612ec8e3915187030b9df0b955c1d"
|
||||
integrity sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==
|
||||
|
||||
"@esbuild/linux-s390x@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz#9ed17b3198fa08ad5ccaa9e74f6c0aff7ad0156d"
|
||||
integrity sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==
|
||||
|
||||
"@esbuild/linux-x64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz#12383dcbf71b7cf6513e58b4b08d95a710bf52a5"
|
||||
integrity sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==
|
||||
|
||||
"@esbuild/netbsd-arm64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz#dd0cb2fa543205fcd931df44f4786bfcce6df7d7"
|
||||
integrity sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==
|
||||
|
||||
"@esbuild/netbsd-x64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz#028ad1807a8e03e155153b2d025b506c3787354b"
|
||||
integrity sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==
|
||||
|
||||
"@esbuild/openbsd-arm64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz#e3c16ff3490c9b59b969fffca87f350ffc0e2af5"
|
||||
integrity sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==
|
||||
|
||||
"@esbuild/openbsd-x64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz#c5a4693fcb03d1cbecbf8b422422468dfc0d2a8b"
|
||||
integrity sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==
|
||||
|
||||
"@esbuild/openharmony-arm64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz#082082444f12db564a0775a41e1991c0e125055e"
|
||||
integrity sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==
|
||||
|
||||
"@esbuild/sunos-x64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz#5ab036c53f929e8405c4e96e865a424160a1b537"
|
||||
integrity sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==
|
||||
|
||||
"@esbuild/win32-arm64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz#38de700ef4b960a0045370c171794526e589862e"
|
||||
integrity sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==
|
||||
|
||||
"@esbuild/win32-ia32@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz#451b93dc03ec5d4f38619e6cd64d9f9eff06f55c"
|
||||
integrity sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==
|
||||
|
||||
"@esbuild/win32-x64@0.27.3":
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz#0eaf705c941a218a43dba8e09f1df1d6cd2f1f17"
|
||||
integrity sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5":
|
||||
version "0.3.13"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
|
||||
integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@jridgewell/remapping@^2.3.5":
|
||||
version "2.3.5"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1"
|
||||
integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@jridgewell/resolve-uri@^3.1.0":
|
||||
version "3.1.2"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
|
||||
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
|
||||
version "1.5.5"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
|
||||
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28":
|
||||
version "0.3.31"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
|
||||
integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@rolldown/pluginutils@1.0.0-rc.3":
|
||||
version "1.0.0-rc.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz#8a88cc92a0f741befc7bc109cb1a4c6b9408e1c5"
|
||||
integrity sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==
|
||||
|
||||
"@rollup/rollup-android-arm-eabi@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz#a6742c74c7d9d6d604ef8a48f99326b4ecda3d82"
|
||||
integrity sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==
|
||||
|
||||
"@rollup/rollup-android-arm64@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz#97247be098de4df0c11971089fd2edf80a5da8cf"
|
||||
integrity sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==
|
||||
|
||||
"@rollup/rollup-darwin-arm64@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz#674852cf14cf11b8056e0b1a2f4e872b523576cf"
|
||||
integrity sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==
|
||||
|
||||
"@rollup/rollup-darwin-x64@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz#36dfd7ed0aaf4d9d89d9ef983af72632455b0246"
|
||||
integrity sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==
|
||||
|
||||
"@rollup/rollup-freebsd-arm64@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz#2f87c2074b4220260fdb52a9996246edfc633c22"
|
||||
integrity sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==
|
||||
|
||||
"@rollup/rollup-freebsd-x64@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz#9b5a26522a38a95dc06616d1939d4d9a76937803"
|
||||
integrity sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz#86aa4859385a8734235b5e40a48e52d770758c3a"
|
||||
integrity sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz#cbe70e56e6ece8dac83eb773b624fc9e5a460976"
|
||||
integrity sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz#d14992a2e653bc3263d284bc6579b7a2890e1c45"
|
||||
integrity sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz#2fdd1ddc434ea90aeaa0851d2044789b4d07f6da"
|
||||
integrity sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==
|
||||
|
||||
"@rollup/rollup-linux-loong64-gnu@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz#8a181e6f89f969f21666a743cd411416c80099e7"
|
||||
integrity sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==
|
||||
|
||||
"@rollup/rollup-linux-loong64-musl@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz#904125af2babc395f8061daa27b5af1f4e3f2f78"
|
||||
integrity sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==
|
||||
|
||||
"@rollup/rollup-linux-ppc64-gnu@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz#a57970ac6864c9a3447411a658224bdcf948be22"
|
||||
integrity sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==
|
||||
|
||||
"@rollup/rollup-linux-ppc64-musl@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz#bb84de5b26870567a4267666e08891e80bb56a63"
|
||||
integrity sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz#72d00d2c7fb375ce3564e759db33f17a35bffab9"
|
||||
integrity sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz#4c166ef58e718f9245bd31873384ba15a5c1a883"
|
||||
integrity sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz#bb5025cde9a61db478c2ca7215808ad3bce73a09"
|
||||
integrity sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz#9b66b1f9cd95c6624c788f021c756269ffed1552"
|
||||
integrity sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==
|
||||
|
||||
"@rollup/rollup-linux-x64-musl@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz#b007ca255dc7166017d57d7d2451963f0bd23fd9"
|
||||
integrity sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==
|
||||
|
||||
"@rollup/rollup-openbsd-x64@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz#e8b357b2d1aa2c8d76a98f5f0d889eabe93f4ef9"
|
||||
integrity sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==
|
||||
|
||||
"@rollup/rollup-openharmony-arm64@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz#96c2e3f4aacd3d921981329831ff8dde492204dc"
|
||||
integrity sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz#2d865149d706d938df8b4b8f117e69a77646d581"
|
||||
integrity sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz#abe1593be0fa92325e9971c8da429c5e05b92c36"
|
||||
integrity sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==
|
||||
|
||||
"@rollup/rollup-win32-x64-gnu@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz#c4af3e9518c9a5cd4b1c163dc81d0ad4d82e7eab"
|
||||
integrity sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc@4.59.0":
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz#4584a8a87b29188a4c1fe987a9fcf701e256d86c"
|
||||
integrity sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==
|
||||
|
||||
"@types/babel__core@^7.20.5":
|
||||
version "7.20.5"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
|
||||
integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.20.7"
|
||||
"@babel/types" "^7.20.7"
|
||||
"@types/babel__generator" "*"
|
||||
"@types/babel__template" "*"
|
||||
"@types/babel__traverse" "*"
|
||||
|
||||
"@types/babel__generator@*":
|
||||
version "7.27.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9"
|
||||
integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==
|
||||
dependencies:
|
||||
"@babel/types" "^7.0.0"
|
||||
|
||||
"@types/babel__template@*":
|
||||
version "7.4.4"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f"
|
||||
integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.1.0"
|
||||
"@babel/types" "^7.0.0"
|
||||
|
||||
"@types/babel__traverse@*":
|
||||
version "7.28.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74"
|
||||
integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==
|
||||
dependencies:
|
||||
"@babel/types" "^7.28.2"
|
||||
|
||||
"@types/estree@1.0.8":
|
||||
version "1.0.8"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
|
||||
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
|
||||
|
||||
"@types/react@^19.0.0":
|
||||
version "19.2.14"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@types/react/-/react-19.2.14.tgz#39604929b5e3957e3a6fa0001dafb17c7af70bad"
|
||||
integrity sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==
|
||||
dependencies:
|
||||
csstype "^3.2.2"
|
||||
|
||||
"@vitejs/plugin-react@^5.0.0":
|
||||
version "5.1.4"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz#5b477e060bf612a7394c4febacc5de33a219b0e4"
|
||||
integrity sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==
|
||||
dependencies:
|
||||
"@babel/core" "^7.29.0"
|
||||
"@babel/plugin-transform-react-jsx-self" "^7.27.1"
|
||||
"@babel/plugin-transform-react-jsx-source" "^7.27.1"
|
||||
"@rolldown/pluginutils" "1.0.0-rc.3"
|
||||
"@types/babel__core" "^7.20.5"
|
||||
react-refresh "^0.18.0"
|
||||
|
||||
baseline-browser-mapping@^2.9.0:
|
||||
version "2.10.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz#5b09935025bf8a80e29130251e337c6a7fc8cbb9"
|
||||
integrity sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==
|
||||
|
||||
browserslist@^4.24.0:
|
||||
version "4.28.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95"
|
||||
integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==
|
||||
dependencies:
|
||||
baseline-browser-mapping "^2.9.0"
|
||||
caniuse-lite "^1.0.30001759"
|
||||
electron-to-chromium "^1.5.263"
|
||||
node-releases "^2.0.27"
|
||||
update-browserslist-db "^1.2.0"
|
||||
|
||||
caniuse-lite@^1.0.30001759:
|
||||
version "1.0.30001772"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/caniuse-lite/-/caniuse-lite-1.0.30001772.tgz#aa8a176eba0006e78c965a8215c7a1ceb030122d"
|
||||
integrity sha512-mIwLZICj+ntVTw4BT2zfp+yu/AqV6GMKfJVJMx3MwPxs+uk/uj2GLl2dH8LQbjiLDX66amCga5nKFyDgRR43kg==
|
||||
|
||||
convert-source-map@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
|
||||
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
|
||||
|
||||
csstype@^3.2.2:
|
||||
version "3.2.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a"
|
||||
integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
|
||||
|
||||
debug@^4.1.0, debug@^4.3.1:
|
||||
version "4.4.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
|
||||
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
|
||||
dependencies:
|
||||
ms "^2.1.3"
|
||||
|
||||
electron-to-chromium@^1.5.263:
|
||||
version "1.5.302"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz#032a5802b31f7119269959c69fe2015d8dad5edb"
|
||||
integrity sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==
|
||||
|
||||
esbuild@^0.27.0:
|
||||
version "0.27.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/esbuild/-/esbuild-0.27.3.tgz#5859ca8e70a3af956b26895ce4954d7e73bd27a8"
|
||||
integrity sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==
|
||||
optionalDependencies:
|
||||
"@esbuild/aix-ppc64" "0.27.3"
|
||||
"@esbuild/android-arm" "0.27.3"
|
||||
"@esbuild/android-arm64" "0.27.3"
|
||||
"@esbuild/android-x64" "0.27.3"
|
||||
"@esbuild/darwin-arm64" "0.27.3"
|
||||
"@esbuild/darwin-x64" "0.27.3"
|
||||
"@esbuild/freebsd-arm64" "0.27.3"
|
||||
"@esbuild/freebsd-x64" "0.27.3"
|
||||
"@esbuild/linux-arm" "0.27.3"
|
||||
"@esbuild/linux-arm64" "0.27.3"
|
||||
"@esbuild/linux-ia32" "0.27.3"
|
||||
"@esbuild/linux-loong64" "0.27.3"
|
||||
"@esbuild/linux-mips64el" "0.27.3"
|
||||
"@esbuild/linux-ppc64" "0.27.3"
|
||||
"@esbuild/linux-riscv64" "0.27.3"
|
||||
"@esbuild/linux-s390x" "0.27.3"
|
||||
"@esbuild/linux-x64" "0.27.3"
|
||||
"@esbuild/netbsd-arm64" "0.27.3"
|
||||
"@esbuild/netbsd-x64" "0.27.3"
|
||||
"@esbuild/openbsd-arm64" "0.27.3"
|
||||
"@esbuild/openbsd-x64" "0.27.3"
|
||||
"@esbuild/openharmony-arm64" "0.27.3"
|
||||
"@esbuild/sunos-x64" "0.27.3"
|
||||
"@esbuild/win32-arm64" "0.27.3"
|
||||
"@esbuild/win32-ia32" "0.27.3"
|
||||
"@esbuild/win32-x64" "0.27.3"
|
||||
|
||||
escalade@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
|
||||
integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
|
||||
|
||||
fdir@^6.5.0:
|
||||
version "6.5.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
|
||||
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
|
||||
|
||||
fsevents@~2.3.2, fsevents@~2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||
|
||||
gensync@^1.0.0-beta.2:
|
||||
version "1.0.0-beta.2"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
jsesc@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
|
||||
integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
|
||||
|
||||
json5@^2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
|
||||
lru-cache@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||
integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
|
||||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
ms@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
nanoid@^3.3.11:
|
||||
version "3.3.11"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
|
||||
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
|
||||
|
||||
node-releases@^2.0.27:
|
||||
version "2.0.27"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e"
|
||||
integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==
|
||||
|
||||
picocolors@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||
|
||||
picomatch@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042"
|
||||
integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
|
||||
|
||||
postcss@^8.5.6:
|
||||
version "8.5.6"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
|
||||
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
|
||||
dependencies:
|
||||
nanoid "^3.3.11"
|
||||
picocolors "^1.1.1"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
react-dom@^19.0.0:
|
||||
version "19.2.4"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/react-dom/-/react-dom-19.2.4.tgz#6fac6bd96f7db477d966c7ec17c1a2b1ad8e6591"
|
||||
integrity sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==
|
||||
dependencies:
|
||||
scheduler "^0.27.0"
|
||||
|
||||
react-refresh@^0.18.0:
|
||||
version "0.18.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/react-refresh/-/react-refresh-0.18.0.tgz#2dce97f4fe932a4d8142fa1630e475c1729c8062"
|
||||
integrity sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==
|
||||
|
||||
react@^19.0.0:
|
||||
version "19.2.4"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/react/-/react-19.2.4.tgz#438e57baa19b77cb23aab516cf635cd0579ee09a"
|
||||
integrity sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==
|
||||
|
||||
rollup@^4.43.0:
|
||||
version "4.59.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/rollup/-/rollup-4.59.0.tgz#cf74edac17c1486f562d728a4d923a694abdf06f"
|
||||
integrity sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==
|
||||
dependencies:
|
||||
"@types/estree" "1.0.8"
|
||||
optionalDependencies:
|
||||
"@rollup/rollup-android-arm-eabi" "4.59.0"
|
||||
"@rollup/rollup-android-arm64" "4.59.0"
|
||||
"@rollup/rollup-darwin-arm64" "4.59.0"
|
||||
"@rollup/rollup-darwin-x64" "4.59.0"
|
||||
"@rollup/rollup-freebsd-arm64" "4.59.0"
|
||||
"@rollup/rollup-freebsd-x64" "4.59.0"
|
||||
"@rollup/rollup-linux-arm-gnueabihf" "4.59.0"
|
||||
"@rollup/rollup-linux-arm-musleabihf" "4.59.0"
|
||||
"@rollup/rollup-linux-arm64-gnu" "4.59.0"
|
||||
"@rollup/rollup-linux-arm64-musl" "4.59.0"
|
||||
"@rollup/rollup-linux-loong64-gnu" "4.59.0"
|
||||
"@rollup/rollup-linux-loong64-musl" "4.59.0"
|
||||
"@rollup/rollup-linux-ppc64-gnu" "4.59.0"
|
||||
"@rollup/rollup-linux-ppc64-musl" "4.59.0"
|
||||
"@rollup/rollup-linux-riscv64-gnu" "4.59.0"
|
||||
"@rollup/rollup-linux-riscv64-musl" "4.59.0"
|
||||
"@rollup/rollup-linux-s390x-gnu" "4.59.0"
|
||||
"@rollup/rollup-linux-x64-gnu" "4.59.0"
|
||||
"@rollup/rollup-linux-x64-musl" "4.59.0"
|
||||
"@rollup/rollup-openbsd-x64" "4.59.0"
|
||||
"@rollup/rollup-openharmony-arm64" "4.59.0"
|
||||
"@rollup/rollup-win32-arm64-msvc" "4.59.0"
|
||||
"@rollup/rollup-win32-ia32-msvc" "4.59.0"
|
||||
"@rollup/rollup-win32-x64-gnu" "4.59.0"
|
||||
"@rollup/rollup-win32-x64-msvc" "4.59.0"
|
||||
fsevents "~2.3.2"
|
||||
|
||||
scheduler@^0.27.0:
|
||||
version "0.27.0"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd"
|
||||
integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==
|
||||
|
||||
semver@^6.3.1:
|
||||
version "6.3.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
source-map-js@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
|
||||
tinyglobby@^0.2.15:
|
||||
version "0.2.15"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2"
|
||||
integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==
|
||||
dependencies:
|
||||
fdir "^6.5.0"
|
||||
picomatch "^4.0.3"
|
||||
|
||||
typescript@^5.6.2:
|
||||
version "5.9.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"
|
||||
integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
|
||||
|
||||
update-browserslist-db@^1.2.0:
|
||||
version "1.2.3"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d"
|
||||
integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==
|
||||
dependencies:
|
||||
escalade "^3.2.0"
|
||||
picocolors "^1.1.1"
|
||||
|
||||
vite@^7.0.0:
|
||||
version "7.3.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/vite/-/vite-7.3.1.tgz#7f6cfe8fb9074138605e822a75d9d30b814d6507"
|
||||
integrity sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==
|
||||
dependencies:
|
||||
esbuild "^0.27.0"
|
||||
fdir "^6.5.0"
|
||||
picomatch "^4.0.3"
|
||||
postcss "^8.5.6"
|
||||
rollup "^4.43.0"
|
||||
tinyglobby "^0.2.15"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
yallist@^3.0.2:
|
||||
version "3.1.1"
|
||||
resolved "https://nexus.beatrice.wtf/repository/npm-group/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
||||
Reference in New Issue
Block a user