extract auth to lib

This commit is contained in:
2026-02-22 20:37:30 +01:00
parent db6813cab1
commit 9f86fe80d7
24 changed files with 2442 additions and 0 deletions

50
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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();
}

View 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
View 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;
}

View 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;
}

View 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;
}

View 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
};
}

View 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
};
}

View 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
};
}

View 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
View 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
};
}

View 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
};
}

View 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
View 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';

View 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
View 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());
}

View 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
View 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
View 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
View 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
View 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==